Skip to content

Commit 3efc1af

Browse files
Merge branch 'python' into globals
2 parents 7d1d73c + 5fb7a80 commit 3efc1af

File tree

10 files changed

+388
-78
lines changed

10 files changed

+388
-78
lines changed

src/core/cell_system/CellStructure.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include <cstddef>
4545
#include <iterator>
4646
#include <memory>
47+
#include <optional>
4748
#include <set>
4849
#include <stdexcept>
4950
#include <string>
@@ -248,12 +249,13 @@ void CellStructure::set_atom_decomposition() {
248249
system.on_cell_structure_change();
249250
}
250251

251-
void CellStructure::set_regular_decomposition(double range) {
252+
void CellStructure::set_regular_decomposition(
253+
double range, std::optional<std::pair<int, int>> fully_connected_boundary) {
252254
auto &system = get_system();
253255
auto &local_geo = *system.local_geo;
254256
auto const &box_geo = *system.box_geo;
255257
set_particle_decomposition(std::make_unique<RegularDecomposition>(
256-
::comm_cart, range, box_geo, local_geo));
258+
::comm_cart, range, box_geo, local_geo, fully_connected_boundary));
257259
m_type = CellStructureType::REGULAR;
258260
local_geo.set_cell_structure_type(m_type);
259261
system.on_cell_structure_change();

src/core/cell_system/CellStructure.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include <cassert>
4747
#include <iterator>
4848
#include <memory>
49+
#include <optional>
4950
#include <set>
5051
#include <span>
5152
#include <stdexcept>
@@ -557,7 +558,9 @@ struct CellStructure : public System::Leaf<CellStructure> {
557558
*
558559
* @param range Interaction range.
559560
*/
560-
void set_regular_decomposition(double range);
561+
void set_regular_decomposition(
562+
double range,
563+
std::optional<std::pair<int, int>> fully_connected_boundary);
561564

562565
/**
563566
* @brief Set the particle decomposition to @ref HybridDecomposition.

src/core/cell_system/HybridDecomposition.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include <cstddef>
3838
#include <functional>
3939
#include <iterator>
40+
#include <optional>
4041
#include <set>
4142
#include <utility>
4243

@@ -48,7 +49,7 @@ HybridDecomposition::HybridDecomposition(boost::mpi::communicator comm,
4849
std::set<int> n_square_types)
4950
: m_comm(std::move(comm)), m_box(box_geo), m_cutoff_regular(cutoff_regular),
5051
m_regular_decomposition(RegularDecomposition(
51-
m_comm, cutoff_regular + skin, m_box, local_box)),
52+
m_comm, cutoff_regular + skin, m_box, local_box, std::nullopt)),
5253
m_n_square(AtomDecomposition(m_comm, m_box)),
5354
m_n_square_types(std::move(n_square_types)),
5455
m_get_global_ghost_flags(std::move(get_ghost_flags)) {

src/core/cell_system/RegularDecomposition.cpp

Lines changed: 58 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,27 @@ void RegularDecomposition::init_cell_interactions() {
397397
auto const &node_grid = ::communicator.node_grid;
398398
auto const global_halo_offset = hadamard_product(node_pos, cell_grid) - halo;
399399
auto const global_size = hadamard_product(node_grid, cell_grid);
400+
auto const at_boundary = [&global_size](int coord, Utils::Vector3i cell_idx) {
401+
return (cell_idx[coord] == 0 or cell_idx[coord] == global_size[coord]);
402+
};
403+
404+
// For the fully connected feature (cells that don't share at least a corner)
405+
// only apply if one cell is a ghost cell (i.e. connections across the
406+
// periodic boundary.
407+
auto const fcb_is_inner_connection = [&global_size, this](Utils::Vector3i a,
408+
Utils::Vector3i b) {
409+
if (fully_connected_boundary()) {
410+
auto const [fc_normal, fc_dir] = *fully_connected_boundary();
411+
auto const involves_ghost_cell =
412+
(a[fc_normal] == -1 or a[fc_normal] == global_size[fc_normal] or
413+
b[fc_normal] == -1 or b[fc_normal] == global_size[fc_normal]);
414+
if (not involves_ghost_cell) {
415+
// check if cells do not share at least a corner
416+
return std::abs((a - b)[fc_dir]) > 1;
417+
}
418+
}
419+
return false;
420+
};
400421

401422
/* Translate a node local index (relative to the origin of the local grid)
402423
* to a global index. */
@@ -418,6 +439,19 @@ void RegularDecomposition::init_cell_interactions() {
418439
return (global_index - global_halo_offset);
419440
};
420441

442+
// sanity checks
443+
if (fully_connected_boundary()) {
444+
auto const [fc_normal, fc_dir] = *fully_connected_boundary();
445+
if (fc_normal == fc_dir) {
446+
throw std::domain_error("fully_connected_boundary normal and connection "
447+
"coordinates need to differ.");
448+
}
449+
if (node_grid[fc_dir] != 1) {
450+
throw std::runtime_error(
451+
"The MPI nodegrid must be 1 in the fully connected direction.");
452+
}
453+
}
454+
421455
/* We only consider local cells (e.g. not halo cells), which
422456
* span the range [(1,1,1), cell_grid) in local coordinates. */
423457
auto const start = global_index(Utils::Vector3i{1, 1, 1});
@@ -431,20 +465,18 @@ void RegularDecomposition::init_cell_interactions() {
431465
Utils::Vector3i lower_index = {m - 1, n - 1, o - 1};
432466
Utils::Vector3i upper_index = {m + 1, n + 1, o + 1};
433467

434-
// /* In the fully connected case, we consider all cells
435-
// * in the direction as neighbors, not only the nearest ones.
468+
/* In the fully connected case, we consider all cells
469+
* in the direction as neighbors, not only the nearest ones.
436470
// */
437-
// for (int i = 0; i < 3; i++) {
438-
// if (dd.fully_connected[i]) {
439-
// // Fully connected is only needed at the box surface
440-
// if (i==0 and (n!=start[1] or n!=end[1]-1) and (o!=start[2]
441-
// or o!=end[2]-1)) continue; if (i==1 and (m!=start[0] or
442-
// m!=end[0]-1) and (o!=start[2] or o!=end[2]-1)) continue;
443-
// if (i==2 and (m!=start[0] or m!=end[0]-1) and (n!=start[1]
444-
// or n!=end[1]-1)) continue; lower_index[i] = 0;
445-
// upper_index[i] = global_size[i] - 1;
446-
// }
447-
// }
471+
if (fully_connected_boundary()) {
472+
auto const [fc_boundary, fc_direction] = *fully_connected_boundary();
473+
474+
// Fully connected is only needed at the box surface
475+
if (at_boundary(fc_boundary, {m, n, o})) {
476+
lower_index[fc_direction] = -1;
477+
upper_index[fc_direction] = global_size[fc_boundary];
478+
}
479+
}
448480

449481
/* In non-periodic directions, the halo needs not
450482
* be considered. */
@@ -466,6 +498,12 @@ void RegularDecomposition::init_cell_interactions() {
466498
for (int p = lower_index[2]; p <= upper_index[2]; p++)
467499
for (int q = lower_index[1]; q <= upper_index[1]; q++)
468500
for (int r = lower_index[0]; r <= upper_index[0]; r++) {
501+
if (fully_connected_boundary()) {
502+
// Avoid fully connecting the boundary layer and the
503+
// next INNER layer
504+
if (fcb_is_inner_connection({m, n, o}, {r, q, p}))
505+
continue;
506+
}
469507
neighbors.insert(Utils::Vector3i{r, q, p});
470508
}
471509

@@ -629,11 +667,13 @@ GhostCommunicator RegularDecomposition::prepare_comm() {
629667
return ghost_comm;
630668
}
631669

632-
RegularDecomposition::RegularDecomposition(boost::mpi::communicator comm,
633-
double range,
634-
BoxGeometry const &box_geo,
635-
LocalBox const &local_geo)
636-
: m_comm(std::move(comm)), m_box(box_geo), m_local_box(local_geo) {
670+
RegularDecomposition::RegularDecomposition(
671+
boost::mpi::communicator comm, double range, BoxGeometry const &box_geo,
672+
LocalBox const &local_geo,
673+
std::optional<std::pair<int, int>> fully_connected)
674+
: m_comm(std::move(comm)), m_box(box_geo), m_local_box(local_geo),
675+
m_fully_connected_boundary(std::move(fully_connected)) {
676+
637677
/* set up new regular decomposition cell structure */
638678
create_cell_grid(range);
639679

src/core/cell_system/RegularDecomposition.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ struct RegularDecomposition : public ParticleDecomposition {
7979
boost::mpi::communicator m_comm;
8080
BoxGeometry const &m_box;
8181
LocalBox m_local_box;
82+
std::optional<std::pair<int, int>> m_fully_connected_boundary = {};
8283
std::vector<Cell> cells;
8384
std::vector<Cell *> m_local_cells;
8485
std::vector<Cell *> m_ghost_cells;
@@ -87,7 +88,8 @@ struct RegularDecomposition : public ParticleDecomposition {
8788

8889
public:
8990
RegularDecomposition(boost::mpi::communicator comm, double range,
90-
BoxGeometry const &box_geo, LocalBox const &local_geo);
91+
BoxGeometry const &box_geo, LocalBox const &local_geo,
92+
std::optional<std::pair<int, int>> fully_connected);
9193

9294
GhostCommunicator const &exchange_ghosts_comm() const override {
9395
return m_exchange_ghosts_comm;
@@ -115,6 +117,8 @@ struct RegularDecomposition : public ParticleDecomposition {
115117
Utils::Vector3d max_cutoff() const override;
116118
Utils::Vector3d max_range() const override;
117119

120+
auto fully_connected_boundary() const { return m_fully_connected_boundary; }
121+
118122
std::optional<BoxGeometry> minimum_image_distance() const override {
119123
return {m_box};
120124
}

src/core/system/System.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,17 @@ void System::set_min_global_cut(double value) {
154154

155155
void System::set_cell_structure_topology(CellStructureType topology) {
156156
if (topology == CellStructureType::REGULAR) {
157-
cell_structure->set_regular_decomposition(get_interaction_range());
157+
if (cell_structure->decomposition_type() == CellStructureType::REGULAR) {
158+
// get fully connected info from exising regular decomposition
159+
auto &old_regular_decomposition =
160+
dynamic_cast<RegularDecomposition const &>(
161+
std::as_const(*cell_structure).decomposition());
162+
cell_structure->set_regular_decomposition(
163+
get_interaction_range(),
164+
old_regular_decomposition.fully_connected_boundary());
165+
} else { // prev. decomposition is not a regular decomposition
166+
cell_structure->set_regular_decomposition(get_interaction_range(), {});
167+
}
158168
} else if (topology == CellStructureType::NSQUARE) {
159169
cell_structure->set_atom_decomposition();
160170
} else {

src/python/espressomd/cell_system.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ def set_regular_decomposition(self, **kwargs):
107107
use_verlet_lists : :obj:`bool`, optional
108108
Activates or deactivates the usage of Verlet lists.
109109
Defaults to ``True``.
110+
fully_connected_boundary : :obj:`dict`, optional
111+
If set, connects all cells on a given boundary along the given direction.
112+
Example: ``{"boundary": "z", "direction": "x"}`` connects all
113+
cells on the boundary normal to the z-direction along the x-axis.
114+
This corresponds to z-axis as shear plane normal and x-axis as
115+
shear direction in Lees-Edwards boundary conditions.
110116
111117
"""
112118
self.call_method("initialize", name="regular_decomposition", **kwargs)

src/script_interface/cell_system/CellSystem.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@
4242
#include <boost/variant.hpp>
4343

4444
#include <algorithm>
45+
#include <cassert>
4546
#include <iterator>
47+
#include <optional>
4648
#include <set>
4749
#include <sstream>
4850
#include <stdexcept>
@@ -51,6 +53,25 @@
5153
#include <utility>
5254
#include <vector>
5355

56+
static int coord(std::string const &s) {
57+
if (s == "x")
58+
return 0;
59+
if (s == "y")
60+
return 1;
61+
if (s == "z")
62+
return 2;
63+
throw std::invalid_argument("Invalid Cartesian coordinate: '" + s + "'");
64+
}
65+
66+
static std::string coord_letter(int c) {
67+
if (c == 0)
68+
return "x";
69+
if (c == 1)
70+
return "y";
71+
assert(c == 2);
72+
return "z";
73+
}
74+
5475
namespace ScriptInterface {
5576
namespace CellSystem {
5677

@@ -112,6 +133,20 @@ CellSystem::CellSystem() {
112133
auto const ns_types = hd.get_n_square_types();
113134
return Variant{std::vector<int>(ns_types.begin(), ns_types.end())};
114135
}},
136+
{"fully_connected_boundary", AutoParameter::read_only,
137+
[this]() {
138+
if (get_cell_structure().decomposition_type() !=
139+
CellStructureType::REGULAR) {
140+
return Variant{none};
141+
}
142+
auto const rd = get_regular_decomposition();
143+
auto const fcb = rd.fully_connected_boundary();
144+
if (not fcb)
145+
return Variant{none};
146+
return Variant{std::unordered_map<std::string, Variant>{
147+
{{"boundary", Variant{coord_letter((*fcb).first)}},
148+
{"direction", Variant{coord_letter((*fcb).second)}}}}};
149+
}},
115150
{"cutoff_regular", AutoParameter::read_only,
116151
[this]() {
117152
if (get_cell_structure().decomposition_type() !=
@@ -264,6 +299,21 @@ void CellSystem::initialize(CellStructureType const &cs_type,
264299
get_value_or<std::vector<int>>(params, "n_square_types", {});
265300
auto n_square_types = std::set<int>{ns_types.begin(), ns_types.end()};
266301
m_cell_structure->set_hybrid_decomposition(cutoff_regular, n_square_types);
302+
} else if (cs_type == CellStructureType::REGULAR) {
303+
std::optional<std::pair<int, int>> fcb_pair = std::nullopt;
304+
if (params.contains("fully_connected_boundary") and
305+
not is_none(params.at("fully_connected_boundary"))) {
306+
auto const variant =
307+
get_value<VariantMap>(params, "fully_connected_boundary");
308+
context()->parallel_try_catch([&fcb_pair, &variant]() {
309+
fcb_pair = {{coord(boost::get<std::string>(variant.at("boundary"))),
310+
coord(boost::get<std::string>(variant.at("direction")))}};
311+
});
312+
}
313+
context()->parallel_try_catch([this, &fcb_pair]() {
314+
m_cell_structure->set_regular_decomposition(
315+
get_system().get_interaction_range(), fcb_pair);
316+
});
267317
} else {
268318
system.set_cell_structure_topology(cs_type);
269319
}

0 commit comments

Comments
 (0)