1use rayon::prelude::*;
7
8use hoomd_interaction::{NetBodyForceAndVirial, NetBodyForceVirialAndTorque};
9use hoomd_microstate::{
10 Microstate,
11 property::{NetForce, NetTorque, NetVirial},
12};
13use hoomd_vector::{Outer, Vector, Wedge};
14
15pub trait UpdateNetForceAndVirial<E> {
65 fn update_net_force_and_virial(&mut self, interaction_model: &E);
67}
68
69pub trait UpdateNetForceVirialAndTorque<E> {
132 fn update_net_force_virial_and_torque(&mut self, interaction_model: &E);
134}
135
136impl<V, B, S, X, C, E> UpdateNetForceAndVirial<E> for Microstate<B, S, X, C>
137where
138 V: Default + Vector + Outer + Send,
139 V::Tensor: Copy + Send,
140 B: NetForce<NetForce = V> + NetVirial<NetVirial = V::Tensor> + Sync,
141 S: Sync,
142 X: Sync,
143 C: Sync,
144 E: NetBodyForceAndVirial<B, S, X, C, Force = V> + Sync,
145{
146 #[inline]
147 fn update_net_force_and_virial(&mut self, interaction_model: &E) {
148 let mut net_force_and_virial_tmp = Vec::new();
149
150 (0..self.bodies().len())
151 .into_par_iter()
152 .map(|body_index| interaction_model.net_body_force_and_virial(self, body_index))
153 .collect_into_vec(&mut net_force_and_virial_tmp);
154
155 for (body_index, (net_force, net_virial)) in net_force_and_virial_tmp.iter().enumerate() {
156 self.set_body_net_force(body_index, *net_force);
157 self.set_body_net_virial(body_index, *net_virial);
158 }
159 }
160}
161
162impl<V, B, S, X, C, E> UpdateNetForceVirialAndTorque<E> for Microstate<B, S, X, C>
163where
164 V: Default + Vector + Wedge + Outer + Send,
165 V::Tensor: Copy + Send,
166 V::Bivector: Copy + Send,
167 B: NetForce<NetForce = V>
168 + NetVirial<NetVirial = V::Tensor>
169 + NetTorque<NetTorque = V::Bivector>
170 + Sync,
171 S: Sync,
172 X: Sync,
173 C: Sync,
174 E: NetBodyForceVirialAndTorque<B, S, X, C, Force = V> + Sync,
175{
176 #[inline]
177 fn update_net_force_virial_and_torque(&mut self, interaction_model: &E) {
178 let mut net_force_virial_and_torque_tmp = Vec::new();
179
180 (0..self.bodies().len())
181 .into_par_iter()
182 .map(|body_index| interaction_model.net_body_force_virial_and_torque(self, body_index))
183 .collect_into_vec(&mut net_force_virial_and_torque_tmp);
184
185 for (body_index, (net_force, net_virial, net_torque)) in
186 net_force_virial_and_torque_tmp.iter().enumerate()
187 {
188 self.set_body_net_force(body_index, *net_force);
189 self.set_body_net_virial(body_index, *net_virial);
190 self.set_body_net_torque(body_index, *net_torque);
191 }
192 }
193}
194
195#[cfg(test)]
196mod test {
197 use super::*;
198 use approxim::assert_relative_eq;
199 use assert2::check;
200
201 use hoomd_interaction::{PairwiseCutoff, Rigid, pairwise::Isotropic, univariate::LennardJones};
202 use hoomd_microstate::{
203 Body,
204 property::{DynamicOrientedPoint, DynamicPoint, Point},
205 };
206 use hoomd_vector::{Angle, Cartesian, Versor};
207
208 #[test]
211 fn net_force_2d() -> anyhow::Result<()> {
212 let mut microstate = Microstate::builder()
213 .bodies([
214 Body::single_site(DynamicPoint::default(), Point::default()),
215 Body::single_site(
216 DynamicPoint {
217 position: Cartesian::<2>::from([2.0, 0.0]),
218 ..Default::default()
219 },
220 Point::default(),
221 ),
222 ])
223 .try_build()?;
224
225 let lennard_jones = LennardJones::<12, 6>::default();
226 let pairwise_cutoff = PairwiseCutoff(Isotropic {
227 interaction: lennard_jones,
228 r_cut: 2.5,
229 });
230 let rigid = Rigid(pairwise_cutoff);
231
232 check!(microstate.bodies()[0].item.properties.net_force == [0.0, 0.0].into());
233 check!(microstate.bodies()[1].item.properties.net_force == [0.0, 0.0].into());
234
235 microstate.update_net_force_and_virial(&rigid);
236
237 assert_relative_eq!(
238 microstate.bodies()[0].item.properties.net_force,
239 [93.0 / 512.0, 0.0].into()
240 );
241 assert_relative_eq!(
242 microstate.bodies()[1].item.properties.net_force,
243 [-93.0 / 512.0, 0.0].into()
244 );
245
246 Ok(())
247 }
248
249 #[test]
250 fn net_force_3d() -> anyhow::Result<()> {
251 let mut microstate = Microstate::builder()
252 .bodies([
253 Body::single_site(DynamicPoint::default(), Point::default()),
254 Body::single_site(
255 DynamicPoint {
256 position: Cartesian::<3>::from([2.0, 0.0, 0.0]),
257 ..Default::default()
258 },
259 Point::default(),
260 ),
261 ])
262 .try_build()?;
263
264 let lennard_jones = LennardJones::<12, 6>::default();
265 let pairwise_cutoff = PairwiseCutoff(Isotropic {
266 interaction: lennard_jones,
267 r_cut: 2.5,
268 });
269 let rigid = Rigid(pairwise_cutoff);
270
271 check!(microstate.bodies()[0].item.properties.net_force == [0.0, 0.0, 0.0].into());
272 check!(microstate.bodies()[1].item.properties.net_force == [0.0, 0.0, 0.0].into());
273
274 microstate.update_net_force_and_virial(&rigid);
275
276 assert_relative_eq!(
277 microstate.bodies()[0].item.properties.net_force,
278 [93.0 / 512.0, 0.0, 0.0].into()
279 );
280 assert_relative_eq!(
281 microstate.bodies()[1].item.properties.net_force,
282 [-93.0 / 512.0, 0.0, 0.0].into()
283 );
284
285 Ok(())
286 }
287
288 #[test]
289 fn net_force_and_torque_2d() -> anyhow::Result<()> {
290 let mut microstate: Microstate<
291 DynamicOrientedPoint<Cartesian<2>, Angle>,
292 Point<Cartesian<2>>,
293 _,
294 _,
295 > = Microstate::builder()
296 .bodies([
297 Body::single_site(
298 DynamicOrientedPoint {
299 position: Cartesian::<2>::from([0.0, -1.0]),
300 ..Default::default()
301 },
302 Point::new([0.0, 1.0].into()),
303 ),
304 Body::single_site(
305 DynamicOrientedPoint {
306 position: Cartesian::<2>::from([2.0, -2.0]),
307 ..Default::default()
308 },
309 Point::new([0.0, 2.0].into()),
310 ),
311 ])
312 .try_build()?;
313
314 let lennard_jones = LennardJones::<12, 6>::default();
315 let pairwise_cutoff = PairwiseCutoff(Isotropic {
316 interaction: lennard_jones,
317 r_cut: 2.5,
318 });
319 let rigid = Rigid(pairwise_cutoff);
320
321 check!(microstate.bodies()[0].item.properties.net_force == [0.0, 0.0].into());
322 check!(microstate.bodies()[1].item.properties.net_force == [0.0, 0.0].into());
323 check!(microstate.bodies()[0].item.properties.net_torque == 0.0);
324 check!(microstate.bodies()[1].item.properties.net_torque == 0.0);
325
326 microstate.update_net_force_virial_and_torque(&rigid);
327
328 assert_relative_eq!(
329 microstate.bodies()[0].item.properties.net_force,
330 [93.0 / 512.0, 0.0].into()
331 );
332 assert_relative_eq!(
333 microstate.bodies()[1].item.properties.net_force,
334 [-93.0 / 512.0, 0.0].into()
335 );
336
337 assert_relative_eq!(
338 microstate.bodies()[0].item.properties.net_torque,
339 -93.0 / 512.0
340 );
341 assert_relative_eq!(
342 microstate.bodies()[1].item.properties.net_torque,
343 2.0 * 93.0 / 512.0
344 );
345
346 Ok(())
347 }
348
349 #[test]
350 fn net_force_and_torque_3d() -> anyhow::Result<()> {
351 let mut microstate: Microstate<
352 DynamicOrientedPoint<Cartesian<3>, Versor>,
353 Point<Cartesian<3>>,
354 _,
355 _,
356 > = Microstate::builder()
357 .bodies([
358 Body::single_site(
359 DynamicOrientedPoint {
360 position: Cartesian::<3>::from([0.0, -1.0, 0.0]),
361 ..Default::default()
362 },
363 Point::new([0.0, 1.0, 0.0].into()),
364 ),
365 Body::single_site(
366 DynamicOrientedPoint {
367 position: Cartesian::<3>::from([2.0, -2.0, 0.0]),
368 ..Default::default()
369 },
370 Point::new([0.0, 2.0, 0.0].into()),
371 ),
372 ])
373 .try_build()?;
374
375 let lennard_jones = LennardJones::<12, 6>::default();
376 let pairwise_cutoff = PairwiseCutoff(Isotropic {
377 interaction: lennard_jones,
378 r_cut: 2.5,
379 });
380 let rigid = Rigid(pairwise_cutoff);
381
382 check!(microstate.bodies()[0].item.properties.net_force == [0.0, 0.0, 0.0].into());
383 check!(microstate.bodies()[1].item.properties.net_force == [0.0, 0.0, 0.0].into());
384 check!(microstate.bodies()[0].item.properties.net_torque == [0.0, 0.0, 0.0].into());
385 check!(microstate.bodies()[1].item.properties.net_torque == [0.0, 0.0, 0.0].into());
386
387 microstate.update_net_force_virial_and_torque(&rigid);
388
389 assert_relative_eq!(
390 microstate.bodies()[0].item.properties.net_force,
391 [93.0 / 512.0, 0.0, 0.0].into()
392 );
393 assert_relative_eq!(
394 microstate.bodies()[1].item.properties.net_force,
395 [-93.0 / 512.0, 0.0, 0.0].into()
396 );
397
398 assert_relative_eq!(
399 microstate.bodies()[0].item.properties.net_torque,
400 [0.0, 0.0, -93.0 / 512.0].into()
401 );
402 assert_relative_eq!(
403 microstate.bodies()[1].item.properties.net_torque,
404 [0.0, 0.0, 2.0 * 93.0 / 512.0].into()
405 );
406
407 Ok(())
408 }
409}