diff --git a/book/content.en/docs/reference/forcefields/external/distance/HarmonicGroovePotential.md b/book/content.en/docs/reference/forcefields/external/distance/HarmonicGroovePotential.md
new file mode 100644
index 000000000..276dc21e3
--- /dev/null
+++ b/book/content.en/docs/reference/forcefields/external/distance/HarmonicGroovePotential.md
@@ -0,0 +1,36 @@
++++
+title = "HarmonicGroove"
+weight = 100000
++++
+
+# HarmonicGroovePotential
+
+It is a Harmonic potential between particle and a spatial structure.
+
+{{<>}}
+U(r) = k(v - v_0)^2
+{{}}
+
+## Example
+
+```toml
+[[forcefields.external]]
+interaction = "Distance"
+potential = "HarmonicGroove"
+shape.name = "AxisAlignedPlane"
+shape.axis = "X"
+shape.position = 0.0
+shape.margin = 1.0
+
+parameters = [
+ {index = 0, k = 1.0, v0 = 1.0},
+ # ...
+]
+```
+
+## Input Reference
+
+- `k`: Floating
+ - It determines the strength of the potential.
+- `v0`: Floating
+ - It determines the native length.
diff --git a/book/content.en/docs/reference/forcefields/external/distance/_index.md b/book/content.en/docs/reference/forcefields/external/distance/_index.md
index 77fddf9b1..73224af0a 100644
--- a/book/content.en/docs/reference/forcefields/external/distance/_index.md
+++ b/book/content.en/docs/reference/forcefields/external/distance/_index.md
@@ -31,6 +31,7 @@ parameters = [
- The following potentials are available.
- [`"LennardJonesWall"`]({{}})
- [`"ExcludedVolumeWall"`]({{}})
+ - [`"HarmonicGroove"`]({{}})
- [`"ImplicitMembrane"`]({{}})
- `shape`: Table
- The shape and the position of a structure that interacts with particles.
diff --git a/book/content.en/docs/reference/forcefields/global/_index.md b/book/content.en/docs/reference/forcefields/global/_index.md
index f777d9d42..6c5caf6aa 100644
--- a/book/content.en/docs/reference/forcefields/global/_index.md
+++ b/book/content.en/docs/reference/forcefields/global/_index.md
@@ -12,11 +12,11 @@ bookCollapseSection = true
It depends on the distances between two particles.
-## [3SPN2BaseBase](3SPN2BaseBaseInteraction.md)
+## [3SPN2BaseBase]({{}})
It is specific to 3SPN2 Coarse-Grained DNA model.
-## [ProteinDNANonSpecific](ProteinDNANonSpecificInteraction.md)
+## [ProteinDNANonSpecific]({{}})
It is a Coarse-Grained hydrogen bond model.
diff --git a/book/content.en/docs/reference/forcefields/global/pair/UniformLennardJonesPotential.md b/book/content.en/docs/reference/forcefields/global/pair/UniformLennardJonesPotential.md
index 8990234df..906e93441 100644
--- a/book/content.en/docs/reference/forcefields/global/pair/UniformLennardJonesPotential.md
+++ b/book/content.en/docs/reference/forcefields/global/pair/UniformLennardJonesPotential.md
@@ -38,7 +38,7 @@ parameters = [
- The cutoff distance relative to the maximum {{}}\sigma_{ij}{{}}.
- `index`: Integer
- The index of the particle.
-- `offset`: Integer (ptional. By default, 0.)
+- `offset`: Integer (optional. By default, 0.)
- Offset of the index.
- `sigma`: Floating
- It determines the effective particle size.
diff --git a/book/content.ja/docs/reference/forcefields/external/distance/HarmonicGroovePotential.md b/book/content.ja/docs/reference/forcefields/external/distance/HarmonicGroovePotential.md
new file mode 100644
index 000000000..482b91b2c
--- /dev/null
+++ b/book/content.ja/docs/reference/forcefields/external/distance/HarmonicGroovePotential.md
@@ -0,0 +1,36 @@
++++
+title = "HarmonicGroove"
+weight = 200000
++++
+
+# HarmonicGroovePotential
+
+シンプルな調和振動子ポテンシャルです。
+
+{{}}
+U(r) = k (v - v_0)^2
+{{}}
+
+## Example
+
+```toml
+[[forcefields.external]]
+interaction = "Distance"
+potential = "HarmonicGroove"
+shape.name = "AxisAlignedPlane"
+shape.axis = "X"
+shape.position = 0.0
+shape.margin = 1.0
+
+parameters = [
+ {index = 0, k = 1.0, v0 = 1.0},
+ # ...
+]
+```
+
+## Input Reference
+
+- `k`: 浮動小数点数型
+ - ポテンシャルの強さを指定します。
+- `v0`: 浮動小数点数型
+ - 最安定点を指定します。
diff --git a/book/content.ja/docs/reference/forcefields/external/distance/_index.md b/book/content.ja/docs/reference/forcefields/external/distance/_index.md
index 77fddf9b1..73224af0a 100644
--- a/book/content.ja/docs/reference/forcefields/external/distance/_index.md
+++ b/book/content.ja/docs/reference/forcefields/external/distance/_index.md
@@ -31,6 +31,7 @@ parameters = [
- The following potentials are available.
- [`"LennardJonesWall"`]({{}})
- [`"ExcludedVolumeWall"`]({{}})
+ - [`"HarmonicGroove"`]({{}})
- [`"ImplicitMembrane"`]({{}})
- `shape`: Table
- The shape and the position of a structure that interacts with particles.
diff --git a/mjolnir/forcefield/external/CMakeLists.txt b/mjolnir/forcefield/external/CMakeLists.txt
index f38cd2e28..e34da6bfa 100644
--- a/mjolnir/forcefield/external/CMakeLists.txt
+++ b/mjolnir/forcefield/external/CMakeLists.txt
@@ -2,6 +2,7 @@ set(mjolnir_forcefield_external_cpp_files
"${CMAKE_CURRENT_SOURCE_DIR}/ExcludedVolumeWallPotential.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/ImplicitMembranePotential.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/LennardJonesWallPotential.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/HarmonicGroovePotential.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/RectangularBoxInteraction.cpp"
)
set(mjolnir_forcefield_cpp_files
diff --git a/mjolnir/forcefield/external/HarmonicGroovePotential.cpp b/mjolnir/forcefield/external/HarmonicGroovePotential.cpp
new file mode 100644
index 000000000..c44402d83
--- /dev/null
+++ b/mjolnir/forcefield/external/HarmonicGroovePotential.cpp
@@ -0,0 +1,11 @@
+#include
+
+#ifndef MJOLNIR_SEPARATE_BUILD
+#error "MJOLNIR_SEPARATE_BUILD flag is required"
+#endif
+
+namespace mjolnir
+{
+template class HarmonicGroovePotential;
+template class HarmonicGroovePotential;
+} // mjolnir
diff --git a/mjolnir/forcefield/external/HarmonicGroovePotential.hpp b/mjolnir/forcefield/external/HarmonicGroovePotential.hpp
new file mode 100644
index 000000000..e472ac97e
--- /dev/null
+++ b/mjolnir/forcefield/external/HarmonicGroovePotential.hpp
@@ -0,0 +1,93 @@
+#ifndef MJOLNIR_POTENTIAL_EXTERNAL_HARMONIC_GROOVE_POTENTIAL_HPP
+#define MJOLNIR_POTENTIAL_EXTERNAL_HARMONIC_GROOVE_POTENTIAL_HPP
+#include
+#include
+
+namespace mjolnir
+{
+
+/*! @brief harmonic potential *
+ * designed for external force field. Force particle-particle Harmonic potential, *
+ * see mjolnir/forcefield/local/HarmonicPotential.hpp */
+template
+class HarmonicGroovePotential
+{
+ public:
+ using real_type = realT;
+ using parameter_type = std::pair;
+ using container_type = std::vector;
+
+ static constexpr real_type default_cutoff() noexcept
+ {
+ return std::numeric_limits::max();
+ }
+ static constexpr parameter_type default_parameter() noexcept
+ {
+ return std::make_pair(real_type(0), real_type(0));
+ }
+
+ public:
+
+ HarmonicGroovePotential(
+ const std::vector>& params)
+ {
+ this->parameters_.resize(params.size());
+ this->participants_.reserve(params.size());
+ for(const auto& idxp : params)
+ {
+ const auto idx = idxp.first;
+ this->participants_.push_back(idx);
+ if(idx >= this->parameters_.size())
+ {
+ this->parameters_.resize(idx+1, default_parameter());
+ }
+ this->parameters_.at(idx) = idxp.second;
+ }
+ }
+ ~HarmonicGroovePotential(){}
+
+ real_type potential(const std::size_t i, const real_type v) const noexcept
+ {
+ const parameter_type k_v0 = this->parameters_[i];
+ const real_type dv = v - k_v0.second;
+ return k_v0.first * dv * dv;
+ }
+
+ real_type derivative(const std::size_t i, const real_type v) const noexcept
+ {
+ const parameter_type k_v0 = this->parameters_[i];
+ return 2 * k_v0.first * (v - k_v0.second);
+ }
+
+ real_type max_cutoff_length() const noexcept
+ {
+ return this->default_cutoff();
+ }
+
+ std::vector const& participants() const noexcept
+ {
+ return participants_;
+ }
+
+ // nothing to do when system parameters change.
+ template
+ void update(const System&) const noexcept {return;}
+
+ const char* name() const noexcept {return "HarmonicGroove";}
+
+ container_type& parameters() noexcept {return parameters_;}
+ container_type const& parameters() const noexcept {return parameters_;}
+
+ private:
+
+ container_type parameters_;
+ std::vector participants_;
+};
+
+#ifndef MJOLNIR_SEPARATE_BUILD
+extern template class HarmonicGroovePotential;
+extern template class HarmonicGroovePotential;
+#endif // MJOLNIR_SEPARATE_BUILD
+
+} // mjolnir
+#endif // MJOLNIR_POTENTIAL_EXTERNAL_HARMONIC_GROOVE_POTENTIAL_HPP
diff --git a/mjolnir/input/read_external_interaction.cpp b/mjolnir/input/read_external_interaction.cpp
index b6975bc91..b049f4e68 100644
--- a/mjolnir/input/read_external_interaction.cpp
+++ b/mjolnir/input/read_external_interaction.cpp
@@ -26,10 +26,10 @@ template std::unique_ptr>> read_afm_flexible_fitting_interaction(const toml::value&);
template std::unique_ptr>> read_afm_flexible_fitting_interaction(const toml::value&);
-template std::unique_ptr >> read_external_distance_interaction_shape(const toml::value& external);
-template std::unique_ptr >> read_external_distance_interaction_shape(const toml::value& external);
-template std::unique_ptr>> read_external_distance_interaction_shape(const toml::value& external);
-template std::unique_ptr>> read_external_distance_interaction_shape(const toml::value& external);
+template std::unique_ptr >> read_external_distance_interaction(const toml::value& external);
+template std::unique_ptr >> read_external_distance_interaction(const toml::value& external);
+template std::unique_ptr>> read_external_distance_interaction(const toml::value& external);
+template std::unique_ptr>> read_external_distance_interaction(const toml::value& external);
template std::unique_ptr >> read_pulling_force_interaction(const toml::value& external);
template std::unique_ptr >> read_pulling_force_interaction(const toml::value& external);
diff --git a/mjolnir/input/read_external_interaction.hpp b/mjolnir/input/read_external_interaction.hpp
index fb399c109..25ab65508 100644
--- a/mjolnir/input/read_external_interaction.hpp
+++ b/mjolnir/input/read_external_interaction.hpp
@@ -22,71 +22,28 @@ namespace mjolnir
// external interaction
// ----------------------------------------------------------------------------
-template
+template
std::unique_ptr>
-read_external_distance_interaction(const toml::value& external, shapeT&& shape)
+read_external_distance_interaction_shape(
+ const toml::value& external, potentialT&& pot, const bool read_margin = true)
{
MJOLNIR_GET_DEFAULT_LOGGER();
MJOLNIR_LOG_FUNCTION();
- using real_type = typename traitsT::real_type;
-
- const auto potential = toml::find(external, "potential");
- if(potential == "ImplicitMembrane")
- {
- MJOLNIR_LOG_NOTICE("-- potential function is Implicit Membrane.");
- using potential_t = ImplicitMembranePotential;
- using interaction_t = ExternalDistanceInteraction<
- traitsT, potential_t, shapeT>;
-
- return make_unique(std::move(shape),
- read_implicit_membrane_potential(external));
- }
- else if(potential == "LennardJonesWall")
- {
- MJOLNIR_LOG_NOTICE("-- potential function is Lennard-Jones.");
- using potential_t = LennardJonesWallPotential;
- using interaction_t = ExternalDistanceInteraction<
- traitsT, potential_t, shapeT>;
- return make_unique(std::move(shape),
- read_lennard_jones_wall_potential(external));
- }
- else if(potential == "ExcludedVolumeWall")
- {
- MJOLNIR_LOG_NOTICE("-- potential function is Excluded-Volume.");
- using potential_t = ExcludedVolumeWallPotential;
- using interaction_t = ExternalDistanceInteraction<
- traitsT, potential_t, shapeT>;
- return make_unique(std::move(shape),
- read_excluded_volume_wall_potential(external));
- }
- else
- {
- throw_exception(toml::format_error("[error] "
- "mjolnir::read_external_distance_interaction: invalid potential",
- toml::find(external, "potential"), "here", {
- "expected value is one of the following.",
- "- \"ExcludedVolumeWall\": repulsive r^12 potential",
- "- \"LennardJonesWall\" : famous r^12 - r^6 potential",
- "- \"ImplicitMembrane\" : single-well interaction that models a membrane"
- }));
- }
-}
-
-template
-std::unique_ptr>
-read_external_distance_interaction_shape(const toml::value& external)
-{
- MJOLNIR_GET_DEFAULT_LOGGER();
- MJOLNIR_LOG_FUNCTION();
- using real_type = typename traitsT::real_type;
const auto shape = toml::find(external, "shape");
const auto name = toml::find(shape, "name");
+ using real_type = typename traitsT::real_type;
+
if(name == "AxisAlignedPlane")
{
const auto pos = toml::find(shape, "position");
- const auto margin = toml::find(shape, "margin");
+ real_type margin = std::numeric_limits::infinity();
+ if(read_margin)
+ {
+ margin = toml::find(shape, "margin");
+ }
+
MJOLNIR_LOG_INFO("pos = ", pos);
MJOLNIR_LOG_INFO("margin = ", margin);
@@ -97,44 +54,49 @@ read_external_distance_interaction_shape(const toml::value& external)
{
MJOLNIR_LOG_NOTICE("-- interaction shape is YZ-Plane (+).");
using shape_t = AxisAlignedPlane>;
-
- return read_external_distance_interaction(
- external, shape_t(pos, margin));
+ using interaction_t = ExternalDistanceInteraction<
+ traitsT, potentialT, shape_t>;
+ return make_unique(shape_t(pos, margin), std::move(pot));
}
else if(axis == "-X")
{
MJOLNIR_LOG_NOTICE("-- interaction shape is YZ-Plane (-).");
using shape_t = AxisAlignedPlane>;
- return read_external_distance_interaction(
- external, shape_t(pos, margin));
+ using interaction_t = ExternalDistanceInteraction<
+ traitsT, potentialT, shape_t>;
+ return make_unique(shape_t(pos, margin), std::move(pot));
}
else if(axis == "Y" || axis == "+Y")
{
MJOLNIR_LOG_NOTICE("-- interaction shape is ZX-Plane (+).");
using shape_t = AxisAlignedPlane>;
- return read_external_distance_interaction(
- external, shape_t(pos, margin));
+ using interaction_t = ExternalDistanceInteraction<
+ traitsT, potentialT, shape_t>;
+ return make_unique(shape_t(pos, margin), std::move(pot));
}
else if(axis == "-Y")
{
MJOLNIR_LOG_NOTICE("-- interaction shape is ZX-Plane (-).");
using shape_t = AxisAlignedPlane>;
- return read_external_distance_interaction(
- external, shape_t(pos, margin));
+ using interaction_t = ExternalDistanceInteraction<
+ traitsT, potentialT, shape_t>;
+ return make_unique(shape_t(pos, margin), std::move(pot));
}
else if(axis == "Z" || axis == "+Z")
{
MJOLNIR_LOG_NOTICE("-- interaction shape is XY-Plane (+).");
using shape_t = AxisAlignedPlane>;
- return read_external_distance_interaction(
- external, shape_t(pos, margin));
+ using interaction_t = ExternalDistanceInteraction<
+ traitsT, potentialT, shape_t>;
+ return make_unique(shape_t(pos, margin), std::move(pot));
}
else if(axis == "-Z")
{
MJOLNIR_LOG_NOTICE("-- interaction shape is XY-Plane (-).");
using shape_t = AxisAlignedPlane>;
- return read_external_distance_interaction(
- external, shape_t(pos, margin));
+ using interaction_t = ExternalDistanceInteraction<
+ traitsT, potentialT, shape_t>;
+ return make_unique(shape_t(pos, margin), std::move(pot));
}
else
{
@@ -147,8 +109,61 @@ read_external_distance_interaction_shape(const toml::value& external)
{
throw std::runtime_error("invalid external forcefield shape: " + name);
}
+
}
+template
+std::unique_ptr>
+read_external_distance_interaction(const toml::value& external)
+{
+ MJOLNIR_GET_DEFAULT_LOGGER();
+ MJOLNIR_LOG_FUNCTION();
+ using real_type = typename traitsT::real_type;
+
+ const auto potential = toml::find(external, "potential");
+ if(potential == "ImplicitMembrane")
+ {
+ MJOLNIR_LOG_NOTICE("-- potential function is Implicit Membrane.");
+ using potential_t = ImplicitMembranePotential;
+ return read_external_distance_interaction_shape(
+ external, read_implicit_membrane_potential(external));
+ }
+ else if(potential == "LennardJonesWall")
+ {
+ MJOLNIR_LOG_NOTICE("-- potential function is Lennard-Jones.");
+ using potential_t = LennardJonesWallPotential;
+ return read_external_distance_interaction_shape(
+ external, read_lennard_jones_wall_potential(external));
+ }
+ else if(potential == "ExcludedVolumeWall")
+ {
+ MJOLNIR_LOG_NOTICE("-- potential function is Excluded-Volume.");
+ using potential_t = ExcludedVolumeWallPotential;
+ return read_external_distance_interaction_shape(
+ external, read_excluded_volume_wall_potential(external));
+ }
+ else if(potential == "HarmonicGroove")
+ {
+ MJOLNIR_LOG_NOTICE("-- potential function is Harmonic.");
+ using potential_t = HarmonicGroovePotential;
+ return read_external_distance_interaction_shape(
+ external, read_harmonic_groove_potential(external), false);
+ }
+ else
+ {
+ throw_exception(toml::format_error("[error] "
+ "mjolnir::read_external_distance_interaction: invalid potential",
+ toml::find(external, "potential"), "here", {
+ "expected value is one of the following.",
+ "- \"HarmonicGroove\" : attractive r^2 potential",
+ "- \"ExcludedVolumeWall\": repulsive r^12 potential",
+ "- \"LennardJonesWall\" : famous r^12 - r^6 potential",
+ "- \"ImplicitMembrane\" : single-well interaction that models a membrane"
+ }));
+ }
+}
+
+
template
std::unique_ptr>
read_external_position_restraint_interaction(const toml::value& external)
@@ -438,7 +453,7 @@ read_external_interaction(const toml::value& external)
if(interaction == "Distance")
{
MJOLNIR_LOG_NOTICE("Distance interaction found.");
- return read_external_distance_interaction_shape(external);
+ return read_external_distance_interaction(external);
}
else if(interaction == "PositionRestraint")
{
@@ -502,10 +517,10 @@ extern template std::unique_ptr>> read_afm_flexible_fitting_interaction(const toml::value&);
extern template std::unique_ptr>> read_afm_flexible_fitting_interaction(const toml::value&);
-extern template std::unique_ptr >> read_external_distance_interaction_shape(const toml::value& external);
-extern template std::unique_ptr >> read_external_distance_interaction_shape(const toml::value& external);
-extern template std::unique_ptr>> read_external_distance_interaction_shape(const toml::value& external);
-extern template std::unique_ptr>> read_external_distance_interaction_shape(const toml::value& external);
+extern template std::unique_ptr >> read_external_distance_interaction(const toml::value& external);
+extern template std::unique_ptr >> read_external_distance_interaction(const toml::value& external);
+extern template std::unique_ptr>> read_external_distance_interaction(const toml::value& external);
+extern template std::unique_ptr>> read_external_distance_interaction(const toml::value& external);
#endif
} // mjolnir
diff --git a/mjolnir/input/read_external_potential.hpp b/mjolnir/input/read_external_potential.hpp
index 3f853ce70..8211e80f9 100644
--- a/mjolnir/input/read_external_potential.hpp
+++ b/mjolnir/input/read_external_potential.hpp
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -90,6 +91,34 @@ read_excluded_volume_wall_potential(const toml::value& external)
return potential_type(eps, cutoff, params);
}
+template
+HarmonicGroovePotential
+read_harmonic_groove_potential(const toml::value& external)
+{
+ MJOLNIR_GET_DEFAULT_LOGGER();
+ MJOLNIR_LOG_FUNCTION();
+ using real_type = realT;
+ using potential_type = HarmonicGroovePotential;
+
+ const auto& env = external.contains("env") ? external.at("env") : toml::value{};
+
+ const auto& ps = toml::find(external, "parameters");
+ MJOLNIR_LOG_INFO(ps.size(), " parameters are found");
+
+ std::vector>> params;
+ params.reserve(ps.size());
+ for(const auto& param : ps)
+ {
+ const auto idx = find_parameter(param, env, "index") +
+ find_parameter_or(param, env, "offset", 0);
+ const auto k = find_parameter(param, env, "k");
+ const auto v0 = find_parameter(param, env, "v0");
+ params.emplace_back(idx, std::make_pair(k, v0));
+ MJOLNIR_LOG_INFO("idx = ", idx, ", sigma = ", k, ", epsilon = ", v0);
+ }
+ return potential_type(params);
+}
+
template
ImplicitMembranePotential
read_implicit_membrane_potential(const toml::value& external)
diff --git a/test/core/CMakeLists.txt b/test/core/CMakeLists.txt
index a411392bb..3a8dd3b97 100644
--- a/test/core/CMakeLists.txt
+++ b/test/core/CMakeLists.txt
@@ -44,6 +44,7 @@ set(TEST_NAMES
test_implicit_membrane_potential
test_excluded_volume_wall_potential
test_lennard_jones_wall_potential
+ test_harmonic_groove_potential
test_axis_aligned_plane_shape
test_bond_length_interaction
@@ -107,6 +108,7 @@ set(TEST_NAMES
test_read_isolf_potential
test_read_excluded_volume_wall_potential
test_read_lennard_jones_wall_potential
+ test_read_harmonic_groove_potential
test_read_implicit_membrane_potential
test_read_uniform_potential
diff --git a/test/core/test_harmonic_groove_potential.cpp b/test/core/test_harmonic_groove_potential.cpp
new file mode 100644
index 000000000..2b00c215a
--- /dev/null
+++ b/test/core/test_harmonic_groove_potential.cpp
@@ -0,0 +1,48 @@
+#define BOOST_TEST_MODULE "test_harmonic_groove_potential"
+
+#ifdef BOOST_TEST_DYN_LINK
+#include
+#else
+#include
+#endif
+
+#include
+
+BOOST_AUTO_TEST_CASE(HarmonicGroovePotential_double)
+{
+ using real_type = double;
+ constexpr static std::size_t N = 10000;
+ constexpr static real_type h = 1e-6;
+ constexpr static real_type tol = 1e-5;
+
+ const real_type k = 1.0;
+ const real_type v0 = 1.0;
+ const std::vector>>
+ k_v0{{0u, {k, v0}}};
+
+ mjolnir::HarmonicGroovePotential hg(k_v0);
+
+ const real_type test_range = 10.0;
+ const real_type z_min = v0 - test_range * 0.5;
+ const real_type z_max = v0 + test_range * 0.5;
+ const real_type dz = (z_max - z_min) / N;
+
+ real_type z = z_min;
+ for(std::size_t i = 0; i < N; ++i)
+ {
+ const real_type pot1 = hg.potential(0, z + h);
+ const real_type pot2 = hg.potential(0, z - h);
+ const real_type dpot = (pot1 - pot2) / (2 * h);
+ const real_type deri = hg.derivative(0, z);
+
+ if(std::abs(deri) > tol)
+ {
+ BOOST_TEST(dpot == deri, boost::test_tools::tolerance(tol));
+ }
+ else
+ {
+ BOOST_TEST(deri == 0.0, boost::test_tools::tolerance(tol));
+ }
+ z += dz;
+ }
+}
diff --git a/test/core/test_read_harmonic_groove_potential.cpp b/test/core/test_read_harmonic_groove_potential.cpp
new file mode 100644
index 000000000..eb9158759
--- /dev/null
+++ b/test/core/test_read_harmonic_groove_potential.cpp
@@ -0,0 +1,83 @@
+#define BOOST_TEST_MODULE "test_read_harmonic_groove_potential"
+
+#ifdef BOOST_TEST_DYN_LINK
+#include
+#else
+#include
+#endif
+
+#include
+#include
+#include
+
+using test_types = std::tuple;
+
+constexpr inline float tolerance_value(float) noexcept {return 1e-4;}
+constexpr inline double tolerance_value(double) noexcept {return 1e08;}
+
+template
+decltype(boost::test_tools::tolerance(std::declval()))
+tolerance() {return boost::test_tools::tolerance(tolerance_value(Real()));}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(read_harmonic_groove_noenv, T, test_types)
+{
+ mjolnir::LoggerManager::set_default_logger("test_read_harmonic_groove.log");
+
+ using real_type = T;
+ {
+ using namespace toml::literals;
+ // TODO: enable to remove shape.margin line
+ const toml::value v = u8R"(
+ interaction = "Distance"
+ potential = "HarmonicGroove"
+ shape.name = "AxisAlignedPlane"
+ shape.axis = "+X"
+ shape.position = 1.0
+ shape.margin = 1.0
+ parameters = [
+ {index = 0, k = 2.0, v0 = 1.5},
+ {index = 9, k = 2.0, v0 = 1.5},
+ ]
+ )"_toml;
+
+ const auto g = mjolnir::read_harmonic_groove_potential(v);
+
+ BOOST_TEST(g.participants().size() == 2u);
+ BOOST_TEST(g.participants().at(0) == 0u);
+ BOOST_TEST(g.participants().at(1) == 9u);
+
+ BOOST_TEST(g.parameters().at( 0).first == real_type(2.0), tolerance());
+ BOOST_TEST(g.parameters().at( 9).first == real_type(2.0), tolerance());
+ BOOST_TEST(g.parameters().at( 0).second == real_type(1.5), tolerance());
+ BOOST_TEST(g.parameters().at( 9).second == real_type(1.5), tolerance());
+ }
+
+ {
+ using namespace toml::literals;
+ // TODO: enable to remove shape.margin line
+ const toml::value v = u8R"(
+ interaction = "Distance"
+ potential = "HarmonicGroove"
+ shape.name = "AxisAlignedPlane"
+ shape.axis = "+X"
+ shape.position = 1.0
+ shape.margin = 1.0
+ parameters = [
+ {index = 0, k = 2.0, v0 = 1.5},
+ {index = 9, k = 2.0, v0 = 1.5},
+ ]
+ )"_toml;
+
+ const auto g = mjolnir::read_harmonic_groove_potential(v);
+
+ BOOST_TEST(g.participants().size() == 2u);
+ BOOST_TEST(g.participants().at(0) == 0u);
+ BOOST_TEST(g.participants().at(1) == 9u);
+
+ BOOST_TEST(g.parameters().at( 0).first == real_type(2.0), tolerance());
+ BOOST_TEST(g.parameters().at( 9).first == real_type(2.0), tolerance());
+ BOOST_TEST(g.parameters().at( 0).second == real_type(1.5), tolerance());
+ BOOST_TEST(g.parameters().at( 9).second == real_type(1.5), tolerance());
+ }
+
+}