ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
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/range/algorithm/min_element.hpp>
41#include <boost/variant.hpp>
42
43#include <algorithm>
44#include <cassert>
45#include <cstddef>
46#include <iterator>
47#include <memory>
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 auto &system = get_system();
254 auto &local_geo = *system.local_geo;
255 auto const &box_geo = *system.box_geo;
256 set_particle_decomposition(std::make_unique<RegularDecomposition>(
257 ::comm_cart, range, box_geo, local_geo));
259 local_geo.set_cell_structure_type(m_type);
260 system.on_cell_structure_change();
261}
262
264 std::set<int> n_square_types) {
265 auto &system = get_system();
266 auto &local_geo = *system.local_geo;
267 auto const &box_geo = *system.box_geo;
268 set_particle_decomposition(std::make_unique<HybridDecomposition>(
269 ::comm_cart, cutoff_regular, m_verlet_skin,
270 [&system]() { return system.get_global_ghost_flags(); }, box_geo,
271 local_geo, n_square_types));
273 local_geo.set_cell_structure_type(m_type);
274 system.on_cell_structure_change();
275}
276
278 assert(value >= 0.);
279 m_verlet_skin = value;
280 m_verlet_skin_set = true;
281 get_system().on_verlet_skin_change();
282}
283
285 assert(not is_verlet_skin_set());
286 auto const max_cut = get_system().maximal_cutoff();
287 if (max_cut <= 0.) {
288 throw std::runtime_error(
289 "cannot automatically determine skin, please set it manually");
290 }
291 /* maximal skin that can be used without resorting is the maximal
292 * range of the cell system minus what is needed for interactions. */
293 auto const max_range = *boost::min_element(max_cutoff());
294 auto const new_skin = std::min(0.4 * max_cut, max_range - max_cut);
295 set_verlet_skin(new_skin);
296}
297
299 /* data parts that are only updated on resort */
300 auto constexpr resort_only_parts =
302
303 auto const global_resort = boost::mpi::all_reduce(
304 ::comm_cart, m_resort_particles, std::bit_or<unsigned>());
305
306 if (global_resort != Cells::RESORT_NONE) {
307 auto const do_global_resort = (global_resort & Cells::RESORT_GLOBAL) != 0;
308
309 /* Resort cell system */
310 resort_particles(do_global_resort);
311 ghosts_count();
312 ghosts_update(data_parts);
313
314 /* Add the ghost particles to the index if we don't already
315 * have them. */
316 for (auto &p : ghost_particles()) {
317 if (get_local_particle(p.id()) == nullptr) {
318 update_particle_index(p.id(), &p);
319 }
320 }
321
322 /* Particles are now sorted */
324 } else {
325 /* Communication step: ghost information */
326 ghosts_update(data_parts & ~resort_only_parts);
327 }
328}
@ 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.
float u[3]
Atom decomposition cell system.
Bond storage.
Definition BondList.hpp:85
virtual Utils::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:440
@ 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(InputIt first, InputIt last, T const &value)
Check whether an iterator 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 set_regular_decomposition(double range)
Set the particle decomposition to RegularDecomposition.
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_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:393
auto const & id() const
Definition Particle.hpp:412