47#include <boost/format.hpp>
48#include <boost/mpi/collectives/all_reduce.hpp>
49#include <boost/mpi/collectives/broadcast.hpp>
50#include <boost/mpi/communicator.hpp>
71#ifdef ESPRESSO_ROTATION
72static std::array<std::array<std::string_view, 3>,
75 "Setting 'dip' is sufficient as it defines the scalar dipole moment."}},
77 "Setting 'quat' is sufficient as it defines the director."}},
79 "Setting 'dip' would overwrite 'quat'. Set 'quat' and 'dipm' instead."}},
81 "Setting 'dip' would overwrite 'director'. Set 'director' and "
88 if (
not params.contains(
"__cpt_sentinel")) {
90 boost::format(
"Contradicting particle attributes: '%s' and '%s'. %s");
92 if (params.contains(std::string{prop1})
and
93 params.contains(std::string{
prop2})) {
95 throw std::invalid_argument(
err_msg);
102#if defined(ESPRESSO_ROTATION) or defined(ESPRESSO_EXTERNAL_FORCES)
115#ifdef ESPRESSO_ROTATION
122 if (q.norm2() == 0.) {
123 throw std::domain_error(
error_msg(name,
"must be non-zero"));
129#ifdef ESPRESSO_THERMOSTAT_PER_PARTICLE
131#ifdef ESPRESSO_PARTICLE_ANISOTROPY
143template <
typename T,
class F>
144T ParticleHandle::get_particle_property(
F const &
fun)
const {
145 auto &cell_structure = get_cell_structure()->get_cell_structure();
147 auto const ptr =
const_cast<Particle const *
>(
149 std::optional<T>
ret;
150 if (ptr ==
nullptr) {
159T ParticleHandle::get_particle_property(T
const &(
Particle::*
getter)()
161 return get_particle_property<T>(
166void ParticleHandle::set_particle_property(
F const &
fun)
const {
168 auto const &comm = context()->get_comm();
170 if (ptr !=
nullptr) {
177void ParticleHandle::set_particle_property(T &(
Particle::*
setter)(),
179 set_particle_property(
183#ifdef ESPRESSO_EXCLUSIONS
184void ParticleHandle::set_exclusions(Variant
const &value) {
192 context()->parallel_try_catch([&]() {
195 context()->get_comm());
198 set_particle_property([&](
Particle const &p) {
199 for (
auto const pid : p.exclusions()) {
211ParticleHandle::ParticleHandle() {
218 {
"id", AutoParameter::read_only, [
this]() {
return m_pid; }},
223 throw std::domain_error(
224 error_msg(
"type",
"must be an integer >= 0"));
238 auto const pos = p.
pos();
240 return get_system()->
box_geo->unfolded_position(pos, image_box);
256 throw std::domain_error(
error_msg(
"mass",
"must be a float > 0"));
264 throw std::runtime_error(
"Feature MASS not compiled in");
270#ifdef ESPRESSO_ELECTROSTATICS
277 throw std::runtime_error(
"Feature ELECTROSTATICS not compiled in");
282#ifdef ESPRESSO_DIPOLES
285 set_particle_property([&value](
Particle &p) {
297#ifdef ESPRESSO_DIPOLE_FIELD_TRACKING
304#ifdef ESPRESSO_THERMAL_STONER_WOHLFARTH
307 set_particle_property([&value](
Particle &p) {
309 if (
dict.contains(
"is_enabled"))
312 if (
dict.contains(
"sw_phi_0"))
315 if (
dict.contains(
"sat_mag"))
318 if (
dict.contains(
"anisotropy_field_inv"))
321 if (
dict.contains(
"anisotropy_energy"))
324 if (
dict.contains(
"sw_tau0_inv"))
327 if (
dict.contains(
"sw_dt_incr"))
345#ifdef ESPRESSO_ROTATION
348 set_particle_property([&value](
Particle &p) {
360 set_particle_property([&quat](
Particle &p) { p.
quat() = quat; });
370 set_particle_property([&value](
Particle &p) {
384 set_particle_property([&value](
Particle &p) {
395 set_particle_property([&value](
Particle &p) {
405#ifdef ESPRESSO_ROTATIONAL_INERTIA
412#ifdef ESPRESSO_LB_ELECTROHYDRODYNAMICS
419#ifdef ESPRESSO_EXTERNAL_FORCES
422 set_particle_property([&value](
Particle &p) {
431 ::detail::get_nth_bit(fixed, 1),
432 ::detail::get_nth_bit(fixed, 2)}};
439#ifdef ESPRESSO_ROTATION
447#ifdef ESPRESSO_THERMOSTAT_PER_PARTICLE
454#ifdef ESPRESSO_ROTATION
463 {
"pos_folded", AutoParameter::read_only,
465 auto const &box_geo = *get_system()->
box_geo;
468 {
"lees_edwards_offset",
473 {
"lees_edwards_flag", AutoParameter::read_only,
475 {
"image_box", AutoParameter::read_only,
477 auto const &box_geo = *get_system()->
box_geo;
479 return box_geo.folded_image_box(p.
pos(), p.
image_box());
481 {
"node", AutoParameter::read_only,
489 throw std::domain_error(
490 error_msg(
"mol_id",
"must be an integer >= 0"));
495#ifdef ESPRESSO_VIRTUAL_SITES_RELATIVE
499 set_particle_property(
510 if (array.size() != 3) {
515 vs_relative.rel_orientation =
519 "vs_relative",
"must take the form [id, distance, quaternion]"));
521 set_particle_property(
526 return std::vector<Variant>{{
vs_rel.to_particle_id,
vs_rel.distance,
535 "propagation",
"propagation combination not accepted: " +
541#ifdef ESPRESSO_ENGINE
544 set_particle_property([&value](
Particle &p) {
548 if (
dict.contains(
"f_swim")) {
551 if (
dict.contains(
"is_engine_force_on_fluid")) {
552 auto const is_engine_force_on_fluid =
554 swim.is_engine_force_on_fluid = is_engine_force_on_fluid;
562 {
"f_swim", swim.f_swim},
563 {
"is_engine_force_on_fluid", swim.is_engine_force_on_fluid},
570Variant ParticleHandle::do_call_method(std::string
const &name,
572 if (name ==
"set_param_parallel") {
574 if (
not params.contains(
"value")) {
577 auto const &value = params.at(
"value");
578 context()->parallel_try_catch(
579 [&]() { do_set_parameter(
param_name, value); });
582 if (name ==
"update_params") {
584 context()->parallel_try_catch([&]() {
585#ifdef ESPRESSO_ROTATION
588 for (
auto const &name : get_parameter_insertion_order()) {
589 if (params.contains(name)
and name !=
"bonds") {
590 do_set_parameter(name, params.at(name));
596 if (params.contains(
"bonds_ids")) {
598 set_particle_property([&](
Particle &p) { p.
bonds().clear(); });
603 for (std::size_t i = 0; i <
bonds_ids.size(); i += 1) {
611#ifdef ESPRESSO_EXCLUSIONS
613 if (params.contains(
"exclusions")) {
614 set_exclusions(params.at(
"exclusions"));
619 if (name ==
"get_bond_by_id") {
620 if (
not context()->is_head_node()) {
623 return get_bonded_ias()->call_method(
"get_bond", params);
625 if (name ==
"get_bonds_view") {
626 if (
not context()->is_head_node()) {
631 for (
auto const &&
bond_view : bond_list) {
634 for (
auto const pid :
bond_view.partner_ids()) {
641 if (name ==
"add_bond") {
645 std::ranges::copy(partner_ids, std::back_inserter(
particle_ids));
648 }
else if (name ==
"del_bond") {
649 set_particle_property([¶ms](
Particle &p) {
654 auto &bond_list = p.
bonds();
655 auto it = std::find(bond_list.begin(), bond_list.end(),
bond_view);
656 if (
it != bond_list.end()) {
660 }
else if (name ==
"delete_all_bonds") {
661 set_particle_property([&](
Particle &p) { p.
bonds().clear(); });
662 }
else if (name ==
"is_valid_bond_id") {
664 return get_system()->
bonded_ias->get_zero_based_type(bond_id) != 0;
666 if (name ==
"remove_particle") {
667 context()->parallel_try_catch([&]() {
673 }
else if (name ==
"is_virtual") {
674 if (
not context()->is_head_node()) {
678#ifdef ESPRESSO_VIRTUAL_SITES_RELATIVE
679 }
else if (name ==
"vs_auto_relate_to") {
680 if (
not context()->is_head_node()) {
687 throw std::invalid_argument(
"A virtual site cannot relate to itself");
690 throw std::domain_error(
"Invalid particle id: " +
693 auto const system = get_system();
708 set_parameter(
"vs_relative",
Variant{std::vector<Variant>{
710 set_parameter(
"propagation",
714#ifdef ESPRESSO_VIRTUAL_SITES_CENTER_OF_MASS
715 }
else if (name ==
"vs_com_relate_to") {
719 if (
not context()->is_head_node()) {
723 throw std::domain_error(
"Invalid molecule id: " + std::to_string(
molid));
726 throw std::runtime_error(
727 "Molecule id: " + std::to_string(
molid) +
728 " is already tracked by virtual site with particle id: " +
731 set_parameter(
"mol_id", params.at(
"molid"));
736#ifdef ESPRESSO_EXCLUSIONS
737 }
else if (name ==
"has_exclusion") {
746 if (name ==
"add_exclusion") {
749 context()->parallel_try_catch([&]() {
751 context()->get_comm());
755 }
else if (name ==
"del_exclusion") {
758 context()->parallel_try_catch([&]() {
760 context()->get_comm());
764#ifdef ESPRESSO_EXCLUSIONS
765 }
else if (name ==
"set_exclusions") {
766 set_exclusions(params.at(
"p_ids"));
768 }
else if (name ==
"get_exclusions") {
769 if (
not context()->is_head_node()) {
775#ifdef ESPRESSO_ROTATION
777 if (name ==
"rotate_particle") {
778 set_particle_property([¶ms](
Particle &p) {
784 if (name ==
"convert_vector_body_to_space") {
791 if (name ==
"convert_vector_space_to_body") {
802std::size_t ParticleHandle::setup_hidden_args(
VariantMap const ¶ms) {
804 if (params.contains(
"__cell_structure")) {
806 params,
"__cell_structure");
807 so->configure(*
this);
808 m_cell_structure =
so;
811 if (params.contains(
"__bonded_ias")) {
813 params,
"__bonded_ias");
825 if (
not params.contains(
"id")) {
838 context()->parallel_try_catch([&]() {
841 auto ptr = cell_structure.get_local_particle(m_pid);
842 if (ptr !=
nullptr) {
843 throw std::invalid_argument(
"Particle " + std::to_string(m_pid) +
848#ifdef ESPRESSO_ROTATION
856 context()->parallel_try_catch([&]() {
859 std::set<std::string_view>
const skip = {
860 "pos_folded",
"pos",
"id",
"node",
"image_box",
"bonds",
861#ifdef ESPRESSO_EXCLUSIONS
864 "lees_edwards_flag",
"__cpt_sentinel",
867 for (
auto const &name : get_parameter_insertion_order()) {
868 if (params.contains(name)
and not skip.contains(name)) {
869 do_set_parameter(name, params.at(name));
872 for (
auto const &name : params | std::views::keys) {
873 if (
not skip.contains(name)
and not name.starts_with(
'_')
and
874 not has_parameter(name)) {
875 auto error_msg =
"Unknown parameter '" + name +
"' for particle.";
876 std::string
hint =
"Hint: a feature is probably not compiled in.";
880 if (
not params.contains(
"type")) {
881 do_set_parameter(
"type", 0);
883#ifdef ESPRESSO_EXCLUSIONS
884 if (params.contains(
"exclusions")) {
885 do_call_method(
"set_exclusions", {{
"p_ids", params.at(
"exclusions")}});
static auto get_real_particle(boost::mpi::communicator const &comm, int p_id)
Vector implementation and trait types for boost qvm interoperability.
Data structures for bonded interactions.
bool add_bond(System::System &system, int bond_id, std::vector< int > const &particle_ids)
Add a bond to a particle.
Immutable view on a bond.
virtual boost::mpi::communicator const & get_comm() const =0
Context * context() const
Responsible context.
std::shared_ptr< BondedInteractionsMap > bonded_ias
void on_particle_change()
Called every time a particle property changes.
std::shared_ptr< BoxGeometry > box_geo
std::shared_ptr< InteractionsNonBonded > nonbonded_ias
std::vector< T > as_vector() const
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.
std::optional< int > get_pid_for_vs_com(CellStructure &cell_structure, int mol_id)
cudaStream_t stream[1]
CUDA streams for parallel computing on CPU and GPU.
@ TRANS_VS_CENTER_OF_MASS
static uint8_t bitfield_from_flag(Utils::Vector3i const &flag)
static void sanity_checks_rotation(VariantMap const ¶ms)
static auto get_quaternion_safe(std::string const &name, Variant const &value)
auto get_real_particle(boost::mpi::communicator const &comm, int p_id, ::CellStructure &cell_structure)
void particle_exclusion_sanity_checks(int pid1, int pid2, ::CellStructure &cell_structure, auto const &comm)
static auto get_gamma_safe(Variant const &value)
static auto quat2vector(Utils::Quaternion< double > const &q)
void local_remove_exclusion(int pid1, int pid2, ::CellStructure &cell_structure)
Locally remove an exclusion to a particle.
void particle_checks(int p_id, Utils::Vector3d const &pos)
auto error_msg(std::string const &name, std::string const &reason)
static std::array< std::array< std::string_view, 3 >, 4 > constexpr contradicting_arguments_quat
void local_add_exclusion(int pid1, int pid2, ::CellStructure &cell_structure)
Locally add an exclusion to a particle.
std::unordered_map< std::string, Variant > VariantMap
auto make_vector_of_variants(std::vector< T > const &v)
make_recursive_variant< ObjectRef > Variant
Possible types for parameters.
T reduce_optional(boost::mpi::communicator const &comm, std::optional< T > const &result)
Reduce an optional on the head node.
constexpr Vector< T, 3 > convert_quaternion_to_director(Quaternion< T > const &quat)
Convert quaternion to director.
Quaternion< T > convert_director_to_quaternion(Vector< T, 3 > const &d)
Convert director to quaternion.
Various procedures concerning interactions between particles.
void make_new_particle(int p_id, Utils::Vector3d const &pos)
Create a new particle and attach it to a cell.
const Particle & get_particle_data(int p_id)
Get particle data.
int get_particle_node(int p_id)
Get the MPI rank which owns the a specific particle.
void set_particle_pos(int p_id, Utils::Vector3d const &pos)
Move particle to a new position.
void remove_particle(int p_id)
Remove particle with a given identity.
int get_maximal_particle_id()
Get maximal particle id.
static auto & get_cell_structure()
Particles creation and deletion.
std::string propagation_bitmask_to_string(int propagation)
Convert a propagation modes bitmask to a string.
bool is_valid_propagation_combination(int propagation)
Note for developers: when enabling new propagation mode combinations, make sure every single line of ...
This file contains all subroutines required to process rotational motion.
Utils::Vector3d convert_vector_body_to_space(const Particle &p, const Utils::Vector3d &vec)
std::pair< Utils::Quaternion< double >, double > convert_dip_to_quat(const Utils::Vector3d &dip)
convert a dipole moment to quaternions and dipolar strength
Utils::Vector3d convert_vector_space_to_body(const Particle &p, const Utils::Vector3d &v)
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.
Properties of a self-propelled particle.
bool swimming
Is the particle a swimmer.
The following properties define, with respect to which real particle a virtual site is placed and at ...
Struct holding all information for one particle.
constexpr auto const & dip_fld() const
bool has_exclusion(int pid) const
constexpr auto const & bonds() const
constexpr auto const & magnetic_anisotropy_field_inv() const
constexpr auto const & stoner_wohlfarth_is_enabled() const
constexpr auto const & quat() const
constexpr auto calc_dip() const
constexpr auto const & pos() const
constexpr auto const & swimming() const
constexpr auto const & rinertia() const
constexpr auto const & mass() const
constexpr auto const & dipm() const
Utils::compact_vector< int > & exclusions()
constexpr auto const & type() const
constexpr auto const & omega() const
constexpr auto const & saturation_magnetization() const
constexpr auto const & stoner_wohlfarth_dt_incr() const
constexpr auto const & magnetic_anisotropy_energy() const
constexpr auto const & ext_force() const
constexpr auto const & propagation() const
constexpr auto const & ext_torque() const
constexpr auto const & rotation() const
constexpr auto const & force() const
constexpr auto is_virtual() const
constexpr auto const & vs_relative() const
constexpr auto const & fixed() const
constexpr auto const & gamma() const
constexpr auto const & gamma_rot() const
constexpr auto const & image_box() const
constexpr auto const & mu_E() const
constexpr auto const & stoner_wohlfarth_tau0_inv() const
constexpr auto const & mol_id() const
constexpr auto const & q() const
constexpr auto const & stoner_wohlfarth_phi_0() const
constexpr auto const & v() const
constexpr auto const & torque() const
constexpr auto const & lees_edwards_flag() const
constexpr auto const & lees_edwards_offset() const
Recursive variant implementation.
Quaternion representation.
std::tuple< Utils::Quaternion< double >, double > calculate_vs_relate_to_params(Particle const &p_vs, Particle const &p_relate_to, BoxGeometry const &box_geo, double min_global_cut, bool override_cutoff_check)
Calculate the rotation quaternion and distance between two particles.