ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
ParticleList.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 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 "ParticleList.hpp"
21#include "ParticleHandle.hpp"
22
25
27#include "core/exclusions.hpp"
30
31#include <utils/Vector.hpp>
34
35#include <boost/mpi/collectives.hpp>
36#include <boost/mpi/communicator.hpp>
37#include <boost/range/algorithm.hpp>
38
39#include <cstddef>
40#include <memory>
41#include <stdexcept>
42#include <string>
43#include <unordered_map>
44#include <utility>
45#include <vector>
46
47namespace ScriptInterface {
48namespace Particles {
49
50#ifdef EXCLUSIONS
51static void set_exclusions(ParticleHandle &p, Variant const &exclusions) {
52 p.call_method("set_exclusions", {{"p_ids", exclusions}});
53}
54#endif // EXCLUSIONS
55
56static void set_bonds(ParticleHandle &p, Variant const &bonds) {
57 auto const bond_list_flat = get_value<std::vector<std::vector<int>>>(bonds);
58 for (auto const &bond_flat : bond_list_flat) {
59 auto const bond_id = bond_flat[0];
60 auto const part_id =
61 std::vector<int>{bond_flat.begin() + 1, bond_flat.end()};
62 p.call_method("add_bond",
63 {{"bond_id", bond_id}, {"part_id", std::move(part_id)}});
64 }
65}
66
67std::string ParticleList::get_internal_state() const {
68 auto const p_ids = get_particle_ids();
69 std::vector<std::string> object_states(p_ids.size());
70
71 boost::transform(p_ids, object_states.begin(), [this](auto const p_id) {
72 auto p_obj =
73 context()->make_shared("Particles::ParticleHandle", {{"id", p_id}});
74 auto &p_handle = dynamic_cast<ParticleHandle &>(*p_obj);
75 auto const packed_state = p_handle.serialize();
76 // custom particle serialization
77 auto state = Utils::unpack<ObjectState>(packed_state);
78 state.name = "Particles::ParticleHandle";
79 auto const bonds_view = p_handle.call_method("get_bonds_view", {});
80 state.params.emplace_back(std::string{"bonds"}, pack(bonds_view));
81#ifdef EXCLUSIONS
82 auto const exclusions = p_handle.call_method("get_exclusions", {});
83 state.params.emplace_back(std::string{"exclusions"}, pack(exclusions));
84#endif // EXCLUSIONS
85 state.params.emplace_back(std::string{"__cpt_sentinel"}, pack(None{}));
86 return Utils::pack(state);
87 });
88
89 return Utils::pack(object_states);
90}
91
92void ParticleList::set_internal_state(std::string const &state) {
93 auto const object_states = Utils::unpack<std::vector<std::string>>(state);
94#ifdef EXCLUSIONS
95 std::unordered_map<int, Variant> exclusions = {};
96#endif // EXCLUSIONS
97 std::unordered_map<int, Variant> bonds = {};
98
99 for (auto const &packed_object : object_states) {
100 auto state = Utils::unpack<ObjectState>(packed_object);
101 VariantMap params = {};
102 for (auto const &kv : state.params) {
103 params[kv.first] = unpack(kv.second, {});
104 }
105 auto const p_id = get_value<int>(params.at("id"));
106 bonds[p_id] = params.extract("bonds").mapped();
107#ifdef EXCLUSIONS
108 exclusions[p_id] = params.extract("exclusions").mapped();
109#endif // EXCLUSIONS
110 context()->make_shared("Particles::ParticleHandle", params);
111 }
112
113 for (auto const p_id : get_particle_ids()) {
114 auto p_obj =
115 context()->make_shared("Particles::ParticleHandle", {{"id", p_id}});
116 auto &p_handle = dynamic_cast<ParticleHandle &>(*p_obj);
117 set_bonds(p_handle, bonds[p_id]);
118#ifdef EXCLUSIONS
119 set_exclusions(p_handle, exclusions[p_id]);
120#endif // EXCLUSIONS
121 }
122}
123
124#ifdef EXCLUSIONS
125/**
126 * @brief Use the bond topology to automatically add exclusions between
127 * particles that are up to @c n_bonds_max bonds apart in a chain.
128 */
129static void auto_exclusions(boost::mpi::communicator const &comm,
130 int const n_bonds_max) {
131 // bookkeeping of particle exclusions, with their n-bond distance
132 std::unordered_map<int, std::vector<std::pair<int, int>>> partners;
133 std::vector<int> bonded_pairs;
134
135 auto &system = ::System::get_system();
136 auto &cell_structure = *system.cell_structure;
137
138 // determine initial connectivity
139 for (auto const &p : cell_structure.local_particles()) {
140 auto const pid1 = p.id();
141 for (auto const bond : p.bonds()) {
142 if (bond.partner_ids().size() == 1) {
143 auto const pid2 = bond.partner_ids()[0];
144 if (pid1 != pid2) {
145 bonded_pairs.emplace_back(pid1);
146 bonded_pairs.emplace_back(pid2);
147 }
148 }
149 }
150 }
151
152 Utils::Mpi::gather_buffer(bonded_pairs, comm);
153
154 if (comm.rank() == 0) {
155 auto const add_partner = [&partners](int pid1, int pid2, int n_bonds) {
156 if (pid2 == pid1)
157 return;
158 for (auto const &partner : partners[pid1])
159 if (partner.first == pid2)
160 return;
161 partners[pid1].emplace_back(pid2, n_bonds);
162 };
163
164 for (auto it = bonded_pairs.begin(); it != bonded_pairs.end(); it += 2) {
165 add_partner(it[0], it[1], 1);
166 add_partner(it[1], it[0], 1);
167 }
168
169 // determine transient connectivity
170 for (int iteration = 1; iteration < n_bonds_max; iteration++) {
171 std::vector<int> pids;
172 for (auto const &kv : partners) {
173 pids.emplace_back(kv.first);
174 }
175 for (auto const pid1 : pids) {
176 // loop over partners (counter-based loops due to iterator invalidation)
177 // NOLINTNEXTLINE(modernize-loop-convert)
178 for (std::size_t i = 0u; i < partners[pid1].size(); ++i) {
179 auto const [pid2, dist21] = partners[pid1][i];
180 if (dist21 > n_bonds_max)
181 continue;
182 // loop over all partners of the partner
183 // NOLINTNEXTLINE(modernize-loop-convert)
184 for (std::size_t j = 0u; j < partners[pid2].size(); ++j) {
185 auto const [pid3, dist32] = partners[pid2][j];
186 auto const dist31 = dist32 + dist21;
187 if (dist31 > n_bonds_max)
188 continue;
189 add_partner(pid1, pid3, dist31);
190 add_partner(pid3, pid1, dist31);
191 }
192 }
193 }
194 }
195 }
196
197 boost::mpi::broadcast(comm, partners, 0);
198 for (auto const &kv : partners) {
199 auto const pid1 = kv.first;
200 auto const &partner_list = kv.second;
201 for (auto const &partner : partner_list) {
202 auto const pid2 = partner.first;
203 if (auto p1 = cell_structure.get_local_particle(pid1)) {
204 add_exclusion(*p1, pid2);
205 }
206 if (auto p2 = cell_structure.get_local_particle(pid2)) {
207 add_exclusion(*p2, pid1);
208 }
209 }
210 }
211 system.on_particle_change();
212}
213#endif // EXCLUSIONS
214
215Variant ParticleList::do_call_method(std::string const &name,
216 VariantMap const &params) {
217#ifdef EXCLUSIONS
218 if (name == "auto_exclusions") {
219 auto const distance = get_value<int>(params, "distance");
220 auto_exclusions(context()->get_comm(), distance);
221 return {};
222 }
223#endif // EXCLUSIONS
224 if (name == "get_highest_particle_id") {
226 }
227 if (name == "clear") {
229 return {};
230 }
231 if (not context()->is_head_node()) {
232 return {};
233 }
234 if (name == "get_n_part") {
235 return get_n_part();
236 }
237 if (name == "get_particle_ids") {
238 return get_particle_ids();
239 }
240 if (name == "particle_exists") {
241 return particle_exists(get_value<int>(params, "p_id"));
242 }
243 if (name == "add_particle") {
244 assert(params.count("bonds") == 0);
245 auto obj = context()->make_shared("Particles::ParticleHandle", params);
246 auto &p_handle = dynamic_cast<ParticleHandle &>(*obj);
247#ifdef EXCLUSIONS
248 if (params.count("exclusions")) {
249 set_exclusions(p_handle, params.at("exclusions"));
250 }
251#endif // EXCLUSIONS
252 return p_handle.get_parameter("id");
253 }
254 return {};
255}
256
257} // namespace Particles
258} // namespace ScriptInterface
Vector implementation and trait types for boost qvm interoperability.
float u[3]
Type to indicate no value in Variant.
std::string serialize() const
Variant call_method(const std::string &name, const VariantMap &params)
Call a method on the object.
void add_exclusion(Particle &p, int p_id)
Insert an exclusion if not already set.
static void set_bonds(ParticleHandle &p, Variant const &bonds)
static void set_exclusions(ParticleHandle &p, Variant const &exclusions)
static void auto_exclusions(boost::mpi::communicator const &comm, int const n_bonds_max)
Use the bond topology to automatically add exclusions between particles that are up to n_bonds_max bo...
PackedVariant pack(const Variant &v)
Transform a Variant to a PackedVariant.
std::unordered_map< std::string, Variant > VariantMap
Definition Variant.hpp:82
boost::make_recursive_variant< None, bool, int, std::size_t, double, std::string, ObjectRef, Utils::Vector3b, Utils::Vector3i, Utils::Vector2d, Utils::Vector3d, Utils::Vector4d, std::vector< int >, std::vector< double >, std::vector< boost::recursive_variant_ >, std::unordered_map< int, boost::recursive_variant_ >, std::unordered_map< std::string, boost::recursive_variant_ > >::type Variant
Possible types for parameters.
Definition Variant.hpp:80
Variant unpack(const PackedVariant &v, std::unordered_map< ObjectId, ObjectRef > const &objects)
Unpack a PackedVariant.
System & get_system()
void gather_buffer(std::vector< T, Allocator > &buffer, boost::mpi::communicator const &comm, int root=0)
Gather buffer with different size on each node.
std::string pack(T const &v)
Pack a serialize type into a string.
Definition pack.hpp:38
void remove_all_particles()
Remove all particles.
int get_maximal_particle_id()
Get maximal particle id.
std::vector< int > get_particle_ids()
Get all particle ids.
int get_n_part()
Get number of particles.
bool particle_exists(int p_id)
Check if particle exists.
Particles creation and deletion.
static SteepestDescentParameters params
Currently active steepest descent instance.