ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
ClusterStructure.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#include "ClusterStructure.hpp"
20
21#include "BoxGeometry.hpp"
22#include "Cluster.hpp"
23#include "PartCfg.hpp"
24#include "errorhandling.hpp"
25#include "particle_node.hpp"
26#include "system/System.hpp"
27
29
30#include <algorithm>
31#include <memory>
32#include <stdexcept>
33#include <utility>
34#include <vector>
35
36namespace ClusterAnalysis {
37
39
41 clusters.clear();
42 cluster_id.clear();
43 m_cluster_identities.clear();
44}
45
47 return cluster_id.find(p.id()) != cluster_id.end();
48}
49
50// Analyze the cluster structure of the given particles
52 // clear data structs
53 clear();
54 sanity_checks();
55
56 // Iterate over pairs
58 Utils::for_each_pair(partCfg.begin(), partCfg.end(),
59 [this](const Particle &p1, const Particle &p2) {
60 this->add_pair(p1, p2);
61 });
62 merge_clusters();
63}
64
66 clear();
67 sanity_checks();
69 for (const auto &p : partCfg) {
70 for (auto const bond : p.bonds()) {
71 if (bond.partner_ids().size() == 1) {
72 add_pair(p, get_particle_data(bond.partner_ids()[0]));
73 }
74 }
75 }
76
77 merge_clusters();
78}
79
80void ClusterStructure::add_pair(const Particle &p1, const Particle &p2) {
81 // * check, if there's a neighbor
82 // * No: Then go on to the next particle
83 // * Yes: Then if
84 // * One of them belongs to a cluster, give the other one the same cluster
85 // id.
86 // * None of them belongs to a cluster: Give them both a new cluster id
87 // * Both belong to different clusters: Mark the clusters as identical
88 // * so that they can be put together later
89 if (!m_pair_criterion) {
90 runtimeErrorMsg() << "No cluster criterion defined";
91 return;
92 }
93 // If the two particles are neighbors...
94 if (m_pair_criterion->decide(p1, p2)) {
95
96 if // None belongs to a cluster
97 ((!part_of_cluster(p1)) && (!part_of_cluster(p2))) {
98 // Both particles belong to the same, new cluster
99 const int cid = get_next_free_cluster_id();
100
101 // assign the cluster_ids
102 cluster_id[p1.id()] = cid;
103 cluster_id[p2.id()] = cid;
104 } else if // p2 belongs to a cluster but p1 doesn't
105 (part_of_cluster(p2) && !part_of_cluster(p1)) {
106 // Give p1 the same cluster id as p2
107 cluster_id[p1.id()] = find_id_for(cluster_id.at(p2.id()));
108 } else if // i belongs to a cluster but j doesn't
109 (part_of_cluster(p1) && !part_of_cluster(p2)) {
110 // give p2 the cluster id from p1
111 cluster_id[p2.id()] = find_id_for(cluster_id.at(p1.id()));
112 } else if // Both belong to different clusters
113 (part_of_cluster(p1) && part_of_cluster(p2) &&
114 cluster_id.at(p1.id()) != cluster_id.at(p2.id())) {
115 // Clusters of p1 and p2 are one and the same. Add an identity to the list
116 // The higher number must be inserted as first value of the pair
117 // because the substitutions later have to be done in descending order
118 const int cid1 = find_id_for(cluster_id.at(p1.id()));
119 const int cid2 = find_id_for(cluster_id.at(p2.id()));
120 if (cid1 > cid2) {
121 m_cluster_identities[cid1] = cid2;
122 } else if (cid1 < cid2) {
123 m_cluster_identities[cid2] = cid1;
124 }
125 // else do nothing. The clusters are already noted for merging.
126 // Connected clusters will be merged later
127 }
128 // The case for both particles being in the same cluster does not need to be
129 // treated.
130 }
131}
132
133void ClusterStructure::merge_clusters() {
134 // Relabel particles according to the cluster identities map
135 // Also create empty cluster objects for the final cluster id
136
137 // Collect needed changes in a separate map, as doing the changes on the fly
138 // would screw up the iterators
139 std::vector<std::pair<int, int>> to_be_changed;
140
141 for (auto it : cluster_id) {
142 // particle id is in it.first and cluster id in it.second
143 // We change the cluster id according to the cluster identities
144 // map
145 const int cid = find_id_for(it.second);
146 // We note the list of changes here, so we don't modify the map
147 // while iterating
148 to_be_changed.emplace_back(it.first, cid);
149 // Empty cluster object
150 if (clusters.find(cid) == clusters.end()) {
151 clusters[cid] = std::make_shared<Cluster>();
152 }
153 }
154
155 // Now act on the changes marke in above iteration
156 for (auto it : to_be_changed) {
157 cluster_id[it.first] = it.second;
158 }
159
160 // Now fill the cluster objects with particle ids
161 // Iterate over particles, fill in the cluster map
162 // to each cluster particle the corresponding cluster id
163 for (auto it : cluster_id) {
164 // If this is the first particle in this cluster, instance a new cluster
165 // object
166 if (clusters.find(it.second) == clusters.end()) {
167 clusters[it.second] = std::make_shared<Cluster>();
168 }
169 clusters[it.second]->particles.push_back(it.first);
170 }
171
172 // Sort particles ids in the clusters
173 for (const auto &c : clusters) {
174 std::sort(c.second->particles.begin(), c.second->particles.end());
175 }
176}
177
178int ClusterStructure::find_id_for(int x) {
179 int tmp = x;
180 while (m_cluster_identities.find(tmp) != m_cluster_identities.end()) {
181 tmp = m_cluster_identities[tmp];
182 }
183 return tmp;
184}
185
186int ClusterStructure::get_next_free_cluster_id() {
187 // iterate over cluster_id'
188 int max_seen_cluster = 0;
189 for (auto it : cluster_id) {
190 int cid = it.second;
191 if (max_seen_cluster < cid) {
192 max_seen_cluster = cid;
193 }
194 }
195 return max_seen_cluster + 1;
196}
197
198void ClusterStructure::sanity_checks() const {
199 if (System::get_system().box_geo->type() != BoxType::CUBOID) {
200 throw std::runtime_error(
201 "Cluster analysis is not compatible with non-cuboid box types");
202 }
203}
204
205} // namespace ClusterAnalysis
std::map< int, int > cluster_id
Map between particle ids and corresponding cluster ids.
void clear()
Clear data structures.
bool part_of_cluster(const Particle &p)
Is particle p part of a cluster.
void run_for_bonded_particles()
Run cluster analysis, consider pairs of particles connected by a bonded interaction.
void run_for_all_pairs()
Run cluster analysis, consider all particle pairs.
std::map< int, std::shared_ptr< Cluster > > clusters
Map holding the individual clusters.
Particle cache on the head node.
Definition PartCfg.hpp:34
std::shared_ptr< BoxGeometry > box_geo
This file contains the errorhandling code for severe errors, like a broken bond or illegal parameter ...
#define runtimeErrorMsg()
System & get_system()
void for_each_pair(ForwardIterator first, ForwardIterator last, BinaryOp op)
Execute op for each pair of elements in [first, last) once.
const Particle & get_particle_data(int p_id)
Get particle data.
Particles creation and deletion.
Struct holding all information for one particle.
Definition Particle.hpp:393
auto const & id() const
Definition Particle.hpp:412