From 45cfd8c201ee32787d8e0df2424c5258a64cd6f7 Mon Sep 17 00:00:00 2001
From: Chris Rogers <chris.rogers@stfc.ac.uk>
Date: Mon, 10 Dec 2018 12:28:45 +0000
Subject: [PATCH] Add SplineTimeDependence element

---
 src/Classic/Algorithms/CMakeLists.txt         |   2 +
 src/Classic/Algorithms/SplineTimeDependence.h |  10 +-
 src/Elements/CMakeLists.txt                   |   2 +
 src/Elements/OpalPolynomialTimeDependence.h   |   3 -
 src/Elements/OpalSplineTimeDependence.cpp     | 101 ++++++++++++++++++
 src/Elements/OpalSplineTimeDependence.h       |  79 ++++++++++++++
 tests/classic_src/Algorithms/CMakeLists.txt   |   1 +
 .../PolynomialTimeDependenceTest.cpp          |  27 +++++
 .../Algorithms/SplineTimeDependenceTest.cpp   |   2 -
 tests/opal_src/Elements/CMakeLists.txt        |   1 +
 .../Elements/OpalSplineTimeDependenceTest.cpp |  23 ++++
 11 files changed, 241 insertions(+), 10 deletions(-)
 create mode 100644 src/Elements/OpalSplineTimeDependence.cpp
 create mode 100644 src/Elements/OpalSplineTimeDependence.h
 create mode 100644 tests/opal_src/Elements/OpalSplineTimeDependenceTest.cpp

diff --git a/src/Classic/Algorithms/CMakeLists.txt b/src/Classic/Algorithms/CMakeLists.txt
index 5c78d2e65..57143a95f 100644
--- a/src/Classic/Algorithms/CMakeLists.txt
+++ b/src/Classic/Algorithms/CMakeLists.txt
@@ -16,6 +16,7 @@ set (_SRCS
   PartData.cpp
   OpalParticle.cpp
   PolynomialTimeDependence.cpp
+  SplineTimeDependence.cpp
   Surveyor.cpp
   ThinMapper.cpp
   ThinTracker.cpp
@@ -57,6 +58,7 @@ set (HDRS
     PolynomialTimeDependence.h
     Quaternion.h
     rbendmap.h
+    SplineTimeDependence.h
     Surveyor.h
     ThinMapper.h
     ThinTracker.h
diff --git a/src/Classic/Algorithms/SplineTimeDependence.h b/src/Classic/Algorithms/SplineTimeDependence.h
index a35a9540f..ede981913 100644
--- a/src/Classic/Algorithms/SplineTimeDependence.h
+++ b/src/Classic/Algorithms/SplineTimeDependence.h
@@ -33,13 +33,9 @@
 #include <gsl/gsl_spline.h>
 
 #include "Ippl.h"
+#include "Utilities/GeneralClassicException.h"
 #include "Algorithms/AbstractTimeDependence.h"
 
-
-namespace interpolation {
-  class PolynomialPatch;
-}
-
 /** @class SplineTimeDependence
  * 
  *  Time dependence that follows a spline. Interpolation is supported at 
@@ -59,6 +55,10 @@ class SplineTimeDependence : public AbstractTimeDependence {
      *         with quadratic smoothing) 
      *  @param times the times of successive elements in the time dependence
      *  @param values the values at each time step.
+     *
+     *  It is an error if times and values are not of equal length, times and
+     *  values length < splineOrder + 1, or times do not increase strictly
+     *  monotonically.
      */
     SplineTimeDependence(size_t splineOrder,
                          std::vector<double> times,
diff --git a/src/Elements/CMakeLists.txt b/src/Elements/CMakeLists.txt
index 2b494e1e0..4bf1fadf1 100644
--- a/src/Elements/CMakeLists.txt
+++ b/src/Elements/CMakeLists.txt
@@ -46,6 +46,7 @@ set (_SRCS
   OpalSlit.cpp
   OpalSolenoid.cpp
   OpalSource.cpp
+  OpalSplineTimeDependence.cpp
   OpalSRot.cpp
   OpalVariableRFCavity.cpp
   OpalVariableRFCavityFringeField.cpp
@@ -108,6 +109,7 @@ set (HDRS
     OpalSlit.h
     OpalSolenoid.h
     OpalSource.h
+    OpalSplineTimeDependence.h
     OpalSRot.h
     OpalStripper.h
     OpalTravelingWave.h
diff --git a/src/Elements/OpalPolynomialTimeDependence.h b/src/Elements/OpalPolynomialTimeDependence.h
index bf0d6910c..f6df68961 100644
--- a/src/Elements/OpalPolynomialTimeDependence.h
+++ b/src/Elements/OpalPolynomialTimeDependence.h
@@ -28,11 +28,8 @@
 #ifndef OPAL_OpalPolynomialTimeDependence_HH
 #define OPAL_OpalPolynomialTimeDependence_HH
 
-#include "Algorithms/PolynomialTimeDependence.h"
 #include "Elements/OpalElement.h"
 
-class PolynomialTimeDependence;
-
 /** OpalPolynomialTimeDependence provides UI wrapper for the
  *  PolynomialTimeDependence
  */
diff --git a/src/Elements/OpalSplineTimeDependence.cpp b/src/Elements/OpalSplineTimeDependence.cpp
new file mode 100644
index 000000000..80a0b30ad
--- /dev/null
+++ b/src/Elements/OpalSplineTimeDependence.cpp
@@ -0,0 +1,101 @@
+/*
+ *  Copyright (c) 2018, Chris Rogers
+ *  All rights reserved.
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. Neither the name of STFC nor the names of its contributors may be used to
+ *     endorse or promote products derived from this software without specific
+ *     prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string>
+#include "Attributes/Attributes.h"
+#include "Utilities/OpalException.h"
+#include "Elements/OpalSplineTimeDependence.h"
+
+const std::string OpalSplineTimeDependence::doc_string =
+    std::string("The \"SPLINE_TIME_DEPENDENCE\" element defines ")+\
+    std::string("an array of times and corresponding values for time lookup, ")+\
+    std::string("for use in time-dependent elements. Lookup is supported at ")+\
+    std::string("first order or third order with quadratic smoothing.");
+
+// I investigated using a StringArray or RealArray here;
+// Don't seem to have capacity to handle variables, so for now not implemented
+OpalSplineTimeDependence::OpalSplineTimeDependence()
+       : OpalElement(int(SIZE),
+                     "SPLINE_TIME_DEPENDENCE",
+                     doc_string.c_str()) {
+    itsAttr[ORDER] = Attributes::makeReal("ORDER",
+      std::string("Order of the lookup - either 1 for linear interpolation, ")+
+      std::string("or 3 for cubic interpolation with quadratic smoothing. ")+
+      std::string("Other values make an error."));
+
+    itsAttr[TIMES] = Attributes::makeRealArray("TIMES",
+      std::string("Array of real times in ns. There must be at least \"ORDER\"+1 ")+
+      std::string("elements in the array and they must be strictly monotically ")+
+      std::string("increasing"));
+
+    itsAttr[VALUES] = Attributes::makeRealArray("VALUES",
+      std::string("Array of real values. The length of \"VALUES\" must be the ")+
+      std::string("same as the length of \"TIMES\"."));
+
+    registerRealAttribute("ORDER");
+    registerRealAttribute("TIMES");
+    registerRealAttribute("VALUES");
+
+    registerOwnership();
+}
+
+OpalSplineTimeDependence* OpalSplineTimeDependence::clone(const std::string &name) {
+    return new OpalSplineTimeDependence(name, this);
+}
+
+void OpalSplineTimeDependence::print(std::ostream& out) const {
+    OpalElement::print(out);
+}
+
+OpalSplineTimeDependence::OpalSplineTimeDependence(const std::string &name,
+                                                   OpalSplineTimeDependence *parent):
+    OpalElement(name, parent) {
+}
+
+OpalSplineTimeDependence::~OpalSplineTimeDependence() {}
+
+void OpalSplineTimeDependence::fillRegisteredAttributes
+                                     (const ElementBase &base, ValueFlag flag) {
+    OpalElement::fillRegisteredAttributes(base, flag);
+}
+
+void OpalSplineTimeDependence::update() {
+
+    double orderReal = Attributes::getReal(itsAttr[ORDER])+1e-10;
+    if ((orderReal - 1.) > 1e-9 && (orderReal - 3.) > 1e-9) {
+        throw OpalException("OpalSplineTimeDependence::update",
+                            "SPLINE_TIME_DEPENDENCE \"ORDER\" should be 1 or 3.");
+    }
+    size_t order(floor(orderReal));
+    std::vector<double> times = Attributes::getRealArray(itsAttr[TIMES]);
+    std::vector<double> values = Attributes::getRealArray(itsAttr[VALUES]);
+    std::shared_ptr<SplineTimeDependence> spline =
+                                  std::make_shared<SplineTimeDependence>(order,
+                                                                        times,
+                                                                        values);
+    AbstractTimeDependence::setTimeDependence(getOpalName(), spline);
+}
diff --git a/src/Elements/OpalSplineTimeDependence.h b/src/Elements/OpalSplineTimeDependence.h
new file mode 100644
index 000000000..9b24ff1e8
--- /dev/null
+++ b/src/Elements/OpalSplineTimeDependence.h
@@ -0,0 +1,79 @@
+/*
+ *  Copyright (c) 2018, Chris Rogers
+ *  All rights reserved.
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. Neither the name of STFC nor the names of its contributors may be used to
+ *     endorse or promote products derived from this software without specific
+ *     prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef OPAL_OpalSplineTimeDependence_HH
+#define OPAL_OpalSplineTimeDependence_HH
+
+#include "Algorithms/SplineTimeDependence.h"
+#include "Elements/OpalElement.h"
+
+/** OpalSplineTimeDependence provides UI wrapper for the
+ *  SplineTimeDependence
+ */
+
+class OpalSplineTimeDependence : public OpalElement {
+  public:
+    /** Enumeration maps to UI parameters */
+    enum {
+        ORDER = COMMON,
+        TIMES,
+        VALUES,
+        SIZE // size of the enum
+    };
+
+    /** Define mapping from enum variables to string UI parameter names */
+    OpalSplineTimeDependence();
+
+    /** No memory allocated so does nothing */
+    virtual ~OpalSplineTimeDependence();
+
+    /** Inherited copy constructor */
+    virtual OpalSplineTimeDependence *clone(const std::string &name);
+
+    /** Calls fillRegisteredAttributes on the OpalElement */
+    void fillRegisteredAttributes(const ElementBase &base, ValueFlag flag);
+
+    /** Receive parameters from the parser and hand them off to the
+     *  SplineTimeDependence
+     */
+    void update();
+
+    /** Calls print on the OpalElement */
+    virtual void print(std::ostream &) const;
+  private:
+    // Not implemented.
+    OpalSplineTimeDependence(const OpalSplineTimeDependence &);
+    void operator=(const OpalSplineTimeDependence &);
+
+    // Clone constructor.
+    OpalSplineTimeDependence(const std::string &name,
+                             OpalSplineTimeDependence *parent);
+
+    static const std::string doc_string;
+};
+
+#endif // OPAL_OpalSplineTimeDependence_HH
diff --git a/tests/classic_src/Algorithms/CMakeLists.txt b/tests/classic_src/Algorithms/CMakeLists.txt
index d20e926d4..027e46ee3 100644
--- a/tests/classic_src/Algorithms/CMakeLists.txt
+++ b/tests/classic_src/Algorithms/CMakeLists.txt
@@ -1,5 +1,6 @@
 set (_SRCS
     PolynomialTimeDependenceTest.cpp
+    SplineTimeDependenceTest.cpp
 )
 
 include_directories (
diff --git a/tests/classic_src/Algorithms/PolynomialTimeDependenceTest.cpp b/tests/classic_src/Algorithms/PolynomialTimeDependenceTest.cpp
index 41241845a..66c5767ca 100644
--- a/tests/classic_src/Algorithms/PolynomialTimeDependenceTest.cpp
+++ b/tests/classic_src/Algorithms/PolynomialTimeDependenceTest.cpp
@@ -1,3 +1,30 @@
+/*
+ *  Copyright (c) 2014, Chris Rogers
+ *  All rights reserved.
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. Neither the name of STFC nor the names of its contributors may be used to
+ *     endorse or promote products derived from this software without specific
+ *     prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
 #include "gtest/gtest.h"
 #include "Utilities/GeneralClassicException.h"
 #include "Algorithms/AbstractTimeDependence.h"
diff --git a/tests/classic_src/Algorithms/SplineTimeDependenceTest.cpp b/tests/classic_src/Algorithms/SplineTimeDependenceTest.cpp
index 437fb8824..494f9a421 100644
--- a/tests/classic_src/Algorithms/SplineTimeDependenceTest.cpp
+++ b/tests/classic_src/Algorithms/SplineTimeDependenceTest.cpp
@@ -30,8 +30,6 @@
 #include "Utilities/GeneralClassicException.h"
 #include "Algorithms/SplineTimeDependence.h"
 
-using interpolation::PolynomialPatch;
-
 class SplineTimeDependenceTest : public ::testing::Test { 
 public:
     SplineTimeDependenceTest() : times_m(10), values_m(10) {
diff --git a/tests/opal_src/Elements/CMakeLists.txt b/tests/opal_src/Elements/CMakeLists.txt
index 33f0be8f4..52b36a42e 100644
--- a/tests/opal_src/Elements/CMakeLists.txt
+++ b/tests/opal_src/Elements/CMakeLists.txt
@@ -1,6 +1,7 @@
 set (_SRCS
     OpalOffsetTest.cpp
     OpalPolynomialTimeDependenceTest.cpp
+    OpalSplineTimeDependenceTest.cpp
     OpalVariableRFCavityTest.cpp
     OpalVariableRFCavityFringeFieldTest.cpp
 )
diff --git a/tests/opal_src/Elements/OpalSplineTimeDependenceTest.cpp b/tests/opal_src/Elements/OpalSplineTimeDependenceTest.cpp
new file mode 100644
index 000000000..fab671c16
--- /dev/null
+++ b/tests/opal_src/Elements/OpalSplineTimeDependenceTest.cpp
@@ -0,0 +1,23 @@
+#include <sstream>
+
+#include "gtest/gtest.h"
+#include "Elements/OpalSplineTimeDependence.h"
+
+#include "opal_test_utilities/SilenceTest.h"
+
+TEST(OpalSplineTimeDependenceTest, ConstructorTest) {
+    OpalTestUtilities::SilenceTest silencer;
+
+    OpalSplineTimeDependence dep;
+    OpalSplineTimeDependence* dep_clone = dep.clone("new name");
+    EXPECT_EQ(dep_clone->getOpalName(), "new name");
+}
+
+TEST(OpalSplineSplineDependenceTest, PrintTest) {
+    OpalTestUtilities::SilenceTest silencer;
+
+    OpalSplineTimeDependence dep;
+    std::stringstream _string;
+    dep.print(_string);
+    EXPECT_EQ(_string.str(), "SPLINE_TIME_DEPENDENCE;\n");
+}
-- 
GitLab