Loading [MathJax]/extensions/TeX/AMSmath.js
ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages Concepts
CellStructure.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2010-2022 The ESPResSo project
3 * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010
4 * Max-Planck-Institute for Polymer Research, Theory Group
5 *
6 * This file is part of ESPResSo.
7 *
8 * ESPResSo is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * ESPResSo is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
23
28
29#include "BoxGeometry.hpp"
30#include "LocalBox.hpp"
31#include "Particle.hpp"
33#include "communication.hpp"
35#include "system/System.hpp"
36
37#include <utils/contains.hpp>
38
39#include <boost/mpi/collectives/all_reduce.hpp>
40#include <boost/variant.hpp>
41
42#include <algorithm>
43#include <cassert>
44#include <cstddef>
45#include <iterator>
46#include <memory>
47#include <optional>
48#include <set>
49#include <stdexcept>
50#include <string>
51#include <utility>
52#include <vector>
53
55 : m_decomposition{std::make_unique<AtomDecomposition>(box)} {}
56
58 auto const max_id = get_max_local_particle_id();
59
60 for (auto const &p : local_particles()) {
61 auto const id = p.id();
62
63 if (id < 0 || id > max_id) {
64 throw std::runtime_error("Particle id out of bounds.");
65 }
66
67 if (get_local_particle(id) != &p) {
68 throw std::runtime_error("Invalid local particle index entry.");
69 }
70 }
71
72 /* checks: local particle id */
73 std::size_t local_part_cnt = 0u;
74 for (int n = 0; n < get_max_local_particle_id() + 1; n++) {
75 if (get_local_particle(n) != nullptr) {
76 local_part_cnt++;
77 if (get_local_particle(n)->id() != n) {
78 throw std::runtime_error("local_particles part has corrupted id.");
79 }
80 }
81 }
82
83 if (local_part_cnt != local_particles().size()) {
84 throw std::runtime_error(
85 std::to_string(local_particles().size()) + " parts in cells but " +
86 std::to_string(local_part_cnt) + " parts in local_particles");
87 }
88}
89
91 for (auto cell : decomposition().local_cells()) {
92 for (auto const &p : cell->particles()) {
93 if (particle_to_cell(p) != cell) {
94 throw std::runtime_error("misplaced particle with id " +
95 std::to_string(p.id()));
96 }
97 }
98 }
99}
100
102 auto remove_all_bonds_to = [id](BondList &bl) {
103 for (auto it = bl.begin(); it != bl.end();) {
104 if (Utils::contains(it->partner_ids(), id)) {
105 it = bl.erase(it);
106 } else {
107 std::advance(it, 1);
108 }
109 }
110 };
111
112 for (auto c : decomposition().local_cells()) {
113 auto &parts = c->particles();
114
115 for (auto it = parts.begin(); it != parts.end();) {
116 if (it->id() == id) {
117 it = parts.erase(it);
118 update_particle_index(id, nullptr);
120 } else {
121 remove_all_bonds_to(it->bonds());
122 it++;
123 }
124 }
125 }
126}
127
129 auto const sort_cell = particle_to_cell(p);
130 if (sort_cell) {
131
132 return std::addressof(
133 append_indexed_particle(sort_cell->particles(), std::move(p)));
134 }
135
136 return {};
137}
138
140 auto const sort_cell = particle_to_cell(p);
141 /* There is always at least one cell, so if the particle
142 * does not belong to a cell on this node we can put it there. */
143 auto cell = sort_cell ? sort_cell : decomposition().local_cells()[0];
144
145 /* If the particle isn't local a global resort may be
146 * needed, otherwise a local resort if sufficient. */
148
149 return std::addressof(
150 append_indexed_particle(cell->particles(), std::move(p)));
151}
152
154 auto it = std::find_if(m_particle_index.rbegin(), m_particle_index.rend(),
155 [](const Particle *p) { return p != nullptr; });
156
157 return (it != m_particle_index.rend()) ? (*it)->id() : -1;
158}
159
161 for (auto c : decomposition().local_cells()) {
162 c->particles().clear();
163 }
164
165 m_particle_index.clear();
166}
167
168/* Map the data parts flags from cells to those used internally
169 * by the ghost communication */
170unsigned map_data_parts(unsigned data_parts) {
171 using namespace Cells;
172
173 /* clang-format off */
174 return GHOSTTRANS_NONE
175 | ((data_parts & DATA_PART_PROPERTIES) ? GHOSTTRANS_PROPRTS : 0u)
176 | ((data_parts & DATA_PART_POSITION) ? GHOSTTRANS_POSITION : 0u)
177 | ((data_parts & DATA_PART_MOMENTUM) ? GHOSTTRANS_MOMENTUM : 0u)
178 | ((data_parts & DATA_PART_FORCE) ? GHOSTTRANS_FORCE : 0u)
179#ifdef BOND_CONSTRAINT
180 | ((data_parts & DATA_PART_RATTLE) ? GHOSTTRANS_RATTLE : 0u)
181#endif
182 | ((data_parts & DATA_PART_BONDS) ? GHOSTTRANS_BONDS : 0u);
183 /* clang-format on */
184}
185
187 ghost_communicator(decomposition().exchange_ghosts_comm(),
188 *get_system().box_geo, GHOSTTRANS_PARTNUM);
189}
190void CellStructure::ghosts_update(unsigned data_parts) {
191 ghost_communicator(decomposition().exchange_ghosts_comm(),
192 *get_system().box_geo, map_data_parts(data_parts));
193}
195 ghost_communicator(decomposition().collect_ghost_force_comm(),
196 *get_system().box_geo, GHOSTTRANS_FORCE);
197}
198#ifdef BOND_CONSTRAINT
203#endif
204
205namespace {
206/**
207 * @brief Apply a @ref ParticleChange to a particle index.
208 */
211
213 cs->update_particle_index(rp.id, nullptr);
214 }
216};
217} // namespace
218
219void CellStructure::resort_particles(bool global_flag) {
220 invalidate_ghosts();
221
222 static std::vector<ParticleChange> diff;
223 diff.clear();
224
225 m_decomposition->resort(global_flag, diff);
226
227 for (auto d : diff) {
228 boost::apply_visitor(UpdateParticleIndexVisitor{this}, d);
229 }
230
231 auto const &lebc = get_system().box_geo->lees_edwards_bc();
232 m_rebuild_verlet_list = true;
233 m_le_pos_offset_at_last_resort = lebc.pos_offset;
234
235#ifdef ADDITIONAL_CHECKS
238#endif
239}
240
242 auto &system = get_system();
243 auto &local_geo = *system.local_geo;
244 auto const &box_geo = *system.box_geo;
245 set_particle_decomposition(
246 std::make_unique<AtomDecomposition>(::comm_cart, box_geo));
248 local_geo.set_cell_structure_type(m_type);
249 system.on_cell_structure_change();
250}
251
253 double range, std::optional<std::pair<int, int>> fully_connected_boundary) {
254 auto &system = get_system();
255 auto &local_geo = *system.local_geo;
256 auto const &box_geo = *system.box_geo;
257 set_particle_decomposition(std::make_unique<RegularDecomposition>(
258 ::comm_cart, range, box_geo, local_geo, fully_connected_boundary));
260 local_geo.set_cell_structure_type(m_type);
261 system.on_cell_structure_change();
262}
263
265 std::set<int> n_square_types) {
266 auto &system = get_system();
267 auto &local_geo = *system.local_geo;
268 auto const &box_geo = *system.box_geo;
269 set_particle_decomposition(std::make_unique<HybridDecomposition>(
270 ::comm_cart, cutoff_regular, m_verlet_skin,
271 [&system]() { return system.get_global_ghost_flags(); }, box_geo,
272 local_geo, n_square_types));
274 local_geo.set_cell_structure_type(m_type);
275 system.on_cell_structure_change();
276}
277
279 assert(value >= 0.);
280 m_verlet_skin = value;
281 m_verlet_skin_set = true;
282 get_system().on_verlet_skin_change();
283}
284
286 assert(not is_verlet_skin_set());
287 auto const max_cut = get_system().maximal_cutoff();
288 if (max_cut <= 0.) {
289 throw std::runtime_error(
290 "cannot automatically determine skin, please set it manually");
291 }
292 /* maximal skin that can be used without resorting is the maximal
293 * range of the cell system minus what is needed for interactions. */
294 auto const max_range = std::ranges::min(max_cutoff());
295 auto const new_skin = std::min(0.4 * max_cut, max_range - max_cut);
296 set_verlet_skin(new_skin);
297}
298
300 /* data parts that are only updated on resort */
301 auto constexpr resort_only_parts =
303
304 auto const global_resort = boost::mpi::all_reduce(
305 ::comm_cart, m_resort_particles, std::bit_or<unsigned>());
306
307 if (global_resort != Cells::RESORT_NONE) {
308 auto const do_global_resort = (global_resort & Cells::RESORT_GLOBAL) != 0;
309
310 /* Resort cell system */
311 resort_particles(do_global_resort);
312 ghosts_count();
313 ghosts_update(data_parts);
314
315 /* Add the ghost particles to the index if we don't already
316 * have them. */
317 for (auto &p : ghost_particles()) {
318 if (get_local_particle(p.id()) == nullptr) {
319 update_particle_index(p.id(), &p);
320 }
321 }
322
323 /* Particles are now sorted */
325 } else {
326 /* Communication step: ghost information */
327 ghosts_update(data_parts & ~resort_only_parts);
328 }
329}
@ NSQUARE
Atom decomposition (N-square).
@ HYBRID
Hybrid decomposition.
@ REGULAR
Regular decomposition.
unsigned map_data_parts(unsigned data_parts)
Map the data parts flags from cells to those used internally by the ghost communication.
unsigned map_data_parts(unsigned data_parts)
Map the data parts flags from cells to those used internally by the ghost communication.
Atom decomposition cell system.
Bond storage.
Definition BondList.hpp:84
virtual std::span< Cell *const > local_cells() const =0
Get pointer to local cells.
boost::mpi::communicator comm_cart
The communicator.
void ghost_communicator(GhostCommunicator const &gcr, BoxGeometry const &box_geo, unsigned int data_parts)
Do a ghost communication with the specified data parts.
Definition ghosts.cpp:442
@ GHOSTTRANS_MOMENTUM
transfer ParticleMomentum
Definition ghosts.hpp:130
@ GHOSTTRANS_RATTLE
transfer ParticleRattle
Definition ghosts.hpp:135
@ GHOSTTRANS_PARTNUM
resize the receiver particle arrays to the size of the senders
Definition ghosts.hpp:138
@ GHOSTTRANS_POSITION
transfer ParticlePosition
Definition ghosts.hpp:128
@ GHOSTTRANS_PROPRTS
transfer ParticleProperties
Definition ghosts.hpp:126
@ GHOSTTRANS_FORCE
transfer ParticleForce
Definition ghosts.hpp:132
@ GHOSTTRANS_NONE
Definition ghosts.hpp:124
@ GHOSTTRANS_BONDS
Definition ghosts.hpp:139
@ DATA_PART_PROPERTIES
Particle::p.
@ DATA_PART_BONDS
Particle::bonds.
bool contains(Range &&rng, T const &value)
Check whether a range contains a value.
Definition contains.hpp:36
Describes a cell structure / cell system.
ParticleRange ghost_particles() const
Particle * get_local_particle(int id)
Get a local particle by id.
void check_particle_sorting() const
Check that particles are in the correct cell.
void clear_resort_particles()
Set the resort level to sorted.
auto is_verlet_skin_set() const
Whether the Verlet skin is set.
ParticleDecomposition const & decomposition() const
Get the underlying particle decomposition.
void update_ghosts_and_resort_particle(unsigned data_parts)
Update ghost particles, with particle resort if needed.
Particle * add_local_particle(Particle &&p)
Add a particle.
void set_verlet_skin_heuristic()
Set the Verlet skin using a heuristic.
void set_verlet_skin(double value)
Set the Verlet skin.
void ghosts_update(unsigned data_parts)
Update ghost particles.
CellStructure(BoxGeometry const &box)
void update_particle_index(int id, Particle *p)
Update local particle index.
void ghosts_reduce_forces()
Add forces from ghost particles to real particles.
Utils::Vector3d max_range() const
Maximal pair range supported by current cell system.
void ghosts_count()
Synchronize number of ghosts.
void set_resort_particles(Cells::Resort level)
Increase the local resort level at least to level.
void remove_particle(int id)
Remove a particle.
Particle * add_particle(Particle &&p)
Add a particle.
void resort_particles(bool global_flag)
Resort particles.
void check_particle_index() const
Check that particle index is commensurate with particles.
void set_regular_decomposition(double range, std::optional< std::pair< int, int > > fully_connected_boundary)
Set the particle decomposition to RegularDecomposition.
void set_atom_decomposition()
Set the particle decomposition to AtomDecomposition.
void remove_all_particles()
Remove all particles from the cell system.
ParticleRange local_particles() const
void ghosts_reduce_rattle_correction()
Add rattle corrections from ghost particles to real particles.
void set_hybrid_decomposition(double cutoff_regular, std::set< int > n_square_types)
Set the particle decomposition to HybridDecomposition.
int get_max_local_particle_id() const
Get the maximal particle id on this node.
Utils::Vector3d max_cutoff() const
Maximal cutoff supported by current cell system.
Struct holding all information for one particle.
Definition Particle.hpp:395
auto const & id() const
Definition Particle.hpp:414