Skip to content

Commit 847a945

Browse files
committed
Improve P3M shared-memory parallelization
Use shared memory in P3M/DP3M charge/dipole/force/torque assignment and real-space force calculation in the short-range loop.
1 parent f499ecd commit 847a945

49 files changed

Lines changed: 968 additions & 509 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

doc/tutorials/charged_system/charged_system.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
"import espressomd.zn\n",
4545
"\n",
4646
"\n",
47-
"espressomd.assert_features(['ELECTROSTATICS', 'P3M', 'WCA'])\n",
47+
"espressomd.assert_features(['ELECTROSTATICS', 'P3M', 'WCA', 'EXTERNAL_FORCES'])\n",
4848
"\n",
4949
"import tqdm\n",
5050
"import numpy as np\n",

doc/tutorials/convert.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ def execute_notebook(nb, src, cell_separator, notebook_filepath):
188188
import importlib_wrapper as iw
189189
notebook_dirname = os.path.dirname(notebook_filepath)
190190
# disable OpenGL GUI
191-
src_no_gui = iw.mock_es_visualization(src)
191+
src_no_gui = iw.mock_es_visualization(src, force_mock=True)
192192
# update notebook with new code
193193
set_code_cells(nb, src_no_gui.split(cell_separator))
194194
# execute notebook

doc/tutorials/electrodes/electrodes_part1.ipynb

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@
192192
"import espressomd.electrostatics\n",
193193
"import espressomd.electrostatic_extensions\n",
194194
"\n",
195-
"espressomd.assert_features(['ELECTROSTATICS'])\n",
195+
"espressomd.assert_features(['ELECTROSTATICS', 'EXTERNAL_FORCES'])\n",
196196
"plt.rcParams.update({'font.size': 18})"
197197
]
198198
},
@@ -251,11 +251,7 @@
251251
"ICC_EPSILON_WALLS = 1e5 # epsilon outside the slit. Very large to mimic metal\n",
252252
"ICC_CONVERGENCE = 1e-3 # ICC numeric/performance parameters\n",
253253
"ICC_RELAXATION = 0.95\n",
254-
"ICC_MAX_ITERATIONS = 1000\n",
255-
"\n",
256-
"# Lennard-Jones parameters\n",
257-
"LJ_SIGMA = 1.0\n",
258-
"LJ_EPSILON = 1.0 \n",
254+
"ICC_MAX_ITERATIONS = 100\n",
259255
"\n",
260256
"# Particle parameters\n",
261257
"TYPES = {\"Cation\": 0, \"Anion\": 1 ,\"Electrodes\": 2}\n",
@@ -309,7 +305,7 @@
309305
"outputs": [],
310306
"source": [
311307
"# SOLUTION CELL\n",
312-
"elc = espressomd.electrostatics.ELC(actor=p3m, gap_size=ELC_GAP, maxPWerror=MAX_PW_ERROR)"
308+
"elc = espressomd.electrostatics.ELC(actor=p3m, gap_size=ELC_GAP, maxPWerror=MAX_PW_ERROR, check_neutrality=False)"
313309
]
314310
},
315311
{

maintainer/parsing/importlib_wrapper.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def configure_and_import(filepath,
4141
script_suffix="",
4242
move_to_script_dir=True,
4343
mock_visualizer=True,
44+
force_visualizer_mock=True,
4445
**parameters):
4546
"""
4647
Copy a Python script to a new location and alter some lines of code:
@@ -69,6 +70,8 @@ def configure_and_import(filepath,
6970
if ``True``, substitute the visualizer with a ``Mock`` class in case
7071
of ``ImportError`` (use ``False`` if an ``ImportError`` is relevant
7172
to your test)
73+
force_visualizer_mock : :obj:`bool`
74+
if ``True``, always substitute the visualizer with a ``Mock`` class
7275
move_to_script_dir : :obj:`bool`
7376
if ``True``, move to the script's directory (useful when the script
7477
needs to load files hardcoded as relative paths, or when files are
@@ -101,7 +104,7 @@ def configure_and_import(filepath,
101104
code = disable_matplotlib_gui(code)
102105
# disable OpenGL GUI in case of ImportError using MagicMock()
103106
if mock_visualizer:
104-
code = mock_es_visualization(code)
107+
code = mock_es_visualization(code, force_visualizer_mock)
105108
# save changes to a new file
106109
output_filepath = filepath.parent / \
107110
f"{filepath.stem}_{script_suffix}_processed.py"
@@ -433,7 +436,7 @@ def visit_ImportFrom(self, node):
433436
node.lineno, node.module, child.name, child.asname)
434437

435438

436-
def mock_es_visualization(code):
439+
def mock_es_visualization(code, force_mock=False):
437440
"""
438441
Replace ``import espressomd.visualization`` by a ``MagicMock``
439442
when the visualization module is unavailable, by catching the
@@ -451,6 +454,12 @@ def mock_es_visualization(code):
451454
import unittest.mock
452455
import espressomd
453456
{1} = unittest.mock.MagicMock()
457+
""".lstrip()
458+
if force_mock:
459+
r_es_vis_mock = r"""
460+
import unittest.mock
461+
import espressomd
462+
{1} = unittest.mock.MagicMock()
454463
""".lstrip()
455464

456465
visitor = GetEspressomdVisualizerImports()

src/config/include/config/config.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,16 @@
9393
#define MAX_OBJECTS_IN_FLUID 10000
9494
#endif
9595

96+
/**
97+
* @brief Special cutoff value for an inactive interaction.
98+
* Non-bonded potentials that have this cutoff are never evaluated.
99+
*/
100+
inline constexpr double INACTIVE_CUTOFF = -1.;
101+
102+
/**
103+
* @brief Special cutoff value for an inactive bond.
104+
* Bonds that have this cutoff are never evaluated.
105+
*/
106+
inline constexpr double BONDED_INACTIVE_CUTOFF = -1.;
107+
96108
#endif

src/core/analysis/statistics.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,10 @@ Utils::Vector3d calc_linear_momentum(System::System const &system,
149149
if (include_particles) {
150150
momentum = reduce_over_local_particles<Utils::Vector3d>(
151151
*(system.cell_structure),
152-
[](Particle const &p, Utils::Vector3d &res) {
153-
res += p.mass() * p.v();
152+
[](Utils::Vector3d &acc, Particle const &p) {
153+
acc += p.mass() * p.v();
154154
},
155-
[](Utils::Vector3d &a, Utils::Vector3d const &b) { a = a + b; });
155+
[](Utils::Vector3d &acc, Utils::Vector3d const &v) { acc = acc + v; });
156156
}
157157
if (include_lbfluid and system.lb.is_solver_set()) {
158158
momentum += system.lb.get_momentum() * system.lb.get_lattice_speed();

src/core/aosoa_pack.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
#pragma once
2121

22+
#include <config/config.hpp>
23+
2224
#ifdef SHARED_MEMORY_PARALLELISM
2325

2426
#include "cell_system/CellStructure.hpp"

src/core/bonded_interactions/bonded_interaction_data.hpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
* For more information on how to add new interactions, see @ref bondedIA_new.
2525
*/
2626

27+
#include <config/config.hpp>
28+
2729
#include "angle_common.hpp"
2830
#include "angle_cosine.hpp"
2931
#include "angle_cossquare.hpp"
@@ -56,11 +58,6 @@
5658
#include <variant>
5759
#include <vector>
5860

59-
/* Special cutoff value for a disabled bond.
60-
* Bonds that have this cutoff are not visited during bond evaluation.
61-
*/
62-
static constexpr double BONDED_INACTIVE_CUTOFF = -1.;
63-
6461
/** Interaction type for unused bonded interaction slots */
6562
struct NoneBond {
6663
static constexpr int num = 0;

src/core/cell_system/CellStructure.cpp

Lines changed: 38 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@
2929
#include "BoxGeometry.hpp"
3030
#include "LocalBox.hpp"
3131
#include "Particle.hpp"
32+
#include "aosoa_pack.hpp"
3233
#include "cell_system/CellStructureType.hpp"
3334
#include "communication.hpp"
35+
#include "custom_verlet_list.hpp"
3436
#include "lees_edwards/lees_edwards.hpp"
3537
#include "particle_enumeration.hpp"
3638
#include "particle_reduction.hpp"
@@ -43,6 +45,13 @@
4345

4446
#include <boost/mpi/collectives/all_reduce.hpp>
4547

48+
#ifdef SHARED_MEMORY_PARALLELISM
49+
#include <Cabana_Core.hpp>
50+
#include <Cabana_NeighborList.hpp>
51+
#include <Kokkos_Core.hpp>
52+
#include <omp.h>
53+
#endif
54+
4655
#include <algorithm>
4756
#include <cassert>
4857
#include <cstddef>
@@ -57,45 +66,29 @@
5766
#include <variant>
5867
#include <vector>
5968

69+
CellStructure::~CellStructure() {
6070
#ifdef SHARED_MEMORY_PARALLELISM
61-
#include "aosoa_pack.hpp"
62-
#include "custom_verlet_list.hpp"
63-
#include <Cabana_Core.hpp>
64-
#include <Cabana_NeighborList.hpp>
65-
#include <Kokkos_Core.hpp>
71+
clear_local_properties();
72+
// Kokkos handle can only be freed after all Cabana containers have been freed
73+
m_kokkos_handle.reset();
6674
#endif
75+
}
6776

68-
CellStructure::~CellStructure() {
6977
#ifdef SHARED_MEMORY_PARALLELISM
70-
if (m_local_force) {
71-
m_local_force.reset();
72-
}
78+
void CellStructure::clear_local_properties() {
79+
m_local_force.reset();
7380
#ifdef ROTATION
74-
if (m_local_torque) {
75-
m_local_torque.reset();
76-
}
81+
m_local_torque.reset();
7782
#endif
7883
#ifdef NPT
79-
if (m_local_virial) {
80-
m_local_virial.reset();
81-
}
82-
#endif
83-
if (m_aosoa) {
84-
m_aosoa.reset();
85-
}
86-
if (m_particle_storage) {
87-
m_particle_storage.reset();
88-
}
89-
if (m_verlet_list_cabana) {
90-
m_verlet_list_cabana.reset();
91-
}
92-
// Kokkos handle can be freed after all Cabana containers have been freed
93-
m_kokkos_handle.reset();
84+
m_local_virial.reset();
9485
#endif
86+
m_aosoa.reset();
87+
m_particle_storage.reset();
88+
m_verlet_list_cabana.reset();
89+
m_rebuild_verlet_list_cabana = true;
9590
}
9691

97-
#ifdef SHARED_MEMORY_PARALLELISM
98-
9992
void CellStructure::set_kokkos_handle(
10093
std::shared_ptr<Communication::KokkosHandle> handle) {
10194
m_kokkos_handle = std::move(handle);
@@ -123,9 +116,10 @@ static auto estimate_max_counts(int max_prefactor, double pair_cutoff,
123116
return max_counts;
124117
}
125118

126-
void CellStructure::rebuild_local_properties(std::size_t const num_threads,
127-
double const pair_cutoff) {
119+
void CellStructure::rebuild_local_properties(double const pair_cutoff) {
128120
assert(m_kokkos_handle);
121+
using execution_space = Kokkos::DefaultExecutionSpace;
122+
auto const num_threads = execution_space().concurrency();
129123
auto const num_part = get_unique_particles().size();
130124
m_local_force =
131125
std::make_unique<ForceType>("local_force", num_part, num_threads);
@@ -145,13 +139,17 @@ void CellStructure::rebuild_local_properties(std::size_t const num_threads,
145139
m_verlet_list_cabana = std::make_unique<ListType>(0ul, num_part, max_counts);
146140
}
147141

142+
void CellStructure::reset_local_force() {
143+
Kokkos::deep_copy(get_local_force(), 0.);
144+
}
145+
148146
void CellStructure::reset_local_properties() {
149-
Kokkos::deep_copy(get_local_force(), 0);
147+
Kokkos::deep_copy(get_local_force(), 0.);
150148
#ifdef ROTATION
151-
Kokkos::deep_copy(get_local_torque(), 0);
149+
Kokkos::deep_copy(get_local_torque(), 0.);
152150
#endif
153151
#ifdef NPT
154-
Kokkos::deep_copy(get_local_virial(), 0);
152+
Kokkos::deep_copy(get_local_virial(), 0.);
155153
#endif
156154
}
157155

@@ -490,12 +488,12 @@ bool CellStructure::check_resort_required(
490488
Utils::Vector3d const &additional_offset) const {
491489
auto const lim = Utils::sqr(m_verlet_skin / 2.) - additional_offset.norm2();
492490

493-
Reduction::AddPartialResultKernel<bool> add_partial = [lim](Particle const &p,
494-
bool &result) {
495-
if ((p.pos() - p.pos_at_last_verlet_update()).norm2() > lim) {
496-
result = true;
497-
}
498-
};
491+
Reduction::AddPartialResultKernel<bool> add_partial =
492+
[lim](bool &result, Particle const &p) {
493+
if ((p.pos() - p.pos_at_last_verlet_update()).norm2() > lim) {
494+
result = true;
495+
}
496+
};
499497

500498
Reduction::ReductionOp<bool> reduce_op = [](bool &acc, bool const &val) {
501499
acc |= val;

src/core/cell_system/CellStructure.hpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -729,8 +729,9 @@ class CellStructure : public System::Leaf<CellStructure> {
729729
auto get_max_id() const { return m_max_id; }
730730

731731
void set_kokkos_handle(std::shared_ptr<Communication::KokkosHandle> handle);
732-
void rebuild_local_properties(std::size_t num_threads, double pair_cutoff);
732+
void rebuild_local_properties(double pair_cutoff);
733733
void reset_local_properties();
734+
void reset_local_force();
734735

735736
auto &get_local_force() { return *m_local_force; }
736737
#ifdef ROTATION
@@ -742,24 +743,24 @@ class CellStructure : public System::Leaf<CellStructure> {
742743
auto &get_aosoa() { return *m_aosoa; }
743744
auto const &get_unique_particles() const { return m_unique_particles; }
744745
auto const &get_verlet_list_cabana() const { return *m_verlet_list_cabana; }
746+
void clear_local_properties();
745747

746748
[[nodiscard]] auto is_verlet_list_cabana_rebuild_needed() const {
747749
return m_rebuild_verlet_list_cabana or (not use_verlet_list);
748750
}
749751

750752
/**
751753
* @brief Reset local properties of the Verlet list.
752-
* @param n_threads Number of threads.
753754
* @param cutoff Pair interaction cutoff.
754755
* @return True if a rebuild is needed.
755756
*/
756-
[[nodiscard]] auto prepare_verlet_list_cabana(int n_threads, double cutoff) {
757+
[[nodiscard]] auto prepare_verlet_list_cabana(double cutoff) {
757758
auto const rebuild = is_verlet_list_cabana_rebuild_needed();
758759
if (rebuild) {
759760
// If we have to rebuild, we need to count the particles
760761
set_index_map(); // parallelized index_map
761762
// Create essential variables for MD
762-
rebuild_local_properties(n_threads, cutoff);
763+
rebuild_local_properties(cutoff);
763764
} else {
764765
// If we do not rebuild we can use the saved map
765766
reset_local_properties();

0 commit comments

Comments
 (0)