ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
script_interface/system/System.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2013-2026 The ESPResSo project
3 *
4 * This file is part of ESPResSo.
5 *
6 * ESPResSo is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * ESPResSo is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "System.hpp"
21
22#include <config/config.hpp>
23
24#include "core/BoxGeometry.hpp"
25#include "core/Particle.hpp"
29#include "core/cells.hpp"
31#include "core/exclusions.hpp"
33#include "core/npt.hpp"
36#include "core/propagation.hpp"
37#include "core/rotation.hpp"
40
61
62#include <utils/Vector.hpp>
63#include <utils/demangle.hpp>
67
68#include <boost/mpi/collectives.hpp>
69
70#include <algorithm>
71#include <array>
72#include <cassert>
73#include <cmath>
74#include <functional>
75#include <initializer_list>
76#include <memory>
77#include <ranges>
78#include <stdexcept>
79#include <string>
80#include <type_traits>
81#include <vector>
82
83namespace ScriptInterface {
84namespace System {
85
86static bool system_created = false;
87
88#ifdef ESPRESSO_EXCLUSIONS
90 Variant const &exclusions) {
91 p.call_method("set_exclusions", {{"p_ids", exclusions}});
92}
93#endif // ESPRESSO_EXCLUSIONS
94
95static void set_bonds(Particles::ParticleHandle &p, Variant const &bonds) {
97 for (auto const &bond_flat : bond_list_flat) {
98 auto const bond_id = bond_flat[0];
99 auto const part_id =
100 std::vector<int>{bond_flat.begin() + 1, bond_flat.end()};
101 p.call_method("add_bond",
102 {{"bond_id", bond_id}, {"part_id", std::move(part_id)}});
103 }
104}
105
106/** @brief Container for leaves of the system class. */
108 Leaves() = default;
109 std::shared_ptr<CellSystem::CellSystem> cell_system;
110 std::shared_ptr<Integrators::IntegratorHandle> integrator;
111 std::shared_ptr<Interactions::BondedInteractions> bonded_interactions;
112#ifdef ESPRESSO_COLLISION_DETECTION
113 std::shared_ptr<CollisionDetection::CollisionDetection> collision_detection;
114#endif
115 std::shared_ptr<Thermostat::Thermostat> thermostat;
116 std::shared_ptr<Analysis::Analysis> analysis;
117 std::shared_ptr<Galilei::ComFixed> comfixed;
118 std::shared_ptr<Galilei::Galilei> galilei;
119 std::shared_ptr<BondBreakage::BreakageSpecs> bond_breakage;
120 std::shared_ptr<LeesEdwards::LeesEdwards> lees_edwards;
121 std::shared_ptr<Accumulators::AutoUpdateAccumulators>
123 std::shared_ptr<Constraints::Constraints> constraints;
124 std::shared_ptr<Interactions::NonBondedInteractions> non_bonded_inter;
125#ifdef ESPRESSO_ELECTROSTATICS
126 std::shared_ptr<Coulomb::Container> electrostatics;
127#endif
128#ifdef ESPRESSO_DIPOLES
129 std::shared_ptr<Dipoles::Container> magnetostatics;
130#endif
131 std::shared_ptr<LB::Container> lb;
132 std::shared_ptr<EK::Container> ek;
133 std::shared_ptr<Particles::ParticleList> part;
134
136 // Clear containers whose elements call MPI callbacks upon destruction.
137 // The containers lifetime is extended by the global context shared object
138 // registry on the head node, which can cause MPI deadlocks if they still
139 // contain elements.
141 bonded_interactions->clear();
142 }
143 if (constraints) {
144 constraints->clear();
145 }
146 }
147};
148
149System::System() : m_instance{}, m_leaves{std::make_unique<Leaves>()} {
150 auto const add_parameter =
151 [this, ptr = m_leaves.get()](std::string key, auto Leaves::*member) {
153 key.c_str(),
154 [this, ptr, member, key](Variant const &val) {
155 auto &dst = ptr->*member;
156 if (dst != nullptr) {
157 throw WriteError(key);
158 }
159 dst = get_value<std::remove_reference_t<decltype(dst)>>(val);
160 dst->bind_system(m_instance);
161 },
162 [ptr, member]() { return ptr->*member; })});
163 };
164
165 add_parameters({
166 {"box_l",
167 [this](Variant const &v) {
168 context()->parallel_try_catch([&]() {
171 throw std::domain_error("Attribute 'box_l' must be > 0");
172 }
173 m_instance->veto_boxl_change();
174 m_instance->box_geo->set_length(new_value);
175 m_instance->on_boxl_change();
176 });
177 },
178 [this]() { return m_instance->box_geo->length(); }},
179 {"periodicity",
180 [this](Variant const &v) {
182 for (unsigned int i = 0u; i < 3u; ++i) {
183 m_instance->box_geo->set_periodic(i, periodicity[i]);
184 }
185 context()->parallel_try_catch(
186 [&]() { m_instance->on_periodicity_change(); });
187 },
188 [this]() {
189 return Utils::Vector3b{m_instance->box_geo->periodic(0),
190 m_instance->box_geo->periodic(1),
191 m_instance->box_geo->periodic(2)};
192 }},
193 {"min_global_cut",
194 [this](Variant const &v) {
195 context()->parallel_try_catch([&]() {
196 auto const new_value = get_value<double>(v);
198 throw std::domain_error("Attribute 'min_global_cut' must be >= 0");
199 }
200 m_instance->set_min_global_cut(new_value);
201 });
202 },
203 [this]() { return m_instance->get_min_global_cut(); }},
204 {"max_oif_objects",
205 [this](Variant const &v) {
206 m_instance->oif_global->max_oif_objects = get_value<int>(v);
207 },
208 [this]() { return m_instance->oif_global->max_oif_objects; }},
209
210 });
211 // note: the order of leaves matters! e.g. bonds depend on thermostats,
212 // and thus a thermostat object must be instantiated before the bonds
213 add_parameter("cell_system", &Leaves::cell_system);
214 add_parameter("integrator", &Leaves::integrator);
215 add_parameter("thermostat", &Leaves::thermostat);
216 add_parameter("analysis", &Leaves::analysis);
217 add_parameter("comfixed", &Leaves::comfixed);
218 add_parameter("galilei", &Leaves::galilei);
219 add_parameter("bonded_inter", &Leaves::bonded_interactions);
220#ifdef ESPRESSO_COLLISION_DETECTION
221 add_parameter("collision_detection", &Leaves::collision_detection);
222#endif
223 add_parameter("bond_breakage", &Leaves::bond_breakage);
224 add_parameter("lees_edwards", &Leaves::lees_edwards);
225 add_parameter("auto_update_accumulators", &Leaves::auto_update_accumulators);
226 add_parameter("constraints", &Leaves::constraints);
227 add_parameter("non_bonded_inter", &Leaves::non_bonded_inter);
228#ifdef ESPRESSO_ELECTROSTATICS
229 add_parameter("electrostatics", &Leaves::electrostatics);
230#endif
231#ifdef ESPRESSO_DIPOLES
232 add_parameter("magnetostatics", &Leaves::magnetostatics);
233#endif
234 add_parameter("lbcontainer", &Leaves::lb);
235 add_parameters({
236 {"ekcontainer",
237 [this](Variant const &v) {
238 if (is_none(v)) {
239 m_leaves->ek->do_call_method("deactivate", {});
240 if (not context()->is_head_node()) {
241 return;
242 }
243 set_parameter("ekcontainer",
244 context()->make_shared("EK::Container", {}));
245 } else {
246 auto const detach_solver = [this]() {
247 auto &solver = m_leaves->ek;
248 if (solver) {
249 solver->do_call_method("deactivate", {});
250 solver->detach_system();
251 }
252 solver.reset();
253 };
254 auto const bind_solver = [this]() {
255 auto &solver = m_leaves->ek;
256 if (solver) {
257 solver->bind_system(m_instance);
258 solver->do_call_method("activate", {});
259 }
260 };
261 auto &solver = m_leaves->ek;
263 auto old_solver = solver;
264 detach_solver();
265 try {
266 solver = new_solver;
267 context()->parallel_try_catch([&]() { bind_solver(); });
268 } catch (...) {
269 detach_solver();
270 solver = old_solver;
271 bind_solver();
272 throw;
273 }
274 }
275 },
276 [this]() { return m_leaves->ek; }},
277 });
278 add_parameter("part", &Leaves::part);
279}
280
281template <typename LeafType>
282void System::do_set_default_parameter(std::string const &name) {
283 assert(context()->is_head_node());
284 auto const so_name = Utils::demangle<LeafType>().substr(17);
285 set_parameter(name, Variant{context()->make_shared(so_name, {})});
286}
287
288void System::do_construct(VariantMap const &params) {
289 /* When reloading the system state from a checkpoint file,
290 * the order of global variables instantiation matters.
291 * The @c box_l must be set before any other global variable.
292 * All these globals re-initialize the cell system, and we
293 * cannot use the default-constructed @c box_geo when e.g.
294 * long-range interactions exist in the system, otherwise
295 * runtime errors about the local geometry being smaller
296 * than the interaction range would be raised.
297 */
298 context()->parallel_try_catch([&]() {
299 if (not params.contains("box_l")) {
300 throw std::domain_error("Required argument 'box_l' not provided.");
301 }
302 if (params.contains("_regular_constructor") and system_created) {
303 throw std::runtime_error(
304 "You can only have one instance of the system class at a time");
305 }
306 });
307 m_instance = ::System::System::create();
308 ::System::set_system(m_instance);
309
310 // domain decomposition can only be set after box_l is set
311 m_instance->set_cell_structure_topology(CellStructureType::NSQUARE);
312 do_set_parameter("box_l", params.at("box_l"));
313 m_instance->set_cell_structure_topology(CellStructureType::REGULAR);
314
315 m_instance->lb.bind_system(m_instance);
316 m_instance->ek.bind_system(m_instance);
317
318 if (params.contains("_regular_constructor")) {
319 std::set<std::string> const setable_properties = {
320 "box_l", "min_global_cut",
321 "periodicity", "time",
322 "time_step", "force_cap",
323 "max_oif_objects", "_regular_constructor"};
324 for (auto const &name : std::views::elements<0>(params)) {
325 if (not setable_properties.contains(name)) {
326 context()->parallel_try_catch([&name]() {
327 throw std::domain_error(
328 "Property '" + name +
329 "' cannot be set via argument to System class");
330 });
331 }
332 }
333 for (std::string attr :
334 {"min_global_cut", "periodicity", "max_oif_objects"}) {
335 if (params.contains(attr)) {
336 do_set_parameter(attr, params.at(attr));
337 }
338 }
339 if (not context()->is_head_node()) {
340 return;
341 }
342 auto integrator = std::dynamic_pointer_cast<Integrators::IntegratorHandle>(
343 context()->make_shared("Integrators::IntegratorHandle", {}));
344 set_parameter("integrator", integrator);
345 for (std::string attr : {"time", "time_step", "force_cap"}) {
346 if (params.contains(attr)) {
347 integrator->set_parameter(attr, params.at(attr));
348 }
349 }
350 // note: the order of leaves matters! e.g. bonds depend on thermostats,
351 // and thus a thermostat object must be instantiated before the bonds
355#ifdef ESPRESSO_COLLISION_DETECTION
357 "collision_detection");
358#endif
365 "auto_update_accumulators");
368 "non_bonded_inter");
369#ifdef ESPRESSO_ELECTROSTATICS
371#endif
372#ifdef ESPRESSO_DIPOLES
374#endif
378 } else {
379 for (auto const &key : get_parameter_insertion_order()) {
380 if (key != "box_l" and params.contains(key)) {
381 do_set_parameter(key, params.at(key));
382 }
383 }
384 }
385 if (not context()->is_head_node()) {
386 return;
387 }
388 call_method("internal_attach_leaves", {});
389}
390
391static void rotate_system(CellStructure &cell_structure, double phi,
392 double theta, double alpha) {
393 auto const particles = cell_structure.local_particles();
394
395 // Calculate center of mass
397 double local_mass = 0.0;
398
399 for (auto const &p : particles) {
400 if (not p.is_virtual()) {
401 local_com += p.mass() * p.pos();
402 local_mass += p.mass();
403 }
404 }
405
406 auto const total_mass =
407 boost::mpi::all_reduce(comm_cart, local_mass, std::plus<>());
408 auto const com =
409 boost::mpi::all_reduce(comm_cart, local_com, std::plus<>()) / total_mass;
410
411 // Rotation axis in Cartesian coordinates
412 Utils::Vector3d axis;
413 axis[0] = std::sin(theta) * std::cos(phi);
414 axis[1] = std::sin(theta) * std::sin(phi);
415 axis[2] = std::cos(theta);
416
417 // Rotate particle coordinates
418 for (auto &p : particles) {
419 // Move the center of mass of the system to the origin
420 p.pos() = com + Utils::vec_rotate(axis, alpha, p.pos() - com);
421#ifdef ESPRESSO_ROTATION
422 local_rotate_particle(p, axis, alpha);
423#endif
424 }
425
429}
430
431Variant System::do_call_method(std::string const &name,
432 VariantMap const &parameters) {
433 if (name == "lock_system_creation") {
434 system_created = true;
435 return {};
436 }
437 if (name == "rescale_boxl") {
438 auto const rescale_particles = [this](unsigned dir, double scale) {
439 for (auto &p : m_instance->cell_structure->local_particles()) {
440 p.pos()[dir] *= scale;
441 }
442 };
443 auto const &box_geo = *m_instance->box_geo;
444 auto const coord = get_value<int>(parameters, "coord");
445 auto const length = get_value<double>(parameters, "length");
446 assert(coord >= 0 and coord <= 3);
447 context()->parallel_try_catch([&]() {
448 if (length <= 0.) {
449 throw std::domain_error("Parameter 'd_new' must be > 0");
450 }
451 m_instance->veto_boxl_change(true);
452 });
453 if (coord == 3) {
454 auto const old_length_inv = box_geo.length_inv();
455 auto const new_value = Utils::Vector3d::broadcast(length);
456 auto const rescale = Utils::hadamard_product(new_value, old_length_inv);
457 // when shrinking, rescale particles before the box resize
458 for (auto axis = 0u; axis < 3u; ++axis) {
459 if (rescale[axis] <= 1.) {
460 rescale_particles(axis, rescale[axis]);
461 }
462 }
463 m_instance->on_particle_change();
464 m_instance->box_geo->set_length(new_value);
465 m_instance->on_boxl_change();
466 // when growing, rescale particles after the box resize
467 for (auto axis = 0u; axis < 3u; ++axis) {
468 if (rescale[axis] > 1.) {
469 rescale_particles(axis, rescale[axis]);
470 }
471 }
472 m_instance->on_particle_change();
473 } else {
474 auto const axis = static_cast<unsigned>(coord);
475 auto const scale = length * box_geo.length_inv()[axis];
476 auto new_value = box_geo.length();
477 new_value[axis] = length;
478 // when shrinking, rescale particles before the box resize
479 if (scale <= 1.) {
480 rescale_particles(axis, scale);
481 m_instance->on_particle_change();
482 }
483 m_instance->box_geo->set_length(new_value);
484 m_instance->on_boxl_change();
485 // when growing, rescale particles after the box resize
486 if (scale > 1.) {
487 rescale_particles(axis, scale);
488 m_instance->on_particle_change();
489 }
490 }
491 return {};
492 }
493 if (name == "reaction_get_maximal_particle_id") {
494 return ::get_maximal_particle_id();
495 }
496 if (name == "get_pids_of_type") {
497 auto const type = get_value<int>(parameters, "ptype");
498 std::vector<int> pids;
499 for (auto const &p : get_system().cell_structure->local_particles()) {
500 if (p.type() == type) {
501 pids.emplace_back(p.id());
502 }
503 }
504 Utils::Mpi::gather_buffer(pids, context()->get_comm());
505 return pids;
506 }
507 if (name == "number_of_particles") {
508 auto const type = get_value<int>(parameters, "type");
509 int local_counter = 0;
510 int global_counter = 0;
511 for (auto const &p : get_system().cell_structure->local_particles()) {
512 if (p.type() == type) {
514 }
515 }
516 boost::mpi::reduce(::comm_cart, local_counter, global_counter,
517 std::plus<>(), 0);
518 return global_counter;
519 }
520 if (name == "velocity_difference") {
521 auto const pos1 = get_value<Utils::Vector3d>(parameters, "pos1");
522 auto const pos2 = get_value<Utils::Vector3d>(parameters, "pos2");
523 auto const v1 = get_value<Utils::Vector3d>(parameters, "v1");
524 auto const v2 = get_value<Utils::Vector3d>(parameters, "v2");
525 return m_instance->box_geo->velocity_difference(pos2, pos1, v2, v1);
526 }
527 if (name == "distance_vec") {
528 auto const pos1 = get_value<Utils::Vector3d>(parameters, "pos1");
529 auto const pos2 = get_value<Utils::Vector3d>(parameters, "pos2");
530 return m_instance->box_geo->get_mi_vector(pos2, pos1);
531 }
532 if (name == "rotate_system") {
533 rotate_system(*m_instance->cell_structure,
536 get_value<double>(parameters, "alpha"));
537 m_instance->on_particle_change();
538 m_instance->update_dependent_particles();
539 return {};
540 }
541 if (name == "get_propagation_modes_enum") {
543 }
544 if (name == "session_shutdown") {
545 if (m_instance) {
546 if (::System::is_same_system(m_instance.get())) {
548 }
549 assert(m_instance.use_count() == 1l);
550 m_leaves.reset();
551 m_instance.reset();
552 }
553 return {};
554 }
555 if (name == "internal_attach_leaves") {
556 m_leaves->part->attach(m_leaves->cell_system,
557 m_leaves->bonded_interactions);
558#ifdef ESPRESSO_COLLISION_DETECTION
559 m_leaves->collision_detection->attach(m_leaves->bonded_interactions);
560#endif
561 return {};
562 }
563 return {};
564}
565
566/**
567 * @brief Serialize particles.
568 * Particles need to be serialized here to reduce overhead,
569 * and also to guarantee particles get instantiated after the cell structure
570 * was instantiated (since they store a weak pointer to it).
571 */
572std::string System::get_internal_state() const {
573 auto const p_ids = get_particle_ids();
574 std::vector<std::string> object_states(p_ids.size());
575
576 std::ranges::transform(p_ids, object_states.begin(), [this](auto const p_id) {
577 auto p_obj = context()->make_shared(
578 "Particles::ParticleHandle",
579 {{"id", p_id}, {"__cell_structure", m_leaves->cell_system}});
580 auto &p_handle = dynamic_cast<Particles::ParticleHandle &>(*p_obj);
581 auto const packed_state = p_handle.serialize();
582 // custom particle serialization
583 auto state = Utils::unpack<ObjectState>(packed_state);
584 state.name = "Particles::ParticleHandle";
585 auto const bonds_view = p_handle.call_method("get_bonds_view", {});
586 state.params.emplace_back(std::string{"bonds"}, pack(bonds_view));
587#ifdef ESPRESSO_EXCLUSIONS
588 auto const exclusions = p_handle.call_method("get_exclusions", {});
589 state.params.emplace_back(std::string{"exclusions"}, pack(exclusions));
590#endif // ESPRESSO_EXCLUSIONS
591 state.params.emplace_back(std::string{"__cpt_sentinel"}, pack(None{}));
592 return Utils::pack(state);
593 });
594
596}
597
598void System::set_internal_state(std::string const &state) {
599 auto const object_states = Utils::unpack<std::vector<std::string>>(state);
600#ifdef ESPRESSO_EXCLUSIONS
601 std::unordered_map<int, Variant> exclusions = {};
602#endif // ESPRESSO_EXCLUSIONS
603 std::unordered_map<int, Variant> bonds = {};
604
605 for (auto const &packed_object : object_states) {
606 auto state = Utils::unpack<ObjectState>(packed_object);
607 VariantMap params = {};
608 for (auto const &[name, packed_value] : state.params) {
609 params[name] = unpack(packed_value, {});
610 }
611 auto const p_id = get_value<int>(params.at("id"));
612 bonds[p_id] = params.extract("bonds").mapped();
613#ifdef ESPRESSO_EXCLUSIONS
614 exclusions[p_id] = params.extract("exclusions").mapped();
615#endif // ESPRESSO_EXCLUSIONS
616 params["__cell_structure"] = get_parameter("cell_system");
617 context()->make_shared("Particles::ParticleHandle", params);
618 }
619
620 for (auto const p_id : get_particle_ids()) {
621 auto p_obj = context()->make_shared(
622 "Particles::ParticleHandle",
623 {{"id", p_id}, {"__cell_structure", m_leaves->cell_system}});
624 auto &p_handle = dynamic_cast<Particles::ParticleHandle &>(*p_obj);
625 set_bonds(p_handle, bonds[p_id]);
626#ifdef ESPRESSO_EXCLUSIONS
627 set_exclusions(p_handle, exclusions[p_id]);
628#endif // ESPRESSO_EXCLUSIONS
629 }
630}
631
632} // namespace System
633} // namespace ScriptInterface
@ NSQUARE
Atom decomposition (N-square).
@ REGULAR
Regular decomposition.
static int coord(std::string const &s)
Vector implementation and trait types for boost qvm interoperability.
This file contains everything related to the global cell structure / cell system.
Describes a cell structure / cell system.
void update_ghosts_and_resort_particle(unsigned data_parts)
Update ghost particles, with particle resort if needed.
void set_resort_particles(Cells::Resort level)
Increase the local resort level at least to level.
ParticleRange local_particles() const
void add_parameters(std::vector< AutoParameter > &&params)
Type to indicate no value in Variant.
Definition None.hpp:31
std::string serialize() const
Variant call_method(const std::string &name, const VariantMap &params)
Call a method on the object.
static std::shared_ptr< System > create()
std::shared_ptr< CellStructure > cell_structure
static DEVICE_QUALIFIER constexpr Vector< T, N > broadcast(typename Base::value_type const &value) noexcept
Create a vector that has all entries set to the same value.
Definition Vector.hpp:134
boost::mpi::communicator comm_cart
The communicator.
constexpr double inactive_cutoff
Special cutoff value for an inactive interaction.
Definition config.hpp:53
This file contains the asynchronous MPI communication.
@ DATA_PART_PROPERTIES
Particle::p.
@ DATA_PART_POSITION
Particle::r.
static void set_bonds(Particles::ParticleHandle &p, Variant const &bonds)
static void set_exclusions(Particles::ParticleHandle &p, Variant const &exclusions)
static void rotate_system(CellStructure &cell_structure, double phi, double theta, double alpha)
constexpr bool is_none(Variant const &v)
Definition Variant.hpp:163
PackedVariant pack(const Variant &v)
Transform a Variant to a PackedVariant.
T get_value(Variant const &v)
Extract value of specific type T from a Variant.
std::unordered_map< std::string, Variant > VariantMap
Definition Variant.hpp:133
auto make_unordered_map_of_variants(std::unordered_map< K, V > const &v)
Definition Variant.hpp:144
Variant unpack(const PackedVariant &v, std::unordered_map< ObjectId, ObjectRef > const &objects)
Unpack a PackedVariant.
make_recursive_variant< ObjectRef > Variant
Possible types for parameters.
Definition Variant.hpp:131
bool is_same_system(System const *const system)
void set_system(std::shared_ptr< System > new_instance)
void gather_buffer(std::vector< T, Allocator > &buffer, boost::mpi::communicator const &comm, int root=0)
Gather buffer with different size on each node.
Vector3d vec_rotate(const Vector3d &axis, double angle, const Vector3d &vector)
Rotate a vector around an axis.
std::string pack(T const &v)
Pack a serialize type into a string.
Definition pack.hpp:38
auto hadamard_product(Vector< T, N > const &a, Vector< U, N > const &b)
Definition Vector.hpp:374
STL namespace.
Various procedures concerning interactions between particles.
Exports for the NpT code.
Routines to calculate the OIF global forces for a particle triple (triangle from mesh).
std::vector< int > get_particle_ids()
Get all particle ids.
Particles creation and deletion.
std::unordered_map< std::string, int > propagation_flags_map()
Convert PropagationMode::PropagationMode to name/value pairs.
This file contains all subroutines required to process rotational motion.
void local_rotate_particle(Particle &p, const Utils::Vector3d &axis_space_frame, const double phi)
Rotate the particle p around the NORMALIZED axis aSpaceFrame by amount phi.
Definition rotation.hpp:134
Description and getter/setter for a parameter.
Container for leaves of the system class.
std::shared_ptr< Thermostat::Thermostat > thermostat
std::shared_ptr< Integrators::IntegratorHandle > integrator
std::shared_ptr< Dipoles::Container > magnetostatics
std::shared_ptr< CellSystem::CellSystem > cell_system
std::shared_ptr< Particles::ParticleList > part
std::shared_ptr< BondBreakage::BreakageSpecs > bond_breakage
std::shared_ptr< Constraints::Constraints > constraints
std::shared_ptr< Interactions::BondedInteractions > bonded_interactions
std::shared_ptr< CollisionDetection::CollisionDetection > collision_detection
std::shared_ptr< Interactions::NonBondedInteractions > non_bonded_inter
std::shared_ptr< Coulomb::Container > electrostatics
std::shared_ptr< Accumulators::AutoUpdateAccumulators > auto_update_accumulators
std::shared_ptr< LeesEdwards::LeesEdwards > lees_edwards
Recursive variant implementation.
Definition Variant.hpp:84