OpenMD 3.2
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
RPYMobility.cpp
1/*
2 * Copyright (c) 2004-present, The University of Notre Dame. All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * SUPPORT OPEN SCIENCE! If you use OpenMD or its source code in your
32 * research, please cite the following paper when you publish your work:
33 *
34 * [1] Drisko et al., J. Open Source Softw. 9, 7004 (2024).
35 */
36
38
39#include <cmath>
40#include <limits>
41
44// Pulls in LU.hpp + CholeskyDecomposition.hpp, and (under HAVE_LAPACK) the
45// DynamicRectMatrix<RealType> overloads that dispatch to LAPACK. Without
46// LAPACK this is exactly the generic templates.
48
49namespace OpenMD {
50
51 RPYMobility::RPYMobility(const std::vector<RealType>& radii,
52 RealType viscosity) :
53 N_(radii.size()), eta_(viscosity), a_(radii) {
54 std::size_t n3 = 3 * N_;
55 M_ = DynamicRectMatrix<RealType>(n3, n3);
56 R_ = DynamicRectMatrix<RealType>(n3, n3);
57 S_ = DynamicRectMatrix<RealType>(n3, n3);
58 }
59
60 bool RPYMobility::update(const std::vector<Vector3d>& positions) {
61 // --- assemble the 3N x 3N translational mobility from tt blocks ---
62 for (std::size_t i = 0; i < N_; ++i) {
63 for (std::size_t j = 0; j < N_; ++j) {
64 Mat3x3d block =
65 (i == j) ? RPY::muTT_self(a_[i], eta_)
66 : RPY::muTT(positions[i] - positions[j], a_[i], a_[j],
67 eta_);
68 // place the 3x3 block at (3i, 3j); confirm setSubMatrix arg order is
69 // (beginRow, beginCol, block) in DynamicRectMatrix.
70 M_.setSubMatrix(3 * i, 3 * j, block);
71 }
72 }
73
74 // --- R = M^{-1} ---
75 // invertMatrix overwrites its first argument with the LU factorization,
76 // so invert a copy and keep M_ intact (HydroProp does the same).
78 if (!invertMatrix(Mwork, R_)) {
79 spd_ = false;
80 return false;
81 }
82
83 // --- S = chol(R), with S S^T = R (R is read, not modified) ---
84 CholeskyDecomposition(R_, S_);
85
86 // RPYC keeps M (and hence R = M^{-1}) SPD by construction, so a
87 // non-positive Cholesky pivot is not a routine event: it signals an
88 // upstream problem (NaN or degenerate coordinates, unphysical radii or
89 // viscosity). Flag it here; the force modifier promotes it to a hard error
90 // with molecule/step context rather than letting clamped (FDT-violating)
91 // noise leak into the run.
92 spd_ = true;
93 RealType dmax = R_.diagonals().abs().max();
94 RealType eps = std::sqrt(dmax) * std::numeric_limits<RealType>::epsilon();
95 for (std::size_t k = 0; k < 3 * N_; ++k)
96 if (!(S_(k, k) > eps)) spd_ = false; // false for 0, negative, and NaN
97
98 return spd_;
99 }
100
101 std::vector<Vector3d> RPYMobility::effectiveAmbient(
102 const std::vector<Vector3d>& positions,
103 const std::vector<Vector3d>& ambient, const Mat3x3d& E) const {
104 std::vector<Vector3d> vEff(ambient);
105 for (std::size_t i = 0; i < N_; ++i) {
106 for (std::size_t j = 0; j < N_; ++j) {
107 if (i == j) continue; // mu_td_ii : E = 0 (single sphere, linear flow)
108 vEff[i] +=
109 RPY::muTD_dot_E(positions[i] - positions[j], a_[i], a_[j], E);
110 }
111 }
112 return vEff;
113 }
114
115 std::vector<Vector3d> RPYMobility::dragForce(
116 const std::vector<Vector3d>& vEff,
117 const std::vector<Vector3d>& vel) const {
118 // f = R ( vEff - vel ), as one 3N matrix-vector product. Flatten the
119 // per-bead velocity mismatch into a 3N vector, apply R, and unpack. When
120 // the DynamicRectMatrix gemv is specialized onto BLAS (see LU/Cholesky
121 // LAPACK path) this is a single dgemv.
122 DynamicVector<RealType> dv(3 * N_);
123 for (std::size_t i = 0; i < N_; ++i)
124 for (int p = 0; p < 3; ++p)
125 dv[3 * i + p] = vEff[i][p] - vel[i][p];
126
127 DynamicVector<RealType> fv = R_ * dv; // confirm operator* (mat * vec)
128
129 std::vector<Vector3d> f(N_, V3Zero);
130 for (std::size_t i = 0; i < N_; ++i)
131 for (int p = 0; p < 3; ++p)
132 f[i][p] = fv[3 * i + p];
133 return f;
134 }
135
136 std::vector<Vector3d> RPYMobility::randomForce(const std::vector<RealType>& Z,
137 RealType kT,
138 RealType dt) const {
139 // F^R = sqrt(2 kT / dt) * S * Z, with S the lower-triangular Cholesky
140 // factor (S S^T = R). CholeskyDecomposition explicitly zeroes the upper
141 // triangle of S, so a full matrix-vector product is exact; a triangular
142 // multiply would save ~2x flops but this is O(N^2) against the O(N^3)
143 // factorization, so it is not worth a special path.
144 DynamicVector<RealType> z(3 * N_);
145 for (std::size_t k = 0; k < 3 * N_; ++k)
146 z[k] = Z[k];
147
148 DynamicVector<RealType> fv = S_ * z;
149 RealType scale = std::sqrt(2.0 * kT / dt);
150
151 std::vector<Vector3d> f(N_, V3Zero);
152 for (std::size_t i = 0; i < N_; ++i)
153 for (int p = 0; p < 3; ++p)
154 f[i][p] = scale * fv[3 * i + p];
155 return f;
156 }
157} // namespace OpenMD
LAPACK-backed fast paths for the dynamic, double-precision dense routines, dispatched by overload.
Pairwise Rotne-Prager-Yamakawa mobility blocks for a bead model.
Mat3x3d muTT(const Vector3d &rij, RealType ai, RealType aj, RealType eta)
translation-translation, carries 1/eta
Vector3d muTD_dot_E(const Vector3d &rij, RealType ai, RealType aj, const Mat3x3d &E)
mu_td_ij : E -> translational disturbance velocity at bead i = hat1 (E rhat) + hat3 (rhat ....
Per-molecule translation-only RPY mobility / resistance assembly.
Rectangular matrix class with contiguous flat storage.
Rectangular matrix class with contiguous flat storage.
Dynamically-sized vector class.
std::vector< Vector3d > randomForce(const std::vector< RealType > &Z, RealType kT, RealType dt) const
FDT random force: sqrt(2 kT / dt) * S * Z, with Z a 3N vector of independent standard normal draws (r...
std::vector< Vector3d > dragForce(const std::vector< Vector3d > &vEff, const std::vector< Vector3d > &vel) const
Hydrodynamic drag: f_i = sum_j R_ij ( vEff_j - vel_j ).
bool update(const std::vector< Vector3d > &positions)
(Re)build M, R = M^{-1}, and S = chol(R) from current bead positions. Positions must be the unwrapped...
std::vector< Vector3d > effectiveAmbient(const std::vector< Vector3d > &positions, const std::vector< Vector3d > &ambient, const Mat3x3d &E) const
vinf_eff_i = ambient_i + sum{j!=i} mu_td_ij : E (E symmetric traceless)
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.
bool invertMatrix(MatrixType &A, MatrixType &AI)
Invert input square matrix A into matrix AI.
Definition LU.hpp:101