ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
core/reaction_methods/ReactionAlgorithm.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2010-2022 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
22#include "reaction_methods/ReactionAlgorithm.hpp"
23
24#include "BoxGeometry.hpp"
25#include "Observable_stat.hpp"
27#include "cells.hpp"
28#include "particle_node.hpp"
29#include "system/System.hpp"
30
31#include <utils/Vector.hpp>
32#include <utils/contains.hpp>
33
34#include <boost/mpi/collectives/all_reduce.hpp>
35#include <boost/mpi/collectives/broadcast.hpp>
36#include <boost/serialization/serialization.hpp>
37
38#include <algorithm>
39#include <cassert>
40#include <cmath>
41#include <functional>
42#include <iterator>
43#include <limits>
44#include <map>
45#include <numbers>
46#include <optional>
47#include <stdexcept>
48#include <string>
49#include <tuple>
50#include <utility>
51#include <vector>
52
53namespace boost::serialization {
54template <typename Archive, typename... Types>
55void serialize(Archive &ar, std::tuple<Types...> &tuple, const unsigned int) {
56 std::apply([&](auto &...item) { ((ar & item), ...); }, tuple);
57}
58} // namespace boost::serialization
59
60namespace ReactionMethods {
61
62/**
63 * Adds a reaction to the reaction system
64 */
66 std::shared_ptr<SingleReaction> const &new_reaction) {
67
68 // make ESPResSo count the particle numbers which take part in the reactions
69 for (int reactant_type : new_reaction->reactant_types)
70 init_type_map(reactant_type);
71 for (int product_type : new_reaction->product_types)
72 init_type_map(product_type);
73
75
76 reactions.push_back(new_reaction);
77}
78
79/**
80 * @details This method tries to keep the cell system overhead to a minimum.
81 * Event callbacks are only called once after all particles are updated,
82 * except for particle deletion (the cell structure is still reinitialized
83 * after each deletion).
84 */
86 auto &system = System::get_system();
87 auto const &old_state = get_old_system_state();
88 auto const &box_geo = *system.box_geo;
89 // restore the properties of changed and hidden particles
90 for (auto const &state : {old_state.changed, old_state.hidden}) {
91 for (auto const &[p_id, p_type] : state) {
93 if (auto p = get_local_particle(p_id)) {
94 p->type() = p_type;
95#ifdef ELECTROSTATICS
96 p->q() = charges_of_types.at(p_type);
97#endif
98 }
99 }
100 }
101 // delete created particles
102 for (auto const p_id : old_state.created) {
103 delete_particle(p_id);
104 }
105 // restore original positions and velocities
106 for (auto const &[p_id, pos, vel] : old_state.moved) {
107 if (auto p = get_local_particle(p_id)) {
108 p->v() = vel;
109 auto folded_pos = pos;
110 auto image_box = Utils::Vector3i{};
111 box_geo.fold_position(folded_pos, image_box);
112 p->pos() = folded_pos;
113 p->image_box() = image_box;
114 }
115 }
116 if (not old_state.moved.empty()) {
117 auto const &system = System::get_system();
118 auto &cell_structure = *system.cell_structure;
119 cell_structure.set_resort_particles(Cells::RESORT_GLOBAL);
120 }
121 system.on_particle_change();
123}
124
125/**
126 * Automatically sets the volume which is used by the reaction ensemble to the
127 * volume of a cuboid box.
128 */
130 auto const &box_geo = *System::get_system().box_geo;
131 volume = box_geo.volume();
132}
133
134/**
135 * Checks whether all particles exist for the provided reaction.
136 */
138 SingleReaction const &current_reaction) const {
139 bool enough_particles = true;
140 for (int i = 0; i < current_reaction.reactant_types.size(); i++) {
141 int current_number =
143 if (current_number < current_reaction.reactant_coefficients[i]) {
144 enough_particles = false;
145 break;
146 }
147 }
148 return enough_particles;
149}
150
152 ParticleChanges &bookkeeping) {
153 // create or hide particles of types with corresponding types in reaction
154 auto const n_product_types = reaction.product_types.size();
155 auto const n_reactant_types = reaction.reactant_types.size();
156 auto const get_random_p_id_of_type = [this](int type) {
157 auto const random_index = i_random(number_of_particles_with_type(type));
158 return get_random_p_id(type, random_index);
159 };
160 auto only_local_changes = true;
161 for (int i = 0; i < std::min(n_product_types, n_reactant_types); i++) {
162 auto const n_product_coef = reaction.product_coefficients[i];
163 auto const n_reactant_coef = reaction.reactant_coefficients[i];
164 // change std::min(reactant_coefficients(i),product_coefficients(i)) many
165 // particles of reactant_types(i) to product_types(i)
166 auto const old_type = reaction.reactant_types[i];
167 auto const new_type = reaction.product_types[i];
168#ifdef ELECTROSTATICS
169 if (charges_of_types.at(new_type) != charges_of_types.at(old_type)) {
170 only_local_changes = false;
171 }
172#endif
173 for (int j = 0; j < std::min(n_product_coef, n_reactant_coef); j++) {
174 auto const p_id = get_random_p_id_of_type(old_type);
175 on_particle_type_change(p_id, old_type, new_type);
176 if (auto p = get_local_particle(p_id)) {
177 p->type() = new_type;
178#ifdef ELECTROSTATICS
179 p->q() = charges_of_types.at(new_type);
180#endif
181 }
182 bookkeeping.changed.emplace_back(p_id, old_type);
183 }
184 // create product_coefficients(i)-reactant_coefficients(i) many product
185 // particles iff product_coefficients(i)-reactant_coefficients(i)>0,
186 // iff product_coefficients(i)-reactant_coefficients(i)<0, hide this number
187 // of reactant particles
188 auto const delta_n = n_product_coef - n_reactant_coef;
189 if (delta_n > 0) {
190 auto const type = reaction.product_types[i];
191 for (int j = 0; j < delta_n; j++) {
192 auto const p_id = create_particle(type);
193 check_exclusion_range(p_id, type);
194 bookkeeping.created.emplace_back(p_id);
195 }
196 only_local_changes = false;
197 } else if (delta_n < 0) {
198 auto const type = reaction.reactant_types[i];
199 for (int j = 0; j < -delta_n; j++) {
200 auto const p_id = get_random_p_id_of_type(type);
201 bookkeeping.hidden.emplace_back(p_id, type);
202 check_exclusion_range(p_id, type);
203 hide_particle(p_id, type);
204 }
205 only_local_changes = false;
206 }
207 }
208 // create or hide particles of types with noncorresponding replacement types
209 for (auto i = std::min(n_product_types, n_reactant_types);
210 i < std::max(n_product_types, n_reactant_types); i++) {
211 if (n_product_types < n_reactant_types) {
212 // hide superfluous reactant_types particles
213 auto const type = reaction.reactant_types[i];
214 for (int j = 0; j < reaction.reactant_coefficients[i]; j++) {
215 auto const p_id = get_random_p_id_of_type(type);
216 bookkeeping.hidden.emplace_back(p_id, type);
217 check_exclusion_range(p_id, type);
218 hide_particle(p_id, type);
219 }
220 } else {
221 // create additional product_types particles
222 auto const type = reaction.product_types[i];
223 for (int j = 0; j < reaction.product_coefficients[i]; j++) {
224 auto const p_id = create_particle(type);
225 check_exclusion_range(p_id, type);
226 bookkeeping.created.emplace_back(p_id);
227 }
228 }
229 }
230 // determine which fine-grained event to trigger
231 if (n_product_types == n_reactant_types and only_local_changes) {
233 } else {
235 }
236}
237
238std::unordered_map<int, int>
239ReactionAlgorithm::get_particle_numbers(SingleReaction const &reaction) const {
240 std::unordered_map<int, int> particle_numbers;
241 // reactants
242 for (int type : reaction.reactant_types) {
243 particle_numbers[type] = ::number_of_particles_with_type(type);
244 }
245 // products
246 for (int type : reaction.product_types) {
247 particle_numbers[type] = ::number_of_particles_with_type(type);
248 }
249 return particle_numbers;
250}
251
252std::optional<double>
254 auto &reaction = *reactions[reaction_id];
255 reaction.tried_moves++;
257 if (!all_reactant_particles_exist(reaction)) {
258 // make sure that no incomplete reaction is performed -> only need to
259 // consider rollback of complete reactions
260 return {};
261 }
262 auto &bookkeeping = make_new_system_state();
263 bookkeeping.reaction_id = reaction_id;
264 bookkeeping.old_particle_numbers = get_particle_numbers(reaction);
265 make_reaction_attempt(reaction, bookkeeping);
266 auto E_pot_new = std::numeric_limits<double>::max();
268 E_pot_new = calculate_potential_energy();
269 }
270 return {E_pot_new};
271}
272
274 double bf,
275 double E_pot_old,
276 double E_pot_new) {
277 auto const exponential = std::exp(-(E_pot_new - E_pot_old) / kT);
278 auto &reaction = *reactions[reaction_id];
279 reaction.accumulator_potential_energy_difference_exponential(
280 std::vector<double>{exponential});
281 if (get_random_uniform_number() >= bf) {
282 // reject trial move: restore previous state, energy is unchanged
284 return E_pot_old;
285 }
286 // accept trial move: delete hidden particles and return new system energy
287 for (auto const &[p_id, p_type] : get_old_system_state().hidden) {
288 delete_particle(p_id);
289 }
290 reaction.accepted_moves++;
292 return E_pot_new;
293}
294
295/**
296 * Hides a particle from short ranged interactions and from the electrostatic
297 * interaction. Additional hiding from interactions would need to be implemented
298 * here.
299 *
300 * Removing the particle charge and changing its type to a non existing one
301 * deactivates all interactions with other particles, as if the particle was
302 * inexistent (currently only type-based interactions are switched off, as well
303 * as the electrostatic interaction).
304 * This function does not break bonds for simple reactions, as long as there
305 * are no reactions like 2A -->B where one of the reacting A particles occurs
306 * in the polymer (think of bond breakages if the monomer in the polymer gets
307 * deleted in the reaction). This constraint is not of fundamental reason, but
308 * there would be a need for a rule for such "collision" reactions (a reaction
309 * like the one above).
310 */
311void ReactionAlgorithm::hide_particle(int p_id, int p_type) const {
313 if (auto p = get_local_particle(p_id)) {
314 p->type() = non_interacting_type;
315#ifdef ELECTROSTATICS
316 p->q() = 0.;
317#endif
318 }
319}
320
321/**
322 * Check if the inserted particle is too close to neighboring particles.
323 */
324void ReactionAlgorithm::check_exclusion_range(int p_id, int p_type) {
325
326 /* Check the exclusion radius of the inserted particle */
327 if (exclusion_radius_per_type.count(p_type) != 0) {
328 if (exclusion_radius_per_type[p_type] == 0.) {
329 return;
330 }
331 }
332
333 auto p1_ptr = get_real_particle(p_id);
334
335 std::vector<int> particle_ids;
337 auto all_ids = get_particle_ids_parallel();
338 /* remove the inserted particle id */
339 std::erase(all_ids, p_id);
340 particle_ids = all_ids;
341 } else {
342 auto &system = System::get_system();
343 system.on_observable_calc();
344 auto const local_ids =
345 get_short_range_neighbors(system, p_id, m_max_exclusion_range);
346 assert(p1_ptr == nullptr or !!local_ids);
347 if (local_ids) {
348 particle_ids = std::move(*local_ids);
349 }
350 }
351
352 if (p1_ptr != nullptr) {
353 auto &p1 = *p1_ptr;
354 auto const &system = System::get_system();
355 auto const &box_geo = *system.box_geo;
356 auto &cell_structure = *system.cell_structure;
357
358 /* Check if the inserted particle within the exclusion radius of any other
359 * particle */
360 for (auto const p2_id : particle_ids) {
361 if (auto const p2_ptr = cell_structure.get_local_particle(p2_id)) {
362 auto const &p2 = *p2_ptr;
363 double excluded_distance;
364 if (exclusion_radius_per_type.count(p_type) == 0 ||
365 exclusion_radius_per_type.count(p2.type()) == 0) {
366 excluded_distance = exclusion_range;
367 } else if (exclusion_radius_per_type[p2.type()] == 0.) {
368 continue;
369 } else {
370 excluded_distance = exclusion_radius_per_type[p_type] +
371 exclusion_radius_per_type[p2.type()];
372 }
373
374 auto const d_min = box_geo.get_mi_vector(p2.pos(), p1.pos()).norm();
375
376 if (d_min < excluded_distance) {
378 break;
379 }
380 }
381 }
382 if (m_comm.rank() != 0) {
384 }
385 } else if (m_comm.rank() == 0) {
386 m_comm.recv(boost::mpi::any_source, 1,
388 }
389 boost::mpi::broadcast(m_comm, particle_inside_exclusion_range_touched, 0);
390}
391
392/**
393 * Deletes the particle with the given p_id and stores the id if the deletion
394 * created a hole in the particle id range. This method is intended to only
395 * delete unbonded particles since bonds are coupled to ids. This is used to
396 * avoid the id range becoming excessively huge.
397 */
399 if (p_id < 0) {
400 throw std::domain_error("Invalid particle id: " + std::to_string(p_id));
401 }
402 auto const old_max_seen_id = ::get_maximal_particle_id();
403 if (p_id == old_max_seen_id) {
404 // last particle, just delete
405 remove_particle(p_id);
406 // remove all saved empty p_ids which are greater than the max_seen_particle
407 // this is needed in order to avoid the creation of holes
408 for (auto p_id_iter = m_empty_p_ids_smaller_than_max_seen_particle.begin();
410 if ((*p_id_iter) >= old_max_seen_id)
412 p_id_iter); // update iterator after container was modified
413 else
414 ++p_id_iter;
415 }
416 } else if (p_id <= old_max_seen_id) {
417 remove_particle(p_id);
419 } else {
420 throw std::runtime_error(
421 "Particle id is greater than the max seen particle id");
422 }
423}
424
425void ReactionAlgorithm::set_cyl_constraint(double center_x, double center_y,
426 double radius) {
427 auto const &box_geo = *System::get_system().box_geo;
428 if (center_x < 0. or center_x > box_geo.length()[0])
429 throw std::domain_error("center_x is outside the box");
430 if (center_y < 0. or center_y > box_geo.length()[1])
431 throw std::domain_error("center_y is outside the box");
432 if (radius < 0.)
433 throw std::domain_error("radius is invalid");
434 m_cyl_x = center_x;
435 m_cyl_y = center_y;
436 m_cyl_radius = radius;
437 m_reaction_constraint = ReactionConstraint::CYL_Z;
438}
439
441 double slab_end_z) {
442 auto const &box_geo = *System::get_system().box_geo;
443 if (slab_start_z < 0. or slab_start_z > box_geo.length()[2])
444 throw std::domain_error("slab_start_z is outside the box");
445 if (slab_end_z < 0. or slab_end_z > box_geo.length()[2])
446 throw std::domain_error("slab_end_z is outside the box");
447 if (slab_end_z < slab_start_z)
448 throw std::domain_error("slab_end_z must be >= slab_start_z");
449 m_slab_start_z = slab_start_z;
450 m_slab_end_z = slab_end_z;
451 m_reaction_constraint = ReactionConstraint::SLAB_Z;
452}
453
454/**
455 * Writes a random position inside the central box into the provided array.
456 */
458 auto const &box_geo = *System::get_system().box_geo;
459 Utils::Vector3d out_pos{};
460
461 if (m_reaction_constraint == ReactionConstraint::CYL_Z) {
462 // see http://mathworld.wolfram.com/DiskPointPicking.html
463 // for uniform disk point picking in cylinder
464 auto const random_radius =
465 m_cyl_radius * std::sqrt(m_uniform_real_distribution(m_generator));
466 auto const random_phi =
467 2. * std::numbers::pi * m_uniform_real_distribution(m_generator);
468 out_pos[0] = m_cyl_x + random_radius * cos(random_phi);
469 out_pos[1] = m_cyl_y + random_radius * sin(random_phi);
470 out_pos[2] = box_geo.length()[2] * m_uniform_real_distribution(m_generator);
471 } else if (m_reaction_constraint == ReactionConstraint::SLAB_Z) {
472 out_pos[0] = box_geo.length()[0] * m_uniform_real_distribution(m_generator);
473 out_pos[1] = box_geo.length()[1] * m_uniform_real_distribution(m_generator);
474 out_pos[2] = m_slab_start_z + (m_slab_end_z - m_slab_start_z) *
475 m_uniform_real_distribution(m_generator);
476 } else {
477 assert(m_reaction_constraint == ReactionConstraint::NONE);
478 out_pos[0] = box_geo.length()[0] * m_uniform_real_distribution(m_generator);
479 out_pos[1] = box_geo.length()[1] * m_uniform_real_distribution(m_generator);
480 out_pos[2] = box_geo.length()[2] * m_uniform_real_distribution(m_generator);
481 }
482 return out_pos;
483}
484
485/**
486 * Creates a particle at the end of the observed particle id range.
487 */
488int ReactionAlgorithm::create_particle(int p_type) {
489 int p_id;
491 auto p_id_iter = std::min_element(
494 p_id = *p_id_iter;
496 } else {
497 p_id = ::get_maximal_particle_id() + 1;
498 }
499
500 // create random velocity vector according to Maxwell-Boltzmann distribution
501 auto pos = get_random_position_in_box();
502 auto vel = get_random_velocity_vector();
503
504 ::make_new_particle(p_id, pos);
505 if (auto p = get_local_particle(p_id)) {
506 p->v() = std::sqrt(kT / p->mass()) * vel;
507 p->type() = p_type;
508#ifdef ELECTROSTATICS
509 p->q() = charges_of_types.at(p_type);
510#endif
511 }
513 return p_id;
514}
515
516void ReactionAlgorithm::displacement_mc_move(int type, int n_particles) {
517 auto &bookkeeping = make_new_system_state();
518 // draw particle ids at random without replacement
519 int p_id = -1;
520 std::vector<int> drawn_pids{p_id};
521 for (int i = 0; i < n_particles; i++) {
522 // draw a new particle id
523 while (Utils::contains(drawn_pids, p_id)) {
524 auto const random_index = i_random(number_of_particles_with_type(type));
525 p_id = get_random_p_id(type, random_index);
526 }
527 drawn_pids.emplace_back(p_id);
528 // write new position and new velocity
529 typename decltype(ParticleChanges::moved)::value_type old_state;
530 auto const new_pos = get_random_position_in_box();
531 auto vel = get_random_velocity_vector();
532 if (auto p = get_real_particle(p_id)) {
533 old_state = {p_id, p->pos(), p->v()};
534 p->v() = std::sqrt(kT / p->mass()) * vel;
535 if (m_comm.rank() != 0) {
536 m_comm.send(0, 42, old_state);
537 }
538 } else if (m_comm.rank() == 0) {
539 m_comm.recv(boost::mpi::any_source, 42, old_state);
540 }
541 boost::mpi::broadcast(m_comm, old_state, 0);
542 bookkeeping.moved.emplace_back(old_state);
543 ::set_particle_pos(p_id, new_pos);
544
545 check_exclusion_range(p_id, type);
547 break;
548 }
549 }
550}
551
553 int n_particles) {
554
555 if (type < 0) {
556 throw std::domain_error("Parameter 'type_mc' must be >= 0");
557 }
558 if (n_particles < 0) {
559 throw std::domain_error(
560 "Parameter 'particle_number_to_be_changed' must be >= 0");
561 }
562
563 if (n_particles == 0) {
564 // reject
565 return false;
566 }
567
570
571 auto const n_particles_of_type = ::number_of_particles_with_type(type);
572 if (n_particles > n_particles_of_type) {
573 // reject
574 return false;
575 }
576
577 auto const E_pot_old = calculate_potential_energy();
578 displacement_mc_move(type, n_particles);
579 auto const E_pot_new = (particle_inside_exclusion_range_touched)
580 ? std::numeric_limits<double>::max()
582
583 // Metropolis algorithm since proposal density is symmetric
584 auto const bf = std::min(1., std::exp(-(E_pot_new - E_pot_old) / kT));
585
586 // // correct for enhanced proposal of small radii by using the
587 // // Metropolis-Hastings algorithm for asymmetric proposal densities
588 // double old_radius =
589 // std::sqrt(std::pow(particle_positions[0][0]-cyl_x,2) +
590 // std::pow(particle_positions[0][1]-cyl_y,2));
591 // double new_radius =
592 // std::sqrt(std::pow(new_pos[0]-cyl_x,2)+std::pow(new_pos[1]-cyl_y,2));
593 // auto const bf = std::min(1.0,
594 // exp(-beta*(E_pot_new-E_pot_old))*new_radius/old_radius);
595
596 // Metropolis-Hastings algorithm for asymmetric proposal density
597 if (m_uniform_real_distribution(m_generator) < bf) {
598 // accept
601 return true;
602 }
603 // reject: restore original particle properties
605 return false;
606}
607
608/**
609 * Cleans the list of empty pids and searches for empty pid in the system
610 */
612 // Clean-up the list of empty pids
614
615 auto particle_ids = get_particle_ids_parallel();
616 std::sort(particle_ids.begin(), particle_ids.end());
617 auto pid1 = -1;
618 for (auto pid2 : particle_ids) {
619 for (int pid = pid1 + 1; pid < pid2; ++pid) {
621 }
622 pid1 = pid2;
623 }
624}
625
627 auto &system = System::get_system();
628 auto const obs = system.calculate_energy();
629 auto pot = obs->accumulate(-obs->kinetic[0]);
630 boost::mpi::broadcast(m_comm, pot, 0);
631 return pot;
632}
633
634Particle *ReactionAlgorithm::get_real_particle(int p_id) const {
635 assert(p_id >= 0);
636 auto const &system = System::get_system();
637 auto ptr = system.cell_structure->get_local_particle(p_id);
638 if (ptr != nullptr and ptr->is_ghost()) {
639 ptr = nullptr;
640 }
641 assert(boost::mpi::all_reduce(m_comm, static_cast<int>(ptr != nullptr),
642 std::plus<>()) == 1);
643 return ptr;
644}
645
646Particle *ReactionAlgorithm::get_local_particle(int p_id) const {
647 assert(p_id >= 0);
648 auto const &system = System::get_system();
649 auto ptr = system.cell_structure->get_local_particle(p_id);
650 assert(boost::mpi::all_reduce(
651 m_comm, static_cast<int>(ptr != nullptr and not ptr->is_ghost()),
652 std::plus<>()) == 1);
653 return ptr;
654}
655
656} // namespace ReactionMethods
Vector implementation and trait types for boost qvm interoperability.
std::optional< std::vector< int > > get_short_range_neighbors(System::System const &system, int const pid, double const distance)
Get ids of particles that are within a certain distance of another particle.
Definition cells.cpp:119
This file contains everything related to the global cell structure / cell system.
void add_reaction(std::shared_ptr< SingleReaction > const &new_reaction)
Adds a reaction to the reaction system.
double calculate_potential_energy() const
Compute the system potential energy.
void make_reaction_attempt(::ReactionMethods::SingleReaction const &reaction, ParticleChanges &bookkeeping)
Carry out a chemical reaction and save the old system state.
Utils::Vector3d get_random_position_in_box()
Writes a random position inside the central box into the provided array.
void set_slab_constraint(double slab_start_z, double slab_end_z)
int i_random(int maxint)
draws a random integer from the uniform distribution in the range [0,maxint-1]
void set_cyl_constraint(double center_x, double center_y, double radius)
void setup_bookkeeping_of_empty_pids()
Cleans the list of empty pids and searches for empty pid in the system.
void displacement_mc_move(int type, int n_particles)
Carry out displacement MC moves for particles of a given type.
void update_volume()
Automatically sets the volume which is used by the reaction ensemble to the volume of a cuboid box.
double make_reaction_mc_move_attempt(int reaction_id, double bf, double E_pot_old, double E_pot_new)
Accept or reject a reaction MC move made by create_new_trial_state based on a probability acceptance ...
void restore_old_system_state()
Restore last valid system state.
bool all_reactant_particles_exist(SingleReaction const &current_reaction) const
Checks whether all particles exist for the provided reaction.
auto & make_new_system_state()
Open new handle for system state tracking.
void clear_old_system_state()
Clear last valid system state.
void delete_particle(int p_id)
Deletes the particle with the given p_id and stores the id if the deletion created a hole in the part...
std::vector< std::shared_ptr< SingleReaction > > reactions
std::optional< double > create_new_trial_state(int reaction_id)
Carry out a reaction MC move and calculate the new potential energy.
bool make_displacement_mc_move_attempt(int type, int n_particles)
Attempt displacement MC moves for particles of a given type.
void on_particle_change()
Called every time a particle property changes.
void on_particle_local_change()
Called every time a particle local property changes.
std::shared_ptr< BoxGeometry > box_geo
This file contains the defaults for ESPResSo.
System & get_system()
bool contains(InputIt first, InputIt last, T const &value)
Check whether an iterator range contains a value.
Definition contains.hpp:36
void serialize(Archive &ar, std::tuple< T... > &pack, unsigned int const)
Serialize std::tuple.
auto constexpr new_part
auto constexpr any_type
std::vector< int > get_particle_ids_parallel()
void make_new_particle(int p_id, Utils::Vector3d const &pos)
Create a new particle and attach it to a cell.
int number_of_particles_with_type(int type)
void set_particle_pos(int p_id, Utils::Vector3d const &pos)
Move particle to a new position.
void init_type_map(int type)
int get_random_p_id(int type, int random_index_in_type_map)
Find a particle of given type and return its id.
void remove_particle(int p_id)
Remove particle with a given identity.
int get_maximal_particle_id()
Get maximal particle id.
void on_particle_type_change(int p_id, int old_type, int new_type)
Particles creation and deletion.
Struct holding all information for one particle.
Definition Particle.hpp:395
std::vector< std::tuple< int, Utils::Vector3d, Utils::Vector3d > > moved