ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
com.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2025-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/**
21 * @file
22 * This file contains routine to handle virtual sites at the center of mass of
23 * a bunch of other particles (say, a molecule). Forces acting on the center of
24 * mass are distributed back onto the constituent particles.
25 * The position/velocity/mass of the virtual site at center of mass
26 * is calculated from the positions/velocities/masses of many particles.
27 *
28 * Virtual sites are like particles, but they will not be integrated.
29 * Step performed for virtual sites:
30 * - update virtual sites
31 * - calculate forces
32 * - distribute forces
33 * - move non-virtual particles
34 * - update virtual sites
35 */
36
37#include <config/config.hpp>
38
39#ifdef ESPRESSO_VIRTUAL_SITES_CENTER_OF_MASS
40
41#include "BoxGeometry.hpp"
42#include "Particle.hpp"
43#include "PropagationMode.hpp"
46#include "cells.hpp"
47#include "communication.hpp"
48
49#include <utils/Vector.hpp>
51
52#include <boost/archive/text_iarchive.hpp>
53#include <boost/archive/text_oarchive.hpp>
54#include <boost/mpi/collectives/all_gather.hpp>
55#include <boost/mpi/collectives/all_reduce.hpp>
56#include <boost/mpi/collectives/broadcast.hpp>
57#include <boost/serialization/unordered_set.hpp>
58#include <boost/serialization/utility.hpp>
59#include <boost/serialization/vector.hpp>
60
61#include <algorithm>
62#include <cassert>
63#include <functional>
64#include <optional>
65#include <ranges>
66#include <unordered_map>
67#include <unordered_set>
68#include <vector>
69
70/**
71 * @brief Center of mass information for a molecule.
72 *
73 * Hold the total mass and the mass-weighted position sum for a molecule,
74 * used in center of mass calculations and MPI communication.
75 */
76struct ComInfo {
77 /** @brief Sum of the masses of all constituent particles. */
78 double total_mass = 0.;
79 /** @brief Sum of (mass * position) for all constituent particles. */
81
83 template <class Archive>
84 void serialize(Archive &ar, unsigned int const version) {
85 ar & total_mass;
87 }
88};
89
92
93static bool is_vs_com(Particle const &p) {
95}
96
97std::optional<int> get_pid_for_vs_com(CellStructure &cell_structure,
98 int mol_id) {
99 auto constexpr parallel_execution_policy = false;
100 std::vector<int> pids;
101 cell_structure.for_each_local_particle(
102 [&](Particle const &p) {
103 if (is_vs_com(p) and p.mol_id() == mol_id) {
104 pids.emplace_back(p.id());
105 }
106 },
107 parallel_execution_policy);
109 std::optional<int> result = std::nullopt;
110 if (not pids.empty()) {
111 assert(pids.size() == 1ul);
112 result = pids.front();
113 }
114 return result;
115}
116
117/**
118 * @brief Synchronize a map across all MPI ranks.
119 *
120 * Gathers all key-value pairs from all ranks, broadcasts the full set to every
121 * rank, and reconstructs the map so all processes have the same data.
122 */
123template <typename K, typename V>
124void gather_buffer_map(std::unordered_map<K, V> &map_data) {
125 std::vector<std::pair<K, V>> flat_map{map_data.begin(), map_data.end()};
127 boost::mpi::broadcast(comm_cart, flat_map, 0);
128 map_data = {flat_map.begin(), flat_map.end()};
129}
130
132 BoxGeometry const &box_geo) {
133
134 auto constexpr parallel_execution_policy = false;
135 // Update ghost positions before computing centers of mass
137
138 // Store virtual site center of mass particles
139 // (mol_id: vs_com_id)
140 std::unordered_map<ParticleId, MoleculeId> virtual_site_id_for_mol_id;
141 // Store com information for each molecule id
142 // (mol_id: com_info)
143 std::unordered_map<MoleculeId, ComInfo> m_com_by_mol_id;
144
145 cell_structure.for_each_local_particle(
146 [&](Particle const &p) {
147 if (is_vs_com(p)) {
148 assert(not virtual_site_id_for_mol_id.contains(p.mol_id()));
149 virtual_site_id_for_mol_id[p.mol_id()] = p.id();
150 } else if (not p.is_virtual()) {
151 if (not m_com_by_mol_id.contains(p.mol_id())) {
152 m_com_by_mol_id[p.mol_id()] = ComInfo{};
153 }
154 auto const pos_unfolded =
155 box_geo.unfolded_position(p.pos(), p.image_box());
156 m_com_by_mol_id[p.mol_id()].total_mass += p.mass();
157 m_com_by_mol_id[p.mol_id()].weighted_position +=
158 p.mass() * pos_unfolded;
159 }
160 },
161 parallel_execution_policy);
162
163 // Reduction of m_com_by_mol_id across all processes
164 // get a list of all molids that need to be communicated
165 std::unordered_set<MoleculeId> local_mol_ids;
166 for (auto const &mol_id : m_com_by_mol_id | std::views::keys) {
167 local_mol_ids.insert(mol_id);
168 }
169
170 // Communicate the list of molids to all processes
171 std::vector<std::unordered_set<MoleculeId>> global_mol_ids{};
172 boost::mpi::all_gather(comm_cart, local_mol_ids, global_mol_ids);
173 std::unordered_set<MoleculeId> unique_mol_ids{};
174 for (auto const &mol_id_set : global_mol_ids) {
175 for (auto const &mol_id : mol_id_set) {
176 unique_mol_ids.insert(mol_id);
177 }
178 }
179 std::vector<MoleculeId> flattened_mol_ids{unique_mol_ids.begin(),
180 unique_mol_ids.end()};
181 std::ranges::sort(flattened_mol_ids);
182
183 // MPI-Allreduce for total mass and weighted position
184 for (auto const mol_id : flattened_mol_ids) {
185 double local_total_mass = 0.;
186 Utils::Vector3d local_weighted_position = {0., 0., 0.};
187
188 if (m_com_by_mol_id.contains(mol_id)) {
189 local_total_mass = m_com_by_mol_id[mol_id].total_mass;
190 local_weighted_position = m_com_by_mol_id[mol_id].weighted_position;
191 }
192
193 auto const total_mass =
194 boost::mpi::all_reduce(comm_cart, local_total_mass, std::plus{});
195 auto const weighted_position =
196 boost::mpi::all_reduce(comm_cart, local_weighted_position, std::plus{});
197
198 if (m_com_by_mol_id.contains(mol_id)) {
199 m_com_by_mol_id[mol_id].total_mass = total_mass;
200 m_com_by_mol_id[mol_id].weighted_position = weighted_position;
201 }
202 }
203
204 // communicate m_com_by_mol_id across all processes
205 gather_buffer_map(m_com_by_mol_id);
206
207 // communicate the virtual_site_id_for_mol_id across all processes
208 gather_buffer_map(virtual_site_id_for_mol_id);
209
210 // Iterate over all the molecules and set the position and mass of the
211 // associated virtual site com particle
212 for (auto const &[mol_id, com_info] : m_com_by_mol_id) {
213 if (not virtual_site_id_for_mol_id.contains(mol_id)) {
214 continue;
215 }
216 auto const vs_id = virtual_site_id_for_mol_id[mol_id];
217 if (auto const vs_ptr = cell_structure.get_local_particle(vs_id)) {
218 auto folded_pos = com_info.weighted_position / com_info.total_mass;
219 auto image_box = Utils::Vector3i{};
220
221 box_geo.fold_position(folded_pos, image_box);
222
223 vs_ptr->image_box() = image_box;
224 vs_ptr->mass() = com_info.total_mass;
225 vs_ptr->pos() = folded_pos;
226 }
227 }
228}
229
230// Distribute forces that have accumulated on virtual particles to the
231// associated real particles
233
234 auto constexpr parallel_execution_policy = false;
235 cell_structure.ghosts_reduce_forces();
236 cell_structure.ghosts_reset_forces();
237
238 // Store forces for virtual site com particles
239 // (vs_com_id: force)
240 std::unordered_map<ParticleId, Utils::Vector3d> force_for_vs_id;
241 // (vs_com_id: mass)
242 std::unordered_map<ParticleId, double> mass_for_vs_id;
243
244 // Store virtual site center of mass particles
245 // (mol_id: vs_com_id)
246 std::unordered_map<MoleculeId, ParticleId> virtual_site_id_for_mol_id;
247 cell_structure.for_each_local_particle(
248 [&](Particle const &p) {
249 if (is_vs_com(p)) { // get vs_com particle
250 assert(not virtual_site_id_for_mol_id.contains(p.mol_id()));
251 virtual_site_id_for_mol_id[p.mol_id()] = p.id();
252 force_for_vs_id[p.id()] = p.force();
253 mass_for_vs_id[p.id()] = p.mass();
254 }
255 },
256 parallel_execution_policy);
257
258 // communicate virtual_site_id_for_mol_id across all processes
259 gather_buffer_map(virtual_site_id_for_mol_id);
260 // communicate force_for_vs_id, namely the force acting on the virtual site
261 // com particles to all processes
262 gather_buffer_map(force_for_vs_id);
263 // communicate mass_for_vs_id, namely the mass of the virtual site com
264 // particles to all processes
265 gather_buffer_map(mass_for_vs_id);
266
267 // Iterate over all the particles in the local cells
268 cell_structure.for_each_local_particle(
269 [&](Particle &p) {
270 if (is_vs_com(p) or
271 not virtual_site_id_for_mol_id.contains(p.mol_id())) {
272 return;
273 }
274 auto const vs_id = virtual_site_id_for_mol_id.at(p.mol_id());
275 p.force() +=
276 (p.mass() / mass_for_vs_id.at(vs_id)) * force_for_vs_id.at(vs_id);
277 },
278 parallel_execution_policy);
279}
280
281#endif // ESPRESSO_VIRTUAL_SITES_CENTER_OF_MASS
Vector implementation and trait types for boost qvm interoperability.
This file contains everything related to the global cell structure / cell system.
auto unfolded_position(Utils::Vector3d const &pos, Utils::Vector3i const &image_box) const
Unfold particle coordinates to image box.
void fold_position(Utils::Vector3d &pos, Utils::Vector3i &image_box) const
Fold coordinates to primary simulation box in-place.
Describes a cell structure / cell system.
Particle * get_local_particle(int id)
Get a local particle by id.
void ghosts_update(unsigned data_parts)
Update ghost particles.
void for_each_local_particle(Callable &&f, bool parallel=true) const
Run a kernel on all local particles.
void ghosts_reset_forces()
Set forces and torques on all ghosts to zero.
void ghosts_reduce_forces()
Add forces and torques from ghost particles to real particles.
static bool is_vs_com(Particle const &p)
Definition com.cpp:93
void vs_com_back_transfer_forces_and_torques(CellStructure &cell_structure)
Definition com.cpp:232
std::optional< int > get_pid_for_vs_com(CellStructure &cell_structure, int mol_id)
Definition com.cpp:97
decltype(ParticleProperties::mol_id) MoleculeId
Definition com.cpp:91
void gather_buffer_map(std::unordered_map< K, V > &map_data)
Synchronize a map across all MPI ranks.
Definition com.cpp:124
void vs_com_update_particles(CellStructure &cell_structure, BoxGeometry const &box_geo)
Definition com.cpp:131
decltype(ParticleProperties::identity) ParticleId
Definition com.cpp:90
boost::mpi::communicator comm_cart
The communicator.
@ DATA_PART_POSITION
Particle::r.
void gather_buffer(std::vector< T, Allocator > &buffer, boost::mpi::communicator const &comm, int root=0)
Gather buffer with different size on each node.
Center of mass information for a molecule.
Definition com.cpp:76
void serialize(Archive &ar, unsigned int const version)
Definition com.cpp:84
double total_mass
Sum of the masses of all constituent particles.
Definition com.cpp:78
friend class boost::serialization::access
Definition com.cpp:82
Utils::Vector3d weighted_position
Sum of (mass * position) for all constituent particles.
Definition com.cpp:80
int identity
unique identifier for the particle.
Definition Particle.hpp:109
int mol_id
Molecule identifier.
Definition Particle.hpp:111
Struct holding all information for one particle.
Definition Particle.hpp:435
auto const & propagation() const
Definition Particle.hpp:461
auto is_virtual() const
Definition Particle.hpp:588
auto const & mass() const
Definition Particle.hpp:492
auto const & image_box() const
Definition Particle.hpp:484
auto const & mol_id() const
Definition Particle.hpp:456
auto const & pos() const
Definition Particle.hpp:471
auto const & force() const
Definition Particle.hpp:475
auto const & id() const
Definition Particle.hpp:454