From d61758487f9cdfeb7a50260e610522e12e9046ea Mon Sep 17 00:00:00 2001 From: Somesh Kurahatti Date: Thu, 22 Aug 2024 18:55:10 +0200 Subject: [PATCH] Collision detection refactoring --- src/core/collision.cpp | 298 +++++++++++------ src/core/collision.hpp | 49 ++- src/core/collision/protocols.hpp | 307 ++++++++++++------ src/python/espressomd/collision_detection.py | 161 ++++++--- .../collision_detection/BindCenters.hpp | 73 +++++ .../collision_detection/BindVS.hpp | 85 +++++ .../CollisionDetection.hpp | 234 +++---------- .../collision_detection/GlueToSurf.hpp | 100 ++++++ .../collision_detection/Off.hpp | 50 +++ .../collision_detection/Protocol.hpp | 88 +++++ .../collision_detection/initialize.cpp | 16 +- .../interactions/BondedInteractions.hpp | 2 +- testsuite/python/bond_breakage.py | 11 +- .../checkpoint_lb_cpu__p3m_cpu/0.checkpoint | Bin 0 -> 199777 bytes testsuite/python/collision_detection.py | 39 +-- .../python/collision_detection_interface.py | 46 +-- testsuite/python/lees_edwards.py | 7 +- testsuite/python/save_checkpoint.py | 3 +- testsuite/python/test_checkpoint.py | 8 +- 19 files changed, 1028 insertions(+), 549 deletions(-) create mode 100644 src/script_interface/collision_detection/BindCenters.hpp create mode 100644 src/script_interface/collision_detection/BindVS.hpp create mode 100644 src/script_interface/collision_detection/GlueToSurf.hpp create mode 100644 src/script_interface/collision_detection/Off.hpp create mode 100644 src/script_interface/collision_detection/Protocol.hpp create mode 100644 testsuite/python/checkpoint_lb_cpu__p3m_cpu/0.checkpoint diff --git a/src/core/collision.cpp b/src/core/collision.cpp index 9effb0dea61..986c9cc532a 100644 --- a/src/core/collision.cpp +++ b/src/core/collision.cpp @@ -75,79 +75,90 @@ Particle &get_part(CellStructure &cell_structure, int id) { } } // namespace namespace Collision { -void MergedProtocol::initialize(BondedInteractionsMap &bonded_ias, InteractionsNonBonded &nonbonded_ias) { - // If mode is OFF, no further checks - if (mode == CollisionModeType::OFF) { - return; - } - // Validate distance - if (mode != CollisionModeType::OFF) { + +void BindCenters::initialize(BondedInteractionsMap &bonded_ias, InteractionsNonBonded &nonbonded_ias) { + // Validate distance if (distance <= 0.) { throw std::domain_error("Parameter 'distance' must be > 0"); } - // Cache square of cutoff distance_sq = Utils::sqr(distance); - } + // Check if bond exists + assert(bonded_ias.contains(bond_centers)); + // If the bond type to bind particle centers is not a pair bond... + // Check that the bonds have the right number of partners + if (number_of_partners(*bonded_ias.at(bond_centers)) != 1) { + throw std::runtime_error("The bond type to be used for binding particle " + "centers needs to be a pair bond"); + } + +} -#ifndef VIRTUAL_SITES_RELATIVE - // The collision modes involving virtual sites also require the creation of a - // bond between the colliding - // If we don't have virtual sites, virtual site binding isn't possible. - if ((mode == CollisionModeType::BIND_VS) or - (mode == CollisionModeType::GLUE_TO_SURF)) { +void BindVS::initialize(BondedInteractionsMap &bonded_ias, InteractionsNonBonded &nonbonded_ias) { + // Validate distance + if (distance <= 0.) { + throw std::domain_error("Parameter 'distance' must be > 0"); + } + // Cache square of cutoff + distance_sq = Utils::sqr(distance); + + #ifndef VIRTUAL_SITES_RELATIVE + // The collision modes involving virtual sites also require the creation of a + // bond between the colliding + // If we don't have virtual sites, virtual site binding isn't possible. throw std::runtime_error("collision modes based on virtual sites require " "the VIRTUAL_SITES_RELATIVE feature"); - } -#endif - -#ifdef VIRTUAL_SITES - // Check vs placement parameter - if (mode == CollisionModeType::BIND_VS) { - if (vs_placement < 0. or vs_placement > 1.) { - throw std::domain_error( + #endif + + #ifdef VIRTUAL_SITES + // Check vs placement parameter + if (vs_placement < 0. or vs_placement > 1.) { + throw std::domain_error( "Parameter 'vs_placement' must be between 0 and 1"); - } - } -#endif - - // Check if bond exists - assert(mode != CollisionModeType::BIND_CENTERS or - bonded_ias.contains(bond_centers)); - assert(mode != CollisionModeType::BIND_VS or bonded_ias.contains(bond_vs)); - - // If the bond type to bind particle centers is not a pair bond... - // Check that the bonds have the right number of partners - if ((mode == CollisionModeType::BIND_CENTERS) and - (number_of_partners(*bonded_ias.at(bond_centers)) != 1)) { - throw std::runtime_error("The bond type to be used for binding particle " - "centers needs to be a pair bond"); - } - - // The bond between the virtual sites can be pair or triple - if ((mode == CollisionModeType::BIND_VS) and - (number_of_partners(*bonded_ias.at(bond_vs)) != 1 and - number_of_partners(*bonded_ias.at(bond_vs)) != 2)) { - throw std::runtime_error("The bond type to be used for binding virtual " + } + + #endif + // Check if bond exists + assert(bonded_ias.contains(bond_vs)); + // The bond between the virtual sites can be pair or triple + if ((number_of_partners(*bonded_ias.at(bond_vs)) != 1 and + number_of_partners(*bonded_ias.at(bond_vs)) != 2)) { + throw std::runtime_error("The bond type to be used for binding virtual " "sites needs to be a pair bond"); - } - - // Create particle types + } - if (mode == CollisionModeType::BIND_VS) { - if (vs_particle_type < 0) { + // Create particle types + if (part_type_vs < 0) { throw std::domain_error("Collision detection particle type for virtual " "sites needs to be >=0"); } - nonbonded_ias.make_particle_type_exist(vs_particle_type); - } + nonbonded_ias.make_particle_type_exist(part_type_vs); + +} - if (mode == CollisionModeType::GLUE_TO_SURF) { - if (vs_particle_type < 0) { +void GlueToSurf::initialize(BondedInteractionsMap &bonded_ias, InteractionsNonBonded &nonbonded_ias) { + // Validate distance + if (distance <= 0.) { + throw std::domain_error("Parameter 'distance' must be > 0"); + } + // Cache square of cutoff + distance_sq = Utils::sqr(distance); + + #ifndef VIRTUAL_SITES_RELATIVE + // The collision modes involving virtual sites also require the creation of a + // bond between the colliding + // If we don't have virtual sites, virtual site binding isn't possible. + { + throw std::runtime_error("collision modes based on virtual sites require " + "the VIRTUAL_SITES_RELATIVE feature"); + } + #endif + + if (part_type_vs < 0) { throw std::domain_error("Collision detection particle type for virtual " "sites needs to be >=0"); } - nonbonded_ias.make_particle_type_exist(vs_particle_type); + nonbonded_ias.make_particle_type_exist(part_type_vs); if (part_type_to_be_glued < 0) { throw std::domain_error("Collision detection particle type to be glued " @@ -166,16 +177,16 @@ void MergedProtocol::initialize(BondedInteractionsMap &bonded_ias, InteractionsN "needs to be >=0"); } nonbonded_ias.make_particle_type_exist(part_type_after_glueing); - } } } + /** @brief Calculate position of vs for GLUE_TO_SURFACE mode. * Returns id of particle to bind vs to. */ static auto const &glue_to_surface_calc_vs_pos( Particle const &p1, Particle const &p2, BoxGeometry const &box_geo, - Collision::MergedProtocol const &collision_params, Utils::Vector3d &pos) { + Collision::GlueToSurf const &collision_params, Utils::Vector3d &pos) { double ratio; auto const vec21 = box_geo.get_mi_vector(p1.pos(), p2.pos()); auto const dist = vec21.norm(); @@ -199,7 +210,7 @@ static auto const &glue_to_surface_calc_vs_pos( static void bind_at_point_of_collision_calc_vs_pos( Particle const &p1, Particle const &p2, BoxGeometry const &box_geo, - Collision::MergedProtocol const &collision_params, Utils::Vector3d &pos1, + Collision::BindVS const &collision_params, Utils::Vector3d &pos1, Utils::Vector3d &pos2) { auto const vec21 = box_geo.get_mi_vector(p1.pos(), p2.pos()); pos1 = p1.pos() - vec21 * collision_params.vs_placement; @@ -209,7 +220,20 @@ static void bind_at_point_of_collision_calc_vs_pos( #ifdef VIRTUAL_SITES_RELATIVE static void place_vs_and_relate_to_particle( CellStructure &cell_structure, BoxGeometry const &box_geo, - Collision::MergedProtocol const &collision_params, double const min_global_cut, + Collision::BindVS const &collision_params, double const min_global_cut, + int const current_vs_pid, Utils::Vector3d const &pos, int const relate_to) { + Particle new_part; + new_part.id() = current_vs_pid; + new_part.pos() = pos; + auto p_vs = cell_structure.add_particle(std::move(new_part)); + vs_relate_to(*p_vs, get_part(cell_structure, relate_to), box_geo, + min_global_cut); + p_vs->type() = collision_params.part_type_vs; +} + +static void place_vs_and_relate_to_particle( + CellStructure &cell_structure, BoxGeometry const &box_geo, + Collision::GlueToSurf const &collision_params, double const min_global_cut, int const current_vs_pid, Utils::Vector3d const &pos, int const relate_to) { Particle new_part; new_part.id() = current_vs_pid; @@ -217,12 +241,12 @@ static void place_vs_and_relate_to_particle( auto p_vs = cell_structure.add_particle(std::move(new_part)); vs_relate_to(*p_vs, get_part(cell_structure, relate_to), box_geo, min_global_cut); - p_vs->type() = collision_params.vs_particle_type; + p_vs->type() = collision_params.part_type_vs; } static void bind_at_poc_create_bond_between_vs( CellStructure &cell_structure, BondedInteractionsMap const &bonded_ias, - Collision::MergedProtocol const &collision_params, int const current_vs_pid, + Collision::BindVS const &collision_params, int const current_vs_pid, CollisionPair const &c) { switch (number_of_partners(*bonded_ias.at(collision_params.bond_vs))) { case 1: { @@ -248,7 +272,7 @@ static void bind_at_poc_create_bond_between_vs( static void glue_to_surface_bind_part_to_vs( Particle const *const p1, Particle const *const p2, - int const vs_pid_plus_one, Collision::MergedProtocol const &collision_params, + int const vs_pid_plus_one, Collision::GlueToSurf const &collision_params, CellStructure &cell_structure) { // Create bond between the virtual particles const int bondG[] = {vs_pid_plus_one - 1}; @@ -279,21 +303,43 @@ void CollisionDetection::handle_collisions(CellStructure &cell_structure) { auto &system = get_system(); auto const &box_geo = *system.box_geo; auto const min_global_cut = system.get_min_global_cut(); - auto update_propagations = m_protocol->handle_collisions(cell_structure, box_geo, min_global_cut, *system.bonded_ias, local_collision_queue); + auto const &BondedIas = *system.bonded_ias; + if (m_protocol){ + bool update_propagations = std::visit([&cell_structure, &box_geo, min_global_cut, &BondedIas, this](auto &protocol){return protocol.handle_collisions(cell_structure, box_geo, min_global_cut, BondedIas, this->local_collision_queue);},*m_protocol); //m_protocol->handle_collisions(cell_structure, box_geo, min_global_cut, *system.bonded_ias, local_collision_queue) if (update_propagations) { system.update_used_propagations(); - } + }} clear_queue(); } -bool Collision::MergedProtocol::handle_collisions(CellStructure &cell_structure, BoxGeometry const &box_geo, double min_global_cut, BondedInteractionsMap const &bonded_ias, std::vector &local_collision_queue) { - // Note that the glue to surface mode adds bonds between the centers - // but does so later in the process. This is needed to guarantee that - // a particle can only be glued once, even if queued twice in a single - // time step - auto const bind_centers = mode != CollisionModeType::OFF and - mode != CollisionModeType::GLUE_TO_SURF; - if (bind_centers) { +bool Collision::BindCenters::handle_collisions(CellStructure &cell_structure, BoxGeometry const &box_geo, double min_global_cut, BondedInteractionsMap const &bonded_ias, std::vector &local_collision_queue) { +{ + // Initialize a flag to track whether any collisions were handled + bool collisions_handled = false; + + for (auto &c : local_collision_queue) { + // Ensure that the bond is associated with the non-ghost particle + if (cell_structure.get_local_particle(c.pp1)->is_ghost()) { + std::swap(c.pp1, c.pp2); + } + + const int bondG[] = {c.pp2}; + + // Insert the bond for the non-ghost particle + get_part(cell_structure, c.pp1).bonds().insert({bond_centers, bondG}); + + // Mark that we have handled at least one collision + collisions_handled = true; + } + + // Return true if any collisions were handled, otherwise false + return collisions_handled; +} + +} + +bool Collision::BindVS::handle_collisions(CellStructure &cell_structure, BoxGeometry const &box_geo, double min_global_cut, BondedInteractionsMap const &bonded_ias, std::vector &local_collision_queue) { + for (auto &c : local_collision_queue) { // put the bond to the non-ghost particle; at least one partner always is if (cell_structure.get_local_particle(c.pp1)->is_ghost()) { @@ -304,11 +350,10 @@ bool Collision::MergedProtocol::handle_collisions(CellStructure &cell_structure, get_part(cell_structure, c.pp1).bonds().insert({bond_centers, bondG}); } - } + #ifdef VIRTUAL_SITES_RELATIVE - if ((mode == CollisionModeType::BIND_VS) || - (mode == CollisionModeType::GLUE_TO_SURF)) { + // Gather the global collision queue, because only one node has a collision // across node boundaries in its queue. // The other node might still have to change particle properties on its @@ -338,25 +383,13 @@ bool Collision::MergedProtocol::handle_collisions(CellStructure &cell_structure, // number of particles created by other nodes if (((!p1 or p1->is_ghost()) and (!p2 or p2->is_ghost())) or !p1 or !p2) { // Increase local counters - if (mode == CollisionModeType::BIND_VS) { - current_vs_pid++; - } - // For glue to surface, we have only one vs current_vs_pid++; - if (mode == CollisionModeType::GLUE_TO_SURF) { - if (p1 and p1->type() == part_type_to_be_glued) { - p1->type() = part_type_after_glueing; - } - if (p2 and p2->type() == part_type_to_be_glued) { - p2->type() = part_type_after_glueing; - } - } // mode glue to surface } else { // We consider the pair because one particle // is local to the node and the other is local or ghost // If we are in the two vs mode // Virtual site related to first particle in the collision - if (mode == CollisionModeType::BIND_VS) { + Utils::Vector3d pos1, pos2; // Enable rotation on the particles to which vs will be attached @@ -392,9 +425,74 @@ bool Collision::MergedProtocol::handle_collisions(CellStructure &cell_structure, bind_at_poc_create_bond_between_vs(cell_structure,bonded_ias, *this, current_vs_pid, c); - } // mode VS + } // we considered the pair + } // Loop over all collisions in the queue +#ifdef ADDITIONAL_CHECKS + if (!Utils::Mpi::all_compare(comm_cart, current_vs_pid)) { + throw std::runtime_error("Nodes disagree about current_vs_pid"); + } +#endif + + // If any node had a collision, all nodes need to resort + if (not global_collision_queue.empty()) { + cell_structure.set_resort_particles(Cells::RESORT_GLOBAL); + cell_structure.update_ghosts_and_resort_particle( + Cells::DATA_PART_PROPERTIES | Cells::DATA_PART_BONDS); + } + return true; +#endif // defined VIRTUAL_SITES_RELATIVE + return false; + +} + +bool Collision::GlueToSurf::handle_collisions(CellStructure &cell_structure, BoxGeometry const &box_geo, double min_global_cut, BondedInteractionsMap const &bonded_ias, std::vector &local_collision_queue) { + // Note that the glue to surface mode adds bonds between the centers + // but does so later in the process. This is needed to guarantee that + // a particle can only be glued once, even if queued twice in a single + // time step + +#ifdef VIRTUAL_SITES_RELATIVE + + // Gather the global collision queue, because only one node has a collision + // across node boundaries in its queue. + // The other node might still have to change particle properties on its + // non-ghost particle + auto global_collision_queue = gather_collision_queue(local_collision_queue); + + // Sync max_seen_part + auto const global_max_seen_particle = boost::mpi::all_reduce( + ::comm_cart, cell_structure.get_max_local_particle_id(), + boost::mpi::maximum()); + + int current_vs_pid = global_max_seen_particle + 1; + + // Iterate over global collision queue + for (auto &c : global_collision_queue) { + + // Get particle pointers + Particle *p1 = cell_structure.get_local_particle(c.pp1); + Particle *p2 = cell_structure.get_local_particle(c.pp2); + + // Only nodes take part in particle creation and binding + // that see both particles + + // If we cannot access both particles, both are ghosts, + // or one is ghost and one is not accessible + // we only increase the counter for the ext id to use based on the + // number of particles created by other nodes + if (((!p1 or p1->is_ghost()) and (!p2 or p2->is_ghost())) or !p1 or !p2) { + // For glue to surface, we have only one vs + current_vs_pid++; + + if (p1 and p1->type() == part_type_to_be_glued) { + p1->type() = part_type_after_glueing; + } + if (p2 and p2->type() == part_type_to_be_glued) { + p2->type() = part_type_after_glueing; + } + - if (mode == CollisionModeType::GLUE_TO_SURF) { + } else { // If particles are made inert by a type change on collision: // We skip the pair if one of the particles has already reacted // but we still increase the particle counters, as other nodes @@ -443,7 +541,7 @@ bool Collision::MergedProtocol::handle_collisions(CellStructure &cell_structure, } glue_to_surface_bind_part_to_vs(p1, p2, current_vs_pid, *this, cell_structure); - } + } // we considered the pair } // Loop over all collisions in the queue #ifdef ADDITIONAL_CHECKS @@ -459,18 +557,30 @@ bool Collision::MergedProtocol::handle_collisions(CellStructure &cell_structure, Cells::DATA_PART_PROPERTIES | Cells::DATA_PART_BONDS); } return true; - } // are we in one of the vs_based methods + #endif // defined VIRTUAL_SITES_RELATIVE - return false; + return false; } void CollisionDetection::initialize() { auto &system = get_system(); auto &bonded_ias = *system.bonded_ias; auto &nonbonded_ias = *system.nonbonded_ias; - m_protocol->initialize(bonded_ias, nonbonded_ias); + //m_protocol->initialize(bonded_ias, nonbonded_ias); + if (m_protocol){ + std::visit([&bonded_ias, &nonbonded_ias](auto &protocol){return protocol.initialize(bonded_ias,nonbonded_ias);},*m_protocol);} system.on_short_range_ia_change(); } +void CollisionDetection::set_protocol(std::shared_ptr protocol) { + m_protocol = std::move(protocol); + initialize(); +} + +void CollisionDetection::unset_protocol() { + m_protocol = nullptr; + auto &system = get_system(); + system.on_short_range_ia_change(); +} #endif // COLLISION_DETECTION diff --git a/src/core/collision.hpp b/src/core/collision.hpp index db8c13d0e98..7b426af2106 100644 --- a/src/core/collision.hpp +++ b/src/core/collision.hpp @@ -28,23 +28,8 @@ #include "system/Leaf.hpp" #include - +#include /** @brief Protocols for collision handling. */ -enum class CollisionModeType : int { - /** @brief Deactivate collision detection. */ - OFF = 0, - /** @brief Create bond between centers of colliding particles. */ - BIND_CENTERS = 1, - /** - * @brief Create a bond between the centers of the colliding particles, - * plus two virtual sites at the point of collision and bind them - * together. This prevents the particles from sliding against each - * other. Requires VIRTUAL_SITES_RELATIVE. - */ - BIND_VS = 2, - /** @brief Glue a particle to a specific spot on another particle. */ - GLUE_TO_SURF = 3, -}; /// Data type holding the info about a single collision struct CollisionPair { @@ -54,18 +39,27 @@ struct CollisionPair { class CollisionDetection : public System::Leaf { public: //private: TODO make private again! - std::shared_ptr m_protocol; - bool m_off; + std::shared_ptr m_protocol; public: - CollisionDetection():m_protocol{std::make_shared()}{} - + CollisionDetection() = default; + /** @brief Get currently active Collision protocol. */ + auto get_protocol() const {return m_protocol;} + /** @brief Set a new Collision protocol. */ + void set_protocol(std::shared_ptr protocol); + /** @brief Delete the currently active Collision protocol. */ + void unset_protocol(); /** @brief Validates parameters and creates particle types if needed. */ void initialize(); - auto is_off() const { return m_off; } + auto is_off() const { return nullptr or std::get_if(m_protocol.get()) != nullptr; } - auto cutoff() const { return m_protocol->cutoff(); } + auto cutoff() const { + if (m_protocol == nullptr) { + return INACTIVE_CUTOFF; + } + return std::visit([](auto const &protocol) { return protocol.cutoff();}, *m_protocol); + } /// Handle queued collisions void handle_collisions(CellStructure &cell_structure); @@ -75,10 +69,15 @@ class CollisionDetection : public System::Leaf { /** @brief Detect (and queue) a collision between the given particles. */ void detect_collision(Particle const &p1, Particle const &p2, double const dist_sq) { - if (m_protocol->detect_collision(p1, p2, dist_sq)) - local_collision_queue.push_back({p1.id(), p2.id()}); + if (m_protocol){ + bool collision_detected = std::visit([&p1, &p2, dist_sq](auto const &protocol) { + return protocol.detect_collision(p1, p2, dist_sq); + }, *m_protocol); + + if (collision_detected) { + local_collision_queue.push_back({p1.id(), p2.id()}); + }} } - // private: /// During force calculation, colliding particles are recorded in the queue. /// The queue is processed after force calculation, when it is safe to add diff --git a/src/core/collision/protocols.hpp b/src/core/collision/protocols.hpp index 77333c78f95..d954184464a 100644 --- a/src/core/collision/protocols.hpp +++ b/src/core/collision/protocols.hpp @@ -7,117 +7,212 @@ #include "nonbonded_interactions/nonbonded_interaction_data.hpp" #include "bonded_interactions/bonded_interaction_data.hpp" +#include struct CollisionPair; namespace Collision{ - enum class CollisionModeType : int { - /** @brief Deactivate collision detection. */ - OFF = 0, - /** @brief Create bond between centers of colliding particles. */ - BIND_CENTERS = 1, - /** - * @brief Create a bond between the centers of the colliding particles, - * plus two virtual sites at the point of collision and bind them - * together. This prevents the particles from sliding against each - * other. Requires VIRTUAL_SITES_RELATIVE. - */ - BIND_VS = 2, - /** @brief Glue a particle to a specific spot on another particle. */ - GLUE_TO_SURF = 3, -}; - -class MergedProtocol { + + class Off { + public: + Off() = default; + + auto cutoff() const { + return INACTIVE_CUTOFF; + } + + void initialize(BondedInteractionsMap &, InteractionsNonBonded &){ + return ; + } + + bool handle_collisions(CellStructure &, BoxGeometry const &, double , BondedInteractionsMap const &, std::vector &) { + // Off-specific collision handling + return false; + } + bool detect_collision(Particle const &p1, Particle const &p2, + double const dist_sq) const { + return false; + } + }; + + class BindCenters { + + public: + + double distance; + // Square of distance at which particle are bound + double distance_sq; + + /// bond type used between centers of colliding particles + int bond_centers; + + BindCenters(double distance, int bond_centers) : distance{distance}, distance_sq{distance*distance}, bond_centers{bond_centers}{} + + void initialize(BondedInteractionsMap &bonded_ias, InteractionsNonBonded &nonbonded_ias); + + auto cutoff() const { + return distance; + } + + bool handle_collisions(CellStructure &cell_structure, BoxGeometry const &box_geo, double min_global_cut, BondedInteractionsMap const &bonded_ias, std::vector &local_collision_queue); + + bool detect_collision(Particle const &p1, Particle const &p2, + double const dist_sq) const { + if (dist_sq > distance_sq) + return false; + + // Ignore virtual particles + if (p1.is_virtual() or p2.is_virtual()) + return false; + + // Check, if there's already a bond between the particles + if (pair_bond_exists_on(p1.bonds(), p2.id(), bond_centers)) + return false; + + if (pair_bond_exists_on(p2.bonds(), p1.id(), bond_centers)) + return false; + + /* If we're still here, there is no previous bond between the particles, + we have a new collision */ + + // do not create bond between ghost particles + if (p1.is_ghost() and p2.is_ghost()) { + return false; + } + return true; + } + }; + + class BindVS { public: - MergedProtocol() - : mode(CollisionModeType::OFF), distance(0.), distance_sq(0.), - bond_centers(-1), bond_vs(-1) {} - - /// collision protocol - CollisionModeType mode; - /// distance at which particles are bound - double distance; - // Square of distance at which particle are bound - double distance_sq; - - /// bond type used between centers of colliding particles - int bond_centers; - /// bond type used between virtual sites - int bond_vs; - /// particle type for virtual sites created on collision - int vs_particle_type; - - /// For mode "glue to surface": The distance from the particle which is to be - /// glued to the new virtual site - double dist_glued_part_to_vs; - /// For mode "glue to surface": The particle type being glued - int part_type_to_be_glued; - /// For mode "glue to surface": The particle type to which the virtual site is - /// attached - int part_type_to_attach_vs_to; - /// Particle type to which the newly glued particle is converted - int part_type_after_glueing; - /** Placement of virtual sites for MODE_VS. - * 0=on same particle as related to, - * 1=on collision partner, - * 0.5=in the middle between - */ - double vs_placement; - /** @brief Validates parameters and creates particle types if needed. */ - void initialize(BondedInteractionsMap &bonded_ias, InteractionsNonBonded &nonbonded_ias); - - auto cutoff() const { - if (mode != CollisionModeType::OFF) { - return distance; - } - return INACTIVE_CUTOFF; - } - - /// Handle queued collisions - bool handle_collisions(CellStructure &cell_structure, BoxGeometry const &box_geo, double min_global_cut, BondedInteractionsMap const &bonded_ias, std::vector &local_collision_queue); - - /** @brief Detect (and queue) a collision between the given particles. */ - bool detect_collision(Particle const &p1, Particle const &p2, - double const dist_sq) { - if (dist_sq > distance_sq) - return false; - - // If we are in the glue to surface mode, check that the particles - // are of the right type - if (mode == CollisionModeType::GLUE_TO_SURF) - if (!glue_to_surface_criterion(p1, p2)) - return false; - - // Ignore virtual particles - if (p1.is_virtual() or p2.is_virtual()) - return false; - - // Check, if there's already a bond between the particles - if (pair_bond_exists_on(p1.bonds(), p2.id(), bond_centers)) - return false; - - if (pair_bond_exists_on(p2.bonds(), p1.id(), bond_centers)) - return false; - - /* If we're still here, there is no previous bond between the particles, - we have a new collision */ - - // do not create bond between ghost particles - if (p1.is_ghost() and p2.is_ghost()) { - return false; - } - return true; - } - - /** @brief Check additional criteria for the glue_to_surface collision mode */ - bool glue_to_surface_criterion(Particle const &p1, Particle const &p2) const { - return (((p1.type() == part_type_to_be_glued) and + double distance; + // Square of distance at which particle are bound + double distance_sq; + + /// bond type used between centers of colliding particles + int bond_centers; + /// bond type used between virtual sites + int bond_vs; + /** Placement of virtual sites for MODE_VS. + * 0=on same particle as related to, + * 1=on collision partner, + * 0.5=in the middle between + */ + double vs_placement; + /// particle type for virtual sites created on collision + int part_type_vs; + + BindVS(double distance, int bond_centers, int bond_vs, double vs_placement, int part_type_vs) : distance{distance}, distance_sq{distance*distance}, bond_centers{bond_centers}, bond_vs{bond_vs}, vs_placement{vs_placement}, part_type_vs{part_type_vs} {} + + void initialize(BondedInteractionsMap &bonded_ias, InteractionsNonBonded &nonbonded_ias); + + auto cutoff() const { + return distance; + } + + bool handle_collisions(CellStructure &cell_structure, BoxGeometry const &box_geo, double min_global_cut, BondedInteractionsMap const &bonded_ias, std::vector &local_collision_queue); + + bool detect_collision(Particle const &p1, Particle const &p2, + double const dist_sq) const { + if (dist_sq > distance_sq) + return false; + + // Ignore virtual particles + if (p1.is_virtual() or p2.is_virtual()) + return false; + + // Check, if there's already a bond between the particles + if (pair_bond_exists_on(p1.bonds(), p2.id(), bond_centers)) + return false; + + if (pair_bond_exists_on(p2.bonds(), p1.id(), bond_centers)) + return false; + + /* If we're still here, there is no previous bond between the particles, + we have a new collision */ + + // do not create bond between ghost particles + if (p1.is_ghost() and p2.is_ghost()) { + return false; + } + return true; + } + }; + + class GlueToSurf{ + public: + double distance; + // Square of distance at which particle are bound + double distance_sq; + /// bond type used between centers of colliding particles + int bond_centers; + /// bond type used between virtual sites + int bond_vs; + /// particle type for virtual sites created on collision + int part_type_vs; + /// For mode "glue to surface": The distance from the particle which is to be + /// glued to the new virtual site + double dist_glued_part_to_vs; + /// For mode "glue to surface": The particle type being glued + int part_type_to_be_glued; + /// For mode "glue to surface": The particle type to which the virtual site is + /// attached + int part_type_to_attach_vs_to; + /// Particle type to which the newly glued particle is converted + int part_type_after_glueing; + + + GlueToSurf(double distance, int bond_centers, int bond_vs, int part_type_vs, double dist_glued_part_to_vs, int part_type_to_be_glued, int part_type_to_attach_vs_to, int part_type_after_glueing) : distance{distance}, distance_sq{distance*distance}, bond_centers{bond_centers}, bond_vs{bond_vs}, part_type_vs{part_type_vs}, dist_glued_part_to_vs{dist_glued_part_to_vs}, part_type_to_be_glued{part_type_to_be_glued}, part_type_to_attach_vs_to{part_type_to_attach_vs_to}, part_type_after_glueing{part_type_after_glueing} {} + + void initialize(BondedInteractionsMap &bonded_ias, InteractionsNonBonded &nonbonded_ias); + + auto cutoff() const { + return distance; + } + + /** @brief Check additional criteria for the glue_to_surface collision mode */ + bool glue_to_surface_criterion(Particle const &p1, Particle const &p2) const { + return (((p1.type() == part_type_to_be_glued) and (p2.type() == part_type_to_attach_vs_to)) or ((p2.type() == part_type_to_be_glued) and (p1.type() == part_type_to_attach_vs_to))); - } - -}; -} - -#endif // COLLISION_DETECTION + } + + bool handle_collisions(CellStructure &cell_structure, BoxGeometry const &box_geo, double min_global_cut, BondedInteractionsMap const &bonded_ias, std::vector &local_collision_queue); + + bool detect_collision(Particle const &p1, Particle const &p2, + double const dist_sq) const { + if (dist_sq > distance_sq) + return false; + + // If we are in the glue to surface mode, check that the particles + // are of the right type + + if (!glue_to_surface_criterion(p1, p2)) + return false; + + // Ignore virtual particles + if (p1.is_virtual() or p2.is_virtual()) + return false; + + // Check, if there's already a bond between the particles + if (pair_bond_exists_on(p1.bonds(), p2.id(), bond_centers)) + return false; + + if (pair_bond_exists_on(p2.bonds(), p1.id(), bond_centers)) + return false; + + /* If we're still here, there is no previous bond between the particles, + we have a new collision */ + + // do not create bond between ghost particles + if (p1.is_ghost() and p2.is_ghost()) { + return false; + } + return true; + } + }; + + using ActiveProtocol = std::variant; +} // namespace Collision +#endif diff --git a/src/python/espressomd/collision_detection.py b/src/python/espressomd/collision_detection.py index ac90777f507..76b86bbda5d 100644 --- a/src/python/espressomd/collision_detection.py +++ b/src/python/espressomd/collision_detection.py @@ -40,71 +40,130 @@ class CollisionDetection(ScriptInterfaceHelper): _so_name = "CollisionDetection::CollisionDetection" _so_features = ("COLLISION_DETECTION",) - # Do not allow setting of individual attributes - def __setattr__(self, *args, **kwargs): - raise Exception( - "Please set all parameters at once via collision_detection.set_params()") - def set_params(self, **kwargs): - """ - Set the parameters for the collision detection + """ + Set the parameters for the collision detection + + See :ref:`Creating bonds when particles collide` for detailed instructions. + + + Parameters + ---------- + mode : :obj:`str`, {"off", "bind_centers", "bind_at_point_of_collision", "glue_to_surface"} + Collision detection mode + + distance : :obj:`float` + Distance below which a pair of particles is considered in the + collision detection + + bond_centers : :obj:`espressomd.interactions.BondedInteraction` + Bond to add between the colliding particles + + bond_vs : :obj:`espressomd.interactions.BondedInteraction` + Bond to add between virtual sites (for modes using virtual sites) + + part_type_vs : :obj:`int` + Particle type of the virtual sites being created on collision + (virtual sites based modes) - See :ref:`Creating bonds when particles collide` for detailed instructions. + part_type_to_be_glued : :obj:`int` + particle type for ``"glue_to_surface"`` mode. See user guide. + part_type_to_attach_vs_to : :obj:`int` + particle type for ``"glue_to_surface"`` mode. See user guide. - Parameters - ---------- - mode : :obj:`str`, {"off", "bind_centers", "bind_at_point_of_collision", "glue_to_surface"} - Collision detection mode + part_type_after_glueing : :obj:`int` + particle type for ``"glue_to_surface"`` mode. See user guide. - distance : :obj:`float` - Distance below which a pair of particles is considered in the - collision detection + distance_glued_particle_to_vs : :obj:`float` + Distance for ``"glue_to_surface"`` mode. See user guide. - bond_centers : :obj:`espressomd.interactions.BondedInteraction` - Bond to add between the colliding particles + """ - bond_vs : :obj:`espressomd.interactions.BondedInteraction` - Bond to add between virtual sites (for modes using virtual sites) +@script_interface_register +class Off(ScriptInterfaceHelper): - part_type_vs : :obj:`int` - Particle type of the virtual sites being created on collision - (virtual sites based modes) + """Collision protocol resulting in ...""" + _so_name = "CollisionDetection::Off" + _so_features = ("COLLISION_DETECTION",) - part_type_to_be_glued : :obj:`int` - particle type for ``"glue_to_surface"`` mode. See user guide. +# system.collision_detection.protocol = BindCenters(distance=1., bond_id=0) - part_type_to_attach_vs_to : :obj:`int` - particle type for ``"glue_to_surface"`` mode. See user guide. +#decorator - part_type_after_glueing : :obj:`int` - particle type for ``"glue_to_surface"`` mode. See user guide. +#def my_decorator(cls): +# cls.__init__ = lambda self, arg1: self.value = arg1 +# return cls + +#@my_decorator +@script_interface_register +class BindCenters(ScriptInterfaceHelper): - distance_glued_particle_to_vs : :obj:`float` - Distance for ``"glue_to_surface"`` mode. See user guide. + """Collision protocol for bind_centers. - """ + Parameters + ---------- + distance : :obj:`float` + "desciption...". + bond_centers : :` ` + "description..". - if "mode" not in kwargs: - raise ValueError( - "Collision mode must be specified via the 'mode' argument") - self.call_method("set_params", **kwargs) + """ + _so_name = "CollisionDetection::BindCenters" + _so_features = ("COLLISION_DETECTION",) + +@script_interface_register +class BindVS(ScriptInterfaceHelper): + + """Collision protocol for bind_vs. + + Parameters + ---------- + distance : :obj:`float` + description... + bond_centers : :obj:` ` + description... + bond_vs : :obj:` ` + description... + + """ + _so_name = "CollisionDetection::BindVS" + _so_features = ("COLLISION_DETECTION",) + +@script_interface_register +class GlueToSurf(ScriptInterfaceHelper): + + """Collision protocol for glue_to_surf. + + Parameters + ---------- + distance : :obj:`float` + description... + bond_centers : :obj:`float` + description... + bond_vs : :obj:`espressomd.interactions.BondedInteraction` + Bond to add between virtual sites (for modes using virtual sites) + + part_type_vs : :obj:`int` + Particle type of the virtual sites being created on collision + (virtual sites based modes) + + part_type_to_be_glued : :obj:`int` + particle type for ``"glue_to_surface"`` mode. See user guide. + + part_type_to_attach_vs_to : :obj:`int` + particle type for ``"glue_to_surface"`` mode. See user guide. + + part_type_after_glueing : :obj:`int` + particle type for ``"glue_to_surface"`` mode. See user guide. + + distance_glued_particle_to_vs : :obj:`float` + Distance for ``"glue_to_surface"`` mode. See user guide. + + + """ + _so_name = "CollisionDetection::GlueToSurf" + _so_features = ("COLLISION_DETECTION",) - def get_parameter(self, name): - value = super().get_parameter(name) - if name in ["bond_centers", "bond_vs"]: - if value == -1: # Not defined - value = None - else: - value = self.call_method("get_bond_by_id", bond_id=value) - return value - def get_params(self): - """Returns the parameters of the collision detection as dict. - """ - params = {} - mode = super().get_parameter("mode") - for name in self.call_method("params_for_mode", mode=mode): - params[name] = self.get_parameter(name) - return params diff --git a/src/script_interface/collision_detection/BindCenters.hpp b/src/script_interface/collision_detection/BindCenters.hpp new file mode 100644 index 00000000000..ed55dc7e820 --- /dev/null +++ b/src/script_interface/collision_detection/BindCenters.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2021-2022 The ESPResSo project + * + * This file is part of ESPResSo. + * + * ESPResSo is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ESPResSo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "Protocol.hpp" + +#include "core/collision/protocols.hpp" + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include +#include + +namespace ScriptInterface { +namespace Collision { + +class BindCenters : public Protocol { + using CoreClass = ::Collision::BindCenters; + +public: + BindCenters(){ + add_parameters( + { + {"bond_centers", + [this](Variant const &v) { + std::get(*m_protocol).bond_centers = find_bond_id(v); + }, + [this]() { return get_bond_variant_by_id(std::get(*m_protocol).bond_centers); }}, + {"_bond_centers", AutoParameter::read_only, [this]() { return std::get(*m_protocol).bond_centers; }}, + {"distance", + [this](Variant const &v) { + std::get(*m_protocol).distance = get_value(v); + }, + [this]() { return std::get(*m_protocol).distance; }} + }); + } + std::shared_ptr<::Collision::ActiveProtocol> protocol() override { + return m_protocol; + } + +private: + std::shared_ptr<::Collision::ActiveProtocol> m_protocol; + +protected: + void do_initialize(VariantMap const ¶ms) override { + m_protocol = std::make_shared<::Collision::ActiveProtocol>(CoreClass( + get_value(params,"distance"), + find_bond_id(params.contains("_bond_centers") ? params.at("_bond_centers") : params.at("bond_centers")))); + } + +}; + +} // Colliion +} // namespace ScriptInterface + diff --git a/src/script_interface/collision_detection/BindVS.hpp b/src/script_interface/collision_detection/BindVS.hpp new file mode 100644 index 00000000000..8772e1a4753 --- /dev/null +++ b/src/script_interface/collision_detection/BindVS.hpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2021-2022 The ESPResSo project + * + * This file is part of ESPResSo. + * + * ESPResSo is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ESPResSo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "Protocol.hpp" + +#include "core/collision/protocols.hpp" + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include +#include + +namespace ScriptInterface { +namespace Collision { + +class BindVS : public Protocol { + using CoreClass = ::Collision::BindVS; + +public: + BindVS(){ + add_parameters( + { + {"bond_centers", + [this](Variant const &v) { + std::get(*m_protocol).bond_centers = find_bond_id(v); + }, + [this]() { return get_bond_variant_by_id(std::get(*m_protocol).bond_centers); }}, + {"distance", + [this](Variant const &v) { + std::get(*m_protocol).distance = get_value(v); + }, + [this]() { return std::get(*m_protocol).distance; }}, + {"bond_vs", + [this](Variant const &v) { + std::get(*m_protocol).bond_vs = find_bond_id(v); + }, + [this]() { return get_bond_variant_by_id(std::get(*m_protocol).bond_vs); }}, + {"vs_placement", + [this](Variant const &v) { + std::get(*m_protocol).vs_placement = get_value(v); + }, + [this]() { return std::get(*m_protocol).vs_placement; }}, + {"part_type_vs", + [this](Variant const &v) { + std::get(*m_protocol).part_type_vs = get_value(v); + }, + [this]() { return std::get(*m_protocol).part_type_vs; }}, + }); + } + std::shared_ptr<::Collision::ActiveProtocol> protocol() override { + return m_protocol; + } + +private: + std::shared_ptr<::Collision::ActiveProtocol> m_protocol; + +protected: +void do_initialize(VariantMap const ¶ms) override { + m_protocol = std::make_shared<::Collision::ActiveProtocol>(CoreClass(get_value(params,"distance"),find_bond_id(params.at("bond_centers")),find_bond_id(params.at("bond_vs")),get_value(params,"vs_placement"),get_value(params,"part_type_vs"))); + } + +}; + +} // Collision +} // namespace ScriptInterface + diff --git a/src/script_interface/collision_detection/CollisionDetection.hpp b/src/script_interface/collision_detection/CollisionDetection.hpp index 38889e4daca..8c892223de0 100644 --- a/src/script_interface/collision_detection/CollisionDetection.hpp +++ b/src/script_interface/collision_detection/CollisionDetection.hpp @@ -20,7 +20,7 @@ */ #pragma once - +#include "Protocol.hpp" #include "config/config.hpp" #ifdef COLLISION_DETECTION @@ -52,216 +52,60 @@ class CollisionDetection : public AutoParameters { std::shared_ptr<::CollisionDetection> m_handle; std::unique_ptr m_params; - std::weak_ptr m_bonded_ias; - - std::unordered_map const cd_mode_to_name = { - {Collision::CollisionModeType::OFF, "off"}, - {Collision::CollisionModeType::BIND_CENTERS, "bind_centers"}, - {Collision::CollisionModeType::BIND_VS, "bind_at_point_of_collision"}, - {Collision::CollisionModeType::GLUE_TO_SURF, "glue_to_surface"}, - }; - std::unordered_map cd_name_to_mode; - std::unordered_map> const cd_mode_to_parameters = { - {Collision::CollisionModeType::OFF, {"mode"}}, - {Collision::CollisionModeType::BIND_CENTERS, {"mode", "bond_centers", "distance"}}, - {Collision::CollisionModeType::BIND_VS, - {"mode", "bond_centers", "bond_vs", "part_type_vs", "distance", - "vs_placement"}}, - {Collision::CollisionModeType::GLUE_TO_SURF, - {"mode", "bond_centers", "bond_vs", "part_type_vs", - "part_type_to_be_glued", "part_type_to_attach_vs_to", - "part_type_after_glueing", "distance", - "distance_glued_particle_to_vs"}}, - }; - - auto find_bond_id(Variant const &v) const { - auto &system = get_system(); - if (is_type(v)) { - auto const bond_id = get_value(v); - std::optional retval = std::nullopt; - if (system.bonded_ias->contains(bond_id)) { - retval = bond_id; - } - return retval; - } - auto obj = get_value>(v); - return system.bonded_ias->find_bond_id(obj->bonded_ia()); - } + std::shared_ptr m_protocol; + std::weak_ptr m_bonded_ias; + std::weak_ptr m_so_bonded_ias; public: CollisionDetection() { - for (auto const &kv : cd_mode_to_name) { - cd_name_to_mode[kv.second] = kv.first; - } add_parameters({ - {"mode", - [this](Variant const &v) { - auto const name = get_value(v); - check_mode_name(name); - m_handle->m_protocol->mode = cd_name_to_mode.at(name); - m_handle->m_off = m_handle->m_protocol->mode == Collision::CollisionModeType::OFF; - }, - [this]() { return cd_mode_to_name.at(m_handle->m_protocol->mode); }}, - {"bond_centers", - [this](Variant const &v) { - auto const bond_id = find_bond_id(v); - if (not bond_id) { - throw std::invalid_argument("Bond in parameter 'bond_centers' was " - "not added to the system"); - } - m_handle->m_protocol->bond_centers = bond_id.value(); - }, - [this]() { return m_handle->m_protocol->bond_centers; }}, - {"bond_vs", - [this](Variant const &v) { - auto const bond_id = find_bond_id(v); - if (not bond_id) { - throw std::invalid_argument( - "Bond in parameter 'bond_vs' was not added to the system"); - } - m_handle->m_protocol->bond_vs = bond_id.value(); - }, - [this]() { return m_handle->m_protocol->bond_vs; }}, - {"distance", - [this](Variant const &v) { - m_handle->m_protocol->distance = get_value(v); - }, - [this]() { return m_handle->m_protocol->distance; }}, - {"distance_glued_particle_to_vs", - [this](Variant const &v) { - m_handle->m_protocol->dist_glued_part_to_vs = get_value(v); - }, - [this]() { return m_handle->m_protocol->dist_glued_part_to_vs; }}, - {"vs_placement", - [this](Variant const &v) { - m_handle->m_protocol->vs_placement = get_value(v); - }, - [this]() { return m_handle->m_protocol->vs_placement; }}, - {"part_type_vs", - [this](Variant const &v) { - m_handle->m_protocol->vs_particle_type = get_value(v); - }, - [this]() { return m_handle->m_protocol->vs_particle_type; }}, - {"part_type_to_be_glued", - [this](Variant const &v) { - m_handle->m_protocol->part_type_to_be_glued = get_value(v); - }, - [this]() { return m_handle->m_protocol->part_type_to_be_glued; }}, - {"part_type_to_attach_vs_to", - [this](Variant const &v) { - m_handle->m_protocol->part_type_to_attach_vs_to = get_value(v); - }, - [this]() { return m_handle->m_protocol->part_type_to_attach_vs_to; }}, - {"part_type_after_glueing", - [this](Variant const &v) { - m_handle->m_protocol->part_type_after_glueing = get_value(v); - }, - [this]() { return m_handle->m_protocol->part_type_after_glueing; }}, - }); - } - - void do_construct(VariantMap const ¶ms) override { - m_params = std::make_unique(params); - if (params.empty()) { - (*m_params)["mode"] = std::string("off"); - } else { - // Assume we are reloading from a checkpoint file. - // This else branch can be removed once issue #4483 is fixed. - m_params = std::make_unique(); - auto const name = get_value(params, "mode"); - check_mode_name(name); - for (auto const &name : - cd_mode_to_parameters.at(cd_name_to_mode.at(name))) { - (*m_params)[name] = params.at(name); + {"protocol",[this](Variant const &value){ + if (is_none(value)) { + m_protocol = nullptr; + m_handle->unset_protocol(); + return; } - (*m_params)["mode"] = params.at("mode"); - } - } - - Variant do_call_method(const std::string &name, - const VariantMap ¶ms) override { - if (name == "set_params") { - context()->parallel_try_catch([this, ¶ms]() { - auto const backup = std::make_shared<::CollisionDetection>(*m_handle); - auto &system = get_system(); - try { - // check provided parameters - check_input_parameters(params); - // set parameters - for (auto const &kv : params) { - do_set_parameter(get_value(kv.first), kv.second); - } - // sanitize parameters and calculate derived parameters - m_handle->initialize(); - return none; - } catch (...) { - // restore original parameters and re-throw exception - m_handle = system.collision_detection = backup; - m_handle->initialize(); - throw; - } - }); - } - if (name == "params_for_mode") { - auto const name = get_value(params, "mode"); - check_mode_name(name); - auto const mode = cd_name_to_mode.at(name); - return make_vector_of_variants(cd_mode_to_parameters.at(mode)); - } - if (name == "get_bond_by_id") { - if (not context()->is_head_node()) { - return {}; + auto const m_protocol_backup = m_protocol; + try { + context()->parallel_try_catch([&]() { + m_protocol = get_value>(value); + m_protocol->bind_bonded_ias(m_bonded_ias, m_so_bonded_ias); + m_handle->set_protocol(m_protocol->protocol()); + }); + } catch (...) { + m_protocol = m_protocol_backup; + if (m_protocol) { + m_handle->set_protocol(m_protocol->protocol()); + } else { + m_protocol = nullptr; + m_handle->unset_protocol(); + } + throw; } - return m_bonded_ias.lock()->call_method("get_bond", params); + }, + [this]() { + if (m_protocol) + return make_variant(m_protocol); + return make_variant(none); } - return none; - } - - void attach(std::weak_ptr bonded_ias) { - m_bonded_ias = bonded_ias; + }}); } -private: - void check_mode_name(std::string const &name) const { - if (not cd_name_to_mode.contains(name)) { - throw std::invalid_argument("Unknown collision mode '" + name + "'"); - } + void do_construct(VariantMap const ¶ms) override { + m_params = std::make_unique(params); } void on_bind_system(::System::System &system) override { m_handle = system.collision_detection; m_handle->bind_system(m_system.lock()); - if (not m_params->empty()) { - do_call_method("set_params", *m_params); - } - m_params.reset(); + m_bonded_ias = system.bonded_ias; } - void check_input_parameters(VariantMap const ¶ms) const { - auto const name = get_value(params, "mode"); - check_mode_name(name); - auto const mode = cd_name_to_mode.at(name); - auto const expected_params = cd_mode_to_parameters.at(mode); - auto const expected_param_names = - std::set{expected_params.begin(), expected_params.end()}; - std::set input_parameter_names = {}; - for (auto const &kv : params) { - auto const ¶m_name = kv.first; - if (not expected_param_names.contains(param_name)) { - // throw if parameter is unknown - std::ignore = get_parameter(param_name); - // throw if parameter is known but doesn't match the selected mode - throw std::runtime_error("Parameter '" + param_name + "' is not " + - "required for mode '" + name + "'"); - } - input_parameter_names.insert(param_name); - } - for (auto const ¶m_name : expected_param_names) { - if (not input_parameter_names.contains(param_name)) { - throw std::runtime_error("Parameter '" + param_name + "' is " + - "required for mode '" + name + "'"); - } + void attach(std::weak_ptr bonded_ias) { + m_so_bonded_ias = bonded_ias; + if (m_params){ + AutoParameters::do_construct(*m_params); + m_params.reset(); } } }; diff --git a/src/script_interface/collision_detection/GlueToSurf.hpp b/src/script_interface/collision_detection/GlueToSurf.hpp new file mode 100644 index 00000000000..01f55f42b30 --- /dev/null +++ b/src/script_interface/collision_detection/GlueToSurf.hpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2021-2022 The ESPResSo project + * + * This file is part of ESPResSo. + * + * ESPResSo is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ESPResSo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "Protocol.hpp" + +#include "core/collision/protocols.hpp" + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include +#include + +namespace ScriptInterface { +namespace Collision { + +class GlueToSurf : public Protocol { + using CoreClass = ::Collision::GlueToSurf; + +public: + GlueToSurf() { + add_parameters( + { + {"bond_centers", + [this](Variant const &v) { + std::get(*m_protocol).bond_centers = find_bond_id(v); + }, + [this]() { return get_bond_variant_by_id(std::get(*m_protocol).bond_centers); }}, + {"distance", + [this](Variant const &v) { + std::get(*m_protocol).distance = get_value(v); + }, + [this]() { return std::get(*m_protocol).distance; }}, + {"bond_vs", + [this](Variant const &v) { + std::get(*m_protocol).bond_vs = find_bond_id(v); + }, + [this]() { return get_bond_variant_by_id(std::get(*m_protocol).bond_vs); }}, + {"part_type_vs", + [this](Variant const &v) { + std::get(*m_protocol).part_type_vs = get_value(v); + }, + [this]() { return std::get(*m_protocol).part_type_vs; }}, + {"distance_glued_particle_to_vs", + [this](Variant const &v) { + std::get(*m_protocol).dist_glued_part_to_vs = get_value(v); + }, + [this]() { return std::get(*m_protocol).dist_glued_part_to_vs; }}, + {"part_type_to_be_glued", + [this](Variant const &v) { + std::get(*m_protocol).part_type_to_be_glued = get_value(v); + }, + [this]() { return std::get(*m_protocol).part_type_to_be_glued; }}, + {"part_type_to_attach_vs_to", + [this](Variant const &v) { + std::get(*m_protocol).part_type_to_attach_vs_to = get_value(v); + }, + [this]() { return std::get(*m_protocol).part_type_to_attach_vs_to; }}, + {"part_type_after_glueing", + [this](Variant const &v) { + std::get(*m_protocol).part_type_after_glueing = get_value(v); + }, + [this]() { return std::get(*m_protocol).part_type_after_glueing; }} + }); + } + std::shared_ptr<::Collision::ActiveProtocol> protocol() override { + return m_protocol; + } + +private: + std::shared_ptr<::Collision::ActiveProtocol> m_protocol; + +protected: +void do_initialize(VariantMap const ¶ms) override { + m_protocol = std::make_shared<::Collision::ActiveProtocol>(CoreClass(get_value(params,"distance"),find_bond_id(params.at("bond_centers")),find_bond_id(params.at("bond_vs")),get_value(params,"part_type_vs"),get_value(params,"distance_glued_particle_to_vs"),get_value(params,"part_type_to_be_glued"),get_value(params,"part_type_to_attach_vs_to"),get_value(params,"part_type_after_glueing"))); + } + +}; + +} // Colliion +} // namespace ScriptInterface + diff --git a/src/script_interface/collision_detection/Off.hpp b/src/script_interface/collision_detection/Off.hpp new file mode 100644 index 00000000000..6a998991bff --- /dev/null +++ b/src/script_interface/collision_detection/Off.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021-2022 The ESPResSo project + * + * This file is part of ESPResSo. + * + * ESPResSo is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ESPResSo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "core/collision.hpp" +#include "Protocol.hpp" +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include + +namespace ScriptInterface { +namespace Collision { + +class Off : public Protocol { +public: + Off() + : m_protocol{std::make_shared<::Collision::ActiveProtocol>( + ::Collision::Off())} {} + std::shared_ptr<::Collision::ActiveProtocol> protocol() override { + return m_protocol; + } + +protected: + void do_initialize(VariantMap const &) override {} + +private: + std::shared_ptr<::Collision::ActiveProtocol> m_protocol; +}; + +} // namespace Collision +} // namespace ScriptInterface + diff --git a/src/script_interface/collision_detection/Protocol.hpp b/src/script_interface/collision_detection/Protocol.hpp new file mode 100644 index 00000000000..56db13b5231 --- /dev/null +++ b/src/script_interface/collision_detection/Protocol.hpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2021-2022 The ESPResSo project + * + * This file is part of ESPResSo. + * + * ESPResSo is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ESPResSo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "script_interface/interactions/BondedInteractions.hpp" + +#include "core/collision/protocols.hpp" +#include "core/bonded_interactions/bonded_interaction_data.hpp" + +#include "script_interface/ScriptInterface.hpp" +#include "script_interface/auto_parameters/AutoParameters.hpp" + +#include + +namespace ScriptInterface { +namespace Collision { + +class Protocol : public AutoParameters { + std::weak_ptr m_bonded_ias; + std::weak_ptr m_so_bonded_ias; + std::unique_ptr m_params; + +public: + virtual std::shared_ptr<::Collision::ActiveProtocol> protocol() = 0; + void bind_bonded_ias(std::weak_ptr bonded_ias, std::weak_ptr so_bonded_ias) { + m_bonded_ias = std::move(bonded_ias); + m_so_bonded_ias = std::move(so_bonded_ias); + if (m_params) { + do_initialize(*m_params); + m_params.reset(); + } + } + + void do_construct(VariantMap const ¶ms) override { + m_params = std::make_unique(params); + } + +protected: + auto find_bond_id(Variant const &v) const { + auto bonded_ias = m_bonded_ias.lock(); + if (not bonded_ias) { + throw Exception("This protocol is not bound to a system"); + } + std::optional retval = std::nullopt; + if (is_type(v)) { + auto const bond_id = get_value(v); + if (bonded_ias->contains(bond_id)) { + retval = bond_id; + } + } + else{ + auto obj = get_value>(v); + retval = bonded_ias->find_bond_id(obj->bonded_ia()); + } + if (not retval) { + throw std::invalid_argument("Bond in parameter list was " + "not added to the system"); + } + return *retval; + } + auto get_bond_variant_by_id(int bond_id) { + auto so_bonded_ias = m_so_bonded_ias.lock(); + assert(so_bonded_ias != nullptr); + return so_bonded_ias->do_call_method("get", {{"key", bond_id}}); + } + virtual void do_initialize(VariantMap const ¶ms) = 0; +}; + +} // namespace Collision +} // namespace ScriptInterface + diff --git a/src/script_interface/collision_detection/initialize.cpp b/src/script_interface/collision_detection/initialize.cpp index daa06ed44a2..9c709f04923 100644 --- a/src/script_interface/collision_detection/initialize.cpp +++ b/src/script_interface/collision_detection/initialize.cpp @@ -19,7 +19,10 @@ #include "config/config.hpp" #include "initialize.hpp" - +#include "Off.hpp" +#include "BindCenters.hpp" +#include "BindVS.hpp" +#include "GlueToSurf.hpp" #include "CollisionDetection.hpp" namespace ScriptInterface { @@ -27,8 +30,19 @@ namespace CollisionDetection { void initialize(Utils::Factory *om) { #ifdef COLLISION_DETECTION + om->register_new( "CollisionDetection::CollisionDetection"); + om->register_new( + "CollisionDetection::Off"); + om->register_new( + "CollisionDetection::BindCenters"); + om->register_new( + "CollisionDetection::BindVS"); + om->register_new( + "CollisionDetection::GlueToSurf"); + + #endif } } /* namespace CollisionDetection */ diff --git a/src/script_interface/interactions/BondedInteractions.hpp b/src/script_interface/interactions/BondedInteractions.hpp index ac9f9788757..9bb8ebc73d6 100644 --- a/src/script_interface/interactions/BondedInteractions.hpp +++ b/src/script_interface/interactions/BondedInteractions.hpp @@ -92,7 +92,7 @@ class BondedInteractions : public BondedInteractionsBase_t { m_bonds.erase(key); } -protected: +public: Variant do_call_method(std::string const &name, VariantMap const ¶ms) override { if (name == "get_size") { diff --git a/testsuite/python/bond_breakage.py b/testsuite/python/bond_breakage.py index c0565641d8a..17be4091b19 100644 --- a/testsuite/python/bond_breakage.py +++ b/testsuite/python/bond_breakage.py @@ -274,11 +274,10 @@ def test_center_bonds(self): crit = 2**(1 / 6) * 2. - self.system.collision_detection.set_params(mode="bind_centers", - distance=2**(1 / 6) * 2.2, bond_centers=harm) + self.system.collision_detection.protocol = espressomd.collision_detection.BindCenters(distance=2**(1 / 6) * 2.2, bond_centers=harm) self.system.integrator.run(1) - self.system.collision_detection.set_params(mode="off") + self.system.collision_detection.protocol = espressomd.collision_detection.Off() self.system.bond_breakage[harm] = BreakageSpec( breakage_length=crit, action_type="delete_bond") self.system.integrator.run(1) @@ -307,12 +306,10 @@ def test_vs_bonds(self): crit = 2**(1 / 6) * 1.5 crit_vs = 2**(1 / 6) * 1 / 3 * 1.2 - self.system.collision_detection.set_params(mode="bind_at_point_of_collision", - distance=crit, bond_centers=virt, bond_vs=harm, - part_type_vs=1, vs_placement=1 / 3) + self.system.collision_detection.protocol = espressomd.collision_detection.BindVS(distance=crit, bond_centers=virt, bond_vs=harm,part_type_vs=1, vs_placement=1 / 3) self.system.integrator.run(1) - self.system.collision_detection.set_params(mode="off") + self.system.collision_detection.protocol = espressomd.collision_detection.Off() self.system.bond_breakage[harm] = BreakageSpec( breakage_length=crit_vs, action_type="revert_bind_at_point_of_collision") self.system.integrator.run(1) diff --git a/testsuite/python/checkpoint_lb_cpu__p3m_cpu/0.checkpoint b/testsuite/python/checkpoint_lb_cpu__p3m_cpu/0.checkpoint new file mode 100644 index 0000000000000000000000000000000000000000..0cfa7caa169ad9e94d5352b6d763b122660d0103 GIT binary patch literal 199777 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 literal 0 HcmV?d00001 diff --git a/testsuite/python/collision_detection.py b/testsuite/python/collision_detection.py index 1f0a2ae2411..2a11720de8c 100644 --- a/testsuite/python/collision_detection.py +++ b/testsuite/python/collision_detection.py @@ -64,7 +64,7 @@ def get_state_set_state_consistency(self): def test_bind_centers(self): system = self.system # Check that it leaves particles alone, when off - system.collision_detection.set_params(mode="off") + system.collision_detection.protocol = espressomd.collision_detection.Off() system.part.clear() p0 = system.part.add(pos=(0, 0, 0), id=0) @@ -74,10 +74,8 @@ def test_bind_centers(self): self.assertEqual(p0.bonds, ()) self.assertEqual(p1.bonds, ()) self.assertEqual(p2.bonds, ()) - # Check that it cannot be activated - system.collision_detection.set_params( - mode="bind_centers", distance=0.11, bond_centers=self.bond_center) + system.collision_detection.protocol = espressomd.collision_detection.BindCenters(distance=0.11, bond_centers=self.bond_center) self.get_state_set_state_consistency() system.integrator.run(1, recalc_forces=True) bond0 = ((system.bonded_inter[0], 1),) @@ -93,10 +91,10 @@ def test_bind_centers(self): self.assertEqual(p2.bonds, ()) # Check turning it off - system.collision_detection.set_params(mode="off") + system.collision_detection.protocol = espressomd.collision_detection.Off() self.get_state_set_state_consistency() - self.assertEqual(system.collision_detection.mode, "off") - + self.assertIsInstance(self.system.collision_detection.protocol, espressomd.collision_detection.Off) + def run_test_bind_at_point_of_collision_for_pos(self, *positions): system = self.system positions = list(positions) @@ -113,9 +111,7 @@ def run_test_bind_at_point_of_collision_for_pos(self, *positions): # 2 non-virtual + 2 virtual + one that doesn't take part expected_np = 4 * len(positions) + 1 - system.collision_detection.set_params( - mode="bind_at_point_of_collision", bond_centers=self.bond_center, - bond_vs=self.bond_vs, part_type_vs=1, vs_placement=0.4, distance=0.11) + system.collision_detection.protocol = espressomd.collision_detection.BindVS(bond_centers=self.bond_center, bond_vs=self.bond_vs, part_type_vs=1, vs_placement=0.4, distance=0.11) self.get_state_set_state_consistency() system.integrator.run(1, recalc_forces=True) self.verify_state_after_bind_at_poc(expected_np) @@ -132,7 +128,7 @@ def run_test_bind_at_point_of_collision_for_pos(self, *positions): self.verify_state_after_bind_at_poc(expected_np) def verify_state_after_bind_at_poc(self, expected_np): - if self.system.collision_detection.bond_vs == self.bond_angle_vs: + if self.system.collision_detection.protocol.bond_vs == self.bond_angle_vs: self.verify_state_after_bind_at_poc_triplet(expected_np) else: self.verify_state_after_bind_at_poc_pair(expected_np) @@ -227,7 +223,7 @@ def verify_bind_at_poc(self, p1, p2, vs1, vs2): system = self.system # Check for presence of vs # Check for bond between vs - if self.system.collision_detection.bond_vs == self.bond_angle_vs: + if self.system.collision_detection.protocol.bond_vs == self.bond_angle_vs: bond_p1 = ((self.bond_pair, p2.id), (self.bond_center, p2.id),) bond_p2 = ((self.bond_pair, p1.id), (self.bond_center, p1.id),) self.assertTrue(p1.bonds == bond_p1 or p2.bonds == bond_p2) @@ -265,7 +261,7 @@ def verify_bind_at_poc(self, p1, p2, vs1, vs2): else: dist_centers = p1.pos - p2.pos expected_pos = system.part.by_id(rel_to).pos_folded + \ - system.collision_detection.vs_placement * dist_centers + system.collision_detection.protocol.vs_placement * dist_centers dist = expected_pos - p.pos_folded dist -= np.round(dist / system.box_l) * system.box_l self.assertLess(np.linalg.norm(dist), 1E-12) @@ -311,8 +307,7 @@ def test_bind_at_point_of_collision_triplet(self): # 2 non-virtual + 2 virtual + one that doesn't take part expected_np = 4 * len(positions) + 1 - system.collision_detection.set_params( - mode="bind_at_point_of_collision", bond_centers=self.bond_center, + system.collision_detection.protocol = espressomd.collision_detection.BindVS(bond_centers=self.bond_center, bond_vs=self.bond_angle_vs, part_type_vs=1, vs_placement=0.4, distance=0.11) self.get_state_set_state_consistency() system.integrator.run(1, recalc_forces=True) @@ -355,13 +350,7 @@ def test_bind_at_point_of_collision_random(self): system.integrator.run(10) # Collision detection - system.collision_detection.set_params( - mode="bind_at_point_of_collision", - distance=0.11, - bond_centers=self.bond_center, - bond_vs=self.bond_vs, - part_type_vs=1, - vs_placement=0.4) + system.collision_detection.protocol=espressomd.collision_detection.BindVS(bond_centers=self.bond_center,bond_vs=self.bond_vs,part_type_vs=1,vs_placement=0.4,distance=0.11) self.get_state_set_state_consistency() # Integrate lj liquid @@ -436,8 +425,7 @@ def run_test_glue_to_surface_for_pos(self, *positions): # 2 non-virtual + 1 virtual + one that doesn't take part expected_np = 3 * len(positions) + 1 - system.collision_detection.set_params( - mode="glue_to_surface", distance=0.11, + system.collision_detection.protocol = espressomd.collision_detection.GlueToSurf(distance=0.11, distance_glued_particle_to_vs=0.02, bond_centers=self.bond_center, bond_vs=self.bond_vs, part_type_vs=self.part_type_vs, part_type_to_attach_vs_to=self.part_type_to_attach_vs_to, @@ -595,8 +583,7 @@ def test_glue_to_surface_random(self): system.integrator.run(10) # Collision detection - system.collision_detection.set_params( - mode="glue_to_surface", distance=0.11, + system.collision_detection.protocol = espressomd.collision_detection.GlueToSurf(distance=0.11, distance_glued_particle_to_vs=0.02, bond_centers=self.bond_center, bond_vs=self.bond_vs, part_type_vs=self.part_type_vs, part_type_to_attach_vs_to=self.part_type_to_attach_vs_to, diff --git a/testsuite/python/collision_detection_interface.py b/testsuite/python/collision_detection_interface.py index 3e2e5dbe7a9..21d55560815 100644 --- a/testsuite/python/collision_detection_interface.py +++ b/testsuite/python/collision_detection_interface.py @@ -10,7 +10,7 @@ # # ESPResSo is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# MERCHAiNTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License @@ -53,35 +53,22 @@ class CollisionDetection(ut.TestCase): "part_type_to_be_glued": 2, "part_type_after_glueing": 3 }, } + classes={"bind_centers":espressomd.collision_detection.BindCenters, + "bind_at_point_of_collision":espressomd.collision_detection.BindVS, + "glue_to_surface":espressomd.collision_detection.GlueToSurf} def tearDown(self): - self.system.collision_detection.set_params(mode="off") + self.system.collision_detection.protocol = None def test_00_interface_and_defaults(self): - # Is it off by default - self.assertEqual(self.system.collision_detection.mode, "off") - - # Make sure params cannot be set individually - with self.assertRaises(Exception): - self.system.collision_detection.mode = "bind_centers" - - # Verify exception throwing for unknown collision modes - with self.assertRaisesRegex(ValueError, "Unknown collision mode 'unknown'"): - self.system.collision_detection.set_params(mode="unknown") - with self.assertRaisesRegex(ValueError, "Collision mode must be specified via the 'mode' argument"): - self.system.collision_detection.set_params() - + self.assertIsNone(self.system.collision_detection.protocol, None) self.assertIsNone(self.system.collision_detection.call_method("none")) - # That should work - self.system.collision_detection.set_params(mode="off") - self.assertEqual(self.system.collision_detection.mode, "off") - def check_stored_parameters(self, mode, **kwargs): """ Check if collision detection stored parameters match input values. """ - parameters = self.system.collision_detection.get_params() + parameters = self.system.collision_detection.protocol.get_params() parameters_ref = self.valid_coldet_params[mode].copy() parameters_ref.update(kwargs) for key, value_ref in parameters_ref.items(): @@ -96,7 +83,7 @@ def set_coldet(self, mode, **invalid_params): """ params = self.valid_coldet_params.get(mode).copy() params.update(invalid_params) - self.system.collision_detection.set_params(mode=mode, **params) + self.system.collision_detection.protocol=self.classes[mode](**params) def test_bind_centers(self): self.set_coldet("bind_centers", distance=0.5) @@ -104,20 +91,13 @@ def test_bind_centers(self): self.set_coldet("bind_centers", distance=-2.) with self.assertRaisesRegex(ValueError, "Parameter 'distance' must be > 0"): self.set_coldet("bind_centers", distance=0.) - with self.assertRaisesRegex(ValueError, "Bond in parameter 'bond_centers' was not added to the system"): + with self.assertRaisesRegex(ValueError, "Bond in parameter list was not added to the system"): bond = espressomd.interactions.HarmonicBond(k=1., r_0=0.1) self.set_coldet("bind_centers", bond_centers=bond) with self.assertRaisesRegex(RuntimeError, "The bond type to be used for binding particle centers needs to be a pair bond"): self.set_coldet("bind_centers", bond_centers=self.bond_angle) - with self.assertRaisesRegex(RuntimeError, "Unknown parameter 'unknown'"): - self.set_coldet("bind_centers", unknown=1) - with self.assertRaisesRegex(RuntimeError, "Parameter 'part_type_vs' is not required for mode 'bind_centers'"): - self.set_coldet("bind_centers", part_type_vs=1) - with self.assertRaisesRegex(RuntimeError, "Parameter 'distance' is required for mode 'bind_centers'"): - self.system.collision_detection.set_params( - mode="bind_centers", bond_centers=self.bond_harmonic) - with self.assertRaisesRegex(Exception, "Please set all parameters at once via collision_detection.set_params"): - self.system.collision_detection.mode = "bind_at_point_of_collision" + with self.assertRaisesRegex(RuntimeError, "Parameter 'distance' is missing"): + self.system.collision_detection.protocol = espressomd.collision_detection.BindCenters(bond_centers=self.bond_harmonic) # check if original parameters have been preserved self.check_stored_parameters("bind_centers", distance=0.5) @@ -128,7 +108,7 @@ def test_bind_at_point_of_collision(self): self.set_coldet("bind_at_point_of_collision", vs_placement=-0.01) with self.assertRaisesRegex(ValueError, "Parameter 'vs_placement' must be between 0 and 1"): self.set_coldet("bind_at_point_of_collision", vs_placement=1.01) - with self.assertRaisesRegex(ValueError, "Bond in parameter 'bond_vs' was not added to the system"): + with self.assertRaisesRegex(ValueError, "Bond in parameter list was not added to the system"): bond = espressomd.interactions.HarmonicBond(k=1., r_0=0.1) self.set_coldet("bind_at_point_of_collision", bond_vs=bond) with self.assertRaisesRegex(RuntimeError, "bond type to be used for binding virtual sites needs to be a pair bond"): @@ -159,7 +139,7 @@ def test_glue_to_surface(self): self.set_coldet("glue_to_surface", part_type_after_glueing=-1) # check if original parameters have been preserved self.check_stored_parameters("glue_to_surface", distance=0.5) - + if __name__ == "__main__": ut.main() diff --git a/testsuite/python/lees_edwards.py b/testsuite/python/lees_edwards.py index 4ebdb2dafbc..99baab3976c 100644 --- a/testsuite/python/lees_edwards.py +++ b/testsuite/python/lees_edwards.py @@ -78,7 +78,7 @@ def tearDown(self): system.bonded_inter.clear() system.lees_edwards.protocol = None if espressomd.has_features("COLLISION_DETECTION"): - system.collision_detection.set_params(mode="off") + system.collision_detection.protocol = espressomd.collision_protocol.Off() def test_00_is_none_by_default(self): @@ -616,8 +616,7 @@ def test_le_colldet(self): virt = espressomd.interactions.Virtual() system.bonded_inter.add(virt) - system.collision_detection.set_params( - mode="bind_centers", distance=1., bond_centers=harm) + system.collision_detection.protocol = espressomd.collision_detection.BindCenters(distance=1., bond_centers=harm) # After two integration steps we should not have a bond, # as the collision detection uses the distant calculation @@ -634,7 +633,7 @@ def test_le_colldet(self): np.testing.assert_array_equal(len(bond_list), 1) system.part.clear() - system.collision_detection.set_params(mode="off") + system.collision_detection.protocol = espressomd.collision_detection.Off() system.time = 0 system.lees_edwards.protocol = espressomd.lees_edwards.LinearShear( diff --git a/testsuite/python/save_checkpoint.py b/testsuite/python/save_checkpoint.py index 839687b2f4b..6410eed7bde 100644 --- a/testsuite/python/save_checkpoint.py +++ b/testsuite/python/save_checkpoint.py @@ -325,8 +325,7 @@ checkpoint.register("particle_force0") checkpoint.register("particle_force1") if espressomd.has_features("COLLISION_DETECTION"): - system.collision_detection.set_params( - mode="bind_centers", distance=0.11, bond_centers=harmonic_bond) + system.collision_detection.protocol = espressomd.collision_detection.BindCenters(distance=0.11, bond_centers=harmonic_bond) particle_propagation0 = p1.propagation particle_propagation1 = p2.propagation diff --git a/testsuite/python/test_checkpoint.py b/testsuite/python/test_checkpoint.py index 6d096aca7fd..d182b0a4785 100644 --- a/testsuite/python/test_checkpoint.py +++ b/testsuite/python/test_checkpoint.py @@ -920,10 +920,10 @@ def test_comfixed(self): @utx.skipIfMissingFeatures('COLLISION_DETECTION') def test_collision_detection(self): - coldet = system.collision_detection - self.assertEqual(coldet.mode, "bind_centers") - self.assertAlmostEqual(coldet.distance, 0.11, delta=1E-9) - self.assertEqual(coldet.bond_centers, system.bonded_inter[0]) + protocol = system.collision_detection.protocol + self.assertIsInstance(protocol, espressomd.collision_detection.BindCenters) + self.assertAlmostEqual(protocol.distance, 0.11, delta=1E-9) + self.assertEqual(protocol.bond_centers, system.bonded_inter[0]) @utx.skipIfMissingFeatures('EXCLUSIONS') def test_exclusions(self):