1use lin_alg::{f32::Vec3, f64::Vec3 as Vec3F64};
4
5use crate::{
6 MdState,
7 water::{H_MASS, MASS_WATER_MOL, O_MASS},
8};
9
10const EPS: f64 = 1e-6;
11
12impl MdState {
13 pub fn zero_linear_momentum(&mut self) {
16 let mut mass_sum = 0.0;
17 let mut p_sum = Vec3F64::new_zero(); for a in &self.atoms {
20 mass_sum += a.mass as f64;
21 let p: Vec3F64 = (a.vel * a.mass).into();
22 p_sum += p;
23 }
24
25 for w in &self.water {
26 mass_sum += MASS_WATER_MOL as f64;
27
28 let p_o: Vec3F64 = (w.o.vel * O_MASS).into();
29 let p_h0: Vec3F64 = (w.h0.vel * H_MASS).into();
30 let p_h1: Vec3F64 = (w.h1.vel * H_MASS).into();
31
32 p_sum += p_o + p_h0 + p_h1;
33 }
34
35 if mass_sum <= EPS {
36 return;
37 }
38
39 let vel_com: Vec3 = (p_sum / mass_sum).into();
40
41 for a in &mut self.atoms {
43 a.vel -= vel_com;
44 }
45
46 for w in &mut self.water {
49 w.o.vel -= vel_com;
50 w.h0.vel -= vel_com;
51 w.h1.vel -= vel_com;
52 }
53 }
54
55 pub fn zero_angular_momentum(&mut self) {
59 let mut mass_sum = 0.0;
60 let mut m_r_sum = Vec3F64::new_zero();
61
62 for a in &self.atoms {
63 mass_sum += a.mass as f64;
64
65 let m_r: Vec3F64 = (a.posit * a.mass).into();
66 m_r_sum += m_r;
67 }
68
69 for w in &self.water {
70 mass_sum += MASS_WATER_MOL as f64;
71
72 let mr_o: Vec3F64 = (w.o.posit * O_MASS).into();
73 let mr_h0: Vec3F64 = (w.h0.posit * H_MASS).into();
74 let mr_h1: Vec3F64 = (w.h1.posit * H_MASS).into();
75
76 m_r_sum += mr_o + mr_h0 + mr_h1;
77 }
78
79 if mass_sum <= EPS {
80 return;
81 }
82 let rot_com: Vec3 = (m_r_sum / mass_sum).into();
83
84 let mut i_xx = 0.0;
86 let mut i_xy = 0.0;
87 let mut i_xz = 0.0;
88 let mut i_yy = 0.0;
89 let mut i_yz = 0.0;
90 let mut i_zz = 0.0;
91
92 let mut L = Vec3::new_zero();
93
94 for a in &self.atoms {
95 let m = a.mass;
96
97 let r = a.posit - rot_com;
98 let vxr = r.cross(a.vel);
99 L += vxr * m;
100
101 let rx = r.x;
102 let ry = r.y;
103 let rz = r.z;
104 let r2 = rx * rx + ry * ry + rz * rz;
105
106 i_xx += m * (r2 - rx * rx);
107 i_yy += m * (r2 - ry * ry);
108 i_zz += m * (r2 - rz * rz);
109 i_xy -= m * (rx * ry);
110 i_xz -= m * (rx * rz);
111 i_yz -= m * (ry * rz);
112 }
113
114 for w in &self.water {
115 for a in [&w.o, &w.h0, &w.h1] {
116 let m = a.mass;
117
118 let r = a.posit - rot_com;
119 let vxr = r.cross(a.vel);
120 L += vxr * m;
121
122 let rx = r.x;
123 let ry = r.y;
124 let rz = r.z;
125 let r2 = rx * rx + ry * ry + rz * rz;
126
127 i_xx += m * (r2 - rx * rx);
128 i_yy += m * (r2 - ry * ry);
129 i_zz += m * (r2 - rz * rz);
130 i_xy -= m * (rx * ry);
131 i_xz -= m * (rx * rz);
132 i_yz -= m * (ry * rz);
133 }
134 }
135
136 let I = [[i_xx, i_xy, i_xz], [i_xy, i_yy, i_yz], [i_xz, i_yz, i_zz]];
138
139 let eps = 1.0e-6f32;
141 let Ireg = [
142 [I[0][0] + eps, I[0][1], I[0][2]],
143 [I[1][0], I[1][1] + eps, I[1][2]],
144 [I[2][0], I[2][1], I[2][2] + eps],
145 ];
146
147 let det = Ireg[0][0] * (Ireg[1][1] * Ireg[2][2] - Ireg[1][2] * Ireg[2][1])
149 - Ireg[0][1] * (Ireg[1][0] * Ireg[2][2] - Ireg[1][2] * Ireg[2][0])
150 + Ireg[0][2] * (Ireg[1][0] * Ireg[2][1] - Ireg[1][1] * Ireg[2][0]);
151
152 if det.abs() < 1e-12 {
153 return;
154 }
155
156 let inv_det = 1.0 / det;
157 let inv = [
158 [
159 (Ireg[1][1] * Ireg[2][2] - Ireg[1][2] * Ireg[2][1]) * inv_det,
160 (Ireg[0][2] * Ireg[2][1] - Ireg[0][1] * Ireg[2][2]) * inv_det,
161 (Ireg[0][1] * Ireg[1][2] - Ireg[0][2] * Ireg[1][1]) * inv_det,
162 ],
163 [
164 (Ireg[1][2] * Ireg[2][0] - Ireg[1][0] * Ireg[2][2]) * inv_det,
165 (Ireg[0][0] * Ireg[2][2] - Ireg[0][2] * Ireg[2][0]) * inv_det,
166 (Ireg[0][2] * Ireg[1][0] - Ireg[0][0] * Ireg[1][2]) * inv_det,
167 ],
168 [
169 (Ireg[1][0] * Ireg[2][1] - Ireg[1][1] * Ireg[2][0]) * inv_det,
170 (Ireg[0][1] * Ireg[2][0] - Ireg[0][0] * Ireg[2][1]) * inv_det,
171 (Ireg[0][0] * Ireg[1][1] - Ireg[0][1] * Ireg[1][0]) * inv_det,
172 ],
173 ];
174
175 let omega = Vec3 {
176 x: inv[0][0] * L.x + inv[0][1] * L.y + inv[0][2] * L.z,
177 y: inv[1][0] * L.x + inv[1][1] * L.y + inv[1][2] * L.z,
178 z: inv[2][0] * L.x + inv[2][1] * L.y + inv[2][2] * L.z,
179 };
180
181 if !omega.x.is_finite() || !omega.y.is_finite() || !omega.z.is_finite() {
183 return;
184 }
185 if omega.magnitude_squared() < 1e-16 {
186 return;
187 }
188
189 for a in &mut self.atoms {
191 let r = a.posit - rot_com;
192 a.vel -= omega.cross(r);
193 }
194
195 for w in &mut self.water {
197 let r_o = w.o.posit - rot_com;
198 let r_h0 = w.h0.posit - rot_com;
199 let r_h1 = w.h1.posit - rot_com;
200
201 w.o.vel -= omega.cross(r_o);
202 w.h0.vel -= omega.cross(r_h0);
203 w.h1.vel -= omega.cross(r_h1);
204 }
205
206 self.zero_linear_momentum();
208 }
209}