ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
bond_breakage.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2022-2026 The ESPResSo project
3 *
4 * This file is part of ESPResSo.
5 *
6 * ESPResSo is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * ESPResSo is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <config/config.hpp>
21
24
26#include "communication.hpp"
27#include "errorhandling.hpp"
28#include "system/System.hpp"
29
32
33#include <boost/mpi.hpp>
34#include <boost/serialization/access.hpp>
35
36#include <algorithm>
37#include <cassert>
38#include <memory>
39#include <mutex>
40#include <span>
41#include <unordered_set>
42#include <utility>
43#include <variant>
44#include <vector>
45
46namespace BondBreakage {
47
48// Variant holding any of the actions
49using Action = std::variant<DeleteBond, DeleteAngleBond, DeleteAllBonds>;
50
51// Set of actions
52using ActionSet = std::unordered_set<Action>;
53
54/** Add a particle+bond combination to the breakage queue */
55void BondBreakage::queue_breakage(int particle_id,
56 BondPartners const &bond_partners,
57 int bond_type) {
58 {
59 std::lock_guard<std::mutex> lock(queue_mtx);
60 m_queue.emplace_back(QueueEntry{particle_id, bond_partners, bond_type});
61 }
62}
63
64/** @brief Gathers combined queue from all mpi ranks */
65static auto gather_global_queue(Queue const &local_queue) {
66 Queue res = local_queue;
67 if (comm_cart.size() > 1) {
69 boost::mpi::broadcast(comm_cart, res, 0);
70 }
71 return res;
72}
73
74/** @brief Constructs the actions to take for a breakage queue entry */
75static ActionSet actions_for_breakage(CellStructure const &cell_structure,
76 QueueEntry const &e,
77 BreakageSpec const &spec) {
78 auto is_angle_bond = [](auto const &bond_partners) {
79 return bond_partners[1];
80 }; // optional for second partner engaged
81
82#ifdef ESPRESSO_VIRTUAL_SITES_RELATIVE
84 if (not is_angle_bond(e.bond_partners)) {
85 // revert bind at point of collision for pair bonds
86 // We need to find the base particles for the two virtual sites
87 // between which the bond broke.
88 auto p1 = cell_structure.get_local_particle(e.particle_id);
89 auto p2 = cell_structure.get_local_particle(*(e.bond_partners[0]));
90 if (p1 and p2) {
91 if (not p1->is_virtual() or not p2->is_virtual()) {
93 << "The REVERT_BIND_AT_POINT_OF_COLLISION bond "
94 "breakage action has to be configured for the "
95 "bond on the virtual site. Encountered a particle "
96 "that is not virtual.";
97 return {};
98 }
99
100 return {
101 // Bond between virtual sites
103 // Bond between base particles. We do not know, on which of these
104 // the bond is defined, since bonds are stored only on one partner
105 DeleteAllBonds{p1->vs_relative().to_particle_id,
106 p2->vs_relative().to_particle_id},
107 DeleteAllBonds{p2->vs_relative().to_particle_id,
108 p1->vs_relative().to_particle_id},
109 };
110 }
111 } else {
112 // revert bind at point of collision for angle bonds
113 auto vs = cell_structure.get_local_particle(e.particle_id);
114 auto p1 = cell_structure.get_local_particle(*(e.bond_partners[0]));
115 auto p2 = cell_structure.get_local_particle(*(e.bond_partners[1]));
116 if (p1 and p2) {
117 if (not vs->is_virtual()) {
119 << "The REVERT_BIND_AT_POINT_OF_COLLISION bond "
120 "breakage action has to be configured for the "
121 "bond on the virtual site. Encountered a particle "
122 "that is not virtual.";
123 return {};
124 }
125
126 return {
127 // Angle bond on the virtual site
128 DeleteAngleBond{e.particle_id, {p1->id(), p2->id()}, e.bond_type},
129 // Bond between base particles. We do not know, on which of these
130 // the bond is defined, since bonds are stored only on one partner
131 DeleteAllBonds{p1->id(), p2->id()},
132 DeleteAllBonds{p2->id(), p1->id()}};
133 }
134 }
135 }
136#endif // ESPRESSO_VIRTUAL_SITES_RELATIVE
137
138 assert(spec.action_type == ActionType::DELETE_BOND);
139 if (is_angle_bond(e.bond_partners)) {
141 {{*(e.bond_partners[0]), *(e.bond_partners[1])}},
142 e.bond_type}};
143 }
144 return {DeleteBond{e.particle_id, *(e.bond_partners[0]), e.bond_type}};
145}
146
147/**
148 * @brief Delete specific bond.
149 */
150static void remove_bond(Particle &p, BondView const &view) {
151 auto &bond_list = p.bonds();
152 auto it = std::find(bond_list.begin(), bond_list.end(), view);
153 if (it != bond_list.end()) {
154 bond_list.erase(it);
155 }
156}
157
158/**
159 * @brief Delete pair bonds to a specific partner
160 */
161static void remove_pair_bonds_to(Particle &p, int other_pid) {
162 std::vector<std::pair<int, int>> to_delete;
163 for (auto b : p.bonds()) {
164 if (b.partner_ids().size() == 1 and b.partner_ids()[0] == other_pid)
165 to_delete.emplace_back(b.bond_id(), other_pid);
166 }
167 for (auto const &b : to_delete) {
168 remove_bond(p, BondView(b.first, {&b.second, 1}));
169 }
170}
171
172// Handler for the different delete events
173class execute {
174 CellStructure &cell_structure;
175
176public:
177 explicit execute(CellStructure &cell_structure)
178 : cell_structure{cell_structure} {}
179
180 void operator()(DeleteBond const &d) const {
181 if (auto p = cell_structure.get_local_particle(d.particle_id)) {
182 remove_bond(*p, BondView(d.bond_type, {&d.bond_partner_id, 1}));
183 }
184 }
185 void operator()(DeleteAngleBond const &d) const {
186 if (auto p = cell_structure.get_local_particle(d.particle_id)) {
187 remove_bond(*p, BondView(d.bond_type, {&d.bond_partner_id[0], 2}));
188 }
189 }
190 void operator()(DeleteAllBonds const &d) const {
191 if (auto p = cell_structure.get_local_particle(d.particle_id_1)) {
193 }
194 }
195};
196
197void BondBreakage::process_queue_impl(System::System &system) {
198 auto global_queue = gather_global_queue(m_queue);
199 auto &cell_structure = *system.cell_structure;
200
201 // Construct delete actions from breakage queue
202 ActionSet actions = {};
203 for (auto const &e : global_queue) {
204 // Retrieve relevant breakage spec
205 assert(breakage_specs.contains(e.bond_type));
206 auto const &spec = breakage_specs.at(e.bond_type);
207 actions.merge(actions_for_breakage(cell_structure, e, *spec));
208 }
209
210 // Execute actions
211 for (auto const &a : actions) {
212 std::visit(execute(cell_structure), a);
213 system.on_particle_change();
214 }
215}
216
217static bool bond_handler(BondBreakage &bond_breakage, Particle &p,
218 std::span<Particle *> partners, int bond_id,
219 BoxGeometry const &box_geo) {
220 auto retval = false;
221 if (partners.size() == 1u) { // pair bonds
222 auto d = box_geo.get_mi_vector(p.pos(), partners[0]->pos()).norm();
223 retval = bond_breakage.check_and_handle_breakage(
224 p.id(), {{partners[0]->id(), std::nullopt}}, bond_id, d);
225 } else if (partners.size() == 2u) { // angle bond
226 auto d =
227 box_geo.get_mi_vector(partners[0]->pos(), partners[1]->pos()).norm();
228 retval = bond_breakage.check_and_handle_breakage(
229 p.id(), {{partners[0]->id(), partners[1]->id()}}, bond_id, d);
230 }
231 return retval;
232}
233
234void BondBreakage::execute_bond_breakage(System::System &system) {
235 system.cell_structure->update_ghosts_and_resort_particle(
236 system.get_global_ghost_flags());
237
238 // Clear the bond breakage queue
239 clear_queue();
240
241 // Create the bond kernel function (the bond handler)
242 auto bond_kernel = [&](Particle &p, int bond_id,
243 std::span<Particle *> partners) {
244 bond_handler(*this, p, partners, bond_id, *system.box_geo);
245 return false;
246 };
247
248 // Use the CellStructure::bond_loop to process bonds
249 system.cell_structure->bond_loop(bond_kernel);
250
251 // Process the bond breakage queue
252 process_queue(system);
253}
254
255} // namespace BondBreakage
execute(CellStructure &cell_structure)
void operator()(DeleteAngleBond const &d) const
void operator()(DeleteBond const &d) const
void operator()(DeleteAllBonds const &d) const
Immutable view on a bond.
Definition BondList.hpp:44
ESPRESSO_ATTR_ALWAYS_INLINE Utils::Vector3< T > get_mi_vector(Utils::Vector3< T > const &a, Utils::Vector3< T > const &b) const noexcept
Get the minimum-image vector between two coordinates.
Describes a cell structure / cell system.
Particle * get_local_particle(int id)
Get a local particle by id.
Main system class.
unsigned get_global_ghost_flags() const
Returns the ghost flags required for running pair kernels for the global state, e....
void on_particle_change()
Called every time a particle property changes.
std::shared_ptr< CellStructure > cell_structure
std::shared_ptr< BoxGeometry > box_geo
boost::mpi::communicator comm_cart
The communicator.
This file contains the errorhandling code for severe errors, like a broken bond or illegal parameter ...
#define runtimeErrorMsg()
static void remove_pair_bonds_to(Particle &p, int other_pid)
Delete pair bonds to a specific partner.
static void remove_bond(Particle &p, BondView const &view)
Delete specific bond.
static ActionSet actions_for_breakage(CellStructure const &cell_structure, QueueEntry const &e, BreakageSpec const &spec)
Constructs the actions to take for a breakage queue entry.
static auto gather_global_queue(Queue const &local_queue)
Gathers combined queue from all mpi ranks.
std::variant< DeleteBond, DeleteAngleBond, DeleteAllBonds > Action
static bool bond_handler(BondBreakage &bond_breakage, Particle &p, std::span< Particle * > partners, int bond_id, BoxGeometry const &box_geo)
std::array< std::optional< int >, 2 > BondPartners
Stores one or two bond partners for pair/angle bonds.
std::vector< QueueEntry > Queue
Record bonds broken during a time step.
std::unordered_set< Action > ActionSet
void gather_buffer(std::vector< T, Allocator > &buffer, boost::mpi::communicator const &comm, int root=0)
Gather buffer with different size on each node.
Struct holding all information for one particle.
Definition Particle.hpp:436
constexpr auto const & bonds() const
Definition Particle.hpp:473
constexpr auto const & pos() const
Definition Particle.hpp:476
constexpr auto const & id() const
Definition Particle.hpp:455