ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
energy.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2010-2026 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
22#include <config/config.hpp>
23
24#include "BoxGeometry.hpp"
25#include "Observable_stat.hpp"
26#include "Particle.hpp"
29#include "constraints/Constraints.hpp"
30#include "energy_cabana.hpp"
31#include "energy_inline.hpp"
33#include "kokkos_helpers.hpp"
37#include "short_range_loop.hpp"
40#include "system/System.hpp"
41
44
45#ifdef ESPRESSO_CALIPER
46#include "caliper_utils.hpp"
47#endif
48
49#include <cmath>
50#include <cstddef>
51#include <memory>
52#include <optional>
53#include <span>
54#include <vector>
55
57 using execution_space = Kokkos::DefaultHostExecutionSpace;
58 std::unique_ptr<Observable_stat> observable;
59 Kokkos::View<double **, Kokkos::LayoutRight, execution_space> local;
60};
61
62namespace System {
63
65
66 if (not m_obs_energy) {
67 auto const n_threads = EnergyObservable::execution_space{}.concurrency();
68 m_obs_energy = std::make_shared<EnergyObservable>();
69 m_obs_energy->observable = std::make_unique<Observable_stat>(1ul, 0ul, 0);
70 m_obs_energy->local =
71 decltype(m_obs_energy->local)("local_energy", n_threads, 1ul);
72 }
73
74 auto &local_energy = m_obs_energy->local;
75 auto &obs_energy = *m_obs_energy->observable;
76 obs_energy.reset(static_cast<std::size_t>(bonded_ias->get_next_key()),
77 nonbonded_ias->get_max_seen_particle_type());
78
79 if (long_range_interactions_sanity_checks()) {
80 return obs_energy;
81 }
82
83#if defined(ESPRESSO_CUDA) and \
84 (defined(ESPRESSO_ELECTROSTATICS) or defined(ESPRESSO_DIPOLES))
85 gpu->clear_energy_on_device();
86 gpu->update();
87#endif
88 on_observable_calc();
89
90 auto const local_parts = cell_structure->local_particles();
91
92 for (auto const &p : local_parts) {
93 obs_energy.kinetic_lin[0] += translational_kinetic_energy(p);
94 obs_energy.kinetic_rot[0] += rotational_kinetic_energy(p);
95 }
96
97 auto const coulomb_kernel = coulomb.pair_energy_kernel();
98 auto const dipoles_kernel = dipoles.pair_energy_kernel();
99
100#ifdef ESPRESSO_CALIPER
101 ESPRESSO_CALI_MARK_BEGIN("cabana_short_range");
102#endif
103 // Factory instead of an eager criterion: construction fills an O(n_types^2)
104 // cutoff table, so it only runs on the link-cell fallback path.
105 auto const make_verlet_criterion = [&] {
106 return VerletCriterion<>{*this,
107 cell_structure->get_verlet_skin(),
108 get_interaction_range(),
109 coulomb.cutoff(),
110 dipoles.cutoff(),
112 };
114
115 EnergyBinLayout layout{
116 static_cast<std::size_t>(bonded_ias->get_next_key()),
117 std::size_t(nonbonded_ias->get_max_seen_particle_type() + 1)};
118
119 using exec = Kokkos::DefaultHostExecutionSpace;
120 if (local_energy.extent(1) != layout.total) {
121 Kokkos::realloc(Kokkos::WithoutInitializing, local_energy,
122 exec{}.concurrency(), layout.total);
123 }
124 kokkos_deep_copy(exec{}, local_energy, 0.);
125
126 auto const &unique_particles = cell_structure->get_unique_particles();
127 auto const n_particles = unique_particles.size();
128 Kokkos::View<int *, Kokkos::LayoutRight, exec> mol_id("mol_id", n_particles);
129 for (std::size_t i = 0; i < n_particles; ++i) {
130 mol_id(i) = unique_particles[i]->mol_id();
131 }
132
133 // Non Bonded energies
134 EnergyKernel pair_e_kernel{*bonded_ias,
135 *nonbonded_ias,
136 coulomb,
137 get_ptr(coulomb_kernel),
138 get_ptr(dipoles_kernel),
139 *box_geo,
140 cell_structure->get_unique_particles(),
141 local_energy,
142 layout,
143 cell_structure->get_aosoa(),
144 mol_id,
145 maximal_cutoff()};
146
147 // Bonded energies: write a BondsEnergyKernelData + *BondsEnergyKernel
148 auto &bs = cell_structure->bond_state();
149 BondsEnergyKernelData bonds_e_data{*bonded_ias, *box_geo, local_energy,
150 layout, cell_structure->get_aosoa()};
151 PairBondsEnergyKernel pair_be_kernel{bonds_e_data, bs.pair_list, bs.pair_ids,
152 get_ptr(coulomb_kernel)};
153 AngleBondsEnergyKernel angle_be_kernel{bonds_e_data, bs.angle_list,
154 bs.angle_ids};
155 DihedralBondsEnergyKernel dih_be_kernel{bonds_e_data, bs.dihedral_list,
156 bs.dihedral_ids};
157
158 cabana_short_range(pair_be_kernel, angle_be_kernel, dih_be_kernel,
159 pair_e_kernel, *cell_structure, get_interaction_range(),
160 bonded_ias->maximal_cutoff(), make_verlet_criterion,
161 propagation->integ_switch);
162
163 reduce_cabana_energy(local_energy, layout, obs_energy, *bonded_ias,
164 nonbonded_ias->get_max_seen_particle_type() + 1);
165#ifdef ESPRESSO_CALIPER
166 ESPRESSO_CALI_MARK_END("cabana_short_range");
167#endif
168
169#ifdef ESPRESSO_ELECTROSTATICS
170 /* calculate k-space part of electrostatic interaction. */
171 obs_energy.coulomb[1] = coulomb.calc_energy_long_range();
172#endif
173
174#ifdef ESPRESSO_DIPOLES
175 /* calculate k-space part of magnetostatic interaction. */
176 obs_energy.dipolar[1] = dipoles.calc_energy_long_range();
177#endif
178
179 constraints->add_energy(local_parts, get_sim_time(), obs_energy);
180
181#if defined(ESPRESSO_CUDA) and \
182 (defined(ESPRESSO_ELECTROSTATICS) or defined(ESPRESSO_DIPOLES))
183 auto const energy_host = gpu->copy_energy_to_host();
184 if (!obs_energy.coulomb.empty())
185 obs_energy.coulomb[1] += static_cast<double>(energy_host.coulomb);
186 if (!obs_energy.dipolar.empty())
187 obs_energy.dipolar[1] += static_cast<double>(energy_host.dipolar);
188#endif
189
190 obs_energy.mpi_reduce();
191 return obs_energy;
192 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
193}
194
196 if (cell_structure->get_resort_particles()) {
197 cell_structure->update_ghosts_and_resort_particle(get_global_ghost_flags());
198 }
199
200 auto ret = 0.0;
201 if (auto const p = cell_structure->get_local_particle(pid)) {
202 auto const coulomb_kernel = coulomb.pair_energy_kernel();
203 auto kernel = [&ret, this](Particle const &p, Particle const &p1,
204 Utils::Vector3d const &vec) {
205#ifdef ESPRESSO_EXCLUSIONS
206 if (not do_nonbonded(p, p1))
207 return;
208#endif
209 auto const &ia_params = nonbonded_ias->get_ia_param(p.type(), p1.type());
210 // Add energy for current particle pair to result
211 ret += calc_non_bonded_pair_energy(p, p1, ia_params, vec, vec.norm(),
212 *bonded_ias, coulomb, nullptr);
213 };
214 cell_structure->run_on_particle_short_range_neighbors(*p, kernel);
215 }
216 return ret;
217}
218
219std::optional<double> System::particle_bond_energy(int pid, int bond_id,
220 std::vector<int> partners) {
221 if (cell_structure->get_resort_particles()) {
222 cell_structure->update_ghosts_and_resort_particle(get_global_ghost_flags());
223 }
224 Particle const *p = cell_structure->get_local_particle(pid);
225 if (not p or p->is_ghost())
226 return {}; // not available on this MPI rank or ghost
227 auto const &iaparams = *bonded_ias->at(bond_id);
228 try {
229 auto resolved_partners = cell_structure->resolve_bond_partners(partners);
230 auto const coulomb_kernel = coulomb.pair_energy_kernel();
231 return calc_bonded_energy(
232 iaparams, *p,
233 std::span(resolved_partners.data(), resolved_partners.size()), *box_geo,
234 get_ptr(coulomb_kernel));
235 } catch (const BondResolutionError &) {
236 bond_broken_error(p->id(), partners);
237 return {};
238 }
239}
240
241} // namespace System
void bond_broken_error(int id, std::span< const int > partner_ids)
Zero-overhead Caliper guards for the inactive (no CALI_CONFIG) case.
#define ESPRESSO_CALI_MARK_END(name)
Guarded CALI_MARK_END — no-op when inactive.
#define ESPRESSO_CALI_MARK_BEGIN(name)
Guarded CALI_MARK_BEGIN — no-op when inactive.
Observable for the pressure and energy.
double particle_short_range_energy_contribution(int pid)
Compute the short-range energy of a particle.
Definition energy.cpp:195
std::optional< double > particle_bond_energy(int pid, int bond_id, std::vector< int > partners)
Compute the energy of a given bond which has to exist on the given particle.
Definition energy.cpp:219
Observable_stat const & calculate_energy()
Calculate the total energy.
Definition energy.cpp:64
Returns true if the particles are to be considered for short range interactions.
constexpr double inactive_cutoff
Special cutoff value for an inactive interaction.
Definition config.hpp:53
const T * get_ptr(std::optional< T > const &opt)
static void reduce_cabana_energy(Kokkos::View< double **, Kokkos::LayoutRight, Kokkos::HostSpace > const &local_energy, EnergyBinLayout const &layout, Observable_stat &obs, BondedInteractionsMap const &bonded_ias, int n_types)
Energy calculation.
std::optional< double > calc_bonded_energy(Bonded_IA_Parameters const &iaparams, Particle const &p1, std::span< Particle * > partners, BoxGeometry const &box_geo, Coulomb::ShortRangeEnergyKernel::kernel_type const *kernel)
double translational_kinetic_energy(Particle const &p)
Calculate kinetic energies from translation for one particle.
double rotational_kinetic_energy(Particle const &p)
Calculate kinetic energies from rotation for one particle.
double calc_non_bonded_pair_energy(Particle const &p1, Particle const &p2, IA_parameters const &ia_params, Utils::Vector3d const &d, double const dist, BondedInteractionsMap const &bonded_ias, Coulomb::Solver const &coulomb, Coulomb::ShortRangeEnergyKernel::kernel_type const *coulomb_kernel)
Calculate non-bonded energies between a pair of particles.
bool do_nonbonded(Particle const &p1, Particle const &p2)
Determine if the non-bonded interactions between p1 and p2 should be calculated.
ESPRESSO_ATTR_ALWAYS_INLINE void kokkos_deep_copy(auto const &exec_space, auto const &view, auto const &value)
Wrapper for Kokkos::deep_copy that skips fork/join when the number of threads is 1.
Various procedures concerning interactions between particles.
void cabana_short_range(auto const &pair_bonds_kernel, auto const &angle_bonds_kernel, auto const &dihedral_bonds_kernel, auto const &nonbonded_kernel, CellStructure &cell_structure, double pair_cutoff, double bond_cutoff, auto const &make_verlet_criterion, auto const integ_switch, ShortRangeVerletPairLoop const &verlet_pair_loop={})
void update_verlet_state(System::System const &system, double const collision_cut)
Exception indicating that a particle id could not be resolved.
std::unique_ptr< Observable_stat > observable
Definition energy.cpp:58
Kokkos::DefaultHostExecutionSpace execution_space
Definition energy.cpp:57
Kokkos::View< double **, Kokkos::LayoutRight, execution_space > local
Definition energy.cpp:59
Struct holding all information for one particle.
Definition Particle.hpp:436
constexpr auto const & id() const
Definition Particle.hpp:455
constexpr bool is_ghost() const
Definition Particle.hpp:485