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()); + } + +}