From 0dc4bd448175da4db2232f404ebb1e4194421784 Mon Sep 17 00:00:00 2001
From: mkirsz <s1351949@sms.ed.ac.uk>
Date: Fri, 7 Feb 2025 22:52:25 +0000
Subject: [PATCH] New Cos_PT cutoff

---
 include/tadah/models/cutoffs.h | 40 +++++++++++++++++++++++++++++++
 src/cutoffs.cpp                | 42 +++++++++++++++++++++++++++++++++
 tests/test_cutoffs.cpp         | 43 ++++++++++++++++++++++++++++++++++
 3 files changed, 125 insertions(+)

diff --git a/include/tadah/models/cutoffs.h b/include/tadah/models/cutoffs.h
index 882f0c9..a961c6f 100644
--- a/include/tadah/models/cutoffs.h
+++ b/include/tadah/models/cutoffs.h
@@ -154,5 +154,45 @@ class Cut_Cos_S : public Cut_Base {
         double calc(double r);
         double calc_prime(double r);
 };
+/**
+ * @class Cut_PT
+ * @brief Represents a smooth cutoff function using hyperbolic tangent smoothing.
+ *
+ * This class implements a smooth cutoff function defined by:
+ * \f[
+ * f(r) = \frac{1}{2} + \frac{1}{2} \tanh\left( \frac{1}{2} \left( \frac{b}{r - a} + \frac{b}{r - R} \right) \right)
+ * \f]
+ * and its derivative:
+ * \f[
+ * f'(r) = -\frac{b}{4} \left( \frac{1}{\cosh\left( \dfrac{b}{2} \left( \dfrac{1}{r - a} + \dfrac{1}{r - R} \right) \right)} \right)^2 \left( \dfrac{1}{(r - a)^2} + \dfrac{1}{(r - R)^2} \right)
+ * \f]
+ * where:
+ * - \( r \) is the radial distance.
+ * - \( a \) is the inner cutoff radius (`rcut_inner`).
+ * - \( R \) is the outer cutoff radius (`rcut`).
+ * - \( b \) is the smoothing parameter.
+ *
+ * The function smoothly transitions from 0 to 1 between the inner cutoff radius \( a \) and the outer cutoff radius \( R \).
+ *
+ */
+class Cut_PT : public Cut_Base {
+    private:
+        std::string lab="Cut_PT";
+        double rcut;
+        double rcut_sq;
+        double rcut_inv;
+        double rcut_inner;
+
+    public:
+        Cut_PT();
+        Cut_PT(double rcut_value);
+        std::string label();
+        void set_rcut(const double r);
+        double get_rcut();
+        double get_rcut_sq();
+        double calc(double r);
+        double calc_prime(double r);
+};
+
 //template<> inline Registry<Cut_Base,double>::Map Registry<Cut_Base,double>::registry{};
 #endif
diff --git a/src/cutoffs.cpp b/src/cutoffs.cpp
index b818cda..72ebb6f 100644
--- a/src/cutoffs.cpp
+++ b/src/cutoffs.cpp
@@ -9,6 +9,7 @@ CONFIG::Registry<Cut_Base,double>::Register<Cut_Cos_S> Cut_Cos_S_1( "Cut_Cos_S"
 CONFIG::Registry<Cut_Base,double>::Register<Cut_Tanh> Cut_Tanh_1( "Cut_Tanh" );
 CONFIG::Registry<Cut_Base,double>::Register<Cut_Poly2> Cut_Poly_1( "Cut_Poly2" );
 CONFIG::Registry<Cut_Base,double>::Register<Cut_Dummy> Cut_Dummy_1( "Cut_Dummy" );
+CONFIG::Registry<Cut_Base,double>::Register<Cut_PT> Cut_PT_1( "Cut_PT" );
 
 
 Cut_Base::~Cut_Base() {}
@@ -207,3 +208,44 @@ double Cut_Cos_S::calc_prime(double r) {
     return -0.5 * M_PI * std::sin(M_PI * rs) * drs_dr;
   }
 }
+
+Cut_PT::Cut_PT() {}
+Cut_PT::Cut_PT(double rcut) {
+  set_rcut(rcut);
+  test_rcut(rcut);
+}
+std::string Cut_PT::label() {
+  return lab;
+}
+
+// Set the outer cutoff radius
+void Cut_PT::set_rcut(const double r) {
+    test_rcut(r);
+    rcut=r;
+    rcut_sq=r*r;
+    rcut_inv= r<=0 ? 0.0 : 1.0/r;
+    rcut_inner=-rcut/2;
+}
+double Cut_PT::get_rcut() {
+  return rcut;
+}
+double Cut_PT::get_rcut_sq() {
+  return rcut_sq;
+}
+double Cut_PT::calc(double r) {
+  if (r>=rcut) return 0.0;
+  double u = 0.5 * ((rcut / (r - rcut_inner)) + (rcut / (r - rcut)));
+  return 0.5 + 0.5 * tanh(u);
+}
+double Cut_PT::calc_prime(double r) {
+  if (r>=rcut) return 0.0;
+  double u = 0.5 * ((rcut / (r - rcut_inner)) + (rcut / (r - rcut)));
+  double cosh_u = cosh(u);
+  double sech2_u = 1.0 / (cosh_u * cosh_u);
+
+  double term1 = 1.0 / ((r - rcut_inner) * (r - rcut_inner));
+  double term2 = 1.0 / ((r - rcut) * (r - rcut));
+  double sum_terms = term1 + term2;
+
+  return - (rcut / 4.0) * sech2_u * sum_terms;
+}
diff --git a/tests/test_cutoffs.cpp b/tests/test_cutoffs.cpp
index aaac9d0..b90be3b 100644
--- a/tests/test_cutoffs.cpp
+++ b/tests/test_cutoffs.cpp
@@ -205,3 +205,46 @@ TEST_CASE( "Testing Cutoffs: Cut_Cos_s", "[Cut_Cos_S]" ) {
                Catch::Matchers::WithinAbs(-1.4939160824, 1e-10));
     delete c2b;
 }
+TEST_CASE( "Testing Cutoffs: Cut_PT", "[Cut_PT]" ) {
+
+    REQUIRE_NOTHROW(Cut_PT());
+    double rcut2b = 6.2;
+    double rcut2bsq = rcut2b*rcut2b;
+    using Cut = Cut_PT;
+    std::string cuttype = "Cut_PT";
+    Cut_Base *c2b = new Cut( rcut2b );
+
+    REQUIRE( c2b->label() == cuttype );
+
+    REQUIRE( c2b->calc(rcut2b) < std::numeric_limits<double>::min() );
+    REQUIRE( c2b->calc_prime(rcut2b) < std::numeric_limits<double>::min() );
+    REQUIRE( std::abs(c2b->get_rcut()-rcut2b)<std::numeric_limits<double>::min() );
+    REQUIRE( std::abs(c2b->get_rcut_sq()-rcut2bsq)<std::numeric_limits<double>::min() );
+
+    // cutoff cannot be negative
+    double temp = -0.1;
+    REQUIRE_THROWS(Cut( temp ));
+    REQUIRE_THROWS_AS(c2b->set_rcut(temp), std::runtime_error);
+    REQUIRE_THROWS_AS(c2b->test_rcut(temp), std::runtime_error);
+
+    // recheck after resetting cutoff
+    rcut2b=3.4;
+    rcut2bsq=rcut2b*rcut2b;
+    c2b->set_rcut(100000);
+    c2b->set_rcut(rcut2b);
+    REQUIRE( c2b->calc(rcut2b) < std::numeric_limits<double>::min() );
+    REQUIRE( c2b->calc_prime(rcut2b) < std::numeric_limits<double>::min() );
+    REQUIRE( std::abs(c2b->get_rcut()-rcut2b)<std::numeric_limits<double>::min() );
+    REQUIRE( std::abs(c2b->get_rcut_sq()-rcut2bsq)<std::numeric_limits<double>::min() );
+
+  REQUIRE_THAT(c2b->calc(2.0),
+               Catch::Matchers::WithinAbs(0.180990296913, 1e-12));
+  REQUIRE_THAT(c2b->calc_prime(2.0),
+               Catch::Matchers::WithinAbs(-0.293953123268, 1e-12));
+
+  REQUIRE_THAT(c2b->calc(3.0),
+               Catch::Matchers::WithinAbs(0.000419261765536, 1e-10));
+  REQUIRE_THAT(c2b->calc_prime(3.0),
+               Catch::Matchers::WithinAbs(-0.00897008113779, 1e-10));
+    delete c2b;
+}
-- 
GitLab