From c480c1bf1727ec093cd68dec465a5c67e245fa65 Mon Sep 17 00:00:00 2001 From: Martin Duy Tat <martin.tat@sjc.ox.ac.uk> Date: Tue, 2 Oct 2018 12:41:30 +0100 Subject: [PATCH] Added classes for handling differential operators used in MultipoleT MultipoleT now converges for curved magnets MultipoleT is split into three derived classed and a base class --- src/Algorithms/ParallelCyclotronTracker.cpp | 55 +- src/Algorithms/ParallelCyclotronTracker.h | 9 + src/Algorithms/ParallelTTracker.h | 10 +- src/CMakeLists.txt | 4 + src/Classic/AbsBeamline/BeamlineVisitor.h | 16 +- src/Classic/AbsBeamline/CMakeLists.txt | 15 +- src/Classic/AbsBeamline/MultipoleT.cpp | 430 ++++++------- src/Classic/AbsBeamline/MultipoleT.h | 448 ++++++-------- src/Classic/AbsBeamline/MultipoleTBase.cpp | 263 ++++++++ src/Classic/AbsBeamline/MultipoleTBase.h | 469 ++++++++++++++ .../MultipoleTCurvedConstRadius.cpp | 131 ++++ .../AbsBeamline/MultipoleTCurvedConstRadius.h | 198 ++++++ .../AbsBeamline/MultipoleTCurvedVarRadius.cpp | 149 +++++ .../AbsBeamline/MultipoleTCurvedVarRadius.h | 205 +++++++ .../MultipoleTFunctions/CMakeLists.txt | 34 ++ .../CoordinateTransform.cpp | 206 +++++++ .../MultipoleTFunctions/CoordinateTransform.h | 131 ++++ .../DifferentialOperator.cpp | 188 ++++++ .../DifferentialOperator.h | 147 +++++ .../DifferentialOperatorTwo.cpp | 228 +++++++ .../DifferentialOperatorTwo.h | 173 ++++++ .../MultipoleTFunctions/Polynomial.cpp | 136 +++++ .../MultipoleTFunctions/Polynomial.h | 151 +++++ .../MultipoleTFunctions/PolynomialSum.cpp | 226 +++++++ .../MultipoleTFunctions/PolynomialSum.h | 151 +++++ .../MultipoleTFunctions/RecursionRelation.cpp | 104 ++++ .../MultipoleTFunctions/RecursionRelation.h | 161 +++++ .../RecursionRelationTwo.cpp | 117 ++++ .../RecursionRelationTwo.h | 233 +++++++ .../MultipoleTFunctions/TwoPolynomial.cpp | 404 ++++++++++++ .../MultipoleTFunctions/TwoPolynomial.h | 250 ++++++++ .../MultipoleTFunctions/tanhDeriv.cpp | 95 +++ .../MultipoleTFunctions/tanhDeriv.h | 66 ++ .../AbsBeamline/MultipoleTStraight.cpp | 105 ++++ src/Classic/AbsBeamline/MultipoleTStraight.h | 184 ++++++ .../AbsBeamline/SpecificElementVisitor.h | 29 +- src/Classic/Algorithms/DefaultVisitor.cpp | 17 +- src/Classic/Algorithms/DefaultVisitor.h | 9 + src/Classic/BeamlineGeometry/CMakeLists.txt | 4 +- .../BeamlineGeometry/VarRadiusGeometry.cpp | 47 ++ .../BeamlineGeometry/VarRadiusGeometry.h | 218 +++++++ src/Classic/CMakeLists.txt | 4 + src/Elements/CMakeLists.txt | 8 +- src/Elements/OpalMultipoleT.cpp | 35 +- src/Elements/OpalMultipoleT.h | 4 +- .../OpalMultipoleTCurvedConstRadius.cpp | 186 ++++++ .../OpalMultipoleTCurvedConstRadius.h | 94 +++ .../OpalMultipoleTCurvedVarRadius.cpp | 184 ++++++ src/Elements/OpalMultipoleTCurvedVarRadius.h | 94 +++ src/Elements/OpalMultipoleTStraight.cpp | 167 +++++ src/Elements/OpalMultipoleTStraight.h | 92 +++ src/OpalConfigure/Configure.cpp | 10 +- src/OpalConfigure/Configure.h | 2 +- tests/CMakeLists.txt | 9 + tests/classic_src/AbsBeamline/CMakeLists.txt | 1 + .../AbsBeamline/MultipoleTTest.cpp | 250 ++++++-- .../AbsBeamline/PolynomialTest.cpp | 573 ++++++++++++++++++ 57 files changed, 7366 insertions(+), 563 deletions(-) create mode 100644 src/Classic/AbsBeamline/MultipoleTBase.cpp create mode 100644 src/Classic/AbsBeamline/MultipoleTBase.h create mode 100644 src/Classic/AbsBeamline/MultipoleTCurvedConstRadius.cpp create mode 100644 src/Classic/AbsBeamline/MultipoleTCurvedConstRadius.h create mode 100644 src/Classic/AbsBeamline/MultipoleTCurvedVarRadius.cpp create mode 100644 src/Classic/AbsBeamline/MultipoleTCurvedVarRadius.h create mode 100644 src/Classic/AbsBeamline/MultipoleTFunctions/CMakeLists.txt create mode 100644 src/Classic/AbsBeamline/MultipoleTFunctions/CoordinateTransform.cpp create mode 100644 src/Classic/AbsBeamline/MultipoleTFunctions/CoordinateTransform.h create mode 100644 src/Classic/AbsBeamline/MultipoleTFunctions/DifferentialOperator.cpp create mode 100644 src/Classic/AbsBeamline/MultipoleTFunctions/DifferentialOperator.h create mode 100644 src/Classic/AbsBeamline/MultipoleTFunctions/DifferentialOperatorTwo.cpp create mode 100644 src/Classic/AbsBeamline/MultipoleTFunctions/DifferentialOperatorTwo.h create mode 100644 src/Classic/AbsBeamline/MultipoleTFunctions/Polynomial.cpp create mode 100644 src/Classic/AbsBeamline/MultipoleTFunctions/Polynomial.h create mode 100644 src/Classic/AbsBeamline/MultipoleTFunctions/PolynomialSum.cpp create mode 100644 src/Classic/AbsBeamline/MultipoleTFunctions/PolynomialSum.h create mode 100644 src/Classic/AbsBeamline/MultipoleTFunctions/RecursionRelation.cpp create mode 100644 src/Classic/AbsBeamline/MultipoleTFunctions/RecursionRelation.h create mode 100644 src/Classic/AbsBeamline/MultipoleTFunctions/RecursionRelationTwo.cpp create mode 100644 src/Classic/AbsBeamline/MultipoleTFunctions/RecursionRelationTwo.h create mode 100644 src/Classic/AbsBeamline/MultipoleTFunctions/TwoPolynomial.cpp create mode 100644 src/Classic/AbsBeamline/MultipoleTFunctions/TwoPolynomial.h create mode 100644 src/Classic/AbsBeamline/MultipoleTFunctions/tanhDeriv.cpp create mode 100644 src/Classic/AbsBeamline/MultipoleTFunctions/tanhDeriv.h create mode 100644 src/Classic/AbsBeamline/MultipoleTStraight.cpp create mode 100644 src/Classic/AbsBeamline/MultipoleTStraight.h create mode 100644 src/Classic/BeamlineGeometry/VarRadiusGeometry.cpp create mode 100644 src/Classic/BeamlineGeometry/VarRadiusGeometry.h create mode 100644 src/Elements/OpalMultipoleTCurvedConstRadius.cpp create mode 100644 src/Elements/OpalMultipoleTCurvedConstRadius.h create mode 100644 src/Elements/OpalMultipoleTCurvedVarRadius.cpp create mode 100644 src/Elements/OpalMultipoleTCurvedVarRadius.h create mode 100644 src/Elements/OpalMultipoleTStraight.cpp create mode 100644 src/Elements/OpalMultipoleTStraight.h create mode 100644 tests/classic_src/AbsBeamline/PolynomialTest.cpp diff --git a/src/Algorithms/ParallelCyclotronTracker.cpp b/src/Algorithms/ParallelCyclotronTracker.cpp index fa08fd61c..6b1c9979d 100644 --- a/src/Algorithms/ParallelCyclotronTracker.cpp +++ b/src/Algorithms/ParallelCyclotronTracker.cpp @@ -40,6 +40,10 @@ #include "AbsBeamline/Monitor.h" #include "AbsBeamline/Multipole.h" #include "AbsBeamline/MultipoleT.h" +#include "AbsBeamline/MultipoleTBase.h" +#include "AbsBeamline/MultipoleTStraight.h" +#include "AbsBeamline/MultipoleTCurvedConstRadius.h" +#include "AbsBeamline/MultipoleTCurvedVarRadius.h" #include "AbsBeamline/Probe.h" #include "AbsBeamline/RBend.h" #include "AbsBeamline/RFCavity.h" @@ -745,7 +749,6 @@ void ParallelCyclotronTracker::visitMonitor(const Monitor &corr) { // applyDrift(flip_s * corr.getElementLength()); } - /** * * @@ -772,6 +775,54 @@ void ParallelCyclotronTracker::visitMultipoleT(const MultipoleT &multT) { myElements.push_back(dynamic_cast<MultipoleT *>(multT.clone())); } +/** + * + * + * @param multTstraight + */ +void ParallelCyclotronTracker::visitMultipoleTStraight(const MultipoleTStraight &multTstraight) { + *gmsg << "Adding MultipoleTStraight" << endl; + if (opalRing_m != NULL) { + opalRing_m->appendElement(multTstraight); + } else { + throw OpalException("ParallelCyclotronTracker::visitMultipoleTStraight", + "Need to define a RINGDEFINITION to use MultipoleTStraight element"); + } + myElements.push_back(dynamic_cast<MultipoleTStraight *>(multTstraight.clone())); +} + +/** + * + * + * @param multTccurv + */ +void ParallelCyclotronTracker::visitMultipoleTCurvedConstRadius(const MultipoleTCurvedConstRadius &multTccurv) { + *gmsg << "Adding MultipoleTCurvedConstRadius" << endl; + if (opalRing_m != NULL) { + opalRing_m->appendElement(multTccurv); + } else { + throw OpalException("ParallelCyclotronTracker::visitMultipoleTCurvedConstRadius", + "Need to define a RINGDEFINITION to use MultipoleTCurvedConstRadius element"); + } + myElements.push_back(dynamic_cast<MultipoleTCurvedConstRadius *>(multTccurv.clone())); +} + +/** + * + * + * @param multTvcurv + */ +void ParallelCyclotronTracker::visitMultipoleTCurvedVarRadius(const MultipoleTCurvedVarRadius &multTvcurv) { + *gmsg << "Adding MultipoleTCurvedVarRadius" << endl; + if (opalRing_m != NULL) { + opalRing_m->appendElement(multTvcurv); + } else { + throw OpalException("ParallelCyclotronTracker::visitMultipoleTCurvedVarRadius", + "Need to define a RINGDEFINITION to use MultipoleTCurvedVarRadius element"); + } + myElements.push_back(dynamic_cast<MultipoleTCurvedVarRadius *>(multTvcurv.clone())); +} + /** * * @@ -3589,4 +3640,4 @@ void ParallelCyclotronTracker::injectBunch_m(bool& flagTransition) { // After this, numBunch_m is wrong but not needed anymore... numBunch_m--; } -} \ No newline at end of file +} diff --git a/src/Algorithms/ParallelCyclotronTracker.h b/src/Algorithms/ParallelCyclotronTracker.h index 3bdf02cef..64dfcffa1 100644 --- a/src/Algorithms/ParallelCyclotronTracker.h +++ b/src/Algorithms/ParallelCyclotronTracker.h @@ -134,6 +134,15 @@ public: /// Apply the algorithm to a MultipoleT virtual void visitMultipoleT (const MultipoleT &); + + /// Apply the algorithm to a MultipoleTStraight + virtual void visitMultipoleTStraight (const MultipoleTStraight &); + + /// Apply the algorithm to a MultipoleTCurvedConstRadius + virtual void visitMultipoleTCurvedConstRadius (const MultipoleTCurvedConstRadius &); + + /// Apply the algorithm to a MultipoleTCurvedVarRadius + virtual void visitMultipoleTCurvedVarRadius (const MultipoleTCurvedVarRadius &); /// Apply the algorithm to a Offset. virtual void visitOffset(const Offset &); diff --git a/src/Algorithms/ParallelTTracker.h b/src/Algorithms/ParallelTTracker.h index d47cd2a24..55159418c 100644 --- a/src/Algorithms/ParallelTTracker.h +++ b/src/Algorithms/ParallelTTracker.h @@ -42,6 +42,7 @@ #include "AbsBeamline/Marker.h" #include "AbsBeamline/Monitor.h" #include "AbsBeamline/Multipole.h" +#include "AbsBeamline/MultipoleT.h" #include "AbsBeamline/Probe.h" #include "AbsBeamline/RBend.h" #include "AbsBeamline/RBend3D.h" @@ -136,6 +137,9 @@ public: /// Apply the algorithm to a Multipole. virtual void visitMultipole(const Multipole &); + /// Apply the algorithm to a MultipoleT. + virtual void visitMultipoleT(const MultipoleT &); + /// Apply the algorithm to a Probe. virtual void visitProbe(const Probe &); @@ -381,6 +385,10 @@ inline void ParallelTTracker::visitMultipole(const Multipole &mult) { itsOpalBeamline_m.visit(mult, *this, itsBunch_m); } +inline void ParallelTTracker::visitMultipoleT(const MultipoleT &mult) { + itsOpalBeamline_m.visit(mult, *this, itsBunch_m); +} + inline void ParallelTTracker::visitProbe(const Probe &prob) { itsOpalBeamline_m.visit(prob, *this, itsBunch_m); } @@ -562,4 +570,4 @@ void ParallelTTracker::kickParticlesDKS() { } #endif -#endif // OPAL_ParallelTTracker_HH \ No newline at end of file +#endif // OPAL_ParallelTTracker_HH diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6f3c7a0ea..ee98c6cee 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -193,3 +193,7 @@ install (FILES ${HDRS} DESTINATION "${CMAKE_INSTALL_PREFIX}/include/src") # cmake-tab-width: 4 # indent-tabs-mode:nil # End: + +#Turn on gcov +#set(CMAKE_CXX_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") +#set(CMAKE_CXX_OUTPUT_EXTENSION_REPLACE 1) diff --git a/src/Classic/AbsBeamline/BeamlineVisitor.h b/src/Classic/AbsBeamline/BeamlineVisitor.h index 15c19cc95..ec62d0537 100644 --- a/src/Classic/AbsBeamline/BeamlineVisitor.h +++ b/src/Classic/AbsBeamline/BeamlineVisitor.h @@ -47,6 +47,9 @@ class Marker; class Monitor; class Multipole; class MultipoleT; +class MultipoleTStraight; +class MultipoleTCurvedConstRadius; +class MultipoleTCurvedVarRadius; class Offset; class Patch; class Probe; @@ -150,9 +153,18 @@ public: /// Apply the algorithm to a multipole. virtual void visitMultipole(const Multipole &) = 0; - /// Apply the algorithm to an arbitrary straight Multipole. + /// Apply the algorithm to an arbitrary Multipole. virtual void visitMultipoleT(const MultipoleT &) = 0; + /// Apply the algorithm to an arbitrary straight Multipole. + virtual void visitMultipoleTStraight(const MultipoleTStraight &) = 0; + + /// Apply the algorithm to an arbitrary curved Multipole of constant radius. + virtual void visitMultipoleTCurvedConstRadius(const MultipoleTCurvedConstRadius &) = 0; + + /// Apply the algorithm to an arbitrary curved Multipole of variable radius. + virtual void visitMultipoleTCurvedVarRadius(const MultipoleTCurvedVarRadius &) = 0; + /// Apply the algorithm to a patch. virtual void visitPatch(const Patch &) = 0; @@ -252,4 +264,4 @@ void BeamlineVisitor::visitRBend3D(const RBend3D &) { } -#endif // CLASSIC_BeamlineVisitor_HH \ No newline at end of file +#endif // CLASSIC_BeamlineVisitor_HH diff --git a/src/Classic/AbsBeamline/CMakeLists.txt b/src/Classic/AbsBeamline/CMakeLists.txt index 2e8dab69e..124b17cfb 100644 --- a/src/Classic/AbsBeamline/CMakeLists.txt +++ b/src/Classic/AbsBeamline/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(EndFieldModel) +add_subdirectory(MultipoleTFunctions) set (_SRCS AlignWrapper.cpp @@ -24,6 +25,10 @@ set (_SRCS Monitor.cpp Multipole.cpp MultipoleT.cpp + MultipoleTBase.cpp + MultipoleTStraight.cpp + MultipoleTCurvedConstRadius.cpp + MultipoleTCurvedVarRadius.cpp Offset.cpp ParallelPlate.cpp Patch.cpp @@ -75,6 +80,10 @@ set (HDRS Monitor.h Multipole.h MultipoleT.h + MultipoleTBase.h + MultipoleTStraight.h + MultipoleTCurvedConstRadius.h + MultipoleTCurvedVarRadius.h Offset.h ParallelPlate.h Patch.h @@ -98,4 +107,8 @@ set (HDRS VariableRFCavity.h ) -install (FILES ${HDRS} DESTINATION "${CMAKE_INSTALL_PREFIX}/include/AbsBeamline") \ No newline at end of file +install (FILES ${HDRS} DESTINATION "${CMAKE_INSTALL_PREFIX}/include/AbsBeamline") + +#Turn on gcov +#set(CMAKE_CXX_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") +#set(CMAKE_CXX_OUTPUT_EXTENSION_REPLACE 1) diff --git a/src/Classic/AbsBeamline/MultipoleT.cpp b/src/Classic/AbsBeamline/MultipoleT.cpp index a9abf93a2..40b13996b 100644 --- a/src/Classic/AbsBeamline/MultipoleT.cpp +++ b/src/Classic/AbsBeamline/MultipoleT.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Titus Dascalu + * Copyright (c) 2018, Martin Duy Tat * All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -29,6 +30,10 @@ #include "Algorithms/PartBunch.h" #include "MultipoleT.h" #include <gsl/gsl_math.h> +#include "MultipoleTFunctions/RecursionRelation.h" +#include "MultipoleTFunctions/RecursionRelationTwo.h" +#include "MultipoleTFunctions/tanhDeriv.h" +#include "MultipoleTFunctions/CoordinateTransform.h" using namespace endfieldmodel; @@ -37,14 +42,15 @@ MultipoleT::MultipoleT(const std::string &name): fringeField_l(endfieldmodel::Tanh()), fringeField_r(endfieldmodel::Tanh()), maxOrder_m(5), + maxOrderX_m(10), transMaxOrder_m(0), planarArcGeometry_m(1., 1.), - varStep_m(0.1), length_m(1.0), angle_m(0.0), entranceAngle_m(0.0), rotation_m(0.0), variableRadius_m(false), + boundingBoxLength_m(0.0), verticalApert_m(0.5), horizApert_m(0.5) { } @@ -54,15 +60,18 @@ MultipoleT::MultipoleT(const MultipoleT &right): fringeField_l(right.fringeField_l), fringeField_r(right.fringeField_r), maxOrder_m(right.maxOrder_m), + maxOrderX_m(right.maxOrderX_m), + recursion_VarRadius_m(right.recursion_VarRadius_m), + recursion_ConstRadius_m(right.recursion_ConstRadius_m), transMaxOrder_m(right.transMaxOrder_m), transProfile_m(right.transProfile_m), planarArcGeometry_m(right.planarArcGeometry_m), - varStep_m(right.varStep_m), length_m(right.length_m), angle_m(right.angle_m), entranceAngle_m(right.entranceAngle_m), rotation_m(right.rotation_m), variableRadius_m(right.variableRadius_m), + boundingBoxLength_m(right.boundingBoxLength_m), verticalApert_m(right.verticalApert_m), horizApert_m(right.horizApert_m), dummy() { @@ -85,15 +94,34 @@ bool MultipoleT::apply(const Vector_t &R, const Vector_t &P, const double &t,Vector_t &E, Vector_t &B) { /** Rotate coordinates around the central axis of the magnet */ Vector_t R_prime = rotateFrame(R); - /** If magnet is not straight go to coords along the magnet */ - if(angle_m != 0.0) { - Vector_t X = R_prime; - R_prime = transformCoords(X); - } + /** If magnet is not straight go to local Frenet-Serret coordinates */ + R_prime[2] *= -1; //OPAL uses different sign convention... + Vector_t X = R_prime; + R_prime = transformCoords(X); if (insideAperture(R_prime)) { - B[0] = getBx(R_prime); + /** Transform B-field from local to lab coordinates */ + double theta; + double prefactor = (length_m / angle_m) * (tanh((fringeField_l.getX0()) + / fringeField_l.getLambda()) + + tanh((fringeField_r.getX0()) + / fringeField_r.getLambda())); + if (angle_m == 0.0) { + theta = 0.0; + } else if (!variableRadius_m) { + theta = R_prime[2] * angle_m / length_m; + } else if (variableRadius_m) { + theta = fringeField_l.getLambda() * log(cosh((R_prime[2] + + fringeField_l.getX0()) / fringeField_l.getLambda())) - + fringeField_r.getLambda() * log(cosh((R_prime[2] - + fringeField_r.getX0()) / fringeField_r.getLambda())); + theta /= prefactor; + } + double Bx = getBx(R_prime); + double Bs = getBs(R_prime); + B[0] = Bx * cos(theta) - Bs * sin(theta); + B[2] = Bx * sin(theta) + Bs * cos(theta); B[1] = getBz(R_prime); - B[2] = getBs(R_prime); + B[2] *= -1; //OPAL uses different sign convention return false; } else { for(int i = 0; i < 3; i++) { @@ -110,9 +138,9 @@ bool MultipoleT::apply(const size_t &i, const double &t, Vector_t MultipoleT::rotateFrame(const Vector_t &R) { Vector_t R_prime(3), R_pprime(3); - /** Apply two 2D rotation matrices to coords vector - * -> rotate around central axis => skew fields - * -> rotate azymuthaly => entrance angle + /** Apply two 2D rotation matrices to coordinate vector + * Rotate around central axis => skew fields + * Rotate azymuthaly => entrance angle */ // 1st rotation R_prime[0] = R[0] * cos(rotation_m) + R[1] * sin(rotation_m); @@ -131,7 +159,7 @@ Vector_t MultipoleT::rotateFrame(const Vector_t &R) { Vector_t MultipoleT::rotateFrameInverse(Vector_t &B) { /** This function represents the inverse of the rotation * around the central axis performed by rotateFrame() method - * -> used to rotate B field back to global coord system + * Used to rotate B field back to global coordinate system */ Vector_t B_prime(3); B_prime[0] = B[0] * cos(rotation_m) + B[1] * sin(rotation_m); @@ -141,7 +169,7 @@ Vector_t MultipoleT::rotateFrameInverse(Vector_t &B) { } bool MultipoleT::insideAperture(const Vector_t &R) { - if (abs(R[1]) <= verticalApert_m / 2. && abs(R[0]) <= horizApert_m / 2.) { + if (std::abs(R[1]) <= verticalApert_m / 2. && std::abs(R[0]) <= horizApert_m / 2.) { return true; } else { @@ -150,53 +178,60 @@ bool MultipoleT::insideAperture(const Vector_t &R) { } Vector_t MultipoleT::transformCoords(const Vector_t &R) { - Vector_t X(3), Y(3); - // if radius not variable - if (not variableRadius_m) { + Vector_t X(3); + // If radius is constant + if (!variableRadius_m) { double radius = length_m / angle_m; - // Transform from Cartesian to Frenet-Seret along the magnet + // Transform from Cartesian to Frenet-Serret along the magnet double alpha = atan(R[2] / (R[0] + radius )); - if (alpha != 0.0) { - X[0] = R[2] / sin(alpha) - radius; + if (alpha != 0.0 && angle_m != 0.0) { + X[0] = R[2] / sin(alpha) - radius; X[1] = R[1]; - X[2] = radius * alpha; + X[2] = radius * alpha;// + boundingBoxLength_m; } else { - X = R; + X[0] = R[0]; + X[1] = R[1]; + X[2] = R[2];// + boundingBoxLength_m; } } else { - // if radius is variable need to transform coordinates at each - // point along the trajectory - double deltaAlpha, S = 0.0, localRadius; - double stepSize = varStep_m; // mm -> has a big effect on tracking time - if (abs(R[2]) <= stepSize) { - return R; // no transformation around origin + // If radius is variable + coordinatetransform::CoordinateTransform t(R[0], R[1], R[2], + fringeField_l.getX0(), + fringeField_l.getLambda(), + fringeField_r.getLambda(), + (length_m / angle_m)); + std::vector<double> r = t.getTransformation(); + X[0] = r[0]; + X[1] = r[1]; + X[2] = r[2]; + } + return X; +} + +void MultipoleT::setMaxOrder(std::size_t maxOrder) { + if (variableRadius_m && angle_m != 0.0) { + std::size_t N = recursion_VarRadius_m.size(); + while (maxOrder >= N) { + polynomial::RecursionRelationTwo r(N, 2 * (N + maxOrderX_m + 1)); + r.resizeX(transMaxOrder_m); + r.truncate(maxOrderX_m); + recursion_VarRadius_m.push_back(r); + N = recursion_VarRadius_m.size(); } - Y = R; - Vector_t temp; - while (abs(Y[2]) > stepSize and getRadius(S) != -1) { - localRadius = getRadius(S); - deltaAlpha = stepSize / localRadius; - if (R[2] < 0) { - deltaAlpha *= - 1.; // rotate in the other direction - } - temp = Y; - // translation - temp[0] += localRadius * (1 - cos(deltaAlpha)); - temp[2] -= localRadius * sin(deltaAlpha); - // + rotation along the ideal trajectory - Y[2] = temp[2] * cos(deltaAlpha) - temp[0] * sin(deltaAlpha); - Y[0] = temp[2] * sin(deltaAlpha) + temp[0] * cos(deltaAlpha); - S += localRadius * deltaAlpha; - // until we reach the actual point from Cartesian coordinates + } else if (!variableRadius_m && angle_m != 0.0) { + std::size_t N = recursion_ConstRadius_m.size(); + while (maxOrder >= N) { + polynomial::RecursionRelation r(N, 2 * (N + maxOrderX_m + 1)); + r.resizeX(transMaxOrder_m); + r.truncate(maxOrderX_m); + recursion_ConstRadius_m.push_back(r); + N = recursion_ConstRadius_m.size(); } - X[0] = Y[0]; - X[1] = Y[1]; - X[2] = S; } - return X; + maxOrder_m = maxOrder; } -void MultipoleT::setTransProfile(unsigned int n, double dTn) { +void MultipoleT::setTransProfile(std::size_t n, double dTn) { if (n > transMaxOrder_m) { transMaxOrder_m = n; transProfile_m.resize(n+1, 0.0); @@ -218,14 +253,14 @@ bool MultipoleT::setFringeField(double s0, double lambda_l, double lambda_r) { double MultipoleT::getBz(const Vector_t &R) { /** Returns the vertical field component - * -> sum_n f_n * z^(2n) / (2n)! + * sum_n f_n * z^(2n) / (2n)! */ double Bz = 0.0; if (angle_m == 0.0) { - // Straight geometry -> use corresponding field expansion - for(unsigned int n = 0; n <= maxOrder_m; n++) { + // Straight geometry -> Use corresponding field expansion directly + for(std::size_t n = 0; n <= maxOrder_m; n++) { double f_n = 0.0; - for(unsigned int i = 0; i <= n; i++) { + for(std::size_t i = 0; i <= n; i++) { f_n += gsl_sf_choose(n, i) * getTransDeriv(2 * i, R[0]) * getFringeDeriv(2 * n - 2 * i, R[2]); } @@ -234,12 +269,12 @@ double MultipoleT::getBz(const Vector_t &R) { } } else { if (variableRadius_m == true and getFringeDeriv(0, R[2]) < 1.0e-12) { - // return 0 if end of fringe field is reached - // this is to avoid functions being called at infinite radius + // Return 0 if end of fringe field is reached + // This is to avoid functions being called at infinite radius return 0.0; } - // Curved geometry -> use corresponding field expansion - for(unsigned int n = 0; n <= maxOrder_m; n++) { + // Curved geometry -> Use full machinery of differential operators + for(std::size_t n = 0; n <= maxOrder_m; n++) { double f_n = getFn(n, R[0], R[2]); Bz += gsl_sf_pow_int(R[1], 2 * n) / gsl_sf_fact(2 * n) * f_n; } @@ -249,14 +284,14 @@ double MultipoleT::getBz(const Vector_t &R) { double MultipoleT::getBx(const Vector_t &R) { /** Returns the radial component of the field - * -> sum_n z^(2n+1) / (2n+1)! * \partial_x f_n + * sum_n z^(2n+1) / (2n+1)! * \partial_x f_n */ double Bx = 0.0; if (angle_m == 0.0) { - // Straight geometry -> use corresponding field expansion - for(unsigned int n = 0; n <= maxOrder_m; n++) { + // Straight geometry -> Use corresponding field expansion directly + for(std::size_t n = 0; n <= maxOrder_m; n++) { double f_n = 0.0; - for(unsigned int i = 0; i <= n; i++) { + for(std::size_t i = 0; i <= n; i++) { f_n += gsl_sf_choose(n, i) * getTransDeriv(2 * i + 1, R[0]) * getFringeDeriv(2 * n - 2 * i, R[2]); } @@ -266,15 +301,15 @@ double MultipoleT::getBx(const Vector_t &R) { } } else { if (variableRadius_m == true and getFringeDeriv(0, R[2]) < 1.0e-12) { - // return 0 if end of fringe field is reached - // this is to avoid functions being called at infinite radius + // Return 0 if end of fringe field is reached + // This is to avoid functions being called at infinite radius return 0.0; } - // Curved geometry -> use corresponding field expansion - for(unsigned int n = 0; n <= maxOrder_m; n++) { + // Curved geometry -> Use full machinery of differential operators + for(std::size_t n = 0; n <= maxOrder_m; n++) { double partialX_fn = getFnDerivX(n, R[0], R[2]); - Bx += gsl_sf_pow_int(R[1], 2 * n + 1) / gsl_sf_fact(2 * n + 1); - Bx *= partialX_fn; + Bx += partialX_fn * gsl_sf_pow_int(R[1], 2 * n + 1) + / gsl_sf_fact(2 * n + 1); } } return Bx; @@ -282,14 +317,14 @@ double MultipoleT::getBx(const Vector_t &R) { double MultipoleT::getBs(const Vector_t &R) { /** Returns the component of the field along the central axis - * -> 1/h_s * sum_n z^(2n+1) / (2n+1)! \partial_s f_n + * 1/h_s * sum_n z^(2n+1) / (2n+1)! \partial_s f_n */ double Bs = 0.0; if (angle_m == 0.0) { - // Straight geometry -> use corresponding field expansion - for(unsigned int n = 0; n <= maxOrder_m; n++) { + // Straight geometry -> Use corresponding field expansion directly + for(std::size_t n = 0; n <= maxOrder_m; n++) { double f_n = 0.0; - for(unsigned int i = 0; i <= n; i++) { + for(std::size_t i = 0; i <= n; i++) { f_n += gsl_sf_choose(n, i) * getTransDeriv(2 * i, R[0]) * getFringeDeriv(2 * n - 2 * i + 1, R[2]); } @@ -299,15 +334,15 @@ double MultipoleT::getBs(const Vector_t &R) { } } else { if (variableRadius_m == true and getFringeDeriv(0, R[2]) < 1.0e-12) { - // return 0 if end of fringe field is reached - // this is to avoid functions being called at infinite radius + // Return 0 if end of fringe field is reached + // This is to avoid functions being called at infinite radius return 0.0; } - // Curved geometry -> use corresponding field expansion - for(unsigned int n = 0; n <= maxOrder_m; n++) { + // Curved geometry -> Use full machinery of differential operators + for(std::size_t n = 0; n <= maxOrder_m; n++) { double partialS_fn = getFnDerivS(n, R[0], R[2]); - Bs += gsl_sf_pow_int(R[1], 2 * n + 1) / gsl_sf_fact(2 * n + 1); - Bs *= partialS_fn; + Bs += partialS_fn * gsl_sf_pow_int(R[1], 2 * n + 1) + / gsl_sf_fact(2 * n + 1); } Bs /= getScaleFactor(R[0], R[2]); } @@ -315,14 +350,19 @@ double MultipoleT::getBs(const Vector_t &R) { } double MultipoleT::getFringeDeriv(int n, double s) { - // Wraps around the getTanh method of endfield - double temp; - temp = fringeField_l.getTanh(s, n) - fringeField_r.getNegTanh(s, n); - temp /= 2; - return temp; + if (n <= 10) { + return (fringeField_l.getTanh(s, n) - fringeField_r.getNegTanh(s, n)) + / 2; + } else { + return tanhderiv::integrate(s, + fringeField_l.Tanh::getX0(), + fringeField_l.Tanh::getLambda(), + fringeField_r.Tanh::getLambda(), + n); + } } -double MultipoleT::getTransDeriv(unsigned int n, double x) { +double MultipoleT::getTransDeriv(std::size_t n, double x) { /** Sets a vector of the coefficients in the polynomial expansion * of transverse profile; shifts them to the left and multiply by * corresponding power each time to take derivative once; @@ -332,20 +372,20 @@ double MultipoleT::getTransDeriv(unsigned int n, double x) { std::vector<double> temp = transProfile_m; if (n <= transMaxOrder_m) { if (n != 0) { - for(unsigned int i = 1; i <= n; i++) { - for(unsigned int j = 0; j <= transMaxOrder_m; j++) { + for(std::size_t i = 1; i <= n; i++) { + for(std::size_t j = 0; j <= transMaxOrder_m; j++) { if (j <= transMaxOrder_m - i) { - // move terms to the left and multiply by power + // Move terms to the left and multiply by power temp[j] = temp[j + 1] * (j + 1); } else { - // put 0 at the end for missing higher powers + // Put 0 at the end for missing higher powers temp[j] = 0.0; } } } } // Now use the vector to calculate value of the function - for(unsigned int k = 0; k <= transMaxOrder_m; k++) { + for(std::size_t k = 0; k <= transMaxOrder_m; k++) { func += temp[k] * gsl_sf_pow_int(x, k); } } @@ -386,185 +426,115 @@ std::vector<double> MultipoleT::getFringeLength() const { } double MultipoleT::getRadius(double s) { - /** The radius is calculated to be inverse proportional to the field on - * the refrence trajectory (central axis of magnet) - * @f$ \rho (s) = \rho(0) * S(0) / S(s) @f$ - */ double centralRadius = length_m / angle_m; - // at the centre of the magnet - double propCoeff = centralRadius * getFringeDeriv(0, 0); - // move to current position on central axis + if (!variableRadius_m) { + return centralRadius; + } if (getFringeDeriv(0, s) != 0.0) { - return propCoeff / getFringeDeriv(0, s); + return centralRadius * getFringeDeriv(0, 0) / getFringeDeriv(0, s); } else { - return -1; // return -1 if radius is infinite + return -1; // Return -1 if radius is infinite } } -double MultipoleT::getRadiusFirstDeriv(double s) { - /** @return The derivative of the function which describes the radius - * as a function of s; - */ - return -1.0 * getRadius(s) * getFringeDeriv(1, s) / getFringeDeriv(0, s); -} - -double MultipoleT::getRadiusSecDeriv(double s) { - /** @return The second derivative of radius function - */ - double deriv = 0.0; - deriv += gsl_pow_2(getFringeDeriv(1, s)) / getFringeDeriv(0, s); - deriv -= getFringeDeriv(2, s); - deriv *= getRadius(s); - return deriv; -} - double MultipoleT::getScaleFactor(double x, double s) { - /** @return The scale factor h_s = [(1 + x / rho)^2 + (d rho / ds)^2]^0.5 - * rho -> radius - * used in field expansion, comes from Laplacian when curvature is variable - * if radius is fixed returns h_s = 1 + x / rho - */ - if (not variableRadius_m) { - double rho = length_m / angle_m; - return 1 + x / rho; + if (!variableRadius_m) { + return 1 + x * angle_m/ length_m; } else { - double temp = 0.0; - temp += gsl_pow_2(getRadiusFirstDeriv(s)); - temp += gsl_pow_2(1 + x / getRadius(s)); - return std::pow(temp, 0.5); + return 1 + x / getRadius(s); } } -double MultipoleT::getScaleFactorDerivX(double x, double s) { - /** Helper function to bunch terms together - * -> returns the partial deriv of the scale factor wrt. x - * -> partial h_s / partial x#include "Algorithms/PartBunch.h" - * -> = (1 + x / rho) / (rho * h_s ^ 0.5) - */ - double temp = 0.0; - temp += (1 + x / getRadius(s)) / getRadius(s); - temp /= std::pow(getScaleFactor(x, s), 0.5); - return temp; -} - -double MultipoleT::getScaleFactorDerivS(double x, double s) { - /** Helper function to bunch terms together - * -> returns partial h_s / partial s - * -> = (h_s)^(-1/2) * partial rho / partial s * - * -> * [-(1+x/rho)*x/rho^2 + partial^2 rho / partial^x] - */ - double temp = 0.0; - temp -= (1 + x /getRadius(s)) * x / gsl_pow_2(getRadius(s)); - temp += getRadiusSecDeriv(s); - temp *= getRadiusFirstDeriv(s) / std::pow(getScaleFactor(x, s), -0.5); - return temp; -} - -double MultipoleT::getFnDerivX(unsigned int n, double x, double s) { - /** Returns the partial derivative of f_n(x, s) wrt x - * -> numerical differentiation - * -> 5-points formula - * -> error of order stepSize ^ 4 - */ +double MultipoleT::getFnDerivX(std::size_t n, double x, double s) { if (n == 0) { return getTransDeriv(1, x) * getFringeDeriv(0, s); } double deriv = 0.0; double stepSize = 1e-3; - deriv += getFn(n, x - 2. * stepSize, s) - 8. * getFn(n, x - stepSize, s); - deriv += 8. * getFn(n, x + stepSize, s) - getFn(n, x + 2. * stepSize, s); - deriv /= 12.0 * stepSize; + deriv += 1. * getFn(n, x - 2. * stepSize, s); + deriv += -8. * getFn(n, x - stepSize, s); + deriv += 8. * getFn(n, x + stepSize, s); + deriv += -1. * getFn(n, x + 2. * stepSize, s); + deriv /= 12 * stepSize; return deriv; } -double MultipoleT::getFnDerivS(unsigned int n, double x, double s) { - /** Returns the partial derivative of f_n(x, s) wrt s - * -> numerical differentiation - * -> 5-points formula - * -> error of order stepSize ^ 4 - */ +double MultipoleT::getFnDerivS(std::size_t n, double x, double s) { if (n == 0) { return getTransDeriv(0, x) * getFringeDeriv(1, s); } double deriv = 0.0; double stepSize = 1e-3; - deriv += getFn(n, x, s - 2. * stepSize) - 8. * getFn(n, x, s - stepSize); - deriv += 8. * getFn(n, x, s + stepSize) - getFn(n, x, s + 2. * stepSize); - deriv /= 12.0 * stepSize; - return deriv; -} - -double MultipoleT::getFnSecDerivX(unsigned int n, double x, double s) { - /** Returns the second partial derivative of f_n(x, s) wrt x - * -> numerical differentiation - * -> 5-points formula - * -> error of order stepSize ^ 4 - */ - if (n == 0) { - return getTransDeriv(2, x) * getFringeDeriv(0, s); - } - double deriv = 0.0; - double stepSize = 1e-3; - deriv += getFn(n, x + 2. * stepSize, s) + getFn(n, x - 2. * stepSize, s); - deriv -= getFn(n, x + stepSize, s) + getFn(n, x - stepSize, s); - deriv /= 3. * gsl_pow_2(stepSize); - return deriv; -} - -double MultipoleT::getFnSecDerivS(unsigned int n, double x, double s) { - /** Returns the second partial derivative of f_n(x, s) wrt s - * -> numerical differentiation - * -> 5-points formula - * -> error of order stepSize ^ 4 - */ - if (n == 0) { - return getTransDeriv(0, x) * getFringeDeriv(2, s); - } - double deriv = 0.0; - double stepSize = 1e-3; - deriv += getFn(n, x, s + 2. * stepSize) + getFn(n, x, s - 2. * stepSize); - deriv -= getFn(n, x, s + stepSize) + getFn(n, x, s - stepSize); - deriv /= 3. * gsl_pow_2(stepSize); + deriv += 1. * getFn(n, x, s - 2. * stepSize); + deriv += -8. * getFn(n, x, s - stepSize); + deriv += 8. * getFn(n, x, s + stepSize); + deriv += -1. * getFn(n, x, s + 2. * stepSize); + deriv /= 12 * stepSize; return deriv; } -double MultipoleT::getFn(unsigned int n, double x, double s) { - /** Returns the function f_n(x, s) for curved geometry - * -> based on recursion - */ +double MultipoleT::getFn(std::size_t n, double x, double s) { if (n == 0) { return getTransDeriv(0, x) * getFringeDeriv(0, s); } - // If radius is constant use corresponding recursion - if (not variableRadius_m) { + if (!variableRadius_m) { double rho = length_m / angle_m; - double h_s = 1 + x / rho; - double temp = 0.0; - temp += getFnDerivX(n - 1, x, s) / (h_s * rho); - temp += getFnSecDerivX(n - 1, x, s); - temp += getFnSecDerivS(n - 1, x, s) / gsl_pow_2(h_s); - temp *= -1.0; - return temp; + double func = 0.0; + + for (std::size_t j = 0; + j <= recursion_ConstRadius_m.at(n).getMaxSDerivatives(); + j++) { + double FringeDerivj = getFringeDeriv(2 * j, s); + for (std::size_t i = 0; + i <= recursion_ConstRadius_m.at(n).getMaxXDerivatives(); + i++) { + if (recursion_ConstRadius_m.at(n).isPolynomialZero(i, j)) { + continue; + } + func += (recursion_ConstRadius_m.at(n) + .evaluatePolynomial(x / rho, i, j) * + getTransDeriv(i, x) * FringeDerivj) / + gsl_sf_pow_int(rho, 2 * n - i - 2 * j); + } + } + func *= gsl_sf_pow_int(-1.0, n); + return func; } else { - // If radius is variable use corresponding recursion - double temp = 0.0; - double h_s = getScaleFactor(x, s); - temp += getScaleFactorDerivX(x, s) * getFnDerivX(n - 1, x, s); - temp -= getScaleFactorDerivS(x, s) * getFnDerivS(n - 1, x, s) / - gsl_pow_2(h_s); - temp += getFnSecDerivX(n - 1, x, s) * h_s; - temp += getFnSecDerivS(n - 1, x, s) / h_s; - temp *= -1.0 / h_s; - return temp; + double rho = length_m / angle_m; + double S_0 = getFringeDeriv(0, 0); + double y = getFringeDeriv(0, s) / (S_0 * rho); + double func = 0.0; + std::vector<double> fringeDerivatives; + for (std::size_t j = 0; + j <= recursion_VarRadius_m.at(n).getMaxSDerivatives(); + j++) { + fringeDerivatives.push_back(getFringeDeriv(j, s) / (S_0 * rho)); + } + for (std::size_t i = 0; + i <= recursion_VarRadius_m.at(n).getMaxXDerivatives(); + i++) { + double temp = 0.0; + for (std::size_t j = 0; + j <= recursion_VarRadius_m.at(n).getMaxSDerivatives(); + j++) { + temp += recursion_VarRadius_m.at(n) + .evaluatePolynomial(x, y, i, j, fringeDerivatives) + * fringeDerivatives.at(j); + } + func += temp * getTransDeriv(i, x); + } + func *= gsl_sf_pow_int(-1.0, n) * S_0 * rho; + return func; } -} - - +} + inline void MultipoleT::initialise(PartBunchBase<double, 3>* bunch, double &startField, double &endField) { RefPartBunch_m = bunch; + planarArcGeometry_m.setElementLength(2 * boundingBoxLength_m); + planarArcGeometry_m.setCurvature(angle_m / length_m); } inline diff --git a/src/Classic/AbsBeamline/MultipoleT.h b/src/Classic/AbsBeamline/MultipoleT.h index 1b4075243..f24b3c7ac 100644 --- a/src/Classic/AbsBeamline/MultipoleT.h +++ b/src/Classic/AbsBeamline/MultipoleT.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Titus Dascalu + * Copyright (c) 2018, Martin Duy Tat * All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -37,7 +38,7 @@ * --------------------------------------------------------------------- * * Class category: AbsBeamline \n - * $Author: Titus Dascalu, Chris Rogers + * $Author: Titus Dascalu, Martin Duy Tat, Chris Rogers * * --------------------------------------------------------------------- * @@ -80,354 +81,273 @@ #include "Algorithms/Vektor.h" #include "AbsBeamline/Component.h" #include "AbsBeamline/BeamlineVisitor.h" +#include "AbsBeamline/MultipoleTFunctions/RecursionRelation.h" +#include "AbsBeamline/MultipoleTFunctions/RecursionRelationTwo.h" #include "gsl/gsl_sf.h" #include <vector> class MultipoleT: public Component { - public: - /** Constructor: &name -> user-defined name */ +public: + /** Constructor + * \param name -> User-defined name + */ explicit MultipoleT(const std::string &name); - /** Copy constructor */ MultipoleT(const MultipoleT &right); - /** Destructor */ ~MultipoleT(); - /** Inheritable copy constructor */ ElementBase* clone() const; - - /** Return a dummy (0.) field value */ + /** Return a dummy field value */ EMField &getField(); - - /** Return a dummy (0.) field value */ + /** Return a dummy field value */ const EMField &getField() const; - /** Not implemented */ void getDimensions(double &zBegin, double &zEnd) const; - /** Calculate the field at some arbitrary position - * - * \param R -> position in the local coordinate system of the multipole - * \param P -> not used - * \param t -> time at which the field is to be calculated - * \param E -> calculated electric field - always 0 (no E-field) - * \param B -> calculated magnetic field - * \returns true if particle is outside the field map, else false + * If particle is outside field map true is returned, + * otherwise false is returned + * \param R -> Position in the lab coordinate system of the multipole + * \param P -> Not used + * \param t -> Time at which the field is to be calculated + * \param E -> Calculated electric field - always 0 (no E-field) + * \param B -> Calculated magnetic field */ bool apply(const Vector_t &R, const Vector_t &P, const double &t, Vector_t &E, Vector_t &B); - /** Calculate the field at the position of the ith particle - * - * \param i -> index of the particle event; field is calculated at this - * position - * \param t -> time at which the field is to be calculated - * \param E -> calculated electric field - always 0 (no E-field) - * \param B -> calculated magnetic field - * \returns true if particle is outside the field map + * \param i -> Index of the particle event; field is calculated at this + * position + * If particle is outside field map true is returned, + * otherwise false is returned + * \param t -> Time at which the field is to be calculated + * \param E -> Calculated electric field - always 0 (no E-field) + * \param B -> Calculated magnetic field */ bool apply(const size_t &i, const double &t, Vector_t &E, Vector_t &B); - - /** Initialise the MultipoleT - * - * \param -> bunch the global bunch object - * \param -> startField not used - * \param -> endField not used - */ - void initialise(PartBunchBase<double, 3>*, double &startField, double &endField); - - /** Finalise the MultipoleT - sets bunch to NULL */ + /** Initialise the MultipoleT + * \param bunch -> Bunch the global bunch object + * \param startField -> Not used + * \param endField -> Not used + */ + void initialise(PartBunchBase<double, 3>*, + double &startField, + double &endField); + /** Finalise the MultipoleT - sets bunch to NULL */ void finalise(); - - /** Return true if dipole component not zero */ + /** Return true if dipole component not zero */ bool bends() const; - /** Return the cell geometry */ PlanarArcGeometry& getGeometry(); - /** Return the cell geometry */ const PlanarArcGeometry& getGeometry() const; - /** Accept a beamline visitor */ void accept(BeamlineVisitor& visitor) const; - /** Get the dipole constant B_0 */ double getDipoleConstant() const; - /** Set the dipole constant B_0 */ void setDipoleConstant(double B0); - - /** Get the number of terms used in calculation of field components - * max power of z in Bz is 2 * maxOrder - */ - unsigned int getMaxOrder() const; - - /** Set the number of terms used in calculation of field components - * max power of z in Bz is 2 * maxOrder - */ - void setMaxOrder(unsigned int maxOrder); - - /** Get the maximum order in the given transverse profile - * -> highest power in given mid-plane field expansion - */ - unsigned int getTransMaxOrder() const; - + /** Get the number of terms used in calculation of field components */ + std::size_t getMaxOrder() const; + /** Set the number of terms used in calculation of field components \n + * Maximum power of z in Bz is 2 * maxOrder_m \n + * \param maxOrder -> Number of terms in expansion in z + */ + void setMaxOrder(std::size_t maxOrder); + /** Get highest power of x in polynomial expansions */ + std::size_t getMaxXOrder() const; + /** Set the number of terms used in polynomial expansions + * \param maxXOrder -> Number of terms in expansion in z + */ + void setMaxXOrder(std::size_t maxXOrder); + /** Get the maximum order in the given transverse profile */ + std::size_t getTransMaxOrder() const; /** Set the maximum order in the given transverse profile - * -> highest power in given mid-plane field expansion - */ - void setTransMaxOrder(unsigned int transMaxOrder); - + * \param transMaxOrder -> Highest power of x in field expansion + */ + void setTransMaxOrder(std::size_t transMaxOrder); /** Set transverse profile T(x) - * * T(x) = B_0 + B1 x + B2 x^2 + B3 x^3 + ... - * n -> the order of the term (d^n/dx^n) to be set - * dTn -> value of n-th derivative + * \param n -> Order of the term (d^n/dx^n) to be set + * \param Bn -> Value of transverse profile coefficient */ - void setTransProfile(unsigned int n, double Bn); - - /** Get transverse profile n-th term */ + void setTransProfile(std::size_t n, double Bn); + /** Get transverse profile + * \param n -> Power of x + */ double getTransProfile(int n) const; - - /** Get all term of transverse profile */ + /** Get all terms of transverse profile */ std::vector<double> getTransProfile() const; - - /** Set fringe field model - * - * Tanh model used here - * @f[ 1/2 * \left [tanh \left( \frac{s + s_0}{\lambda_{left}} \right) - * - tanh \left( \frac{s - s_0}{\lambda_{right}} \right) \right] @f] - * s0 -> centre field length and - * @f$ \lambda_{left} @f$ -> left end field length - * @f$ \lambda_{right} @f$ -> right end field length - * max_index -> (default 10) used to set up for differentiation - cannot - * calculate higher differentials than exist in max_index (this is always - * set to be 1 + twice the maxOrder_m when setting the fringe field) - * return true after fringe field is set - */ + /** Set fringe field model \n + * Tanh model used here \n + * @f[ 1/2 * \left [tanh \left( \frac{s + s_0}{\lambda_{left}} \right) + * - tanh \left( \frac{s - s_0}{\lambda_{right}} \right) \right] @f] + * \param s0 -> Centre field length + * \param \lambda_{left} -> Left end field length + * \param \lambda_{right} -> Right end field length + */ bool setFringeField(double s0, double lambda_left, double lambda_right); - /** Return vector of 2 doubles * [left fringe length, right fringelength] */ std::vector<double> getFringeLength() const; - /** Set the bending angle of the magnet */ void setBendAngle(double angle); - /** Get the bending angle of the magnet */ double getBendAngle() const; - - /** Set the entrance angle */ + /** Set the entrance angle + * \param entranceAngle -> Entrance angle + */ void setEntranceAngle(double entranceAngle); - /** Get the entrance angle */ double getEntranceAngle() const; - - /** Get the bending radius - * not used - * when needed radius is found from length_m / angle_m + /** Get the bending radius \n + * Not used, when needed radius is found from length_m / angle_m */ double getBendRadius() const; - - /** Set the length of the magnet - * if straight-> actual length - * if curved -> arc length + /** Set the length of the magnet \n + * If straight-> Actual length \n + * If curved -> Arc length \n */ void setLength(double length); - /** Get the length of the magnet */ double getLength() const; - - /** not used */ + /** Not used */ double getChordLength() const; - /** Set the aperture dimensions - * this element only supports a rectangular aperture + * This element only supports a rectangular aperture + * \param vertAp -> Vertical aperture length + * \param horizAp -> Horisontal aperture length */ void setAperture(double vertAp, double horizAp); - /** Get the aperture dimensions - * returns a vector of 2 doubles + * Returns a vector of 2 doubles */ std::vector<double> getAperture() const; - - /** Set the angle of rotation of the magnet around its axis - * -> enables to make skew components - */ + /** Set the angle of rotation of the magnet around its axis \n + * To make skew components + * \param rot -> Angle of rotation + */ void setRotation(double rot); - /** Get the angle of rotation of the magnet around its axis */ double getRotation() const; - /** Set variable radius flag to true */ void setVarRadius(); - /** Get the value of variableRadius_m */ bool getVarRadius() const; + /** Get distance between centre of magnet and entrance */ + double getBoundingBoxLength() const; + /** Set distance between centre of magnet and enctrance + * \param boundingBoxLength -> Distance between centre and entrance + */ + void setBoundingBoxLength(const double &boundingBoxLength); - /** Set the step used in changing coord systems for variable radius - * -> units used: mm - * -> default: 50 - * -> has a considerable effect on tracking time - */ - void setVarStep(double step); - - /** Get the step used in changing coord system for variable radius */ - double getVarStep() const; - - private: +private: MultipoleT operator=(const MultipoleT& rhs); - // End fields - endfieldmodel::Tanh fringeField_l; // left - endfieldmodel::Tanh fringeField_r; // right - - /** Field expansion parameters */ - - // number of terms used in calculating field components - unsigned int maxOrder_m = 0; - - // highest power in given mid-plane field - unsigned int transMaxOrder_m = 0; - + endfieldmodel::Tanh fringeField_l; // Left + endfieldmodel::Tanh fringeField_r; // Right + /** Field expansion parameters \n + * Number of terms in z expansion used in calculating field components + */ + std::size_t maxOrder_m = 0; + /** Highest order of polynomial expansions in x */ + std::size_t maxOrderX_m = 0; + /** Objects for storing differential operator acting on Fn */ + std::vector<polynomial::RecursionRelationTwo> recursion_VarRadius_m; + std::vector<polynomial::RecursionRelation> recursion_ConstRadius_m; + /** Highest power in given mid-plane field */ + std::size_t transMaxOrder_m = 0; + /** List of transverse profile coefficients */ std::vector<double> transProfile_m; - - // Geometry + /** Geometry */ PlanarArcGeometry planarArcGeometry_m; - /** Rotate frame for skew elements - * consecutive rotations: - * 1st -> about central axis - * 2nd -> azymuthal rotation - */ + * Consecutive rotations: + * 1st -> about central axis + * 2nd -> azimuthal rotation + * \param R -> Vector to be rotated + */ Vector_t rotateFrame(const Vector_t &R); - - /** Inverse of the 1st rotation in rotateFrame() method - * -> used to rotate B field back to global coord system - */ + /** Inverse of the 1st rotation in rotateFrame() method \n + * Used to rotate B field back to global coordinate system + */ Vector_t rotateFrameInverse(Vector_t &B); - - /** Transform to Frenet-Serret coordinates for sector magnets - * -> if the radius is variable, the coord system is rotated - * step by step along the reference trajectory - * -> step size is set by setVarStep() method - */ + /** Transform to Frenet-Serret coordinates for sector magnets */ Vector_t transformCoords(const Vector_t &R); - - // Step size for changing coordinates along trajectory - // when radius is variable - double varStep_m; - - // Magnet parameters + /** Magnet parameters */ double length_m; double angle_m; double entranceAngle_m; double rotation_m; - - // Variable radius flag; + /** Variable radius flag */ bool variableRadius_m; - - // Get field component methods + /** Distance between centre of magnet and entrance */ + double boundingBoxLength_m; + /** Get field component methods + */ double getBx (const Vector_t &R); double getBz (const Vector_t &R); double getBs (const Vector_t &R); - - // Assume rectangular aperture with dimensions given by + /** Assume rectangular aperture with these dimensions */ double verticalApert_m; double horizApert_m; - - // Not implemented + /** Not implemented */ BMultipoleField dummy; - - // Returns the value of the fringe field n-th derivative at s + /** Returns the value of the fringe field n-th derivative at s + * \param n -> nth derivative + * \param s -> Coordinate s + */ double getFringeDeriv(int n, double s); - - // Returns the value of the transverse field n-th derivative at x - double getTransDeriv(unsigned int n, double x); - - // Tests if inside the magnet + /** Returns the value of the transverse field n-th derivative at x + * \param n -> nth derivative + * \param x -> Coordinate x + */ + double getTransDeriv(std::size_t n, double x); + /** Tests if inside the magnet + * \param R -> Coordinate vector + */ bool insideAperture(const Vector_t &R); - + /** Radius of curvature \n + * If radius of curvature is infinite, -1 is returned \n + * If radius is constant, then \n + * @f$ \rho(s) = length_m / angle_m @f$ \n + * If radius is variable, then + * @f$ \rho(s) = rho(0) * S(0) / S(s) @f$ + * where S(s) is the fringe field + * \param s -> Coordinate s + */ double getRadius(double s); - /** The radius is calculated to be proportional to the field on the - * refrence trajectory (central axis of magnet) - * @f$ \rho (s) = \rho(0) * S(0) / S(s) @f$ - * -> returns -1 if radius is infinite - */ - - double getRadiusFirstDeriv(double s); - /** @return the derivative of the function which describes the radius - * as a function of s; - */ - - double getRadiusSecDeriv(double s); - /** @return The second derivative of radius function - */ - + /** Returns the scale factor @f$ h_s = 1 + x / \rho(s) @f$ + * \param x -> Coordinate x + * \param s -> Coordinate s + */ double getScaleFactor(double x, double s); - /** Returns the scale factor @f$ h_s = [(1 + x / \rho)^2 + - * (d \rho / ds)^2]^0.5 @f$ - * rho -> radius - * used in field expansion, comes from Laplacian when curvature is variable - * if radius is fixed returns @f$ h_s = 1 + x / \rho @f$ - */ - - double getScaleFactorDerivX(double x, double s); - /** Helper function to bunch terms together - * -> returns the partial deriv of the scale factor wrt. x - * -> @f$ \partial_x h_s = (1 + x / \rho) / (\rho * h_s ^ 0.5) @f$ - */ - - double getScaleFactorDerivS(double x, double s); - /** Helper function to bunch terms together - * -> returns @f$ \partial_s h_s @f$ - * -> @f$ = (h_s)^(-1/2) * \partial_s \rho * - * [-(1+x/\rho)*x/\rho^2 + {\partial_x}^2 \rho] @f$ - */ - - double getFnDerivX(unsigned int n, double x, double s); - /** Returns the partial derivative of f_n(x, s) wrt x - * -> numerical differentiation - * -> 5-points formula - * -> error of order stepSize ^ 4 - */ - - double getFnDerivS(unsigned int n, double x, double s); - /** Returns the partial derivative of f_n(x, s) wrt s - * -> numerical differentiation - * -> 5-points formula - * -> error of order stepSize ^ 4 - */ - - double getFnSecDerivX(unsigned int n, double x, double s); - /** Returns the second partial derivative of f_n(x, s) wrt x - * -> numerical differentiation - * -> 5-points formula - * -> error of order stepSize ^ 4 - */ - - double getFnSecDerivS(unsigned int n, double x, double s); - /** Returns the second partial derivative of f_n(x, s) wrt s - * -> numerical differentiation - * -> 5-points formula - * -> error of order stepSize ^ 4 - */ - - double getFn(unsigned int n, double x, double s); - /** Returns the function @f$ f_n(x, s) @f$ for curved geometry - * -> based on recursion - */ - + /** Calculate partial derivative of fn wrt x using a 5-point + * finite difference formula \n + * Error of order stepSize^4 + * \param n -> nth derivative + * \param x -> Coordinate x + * \param s -> Coordinate s + */ + double getFnDerivX(std::size_t n, double x, double s); + /** Calculate partial derivative of fn wrt s using a 5-point + * finite difference formuln + * Error of order stepSize^4 + * \param n -> nth derivative + * \param x -> Coordinate x + * \param s -> Coordinate s + */ + double getFnDerivS(std::size_t n, double x, double s); + /** Calculate fn(x, s) by expanding the differential operator + * (from Laplacian and scalar potential) in terms of polynomials + * \param n -> nth derivative + * \param x -> Coordinate x + * \param s -> Coordinate s + */ + double getFn(std::size_t n, double x, double s); }; -inline - void MultipoleT::setVarStep(double step) { - varStep_m = step; -} -inline - double MultipoleT::getVarStep() const { - return varStep_m; -} + inline void MultipoleT::setVarRadius() { variableRadius_m = true; @@ -457,19 +377,25 @@ inline return transProfile_m[0]; } inline - unsigned int MultipoleT::getMaxOrder() const { + std::size_t MultipoleT::getMaxOrder() const { return maxOrder_m; } + inline - void MultipoleT::setMaxOrder(unsigned int maxOrder) { - maxOrder_m = maxOrder; + std::size_t MultipoleT::getMaxXOrder() const { + return maxOrderX_m; } + inline - unsigned int MultipoleT::getTransMaxOrder() const { + void MultipoleT::setMaxXOrder(std::size_t maxOrderX) { + maxOrderX_m = maxOrderX; +} +inline + std::size_t MultipoleT::getTransMaxOrder() const { return transMaxOrder_m; } inline - void MultipoleT::setTransMaxOrder(unsigned int transMaxOrder) { + void MultipoleT::setTransMaxOrder(std::size_t transMaxOrder) { transMaxOrder_m = transMaxOrder; transProfile_m.resize(transMaxOrder + 1, 0.); } @@ -497,5 +423,13 @@ inline double MultipoleT::getLength() const { return length_m; } +inline + double MultipoleT::getBoundingBoxLength() const { + return boundingBoxLength_m; +} +inline + void MultipoleT::setBoundingBoxLength(const double &boundingBoxLength) { + boundingBoxLength_m = boundingBoxLength; +} #endif diff --git a/src/Classic/AbsBeamline/MultipoleTBase.cpp b/src/Classic/AbsBeamline/MultipoleTBase.cpp new file mode 100644 index 000000000..a540eaffd --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTBase.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2017, Titus Dascalu + * Copyright (c) 2018, Martin Duy Tat + * 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 "MultipoleTBase.h" +#include <gsl/gsl_sf_gamma.h> +#include "AbsBeamline/MultipoleTFunctions/tanhDeriv.h" + +using namespace endfieldmodel; + +MultipoleTBase::MultipoleTBase(): + Component(), + fringeField_l(endfieldmodel::Tanh()), + fringeField_r(endfieldmodel::Tanh()), + maxOrder_m(3), + transMaxOrder_m(0), + length_m(1.0), + entranceAngle_m(0.0), + rotation_m(0.0), + boundingBoxLength_m(0.0), + verticalApert_m(0.5), + horizontalApert_m(0.5) { +} + +MultipoleTBase::MultipoleTBase(const std::string &name): + Component(name), + fringeField_l(endfieldmodel::Tanh()), + fringeField_r(endfieldmodel::Tanh()), + maxOrder_m(3), + transMaxOrder_m(0), + length_m(1.0), + entranceAngle_m(0.0), + rotation_m(0.0), + boundingBoxLength_m(0.0), + verticalApert_m(0.5), + horizontalApert_m(0.5) { +} + +MultipoleTBase::MultipoleTBase(const MultipoleTBase &right): + Component(right), + fringeField_l(right.fringeField_l), + fringeField_r(right.fringeField_r), + maxOrder_m(right.maxOrder_m), + transMaxOrder_m(right.transMaxOrder_m), + transProfile_m(right.transProfile_m), + length_m(right.length_m), + entranceAngle_m(right.entranceAngle_m), + rotation_m(right.rotation_m), + boundingBoxLength_m(right.boundingBoxLength_m), + verticalApert_m(right.verticalApert_m), + horizontalApert_m(right.horizontalApert_m), + dummy() { + RefPartBunch_m = right.RefPartBunch_m; +} + +MultipoleTBase::~MultipoleTBase() { +} + +bool MultipoleTBase::apply(const Vector_t &R, const Vector_t &P, + const double &t,Vector_t &E, Vector_t &B) { + /** Rotate coordinates around the central axis of the magnet */ + Vector_t R_prime = rotateFrame(R); + /** Go to local Frenet-Serret coordinates */ + R_prime[2] *= -1; //OPAL uses a different sign convention... + transformCoords(R_prime); + if (insideAperture(R_prime)) { + /** Calculate B-field in the local Frenet-Serret frame */ + B[0] = getBx(R_prime); + B[1] = getBz(R_prime); + B[2] = getBs(R_prime); + /** Transform B-field from local to lab coordinates */ + transformBField(B, R_prime); + B[2] *= -1; //OPAL uses a different sign convention... + return false; + } else { + for(int i = 0; i < 3; i++) { + B[i] = 0.0; + } + return true; + } +} + +Vector_t MultipoleTBase::rotateFrame(const Vector_t &R) { + Vector_t R_prime(3), R_pprime(3); + /** Apply two 2D rotation matrices to coordinate vector + * Rotate around central axis => skew fields + * Rotate azymuthaly => entrance angle + */ + // 1st rotation + R_prime[0] = R[0] * cos(rotation_m) + R[1] * sin(rotation_m); + R_prime[1] = - R[0] * sin(rotation_m) + R[1] * cos(rotation_m); + R_prime[2] = R[2]; + // 2nd rotation + R_pprime[0] = R_prime[2] * sin(entranceAngle_m) + + R_prime[0] * cos(entranceAngle_m); + R_pprime[1] = R_prime[1]; + R_pprime[2] = R_prime[2] * cos(entranceAngle_m) - + R_prime[0] * sin(entranceAngle_m); + return R_pprime; + +} + +Vector_t MultipoleTBase::rotateFrameInverse(Vector_t &B) { + /** This function represents the inverse of the rotation + * around the central axis performed by rotateFrame() method + * Used to rotate B field back to global coordinate system + */ + Vector_t B_prime(3); + B_prime[0] = B[0] * cos(rotation_m) + B[1] * sin(rotation_m); + B_prime[1] = - B[0] * sin(rotation_m) + B[1] * cos(rotation_m); + B_prime[2] = B[2]; + return B_prime; +} + +bool MultipoleTBase::setFringeField(const double &s0, + const double &lambda_l, + const double &lambda_r) { + fringeField_l.Tanh::setLambda(lambda_l); + fringeField_l.Tanh::setX0(s0); + fringeField_l.Tanh::setTanhDiffIndices(2 * maxOrder_m + 1); + fringeField_r.Tanh::setLambda(lambda_r); + fringeField_r.Tanh::setX0(s0); + fringeField_r.Tanh::setTanhDiffIndices(2 * maxOrder_m + 1); + return true; +} + +double MultipoleTBase::getBz(const Vector_t &R) { + if (fabs(getFringeDeriv(0, R[2])) < 1.0e-12) { + return 0.0; + } + double Bz = 0.0; + std::size_t n = getMaxOrder() + 1; + while (n != 0) { + n--; + Bz = Bz * R[1] * R[1] + getFn(n, R[0], R[2]) + / gsl_sf_fact(2 * n); + } + return Bz; +} + +double MultipoleTBase::getBx(const Vector_t &R) { + if (fabs(getFringeDeriv(0, R[2])) < 1.0e-12) { + return 0.0; + } + double Bx = 0.0; + std::size_t n = getMaxOrder() + 1; + while (n != 0) { + n--; + Bx = Bx * R[1] * R[1] + getFnDerivX(n, R[0], R[2]) + / gsl_sf_fact(2 * n + 1); + } + Bx *= R[1]; + return Bx; +} + +double MultipoleTBase::getBs(const Vector_t &R) { + if (fabs(getFringeDeriv(0, R[2])) < 1.0e-12) { + return 0.0; + } + double Bs = 0.0; + std::size_t n = getMaxOrder() + 1; + while (n != 0) { + n--; + Bs = Bs * R[1] * R[1] + getFnDerivS(n, R[0], R[2]) + / gsl_sf_fact(2 * n + 1); + } + Bs *= R[1] / getScaleFactor(R[0], R[2]); + return Bs; +} + +double MultipoleTBase::getFringeDeriv(const std::size_t &n, const double &s) { + if (n <= 10) { + return (fringeField_l.getTanh(s, n) - fringeField_r.getNegTanh(s, n)) + / 2; + } else { + return tanhderiv::integrate(s, + fringeField_l.Tanh::getX0(), + fringeField_l.Tanh::getLambda(), + fringeField_r.Tanh::getLambda(), + n); + } +} + +double MultipoleTBase::getTransDeriv(const std::size_t &n, const double &x) { + double func = 0.0; + std::size_t transMaxOrder = getTransMaxOrder(); + if (n > transMaxOrder) { + return func; + } + std::vector<double> temp = getTransProfile(); + for(std::size_t i = 1; i <= n; i++) { + for(std::size_t j = 0; j <= transMaxOrder; j++) { + if (j <= transMaxOrder_m - i) { + temp[j] = temp[j + 1] * (j + 1); + } else { + temp[j] = 0.0; + } + } + } + std::size_t k = transMaxOrder - n + 1; + while (k != 0) { + k--; + func = func * x + temp[k]; + } + return func; +} + +double MultipoleTBase::getFnDerivX(const std::size_t &n, + const double &x, + const double &s) { + if (n == 0) { + return getTransDeriv(1, x) * getFringeDeriv(0, s); + } + double deriv = 0.0; + double stepSize = 1e-3; + deriv += 1. * getFn(n, x - 2. * stepSize, s); + deriv += -8. * getFn(n, x - stepSize, s); + deriv += 8. * getFn(n, x + stepSize, s); + deriv += -1. * getFn(n, x + 2. * stepSize, s); + deriv /= 12 * stepSize; + return deriv; +} + +double MultipoleTBase::getFnDerivS(const std::size_t &n, + const double &x, + const double &s) { + if (n == 0) { + return getTransDeriv(0, x) * getFringeDeriv(1, s); + } + double deriv = 0.0; + double stepSize = 1e-3; + deriv += 1. * getFn(n, x, s - 2. * stepSize); + deriv += -8. * getFn(n, x, s - stepSize); + deriv += 8. * getFn(n, x, s + stepSize); + deriv += -1. * getFn(n, x, s + 2. * stepSize); + deriv /= 12 * stepSize; + return deriv; +} diff --git a/src/Classic/AbsBeamline/MultipoleTBase.h b/src/Classic/AbsBeamline/MultipoleTBase.h new file mode 100644 index 000000000..1ca42c3ce --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTBase.h @@ -0,0 +1,469 @@ +/* + * Copyright (c) 2017, Titus Dascalu + * Copyright (c) 2018, Martin Duy Tat + * 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 CLASSIC_MULTIPOLETBASE_H +#define CLASSIC_MULTIPOLETBASE_H + +/** --------------------------------------------------------------------- + * + * MultipoleTBase is a base class for a straight or curved combined \n + * function magnet (up to arbitrary multipole component) with fringe fields.\n + * + * --------------------------------------------------------------------- + * + * Class category: AbsBeamline \n + * $Author: Titus Dascalu, Martin Duy Tat, Chris Rogers + * + * --------------------------------------------------------------------- + * + * The field is obtained from the scalar potential \n + * @f[ V = f_0(x,s) z + f_1 (x,s) \frac{z^3}{3!} + f_2 (x,s) \frac{z^5}{5!} + + * ... @f] \n + * (x,z,s) -> Frenet-Serret local coordinates along the magnet \n + * z -> vertical component \n + * assume mid-plane symmetry \n + * set field on mid-plane -> @f$ B_z = f_0(x,s) = T(x) \cdot S(s) @f$ \n + * T(x) -> transverse profile; this is a polynomial describing + * the field expansion on the mid-plane inside the magnet + * (not in the fringe field); + * 1st term is the dipole strength, 2nd term is the + * quadrupole gradient * x, etc. \n + * -> when setting the magnet, one gives the multipole + * coefficients of this polynomial (i.e. dipole strength, + * quadrupole gradient, etc.) \n + * \n + * ------------- example ----------------------------------------------- \n + * Setting a combined function magnet with dipole, quadrupole and + * sextupole components: \n + * @f$ T(x) = B_0 + B_1 \cdot x + B_2 \cdot x^2 @f$\n + * user gives @f$ B_0, B_1, B_2 @f$ \n + * ------------- example end ------------------------------------------- \n + * \n + * S(s) -> fringe field \n + * recursion -> @f$ f_n (x,s) = (-1)^n \cdot \sum_{i=0}^{n} C_n^i + * \cdot T^{(2i)} \cdot S^{(2n-2i)} @f$ \n + * for curved magnets the above recursion is more complicated \n + * @f$ C_n^i @f$ -> binomial coeff; + * @f$ T^{(n)} @f$ -> n-th derivative + * + * --------------------------------------------------------------------- + */ + +#include "Algorithms/PartBunch.h" +#include "AbsBeamline/EndFieldModel/Tanh.h" +#include "Fields/BMultipoleField.h" +#include "Algorithms/Vektor.h" +#include "AbsBeamline/Component.h" +#include "AbsBeamline/BeamlineVisitor.h" +#include <vector> + +class MultipoleTBase: public Component { +public: + /** Default constructor */ + MultipoleTBase(); + /** Constructor + * \param name -> User-defined name + */ + explicit MultipoleTBase(const std::string &name); + /** Copy constructor */ + MultipoleTBase(const MultipoleTBase &right); + /** Destructor */ + ~MultipoleTBase(); + /** Return a dummy field value */ + EMField &getField(); + /** Return a dummy field value */ + const EMField &getField() const; + /** Calculate the field at some arbitrary position \n + * If particle is outside field map true is returned, + * otherwise false is returned + * \param R -> Position in the lab coordinate system of the multipole + * \param P -> Not used + * \param t -> Time at which the field is to be calculated + * \param E -> Calculated electric field - always 0 (no E-field) + * \param B -> Calculated magnetic field + */ + bool apply(const Vector_t &R, const Vector_t &P, const double &t, + Vector_t &E, Vector_t &B); + /** Calculate the field at the position of the ith particle + * \param i -> Index of the particle event; field is calculated at this + * position + * If particle is outside field map true is returned, + * otherwise false is returned + * \param t -> Time at which the field is to be calculated + * \param E -> Calculated electric field - always 0 (no E-field) + * \param B -> Calculated magnetic field + */ + bool apply(const size_t &i, const double &t, Vector_t &E, Vector_t &B); + /** Initialise the MultipoleT + * \param bunch -> Bunch the global bunch object + * \param startField -> Not used + * \param endField -> Not used + */ + void initialise(PartBunchBase<double, 3>*, + double &startField, + double &endField); + /** Finalise the MultipoleT - sets bunch to NULL */ + void finalise(); + /** Return true if dipole component not zero */ + bool bends() const; + /** Get the dipole constant B_0 */ + double getDipoleConstant() const; + /** Set the dipole constant B_0 */ + void setDipoleConstant(const double &B0); + /** Get the number of terms used in calculation of field components */ + std::size_t getMaxOrder() const; + /** Set the number of terms used in calculation of field components \n + * Maximum power of z in Bz is 2 * maxOrder_m + * \param maxOrder -> Number of terms in expansion in z + */ + virtual void setMaxOrder(const std::size_t &maxOrder); + /** Get the maximum order in the given transverse profile */ + std::size_t getTransMaxOrder() const; + /** Set the maximum order in the given transverse profile + * \param transMaxOrder -> Highest power of x in field expansion + */ + void setTransMaxOrder(const std::size_t &transMaxOrder); + /** Set transverse profile T(x) + * T(x) = B_0 + B1 x + B2 x^2 + B3 x^3 + ... + * \param n -> Order of the term (d^n/dx^n) to be set + * \param Bn -> Value of transverse profile coefficient + */ + void setTransProfile(const std::size_t &n, const double &Bn); + /** Get transverse profile + * \param n -> Power of x + */ + double getTransProfile(const std::size_t &n) const; + /** Get all terms of transverse profile */ + std::vector<double> getTransProfile() const; + /** Set fringe field model \n + * Tanh model used here \n + * @f[ 1/2 * \left [tanh \left( \frac{s + s_0}{\lambda_{left}} \right) + * - tanh \left( \frac{s - s_0}{\lambda_{right}} \right) \right] @f] + * \param s0 -> Centre field length and + * \lambda_{left} -> Left end field length + * \lambda_{right} -> Right end field length + */ + bool setFringeField(const double &s0, + const double &lambda_left, + const double &lambda_right); + /** Return vector of 2 doubles + * [left fringe length, right fringelength] + */ + std::vector<double> getFringeLength() const; + /** Set the entrance angle + * \param entranceAngle -> Entrance angle + */ + void setEntranceAngle(const double &entranceAngle); + /** Set the bending angle of the magnet */ + virtual void setBendAngle(const double &angle); + /** Get the bending angle of the magnet */ + virtual double getBendAngle() const; + /** Get the entrance angle */ + double getEntranceAngle() const; + /** Set the length of the magnet + * If straight-> Actual length + * If curved -> Arc length + */ + void setLength(const double &length); + /** Get the length of the magnet */ + double getLength() const; + /** Set the aperture dimensions \n + * This element only supports a rectangular aperture + * \param vertAp -> Vertical aperture length + * \param horizAp -> Horisontal aperture length + */ + void setAperture(const double &vertAp, const double &horizAp); + /** Get the aperture dimensions + * Returns a vector of 2 doubles + */ + std::vector<double> getAperture() const; + /** Set the angle of rotation of the magnet around its axis \n + * To make skew components + * \param rot -> Angle of rotation + */ + void setRotation(const double &rot); + /** Get the angle of rotation of the magnet around its axis */ + double getRotation() const; + /** Get distance between centre of magnet and entrance */ + double getBoundingBoxLength() const; + /** Set distance between centre of magnet and enctrance + * \param boundingBoxLength -> Distance between centre and entrance + */ + void setBoundingBoxLength(const double &boundingBoxLength); + /** Not implemented */ + virtual void getDimensions(double &zBegin, double &zEnd) const; +protected: + /** Returns the value of the fringe field n-th derivative at s + * \param n -> nth derivative + * \param s -> Coordinate s + */ + double getFringeDeriv(const std::size_t &n, const double &s); + /** Returns the value of the transverse field n-th derivative at x \n + * Transverse field is a polynomial in x, differentiation follows + * usual polynomial rules of differentiation + * \param n -> nth derivative + * \param x -> Coordinate x + */ + double getTransDeriv(const std::size_t &n, const double &x); +private: + //MultipoleTBase operator=(const MultipoleTBase &rhs); + // End fields + endfieldmodel::Tanh fringeField_l; // Left + endfieldmodel::Tanh fringeField_r; // Right + /** Number of terms in z expansion used in calculating field components */ + std::size_t maxOrder_m; + /** Highest power in given mid-plane field */ + std::size_t transMaxOrder_m = 0; + /** List of transverse profile coefficients */ + std::vector<double> transProfile_m; + /** Rotate frame for skew elements \n + * Consecutive rotations: + * 1st -> about central axis + * 2nd -> azimuthal rotation + * \param R -> Vector to be rotated + */ + Vector_t rotateFrame(const Vector_t &R); + /** Inverse of the 1st rotation in rotateFrame() method \n + * Used to rotate B field back to global coordinate system + */ + Vector_t rotateFrameInverse(Vector_t &B); + /** Transform to Frenet-Serret coordinates for sector magnets */ + virtual void transformCoords(Vector_t &R) = 0; + /** Transform B-field from Frenet-Serret coordinates to lab coordinates */ + virtual void transformBField(Vector_t &B, const Vector_t &R) = 0; + /** Magnet parameters */ + double length_m; + double entranceAngle_m; + double rotation_m; + /** Distance between centre of magnet and entrance */ + double boundingBoxLength_m; + /** Returns the radial component of the field \n + * Returns zero far outside fringe field + * @f$ Bx = sum_n z^(2n+1) / (2n+1)! * \partial_x f_n @f$ + */ + virtual double getBx (const Vector_t &R); + /** Returns the vertical field component \n + * Returns zero far outside fringe field + * @f$ Bz = sum_n f_n * z^(2n) / (2n)! @f$ + */ + double getBz (const Vector_t &R); + /** Returns the component of the field along the central axis \n + * Returns zero far outside fringe field + * @f$ Bs = sum_n z^(2n+1) / (2n+1)! \partial_s f_n / h_s @f$ + */ + virtual double getBs (const Vector_t &R); + /** Assume rectangular aperture with these dimensions */ + double verticalApert_m; + double horizontalApert_m; + /** Not implemented */ + BMultipoleField dummy; + /** Tests if inside the magnet + * \param R -> Coordinate vector + */ + bool insideAperture(const Vector_t &R); + /** Radius of curvature + * \param s -> Coordinate s + */ + virtual double getRadius(const double &s) = 0; + /** Returns the scale factor @f$ h_s = 1 + x / \rho(s) @f$ + * \param x -> Coordinate x + * \param s -> Coordinate s + */ + virtual double getScaleFactor(const double &x, const double &s) = 0; + /** Calculate partial derivative of fn wrt x using a 5-point + * finite difference formula + * Error of order stepSize^4 + * \param n -> nth derivative + * \param x -> Coordinate x + * \param s -> Coordinate s + */ + double getFnDerivX(const std::size_t &n, + const double &x, + const double &s); + /** Calculate partial derivative of fn wrt s using a 5-point + * finite difference formula + * Error of order stepSize^4 + * \param n -> nth derivative + * \param x -> Coordinate x + * \param s -> Coordinate s + */ + double getFnDerivS(const std::size_t &n, + const double &x, + const double &s); + /** Calculate fn(x, s) by expanding the differential operator + * (from Laplacian and scalar potential) in terms of polynomials + * \param n -> nth derivative + * \param x -> Coordinate x + * \param s -> Coordinate s + */ + virtual double getFn(const std::size_t &n, + const double &x, + const double &s) = 0; +}; + +inline + void MultipoleTBase::finalise() { + RefPartBunch_m = NULL; +} +inline + bool MultipoleTBase::apply(const size_t &i, const double &t, + Vector_t &E, Vector_t &B) { + return apply(RefPartBunch_m->R[i], RefPartBunch_m->P[i], t, E, B); +} +inline + void MultipoleTBase::setBendAngle(const double &angle) { +} +inline + double MultipoleTBase::getBendAngle() const { + return 0.0; +} +inline + void MultipoleTBase::setEntranceAngle(const double &entranceAngle) { + entranceAngle_m = entranceAngle; +} +inline + bool MultipoleTBase::insideAperture(const Vector_t &R) { + return (fabs(R[1]) <= (verticalApert_m / 2.0) && + fabs(R[0]) <= (horizontalApert_m / 2.0)); +} +inline + double MultipoleTBase::getEntranceAngle() const { + return entranceAngle_m; +} +inline + double MultipoleTBase::getTransProfile(const std::size_t &n) const { + return transProfile_m[n]; +} +inline + std::vector<double> MultipoleTBase::getTransProfile() const { + return transProfile_m; +} +inline + double MultipoleTBase::getDipoleConstant() const { + return transProfile_m[0]; +} +inline + void MultipoleTBase::setMaxOrder(const std::size_t &maxOrder) { + maxOrder_m = maxOrder; +} +inline + std::size_t MultipoleTBase::getMaxOrder() const { + return maxOrder_m; +} +inline + std::size_t MultipoleTBase::getTransMaxOrder() const { + return transMaxOrder_m; +} +inline + void MultipoleTBase::setTransMaxOrder(const std::size_t &transMaxOrder) { + transMaxOrder_m = transMaxOrder; + transProfile_m.resize(transMaxOrder + 1, 0.); +} +inline + double MultipoleTBase::getRotation() const { + return rotation_m; +} +inline + void MultipoleTBase::setRotation(const double &rot) { + rotation_m = rot; +} +inline + void MultipoleTBase::setLength(const double &length) { + length_m = std::abs(length); +} +inline + double MultipoleTBase::getLength() const { + return length_m; +} +inline + double MultipoleTBase::getBoundingBoxLength() const { + return boundingBoxLength_m; +} +inline + void MultipoleTBase::setBoundingBoxLength(const double &boundingBoxLength) { + boundingBoxLength_m = boundingBoxLength; +} +inline + void MultipoleTBase::setTransProfile(const std::size_t &n, + const double &dTn) { + if (n > transMaxOrder_m) { + transMaxOrder_m = n; + transProfile_m.resize(n + 1, 0.0); + } + transProfile_m[n] = dTn; +} +inline + void MultipoleTBase::setDipoleConstant(const double &B0) { + if (transMaxOrder_m < 1) { + transProfile_m.resize(1, 0.); + } + transProfile_m[0] = B0; +} +inline + void MultipoleTBase::setAperture(const double &vertAp, + const double &horizAp) { + verticalApert_m = vertAp; + horizontalApert_m = horizAp; +} +inline + std::vector<double> MultipoleTBase::getAperture() const { + std::vector<double> temp(2, 0.0); + temp[0] = verticalApert_m; + temp[1] = horizontalApert_m; + return temp; +} +inline + std::vector<double> MultipoleTBase::getFringeLength() const { + std::vector<double> temp(2, 0.0); + temp[0] = fringeField_l.getLambda(); + temp[1] = fringeField_r.getLambda(); + return temp; +} +inline + void MultipoleTBase::initialise(PartBunchBase<double, 3>* bunch, + double &startField, + double &endField) { +} +inline + bool MultipoleTBase::bends() const { + return transProfile_m[0] != 0; +} +inline + EMField &MultipoleTBase::getField() { + return dummy; +} +inline + const EMField &MultipoleTBase::getField() const { + return dummy; +} +inline + void MultipoleTBase::getDimensions(double &zBegin, double &zEnd) const { +} + +#endif diff --git a/src/Classic/AbsBeamline/MultipoleTCurvedConstRadius.cpp b/src/Classic/AbsBeamline/MultipoleTCurvedConstRadius.cpp new file mode 100644 index 000000000..35482105e --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTCurvedConstRadius.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2017, Titus Dascalu + * Copyright (c) 2018, Martin Duy Tat + * 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 "MultipoleTCurvedConstRadius.h" +#include "gsl/gsl_sf_pow_int.h" + +using namespace endfieldmodel; + +MultipoleTCurvedConstRadius::MultipoleTCurvedConstRadius( + const std::string &name): + MultipoleTBase(name), + maxOrderX_m(10), + planarArcGeometry_m(1.0, 1.0), + angle_m(0.0) { +} + +MultipoleTCurvedConstRadius::MultipoleTCurvedConstRadius( + const MultipoleTCurvedConstRadius &right): + MultipoleTBase(right), + maxOrderX_m(right.maxOrderX_m), + recursion_m(right.recursion_m), + planarArcGeometry_m(right.planarArcGeometry_m), + angle_m(right.angle_m) { + RefPartBunch_m = right.RefPartBunch_m; +} + +MultipoleTCurvedConstRadius::~MultipoleTCurvedConstRadius() { +} + +ElementBase* MultipoleTCurvedConstRadius::clone() const { + return new MultipoleTCurvedConstRadius(*this); +} + +void MultipoleTCurvedConstRadius::transformCoords(Vector_t &R) { + double radius = getLength() / angle_m; + double alpha = atan(R[2] / (R[0] + radius )); + if (alpha != 0.0 && angle_m != 0.0) { + R[0] = R[2] / sin(alpha) - radius; + R[2] = radius * alpha;// + getBoundingBoxLength(); + } else { + //R[2] = R[2] + getBoundingBoxLength(); + } +} + +void MultipoleTCurvedConstRadius::transformBField(Vector_t &B, + const Vector_t &R) { + double theta = R[2] * angle_m / getLength(); + double Bx = B[0]; + double Bs = B[2]; + B[0] = Bx * cos(theta) - Bs * sin(theta); + B[2] = Bx * sin(theta) + Bs * cos(theta); +} + +void MultipoleTCurvedConstRadius::setMaxOrder(const std::size_t &maxOrder) { + MultipoleTBase::setMaxOrder(maxOrder); + std::size_t N = recursion_m.size(); + while (maxOrder >= N) { + polynomial::RecursionRelation r(N, 2 * (N + maxOrderX_m + 1)); + r.resizeX(getTransMaxOrder()); + r.truncate(maxOrderX_m); + recursion_m.push_back(r); + N = recursion_m.size(); + } +} + +double MultipoleTCurvedConstRadius::getRadius(const double &s) { + if (angle_m == 0.0) { + return -1.0; + } else { + return getLength() / angle_m; + } +} + +double MultipoleTCurvedConstRadius::getScaleFactor(const double &x, + const double &s) { + return (1 + x * angle_m / getLength()); +} + +double MultipoleTCurvedConstRadius::getFn(const std::size_t &n, + const double &x, + const double &s) { + if (n == 0) { + return getTransDeriv(0, x) * getFringeDeriv(0, s); + } + double rho = getLength() / angle_m; + double func = 0.0; + for (std::size_t j = 0; + j <= recursion_m.at(n).getMaxSDerivatives(); + j++) { + double FringeDerivj = getFringeDeriv(2 * j, s); + for (std::size_t i = 0; + i <= recursion_m.at(n).getMaxXDerivatives(); + i++) { + if (recursion_m.at(n).isPolynomialZero(i, j)) { + continue; + } + func += (recursion_m.at(n).evaluatePolynomial(x / rho, i, j) + * getTransDeriv(i, x) * FringeDerivj) + / gsl_sf_pow_int(rho, 2 * n - i - 2 * j); + } + } + func *= gsl_sf_pow_int(-1.0, n); + return func; +} + diff --git a/src/Classic/AbsBeamline/MultipoleTCurvedConstRadius.h b/src/Classic/AbsBeamline/MultipoleTCurvedConstRadius.h new file mode 100644 index 000000000..b62ad836f --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTCurvedConstRadius.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2017, Titus Dascalu + * Copyright (c) 2018, Martin Duy Tat + * 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 CLASSIC_MULTIPOLET_CURVED_CONST_RADIUS_H +#define CLASSIC_MULTIPOLET_CURVED_CONST_RADIUS_H + +/** --------------------------------------------------------------------- + * + * MultipoleT defines a curved combined function magnet with constant radius\n + * of curvature, (up to arbitrary multipole component) with fringe fields + * + * --------------------------------------------------------------------- + * + * Class category: AbsBeamline \n + * $Author: Titus Dascalu, Martin Duy Tat, Chris Rogers + * + * --------------------------------------------------------------------- + * + * The field is obtained from the scalar potential \n + * @f[ V = f_0(x,s) z + f_1 (x,s) \frac{z^3}{3!} + f_2 (x,s) \frac{z^5}{5!} + * + ... @f] \n + * (x,z,s) -> Frenet-Serret local coordinates along the magnet \n + * z -> vertical component \n + * assume mid-plane symmetry \n + * set field on mid-plane -> @f$ B_z = f_0(x,s) = T(x) \cdot S(s) @f$ \n + * T(x) -> transverse profile; this is a polynomial describing + * the field expansion on the mid-plane inside the magnet + * (not in the fringe field); + * 1st term is the dipole strength, 2nd term is the + * quadrupole gradient * x, etc. \n + * -> when setting the magnet, one gives the multipole + * coefficients of this polynomial (i.e. dipole strength, + * quadrupole gradient, etc.) \n + * \n + * ------------- example ----------------------------------------------- \n + * Setting a combined function magnet with dipole, quadrupole and + * sextupole components: \n + * @f$ T(x) = B_0 + B_1 \cdot x + B_2 \cdot x^2 @f$\n + * user gives @f$ B_0, B_1, B_2 @f$ \n + * ------------- example end ------------------------------------------- \n + * \n + * S(s) -> fringe field \n + * recursion -> @f$ f_n (x,s) = (-1)^n \cdot \sum_{i=0}^{n} C_n^i + * \cdot T^{(2i)} \cdot S^{(2n-2i)} @f$ \n + * for curved magnets the above recursion is more complicated \n + * @f$ C_n^i @f$ -> binomial coeff; + * @f$ T^{(n)} @f$ -> n-th derivative + * + * --------------------------------------------------------------------- + */ + +#include "BeamlineGeometry/PlanarArcGeometry.h" +#include "AbsBeamline/MultipoleTBase.h" +#include "AbsBeamline/MultipoleTFunctions/RecursionRelation.h" +#include <vector> + +class MultipoleTCurvedConstRadius: public MultipoleTBase { +public: + /** Constructor + * \param name -> User-defined name + */ + explicit MultipoleTCurvedConstRadius(const std::string &name); + /** Copy constructor */ + MultipoleTCurvedConstRadius(const MultipoleTCurvedConstRadius &right); + /** Destructor */ + ~MultipoleTCurvedConstRadius(); + /** Inheritable copy constructor */ + virtual ElementBase* clone() const; + /** Accept a beamline visitor */ + void accept(BeamlineVisitor &visitor) const; + /** Return the cell geometry */ + PlanarArcGeometry& getGeometry(); + /** Return the cell geometry */ + const PlanarArcGeometry& getGeometry() const; + /** Set the number of terms used in calculation of field components \n + * Maximum power of z in Bz is 2 * maxOrder_m + * \param maxOrder -> Number of terms in expansion in z + */ + virtual void setMaxOrder(const std::size_t &maxOrder); + /** Get highest power of x in polynomial expansions */ + std::size_t getMaxXOrder() const; + /** Set the number of terms used in polynomial expansions + * \param maxXOrder -> Number of terms in expansion in z + */ + void setMaxXOrder(const std::size_t &maxXOrder); + /** Set the bending angle of the magnet */ + virtual void setBendAngle(const double &angle); + /** Get the bending angle of the magnet */ + virtual double getBendAngle() const; + /** Initialise the MultipoleT + * \param bunch -> Bunch the global bunch object + * \param startField -> Not used + * \param endField -> Not used + */ + virtual void initialise(PartBunchBase<double, 3>* bunch, + double &startField, + double &endField); +private: + MultipoleTCurvedConstRadius operator=( + const MultipoleTCurvedConstRadius &rhs); + /** Highest order of polynomial expansions in x */ + std::size_t maxOrderX_m; + /** Object for storing differential operator acting on Fn */ + std::vector<polynomial::RecursionRelation> recursion_m; + /** Geometry */ + PlanarArcGeometry planarArcGeometry_m; + /** Transform to Frenet-Serret coordinates for sector magnets */ + virtual void transformCoords(Vector_t &R); + /** Transform B-field from Frenet-Serret coordinates to lab coordinates */ + virtual void transformBField(Vector_t &B, const Vector_t &R); + double angle_m; + /** Radius of curvature \n + * If radius of curvature is infinite, -1 is returned \n + * @f$ \rho(s) = length_m / angle_m @f$ + * \param s -> Coordinate s + */ + virtual double getRadius(const double &s); + /** Returns the scale factor @f$ h_s = 1 + x / \rho @f$ + * \param x -> Coordinate x + * \param s -> Coordinate s + */ + virtual double getScaleFactor(const double &x, const double &s); + /** Calculate fn(x, s) by expanding the differential operator + * (from Laplacian and scalar potential) in terms of polynomials + * \param n -> nth derivative + * \param x -> Coordinate x + * \param s -> Coordinate s + */ + virtual double getFn(const std::size_t &n, + const double &x, + const double &s); +}; + +inline + void MultipoleTCurvedConstRadius::accept(BeamlineVisitor &visitor) const { + visitor.visitMultipoleTCurvedConstRadius(*this); +} +inline + void MultipoleTCurvedConstRadius::setMaxXOrder( + const std::size_t &maxXorder) { + maxOrderX_m = maxXorder; +} +inline + std::size_t MultipoleTCurvedConstRadius::getMaxXOrder() const { + return maxOrderX_m; +} +inline + PlanarArcGeometry& MultipoleTCurvedConstRadius::getGeometry() { + return planarArcGeometry_m; +} +inline + const PlanarArcGeometry& MultipoleTCurvedConstRadius::getGeometry() const { + return planarArcGeometry_m; +} +inline + void MultipoleTCurvedConstRadius::setBendAngle(const double &angle) { + angle_m = angle; +} +inline + double MultipoleTCurvedConstRadius::getBendAngle() const { + return angle_m; +} +inline + void MultipoleTCurvedConstRadius::initialise(PartBunchBase<double, 3>* bunch, + double &startField, + double &endField) { + RefPartBunch_m = bunch; + planarArcGeometry_m.setElementLength(2 * getBoundingBoxLength()); + planarArcGeometry_m.setCurvature(angle_m / getLength()); +} + +#endif diff --git a/src/Classic/AbsBeamline/MultipoleTCurvedVarRadius.cpp b/src/Classic/AbsBeamline/MultipoleTCurvedVarRadius.cpp new file mode 100644 index 000000000..43e50b7ef --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTCurvedVarRadius.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2017, Titus Dascalu + * Copyright (c) 2018, Martin Duy Tat + * 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 <vector> +#include "gsl/gsl_sf_pow_int.h" +#include "MultipoleTCurvedVarRadius.h" +#include "AbsBeamline/MultipoleTFunctions/CoordinateTransform.h" + +using namespace endfieldmodel; + +MultipoleTCurvedVarRadius::MultipoleTCurvedVarRadius(const std::string &name): + MultipoleTBase(name), + maxOrderX_m(10), + varRadiusGeometry_m(1.0, 1.0, 1.0, 1.0, 1.0), + angle_m(0.0) { +} + +MultipoleTCurvedVarRadius::MultipoleTCurvedVarRadius( + const MultipoleTCurvedVarRadius &right): + MultipoleTBase(right), + maxOrderX_m(right.maxOrderX_m), + recursion_m(right.recursion_m), + varRadiusGeometry_m(right.varRadiusGeometry_m), + angle_m(right.angle_m) { + RefPartBunch_m = right.RefPartBunch_m; +} + + +MultipoleTCurvedVarRadius::~MultipoleTCurvedVarRadius() { +} + +ElementBase* MultipoleTCurvedVarRadius::clone() const { + return new MultipoleTCurvedVarRadius(*this); +} + +void MultipoleTCurvedVarRadius::transformCoords(Vector_t &R) { + std::vector<double> fringeLength = getFringeLength(); + coordinatetransform::CoordinateTransform t(R[0], R[1], R[2], + getLength() / 2, + fringeLength[0], + fringeLength[1], + (getLength() / angle_m)); + std::vector<double> r = t.getTransformation(); + R[0] = r[0]; + R[1] = r[1]; + R[2] = r[2]; +} + +void MultipoleTCurvedVarRadius::transformBField(Vector_t &B, + const Vector_t &R) { + double length = getLength(); + std::vector<double> fringeLength = getFringeLength(); + double prefactor = (length / angle_m) * + (tanh((length / 2) / fringeLength[0]) + + tanh((length / 2) / fringeLength[1])); + double theta = fringeLength[0] * log(cosh((R[2] + + (length / 2)) / fringeLength[0])) + - fringeLength[1] * log(cosh((R[2] + - (length / 2)) / fringeLength[1])); + theta /= prefactor; + double Bx = B[0], Bs = B[2]; + B[0] = Bx * cos(theta) - Bs * sin(theta); + B[2] = Bx * sin(theta) + Bs * cos(theta); +} + +void MultipoleTCurvedVarRadius::setMaxOrder(const std::size_t &maxOrder) { + MultipoleTBase::setMaxOrder(maxOrder); + std::size_t N = recursion_m.size(); + while (maxOrder >= N) { + polynomial::RecursionRelationTwo r(N, 2 * (N + maxOrderX_m + 1)); + r.resizeX(getTransMaxOrder()); + r.truncate(maxOrderX_m); + recursion_m.push_back(r); + N = recursion_m.size(); + } +} + +double MultipoleTCurvedVarRadius::getRadius(const double &s) { + if (getFringeDeriv(0, s) > 1.0e-12 && angle_m != 0.0) { + return getLength() * getFringeDeriv(0, 0) + / (getFringeDeriv(0, s) * angle_m); + } else { + return 1e300; // Return -1 if radius is infinite + } +} + +double MultipoleTCurvedVarRadius::getScaleFactor(const double &x, + const double &s) { + return (1 + x / getRadius(s)); +} + +double MultipoleTCurvedVarRadius::getFn(const std::size_t &n, + const double &x, + const double &s) { + if (n == 0) { + return getTransDeriv(0, x) * getFringeDeriv(0, s); + } + double rho = getLength() / angle_m; + double S_0 = getFringeDeriv(0, 0); + double y = getFringeDeriv(0, s) / (S_0 * rho); + double func = 0.0; + std::vector<double> fringeDerivatives; + for (std::size_t j = 0; + j <= recursion_m.at(n).getMaxSDerivatives(); + j++) { + fringeDerivatives.push_back(getFringeDeriv(j, s) / (S_0 * rho)); + } + for (std::size_t i = 0; + i <= recursion_m.at(n).getMaxXDerivatives(); + i++) { + double temp = 0.0; + for (std::size_t j = 0; + j <= recursion_m.at(n).getMaxSDerivatives(); + j++) { + temp += recursion_m.at(n) + .evaluatePolynomial(x, y, i, j, fringeDerivatives) + * fringeDerivatives.at(j); + } + func += temp * getTransDeriv(i, x); + } + func *= gsl_sf_pow_int(-1.0, n) * S_0 * rho; + return func; +} diff --git a/src/Classic/AbsBeamline/MultipoleTCurvedVarRadius.h b/src/Classic/AbsBeamline/MultipoleTCurvedVarRadius.h new file mode 100644 index 000000000..11ab76d0b --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTCurvedVarRadius.h @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2017, Titus Dascalu + * Copyright (c) 2018, Martin Duy Tat + * 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 CLASSIC_MULTIPOLET_CURVED_VAR_RADIUS_H +#define CLASSIC_MULTIPOLET_CURVED_VAR_RADIUS_H + +/** --------------------------------------------------------------------- + * + * MultipoleTCurvedVarRadius defines a curved combined function magnet with\n + * variable radius of curvature (up to arbitrary multipole component) \n + * with fringe fields + * + * --------------------------------------------------------------------- + * + * Class category: AbsBeamline \n + * $Author: Titus Dascalu, Martin Duy Tat, Chris Rogers + * + * --------------------------------------------------------------------- + * + * The field is obtained from the scalar potential \n + * @f[ V = f_0(x,s) z + f_1 (x,s) \frac{z^3}{3!} + f_2 (x,s) \frac{z^5}{5!} + * + ... @f] \n + * (x,z,s) -> Frenet-Serret local coordinates along the magnet \n + * z -> vertical component \n + * assume mid-plane symmetry \n + * set field on mid-plane -> @f$ B_z = f_0(x,s) = T(x) \cdot S(s) @f$ \n + * T(x) -> transverse profile; this is a polynomial describing + * the field expansion on the mid-plane inside the magnet + * (not in the fringe field); + * 1st term is the dipole strength, 2nd term is the + * quadrupole gradient * x, etc. \n + * -> when setting the magnet, one gives the multipole + * coefficients of this polynomial (i.e. dipole strength, + * quadrupole gradient, etc.) \n + * \n + * ------------- example ----------------------------------------------- \n + * Setting a combined function magnet with dipole, quadrupole and + * sextupole components: \n + * @f$ T(x) = B_0 + B_1 \cdot x + B_2 \cdot x^2 @f$\n + * user gives @f$ B_0, B_1, B_2 @f$ \n + * ------------- example end ------------------------------------------- \n + * \n + * S(s) -> fringe field \n + * recursion -> @f$ f_n (x,s) = (-1)^n \cdot \sum_{i=0}^{n} C_n^i + * \cdot T^{(2i)} \cdot S^{(2n-2i)} @f$ \n + * for curved magnets the above recursion is more complicated \n + * @f$ C_n^i @f$ -> binomial coeff; + * @f$ T^{(n)} @f$ -> n-th derivative + * + * --------------------------------------------------------------------- + */ + +#include "BeamlineGeometry/VarRadiusGeometry.h" +#include "Algorithms/Vektor.h" +#include "AbsBeamline/MultipoleTBase.h" +#include "AbsBeamline/MultipoleTFunctions/RecursionRelationTwo.h" +#include <vector> + +class MultipoleTCurvedVarRadius: public MultipoleTBase { +public: + /** Constructor + * \param name -> User-defined name + */ + explicit MultipoleTCurvedVarRadius(const std::string &name); + /** Copy constructor */ + MultipoleTCurvedVarRadius(const MultipoleTCurvedVarRadius &right); + /** Destructor */ + ~MultipoleTCurvedVarRadius(); + /** Inheritable copy constructor */ + virtual ElementBase* clone() const; + /** Accept a beamline visitor */ + void accept(BeamlineVisitor &visitor) const; + /** Return the cell geometry */ + VarRadiusGeometry& getGeometry(); + /** Return the cell geometry */ + const VarRadiusGeometry& getGeometry() const; + /** Set the number of terms used in calculation of field components \n + * Maximum power of z in Bz is 2 * maxOrder_m + * \param maxOrder -> Number of terms in expansion in z + */ + virtual void setMaxOrder(const std::size_t &maxOrder); + /** Get highest power of x in polynomial expansions */ + std::size_t getMaxXOrder() const; + /** Set the number of terms used in polynomial expansions + * \param maxXOrder -> Number of terms in expansion in z + */ + void setMaxXOrder(const std::size_t &maxXOrder); + /** Set the bending angle of the magnet */ + virtual void setBendAngle(const double &angle); + /** Get the bending angle of the magnet */ + virtual double getBendAngle() const; + /** Initialise the MultipoleT + * \param bunch -> Bunch the global bunch object + * \param startField -> Not used + * \param endField -> Not used + */ + virtual void initialise(PartBunchBase<double, 3>* bunch, + double &startField, + double &endField); +private: + MultipoleTCurvedVarRadius operator=(const MultipoleTCurvedVarRadius& rhs); + /** Highest order of polynomial expansions in x */ + std::size_t maxOrderX_m; + /** Objects for storing differential operator acting on Fn */ + std::vector<polynomial::RecursionRelationTwo> recursion_m; + /** Geometry */ + VarRadiusGeometry varRadiusGeometry_m; + /** Transform to Frenet-Serret coordinates for sector magnets */ + virtual void transformCoords(Vector_t &R); + /** Transform B-field from Frenet-Serret coordinates to lab coordinates */ + virtual void transformBField(Vector_t &B, const Vector_t &R); + double angle_m; + /** Radius of curvature \n + * If radius of curvature is infinite, -1 is returned \n + * @f$ \rho(s) = rho(0) * S(0) / S(s) @f$ + * where S(s) is the fringe field + * \param s -> Coordinate s + */ + virtual double getRadius(const double &s); + /** Returns the scale factor @f$ h_s = 1 + x / \rho(s) @f$ + * \param x -> Coordinate x + * \param s -> Coordinate s + */ + virtual double getScaleFactor(const double &x, const double &s); + /** Calculate fn(x, s) by expanding the differential operator + * (from Laplacian and scalar potential) in terms of polynomials + * \param n -> nth derivative + * \param x -> Coordinate x + * \param s -> Coordinate s + */ + virtual double getFn(const std::size_t &n, + const double &x, + const double &s); +}; + +inline + void MultipoleTCurvedVarRadius::accept(BeamlineVisitor &visitor) const { + visitor.visitMultipoleTCurvedVarRadius(*this); +} +inline + void MultipoleTCurvedVarRadius::setMaxXOrder( + const std::size_t &maxXorder) { + maxOrderX_m = maxXorder; +} +inline + std::size_t MultipoleTCurvedVarRadius::getMaxXOrder() const { + return maxOrderX_m; +} +inline + VarRadiusGeometry& MultipoleTCurvedVarRadius::getGeometry() { + return varRadiusGeometry_m; +} +inline + const VarRadiusGeometry& MultipoleTCurvedVarRadius::getGeometry() const { + return varRadiusGeometry_m; +} +inline + void MultipoleTCurvedVarRadius::setBendAngle(const double &angle) { + angle_m = angle; +} +inline + double MultipoleTCurvedVarRadius::getBendAngle() const { + return angle_m; +} +inline + void MultipoleTCurvedVarRadius::initialise(PartBunchBase<double, 3>* bunch, + double &startField, + double &endField) { + RefPartBunch_m = bunch; + double length = getLength(); + varRadiusGeometry_m.setElementLength(2 * getBoundingBoxLength()); + varRadiusGeometry_m.setRadius(length / angle_m); + varRadiusGeometry_m.setS0(length / 2); + std::vector<double> fringeLength = getFringeLength(); + varRadiusGeometry_m.setLambdaLeft(fringeLength[0]); + varRadiusGeometry_m.setLambdaRight(fringeLength[1]); +} + +#endif diff --git a/src/Classic/AbsBeamline/MultipoleTFunctions/CMakeLists.txt b/src/Classic/AbsBeamline/MultipoleTFunctions/CMakeLists.txt new file mode 100644 index 000000000..fbca45eb7 --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTFunctions/CMakeLists.txt @@ -0,0 +1,34 @@ +set (_SRCS + CoordinateTransform.cpp + DifferentialOperator.cpp + DifferentialOperatorTwo.cpp + Polynomial.cpp + PolynomialSum.cpp + RecursionRelation.cpp + RecursionRelationTwo.cpp + tanhDeriv.cpp + TwoPolynomial.cpp + ) + +include_directories ( + ${CMAKE_CURRENT_SOURCE_DIR} + ) + +ADD_OPAL_SOURCES(${_SRCS}) + +set (HDRS + CoordinateTransform.h + DifferentialOperator.h + DifferentialOperatorTwo.h + Polynomial.h + PolynomialSum.h + RecursionRelation.h + RecursionRelationTwo.h + tanhDeriv.h + TwoPolynomial.h +) + +install (FILES ${HDRS} DESTINATION "${CMAKE_INSTALL_PREFIX}/include/AbsBeamline/MultipoleTFunctions/") +#Turn on gcov +#set(CMAKE_CXX_OUTPUT_EXTENSION_REPLACE 1) +#set(CMAKE_CXX_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") diff --git a/src/Classic/AbsBeamline/MultipoleTFunctions/CoordinateTransform.cpp b/src/Classic/AbsBeamline/MultipoleTFunctions/CoordinateTransform.cpp new file mode 100644 index 000000000..f7e372aa7 --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTFunctions/CoordinateTransform.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2018, Martin Duy Tat + * 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 <cmath> +#include <vector> +#include "gsl/gsl_integration.h" +#include "gsl/gsl_sf_pow_int.h" +#include "CoordinateTransform.h" + +namespace coordinatetransform { + +CoordinateTransform::CoordinateTransform(): x_m(0), z_m(0), s_m(0) { +} + +CoordinateTransform::CoordinateTransform(const double &xlab, + const double &ylab, + const double &zlab, + const double &s_0, + const double &lambdaleft, + const double &lambdaright, + const double &rho): + s_0_m(s_0), lambdaleft_m(lambdaleft), + lambdaright_m(lambdaright), rho_m(rho) { + std::vector<double> coordinates; + coordinates.push_back(xlab); + coordinates.push_back(zlab); + //transformFromEntranceCoordinates(coordinates, boundingBoxLength); + calcSCoordinate(coordinates[0], coordinates[1]); + calcXCoordinate(coordinates[0], coordinates[1]); + z_m = ylab; +} + +CoordinateTransform::CoordinateTransform(const CoordinateTransform &transform): + s_0_m(transform.s_0_m), lambdaleft_m(transform.lambdaleft_m), + lambdaright_m(transform.lambdaright_m), + rho_m(transform.rho_m), x_m(transform.x_m), + z_m(transform.z_m), s_m(transform.s_m) { +} + +CoordinateTransform::~CoordinateTransform() { +} + +CoordinateTransform& CoordinateTransform::operator= ( + const CoordinateTransform& transform) { + s_0_m = transform.s_0_m; + lambdaleft_m = transform.lambdaleft_m; + lambdaright_m = transform.lambdaright_m; + x_m = transform.x_m; + z_m = transform.z_m; + s_m = transform.s_m; + rho_m = transform.rho_m; + return *this; +} + +std::vector<double> CoordinateTransform::getTransformation() const { + std::vector<double> result; + result.push_back(x_m); + result.push_back(z_m); + result.push_back(s_m); + return result; +} + +std::vector<double> CoordinateTransform::getUnitTangentVector( + const double &s) const { + std::vector<double> result; + double prefactor = rho_m * (tanh(s_0_m / lambdaleft_m) - + tanh(-s_0_m / lambdaright_m)); + result.push_back(-sin((lambdaleft_m * log(cosh((s + s_0_m) / lambdaleft_m)) + - lambdaright_m * log(cosh((s - s_0_m) / lambdaright_m))) + / prefactor)); + result.push_back(cos((lambdaleft_m * log(cosh((s + s_0_m) / lambdaleft_m)) + - lambdaright_m * log(cosh((s - s_0_m) / lambdaright_m))) + / prefactor)); + return result; +} + +std::vector<double> CoordinateTransform::calcReferenceTrajectory( + const double &s) const { + struct myParams params = {s_0_m, lambdaleft_m, lambdaright_m, rho_m}; + gsl_function FX, FY; + FX.function = &getUnitTangentVectorX; + FY.function = &getUnitTangentVectorY; + FX.params = ¶ms; + FY.params = ¶ms; + gsl_integration_workspace *w = gsl_integration_workspace_alloc(1000); + double error = gsl_sf_pow_int(10, -10); + double *resultX = new double; + double *resultY = new double; + double *abserr = new double; + gsl_integration_qag(&FX, 0, s, 0, error, 1000, 6, w, resultX, abserr); + gsl_integration_qag(&FY, 0, s, 0, error, 1000, 6, w, resultY, abserr); + gsl_integration_workspace_free(w); + std::vector<double> result; + result.push_back(*resultX); + result.push_back(*resultY); + delete resultX; + delete resultY; + delete abserr; + return result; +} + +void CoordinateTransform::calcSCoordinate(const double &xlab, + const double &zlab) { + double s1 = -(5 * lambdaleft_m + s_0_m), s2 = (5 * lambdaright_m + s_0_m); + std::vector<double> shat1 = getUnitTangentVector(s1); + std::vector<double> shat2 = getUnitTangentVector(s2); + std::vector<double> r_1 = calcReferenceTrajectory(s1); + std::vector<double> r_2 = calcReferenceTrajectory(s2); + double eqn1 = (r_1[0] - xlab) * shat1[0] + (r_1[1] - zlab) * shat1[1]; + double eqn2 = (r_2[0] - xlab) * shat2[0] + (r_2[1] - zlab) * shat2[1]; + if (eqn1 * eqn2 > 0) { + s_m = 10 * s_0_m; + return; + } + if (eqn1 > 0 && eqn2 < 0) { + double temp = eqn2; + eqn2 = eqn1; + eqn1 = temp; + } + int n = 0; + while (n < 10000 && fabs(s1 - s2) > 1e-12) { + double stemp = (s2 + s1) / 2; + std::vector<double> r_temp = calcReferenceTrajectory(stemp); + std::vector<double> shattemp = getUnitTangentVector(stemp); + double eqntemp = (r_temp[0] - xlab) * shattemp[0] + + (r_temp[1] - zlab) * shattemp[1]; + if (eqntemp > 0) { + s2 = stemp; + } else if (eqntemp < 0) { + s1 = stemp; + } else { + s_m = stemp; + return; + } + n++; + } + if (n == 10000) { + s_m = 0.0; + return; + } + s_m = (s2 + s1) / 2; +} + +void CoordinateTransform::calcXCoordinate(const double &xlab, + const double &zlab) { + std::vector<double> shat = getUnitTangentVector(s_m); + std::vector<double> r_0 = calcReferenceTrajectory(s_m); + x_m = (xlab * shat[1] - zlab * shat[0] - + r_0[0] * shat[1] + r_0[1] * shat[0]); +} + +void CoordinateTransform::transformFromEntranceCoordinates( + std::vector<double> &coordinates, + const double &boundingBoxLength) { + std::vector<double> r_entrance = + calcReferenceTrajectory(-boundingBoxLength); + std::vector<double> shat = getUnitTangentVector(-boundingBoxLength); + double x = coordinates[0], z = coordinates[1]; + coordinates[0] = x * shat[1] + z * shat[0] + r_entrance[0]; + coordinates[1] = -x * shat[0] + z * shat[1] + r_entrance[1]; +} + +double getUnitTangentVectorX(double s, void *p) { + struct myParams *params = (struct myParams *)p; + double prefactor = params->rho * (tanh(params->s_0 / params->lambdaleft) - + tanh(-params->s_0 / params->lambdaright)); + return -sin((params->lambdaleft * log(cosh((s + params->s_0) + / params->lambdaleft)) - params->lambdaright + * log(cosh((s - params->s_0) / params->lambdaright))) / prefactor); +} + +double getUnitTangentVectorY(double s, void *p) { + struct myParams *params = (struct myParams *)p; + double prefactor = params->rho * (tanh(params->s_0 / params->lambdaleft) - + tanh(-params->s_0 / params->lambdaright)); + return cos((params->lambdaleft * log(cosh((s + params->s_0) + / params->lambdaleft)) - params->lambdaright + * log(cosh((s - params->s_0) / params->lambdaright))) / prefactor); +} + +} + diff --git a/src/Classic/AbsBeamline/MultipoleTFunctions/CoordinateTransform.h b/src/Classic/AbsBeamline/MultipoleTFunctions/CoordinateTransform.h new file mode 100644 index 000000000..49de4b91a --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTFunctions/CoordinateTransform.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2018, Martin Duy Tat + * 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 COORDINATE_TRANSFORM_H +#define COORDINATE_TRANSFORM_H + +/** --------------------------------------------------------------------- + * + * CoordinateTransform transforms between the coordinate system centred \n + * at the entrance and the local Frenet-Serret coordinate system. Assuming \n + * CoordinateTransform takes in the fringe field parameters and calculates \n + * the coordinate transformation. First the coordinates are transformed to\n + * a coordinate system centred in the middle of the magnet. From here \n + * the Frenet-Serret coordinates are found using numerical integration. + * + * --------------------------------------------------------------------- + * + * Class category: AbsBeamline \n + * Author: Martin Duy Tat\n + * + * --------------------------------------------------------------------- + */ + +#include <vector> + +namespace coordinatetransform { + +struct myParams { + double s_0; + double lambdaleft; + double lambdaright; + double rho; +}; + +class CoordinateTransform { +public: + /** Default constructor, transforms everything to the origin */ + CoordinateTransform(); + /** Constructor, calculates coordinate transformation from lab coordinates + * to Frenet-Serret coordinates, given fringe field parameters + * \param xlab -> x-coordinate in lab frame + * \param ylab -> y-coordinate in lab frame + * \param zlab -> z-coordinate in lab frame + * \param s_0 -> Centre field length + * \param lambdaleft -> Left end field length + * \param lambdaright -> Right end field length + * \param rho -> Centre radius of curvature + */ + CoordinateTransform(const double &xlab, + const double &ylab, + const double &zlab, + const double &s_0, + const double &lambdaleft, + const double &lambdaright, + const double &rho); + /** Copy constructor */ + CoordinateTransform(const CoordinateTransform &transform); + /** Destructor, does nothing */ + ~CoordinateTransform(); + /** Assigment operator */ + CoordinateTransform& operator= (const CoordinateTransform &transform); + /** Returns a list of transformed coordinates */ + std::vector<double> getTransformation() const; + /** Calculates reference trajectory by integrating the unit tangent vector + * \param s -> s-coordinate in local Frenet-Serret coordinates + */ + std::vector<double> calcReferenceTrajectory(const double &s) const; + /** Returns unit tangent vector + * \param s -> s-coordinate in local Frenet-Serret coordinates + */ + std::vector<double> getUnitTangentVector(const double &s) const; +private: + /** Calculates the coordinate s + * \param xlab -> x-coordinate in lab frame + * \param ylab -> y-coordinate in lab frame + */ + void calcSCoordinate(const double &xlab, const double &ylab); + /** Calculates the coordinate x, coordinate s must be calculated first! + * \param xlab -> x-coordinate in lab frame + * \param ylab -> y-coordinate in lab frame + */ + void calcXCoordinate(const double &xlab, const double &ylab); + /** Transforms from coordinate system centred in the middle of the magnet + * to the coordinate system placed at the entrance + * \param coordinates -> Coordinates in coordinate system centred + * in the middle of the magnet + * \param boundingBoxLength -> Length along the magnet from the magnet + * entrance to the middle of the magnet + */ + void transformFromEntranceCoordinates(std::vector<double> &coordinates, + const double &boundingBoxLength); + double s_0_m; + double lambdaleft_m; + double lambdaright_m; + double rho_m; + double x_m; + double z_m; + double s_m; +}; + +/** Internal function used for GSL numerical integration */ +double getUnitTangentVectorX(double s, void *p); +/** Internal function used for GSL numerical integration */ +double getUnitTangentVectorY(double s, void *p); + +} +#endif diff --git a/src/Classic/AbsBeamline/MultipoleTFunctions/DifferentialOperator.cpp b/src/Classic/AbsBeamline/MultipoleTFunctions/DifferentialOperator.cpp new file mode 100644 index 000000000..758247458 --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTFunctions/DifferentialOperator.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2018, Martin Duy Tat + * 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 <iostream> +#include <vector> +#include "DifferentialOperator.h" + +namespace polynomial { + +DifferentialOperator::DifferentialOperator(): + xDerivatives_m(0), sDerivatives_m(0){ + std::vector<int> temp; + temp.push_back(1); + polynomials_m.resize(1); + polynomials_m[0].push_back(temp); +} + +DifferentialOperator::DifferentialOperator(const std::size_t &xDerivatives, + const std::size_t &sDerivatives): + xDerivatives_m(xDerivatives), sDerivatives_m(sDerivatives) { + polynomials_m.resize(xDerivatives_m + 1); + for (std::size_t i = 0; i <= xDerivatives_m; i++) { + polynomials_m[i].resize(sDerivatives_m + 1); + } +} + +DifferentialOperator::DifferentialOperator( + const DifferentialOperator &doperator): + polynomials_m(doperator.polynomials_m), + xDerivatives_m(doperator.xDerivatives_m), + sDerivatives_m(doperator.sDerivatives_m) { +} + +DifferentialOperator::~DifferentialOperator() { +} + +DifferentialOperator& DifferentialOperator::operator= ( + const DifferentialOperator &doperator) { + polynomials_m = doperator.polynomials_m; + xDerivatives_m = doperator.xDerivatives_m; + sDerivatives_m = doperator.sDerivatives_m; + return *this; +} + +void DifferentialOperator::resizeX(const std::size_t &xDerivatives) { + std::size_t oldxDerivatives = xDerivatives_m; + xDerivatives_m = xDerivatives; + polynomials_m.resize(xDerivatives_m + 1); + if (xDerivatives_m > oldxDerivatives) { + for (std::size_t i = oldxDerivatives + 1; i <= xDerivatives_m; i++) { + polynomials_m[i].resize(sDerivatives_m + 1); + } + } +} + +void DifferentialOperator::resizeS(const std::size_t &sDerivatives) { + sDerivatives_m = sDerivatives; + for (std::size_t i = 0; i <= xDerivatives_m; i++) { + polynomials_m[i].resize(sDerivatives_m + 1); + } +} + +void DifferentialOperator::differentiateX() { + resizeX(xDerivatives_m + 1); + for (std::size_t j = 0; j <= sDerivatives_m; j++) { + std::size_t i = xDerivatives_m; + while (i != 0) { + i--; + polynomials_m[i + 1][j].addPolynomial(polynomials_m[i][j]); + polynomials_m[i][j].differentiatePolynomial(); + } + } +} + +void DifferentialOperator::doubleDifferentiateS() { + resizeS(sDerivatives_m + 1); + for (std::size_t i = 0; i <= xDerivatives_m; i++) { + std::size_t j = sDerivatives_m; + while (j != 0) { + j--; + polynomials_m[i][j + 1].addPolynomial(polynomials_m[i][j]); + polynomials_m[i][j].setZero(); + } + } +} + +void DifferentialOperator::multiplyPolynomial(const Polynomial &poly) { + for (std::size_t i = 0; i <= xDerivatives_m; i++) { + for (std::size_t j = 0; j <= sDerivatives_m; j++) { + polynomials_m[i][j].multiplyPolynomial(poly); + } + } +} + +void DifferentialOperator::setPolynomial(const std::vector<int> &poly, + const std::size_t &x, + const std::size_t &s) { + if (x > xDerivatives_m) { + resizeX(x); + } + if (s > sDerivatives_m) { + resizeS(s); + } + polynomials_m[x][s] = Polynomial(poly); +} + +void DifferentialOperator::printOperator() const { + polynomials_m[0][0].printPolynomial(); + for (std::size_t i = 0; i <= xDerivatives_m; i++) { + for (std::size_t j = 0; j <= sDerivatives_m; j++) { + if ((i != 0 || j != 0) && + (polynomials_m[i][j].getMaxXorder() != 0 || + polynomials_m[i][j].getCoefficient(0) != 0)) { + std::cout << " + ("; + polynomials_m[i][j].printPolynomial(); + std::cout << ")" << "(d/dx)^" << i << "(d/ds)^" << j; + } + } + } + std::cout << std::endl; +} + +void DifferentialOperator::addOperator(const DifferentialOperator &doperator) { + if (xDerivatives_m < doperator.xDerivatives_m) { + resizeX(doperator.xDerivatives_m); + } + if (sDerivatives_m < doperator.sDerivatives_m) { + resizeS(doperator.sDerivatives_m); + } + for (std::size_t i = 0; i <= doperator.xDerivatives_m; i++) { + for (std::size_t j = 0; j <= doperator.sDerivatives_m; j++) { + polynomials_m[i][j].addPolynomial(doperator.polynomials_m[i][j]); + } + } +} + +bool DifferentialOperator::isPolynomialZero(const std::size_t &x, + const std::size_t &s) const { + if (x > xDerivatives_m || s > sDerivatives_m) { + return true; + } + return (polynomials_m[x][s].getCoefficient(0) == 0 && + polynomials_m[x][s].getMaxXorder() == 0); +} + +void DifferentialOperator::truncate(const std::size_t &truncateOrder) { + for (std::size_t i = 0; i <= xDerivatives_m; i++) { + for (std::size_t j = 0; j <= sDerivatives_m; j++) { + polynomials_m[i][j].truncate(truncateOrder); + } + } +} + +double DifferentialOperator::evaluatePolynomial( + const double &x, + const std::size_t &xDerivative, + const std::size_t &sDerivative) const { + if (xDerivative > xDerivatives_m || sDerivative > sDerivatives_m) { + return 0.0; + } + return polynomials_m[xDerivative][sDerivative].evaluatePolynomial(x); +} + +} diff --git a/src/Classic/AbsBeamline/MultipoleTFunctions/DifferentialOperator.h b/src/Classic/AbsBeamline/MultipoleTFunctions/DifferentialOperator.h new file mode 100644 index 000000000..632c3cf0f --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTFunctions/DifferentialOperator.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2018, Martin Duy Tat + * 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 DIFFERENTIAL_OPERATOR_H +#define DIFFERENTIAL_OPERATOR_H + +/** --------------------------------------------------------------------- + * + * DifferentialOperator describes a differential operator in terms of \n + * polynomial coefficients. \n + * + * --------------------------------------------------------------------- + * + * Class category: AbsBeamline \n + * Author: Martin Duy Tat\n + * + * --------------------------------------------------------------------- + * A differential operator is a linear sum of operators in the form \n + * @f[p(x)\frac{\partial^n}{\partial x^n}\frac{\partial^m}{\partial s^m}@f], + * \n and the polynomials p(x) are stored in a n by m list. + */ + +#include <vector> +#include "Polynomial.h" + +namespace polynomial { + +class DifferentialOperator { +public: + /** Default constructor, initialises identity operator (constant 1) */ + DifferentialOperator(); + /** Constructor, initialises operator with zero polynomials and derivatives + * up to xDerivatives in x and sDerivatives in s + * \param xDerivatives -> Highest derivative of x + * \param sDerivatives -> Highest serivative in s + */ + DifferentialOperator(const std::size_t &xDerivatives, + const std::size_t &sDerivatives); + /** Copy constructor */ + DifferentialOperator(const DifferentialOperator &doperator); + /** Assigment operator */ + DifferentialOperator& operator= (const DifferentialOperator &doperator); + /** Destructor, does nothing */ + ~DifferentialOperator(); + /** Set highest derivative in x to xDerivatives + * \param xDerivatives -> Highest derivative in x + */ + void resizeX(const std::size_t &xDerivatives); + /** Set highest derivative in s to sDerivatives + * \param sDerivatives -> Highest derivative in s + */ + void resizeS(const std::size_t &sDerivatives); + /** Differentiate wrt x using product rule */ + void differentiateX(); + /** Differentiate wrt s twice */ + void doubleDifferentiateS(); + /** Multiplies each term with given polynomial + * \param poly -> Polynomial that multiplies each term + */ + void multiplyPolynomial(const Polynomial &poly); + /** Assign the input polynomial \n + * If x or s is greater than xDerivatives_m or sDerivatives_m, + * they are adjusted accordingly and polynomials_m is resized + * \param poly -> Polynomial to be assigned to the operator + * \param x -> Number of x-derivatives + * \param s -> Number of s-derivatives + */ + void setPolynomial(const std::vector<int> &poly, + const std::size_t &x, + const std::size_t &s); + /** Print operator, for internal debugging */ + void printOperator() const; + /** Add the operator to Operator, term by term + * \param doperator -> DifferentialOperator object to be added with + * this DifferentialOperator object + */ + void addOperator(const DifferentialOperator &doperator); + /** Returns highest derivative in x */ + std::size_t getXDerivatives() const; + /** Returns highest derivative in s */ + std::size_t getSDerivatives() const; + /** Check if polynomial with x x-derivaties and s s-derivatives is a + * zero polynomial \n + * If x or s is larger than xDerivatives or sDerivatives true is returned + * \param x -> Number of x-derivatives + * \param s -> Number of s-derivatives + */ + bool isPolynomialZero(const std::size_t &x, const std::size_t &s) const; + /** Truncate all polynomials to truncateOrder \n + * If truncateOrder is greater than the highest power of x then + * zeroes are appended to the end + * \param truncateOrder -> Highest order of x after truncation + */ + void truncate(const std::size_t &truncateOrder); + /** Evaluate polynomial with xDerivative x-derivatives and + * sDerivative s-derivatives \n + * If xDerivative or sDerivative is greater than xDerivative_m + * or sDerivative then zero is returned + * \param x -> Point at which polynomial is evaluated + * \param xDerivative -> Number of x-derivatives + * \param sDerivative -> Number of s-derivatives + */ + double evaluatePolynomial(const double &x, + const std::size_t &xDerivative, + const std::size_t &sDerivative) const; +private: + std::vector<std::vector<Polynomial>> polynomials_m; + std::size_t xDerivatives_m; + std::size_t sDerivatives_m; +}; + +inline + std::size_t DifferentialOperator::getXDerivatives() const { + return xDerivatives_m; +} +inline + std::size_t DifferentialOperator::getSDerivatives() const { + return sDerivatives_m; +} + +} + +#endif diff --git a/src/Classic/AbsBeamline/MultipoleTFunctions/DifferentialOperatorTwo.cpp b/src/Classic/AbsBeamline/MultipoleTFunctions/DifferentialOperatorTwo.cpp new file mode 100644 index 000000000..1e4b0db2d --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTFunctions/DifferentialOperatorTwo.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2018, Martin Duy Tat + * 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 <iostream> +#include <vector> +#include "DifferentialOperatorTwo.h" +#include "PolynomialSum.h" +#include "TwoPolynomial.h" + +namespace polynomial { + +DifferentialOperatorTwo::DifferentialOperatorTwo(): + xDerivatives_m(0), sDerivatives_m(0){ + std::vector<int> dummy1; + dummy1.push_back(1); + std::vector<std::vector<int>> dummy2; + dummy2.push_back(dummy1); + polynomials_m.resize(1); + polynomials_m[0].push_back(PolynomialSum(TwoPolynomial(dummy2))); +} + +DifferentialOperatorTwo::DifferentialOperatorTwo( + const std::size_t &xDerivatives, + const std::size_t &sDerivatives): + xDerivatives_m(xDerivatives), sDerivatives_m(sDerivatives) { + polynomials_m.resize(xDerivatives_m + 1); + for (std::size_t i = 0; i <= xDerivatives_m; i++) { + polynomials_m[i].resize(sDerivatives_m + 1); + } +} + +DifferentialOperatorTwo::DifferentialOperatorTwo( + const DifferentialOperatorTwo &doperator): + polynomials_m(doperator.polynomials_m), + xDerivatives_m(doperator.xDerivatives_m), + sDerivatives_m(doperator.sDerivatives_m) { +} + +DifferentialOperatorTwo::~DifferentialOperatorTwo() { +} + +DifferentialOperatorTwo& DifferentialOperatorTwo::operator= ( + const DifferentialOperatorTwo &doperator) { + polynomials_m = doperator.polynomials_m; + xDerivatives_m = doperator.xDerivatives_m; + sDerivatives_m = doperator.sDerivatives_m; + return *this; +} + +void DifferentialOperatorTwo::resizeX(const std::size_t &xDerivatives) { + std::size_t oldxDerivatives = xDerivatives_m; + xDerivatives_m = xDerivatives; + polynomials_m.resize(xDerivatives_m + 1); + if (xDerivatives_m > oldxDerivatives) { + for (std::size_t i = oldxDerivatives + 1; i <= xDerivatives_m; i++) { + polynomials_m[i].resize(sDerivatives_m + 1); + } + } +} + +void DifferentialOperatorTwo::resizeS(const std::size_t &sDerivatives) { + sDerivatives_m = sDerivatives; + for (std::size_t i = 0; i <= xDerivatives_m; i++) { + polynomials_m[i].resize(sDerivatives_m + 1); + } +} + +void DifferentialOperatorTwo::differentiateX() { + resizeX(xDerivatives_m + 1); + for (std::size_t j = 0; j <= sDerivatives_m; j++) { + std::size_t i = xDerivatives_m; + while (i != 0) { + i--; + polynomials_m[i + 1][j].addPolynomial(polynomials_m[i][j]); + polynomials_m[i][j].differentiateX(); + } + } +} + +void DifferentialOperatorTwo::differentiateS() { + resizeS(sDerivatives_m + 1); + for (std::size_t i = 0; i <= xDerivatives_m; i++) { + std::size_t j = sDerivatives_m; + while (j != 0) { + j--; + polynomials_m[i][j + 1].addPolynomial(polynomials_m[i][j]); + polynomials_m[i][j].differentiateS(); + } + } +} + +void DifferentialOperatorTwo::multiplyPolynomial(const TwoPolynomial &poly) { + for (std::size_t i = 0; i <= xDerivatives_m; i++) { + for (std::size_t j = 0; j <= sDerivatives_m; j++) { + polynomials_m[i][j].multiplyPolynomial(poly); + } + } +} + +void DifferentialOperatorTwo::setPolynomial(const TwoPolynomial &poly, + const std::size_t &x, + const std::size_t &s) { + if (x > xDerivatives_m) { + resizeX(x); + } + if (s > sDerivatives_m) { + resizeS(s); + } + polynomials_m[x][s] = TwoPolynomial(poly); +} + +void DifferentialOperatorTwo::printOperator() const { + for (std::size_t i = 0; i <= xDerivatives_m; i++) { + for (std::size_t j = 0; j <= sDerivatives_m; j++) { + std::size_t zeros = 0; + std::cout << "("; + for (std::size_t k = 0; + k < polynomials_m[i][j].numberOfTerms(); k++) { + if (polynomials_m[i][j].isPolynomialZero(k)) zeros++; + } + if (zeros != polynomials_m[i][j].numberOfTerms()) { + polynomials_m[i][j].printPolynomial(); + std::cout << "(d/dx)^" << i << "(d/ds)^" << j << ")"; + } + } + } + std::cout << std::endl; +} + +void DifferentialOperatorTwo::addOperator( + const DifferentialOperatorTwo &doperator) { + if (xDerivatives_m < doperator.xDerivatives_m) { + resizeX(doperator.xDerivatives_m); + } + if (sDerivatives_m < doperator.sDerivatives_m) { + resizeS(doperator.sDerivatives_m); + } + for (std::size_t i = 0; i <= doperator.xDerivatives_m; i++) { + for (std::size_t j = 0; j <= doperator.sDerivatives_m; j++) { + polynomials_m[i][j].addPolynomial(doperator.polynomials_m[i][j]); + } + } +} + +bool DifferentialOperatorTwo::isPolynomialZero(const std::size_t &x, + const std::size_t &s, + const std::size_t &term) const { + if (x > xDerivatives_m || s > sDerivatives_m) { + return true; + } + return polynomials_m[x][s].isPolynomialZero(term); +} + +void DifferentialOperatorTwo::truncate( + const std::size_t &truncateOrder) { + for (std::size_t i = 0; i <= xDerivatives_m; i++) { + for (std::size_t j = 0; j <= sDerivatives_m; j++) { + polynomials_m[i][j].truncate(truncateOrder); + } + } +} + +double DifferentialOperatorTwo::evaluatePolynomial( + const double &x, + const double &s, + const std::size_t &xDerivative, + const std::size_t &sDerivative, + const std::vector<double> &dSvalues) const { + if (xDerivative > xDerivatives_m || sDerivative > sDerivatives_m) { + return 0.0; + } + return polynomials_m[xDerivative][sDerivative] + .evaluatePolynomial2(x, s, dSvalues); +} + +std::size_t DifferentialOperatorTwo::numberOfTerms( + const std::size_t &xDerivatives, + const std::size_t &sDerivatives) const { + if (xDerivatives > xDerivatives_m || sDerivatives > sDerivatives_m) { + return 0; + } + return polynomials_m[xDerivatives][sDerivatives].numberOfTerms(); +} + +std::vector<std::size_t> DifferentialOperatorTwo::getdSFactors( + const std::size_t &xDerivatives, + const std::size_t &sDerivatives, + const std::size_t &p) const{ + if (xDerivatives > xDerivatives_m || sDerivatives > sDerivatives_m) { + std::vector<std::size_t> dummy; + return dummy; + } + return polynomials_m[xDerivatives][sDerivatives].getdSfactors(p); +} + +void DifferentialOperatorTwo::sortTerms() { + for (std::size_t i = 0; i <= xDerivatives_m; i++) { + for (std::size_t j = 0; j <= sDerivatives_m; j++) { + polynomials_m[i][j].sortTerms(); + } + } +} + +} diff --git a/src/Classic/AbsBeamline/MultipoleTFunctions/DifferentialOperatorTwo.h b/src/Classic/AbsBeamline/MultipoleTFunctions/DifferentialOperatorTwo.h new file mode 100644 index 000000000..ecac3df35 --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTFunctions/DifferentialOperatorTwo.h @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2018, Martin Duy Tat + * 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 DIFFERENTIAL_OPERATOR_TWO_H +#define DIFFERENTIAL_OPERATOR_TWO_H + +/** --------------------------------------------------------------------- + * + * DifferentialOperatorTwo describes a differential operator in terms of \n + * polynomial coefficients. The polynomials have two variables x and S(s). \n + * + * --------------------------------------------------------------------- + * + * Class category: AbsBeamline \n + * Author: Martin Duy Tat\n + * + * --------------------------------------------------------------------- + * A differential operator is a linear sum of operators in the form \n + * @f[p(x, S(s))\frac{\partial^n}{\partial x^n} + * \frac{\partial^m}{\partial s^m}@f], \n + * and the polynomials p(x, S(s)) are stored in a n by m list. + */ + +#include <vector> +#include "PolynomialSum.h" +#include "TwoPolynomial.h" + +namespace polynomial { + +class DifferentialOperatorTwo { +public: + /** Default constructor, initalises identity operator */ + DifferentialOperatorTwo(); + /** Constructor, initialises operator with zero polynomials and + * derivatives up to xDerivatives in x and sDerivatives in s + * \param xDerivatives -> Highest x-derivative + * \param sDerivatives -> Highest s-derivative + */ + DifferentialOperatorTwo(const std::size_t &xDerivatives, + const std::size_t &sDerivatives); + /** Copy constructor */ + DifferentialOperatorTwo(const DifferentialOperatorTwo &doperator); + /** Destructor */ + ~DifferentialOperatorTwo(); + /** Assigment operator */ + DifferentialOperatorTwo& operator= ( + const DifferentialOperatorTwo &doperator); + /** Set highest derivative in x to xDerivatives111 + * \param xDerivatives -> Highest x-derivative + */ + void resizeX(const std::size_t &xDerivatives); + /** Set highest derivative in s to xDerivatives + * \param sDerivatives -> Highest s-derivative + */ + void resizeS(const std::size_t &sDerivatives); + /** Differentiate wrt x using product rule */ + void differentiateX(); + /** Differentiate wrt s using product rule */ + void differentiateS(); + /** Multiplies each term with given polynomial + * \param poly -> Polynomial that is multiplied with this polynomial + */ + void multiplyPolynomial(const TwoPolynomial &poly); + /** Assign the polynomial poly to the operator + * \param poly -> Polynomial to be assigned + * \param x -> Number of x-derivatives + * \param s -> Numbe of s-derivatives + */ + void setPolynomial(const TwoPolynomial &poly, + const std::size_t &x, + const std::size_t &s); + /** Print operator, for internal debugging */ + void printOperator() const; + /** Add the doperator to operator, term by term + * \param doperator -> Differential operator to be added + */ + void addOperator(const DifferentialOperatorTwo &doperator); + /** Returns highest derivative in x */ + std::size_t getXDerivatives() const; + /** Returns highest derivative in s */ + std::size_t getSDerivatives() const; + /** Check if polynomial with x x-derivaties and s s-derivatives is + * a zero polynomial \n + * If x or s are out of range, true is returned + * \param x -> Number of x-derivatives + * \param s -> Number of s-derivatives + * \param term -> Term index + */ + bool isPolynomialZero(const std::size_t &x, + const std::size_t &s, + const std::size_t &term) const; + /** Truncate all polynomials to truncateOrder + * \param truncateOrder -> Highest order of x after truncation + */ + void truncate(const std::size_t &truncateOrder); + /** Evaluate polynomial specified + * \param x -> Point x where polynomial is evaluated + * \param s -> Point s where polynomial is evaluated + * \param xDerivative -> Number of x-derivatives + * \param sDerivative -> Number of s-derivatives + * \param term -> Term index + */ + double evaluatePolynomial(const double &x, + const double &s, + const std::size_t &xDerivative, + const std::size_t &sDerivative, + const std::vector<double> &dSvalues) const; + /** Returns number of terms in the sum with xDerivatives + * x-derivatives and sDerivatives s-derivatives \n + * If xDerivative or sDerivative are out of + * range zero is returned + * \param xDerivatives -> Number of x-derivatives + * \param sDerivatives -> Number of s-derivatives + */ + std::size_t numberOfTerms(const std::size_t &xDerivatives, + const std::size_t &sDerivatives) const; + /** Returns list of S(s)-derivatives from term p in polynomial with + * xDerivatives x-derivatives and sDerivatives s-derivatives \n + * If xDerivatives or sDerivatives are out of range + * an empty vector is returned + * \param xDerivatives -> Number of x-derivatives + * \param sDerivatives -> Number of s-derivatives + * \param p -> Term index, starting with term 0 + */ + std::vector<std::size_t> getdSFactors(const std::size_t &xDerivatives, + const std::size_t &sDerivatives, + const std::size_t &p) const; + /** Sort the terms in each sum, with fewest S(s)-derivatives first, + * then in increasing powers + */ + void sortTerms(); +private: + std::vector<std::vector<PolynomialSum>> polynomials_m; + std::size_t xDerivatives_m; + std::size_t sDerivatives_m; +}; + +inline + std::size_t DifferentialOperatorTwo::getXDerivatives() const { + return xDerivatives_m; +} +inline + std::size_t DifferentialOperatorTwo::getSDerivatives() const { + return sDerivatives_m; +} + +} + +#endif diff --git a/src/Classic/AbsBeamline/MultipoleTFunctions/Polynomial.cpp b/src/Classic/AbsBeamline/MultipoleTFunctions/Polynomial.cpp new file mode 100644 index 000000000..dd7862baf --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTFunctions/Polynomial.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2018, Martin Duy Tat + * 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 <cmath> +#include <iostream> +#include "Polynomial.h" + +namespace polynomial { + +Polynomial::Polynomial() { + coefficients_m.push_back(0); + maxXorder_m = 0; +} + +Polynomial::Polynomial(const std::vector<int> &coefficients) { + if (coefficients.size() == 0) { + coefficients_m.push_back(0); + return; + } + coefficients_m = coefficients; + maxXorder_m = coefficients_m.size() - 1; +} + +Polynomial::Polynomial(const Polynomial &poly) { + coefficients_m = poly.coefficients_m; + maxXorder_m = coefficients_m.size() - 1; +} + +Polynomial::~Polynomial() { +} + +Polynomial& Polynomial::operator= (const Polynomial& poly) { + maxXorder_m = poly.maxXorder_m; + coefficients_m = poly.coefficients_m; + return *this; +} + +void Polynomial::differentiatePolynomial() { + if (maxXorder_m == 0 && coefficients_m[0] == 0) { + return; + } + if (maxXorder_m == 0 && coefficients_m[0] != 0) { + coefficients_m[0] = 0; + return; + } + for (std::size_t i = 0; i < maxXorder_m; i++) { + coefficients_m[i] = coefficients_m[i + 1] * (i + 1); + } + coefficients_m.pop_back(); + maxXorder_m--; +} + +void Polynomial::multiplyPolynomial(const Polynomial &poly) { + if (maxXorder_m == 0 && coefficients_m[0] == 0) { + return; + } + if (poly.maxXorder_m == 0 && poly.coefficients_m[0] == 0) { + setZero(); + return; + } + std::vector<int> newPoly(maxXorder_m + poly.maxXorder_m + 1, 0); + for (std::size_t i = 0; i <= maxXorder_m; i++) { + for (std::size_t j = 0; j <= poly.maxXorder_m; j ++) { + newPoly[i + j] = newPoly[i + j] + + coefficients_m[i] * poly.coefficients_m[j]; + } + } + coefficients_m = newPoly; + maxXorder_m = coefficients_m.size() - 1; +} + +void Polynomial::printPolynomial() const { + std::cout << coefficients_m[0]; + for (std::size_t i = 1; i <= maxXorder_m; i++) { + if (coefficients_m[i] >= 0) { + std::cout << " + " << coefficients_m[i] << "x^" << i; + } else { + std::cout << " - " << -coefficients_m[i] << "x^" << i; + } + } +} + +void Polynomial::addPolynomial(const Polynomial &poly) { + if (poly.maxXorder_m > maxXorder_m) { + maxXorder_m = poly.maxXorder_m; + coefficients_m.resize(maxXorder_m + 1, 0); + } + for (std::size_t i = 0; i <= poly.maxXorder_m; i++) { + coefficients_m[i] = coefficients_m[i] + poly.coefficients_m[i]; + } +} + +void Polynomial::setCoefficient(const int &coefficient, + const std::size_t &order) { + if (order > maxXorder_m) { + coefficients_m.resize(order + 1, 0); + maxXorder_m = coefficients_m.size() - 1; + } + coefficients_m[order] = coefficient; +} + +double Polynomial::evaluatePolynomial(const double &x) const { + double result = 0.0; + std::size_t i = maxXorder_m + 1; + while (i != 0) { + i--; + result = result * x + coefficients_m[i]; + } + return result; +} + +} diff --git a/src/Classic/AbsBeamline/MultipoleTFunctions/Polynomial.h b/src/Classic/AbsBeamline/MultipoleTFunctions/Polynomial.h new file mode 100644 index 000000000..9847b14f1 --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTFunctions/Polynomial.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2018, Martin Duy Tat + * 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 POLYNOMIAL_H +#define POLYNOMIAL_H + +/** --------------------------------------------------------------------- + * + * Polynomial describes a polynomial of one variable.\n + * + * --------------------------------------------------------------------- + * + * Class category: AbsBeamline \n + * Author: Martin Duy Tat\n + * + * --------------------------------------------------------------------- + * The polynomial @f[p(x) = a_0 + a_1x + ... + a_nx^n @f] is stored as a \n + * list (a_0, a_1, ..., a_n).\n + * BUG: For large n the integer type might overflow. If you need this many \n + * terms change all int to long int. \n + */ + +#include <vector> + +namespace polynomial { + +class Polynomial { +public: + /** Default constructor, initialises zero polynomial */ + Polynomial(); + /** Initialises polynomial with input coefficients, starting with the + * const term \n + * If coefficients is an empty vector, a zero polynomial with one + * constant zero term is created + * \param coefficients -> List of polynomial coefficients, starting + * with the const term + */ + Polynomial(const std::vector<int> &coefficients); + /** Copy constructor */ + Polynomial(const Polynomial &poly); + /** Destructor, does nothing */ + ~Polynomial(); + /** Assigment operator */ + Polynomial& operator= (const Polynomial &poly); + /** Differentiate polynomial using \f$\frac{d}{dx}x^n = nx^{n - 1}\f$ */ + void differentiatePolynomial(); + /** Multiply polynomial with input polynomial by multiplying + * term by term \n + * maxXorder_m is adjusted accordingly and coefficients_m is resized + * \param poly -> Polynomial to be multiplied with this polynomial + */ + void multiplyPolynomial(const Polynomial &poly); + /** Add polynomial to input polynomial \n + * If input polynomial has higher order terms, + * then maxXorder_m is adjusted and coefficients_m is resized + * \param poly -> Polynomial to be added to this polynomial + */ + void addPolynomial(const Polynomial &poly); + /** Print polynomial for internal debugging */ + void printPolynomial() const; + /** Returns coefficient in front of x^order + * \param order -> The power of x belonging to this coefficient + */ + int getCoefficient(const std::size_t &order) const; + /** Set the coefficient in front of x^order \n + * If order is larger than maxXorder_m maxXorder_m is adjusted + * and coefficients_m is resized + * \param coefficient -> Coefficient we wish to insert + * \param order -> The power of x belonging to this coefficient + */ + void setCoefficient(const int &coefficient, const std::size_t &order); + /** Returns the highest order of x, does nothing if order is negative */ + int getMaxXorder() const; + /** Set the highest order of x to maxXorder, filling in zeros if necessary + * \param maxXorder -> Highest power of x + */ + void setMaxXorder(const std::size_t &maxXorder); + /** Set polynomial to zero */ + void setZero(); + /** Evaluate the polynomial + * \param x -> The point at which polynomial is evaluated + */ + double evaluatePolynomial(const double &x) const; + /** Truncate polynomial to truncateOrder \n + * If truncateOrder is greater than maxXorder_m, maxXorder_m is adjusted + * and coefficients is resized with zero coefficients at the end + * \param truncateOrder -> Highest order of x after truncation + */ + void truncate(const std::size_t &truncateOrder); +private: + std::size_t maxXorder_m; + std::vector<int> coefficients_m; +}; + +inline + int Polynomial::getCoefficient(const std::size_t &order) const { + if (order > maxXorder_m || order < 0) { + return 0; + } + return coefficients_m[order]; +} +inline + int Polynomial::getMaxXorder() const{ + return maxXorder_m; +} +inline + void Polynomial::setMaxXorder(const std::size_t &maxXorder) { + coefficients_m.resize(maxXorder + 1, 0); + maxXorder_m = maxXorder; +} +inline + void Polynomial::setZero() { + maxXorder_m = 0; + coefficients_m.resize(1); + coefficients_m[0] = 0; +} +inline + void Polynomial::truncate(const std::size_t &truncateOrder) { + if (truncateOrder < maxXorder_m) { + coefficients_m.resize(truncateOrder + 1); + maxXorder_m = truncateOrder; + } +} + +} + +#endif diff --git a/src/Classic/AbsBeamline/MultipoleTFunctions/PolynomialSum.cpp b/src/Classic/AbsBeamline/MultipoleTFunctions/PolynomialSum.cpp new file mode 100644 index 000000000..6a2d85368 --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTFunctions/PolynomialSum.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2018, Martin Duy Tat + * 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 <iostream> +#include <algorithm> +#include <gsl/gsl_sf_pow_int.h> +#include "PolynomialSum.h" +#include "TwoPolynomial.h" + +namespace polynomial { + +PolynomialSum::PolynomialSum() { +} + +PolynomialSum::PolynomialSum(const TwoPolynomial &polynomial) { + polynomialSum_m.push_back(polynomial); +} + +PolynomialSum::PolynomialSum(const PolynomialSum &polynomialSum): + polynomialSum_m(polynomialSum.polynomialSum_m) { +} + +PolynomialSum::~PolynomialSum() { +} + +PolynomialSum& PolynomialSum::operator= (const PolynomialSum &sum) { + polynomialSum_m = sum.polynomialSum_m; + return *this; +} + +void PolynomialSum::differentiateX() { + for (std::size_t i = 0; i < polynomialSum_m.size(); i++) { + polynomialSum_m[i].differentiateX(); + } +} + +void PolynomialSum::differentiateS() { + std::size_t pSize = polynomialSum_m.size(); + for (std::size_t i = 0; i < pSize; i++) { + std::size_t N = polynomialSum_m[i].getNdSfactors(); + for (std::size_t j = 0; j < N; j++) { + std::vector<std::size_t> tempdS = + polynomialSum_m[i].getdSfactors(); + if (tempdS[j] != 0) { + polynomialSum_m.push_back(polynomialSum_m[i]); + polynomialSum_m.back().multiplyConstant(tempdS[j]); + tempdS[j] = tempdS[j] - 1; + if ((j + 1) == N) tempdS.push_back(0); + tempdS[j + 1] = tempdS[j + 1] + 1; + polynomialSum_m.back().setdSfactors(tempdS); + } + } + polynomialSum_m[i].differentiateS(); + } +} + +void PolynomialSum::multiplyPolynomial(const TwoPolynomial &poly) { + for (std::size_t i = 0; i < polynomialSum_m.size(); i++) { + polynomialSum_m[i].multiplyPolynomial(poly); + } +} + +void PolynomialSum::printPolynomial() const { + for (std::size_t i = 0; i < polynomialSum_m.size(); i++) { + if (!polynomialSum_m[i].isZero()) { + std::cout << " + "; + polynomialSum_m[i].printPolynomial(); + } + } +} + +bool PolynomialSum::isPolynomialZero(const std::size_t &p) const { + if (p < polynomialSum_m.size()) { + return polynomialSum_m[p].isZero(); + } + else { + return true; + } +} + +void PolynomialSum::truncate(const std::size_t &truncateOrder) { + for (std::size_t i = 0; i < polynomialSum_m.size(); i++) { + polynomialSum_m[i].truncate(truncateOrder); + } +} + +void PolynomialSum::addPolynomial(const PolynomialSum &poly) { + std::vector<TwoPolynomial> newPoly; + newPoly.reserve(polynomialSum_m.size() + poly.polynomialSum_m.size()); + newPoly.insert(newPoly.end(), + polynomialSum_m.begin(), + polynomialSum_m.end()); + newPoly.insert(newPoly.end(), + poly.polynomialSum_m.begin(), + poly.polynomialSum_m.end()); + polynomialSum_m = newPoly; +} + +std::vector<std::size_t> PolynomialSum::getdSfactors( + const std::size_t &p) const { + if (p >= polynomialSum_m.size()) { + std::vector<std::size_t> dummy; + return dummy; + } + return polynomialSum_m[p].getdSfactors(); +} + +void PolynomialSum::sortTerms() { + std::size_t N = polynomialSum_m.size(); + if (N < 2) { + return; + } + std::sort(polynomialSum_m.begin(), polynomialSum_m.end()); + while (polynomialSum_m[0].isZero()) { + polynomialSum_m.erase(polynomialSum_m.begin()); + if (polynomialSum_m.size() < 2) { + return; + } + } + std::size_t i = 1; + while (i < polynomialSum_m.size()) { + if (polynomialSum_m[i].isZero()) { + polynomialSum_m.erase(polynomialSum_m.begin() + i); + continue; + } + if (polynomialSum_m[i - 1] == polynomialSum_m[i]) { + polynomialSum_m[i - 1].addPolynomial(polynomialSum_m[i]); + polynomialSum_m.erase(polynomialSum_m.begin() + i); + } else { + i++; + } + } +} + +void PolynomialSum::putSumTogether( + const std::vector<double> &dSvalues, + std::vector<std::vector<double>> &finalPolynomial) const { + finalPolynomial.resize(1); + finalPolynomial[0].resize(1, 0.0); + std::size_t nx = 0, ns = 0; + for (std::size_t p = 0; p < polynomialSum_m.size(); p++) { + if (polynomialSum_m[p].isZero()) { + continue; + } + double dSfactoreval = 1.0; + for (std::size_t q = 0; + q < polynomialSum_m[p].dSfactors_m.size(); + q++) { + dSfactoreval *= gsl_sf_pow_int(dSvalues[q + 1], + polynomialSum_m[p].dSfactors_m[q]); + } + if (nx < polynomialSum_m[p].maxXorder_m) { + finalPolynomial.resize(polynomialSum_m[p].maxXorder_m + 1); + for (std::size_t k = nx + 1; + k <= polynomialSum_m[p].maxXorder_m; + k++) { + finalPolynomial[nx].resize(ns + 1, 0.0); + } + nx = polynomialSum_m[p].maxXorder_m; + } + if (ns < polynomialSum_m[p].maxSorder_m) { + ns = polynomialSum_m[p].maxSorder_m; + for (std::size_t k = 0; k <= nx; k++) { + finalPolynomial[k].resize(ns + 1, 0.0); + } + } + for (std::size_t i = 0; + i <= polynomialSum_m[p].maxXorder_m; + i++) { + for (std::size_t j = 0; + j <= polynomialSum_m[p].maxSorder_m; + j++) { + finalPolynomial[i][j] += polynomialSum_m[p] + .coefficients_m[i][j] + * dSfactoreval; + } + } + } +} + +double PolynomialSum::evaluatePolynomial2( + const double &x, + const double &s, + const std::vector<double> &dSvalues) const { + std::vector<std::vector<double>> coefficients; + putSumTogether(dSvalues, coefficients); + double result = 0.0; + std::size_t i = coefficients.size(); + while (i != 0) { + i--; + std::size_t j = coefficients[0].size(); + double temp = 0.0; + while (j != 0) { + j--; + temp = temp * s + coefficients[i][j]; + } + result = result * x + temp; + } + return result; +} + +} diff --git a/src/Classic/AbsBeamline/MultipoleTFunctions/PolynomialSum.h b/src/Classic/AbsBeamline/MultipoleTFunctions/PolynomialSum.h new file mode 100644 index 000000000..c51772f2a --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTFunctions/PolynomialSum.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2018, Martin Duy Tat + * 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 POLYNOMIAL_SUM_H +#define POLYNOMIAL_SUM_H + +/** --------------------------------------------------------------------- + * + * PolynomialSum describes a sum of TwoPolynomial objects.\n + * + * --------------------------------------------------------------------- + * + * Class category: AbsBeamline \n + * Author: Martin Duy Tat\n + * + * --------------------------------------------------------------------- + * The polynomial of two variables @f[p(x) = a_{00} + a_{10}x + a_{11}xS(s) + * + ... + a_{nm}x^nS(s)^m @f] cannot be summed with other polynomials \n + * unless all the powers of S(s)-derivatives are identical. \n + * Instead all terms are just stored seperately in a list. + */ + +#include <vector> +#include "TwoPolynomial.h" + +namespace polynomial { + +class PolynomialSum { +public: + /** Default constructor, initialises empty sum */ + PolynomialSum(); + /** Constructor, initalises sum with a single polynomial from input + * \param polynomial -> The polynomial sum is initialised with this \n + * polynomial as the first and only term + */ + PolynomialSum(const TwoPolynomial &polynomial); + /** Copy constructor */ + PolynomialSum(const PolynomialSum &polynomialSum); + /** Desctructor, does nothing */ + ~PolynomialSum(); + /** Assigment operator */ + PolynomialSum& operator= (const PolynomialSum &sum); + /** Differentiate each term wrt x */ + void differentiateX(); + /** Differentiate each term wrt s */ + void differentiateS(); + /** Multiply term with input polynomial + * \param poly -> Polynomial to be multiplied with this polynomial + */ + void multiplyPolynomial(const TwoPolynomial &poly); + /** Print polynomial, for internal debugging */ + void printPolynomial() const; + /** Returns number of terms in the sum */ + std::size_t numberOfTerms() const; + /** Check if term p is a zero polynomial \n + * Returns true if p is negative or outside the range of list range + * \param p -> Term index, starting with term 0 + */ + bool isPolynomialZero(const std::size_t &p) const; + /** Truncate series in x at truncateOrder + * \param truncateOrder -> Highest power of x after truncation + */ + void truncate(const std::size_t &truncateOrder); + /** Evaluate polynomial in term p at the point (x, s) \n + * If p is outside list range zero is returned + * \param p -> Term index, starting with term 0 + * \param x -> Point x where polynomial is evaluated + * \param s -> Point s where polynomial is evaluated + */ + double evaluatePolynomial(const std::size_t &p, + const double &x, + const double &s) const; + /** Put together all terms in the PolynomialSum by evaluating the \n + * S(s)-derivatives and multiply them with the polynomial coefficients \n + * Polynomial coefficients are now of type double + * \param dSvalues -> List of evaluated S(s)-derivates, starting + * with zeroth (no) derivative + * \param finalPolynomial -> Resulting polynomial, must pass an + * empty vector of vectors into this function + */ + void putSumTogether(const std::vector<double> &dSvalues, + std::vector<std::vector<double>> &finalPolynomial) const; + /** Evaluate polynomial after putting the sum together into one polynomial + * \param x -> Point x where polynomial is evaluated + * \param s -> Point s where polynomial is evaluated + * \param dSvalues -> List of evaluated S(s)-derivatives, starting + * with zeroth (no) derivative + */ + double evaluatePolynomial2(const double &x, + const double &s, + const std::vector<double> &dSvalues) const; + /** Add poly to the sum by concatenating the lists + * \param poly -> Polynomial that is added to the list + */ + void addPolynomial(const PolynomialSum &poly); + /** Returns lists of S(s)-derivatives in term p \n + * Returns empty list if p is negative or outside list range + * \param p -> Term index, starting with term 0 + */ + std::vector<std::size_t> getdSfactors(const std::size_t &p) const; + /** Sort polynomialSum_m such that the TwoPolynomial objects with fewest \n + * S(s)-derivatives come first, in ascending order \n + * If any TwoPolynomial objects have identical S(s)-derivatives these + * are put together by adding the polynomials + */ + void sortTerms(); +private: + std::vector<TwoPolynomial> polynomialSum_m; +}; + +inline + std::size_t PolynomialSum::numberOfTerms() const { + return polynomialSum_m.size(); +} +inline + double PolynomialSum::evaluatePolynomial(const std::size_t &p, + const double &x, + const double &s) const { + if (p >= polynomialSum_m.size()) { + return 0.0; + } + return polynomialSum_m[p].evaluatePolynomial(x, s); +} + +} + +#endif diff --git a/src/Classic/AbsBeamline/MultipoleTFunctions/RecursionRelation.cpp b/src/Classic/AbsBeamline/MultipoleTFunctions/RecursionRelation.cpp new file mode 100644 index 000000000..470c28b35 --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTFunctions/RecursionRelation.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2018, Martin Duy Tat + * 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 <cmath> +#include <iostream> +#include <vector> +#include "RecursionRelation.h" +#include "DifferentialOperator.h" + +namespace polynomial { + +RecursionRelation::RecursionRelation(): power_m(0), highestXorder_m(10) { + std::vector<int> poly(1, 1); + operator_m.setPolynomial(poly, 0, 0); +} + +RecursionRelation::RecursionRelation(const std::size_t &power, + const std::size_t &highestXorder): + power_m(power), highestXorder_m(highestXorder) { + std::vector<int> poly(1, 1); + operator_m.setPolynomial(poly, 0, 0); + for (std::size_t i = 0; i < power_m; i++) { + applyOperator(); + } +} + +RecursionRelation::RecursionRelation(const RecursionRelation &doperator): + power_m(doperator.power_m) { + operator_m = DifferentialOperator(doperator.operator_m); +} + +RecursionRelation::~RecursionRelation() { +} + +RecursionRelation& RecursionRelation::operator= ( + const RecursionRelation &recursion) { + operator_m = recursion.operator_m; + power_m = recursion.power_m; + highestXorder_m = recursion.highestXorder_m; + return *this; +} + +void RecursionRelation::truncate(std::size_t highestXorder) { + highestXorder_m = highestXorder; + operator_m.truncate(highestXorder); +} + +/* This function increases n by 1 in the differential operator used to find \n + * the magnetic scalar potential + */ +void RecursionRelation::applyOperator() { +/* Differential operator has 3 terms, make three copies of current operator */ + DifferentialOperator firstTerm(operator_m); + DifferentialOperator secondTerm(operator_m); + DifferentialOperator thirdTerm(operator_m); +/* Initialise polynomials + * p(x) = 1 - x + x^2 - ... + * q(x) = 1 - 2x + 3x^2 - ... + */ + std::vector<int> p, q; + for (std::size_t i = 0; i <= highestXorder_m; i++) { + p.push_back(pow(-1, i)); + q.push_back(pow(-1, i) * (i + 1)); + } +/* Differentiate first term by x, then multiply by p(x) */ + firstTerm.differentiateX(); + firstTerm.multiplyPolynomial(Polynomial(p)); +/* Differentiate second term twice by x */ + secondTerm.differentiateX(); + secondTerm.differentiateX(); +/* Differentiate third term by s twice, then multiply by q(x) */ + thirdTerm.doubleDifferentiateS(); + thirdTerm.multiplyPolynomial(Polynomial(q)); +/* Add operators to obtain final operator */ + operator_m = DifferentialOperator(firstTerm); + operator_m.addOperator(secondTerm); + operator_m.addOperator(thirdTerm); +} + +} diff --git a/src/Classic/AbsBeamline/MultipoleTFunctions/RecursionRelation.h b/src/Classic/AbsBeamline/MultipoleTFunctions/RecursionRelation.h new file mode 100644 index 000000000..a31fbee48 --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTFunctions/RecursionRelation.h @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2018, Martin Duy Tat + * 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 RECURSION_RELATION_H +#define RECURSION_RELATION_H + +/** --------------------------------------------------------------------- + * + * RecursionRelation describes the differential operator used to find the \n + * coefficients in the expansion of the magnetic scalar potential \n + * It contains member functions for extracting all information required to \n + * reconstruct the differential operator and evaluate its terms.\n + * + * --------------------------------------------------------------------- + * + * Class category: AbsBeamline \n + * Author: Martin Duy Tat\n + * + * --------------------------------------------------------------------- + * The operator of interest is + * \f{eqnarray*} { + * \Big(\frac{1}{\rho(1 &+& x/\rho)}\frac{\partial}{\partial x} + * + \frac{\partial^2}{\partial x^2} + * &+& \frac{1}{(1 + x/\rho)^2}\frac{\partial^2}{\partial s^2}\Big)^n + * \f} + * and it can be initialised to any power of x and up to any n. + */ + +#include "DifferentialOperator.h" +#include "Polynomial.h" + +namespace polynomial { + +class RecursionRelation { +public: + /** Default constructor, initialises identity operator */ + RecursionRelation(); + /** Constructor, initialises the operator + * \f{eqnarray*} { + * \Big(\frac{1}{\rho(1 + x/\rho)}\frac{\partial}{\partial x} + * + \frac{\partial^2}{\partial x^2} + * + \frac{1}{(1 + x/\rho)^2}\frac{\partial^2}{\partial s^2}\Big)^n + * \f} + * where power = n. The denominators are expanded in x and \n + * highestXorder is the highest power of x \n + * \param power -> Number of times the differential operator is applied + * \param highestXorder -> Highest order of x before truncation + */ + RecursionRelation(const std::size_t &power, + const std::size_t &highestXorder); + /** Copy constructor */ + RecursionRelation(const RecursionRelation &doperator); + /** Desctructor, does nothing */ + ~RecursionRelation(); + /** Assigment operator */ + RecursionRelation& operator= (const RecursionRelation &recursion); + /** Print operator, used for internal debugging */ + void printOperator() const; + /** Truncate series in x at highestXorder + * \param highestXorder -> Highest order of x after truncation + */ + void truncate(std::size_t highestXorder); + /** Increase power n by one */ + void applyOperator(); + /** Apply a differential operator in x */ + void differentiateX(); + /** Returns highest derivative of x */ + std::size_t getMaxXDerivatives() const; + /** Returns highest derivative of s */ + std::size_t getMaxSDerivatives() const; + /** Evaluates polynomial + * \param x -> Point at which polynomial is evaluated + * \param xDerivative -> Number of x-derivatives + * \param sDerivative -> Number of s-derivatives + */ + double evaluatePolynomial(const double &x, + const std::size_t &xDerivative, + const std::size_t &sDerivative) const; + /** Check if polynomial with x x-derivatives and s s-derivatives is zero + * \param xDerivative -> Number of x-derivatives + * \param sDerivative -> Number of s-derivatives + */ + bool isPolynomialZero(const std::size_t &x, const std::size_t &s) const; + /** Change number of x-derivatives to xDerivatives + * \param xDerivative -> Number of x-derivatives + */ + void resizeX(const std::size_t &xDerivatives); + /** Change number of s-derivatives to sDerivatives + * \param sDerivative -> Number of s-derivatives + */ + void resizeS(const std::size_t &sDerivatives); +private: + DifferentialOperator operator_m; + std::size_t power_m; + std::size_t highestXorder_m; +}; + +inline + void RecursionRelation::printOperator() const { + operator_m.printOperator(); +} +inline + void RecursionRelation::differentiateX() { + operator_m.differentiateX(); +} +inline + std::size_t RecursionRelation::getMaxXDerivatives() const { + return operator_m.getXDerivatives(); +} +inline + std::size_t RecursionRelation::getMaxSDerivatives() const { + return operator_m.getSDerivatives(); +} +inline + double RecursionRelation::evaluatePolynomial( + const double &x, + const std::size_t &xDerivative, + const std::size_t &sDerivative) const { + return operator_m.evaluatePolynomial(x, xDerivative, sDerivative); +} +inline + bool RecursionRelation::isPolynomialZero(const std::size_t &x, + const std::size_t &s) const { + return operator_m.isPolynomialZero(x, s); +} +inline + void RecursionRelation::resizeX(const std::size_t &xDerivatives) { + operator_m.resizeX(xDerivatives); +} +inline + void RecursionRelation::resizeS(const std::size_t &sDerivatives) { + operator_m.resizeS(sDerivatives); +} + +} + +#endif diff --git a/src/Classic/AbsBeamline/MultipoleTFunctions/RecursionRelationTwo.cpp b/src/Classic/AbsBeamline/MultipoleTFunctions/RecursionRelationTwo.cpp new file mode 100644 index 000000000..4c4485ac4 --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTFunctions/RecursionRelationTwo.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2018, Martin Duy Tat + * 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 <cmath> +#include <vector> +#include <iostream> +#include "RecursionRelationTwo.h" +#include "DifferentialOperatorTwo.h" +#include "TwoPolynomial.h" + +namespace polynomial { + +RecursionRelationTwo::RecursionRelationTwo(): power_m(0), highestXorder_m(10) { + std::vector<int> poly2(1, 1); + std::vector<std::vector<int>> poly1; + poly1.push_back(poly2); + TwoPolynomial poly(poly1); + operator_m.setPolynomial(poly, 0, 0); +} + +RecursionRelationTwo::RecursionRelationTwo(const std::size_t &power, + const std::size_t &highestXorder): + power_m(power), highestXorder_m(highestXorder) { + std::vector<int> poly2(1, 1); + std::vector<std::vector<int>> poly1; + poly1.push_back(poly2); + TwoPolynomial poly(poly1); + operator_m.setPolynomial(poly, 0, 0); + for (std::size_t i = 0; i < power_m; i++) { + applyOperator(); + truncate(highestXorder); + sortTerms(); + } +} + +RecursionRelationTwo::RecursionRelationTwo( + const RecursionRelationTwo &doperator): + power_m(doperator.power_m) { + operator_m = DifferentialOperatorTwo(doperator.operator_m); +} + +RecursionRelationTwo::~RecursionRelationTwo() { +} + +RecursionRelationTwo& RecursionRelationTwo::operator= ( + const RecursionRelationTwo &recursion) { + operator_m = recursion.operator_m; + power_m = recursion.power_m; + highestXorder_m = recursion.highestXorder_m; + return *this; +} + +/* This function increases n by 1 in the differential operator used to find \n + * the magnetic scalar potential + */ +void RecursionRelationTwo::applyOperator() { +/* Differential operator has 3 terms, make three copies of current operator */ + DifferentialOperatorTwo firstTerm(operator_m); + DifferentialOperatorTwo secondTerm(operator_m); + DifferentialOperatorTwo thirdTerm(operator_m); +/* Initialise polynomials + * p(x) = s - xs^2 + x^2s^3 - ... + * q(x) = 1 - x + x^2 - ... + */ + std::vector<std::vector<int>> p, q; + p.resize(highestXorder_m + 1); + q.resize(highestXorder_m + 1); + for (std::size_t i = 0; i <= highestXorder_m; i++) { + p[i].resize(highestXorder_m + 2, 0); + q[i].resize(highestXorder_m + 1, 0); + p[i][i + 1] = pow(-1, i); + q[i][i] = pow(-1, i); + } +/* Differentiate first term by x, then multiply by p(x) */ + firstTerm.differentiateX(); + firstTerm.multiplyPolynomial(TwoPolynomial(p)); +/* Differentiate second term twice by x */ + secondTerm.differentiateX(); + secondTerm.differentiateX(); +/* Differentiate third term by s, multiply by q(x) + * and then differentiate by s again + */ + thirdTerm.differentiateS(); + thirdTerm.multiplyPolynomial(TwoPolynomial(q)); + thirdTerm.differentiateS(); + thirdTerm.multiplyPolynomial(TwoPolynomial(q)); +/* Add operators to obtain final operator */ + operator_m = DifferentialOperatorTwo(firstTerm); + operator_m.addOperator(secondTerm); + operator_m.addOperator(thirdTerm); +} + +} diff --git a/src/Classic/AbsBeamline/MultipoleTFunctions/RecursionRelationTwo.h b/src/Classic/AbsBeamline/MultipoleTFunctions/RecursionRelationTwo.h new file mode 100644 index 000000000..5d3cd9cae --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTFunctions/RecursionRelationTwo.h @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2018, Martin Duy Tat + * 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 RECURSION_RELATION_TWO_H +#define RECURSION_RELATION_TWO_H + +/** --------------------------------------------------------------------- + * + * RecursionRelationTwo describes the differential operator used to find \n + * the coefficients in the expansion of the magnetic scalar potential. It \n + * contains member functions for extracting all information required to \n + * reconstruct the differential operator and evaluate its terms.\n + * + * --------------------------------------------------------------------- + * + * Class category: AbsBeamline \n + * Author: Martin Duy Tat\n + * + * --------------------------------------------------------------------- + * The operator of interest is + * \f{eqnarray*} { + * \Big(\frac{1}{\rho(s)(1 &+& x/\rho(s))}\frac{\partial}{\partial x} + + * \frac{\partial^2}{\partial x^2} &+& \frac{1}{1 + + * x/\rho(s)}\frac{\partial}{\partial s}\Big(\frac{1}{1 + + * x/\rho(s)}\frac{\partial}{\partial s}\Big)\Big)^n + * \f} + * and it can be initialised to any power of x and up to any n. + */ + +#include <vector> +#include "DifferentialOperatorTwo.h" +#include "PolynomialSum.h" +#include "TwoPolynomial.h" + +namespace polynomial { + +class RecursionRelationTwo { +public: + /** Default constructor, initialises identity operator */ + RecursionRelationTwo(); + /** Constructor, initialises the operator + * \f{eqnarray*} { + * \Big(\frac{1}{\rho(s)(1 + x/\rho(s))}\frac{\partial}{\partial x} + + * \frac{\partial^2}{\partial x^2} + \frac{1}{1 + x/\rho(s)} + * \frac{\partial}{\partial s}\Big(\frac{1}{1 + x/\rho(s)} + * \frac{\partial}{\partial s}\Big)\Big)^n + * \f} + * where power = n. The denominators are expanded in x and highestXorder \n + * is the highest power of x \n + * \param power -> Value of n of the coefficient fn + * \param highestXorder -> Highest power of x + */ + RecursionRelationTwo(const std::size_t &power, + const std::size_t &highestXorder); + /** Copy constructor */ + RecursionRelationTwo(const RecursionRelationTwo &doperator); + /** Desctructor, does nothing */ + ~RecursionRelationTwo(); + /** Assigment operator */ + RecursionRelationTwo& operator= (const RecursionRelationTwo &recursion); + /** Print operator, used for internal debugging */ + void printOperator() const; + /** Truncate series in x at highestXorder + * \param highestXorder -> Highest power of x after truncation + */ + void truncate(std::size_t highestXorder); + /** Applies another differential operator to the existing operator */ + void applyOperator(); + /** Apply a differential operator in x */ + void differentiateX(); + /** Apply a differential operator in s */ + void differentiateS(); + /** Returns highest derivative of x */ + std::size_t getMaxXDerivatives() const; + /** Returns highest derivative of s */ + std::size_t getMaxSDerivatives() const; + /** Evaluates polynomial term p with xDerivative x-derivatives \n + * and sDerivative s-derivatives + * If xDerivative, sDerivative or term are out or range + * then zero is returned \n + * \param x -> Point x where polynomial is evaluated + * \param s -> Point s where polynomial is evaluated + * \param xDerivative -> Number of x-derivatives + * \param sDerivative -> Number of s-derivatives + * \param term -> Term index + */ + double evaluatePolynomial(const double &x, + const double &s, + const std::size_t &xDerivative, + const std::size_t &sDerivative, + const std::vector<double> &dSvalues) const; + /** Check if polynomial term p with x x-derivatives + * and s s-derivatives is zero \n + * If x, s or term are negative true is returned + * \param x -> Number of x-derivatives + * \param s -> Number of s-derivatives + * \param term -> Term index + */ + bool isPolynomialZero(const std::size_t &x, + const std::size_t &s, + const std::size_t &term) const; + /** Change number of x-derivatives to xDerivatives + * \param xDerivatives -> Number of x-derivatives after resize + */ + void resizeX(const std::size_t &xDerivatives); + /** Change number of s-derivatives to sDerivatives + * \param sDerivatives -> Number of s-derivatives after resize + */ + void resizeS(const std::size_t &sDerivatives); + /** Returns number of terms in the sum with xDerivative x-derivatives + * and sDerivative s-derivatives \n + * If xDerivative or sDerivative is negative zero is returned + * \param x -> Number of x-derivatives + * \param s -> Number of s-derivatives + */ + std::size_t numberOfTerms(const std::size_t &xDerivative, + const std::size_t &sDerivative) const; + /** Returns list of S(s)-derivatives in term p with xDerivative + * x-derivatives and sDerivative s-derivatives \n + * If xDerivative or sDerivative are out of range + * an empty list is returned \n + * \param x -> Number of x-derivatives + * \param s -> Number of s-derivatives + * \param term -> Term index + */ + std::vector<std::size_t> getdSfactors(const std::size_t &xDerivative, + const std::size_t &sDerivative, + const std::size_t &p) const; + /** Sort the terms in each sum, with fewest S(s)-derivatives first, + * then in increasing powers + */ + void sortTerms(); +private: + DifferentialOperatorTwo operator_m; + std::size_t power_m; + std::size_t highestXorder_m; +}; + +/*inline + void RecursionRelationTwo::printOperator() const { + operator_m.printOperator(); +}*/ +inline + void RecursionRelationTwo::truncate(std::size_t highestXorder) { + highestXorder_m = highestXorder; + operator_m.truncate(highestXorder); +} +inline + void RecursionRelationTwo::differentiateX() { + operator_m.differentiateX(); +} +inline + void RecursionRelationTwo::differentiateS() { + operator_m.differentiateS(); +} +inline + std::size_t RecursionRelationTwo::getMaxXDerivatives() const { + return operator_m.getXDerivatives(); +} +inline + std::size_t RecursionRelationTwo::getMaxSDerivatives() const { + return operator_m.getSDerivatives(); +} +inline + double RecursionRelationTwo::evaluatePolynomial( + const double &x, + const double &s, + const std::size_t &xDerivative, + const std::size_t &sDerivative, + const std::vector<double> &dSvalues) const { + return operator_m.evaluatePolynomial(x, s, + xDerivative, sDerivative, + dSvalues); +} +inline + bool RecursionRelationTwo::isPolynomialZero(const std::size_t &x, + const std::size_t &s, + const std::size_t &term) const { + return operator_m.isPolynomialZero(x, s, term); +} +inline + void RecursionRelationTwo::resizeX(const std::size_t &xDerivatives) { + operator_m.resizeX(xDerivatives); +} +inline + void RecursionRelationTwo::resizeS(const std::size_t &sDerivatives) { + operator_m.resizeS(sDerivatives); +} +inline + std::size_t RecursionRelationTwo::numberOfTerms( + const std::size_t &xDerivative, + const std::size_t &sDerivative) const { + return operator_m.numberOfTerms(xDerivative, sDerivative); +} +inline + std::vector<std::size_t> RecursionRelationTwo::getdSfactors( + const std::size_t &xDerivative, + const std::size_t &sDerivative, + const std::size_t &p) const { + return operator_m.getdSFactors(xDerivative, sDerivative, p); +} +inline + void RecursionRelationTwo::sortTerms() { + operator_m.sortTerms(); +} + +} + +#endif diff --git a/src/Classic/AbsBeamline/MultipoleTFunctions/TwoPolynomial.cpp b/src/Classic/AbsBeamline/MultipoleTFunctions/TwoPolynomial.cpp new file mode 100644 index 000000000..d3e8aeeed --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTFunctions/TwoPolynomial.cpp @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2018, Martin Duy Tat + * 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 <cmath> +#include <iostream> +#include <stdexcept> +#include "gsl/gsl_fft_complex.h" +#include "TwoPolynomial.h" + +namespace polynomial { + +TwoPolynomial::TwoPolynomial(): maxXorder_m(0), maxSorder_m(0) { + std::vector<int> temp; + temp.push_back(0); + coefficients_m.push_back(temp); +} + +TwoPolynomial::TwoPolynomial(const std::vector<std::vector<int>> &coefficients): + maxXorder_m(coefficients.size() - 1), + coefficients_m(coefficients) { + if (coefficients.size() == 0) { + maxXorder_m = 0; + maxSorder_m = 0; + std::vector<int> temp; + temp.push_back(0); + coefficients_m.push_back(temp); + return; + } + maxSorder_m = coefficients[0].size() - 1; + std::size_t len = maxSorder_m + 1; + for (std::size_t i = 0; i < coefficients.size(); i++) { + if (coefficients[i].size() != len) { + throw std::length_error("2D vector not rectangular"); + } + if (coefficients[i].size() == 0) { + maxXorder_m = 0; + maxSorder_m = 0; + std::vector<int> temp; + temp.push_back(0); + coefficients_m.resize(0); + coefficients_m.push_back(temp); + return; + } + } +} + +TwoPolynomial::TwoPolynomial(const TwoPolynomial &poly): + maxXorder_m(poly.maxXorder_m), maxSorder_m(poly.maxSorder_m), + coefficients_m(poly.coefficients_m), dSfactors_m(poly.dSfactors_m) { +} + +TwoPolynomial::~TwoPolynomial() { +} + +TwoPolynomial& TwoPolynomial::operator= (const TwoPolynomial &poly) { + maxXorder_m = poly.maxXorder_m; + maxSorder_m = poly.maxSorder_m; + coefficients_m = poly.coefficients_m; + dSfactors_m = poly.dSfactors_m; + return *this; +} + +void TwoPolynomial::differentiateX() { + if (maxXorder_m == 0 && maxSorder_m == 0 && coefficients_m[0][0] == 0) { + return; + } + if (maxXorder_m == 0) { + std::vector<std::vector<int>> temp {{0}}; + coefficients_m = temp; + maxSorder_m = 0; + return; + } + for (std::size_t i = 0; i < maxXorder_m; i++) { + for (std::size_t j = 0; j <= maxSorder_m; j++) { + coefficients_m[i][j] = coefficients_m[i + 1][j] * (i + 1); + } + } + coefficients_m.pop_back(); + maxXorder_m--; +} + +void TwoPolynomial::differentiateS() { + if (maxXorder_m == 0 && maxSorder_m == 0 && coefficients_m[0][0] == 0) { + return; + } + if (maxSorder_m == 0) { + std::vector<std::vector<int>> temp {{0}}; + coefficients_m = temp; + maxXorder_m = 0; + return; + } + for (std::size_t i = 0; i <= maxXorder_m; i++) { + for (std::size_t j = 0; j < maxSorder_m; j++) { + coefficients_m[i][j] = coefficients_m[i][j + 1] * (j + 1); + } + coefficients_m[i].pop_back(); + } + if (dSfactors_m.size() != 0) { + dSfactors_m[0] = dSfactors_m[0] + 1; + } else { + dSfactors_m.push_back(1); + } + maxSorder_m--; +} + +void TwoPolynomial::multiplyConstant (const int &constant) { + if (maxXorder_m == 0 && maxSorder_m == 0 && coefficients_m[0][0] == 0) { + return; + } + if (constant == 0) { + setZero(); + return; + } + for (std::size_t i = 0; i <= maxXorder_m; i++) { + for (std::size_t j = 0; j <= maxSorder_m; j++) { + coefficients_m[i][j] = constant * coefficients_m[i][j]; + } + } +} + +void TwoPolynomial::multiplydSfactors(const TwoPolynomial &poly) { + if (poly.dSfactors_m.size() > dSfactors_m.size()) { + dSfactors_m.resize(poly.dSfactors_m.size(), 0); + } + for (std::size_t k = 0; k < poly.dSfactors_m.size(); k++) { + dSfactors_m[k] = dSfactors_m[k] + poly.dSfactors_m[k]; + } +} + +void TwoPolynomial::convert2Dto1Darray(double *vec1, double *vec2, + const vectorLengths &nn, + const TwoPolynomial &poly) const { + for (std::size_t i = 0; i < nn.nx; i++) { + for (std::size_t j = 0; j < nn.ny; j++) { + if (i < nn.nx1 && j < nn.ny1) { + vec1[2 * (nn.ny * i + j)] = coefficients_m[i][j]; + vec1[2 * (nn.ny * i + j) + 1] = 0; + } else { + vec1[2 * (nn.ny * i + j)] = 0; + vec1[2 * (nn.ny * i + j) + 1] = 0; + } + if (i < nn.nx2 && j < nn.ny2) { + vec2[2 * (nn.ny * i + j)] = poly.coefficients_m[i][j]; + vec2[2 * (nn.ny * i + j) + 1] = 0; + } else { + vec2[2 * (nn.ny * i + j)] = 0; + vec2[2 * (nn.ny * i + j) + 1] = 0; + } + } + } +} + +void TwoPolynomial::convolution(double *vec1, + double *vec2, + const std::size_t &nx, + const std::size_t &ny) const { + /** Do 2D FFT, 1D FFT on each row and column */ + for (std::size_t i = 0; i < nx; i++) { + double *p1 = &vec1[0] + 2 * i * ny; + gsl_fft_complex_radix2_forward(p1, 1, ny); + double *p2 = &vec2[0] + 2 * i * ny; + gsl_fft_complex_radix2_forward(p2, 1, ny); + } + for (std::size_t j = 0; j < ny; j++) { + double *p1 = &vec1[0] + 2 * j; + gsl_fft_complex_radix2_forward(p1, ny, nx); + double *p2 = &vec2[0] + 2 * j; + gsl_fft_complex_radix2_forward(p2, ny, nx); + } + /** Convolution, multiply each element (complex numbers) */ + for (std::size_t i = 0; i < nx; i++) { + for (std::size_t j = 0; j < ny; j++) { + double real1 = vec1[2 * (ny * i + j)]; + double real2 = vec2[2 * (ny * i + j)]; + double imag1 = vec1[2 * (ny * i + j) + 1]; + double imag2 = vec2[2 * (ny * i + j) + 1]; + vec1[2 * (ny * i + j)] = real1 * real2 - imag1 * imag2; + vec1[2 * (ny * i + j) + 1] = real1 * imag2 + imag1 * real2; + } + } + /** Inverse 2D FFT */ + for (std::size_t i = 0; i < nx; i++) { + double *p1 = &vec1[0] + 2 * i * ny; + gsl_fft_complex_radix2_inverse(p1, 1, ny); + } + for (std::size_t j = 0; j < ny; j++) { + double *p1 = &vec1[0] + 2 * j; + gsl_fft_complex_radix2_inverse(p1, ny, nx); + } +} + +void TwoPolynomial::convert1Dto2Darray(double *vec1, + const vectorLengths &nn) { + coefficients_m.resize(nn.nx1 + nn.nx2 - 1); + for (std::size_t i = 0; i < (nn.nx1 + nn.nx2 - 1); i++) { + coefficients_m[i].resize(nn.ny1 + nn.ny2 - 1); + for (std::size_t j = 0; j < (nn.ny1 + nn.ny2 - 1); j++) { + coefficients_m[i][j] = std::round(vec1[2 * (nn.ny * i + j)]); + } + } +} + +void TwoPolynomial::multiplyPolynomial(const TwoPolynomial &poly) { + if (maxXorder_m == 0 && maxSorder_m == 0 && coefficients_m[0][0] == 0) { + return; + } + if (poly.maxXorder_m == 0 && + poly.maxSorder_m == 0 && + poly.coefficients_m[0][0] == 0) { + setZero(); + return; + } + /** Multiply the S(s)-derivatives */ + multiplydSfactors(poly); + /** Find vector sizes and round up to nearest power of 2 */ + vectorLengths nn; + nn.nx = 1; + nn.ny = 1; + nn.nx1 = coefficients_m.size(); + nn.nx2 = poly.coefficients_m.size(); + nn.ny1 = coefficients_m[0].size(); + nn.ny2 = poly.coefficients_m[0].size(); + while ((nn.nx1 + nn.nx2 - 1) > nn.nx) { + nn.nx *= 2; + } + while ((nn.ny1 + nn.ny2 - 1) > nn.ny) { + nn.ny *= 2; + } + /** Allocate memory for 1D array */ + double *vec1 = new double[2 * nn.nx * nn.ny]; + double *vec2 = new double[2 * nn.nx * nn.ny]; + /** Convert 2D vectors into 1D vectors with indexing, change to double */ + convert2Dto1Darray(vec1, vec2, nn, poly); + /** Do convolution */ + convolution(vec1, vec2, nn.nx, nn.ny); + /** Convert back to 2D vector and change to int by rounding */ + convert1Dto2Darray(vec1, nn); + delete[] vec1; + delete[] vec2; + maxXorder_m = coefficients_m.size() - 1; + maxSorder_m = coefficients_m[0].size() - 1; +} + +void TwoPolynomial::printPolynomial() const { + if (maxXorder_m == 0 && maxSorder_m == 0 && coefficients_m[0][0] == 0) { + return; + } + std::cout << "("; + for (std::size_t i = 0; i <= maxXorder_m; i++) { + for (std::size_t j = 0; j <= maxSorder_m; j++) { + if (coefficients_m[i][j] > 0) { + std::cout << " + " << coefficients_m[i][j]; + std::cout << "x^" << i << "s^" << j; + } else if (coefficients_m[i][j] < 0) { + std::cout << " - " << -coefficients_m[i][j]; + std::cout << "x^" << i << "s^" << j; + } + } + } + std::cout << ")"; + for (std::size_t i = 0; i < dSfactors_m.size(); i++) { + std::cout << "(d^" << i + 1 << "S/ds)^" << dSfactors_m[i]; + } +} + +int TwoPolynomial::getCoefficient(const std::size_t &Xorder, + const std::size_t &Sorder) const { + if (Xorder > maxXorder_m || Sorder > maxSorder_m) { + return 0; + } + return coefficients_m[Xorder][Sorder]; +} + +void TwoPolynomial::setCoefficient(const int &coefficient, + const std::size_t &Xorder, + const std::size_t &Sorder) { + if (Xorder > maxXorder_m) { + coefficients_m.resize(Xorder + 1); + for (std::size_t i = (maxXorder_m + 1); i <= Xorder; i++) { + coefficients_m[i].resize(maxSorder_m + 1, 0); + } + maxXorder_m = coefficients_m.size() - 1; + } + if (Sorder > maxSorder_m) { + for (std::size_t i = 0; i < maxXorder_m; i++) { + coefficients_m[i].resize(Sorder + 1, 0); + } + maxSorder_m = coefficients_m[0].size() - 1; + } + coefficients_m[Xorder][Sorder] = coefficient; +} + +void TwoPolynomial::setMaxXorder(const std::size_t &maxXorder) { + coefficients_m.resize(maxXorder + 1); + for (std::size_t i = (maxXorder_m + 1); i <= maxXorder; i++) { + coefficients_m[i].resize(maxSorder_m + 1); + } + maxXorder_m = maxXorder; +} + +void TwoPolynomial::setMaxSorder(const std::size_t &maxSorder) { + for (std::size_t i = 0; i <= maxXorder_m; i++) { + coefficients_m[i].resize(maxSorder + 1); + } + maxSorder_m = maxSorder; +} + +void TwoPolynomial::setZero() { + maxXorder_m = 0; + maxSorder_m = 0; + coefficients_m.resize(1); + coefficients_m[0].resize(1); + coefficients_m[0][0] = 0; +} + +double TwoPolynomial::evaluatePolynomial(const double &x, + const double &s) const { + if (maxXorder_m == 0 && maxSorder_m == 0 && + coefficients_m[0][0] == 0) { + return 0; + } + double result = 0.0; + std::size_t i = maxXorder_m + 1; + while (i != 0) { + i--; + std::size_t j = maxSorder_m + 1; + double temp = 0.0; + while (j != 0) { + j--; + temp = temp * s + coefficients_m[i][j]; + } + result = result * x + temp; + } + return result; +} + +void TwoPolynomial::addPolynomial(const TwoPolynomial &poly) { + if (maxXorder_m < poly.maxXorder_m) { + setMaxXorder(poly.maxXorder_m); + } + if (maxSorder_m < poly.maxSorder_m) { + setMaxSorder(poly.maxSorder_m); + } + for (std::size_t i = 0; i <= poly.maxXorder_m; i++) { + for (std::size_t j = 0; j <= poly.maxSorder_m; j++) { + coefficients_m[i][j] += poly.coefficients_m[i][j]; + } + } +} + +bool operator < (const TwoPolynomial &left, const TwoPolynomial &right) { + std::size_t nleft = left.dSfactors_m.size(); + std::size_t nright = right.dSfactors_m.size(); + if (nleft < nright) { + return true; + } else if (nright < nleft) { + return false; + } else { + for (std::size_t n = 0; n < nleft; n++) { + std::size_t elemleft = left.dSfactors_m[n]; + std::size_t elemright = right.dSfactors_m[n]; + if (elemleft < elemright) { + return true; + } else if (elemright < elemleft) { + return false; + } + } + return false; + } +} + +bool operator == (const TwoPolynomial &left, const TwoPolynomial &right) { + if (left < right || right < left) { + return false; + } else { + return true; + } +} + +} diff --git a/src/Classic/AbsBeamline/MultipoleTFunctions/TwoPolynomial.h b/src/Classic/AbsBeamline/MultipoleTFunctions/TwoPolynomial.h new file mode 100644 index 000000000..e3b121e18 --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTFunctions/TwoPolynomial.h @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2018, Martin Duy Tat + * 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 TWO_POLYNOMIAL_H +#define TWO_POLYNOMIAL_H + +/** --------------------------------------------------------------------- + * + * TwoPolynomial describes a polynomial of two variables. In addition, \n + * TwoPolynomial also stores a list of derivatives of the fringe field S(s), \n + * because the second variable is S(s). Every s-derivative will therefore \n + * give various terms of S(s)-derivatives from the chain rule. \n + * + * --------------------------------------------------------------------- + * + * Class category: AbsBeamline \n + * Author: Martin Duy Tat\n + * + * --------------------------------------------------------------------- + * The polynomial @f[p(x) = a_{00} + a_{10}x + a_{11}xS(s) + ... + * + a_{nm}x^nS(s)^m $f] is \n stored as a two-dimensional list \n + * (a_{00}, a_{10}, ..., a_{n0}; a_{01}, ...). \n + * In addition a list @f[b_k] of powers of S(s)-derivatives is stored, + * representing @f[\prod_k\frac{dS(s)}{ds}^{b_k}] + * A TwoPolynomial object therefore represents a product of a polynomial \n + * in x and S(s), multiplied by derivatives of S(s), and it is the powers \n + * that are stored in this list + * BUG: For large n the integer type might overflow. If you need this \n + * many terms change all int to long int. \n + */ + +#include <vector> + +namespace polynomial { + +/** Struct for storing vector lengths used in multiplyPolynomial() + * \param nx -> Size of first dimension of resulting array + * \param ny -> Size of second dimension of resulting array + * \param nx1 -> Size of first dimension of first array + * \param ny1 -> Size of second dimension of first array + * \param nx2 -> Size of first dimension of second array + * \param ny2 -> Size of second dimension of second array + */ +struct vectorLengths { + std::size_t nx; + std::size_t ny; + std::size_t nx1; + std::size_t ny1; + std::size_t nx2; + std::size_t ny2; +}; + +class TwoPolynomial { +public: + /** Default constructor, initialises zero polynomial */ + TwoPolynomial(); + /** Constructor, initialises polynomial with the input coefficients \n + * If either list dimensions are empty, a zero polynomial is initialised + * \param coefficients -> 2D list of polynomial coefficients, with + * x powers as first argument and s powers as second argument, starting + * with a constant term + */ + TwoPolynomial(const std::vector<std::vector<int>> &coefficients); + /** Copy constructor */ + TwoPolynomial(const TwoPolynomial &poly); + /** Assigment operator */ + TwoPolynomial& operator= (const TwoPolynomial &poly); + /** Destructor, does nothing */ + ~TwoPolynomial(); + /** Differentiate polynomial wrt x */ + void differentiateX(); + /** Differentiate polynomial wrt s */ + void differentiateS(); + /** Multiply polynomial with input constant + * \param constant -> Constant to be multiplied with polynomial + */ + void multiplyConstant (const int &constant); + /** Multiply S(s)-derivatives part with input polynomial + * \param poly -> Polynomial to be multiplied with current polynomial + */ + void multiplydSfactors(const TwoPolynomial &poly); + /** Convert two 2D vector<int> array into 1D double C-array by indexing + * \param vec1 -> Pointer to the first 1D array + * \param vec2 -> Pointer to the second 1D array + * \param nn -> Vector sizes + * \param poly -> Polynomial to be multiplied with current polynomial + */ + void convert2Dto1Darray(double *vec1, double *vec2, + const vectorLengths &nn, + const TwoPolynomial &poly) const; + /** Perform a 2D convolution on two 1D indexed arrays using FFT \n + * First a 2D FFT is done by doing 1D FFT on each row and and column \n + * Convolution in Fourier space is a simple product \n + * Then the inverse 2D FFT is done on first list + * \param vec1 -> First array, also resulting array + * \param vec2 -> Second array + * \param nx -> Size of first dimension of resulting array + * \param ny -> Size of second dimension of resulting array + */ + void convolution(double *vec1, double *vec2, + const std::size_t &nx, const std::size_t &ny) const; + /** Convert 1D double indexed C-array to 2D vector<int> array by rounding + * \param vec1 -> Pointer to resulting 1D array + * \param nn -> Vector sizes + */ + void convert1Dto2Darray(double *vec1, const vectorLengths &nn); + /** Multiply polynomial with input polynomial using FFT and convolution + * \param poly -> Polynomial to be multiplied with current polynomial + */ + void multiplyPolynomial(const TwoPolynomial &poly); + /** Print polynomial, for internal debugging */ + void printPolynomial() const; + /** Returns coefficient with power Xorder in x and Sorder in s + * \param Xorder -> Power of x + * \param Sorder -> Power of s + */ + int getCoefficient(const std::size_t &Xorder, + const std::size_t &Sorder) const; + /** Set coefficient of term with Xorder powers of x and Sorder powers of s + * \param coefficient -> Value assigned to polynomial coefficient + * \param Xorder -> Power of x + * \param Sorder -> Power of s + */ + void setCoefficient(const int &coefficient, + const std::size_t &Xorder, + const std::size_t &Sorder); + /** Returns highest power of x */ + std::size_t getMaxXorder() const; + /** Returns highest power of s */ + std::size_t getMaxSorder() const; + /** Set highest power of x + * \param maxXorder -> Highest power of x + */ + void setMaxXorder(const std::size_t &maxXorder); + /** Set highest power of s + * \param maxSorder -> Highest power of s + */ + void setMaxSorder(const std::size_t &maxSorder); + /** Return highest derivative of S(s) */ + std::size_t getNdSfactors() const; + /** Returns the list of powers of S(s)-derivatives */ + std::vector<std::size_t> getdSfactors() const; + /** Set the list of S(s)-derivatives + * \param dSfactors -> List of S(s)-derivatives + */ + void setdSfactors(const std::vector<std::size_t> &dSfactors); + /** Set polynomial to zero */ + void setZero(); + /** Check if polynomial is zero */ + bool isZero() const; + /** Evaluate polynomial at point (x, s) + * \param x -> x-coordinate where polynomial is evaluated + * \param s -> s-coordinate where polynomial is evaluated + */ + double evaluatePolynomial(const double &x, const double &s) const; + /** Truncate polynomial in x at truncateOrder + * \param truncateOrder -> Highest power of x after truncation + */ + void truncate(const std::size_t &truncateOrder); + /** Add two polynomials \n + * The S(s)-derivatives are untouched, the user must ensure + * that these are identical! + * \param poly -> TwoPolynomial added to this polynomial + */ + void addPolynomial(const TwoPolynomial &poly); + /** Operator < definition + * First the TwoPolynomial with the longest dSfactors_m list is greatest \n + * If dSfactors_m have the same length, each list element is compared + * individually, starting with the first element (lowest derivative) + * \param poly -> The TwoPolynomial to be compared with + */ + friend bool operator < (const TwoPolynomial &left, + const TwoPolynomial &right); + /** Operator == definition \n + * Returns true if dSfactors_m are identical \n + * Only uses the < operator + * \param poly -> The TwoPolynomial to be compared with + */ + friend bool operator == (const TwoPolynomial &left, + const TwoPolynomial &right); + /** PolynomialSum is a sum of TwoPolynomials, useful to have as friend */ + friend class PolynomialSum; +private: + std::size_t maxXorder_m; + std::size_t maxSorder_m; + std::vector<std::vector<int>> coefficients_m; + std::vector<std::size_t> dSfactors_m; +}; + +inline + std::size_t TwoPolynomial::getMaxXorder() const { + return maxXorder_m; +} +inline + std::size_t TwoPolynomial::getMaxSorder() const { + return maxSorder_m; +} +inline + std::size_t TwoPolynomial::getNdSfactors() const { + return dSfactors_m.size(); +} +inline + std::vector<std::size_t> TwoPolynomial::getdSfactors() const { + return dSfactors_m; +} +inline + void TwoPolynomial::setdSfactors( + const std::vector<std::size_t> &dSfactors) { + dSfactors_m = dSfactors; +} +inline + bool TwoPolynomial::isZero() const { + return (maxXorder_m == 0 && maxSorder_m == 0 && + coefficients_m[0][0] == 0); +} +inline + void TwoPolynomial::truncate(const std::size_t &truncateOrder) { + if (truncateOrder < maxXorder_m) { + coefficients_m.resize(truncateOrder + 1); + maxXorder_m = truncateOrder; + } +} + +} + +#endif diff --git a/src/Classic/AbsBeamline/MultipoleTFunctions/tanhDeriv.cpp b/src/Classic/AbsBeamline/MultipoleTFunctions/tanhDeriv.cpp new file mode 100644 index 000000000..6463c3d3b --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTFunctions/tanhDeriv.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2018, Martin Duy Tat + * 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 <gsl/gsl_integration.h> +#include <gsl/gsl_complex.h> +#include <gsl/gsl_complex_math.h> +#include <gsl/gsl_sf_pow_int.h> +#include <gsl/gsl_math.h> +#include <gsl/gsl_errno.h> +#include <gsl/gsl_sf_gamma.h> +#include "tanhDeriv.h" + +namespace tanhderiv { + +struct my_f_params { + double a; + double s0; + double lambdaleft; + double lambdaright; + double r; + int n; +}; + +double my_f (double x, void *p) { + struct my_f_params *params = (struct my_f_params *)p; + gsl_complex z = gsl_complex_add(gsl_complex_rect(params->a, 0), + gsl_complex_polar(params->r, x)); + gsl_complex z1 = gsl_complex_div(gsl_complex_add(z, + gsl_complex_rect(params->s0, 0)), + gsl_complex_rect(params->lambdaleft, 0)); + gsl_complex z2 = gsl_complex_div(gsl_complex_sub(z, + gsl_complex_rect(params->s0, 0)), + gsl_complex_rect(params->lambdaright, 0)); + gsl_complex func = gsl_complex_div(gsl_complex_sub(gsl_complex_tanh(z1), + gsl_complex_tanh(z2)), + gsl_complex_rect(2, 0)); + func = gsl_complex_mul(func, gsl_complex_polar(1, -params->n * x)); + return gsl_sf_fact(params->n) * GSL_REAL(func) + / (2 * M_PI * gsl_sf_pow_int(params->r, params->n)); +} + +double integrate(const double &a, + const double &s0, + const double &lambdaleft, + const double &lambdaright, + const int &n) { + gsl_function F; + double radius = gsl_hypot(a - 2, lambdaright * M_PI / 2) - 0.01; + double radius2 = gsl_hypot(a + 2, lambdaleft * M_PI / 2) - 0.01; + if (radius > radius2) radius = radius2; + struct my_f_params params = {a, s0, lambdaleft, lambdaright, radius, n}; + F.function = &my_f; + F.params = ¶ms; + gsl_integration_workspace *w = gsl_integration_workspace_alloc(100); + double error = gsl_sf_pow_int(10, -12); + double *result = new double; + double *abserr = new double; + gsl_set_error_handler_off(); + int status = gsl_integration_qag(&F, 0, 2 * M_PI, 0, error, + 100, 6, w, result, abserr); + gsl_integration_workspace_free(w); + double finalResult = *result; + delete result; + delete abserr; + if (status) { + return 0; + } + else return finalResult; +} + +} diff --git a/src/Classic/AbsBeamline/MultipoleTFunctions/tanhDeriv.h b/src/Classic/AbsBeamline/MultipoleTFunctions/tanhDeriv.h new file mode 100644 index 000000000..cccde2c8e --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTFunctions/tanhDeriv.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018, Martin Duy Tat + * 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 TANH_INTEGRAL +#define TANH_INTEGRAL + +/** --------------------------------------------------------------------- + * + * Integrate performs a contour integral to find the nth derivative of the \n + * Tanh model fringe function. It uses Cauchy's integral formula to do so. \n + * + * --------------------------------------------------------------------- + * + * Class category: AbsBeamline \n + * Author: Martin Duy Tat\n + * + * --------------------------------------------------------------------- + */ + +namespace tanhderiv { + +/** Integrand + * \param x -> Independent parameter to be integrated + * \param p -> Pointer to struct of parameters + */ +double my_f (double x, void *p); +/** Perform Cauchy's integral to find derivative + * \param a -> Point of differentiation + * \param s0 -> Centre fringe length + * \param lambdaleft -> Left fringe field length + * \param lambdaright -> Left fringe field length + * \param n -> nth derivative + */ +double integrate(const double &a, + const double &s0, + const double &lambdaleft, + const double &lambdaright, + const int &n); + +} + +#endif diff --git a/src/Classic/AbsBeamline/MultipoleTStraight.cpp b/src/Classic/AbsBeamline/MultipoleTStraight.cpp new file mode 100644 index 000000000..21769e69f --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTStraight.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017, Titus Dascalu + * Copyright (c) 2018, Martin Duy Tat + * 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 "gsl/gsl_sf_gamma.h" +#include "gsl/gsl_sf_pow_int.h" +#include "MultipoleTStraight.h" + +using namespace endfieldmodel; + +MultipoleTStraight::MultipoleTStraight(const std::string &name): + MultipoleTBase(name), + straightGeometry_m(getLength()) { +} + +MultipoleTStraight::MultipoleTStraight(const MultipoleTStraight &right): + MultipoleTBase(right), + straightGeometry_m(right.straightGeometry_m) { + RefPartBunch_m = right.RefPartBunch_m; +} + + +MultipoleTStraight::~MultipoleTStraight() { +} + +ElementBase* MultipoleTStraight::clone() const { + return new MultipoleTStraight(*this); +} + +void MultipoleTStraight::transformCoords(Vector_t &R) { + //R[2] += getBoundingBoxLength(); +} + +void MultipoleTStraight::setMaxOrder(const std::size_t &maxOrder) { + MultipoleTBase::setMaxOrder(maxOrder); +} + +double MultipoleTStraight::getBx(const Vector_t &R) { + double Bx = 0.0; + for(std::size_t n = 0; n <= getMaxOrder(); n++) { + double f_n = 0.0; + for(std::size_t i = 0; i <= n; i++) { + f_n += gsl_sf_choose(n, i) * getTransDeriv(2 * i + 1, R[0]) * + getFringeDeriv(2 * n - 2 * i, R[2]); + } + f_n *= gsl_sf_pow_int(-1.0, n); + Bx += f_n * gsl_sf_pow_int(R[1], 2 * n + 1) / gsl_sf_fact(2 * n + 1); + } + return Bx; +} + +double MultipoleTStraight::getBs(const Vector_t &R) { + double Bs = 0.0; + for(std::size_t n = 0; n <= getMaxOrder(); n++) { + double f_n = 0.0; + for(std::size_t i = 0; i <= n; i++) { + f_n += gsl_sf_choose(n, i) * getTransDeriv(2 * i, R[0]) * + getFringeDeriv(2 * n - 2 * i + 1, R[2]); + } + f_n *= gsl_sf_pow_int(-1.0, n); + Bs += f_n * gsl_sf_pow_int(R[1], 2 * n + 1) / gsl_sf_fact(2 * n + 1); + } + return Bs; +} + +double MultipoleTStraight::getFn(const std::size_t &n, + const double &x, + const double &s) { + if (n == 0) { + return getTransDeriv(0, x) * getFringeDeriv(0, s); + } + double f_n = 0.0; + for (std::size_t i = 0; i <= n; i++) { + f_n += gsl_sf_choose(n, i) * getTransDeriv(2 * i, x) * + getFringeDeriv(2 * n - 2 * i, s); + } + f_n *= gsl_sf_pow_int(-1.0, n); + return f_n; +} + diff --git a/src/Classic/AbsBeamline/MultipoleTStraight.h b/src/Classic/AbsBeamline/MultipoleTStraight.h new file mode 100644 index 000000000..38157a974 --- /dev/null +++ b/src/Classic/AbsBeamline/MultipoleTStraight.h @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2017, Titus Dascalu + * Copyright (c) 2018, Martin Duy Tat + * 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 CLASSIC_MULTIPOLET_STRAIGHT_H +#define CLASSIC_MULTIPOLET_STRAIGHT_H + +/** --------------------------------------------------------------------- + * + * MultipoleTiStraight defines a straight combined function magnet (up + * to arbitrary multipole component) with fringe fields + * + * --------------------------------------------------------------------- + * + * Class category: AbsBeamline \n + * $Author: Titus Dascalu, Martin Duy Tat, Chris Rogers + * + * --------------------------------------------------------------------- + * + * The field is obtained from the scalar potential \n + * @f[ V = f_0(x,s) z + f_1 (x,s) \frac{z^3}{3!} + f_2 (x,s) \frac{z^5}{5!} + * + ... @f] \n + * (x,z,s) -> Frenet-Serret local coordinates along the magnet \n + * z -> vertical component \n + * assume mid-plane symmetry \n + * set field on mid-plane -> @f$ B_z = f_0(x,s) = T(x) \cdot S(s) @f$ \n + * T(x) -> transverse profile; this is a polynomial describing + * the field expansion on the mid-plane inside the magnet + * (not in the fringe field); + * 1st term is the dipole strength, 2nd term is the + * quadrupole gradient * x, etc. \n + * -> when setting the magnet, one gives the multipole + * coefficients of this polynomial (i.e. dipole strength, + * quadrupole gradient, etc.) \n + * \n + * ------------- example ----------------------------------------------- \n + * Setting a combined function magnet with dipole, quadrupole and + * sextupole components: \n + * @f$ T(x) = B_0 + B_1 \cdot x + B_2 \cdot x^2 @f$\n + * user gives @f$ B_0, B_1, B_2 @f$ \n + * ------------- example end ------------------------------------------- \n + * \n + * S(s) -> fringe field \n + * recursion -> @f$ f_n (x,s) = (-1)^n \cdot \sum_{i=0}^{n} C_n^i + * \cdot T^{(2i)} \cdot S^{(2n-2i)} @f$ \n + * for curved magnets the above recursion is more complicated \n + * @f$ C_n^i @f$ -> binomial coeff; + * @f$ T^{(n)} @f$ -> n-th derivative + * + * --------------------------------------------------------------------- + */ + +#include "BeamlineGeometry/StraightGeometry.h" +#include "AbsBeamline/MultipoleTBase.h" +#include <vector> + +class MultipoleTStraight: public MultipoleTBase { +public: + /** Constructor + * \param name -> User-defined name + */ + explicit MultipoleTStraight(const std::string &name); + /** Copy constructor */ + MultipoleTStraight(const MultipoleTStraight &right); + /** Destructor */ + ~MultipoleTStraight(); + /** Inheritable copy constructor */ + virtual ElementBase* clone() const; + /** Accept a beamline visitor */ + void accept(BeamlineVisitor &visitor) const; + /** Set the number of terms used in calculation of field components \n + * Maximum power of z in Bz is 2 * maxOrder_m + * \param maxOrder -> Number of terms in expansion in z + */ + virtual void setMaxOrder(const std::size_t &maxOrder); + /** Return the cell geometry */ + StraightGeometry& getGeometry(); + /** Return the cell geometry */ + const StraightGeometry& getGeometry() const; + /** Initialise the MultipoleT + * \param bunch -> Bunch the global bunch object + * \param startField -> Not used + * \param endField -> Not used + */ + virtual void initialise(PartBunchBase<double, 3>* bunch, + double &startField, + double &endField); +private: + MultipoleTStraight operator=(const MultipoleTStraight &rhs); + /** Geometry */ + StraightGeometry straightGeometry_m; + /** Transform to Frenet-Serret coordinates for sector magnets */ + virtual void transformCoords(Vector_t &R); + /** Transform B-field from Frenet-Serret coordinates to lab coordinates */ + virtual void transformBField(Vector_t &B, const Vector_t &R); + /** Radius of curvature \n + * Straight magnet, infinite radius, infinity (1.0e300) is returned + * \param s -> Coordinate s + */ + virtual double getRadius(const double &s); + /** Returns the scale factor @f$ h_s = 1@f$ + * \param x -> Coordinate x + * \param s -> Coordinate s + */ + virtual double getScaleFactor(const double &x, const double &s); + /** Get x-component of the B-field \n + * This function has been overloaded because calculating \n + * the B-field directly is quicker and more accurate + */ + virtual double getBx (const Vector_t &R); + /** Get s-component of the B-field \n + * This function has been overloaded because calculating \n + * the B-field directly is quicker and more accurate + */ + virtual double getBs (const Vector_t &R); + /** Calculate fn(x, s) by expanding the differential operator + * (from Laplacian and scalar potential) in terms of polynomials + * \param n -> nth derivative + * \param x -> Coordinate x + * \param s -> Coordinate s + */ + virtual double getFn(const std::size_t &n, + const double &x, + const double &s); +}; + +inline + void MultipoleTStraight::accept(BeamlineVisitor &visitor) const { + visitor.visitMultipoleTStraight(*this); +} +inline + void MultipoleTStraight::transformBField(Vector_t &B, const Vector_t &R) { +} +inline + double MultipoleTStraight::getRadius(const double &s) { + return 1e300; +} +inline + double MultipoleTStraight::getScaleFactor(const double &x, + const double &s) { + return 1.0; +} +inline + StraightGeometry& MultipoleTStraight::getGeometry() { + return straightGeometry_m; +} +inline + const StraightGeometry& MultipoleTStraight::getGeometry() const { + return straightGeometry_m; +} +inline + void MultipoleTStraight::initialise(PartBunchBase<double, 3>* bunch, + double &startField, + double &endField) { + RefPartBunch_m = bunch; + straightGeometry_m.setElementLength(2 * getBoundingBoxLength()); +} + +#endif diff --git a/src/Classic/AbsBeamline/SpecificElementVisitor.h b/src/Classic/AbsBeamline/SpecificElementVisitor.h index 62af61576..255f0b47a 100644 --- a/src/Classic/AbsBeamline/SpecificElementVisitor.h +++ b/src/Classic/AbsBeamline/SpecificElementVisitor.h @@ -21,6 +21,9 @@ #include "AbsBeamline/Monitor.h" #include "AbsBeamline/Multipole.h" #include "AbsBeamline/MultipoleT.h" +#include "AbsBeamline/MultipoleTStraight.h" +#include "AbsBeamline/MultipoleTCurvedConstRadius.h" +#include "AbsBeamline/MultipoleTCurvedVarRadius.h" #include "AbsBeamline/Patch.h" #include "AbsBeamline/Probe.h" #include "AbsBeamline/RBend.h" @@ -121,6 +124,15 @@ public: /// Apply the algorithm to a multipoleT. virtual void visitMultipoleT(const MultipoleT &); + /// Apply the algorithm to a multipoleTStraight. + virtual void visitMultipoleTStraight(const MultipoleTStraight &); + + /// Apply the algorithm to a multipoleT. + virtual void visitMultipoleTCurvedConstRadius(const MultipoleTCurvedConstRadius &); + + /// Apply the algorithm to a multipoleT. + virtual void visitMultipoleTCurvedVarRadius(const MultipoleTCurvedVarRadius &); + /// Apply the algorithm to an Offset. virtual void visitOffset(const Offset &); @@ -322,6 +334,21 @@ void SpecificElementVisitor<ELEM>::visitMultipoleT(const MultipoleT &element) { CastsTrait<ELEM, MultipoleT>::apply(allElementsOfTypeE, element); } +template<class ELEM> +void SpecificElementVisitor<ELEM>::visitMultipoleTStraight(const MultipoleTStraight &element) { + CastsTrait<ELEM, MultipoleTStraight>::apply(allElementsOfTypeE, element); +} + +template<class ELEM> +void SpecificElementVisitor<ELEM>::visitMultipoleTCurvedConstRadius(const MultipoleTCurvedConstRadius &element) { + CastsTrait<ELEM, MultipoleTCurvedConstRadius>::apply(allElementsOfTypeE, element); +} + +template<class ELEM> +void SpecificElementVisitor<ELEM>::visitMultipoleTCurvedVarRadius(const MultipoleTCurvedVarRadius &element) { + CastsTrait<ELEM, MultipoleTCurvedVarRadius>::apply(allElementsOfTypeE, element); +} + template<class ELEM> void SpecificElementVisitor<ELEM>::visitOffset(const Offset &element) { CastsTrait<ELEM, Offset>::apply(allElementsOfTypeE, element); @@ -509,4 +536,4 @@ typename SpecificElementVisitor<ELEM>::const_reference_t SpecificElementVisitor< return allElementsOfTypeE.front(); } -#endif \ No newline at end of file +#endif diff --git a/src/Classic/Algorithms/DefaultVisitor.cpp b/src/Classic/Algorithms/DefaultVisitor.cpp index 2125b2f0f..1bb44a3a2 100644 --- a/src/Classic/Algorithms/DefaultVisitor.cpp +++ b/src/Classic/Algorithms/DefaultVisitor.cpp @@ -36,6 +36,9 @@ #include "AbsBeamline/Monitor.h" #include "AbsBeamline/Multipole.h" #include "AbsBeamline/MultipoleT.h" +#include "AbsBeamline/MultipoleTStraight.h" +#include "AbsBeamline/MultipoleTCurvedConstRadius.h" +#include "AbsBeamline/MultipoleTCurvedVarRadius.h" #include "AbsBeamline/Patch.h" #include "AbsBeamline/Probe.h" #include "AbsBeamline/RBend.h" @@ -151,6 +154,18 @@ void DefaultVisitor::visitMultipoleT(const MultipoleT &multT) { applyDefault(multT); } +void DefaultVisitor::visitMultipoleTStraight(const MultipoleTStraight &multTstraight) { + applyDefault(multTstraight); +} + +void DefaultVisitor::visitMultipoleTCurvedConstRadius(const MultipoleTCurvedConstRadius &multTccurv) { + applyDefault(multTccurv); +} + +void DefaultVisitor::visitMultipoleTCurvedVarRadius(const MultipoleTCurvedVarRadius &multTvcurv) { + applyDefault(multTvcurv); +} + void DefaultVisitor::visitOffset(const Offset& off) { applyDefault(off); } @@ -308,4 +323,4 @@ void DefaultVisitor::visitTrackIntegrator(const TrackIntegrator &i) { void DefaultVisitor::applyDefault(const ElementBase &) -{} \ No newline at end of file +{} diff --git a/src/Classic/Algorithms/DefaultVisitor.h b/src/Classic/Algorithms/DefaultVisitor.h index 3006f0e93..df664cfa9 100644 --- a/src/Classic/Algorithms/DefaultVisitor.h +++ b/src/Classic/Algorithms/DefaultVisitor.h @@ -97,6 +97,15 @@ public: /// Apply the algorithm to a multipoleT. virtual void visitMultipoleT(const MultipoleT &); + /// Apply the algorithm to a multipoleTStraight. + virtual void visitMultipoleTStraight(const MultipoleTStraight &); + + /// Apply the algorithm to a multipoleTCurvedConstRadius. + virtual void visitMultipoleTCurvedConstRadius(const MultipoleTCurvedConstRadius &); + + /// Apply the algorithm to a multipoleTCurvedVarRadius. + virtual void visitMultipoleTCurvedVarRadius(const MultipoleTCurvedVarRadius &); + /// Apply the algorithm to an Offset. virtual void visitOffset(const Offset &); diff --git a/src/Classic/BeamlineGeometry/CMakeLists.txt b/src/Classic/BeamlineGeometry/CMakeLists.txt index 520c5b479..bae0b0b3c 100644 --- a/src/Classic/BeamlineGeometry/CMakeLists.txt +++ b/src/Classic/BeamlineGeometry/CMakeLists.txt @@ -6,6 +6,7 @@ set (_SRCS NullGeometry.cpp OffsetGeometry.cpp PlanarArcGeometry.cpp + VarRadiusGeometry.cpp RBendGeometry.cpp Rotation3D.cpp SRotatedGeometry.cpp @@ -27,6 +28,7 @@ set (HDRS NullGeometry.h OffsetGeometry.h PlanarArcGeometry.h + VarRadiusGeometry.h RBendGeometry.h Rotation3D.h SRotatedGeometry.h @@ -34,4 +36,4 @@ set (HDRS Vector3D.h ) -install (FILES ${HDRS} DESTINATION "${CMAKE_INSTALL_PREFIX}/include/BeamlineGeometry") \ No newline at end of file +install (FILES ${HDRS} DESTINATION "${CMAKE_INSTALL_PREFIX}/include/BeamlineGeometry") diff --git a/src/Classic/BeamlineGeometry/VarRadiusGeometry.cpp b/src/Classic/BeamlineGeometry/VarRadiusGeometry.cpp new file mode 100644 index 000000000..32bc5d5ae --- /dev/null +++ b/src/Classic/BeamlineGeometry/VarRadiusGeometry.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018, Martin Duy Tat + * 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 <cmath> +#include <vector> +#include "BeamlineGeometry/VarRadiusGeometry.h" +#include "BeamlineGeometry/Euclid3D.h" +#include "AbsBeamline/MultipoleTFunctions/CoordinateTransform.h" + +Euclid3D VarRadiusGeometry::getTransform(double fromS, double toS) const { + Euclid3D v; + coordinatetransform::CoordinateTransform t(0.0, 0.0, 0.0, + s_0_m, lambda_left_m, + lambda_right_m, rho_m); + double phifrom = acos(t.getUnitTangentVector(fromS)[1]); + double phito = acos(t.getUnitTangentVector(toS)[1]); + std::vector<double> ref_from = t.calcReferenceTrajectory(fromS); + std::vector<double> ref_to = t.calcReferenceTrajectory(toS); + v = Euclid3D::YRotation(-(phifrom + phito)); + v.setX(ref_to[0] - ref_from[0]); + v.setZ(ref_to[1] - ref_from[1]); + return v; +} diff --git a/src/Classic/BeamlineGeometry/VarRadiusGeometry.h b/src/Classic/BeamlineGeometry/VarRadiusGeometry.h new file mode 100644 index 000000000..b3cf41ef7 --- /dev/null +++ b/src/Classic/BeamlineGeometry/VarRadiusGeometry.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2018, Martin Duy Tat + * 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 CLASSIC_VarRadiusGeometry_HH +#define CLASSIC_VarRadiusGeometry_HH + +/** --------------------------------------------------------------------- + * + * VarRadiusGeometry represents a Geometry with variable radius. Assuming \n + * a Tanh model for fringe fields, the radius of curvature varies inversely \n + * proportional with the fringe field. Such a magnet will follow the \n + * trajectory of the reference particle. + * The origin is defined at the centre and extends from -length / 2 to \n + * +length / 2. + * Transformations are calculated using a CoordinateTransformation object, \n + * which integrates to find the reference trajectory. + * + * --------------------------------------------------------------------- + * + * Class category: AbsBeamline \n + * $Author: Martin Duy Tat, Chris Rogers + * + * --------------------------------------------------------------------- + * + * + * --------------------------------------------------------------------- + */ + +#include "BeamlineGeometry/Geometry.h" + +class VarRadiusGeometry: public BGeometryBase { +public: + /** Build VarRadiusGeometry with given length, centre radius of curvature + * and fringe field + * \param length -> Length of geometry + * \param rho -> Centre radius of curvature of geometry + * \param s_0 -> Length of central fringe field + * \param lambda_left -> Length of left end fringe field + * \param lambda_right -> Length of right end fringe field + */ + VarRadiusGeometry(double length, + double rho, + double s_0, + double lambda_left, + double lambda_right); + /** Copy constructor */ + VarRadiusGeometry(const VarRadiusGeometry &right); + /** Destructor */ + virtual ~VarRadiusGeometry(); + /** Assigment operator */ + const VarRadiusGeometry &operator=(const VarRadiusGeometry &right); + /** Arc length along the design arc */ + virtual double getArcLength() const; + /** Get element length measured along hte design arc */ + virtual double getElementLength() const; + /** Set arc length + * \param length -> Length of element + */ + virtual void setElementLength(double length); + /** Get centre radius of curvature */ + double getRadius() const; + /** Set centre radius of curvature + * \param rho -> Central radius of curvature + */ + void setRadius(const double &rho); + /** Get central fringe field length */ + double getS0() const; + /** Set central fringe field length + * \param s_0 -> Central fringe field length + */ + void setS0(const double &s_0); + /** Get left end fringe field length */ + double getLambdaLeft() const; + /** Set left end fringe field length + * \param lambda_left -> Left end fringe field length + */ + void setLambdaLeft(const double &lambda_left); + /** Get right end fringe field length */ + double getLambdaRight() const; + /** Set right end fringe field length + * \param lambda_right -> Right end fringe field length + */ + void setLambdaRight(const double &lambda_right); + /** Transform of the local coordinate system + * \param fromS -> Transform from this position + * \param toS -> Transform to this position + */ + virtual Euclid3D getTransform(double fromS, double toS) const; + /** Transform of the local coordinate system from the origin + * to the entrance of the element + * Equivalent to getTransform(0.0, getEntrance()) + */ + virtual Euclid3D getEntranceFrame() const; + /** Transform of the local coordinate system from the origin + * to the exit of the element + * Equivalent to getTransform(0.0, getExit()) + */ + virtual Euclid3D getExitFrame() const; +private: + double length_m; + double rho_m; + double s_0_m; + double lambda_left_m; + double lambda_right_m; +}; + +// inlined (trivial) member functions + +inline + VarRadiusGeometry::VarRadiusGeometry(double length, + double rho, + double s_0, + double lambda_left, + double lambda_right): + length_m(length), rho_m(rho), s_0_m(s_0), + lambda_left_m(lambda_left), lambda_right_m(lambda_right) { +} + +inline + VarRadiusGeometry::VarRadiusGeometry(const VarRadiusGeometry &rhs): + BGeometryBase(rhs), + length_m(rhs.length_m), rho_m(rhs.rho_m), s_0_m(rhs.s_0_m), + lambda_left_m(rhs.lambda_left_m), lambda_right_m(rhs.lambda_right_m) { +} + + +inline + const VarRadiusGeometry &VarRadiusGeometry::operator= ( + const VarRadiusGeometry &rhs) { + length_m = rhs.length_m; + rho_m = rhs.rho_m; + s_0_m = rhs.s_0_m; + lambda_left_m = rhs.lambda_left_m; + lambda_right_m = rhs.lambda_right_m; + return *this; +} +inline + VarRadiusGeometry::~VarRadiusGeometry() { +} +inline + double VarRadiusGeometry::getArcLength() const { + return length_m; +} +inline + double VarRadiusGeometry::getElementLength() const { + return length_m; +} +inline + void VarRadiusGeometry::setElementLength(double length) { + length_m = length; +} +inline + double VarRadiusGeometry::getRadius() const { + return rho_m; +} +inline + void VarRadiusGeometry::setRadius(const double &rho) { + rho_m = rho; +} +inline + double VarRadiusGeometry::getS0() const { + return s_0_m; +} +inline + void VarRadiusGeometry::setS0(const double &s_0) { + s_0_m = s_0; +} +inline + double VarRadiusGeometry::getLambdaLeft() const { + return lambda_left_m; +} +inline + void VarRadiusGeometry::setLambdaLeft(const double &lambda_left) { + lambda_left_m = lambda_left; +} +inline + double VarRadiusGeometry::getLambdaRight() const { + return lambda_right_m; +} +inline + void VarRadiusGeometry::setLambdaRight(const double &lambda_right) { + lambda_right_m = lambda_right; +} +inline + Euclid3D VarRadiusGeometry::getEntranceFrame() const { + return getTransform(getOrigin(), getEntrance()); +} +inline + Euclid3D VarRadiusGeometry::getExitFrame() const { + return getTransform(getOrigin(), getExit()); +} + +#endif + diff --git a/src/Classic/CMakeLists.txt b/src/Classic/CMakeLists.txt index 14b8cdfc9..8eae3ea09 100644 --- a/src/Classic/CMakeLists.txt +++ b/src/Classic/CMakeLists.txt @@ -25,3 +25,7 @@ install (FILES Utilities/ClassicException.h Utilities/ClassicRandom.h Utilities/ ) set(OPAL_SRCS "${OPAL_SRCS}" PARENT_SCOPE) + +#Turn on gcov +set(CMAKE_CXX_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") +set(CMAKE_CXX_OUTPUT_EXTENSION_REPLACE 1) diff --git a/src/Elements/CMakeLists.txt b/src/Elements/CMakeLists.txt index 826582a49..9e1e1319c 100644 --- a/src/Elements/CMakeLists.txt +++ b/src/Elements/CMakeLists.txt @@ -20,6 +20,9 @@ set (_SRCS OpalMonitor.cpp OpalMultipole.cpp OpalMultipoleT.cpp + OpalMultipoleTStraight.cpp + OpalMultipoleTCurvedConstRadius.cpp + OpalMultipoleTCurvedVarRadius.cpp OpalOctupole.cpp OpalOffset/OpalLocalCylindricalOffset.cpp OpalOffset/OpalLocalCartesianOffset.cpp @@ -82,6 +85,9 @@ set (HDRS OpalMonitor.h OpalMultipole.h OpalMultipoleT.h + OpalMultipoleTStraight.h + OpalMultipoleTCurvedConstRadius.h + OpalMultipoleTCurvedVarRadius.h OpalOctupole.h OpalParallelPlate.h OpalPatch.h @@ -114,4 +120,4 @@ set (HDRS OpalOffset/OpalLocalCylindricalOffset.h ) -install (FILES ${HDRS} DESTINATION "${CMAKE_INSTALL_PREFIX}/include/Elements/OpalOffset") \ No newline at end of file +install (FILES ${HDRS} DESTINATION "${CMAKE_INSTALL_PREFIX}/include/Elements/OpalOffset") diff --git a/src/Elements/OpalMultipoleT.cpp b/src/Elements/OpalMultipoleT.cpp index e27147877..0c99707e4 100644 --- a/src/Elements/OpalMultipoleT.cpp +++ b/src/Elements/OpalMultipoleT.cpp @@ -68,15 +68,18 @@ OpalMultipoleT::OpalMultipoleT(): itsAttr[MAXFORDER] = Attributes::makeReal ("MAXFORDER", "Number of terms used in each field component"); + itsAttr[MAXXORDER] = Attributes::makeReal + ("MAXXORDER", + "Number of terms used in polynomial expansions"); itsAttr[ROTATION] = Attributes::makeReal ("ROTATION", "Rotation angle about its axis for skew elements (rad)"); itsAttr[VARRADIUS] = Attributes::makeBool ("VARRADIUS", "Set true if radius of magnet is variable"); - itsAttr[VARSTEP] = Attributes::makeReal - ("VARSTEP", - "Step size used in rotating coords along ref trajectory"); + itsAttr[BBLENGTH] = Attributes::makeReal + ("BBLENGTH", + "Distance between centre of magnet and entrance in m"); //registerRealAttribute("FRINGELEN"); registerOwnership(); @@ -111,7 +114,6 @@ fillRegisteredAttributes(const ElementBase &base, ValueFlag flag) { OpalElement::fillRegisteredAttributes(base, flag); const MultipoleT *multT = dynamic_cast<const MultipoleT*>(base.removeAlignWrapper()); - for(unsigned int order = 1; order <= multT->getTransMaxOrder(); order++) { std::ostringstream ss; ss << order; @@ -125,10 +127,11 @@ fillRegisteredAttributes(const ElementBase &base, ValueFlag flag) { registerRealAttribute("VAPERT")->setReal(multT->getAperture()[0]); registerRealAttribute("HAPERT")->setReal(multT->getAperture()[1]); registerRealAttribute("MAXFORDER")->setReal(multT->getMaxOrder()); + registerRealAttribute("MAXXORDER")->setReal(multT->getMaxXOrder()); registerRealAttribute("ROTATION")->setReal(multT->getRotation()); registerRealAttribute("EANGLE")->setReal(multT->getEntranceAngle()); - registerRealAttribute("VARSTEP")->setReal(multT->getVarStep()); - //registerRealAttribute("VARRADIUS")->setReal(multT->getVarRadius()); + //registerRealAttribute("VARRADIUS")->setBool(multT->getVarRadius()); + registerRealAttribute("BBLENGTH")->setReal(multT->getBoundingBoxLength()); } @@ -138,10 +141,11 @@ void OpalMultipoleT::update() { // Magnet length. MultipoleT *multT = - dynamic_cast<MultipoleT*>(getElement()->removeWrappers()); + dynamic_cast<MultipoleT*>(getElement()->removeWrappers()); double length = Attributes::getReal(itsAttr[LENGTH]); double angle = Attributes::getReal(itsAttr[ANGLE]); - multT->setElementLength(length/2); + double boundingBoxLength = Attributes::getReal(itsAttr[BBLENGTH]); + multT->setElementLength(length); multT->setLength(length); multT->setBendAngle(angle); multT->setAperture(Attributes::getReal(itsAttr[VAPERT]), @@ -152,23 +156,26 @@ void OpalMultipoleT::update() { Attributes::getReal(itsAttr[RFRINGE])); if (Attributes::getBool(itsAttr[VARRADIUS])) { multT->setVarRadius(); - if(Attributes::getReal(itsAttr[VARSTEP])) { - multT->setVarStep(Attributes::getReal(itsAttr[VARSTEP])); - } } + multT->setBoundingBoxLength(Attributes::getReal(itsAttr[BBLENGTH])); const std::vector<double> transProfile = Attributes::getRealArray(itsAttr[TP]); int transSize = transProfile.size(); - multT->setTransMaxOrder(transSize - 1); + if (transSize == 0) { + multT->setTransMaxOrder(0); + } else { + multT->setTransMaxOrder(transSize - 1); + } multT->setMaxOrder(Attributes::getReal(itsAttr[MAXFORDER])); + multT->setMaxXOrder(Attributes::getReal(itsAttr[MAXXORDER])); multT->setRotation(Attributes::getReal(itsAttr[ROTATION])); multT->setEntranceAngle(Attributes::getReal(itsAttr[EANGLE])); PlanarArcGeometry &geometry = multT->getGeometry(); - + if(length) { - geometry = PlanarArcGeometry(length/2, angle / length); + geometry = PlanarArcGeometry(2 * boundingBoxLength, angle / length); } else { geometry = PlanarArcGeometry(angle); } diff --git a/src/Elements/OpalMultipoleT.h b/src/Elements/OpalMultipoleT.h index ada5375b3..53a663d64 100644 --- a/src/Elements/OpalMultipoleT.h +++ b/src/Elements/OpalMultipoleT.h @@ -48,12 +48,12 @@ public: HAPERT, // Aperture horizontal dimension VAPERT, // Aperture vertical dimension MAXFORDER, // Maximum order in the field expansion + MAXXORDER, // Maximum order in x in polynomial expansions ANGLE, // Bending angle of a sector magnet ROTATION, // Rotation angle about central axis for skew elements EANGLE, // Entrance angle VARRADIUS, // Variable radius flag - VARSTEP, // Step size used in rotating coords - // along ref trajectory (mm) + BBLENGTH, // Distance between centre of magnet and entrance SIZE // size of the enum }; diff --git a/src/Elements/OpalMultipoleTCurvedConstRadius.cpp b/src/Elements/OpalMultipoleTCurvedConstRadius.cpp new file mode 100644 index 000000000..8082f6201 --- /dev/null +++ b/src/Elements/OpalMultipoleTCurvedConstRadius.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2017, Titus Dascalu + * Copyright (c) 2018, Martin Duy Tat + * 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 "Elements/OpalMultipoleTCurvedConstRadius.h" +#include "AbstractObjects/AttributeHandler.h" +#include "AbstractObjects/Expressions.h" +#include "AbstractObjects/OpalData.h" +#include "Attributes/Attributes.h" +#include "ComponentWrappers/MultipoleWrapper.h" +#include "Expressions/SValue.h" +#include "Expressions/SRefExpr.h" +#include "Physics/Physics.h" +#include "Utilities/Options.h" +#include <iostream> +#if defined(__GNUC__) && __GNUC__ < 3 +#include <strstream> +#else +#include <sstream> +#endif +#include <vector> + + +// Class OpalMultipoleTCurvedConstRadius +// ------------------------------------------------------------------------ + +OpalMultipoleTCurvedConstRadius::OpalMultipoleTCurvedConstRadius(): + OpalElement(SIZE, "MULTIPOLETCURVEDCONSTRADIUS", + "The \"MULTIPOLETCURVEDCONSTRADIUS\" element defines a curved combined function multipole magnet of constant curvature.") { + itsAttr[TP] = Attributes::makeRealArray + ("TP", "Transverse Profile derivatives in m^(-k)"); + itsAttr[LFRINGE] = Attributes::makeReal + ("LFRINGE", "The length of the left end field in m"); + itsAttr[RFRINGE] = Attributes::makeReal + ("RFRINGE", "The length of the right end field in m"); + itsAttr[HAPERT] = Attributes::makeReal + ("HAPERT", "The aperture width in m"); + itsAttr[VAPERT] = Attributes::makeReal + ("VAPERT", "The aperture height in m"); + itsAttr[ANGLE] = Attributes::makeReal + ("ANGLE", "The azimuthal angle of the magnet in ring (rad)"); + itsAttr[EANGLE] = Attributes::makeReal + ("EANGLE", "The entrance angle (rad)"); + itsAttr[MAXFORDER] = Attributes::makeReal + ("MAXFORDER", + "Number of terms used in each field component"); + itsAttr[MAXXORDER] = Attributes::makeReal + ("MAXXORDER", + "Number of terms used in polynomial expansions"); + itsAttr[ROTATION] = Attributes::makeReal + ("ROTATION", + "Rotation angle about its axis for skew elements (rad)"); + itsAttr[BBLENGTH] = Attributes::makeReal + ("BBLENGTH", + "Distance between centre of magnet and entrance in m"); + + registerOwnership(); + + + setElement((new MultipoleTCurvedConstRadius("MULTIPOLETCURVEDCONSTRADIUS"))->makeWrappers()); +} + + +OpalMultipoleTCurvedConstRadius::OpalMultipoleTCurvedConstRadius(const std::string &name, + OpalMultipoleTCurvedConstRadius *parent): + OpalElement(name, parent) { + setElement((new MultipoleTCurvedConstRadius(name))->makeWrappers()); +} + + +OpalMultipoleTCurvedConstRadius::~OpalMultipoleTCurvedConstRadius() +{} + + +OpalMultipoleTCurvedConstRadius *OpalMultipoleTCurvedConstRadius::clone(const std::string &name) { + return new OpalMultipoleTCurvedConstRadius(name, this); +} + + +void OpalMultipoleTCurvedConstRadius::print(std::ostream &os) const { + OpalElement::print(os); +} + + +void OpalMultipoleTCurvedConstRadius:: +fillRegisteredAttributes(const ElementBase &base, ValueFlag flag) { + OpalElement::fillRegisteredAttributes(base, flag); + const MultipoleTCurvedConstRadius *multT = + dynamic_cast<const MultipoleTCurvedConstRadius*>(base.removeAlignWrapper()); + + for(unsigned int order = 1; order <= multT->getTransMaxOrder(); order++) { + std::ostringstream ss; + ss << order; + std::string orderString = ss.str(); + std::string attrName = "TP" + orderString; + registerRealAttribute(attrName)->setReal(multT->getTransProfile(order)); + } + + registerRealAttribute("LFRINGE")->setReal(multT->getFringeLength().at(0)); + registerRealAttribute("RFRINGE")->setReal(multT->getFringeLength().at(1)); + registerRealAttribute("VAPERT")->setReal(multT->getAperture()[0]); + registerRealAttribute("HAPERT")->setReal(multT->getAperture()[1]); + registerRealAttribute("MAXFORDER")->setReal(multT->getMaxOrder()); + registerRealAttribute("MAXXORDER")->setReal(multT->getMaxXOrder()); + registerRealAttribute("ROTATION")->setReal(multT->getRotation()); + registerRealAttribute("ANGLE")->setReal(multT->getBendAngle()); + registerRealAttribute("EANGLE")->setReal(multT->getEntranceAngle()); + registerRealAttribute("BBLENGTH")->setReal(multT->getBoundingBoxLength()); + +} + + +void OpalMultipoleTCurvedConstRadius::update() { + OpalElement::update(); + + // Magnet length. + MultipoleTCurvedConstRadius *multT = + dynamic_cast<MultipoleTCurvedConstRadius*>(getElement()->removeWrappers()); + double length = Attributes::getReal(itsAttr[LENGTH]); + double angle = Attributes::getReal(itsAttr[ANGLE]); + double boundingBoxLength = Attributes::getReal(itsAttr[BBLENGTH]); + multT->setElementLength(length); + multT->setLength(length); + multT->setBendAngle(angle); + multT->setAperture(Attributes::getReal(itsAttr[VAPERT]), + Attributes::getReal(itsAttr[HAPERT])); + + multT->setFringeField(Attributes::getReal(itsAttr[LENGTH])/2, + Attributes::getReal(itsAttr[LFRINGE]), + Attributes::getReal(itsAttr[RFRINGE])); + multT->setBoundingBoxLength(Attributes::getReal(itsAttr[BBLENGTH])); + const std::vector<double> transProfile = + Attributes::getRealArray(itsAttr[TP]); + std::size_t transSize = transProfile.size(); + if (transSize == 0) { + multT->setTransMaxOrder(0); + } else { + multT->setTransMaxOrder(transSize - 1); + } + + multT->setMaxOrder(Attributes::getReal(itsAttr[MAXFORDER])); + multT->setMaxXOrder(Attributes::getReal(itsAttr[MAXXORDER])); + multT->setRotation(Attributes::getReal(itsAttr[ROTATION])); + multT->setEntranceAngle(Attributes::getReal(itsAttr[EANGLE])); + + PlanarArcGeometry &geometry = multT->getGeometry(); + + if(length) { + geometry = PlanarArcGeometry(2 * boundingBoxLength, angle / length); + } else { + geometry = PlanarArcGeometry(angle); + } + + for(std::size_t comp = 0; comp < transSize; comp++) { + multT->setTransProfile(comp, transProfile[comp]); + } + // Transmit "unknown" attributes. + OpalElement::updateUnknown(multT); + + setElement(multT->makeWrappers()); +} diff --git a/src/Elements/OpalMultipoleTCurvedConstRadius.h b/src/Elements/OpalMultipoleTCurvedConstRadius.h new file mode 100644 index 000000000..8adcced7f --- /dev/null +++ b/src/Elements/OpalMultipoleTCurvedConstRadius.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017, Titus Dascalu + * Copyright (c) 2018, Martin Duy Tat + * 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_OPALMULTIPOLET_CURVED_CONST_RADIUS_HH +#define OPAL_OPALMULTIPOLET_CURVED_CONST_RADIUS_HH + +#include "Elements/OpalElement.h" +#include "AbsBeamline/MultipoleTCurvedConstRadius.h" + +/** OpalMultipoleTCurvedConstRadius provides user interface information for the MultipoleTCurvedConstRadius object + * + * Defines input parameters + */ +class OpalMultipoleTCurvedConstRadius: public OpalElement { + +public: + + // The attributes of class OpalMultipoleTCurvedConstRadius + enum { + TP = COMMON, // Transverse field components + RFRINGE, // Length of right fringe field + LFRINGE, // Length of left fringe field + HAPERT, // Aperture horizontal dimension + VAPERT, // Aperture vertical dimension + MAXFORDER, // Maximum order in the field expansion + MAXXORDER, // Maximum order in x in polynomial expansions + ANGLE, // Bending angle of a sector magnet + ROTATION, // Rotation angle about central axis for skew elements + EANGLE, // Entrance angle + BBLENGTH, // Distance between centre of magnet and entrance + SIZE // size of the enum + }; + + /** Default constructor initialises UI parameters. */ + OpalMultipoleTCurvedConstRadius(); + + /** Destructor does nothing */ + virtual ~OpalMultipoleTCurvedConstRadius(); + + /** Inherited copy constructor */ + virtual OpalMultipoleTCurvedConstRadius *clone(const std::string &name); + + /** Fill in all registered attributes + * + * Just calls fillRegisteredAttributes on the base class + */ + virtual void fillRegisteredAttributes(const ElementBase &, ValueFlag); + + /** Update the MultipoleTCurvedConstRadius with new parameters from UI parser */ + virtual void update(); + + void print(std::ostream &os) const; + + private: + // Not implemented. + OpalMultipoleTCurvedConstRadius(const OpalMultipoleTCurvedConstRadius &); + void operator=(const OpalMultipoleTCurvedConstRadius &); + + // Clone constructor. + OpalMultipoleTCurvedConstRadius(const std::string &name, OpalMultipoleTCurvedConstRadius *parent); +}; + +#endif + + + + + diff --git a/src/Elements/OpalMultipoleTCurvedVarRadius.cpp b/src/Elements/OpalMultipoleTCurvedVarRadius.cpp new file mode 100644 index 000000000..225e09a10 --- /dev/null +++ b/src/Elements/OpalMultipoleTCurvedVarRadius.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2017, Titus Dascalu + * Copyright (c) 2018, Martin Duy Tat + * 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 "Elements/OpalMultipoleTCurvedVarRadius.h" +#include "AbstractObjects/AttributeHandler.h" +#include "AbstractObjects/Expressions.h" +#include "AbstractObjects/OpalData.h" +#include "Attributes/Attributes.h" +#include "ComponentWrappers/MultipoleWrapper.h" +#include "Expressions/SValue.h" +#include "Expressions/SRefExpr.h" +#include "Physics/Physics.h" +#include "Utilities/Options.h" +#include <iostream> +#if defined(__GNUC__) && __GNUC__ < 3 +#include <strstream> +#else +#include <sstream> +#endif +#include <vector> + + +// Class OpalMultipoleTCurvedVarRadius +// ------------------------------------------------------------------------ + +OpalMultipoleTCurvedVarRadius::OpalMultipoleTCurvedVarRadius(): + OpalElement(SIZE, "MULTIPOLETCURVEDVARRADIUS", + "The \"MULTIPOLETCURVEDVARRADIUS\" element defines a combined function multipole.") { + itsAttr[TP] = Attributes::makeRealArray + ("TP", "Transverse Profile derivatives in m^(-k)"); + itsAttr[LFRINGE] = Attributes::makeReal + ("LFRINGE", "The length of the left end field in m"); + itsAttr[RFRINGE] = Attributes::makeReal + ("RFRINGE", "The length of the right end field in m"); + itsAttr[HAPERT] = Attributes::makeReal + ("HAPERT", "The aperture width in m"); + itsAttr[VAPERT] = Attributes::makeReal + ("VAPERT", "The aperture height in m"); + itsAttr[ANGLE] = Attributes::makeReal + ("ANGLE", "The azimuthal angle of the magnet in ring (rad)"); + itsAttr[EANGLE] = Attributes::makeReal + ("EANGLE", "The entrance angle (rad)"); + itsAttr[MAXFORDER] = Attributes::makeReal + ("MAXFORDER", + "Number of terms used in each field component"); + itsAttr[MAXXORDER] = Attributes::makeReal + ("MAXXORDER", + "Number of terms used in polynomial expansions"); + itsAttr[ROTATION] = Attributes::makeReal + ("ROTATION", + "Rotation angle about its axis for skew elements (rad)"); + itsAttr[BBLENGTH] = Attributes::makeReal + ("BBLENGTH", + "Distance between centre of magnet and entrance in m"); + + registerOwnership(); + + setElement((new MultipoleTCurvedVarRadius("MULTIPOLETCURVEDVARRADIUS"))->makeWrappers()); +} + + +OpalMultipoleTCurvedVarRadius::OpalMultipoleTCurvedVarRadius(const std::string &name, + OpalMultipoleTCurvedVarRadius *parent): + OpalElement(name, parent) { + setElement((new MultipoleTCurvedVarRadius(name))->makeWrappers()); +} + + +OpalMultipoleTCurvedVarRadius::~OpalMultipoleTCurvedVarRadius() +{} + + +OpalMultipoleTCurvedVarRadius *OpalMultipoleTCurvedVarRadius::clone(const std::string &name) { + return new OpalMultipoleTCurvedVarRadius(name, this); +} + + +void OpalMultipoleTCurvedVarRadius::print(std::ostream &os) const { + OpalElement::print(os); +} + + +void OpalMultipoleTCurvedVarRadius:: +fillRegisteredAttributes(const ElementBase &base, ValueFlag flag) { + OpalElement::fillRegisteredAttributes(base, flag); + const MultipoleTCurvedVarRadius *multT = + dynamic_cast<const MultipoleTCurvedVarRadius*>(base.removeAlignWrapper()); + + for(unsigned int order = 1; order <= multT->getTransMaxOrder(); order++) { + std::ostringstream ss; + ss << order; + std::string orderString = ss.str(); + std::string attrName = "TP" + orderString; + registerRealAttribute(attrName)->setReal(multT->getTransProfile(order)); + } + + registerRealAttribute("LFRINGE")->setReal(multT->getFringeLength().at(0)); + registerRealAttribute("RFRINGE")->setReal(multT->getFringeLength().at(1)); + registerRealAttribute("VAPERT")->setReal(multT->getAperture()[0]); + registerRealAttribute("HAPERT")->setReal(multT->getAperture()[1]); + registerRealAttribute("MAXFORDER")->setReal(multT->getMaxOrder()); + registerRealAttribute("MAXXORDER")->setReal(multT->getMaxXOrder()); + registerRealAttribute("ROTATION")->setReal(multT->getRotation()); + registerRealAttribute("EANGLE")->setReal(multT->getEntranceAngle()); + registerRealAttribute("BBLENGTH")->setReal(multT->getBoundingBoxLength()); + +} + + +void OpalMultipoleTCurvedVarRadius::update() { + OpalElement::update(); + + // Magnet length. + MultipoleTCurvedVarRadius *multT = + dynamic_cast<MultipoleTCurvedVarRadius*>(getElement()->removeWrappers()); + double length = Attributes::getReal(itsAttr[LENGTH]); + double angle = Attributes::getReal(itsAttr[ANGLE]); + double boundingBoxLength = Attributes::getReal(itsAttr[BBLENGTH]); + multT->setElementLength(length); + multT->setLength(length); + multT->setBendAngle(angle); + multT->setAperture(Attributes::getReal(itsAttr[VAPERT]), + Attributes::getReal(itsAttr[HAPERT])); + + multT->setFringeField(Attributes::getReal(itsAttr[LENGTH])/2, + Attributes::getReal(itsAttr[LFRINGE]), + Attributes::getReal(itsAttr[RFRINGE])); + multT->setBoundingBoxLength(Attributes::getReal(itsAttr[BBLENGTH])); + const std::vector<double> transProfile = + Attributes::getRealArray(itsAttr[TP]); + std::size_t transSize = transProfile.size(); + + if (transSize == 0) { + multT->setTransMaxOrder(0); + } else { + multT->setTransMaxOrder(transSize - 1); + } + multT->setMaxOrder(Attributes::getReal(itsAttr[MAXFORDER])); + multT->setMaxXOrder(Attributes::getReal(itsAttr[MAXXORDER])); + multT->setRotation(Attributes::getReal(itsAttr[ROTATION])); + multT->setEntranceAngle(Attributes::getReal(itsAttr[EANGLE])); + + VarRadiusGeometry &geometry = multT->getGeometry(); + + geometry = VarRadiusGeometry(2 * boundingBoxLength, + (length / angle), + length / 2, + Attributes::getReal(itsAttr[LFRINGE]), + Attributes::getReal(itsAttr[RFRINGE])); + + for(std::size_t comp = 0; comp < transSize; comp++) { + multT->setTransProfile(comp, transProfile[comp]); + } + // Transmit "unknown" attributes. + OpalElement::updateUnknown(multT); + + setElement(multT->makeWrappers()); +} diff --git a/src/Elements/OpalMultipoleTCurvedVarRadius.h b/src/Elements/OpalMultipoleTCurvedVarRadius.h new file mode 100644 index 000000000..e3bddee93 --- /dev/null +++ b/src/Elements/OpalMultipoleTCurvedVarRadius.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017, Titus Dascalu + * Copyright (c) 2018, Martin Duy Tat + * 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_OPALMULTIPOLET_CURVED_VAR_RADIUS_HH +#define OPAL_OPALMULTIPOLET_CURVED_VAR_RADIUS_HH + +#include "Elements/OpalElement.h" +#include "AbsBeamline/MultipoleTCurvedVarRadius.h" + +/** OpalMultipoleTCurvedVarRadius provides user interface information for the MultipoleTCurvedVarRadius object + * + * Defines input parameters + */ +class OpalMultipoleTCurvedVarRadius: public OpalElement { + +public: + + // The attributes of class OpalMultipoleTCurvedVarRadius + enum { + TP = COMMON, // Transverse field components + RFRINGE, // Length of right fringe field + LFRINGE, // Length of left fringe field + HAPERT, // Aperture horizontal dimension + VAPERT, // Aperture vertical dimension + MAXFORDER, // Maximum order in the field expansion + MAXXORDER, // Maximum order in x in polynomial expansions + ANGLE, // Bending angle of a sector magnet + ROTATION, // Rotation angle about central axis for skew elements + EANGLE, // Entrance angle + BBLENGTH, // Distance between centre of magnet and entrance + SIZE // size of the enum + }; + + /** Default constructor initialises UI parameters. */ + OpalMultipoleTCurvedVarRadius(); + + /** Destructor does nothing */ + virtual ~OpalMultipoleTCurvedVarRadius(); + + /** Inherited copy constructor */ + virtual OpalMultipoleTCurvedVarRadius *clone(const std::string &name); + + /** Fill in all registered attributes + * + * Just calls fillRegisteredAttributes on the base class + */ + virtual void fillRegisteredAttributes(const ElementBase &, ValueFlag); + + /** Update the MultipoleTCurvedVarRadius with new parameters from UI parser */ + virtual void update(); + + void print(std::ostream &os) const; + + private: + // Not implemented. + OpalMultipoleTCurvedVarRadius(const OpalMultipoleTCurvedVarRadius &); + void operator=(const OpalMultipoleTCurvedVarRadius &); + + // Clone constructor. + OpalMultipoleTCurvedVarRadius(const std::string &name, OpalMultipoleTCurvedVarRadius *parent); +}; + +#endif + + + + + diff --git a/src/Elements/OpalMultipoleTStraight.cpp b/src/Elements/OpalMultipoleTStraight.cpp new file mode 100644 index 000000000..0a380a6a3 --- /dev/null +++ b/src/Elements/OpalMultipoleTStraight.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2017, Titus Dascalu + * Copyright (c) 2018, Martin Duy Tat + * 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 "Elements/OpalMultipoleTStraight.h" +#include "AbstractObjects/AttributeHandler.h" +#include "AbstractObjects/Expressions.h" +#include "AbstractObjects/OpalData.h" +#include "Attributes/Attributes.h" +#include "ComponentWrappers/MultipoleWrapper.h" +#include "Expressions/SValue.h" +#include "Expressions/SRefExpr.h" +#include "Physics/Physics.h" +#include "Utilities/Options.h" +#include <iostream> +#if defined(__GNUC__) && __GNUC__ < 3 +#include <strstream> +#else +#include <sstream> +#endif +#include <vector> + + +// Class OpalMultipoleTStraight +// ------------------------------------------------------------------------ + +OpalMultipoleTStraight::OpalMultipoleTStraight(): + OpalElement(SIZE, "MULTIPOLETSTRAIGHT", + "The \"MULTIPOLETSTRAIGHT\" element defines a straight, combined function multipole magnet.") { + itsAttr[TP] = Attributes::makeRealArray + ("TP", "Transverse Profile derivatives in m^(-k)"); + itsAttr[LFRINGE] = Attributes::makeReal + ("LFRINGE", "The length of the left end field in m"); + itsAttr[RFRINGE] = Attributes::makeReal + ("RFRINGE", "The length of the right end field in m"); + itsAttr[HAPERT] = Attributes::makeReal + ("HAPERT", "The aperture width in m"); + itsAttr[VAPERT] = Attributes::makeReal + ("VAPERT", "The aperture height in m"); + itsAttr[EANGLE] = Attributes::makeReal + ("EANGLE", "The entrance angle (rad)"); + itsAttr[MAXFORDER] = Attributes::makeReal + ("MAXFORDER", + "Number of terms used in each field component"); + itsAttr[ROTATION] = Attributes::makeReal + ("ROTATION", + "Rotation angle about its axis for skew elements (rad)"); + itsAttr[BBLENGTH] = Attributes::makeReal + ("BBLENGTH", + "Distance between centre of magnet and entrance in m"); + + registerOwnership(); + + setElement((new MultipoleTStraight("MULTIPOLETSTRAIGHT"))->makeWrappers()); +} + + +OpalMultipoleTStraight::OpalMultipoleTStraight(const std::string &name, + OpalMultipoleTStraight *parent): + OpalElement(name, parent) { + setElement((new MultipoleTStraight(name))->makeWrappers()); +} + + +OpalMultipoleTStraight::~OpalMultipoleTStraight() +{} + + +OpalMultipoleTStraight *OpalMultipoleTStraight::clone(const std::string &name) { + return new OpalMultipoleTStraight(name, this); +} + + +void OpalMultipoleTStraight::print(std::ostream &os) const { + OpalElement::print(os); +} + + +void OpalMultipoleTStraight:: +fillRegisteredAttributes(const ElementBase &base, ValueFlag flag) { + OpalElement::fillRegisteredAttributes(base, flag); + const MultipoleTStraight *multT = + dynamic_cast<const MultipoleTStraight*>(base.removeAlignWrapper()); + + for(unsigned int order = 1; order <= multT->getTransMaxOrder(); order++) { + std::ostringstream ss; + ss << order; + std::string orderString = ss.str(); + std::string attrName = "TP" + orderString; + registerRealAttribute(attrName)->setReal(multT->getTransProfile(order)); + } + + registerRealAttribute("LFRINGE")->setReal(multT->getFringeLength().at(0)); + registerRealAttribute("RFRINGE")->setReal(multT->getFringeLength().at(1)); + registerRealAttribute("VAPERT")->setReal(multT->getAperture()[0]); + registerRealAttribute("HAPERT")->setReal(multT->getAperture()[1]); + registerRealAttribute("MAXFORDER")->setReal(multT->getMaxOrder()); + registerRealAttribute("ROTATION")->setReal(multT->getRotation()); + registerRealAttribute("EANGLE")->setReal(multT->getEntranceAngle()); + registerRealAttribute("BBLENGTH")->setReal(multT->getBoundingBoxLength()); + +} + + +void OpalMultipoleTStraight::update() { + OpalElement::update(); + + // Magnet length. + MultipoleTStraight *multT = + dynamic_cast<MultipoleTStraight*>(getElement()->removeWrappers()); + double length = Attributes::getReal(itsAttr[LENGTH]); + double boundingBoxLength = Attributes::getReal(itsAttr[BBLENGTH]); + multT->setElementLength(length); + multT->setLength(length); + multT->setAperture(Attributes::getReal(itsAttr[VAPERT]), + Attributes::getReal(itsAttr[HAPERT])); + + multT->setFringeField(Attributes::getReal(itsAttr[LENGTH])/2, + Attributes::getReal(itsAttr[LFRINGE]), + Attributes::getReal(itsAttr[RFRINGE])); + multT->setBoundingBoxLength(Attributes::getReal(itsAttr[BBLENGTH])); + const std::vector<double> transProfile = + Attributes::getRealArray(itsAttr[TP]); + int transSize = transProfile.size(); + + multT->setTransMaxOrder(transSize - 1); + multT->setMaxOrder(Attributes::getReal(itsAttr[MAXFORDER])); + multT->setRotation(Attributes::getReal(itsAttr[ROTATION])); + multT->setEntranceAngle(Attributes::getReal(itsAttr[EANGLE])); + + StraightGeometry &geometry = multT->getGeometry(); + + geometry = StraightGeometry(2 * boundingBoxLength); + + for(int comp = 0; comp < transSize; comp++) { + multT->setTransProfile(comp, transProfile[comp]); + } + // Transmit "unknown" attributes. + OpalElement::updateUnknown(multT); + + setElement(multT->makeWrappers()); +} diff --git a/src/Elements/OpalMultipoleTStraight.h b/src/Elements/OpalMultipoleTStraight.h new file mode 100644 index 000000000..956a58eea --- /dev/null +++ b/src/Elements/OpalMultipoleTStraight.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2017, Titus Dascalu + * Copyright (c) 2018, Martin Duy Tat + * 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_OPALMULTIPOLET_STRAIGHT_HH +#define OPAL_OPALMULTIPOLET_STRAIGHT_HH + +#include "Elements/OpalElement.h" +#include "AbsBeamline/MultipoleTStraight.h" + +/** OpalMultipoleTStraight provides user interface information for the MultipoleTStraight object + * + * Defines input parameters + */ +class OpalMultipoleTStraight: public OpalElement { + +public: + + // The attributes of class OpalMultipoleTStraight + enum { + TP = COMMON, // Transverse field components + RFRINGE, // Length of right fringe field + LFRINGE, // Length of left fringe field + HAPERT, // Aperture horizontal dimension + VAPERT, // Aperture vertical dimension + MAXFORDER, // Maximum order in the field expansion + ROTATION, // Rotation angle about central axis for skew elements + EANGLE, // Entrance angle + BBLENGTH, // Distance between centre of magnet and entrance + SIZE // size of the enum + }; + + /** Default constructor initialises UI parameters. */ + OpalMultipoleTStraight(); + + /** Destructor does nothing */ + virtual ~OpalMultipoleTStraight(); + + /** Inherited copy constructor */ + virtual OpalMultipoleTStraight *clone(const std::string &name); + + /** Fill in all registered attributes + * + * Just calls fillRegisteredAttributes on the base class + */ + virtual void fillRegisteredAttributes(const ElementBase &, ValueFlag); + + /** Update the MultipoleT with new parameters from UI parser */ + virtual void update(); + + void print(std::ostream &os) const; + + private: + // Not implemented. + OpalMultipoleTStraight(const OpalMultipoleTStraight &); + void operator=(const OpalMultipoleTStraight &); + + // Clone constructor. + OpalMultipoleTStraight(const std::string &name, OpalMultipoleTStraight *parent); +}; + +#endif + + + + + diff --git a/src/OpalConfigure/Configure.cpp b/src/OpalConfigure/Configure.cpp index 93d0c9424..e22e88902 100644 --- a/src/OpalConfigure/Configure.cpp +++ b/src/OpalConfigure/Configure.cpp @@ -104,6 +104,10 @@ #include "Elements/OpalMarker.h" #include "Elements/OpalMonitor.h" #include "Elements/OpalMultipole.h" +#include "Elements/OpalMultipoleT.h" +#include "Elements/OpalMultipoleTStraight.h" +#include "Elements/OpalMultipoleTCurvedConstRadius.h" +#include "Elements/OpalMultipoleTCurvedVarRadius.h" #include "Elements/OpalOctupole.h" #include "Elements/OpalOffset/OpalLocalCartesianOffset.h" #include "Elements/OpalOffset/OpalLocalCylindricalOffset.h" @@ -253,6 +257,10 @@ namespace { opal->create(new OpalMarker()); opal->create(new OpalMonitor()); opal->create(new OpalMultipole()); + opal->create(new OpalMultipoleT()); + opal->create(new OpalMultipoleTStraight()); + opal->create(new OpalMultipoleTCurvedConstRadius()); + opal->create(new OpalMultipoleTCurvedVarRadius()); opal->create(new OpalOctupole()); opal->create(new OpalOffset::OpalLocalCartesianOffset()); // opal->create(new OpalOffset::OpalLocalCylindricalOffset()); @@ -298,4 +306,4 @@ namespace Configure { makeActions(); Versions::fillChanges(); } -}; \ No newline at end of file +}; diff --git a/src/OpalConfigure/Configure.h b/src/OpalConfigure/Configure.h index 0ff434be8..aea42345f 100644 --- a/src/OpalConfigure/Configure.h +++ b/src/OpalConfigure/Configure.h @@ -1,5 +1,5 @@ #ifndef OPAL_Configure_HH -#define OPAL_Configure_HH 1 +#define OPAL_Configure_HH // ------------------------------------------------------------------------ // $RCSfile: Configure.h,v $ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cc53315c7..e4d61e2ce 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -69,3 +69,12 @@ target_link_libraries ( ${GTEST_BOTH_LIBRARIES} -lpthread ) + +#Flags for gprof +#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg") +#SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pg") +#SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -pg") +################ +#Turn of gcov +#set(CMAKE_CXX_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") +#set(CMAKE_CXX_OUTPUT_EXTENSION_REPLACE 1) diff --git a/tests/classic_src/AbsBeamline/CMakeLists.txt b/tests/classic_src/AbsBeamline/CMakeLists.txt index 0dfd61844..008588eee 100644 --- a/tests/classic_src/AbsBeamline/CMakeLists.txt +++ b/tests/classic_src/AbsBeamline/CMakeLists.txt @@ -2,6 +2,7 @@ set (_SRCS DipoleFieldTest.cpp MultipoleTTest.cpp OffsetTest.cpp + PolynomialTest.cpp RingTest.cpp SBend3DTest.cpp ScalingFFAGMagnetTest.cpp diff --git a/tests/classic_src/AbsBeamline/MultipoleTTest.cpp b/tests/classic_src/AbsBeamline/MultipoleTTest.cpp index 25d485530..07aaf8961 100644 --- a/tests/classic_src/AbsBeamline/MultipoleTTest.cpp +++ b/tests/classic_src/AbsBeamline/MultipoleTTest.cpp @@ -1,13 +1,21 @@ #include "gtest/gtest.h" +#include "AbsBeamline/Component.h" #include "AbsBeamline/MultipoleT.h" +#include "AbsBeamline/MultipoleTBase.h" +#include "AbsBeamline/MultipoleTStraight.h" +#include "AbsBeamline/MultipoleTCurvedConstRadius.h" +#include "AbsBeamline/MultipoleTCurvedVarRadius.h" +#include "AbsBeamline/MultipoleTFunctions/CoordinateTransform.h" +#include "AbsBeamline/EndFieldModel/Tanh.h" #include "opal_test_utilities/SilenceTest.h" -#include<fstream> +#include <fstream> +#include <cmath> using namespace std; -vector< vector<double> > partialsDerivB(const Vector_t &R,const Vector_t B, double stepSize, MultipoleT* dummyField) +/*vector< vector<double> > partialsDerivB(const Vector_t &R,const Vector_t B, double stepSize, Component* dummyField) { // builds a matrix of all partial derivatives of B -> dx_i B_j vector< vector<double> > allPartials(3, vector<double>(3)); @@ -26,9 +34,9 @@ vector< vector<double> > partialsDerivB(const Vector_t &R,const Vector_t B, doub allPartials[i][j] = (B_next[j] - B_prev[j]) / (2 * stepSize); } return allPartials; -} +}*/ -vector< vector<double> > partialsDerivB_5(const Vector_t &R,const Vector_t B, double stepSize, MultipoleT* dummyField) +vector< vector<double> > partialsDerivB(const Vector_t &R,const Vector_t B, double stepSize, Component* dummyField) { // builds a matrix of all partial derivatives of B -> dx_i B_j vector< vector<double> > allPartials(3, vector<double>(3)); @@ -53,7 +61,7 @@ vector< vector<double> > partialsDerivB_5(const Vector_t &R,const Vector_t B, do return allPartials; } -double calcDivB(Vector_t &R, Vector_t B, double stepSize, MultipoleT* dummyField ) +double calcDivB(Vector_t &R, Vector_t B, double stepSize, Component* dummyField ) { double div = 0; vector< vector<double> > partials (3, vector<double>(3)); @@ -63,7 +71,7 @@ double calcDivB(Vector_t &R, Vector_t B, double stepSize, MultipoleT* dummyField return div; } -vector<double> calcCurlB(Vector_t &R, Vector_t B, double stepSize, MultipoleT* dummyField) +vector<double> calcCurlB(Vector_t &R, Vector_t B, double stepSize, Component* dummyField) { vector<double> curl(3); vector< vector<double> > partials(3, vector<double>(3)); @@ -74,7 +82,7 @@ vector<double> calcCurlB(Vector_t &R, Vector_t B, double stepSize, MultipoleT* d return curl; } -TEST(MultipoleTTest, Field) +TEST(MultipoleTTest, DISABLED_Field) { OpalTestUtilities::SilenceTest silencer; @@ -109,6 +117,7 @@ TEST(MultipoleTTest, Field) } } fout.close(); + delete myMagnet; } TEST(MultipoleTTest, Maxwell) { @@ -118,7 +127,7 @@ TEST(MultipoleTTest, Maxwell) { double centralField = 5; double fringeLength = 0.5; // the highest differential in the fringe field - double max_index = 20; + double max_index = 0.5; //Set the magnet myMagnet->setFringeField(centralField, fringeLength, max_index); myMagnet->setTransMaxOrder(1); @@ -129,8 +138,8 @@ TEST(MultipoleTTest, Maxwell) { myMagnet->setMaxOrder(3); //ofstream fout("Quad_CurlB_off"); Vector_t R(0., 0., 0.), P(3), E(3); - double t = 0., x, z, s, stepSize= 1e-6; - for(x = 0.0; x <= 0.0 ; x += 0.001) { + double t = 0., x, z, s, stepSize= 1e-7; + for(x = -0.2; x <= 0.2 ; x += 0.1) { for(z = 0.0; z <= 0.02 ; z += 0.001) { for(s = -10; s <= 10 ; s += 0.1) { R[0] = x; @@ -138,62 +147,207 @@ TEST(MultipoleTTest, Maxwell) { R[2] = s; Vector_t B(0., 0., 0.); myMagnet->apply(R, P, t, E, B); - double div = 0.; - div = calcDivB(R, B, stepSize, myMagnet); + double div = calcDivB(R, B, stepSize, myMagnet); + vector<double> curl = calcCurlB(R, B, stepSize, myMagnet); EXPECT_NEAR(div, 0.0, 0.01); - vector<double> curl; - curl = calcCurlB(R, B, stepSize, myMagnet); EXPECT_NEAR(curl[0], 0.0, 1e-4); EXPECT_NEAR(curl[1], 0.0, 1e-4); EXPECT_NEAR(curl[2], 0.0, 1e-4); } } } + delete myMagnet; } TEST(MultipoleTTest, CurvedMagnet) { OpalTestUtilities::SilenceTest silencer; - + MultipoleT* myMagnet = new MultipoleT("Combined function"); - myMagnet->setLength(4.0); - myMagnet->setBendAngle(0.0); // BUG small, non-zero bend angle ruins the convergence - myMagnet->setAperture(0.4, 0.4); - myMagnet->setFringeField(2, 0.5, 0.5); + myMagnet->setLength(4.4); + myMagnet->setBoundingBoxLength(0.0); + myMagnet->setBendAngle(0.628); + myMagnet->setAperture(3.5, 3.5); + myMagnet->setFringeField(2.2, 0.3, 0.3); myMagnet->setVarRadius(); - myMagnet->setVarStep(0.1); myMagnet->setTransMaxOrder(1); - myMagnet->setMaxOrder(4); myMagnet->setRotation(0.0); myMagnet->setEntranceAngle(0.0); - myMagnet->setTransProfile(0, 10); + myMagnet->setTransProfile(0, 1); myMagnet->setTransProfile(1, 1); - double t=0., x, y, z; - double stepSize = 1e-7; + myMagnet->setMaxXOrder(10); + myMagnet->setMaxOrder(5); + double t = 0.0; + double stepSize = 1e-3; + double x[21] = {-1.12, -0.99, -0.86, -0.77, -0.65, -0.53, -0.42, -0.29, -0.19, -0.11, -0.039, 0.00, -0.030, -0.12, -0.26, -0.40, -0.56, -0.72, -0.86, -0.96, -1.12}; + double y[21] = {-2.74, -2.58, -2.30, -2.27, -2.00, -1.83, -1.62, -1.45, -1.13, -0.87, 0.53, 0.00, 0.46, 0.90, 1.36, 1.60, 1.83, 2.17, 2.30, 2.45, 2.77}; + double z = 0.2; Vector_t R(0.0, 0.0, 0.0), P(3), E(3); - for(x = -0.2 ; x <= 0.2 ; x += 0.1) { - for(y = -6.0; y <= 6.0 ; y += 1.0) { - for(z = 0.1; z <= 0.101 ; z += 0.1) { - R[0] = x; - R[1] = z; - R[2] = y; - Vector_t B(0., 0., 0.); - myMagnet->apply(R, P, t, E, B); - double div = calcDivB(R, B, stepSize, myMagnet); - vector<double> curl = calcCurlB(R, B, stepSize, myMagnet); - double curlMag = 0.0; - curlMag += gsl_sf_pow_int(curl[0], 2.0); - curlMag += gsl_sf_pow_int(curl[1], 2.0); - curlMag += gsl_sf_pow_int(curl[2], 2.0); - curlMag = sqrt(curlMag); - //if (sqrt(gsl_sf_pow_int(B[0], 2) + gsl_sf_pow_int(B[1], 2) + gsl_sf_pow_int(B[2], 2)) != 0) { - // abs /= sqrt(gsl_sf_pow_int(B[0], 2) + gsl_sf_pow_int(B[1], 2) + gsl_sf_pow_int(B[2], 2)); - //} - EXPECT_NEAR(curlMag+fabs(div), 0, 1e-6) - << "R: " << x << " " << y << " " << z - << " B: " << B[0] << " " << B[1] << " " << B[2] - << " Del: " << div << " " << curlMag << std::endl; - } + for (int n = 0; n < 21; n++) { + R[0] = x[n]; + R[1] = z; + R[2] = y[n]; + Vector_t B(0., 0., 0.); + myMagnet->apply(R, P, t, E, B); + double div = calcDivB(R, B, stepSize, myMagnet); + vector<double> curl = calcCurlB(R, B, stepSize, myMagnet); + double curlMag = 0.0; + curlMag += gsl_sf_pow_int(curl[0], 2.0); + curlMag += gsl_sf_pow_int(curl[1], 2.0); + curlMag += gsl_sf_pow_int(curl[2], 2.0); + curlMag = sqrt(curlMag); + coordinatetransform::CoordinateTransform t(x[n], z, y[n], 2.2, 0.3, 0.3, 4.4 / 0.628); + std::vector<double> r = t.getTransformation(); + EXPECT_NEAR(div, 0, 5e-3) + << "R: " << r[0] << " " << r[1] << " " << r[2] << std::endl + << "R: " << x[n] << " " << z << " " << y[n] << std::endl + << "B: " << B[0] << " " << B[1] << " " << B[2] << std::endl + << "Del: " << div << " " << curl[0] << " " << curl[1] << " " << curl[2] << std::endl; + EXPECT_NEAR(curlMag, 0, 1e-9) + << "R: " << r[0] << " " << r[1] << " " << r[2] << std::endl + << "R: " << x[n] << " " << z << " " << y[n] << std::endl + << "B: " << B[0] << " " << B[1] << " " << B[2] << std::endl + << "Del: " << div << " " << curl[0] << " " << curl[1] << " " << curl[2] << std::endl; + } + delete myMagnet; +} + +TEST(MultipoleTTest, MultipoleTStraight) { + OpalTestUtilities::SilenceTest silencer; + + MultipoleTStraight* myMagnet = new MultipoleTStraight("Combined function"); + myMagnet->setLength(4.4); + myMagnet->setAperture(3.5, 3.5); + myMagnet->setFringeField(2.2, 0.3, 0.3); + myMagnet->setTransMaxOrder(1); + myMagnet->setRotation(0.0); + myMagnet->setEntranceAngle(0.0); + myMagnet->setTransProfile(0, 1); + myMagnet->setTransProfile(1, 1); + myMagnet->setMaxOrder(5); + double t = 0.0; + double stepSize = 1e-3; + double z = -0.3; + Vector_t R(0.0, 0.0, 0.0), P(3), E(3); + for (double x = -0.3; x <= 0.300001; x += 0.1) { + for (double y = -3.0; y <= 3.00001; y += 1.0) { + R[0] = x; + R[1] = z; + R[2] = y; + Vector_t B(0., 0., 0.); + myMagnet->apply(R, P, t, E, B); + double div = calcDivB(R, B, stepSize, myMagnet); + vector<double> curl = calcCurlB(R, B, stepSize, myMagnet); + double curlMag = 0.0; + curlMag += gsl_sf_pow_int(curl[0], 2.0); + curlMag += gsl_sf_pow_int(curl[1], 2.0); + curlMag += gsl_sf_pow_int(curl[2], 2.0); + curlMag = sqrt(curlMag); + EXPECT_NEAR(div, 0, 1e-6) + << "R: " << x << " " << z << " " << y << std::endl + << "B: " << B[0] << " " << B[1] << " " << B[2] << std::endl + << "Del: " << div << " " << curl[0] << " " << curl[1] << " " << curl[2] << std::endl; + EXPECT_NEAR(curlMag, 0, 1e-9) + << "R: " << x << " " << z << " " << y << std::endl + << "B: " << B[0] << " " << B[1] << " " << B[2] << std::endl + << "Del: " << div << " " << curl[0] << " " << curl[1] << " " << curl[2] << std::endl; } } + delete myMagnet; +} + +TEST(MultipoleTTest, MultipoleTCurvedConstRadius) { + OpalTestUtilities::SilenceTest silencer; + + MultipoleTCurvedConstRadius* myMagnet = new MultipoleTCurvedConstRadius("Combined function"); + myMagnet->setLength(4.4); + myMagnet->setBendAngle(0.628); + myMagnet->setAperture(3.5, 3.5); + myMagnet->setFringeField(2.2, 0.3, 0.3); + myMagnet->setTransMaxOrder(1); + myMagnet->setRotation(0.0); + myMagnet->setEntranceAngle(0.0); + myMagnet->setTransProfile(0, 1); + myMagnet->setTransProfile(1, 1); + myMagnet->setMaxXOrder(20); + myMagnet->setMaxOrder(5); + double t = 0.0; + double stepSize = 1e-3; + double radius = 4.4 / 0.628; + double z = 0.2; + Vector_t R(0.0, 0.0, 0.0), P(3), E(3); + for (double theta = 0; theta <= 0.3001; theta += 0.2) { + double x = radius * cos(theta) - radius; + double y = radius * sin(theta); + for (double delta = -0.3; delta <= 0.3001; delta += 0.02) { + Vector_t R(0.0, 0.0, 0.0), P(3), E(3); + R[0] = x + delta * cos(theta); + R[1] = z; + R[2] = y + delta * sin(theta); + Vector_t B(0., 0., 0.); + myMagnet->apply(R, P, t, E, B); + double div = calcDivB(R, B, stepSize, myMagnet); + vector<double> curl = calcCurlB(R, B, stepSize, myMagnet); + double curlMag = 0.0; + curlMag += gsl_sf_pow_int(curl[0], 2.0); + curlMag += gsl_sf_pow_int(curl[1], 2.0); + curlMag += gsl_sf_pow_int(curl[2], 2.0); + curlMag = sqrt(curlMag); + EXPECT_NEAR(div, 0, 5e-6) + << "R: " << delta << " " << z << " " << radius * theta << std::endl + << "B: " << B[0] << " " << B[1] << " " << B[2] << std::endl + << "Del: " << div << " " << curl[0] << " " << curl[1] << " " << curl[2] << std::endl; + EXPECT_NEAR(curlMag, 0, 1e-9) + << "R: " << delta << " " << z << " " << radius * theta << std::endl + << "B: " << B[0] << " " << B[1] << " " << B[2] << std::endl + << "Del: " << div << " " << curl[0] << " " << curl[1] << " " << curl[2] << std::endl; + } + } + delete myMagnet; +} + +TEST(MultipoleTTest, MultipoleTCurvedVarRadius) { + OpalTestUtilities::SilenceTest silencer; + + MultipoleTCurvedVarRadius* myMagnet = new MultipoleTCurvedVarRadius("Combined function"); + myMagnet->setLength(4.4); + myMagnet->setBendAngle(0.628); + myMagnet->setAperture(3.5, 3.5); + myMagnet->setFringeField(2.2, 0.3, 0.3); + myMagnet->setTransMaxOrder(1); + myMagnet->setRotation(0.0); + myMagnet->setEntranceAngle(0.0); + myMagnet->setTransProfile(0, 1); + myMagnet->setTransProfile(1, 1); + myMagnet->setMaxXOrder(10); + myMagnet->setMaxOrder(5); + double t = 0.0; + double stepSize = 1e-3; + double x[21] = {-1.12, -0.99, -0.86, -0.77, -0.65, -0.53, -0.42, -0.29, -0.19, -0.11, -0.039, 0.00, -0.030, -0.12, -0.26, -0.40, -0.56, -0.72, -0.86, -0.96, -1.12}; + double y[21] = {-2.74, -2.58, -2.30, -2.27, -2.00, -1.83, -1.62, -1.45, -1.13, -0.87, 0.53, 0.00, 0.46, 0.90, 1.36, 1.60, 1.83, 2.17, 2.30, 2.45, 2.77}; + double z = 0.2; + Vector_t R(0.0, 0.0, 0.0), P(3), E(3); + for (int n = 0; n < 21; n++) { + R[0] = x[n]; + R[1] = z; + R[2] = y[n]; + Vector_t B(0., 0., 0.); + myMagnet->apply(R, P, t, E, B); + double div = calcDivB(R, B, stepSize, myMagnet); + vector<double> curl = calcCurlB(R, B, stepSize, myMagnet); + double curlMag = 0.0; + curlMag += gsl_sf_pow_int(curl[0], 2.0); + curlMag += gsl_sf_pow_int(curl[1], 2.0); + curlMag += gsl_sf_pow_int(curl[2], 2.0); + curlMag = sqrt(curlMag); + EXPECT_NEAR(div, 0, 5e-3) + << "R: " << x[n] << " " << z << " " << y[n] << std::endl + << "B: " << B[0] << " " << B[1] << " " << B[2] << std::endl + << "Del: " << div << " " << curl[0] << " " << curl[1] << " " << curl[2] << std::endl; + EXPECT_NEAR(curlMag, 0, 1e-9) + << "R: " << x[n] << " " << z << " " << y[n] << std::endl + << "B: " << B[0] << " " << B[1] << " " << B[2] << std::endl + << "Del: " << div << " " << curl[0] << " " << curl[1] << " " << curl[2] << std::endl; + } + delete myMagnet; +} -} \ No newline at end of file diff --git a/tests/classic_src/AbsBeamline/PolynomialTest.cpp b/tests/classic_src/AbsBeamline/PolynomialTest.cpp new file mode 100644 index 000000000..396a392ef --- /dev/null +++ b/tests/classic_src/AbsBeamline/PolynomialTest.cpp @@ -0,0 +1,573 @@ +#include "gtest/gtest.h" +#include "opal_test_utilities/SilenceTest.h" +#include "AbsBeamline/MultipoleTFunctions/Polynomial.h" +#include "AbsBeamline/MultipoleTFunctions/DifferentialOperator.h" +#include "AbsBeamline/MultipoleTFunctions/RecursionRelation.h" +#include "AbsBeamline/MultipoleTFunctions/TwoPolynomial.h" +#include "AbsBeamline/MultipoleTFunctions/PolynomialSum.h" +#include "AbsBeamline/MultipoleTFunctions/DifferentialOperatorTwo.h" +#include "AbsBeamline/MultipoleTFunctions/RecursionRelationTwo.h" +#include "AbsBeamline/MultipoleTFunctions/tanhDeriv.h" +#include <vector> + +TEST(PolynomialTest, Polynomial) { + OpalTestUtilities::SilenceTest silencer; + std::vector<int> vec1, vec2; + for (int i = 0; i < 10; i++) { + vec1.push_back(i); + vec2.push_back(1); + } + polynomial::Polynomial poly1, poly2(vec1); + polynomial::Polynomial poly3(poly2); + polynomial::Polynomial poly4; + poly4 = poly3; + polynomial::Polynomial poly5(vec2); + for (int i = 0; i < 10; i++) { + EXPECT_EQ(poly1.getCoefficient(i), 0); + EXPECT_EQ(poly2.getCoefficient(i), i); + EXPECT_EQ(poly3.getCoefficient(i), i); + EXPECT_EQ(poly4.getCoefficient(i), i); + EXPECT_EQ(poly5.getCoefficient(i), 1); + } + poly2.differentiatePolynomial(); + poly3.multiplyPolynomial(poly1); + poly4.addPolynomial(poly4); + poly5.multiplyPolynomial(poly5); + for (int i = 0; i < 9; i++) { + EXPECT_EQ(poly1.getCoefficient(i), 0); + EXPECT_EQ(poly2.getCoefficient(i), (i + 1) * (i + 1)); + EXPECT_EQ(poly3.getCoefficient(i), 0); + EXPECT_EQ(poly4.getCoefficient(i), 2 * i); + EXPECT_EQ(poly5.getCoefficient(i), i + 1); + } + EXPECT_EQ(poly1.getMaxXorder(), 0); + EXPECT_EQ(poly2.getMaxXorder(), 8); + EXPECT_EQ(poly3.getMaxXorder(), 0); + poly3.setMaxXorder(17); + EXPECT_EQ(poly3.getMaxXorder(), 17); + EXPECT_EQ(poly4.getMaxXorder(), 9); + EXPECT_EQ(poly5.getMaxXorder(), 18); + poly2.setZero(); + EXPECT_EQ(poly2.getMaxXorder(), 0); + poly5.truncate(5); + EXPECT_EQ(poly5.getMaxXorder(), 5); + EXPECT_NEAR(poly5.evaluatePolynomial(0.2), 1.56192, 1e-5); + poly2.setCoefficient(18, 2); + EXPECT_EQ(poly2.getCoefficient(2), 18); + poly2.setCoefficient(7, 6); + EXPECT_EQ(poly2.getCoefficient(6), 7); + poly2.setCoefficient(0, 0); + EXPECT_EQ(poly2.getCoefficient(0), 0); +} + +TEST(PolynomialTest, DifferentialOperator) { + OpalTestUtilities::SilenceTest silencer; + std::vector<int> vec1, vec2; + for (int i = 0; i < 2; i++) { + vec1.push_back(i + 1); + vec2.push_back(1); + } + polynomial::DifferentialOperator op1, op2(1, 1), op3(1, 1); + polynomial::DifferentialOperator op4(op3), op5; + op5 = op4; + for (int i = 0; i < 2; i++) { + EXPECT_EQ((int)op1.getXDerivatives(), 0); + EXPECT_EQ((int)op1.getSDerivatives(), 0); + EXPECT_EQ((int)op2.getXDerivatives(), 1); + EXPECT_EQ((int)op2.getXDerivatives(), 1); + EXPECT_EQ((int)op3.getXDerivatives(), 1); + EXPECT_EQ((int)op3.getXDerivatives(), 1); + EXPECT_EQ((int)op4.getXDerivatives(), 1); + EXPECT_EQ((int)op4.getXDerivatives(), 1); + EXPECT_EQ((int)op5.getXDerivatives(), 1); + EXPECT_EQ((int)op5.getXDerivatives(), 1); + } + op3.resizeX(5); + EXPECT_EQ((int)op3.getXDerivatives(), 5); + op3.resizeS(6); + EXPECT_EQ((int)op3.getSDerivatives(), 6); + op2.setPolynomial(vec1, 1, 1); + op2.setPolynomial(vec2, 0, 0); + EXPECT_TRUE(op2.isPolynomialZero(1, 0)); + EXPECT_TRUE(op2.isPolynomialZero(0, 1)); + op2.addOperator(op2); + EXPECT_TRUE(op2.isPolynomialZero(1, 0)); + EXPECT_TRUE(op2.isPolynomialZero(0, 1)); + op5 = op2; + op1 = op2; + op2.differentiateX(); + op5.doubleDifferentiateS(); + EXPECT_NEAR(op2.evaluatePolynomial(0.2, 0, 0), 2.0, 1e-6); + EXPECT_NEAR(op2.evaluatePolynomial(0.2, 0, 1), 0.0, 1e-6); + EXPECT_NEAR(op2.evaluatePolynomial(0.2, 1, 0), 2.4, 1e-6); + EXPECT_NEAR(op2.evaluatePolynomial(0.2, 1, 1), 4.0, 1e-6); + EXPECT_NEAR(op2.evaluatePolynomial(0.2, 1, 2), 0.0, 1e-6); + EXPECT_NEAR(op2.evaluatePolynomial(0.2, 2, 1), 2.8, 1e-6); + EXPECT_NEAR(op5.evaluatePolynomial(0.2, 0, 0), 0.0, 1e-6); + EXPECT_NEAR(op5.evaluatePolynomial(0.2, 0, 1), 2.4, 1e-6); + EXPECT_NEAR(op5.evaluatePolynomial(0.2, 1, 0), 0.0, 1e-6); + EXPECT_NEAR(op5.evaluatePolynomial(0.2, 1, 1), 0.0, 1e-6); + EXPECT_NEAR(op5.evaluatePolynomial(0.2, 2, 1), 0.0, 1e-6); + EXPECT_NEAR(op5.evaluatePolynomial(0.2, 1, 2), 2.8, 1e-6); + std::vector<int> vec3; + vec3.resize(10); + vec3[9] = 10; + vec3[8] = 1; + polynomial::DifferentialOperator op6; + op6.setPolynomial(vec3, 2, 2); + EXPECT_NEAR(op6.evaluatePolynomial(1.2, 2, 2), 55.897620, 1e-6); + op6.truncate(8); + EXPECT_NEAR(op6.evaluatePolynomial(1.2, 2, 2), 4.299817, 1e-6); +} + +TEST (PolynomialTest, RecursionRelation) { + polynomial::RecursionRelation r1, r2(1, 4); + polynomial::RecursionRelation r3(r2), r4, r5(1, 4); + r4 = r3; + r5.applyOperator(); + EXPECT_EQ(r1.getMaxXDerivatives(), 0); + EXPECT_EQ(r1.getMaxSDerivatives(), 0); + EXPECT_EQ(r2.getMaxXDerivatives(), 2); + EXPECT_EQ(r2.getMaxSDerivatives(), 1); + EXPECT_EQ(r3.getMaxXDerivatives(), 2); + EXPECT_EQ(r3.getMaxSDerivatives(), 1); + EXPECT_EQ(r4.getMaxXDerivatives(), 2); + EXPECT_EQ(r4.getMaxSDerivatives(), 1); + EXPECT_EQ(r5.getMaxXDerivatives(), 4); + EXPECT_EQ(r5.getMaxSDerivatives(), 2); + EXPECT_NEAR(r4.evaluatePolynomial(0.2, 1, 0), 0.8336, 1e-6); + r4.truncate(2); + EXPECT_NEAR(r4.evaluatePolynomial(0.2, 1, 0), 0.84, 1e-6); + r5.truncate(2); + EXPECT_NEAR(r5.evaluatePolynomial(0.2, 4, 0), 1.0, 1e-6); + r5.truncate(1); + EXPECT_NEAR(r5.evaluatePolynomial(0.2, 2, 1), 1.2, 1e-6); + EXPECT_TRUE(r2.isPolynomialZero(0, 0)); + EXPECT_TRUE(r2.isPolynomialZero(2, 1)); + EXPECT_TRUE(r2.isPolynomialZero(1, 1)); + r2.resizeX(4); + EXPECT_EQ(r2.getMaxXDerivatives(), 4); + r2.resizeS(7); + EXPECT_EQ(r2.getMaxSDerivatives(), 7); + r3.differentiateX(); + EXPECT_NEAR(r3.evaluatePolynomial(0.2, 3, 0), 1.0, 1e-6); + EXPECT_NEAR(r3.evaluatePolynomial(0.2, 1, 1), 0.696, 1e-6); +} + +TEST (PolynomialTest, TwoPolynomial) { + std::vector<int> v1, v2, v3; + std::vector<std::size_t> vv1, vv2, vv3; + v1.resize(3); + v2.resize(3); + v3.resize(3); + vv1.resize(3); + vv2.resize(3); + vv3.resize(3); + for (int i = 0; i < 3; i++) { + v1[i] = i + 1; + vv1[i] = i + 1; + v2[i] = (i + 1) * (i + 1); + vv2[i] = (i + 1) * (i + 1); + v3[i] = 0; + vv3[i] = 0; + } + std::vector<std::vector<int>> vec1, vec2; + polynomial::TwoPolynomial zpoly(vec1); + EXPECT_EQ(zpoly.getMaxXorder(), 0); + EXPECT_EQ(zpoly.getMaxSorder(), 0); + EXPECT_EQ(zpoly.getCoefficient(0, 0), 0); + vec1.resize(2); + vec2.resize(3); + polynomial::TwoPolynomial zpoly2(vec1); + EXPECT_EQ(zpoly2.getMaxXorder(), 0); + EXPECT_EQ(zpoly2.getMaxSorder(), 0); + EXPECT_EQ(zpoly2.getCoefficient(0, 0), 0); + vec1[0] = v1; + EXPECT_THROW({polynomial::TwoPolynomial dummy(vec1);}, std::length_error); + vec1[1] = v2; + vec2[0] = v3; + vec2[1] = v2; + vec2[2] = v1; + polynomial::TwoPolynomial poly1, poly2(vec1), poly3(vec2); + polynomial::TwoPolynomial poly4(poly2), poly5; + poly5 = poly3; + EXPECT_TRUE(poly1.isZero()); + EXPECT_EQ(poly1.getCoefficient(0, 0), 0); + EXPECT_EQ(poly2.getCoefficient(0, 0), 1); + EXPECT_EQ(poly3.getCoefficient(0, 0), 0); + EXPECT_EQ(poly4.getCoefficient(0, 0), 1); + EXPECT_EQ(poly5.getCoefficient(0, 0), 0); + EXPECT_EQ(poly1.getCoefficient(0, 1), 0); + EXPECT_EQ(poly2.getCoefficient(0, 1), 2); + EXPECT_EQ(poly3.getCoefficient(0, 1), 0); + EXPECT_EQ(poly4.getCoefficient(0, 1), 2); + EXPECT_EQ(poly5.getCoefficient(0, 1), 0); + EXPECT_EQ(poly1.getCoefficient(1, 0), 0); + EXPECT_EQ(poly2.getCoefficient(1, 0), 1); + EXPECT_EQ(poly3.getCoefficient(1, 0), 1); + EXPECT_EQ(poly4.getCoefficient(1, 0), 1); + EXPECT_EQ(poly5.getCoefficient(1, 0), 1); + EXPECT_EQ(poly1.getCoefficient(1, 1), 0); + EXPECT_EQ(poly2.getCoefficient(1, 1), 4); + EXPECT_EQ(poly3.getCoefficient(1, 1), 4); + EXPECT_EQ(poly4.getCoefficient(1, 1), 4); + EXPECT_EQ(poly5.getCoefficient(1, 1), 4); + EXPECT_EQ(poly1.getCoefficient(0, 2), 0); + EXPECT_EQ(poly2.getCoefficient(0, 2), 3); + EXPECT_EQ(poly3.getCoefficient(0, 2), 0); + EXPECT_EQ(poly4.getCoefficient(0, 2), 3); + EXPECT_EQ(poly5.getCoefficient(0, 2), 0); + EXPECT_EQ(poly1.getCoefficient(1, 2), 0); + EXPECT_EQ(poly2.getCoefficient(1, 2), 9); + EXPECT_EQ(poly3.getCoefficient(1, 2), 9); + EXPECT_EQ(poly4.getCoefficient(1, 2), 9); + EXPECT_EQ(poly5.getCoefficient(1, 2), 9); + EXPECT_EQ(poly3.getCoefficient(2, 2), 3); + EXPECT_EQ(poly5.getCoefficient(2, 2), 3); + EXPECT_EQ(poly3.getCoefficient(2, 1), 2); + EXPECT_EQ(poly5.getCoefficient(2, 1), 2); + poly1.setCoefficient(6, 2, 0); + EXPECT_NEAR(poly1.evaluatePolynomial(2, 0), 24.0, 1e-6); + EXPECT_NEAR(poly1.evaluatePolynomial(0, 2), 0.0, 1e-6); + poly1.multiplyConstant(4); + EXPECT_NEAR(poly1.evaluatePolynomial(2, 0), 96.0, 1e-6); + EXPECT_NEAR(poly1.evaluatePolynomial(0, 2), 0.0, 1e-6); + poly2.multiplyPolynomial(poly1); + EXPECT_EQ(poly2.getCoefficient(0, 0), 0); + EXPECT_EQ(poly2.getCoefficient(2, 0), 24); + EXPECT_EQ(poly2.getCoefficient(2, 2), 72); + EXPECT_EQ(poly2.getCoefficient(3, 1), 96); + EXPECT_EQ(poly2.getMaxXorder(), 3); + EXPECT_EQ(poly2.getMaxSorder(), 2); + poly2.setMaxXorder(6); + poly2.setMaxSorder(7); + EXPECT_EQ(poly2.getMaxXorder(), 6); + EXPECT_EQ(poly2.getMaxSorder(), 7); + EXPECT_EQ(poly3.getNdSfactors(), 0); + poly5.differentiateX(); + EXPECT_EQ(poly5.getCoefficient(0, 1), 4); + EXPECT_EQ(poly5.getCoefficient(1, 1), 4); + EXPECT_EQ(poly5.getCoefficient(0, 2), 9); + EXPECT_EQ(poly5.getCoefficient(1, 0), 2); + poly3.differentiateS(); + EXPECT_EQ(poly3.getCoefficient(0, 1), 0); + EXPECT_EQ(poly3.getCoefficient(1, 1), 18); + EXPECT_EQ(poly3.getCoefficient(0, 2), 0); + EXPECT_EQ(poly3.getCoefficient(1, 0), 4); + EXPECT_EQ(poly3.getNdSfactors(), 1); + poly3.setdSfactors(vv1); + EXPECT_EQ(poly3.getNdSfactors(), 3); + std::vector<std::size_t> v4 = poly3.getdSfactors(); + EXPECT_EQ(v4[0], 1); + EXPECT_EQ(v4[1], 2); + EXPECT_EQ(v4[2], 3); + poly3.setZero(); + EXPECT_TRUE(poly3.isZero()); + EXPECT_NEAR(poly2.evaluatePolynomial(2, 1), 3264.0, 1e-6); + EXPECT_NEAR(poly2.evaluatePolynomial(1, 2), 1488.0, 1e-6); + EXPECT_NEAR(poly2.evaluatePolynomial(2, 2), 10272.0, 1e-6); + poly2.truncate(2); + EXPECT_NEAR(poly2.evaluatePolynomial(2, 1), 576.0, 1e-6); + EXPECT_NEAR(poly2.evaluatePolynomial(1, 2), 408.0, 1e-6); + EXPECT_NEAR(poly2.evaluatePolynomial(2, 2), 1632.0, 1e-6); + polynomial::TwoPolynomial greater, smaller; + greater.setdSfactors(vv2); + smaller.setdSfactors(vv1); + EXPECT_TRUE(smaller < greater); + EXPECT_FALSE(smaller == greater); + greater.setdSfactors(vv1); + smaller.setdSfactors(vv3); + EXPECT_TRUE(smaller < greater); + EXPECT_FALSE(smaller == greater); + greater.setdSfactors(vv2); + smaller.setdSfactors(vv3); + EXPECT_TRUE(smaller < greater); + EXPECT_FALSE(smaller == greater); + greater.setdSfactors(vv2); + smaller.setdSfactors(vv2); + EXPECT_FALSE(smaller < greater); + EXPECT_TRUE(smaller == greater); + greater.setdSfactors(vv2); + smaller.setdSfactors(vv2); + EXPECT_FALSE(greater < smaller); + EXPECT_TRUE(smaller == greater); + vv2[2] = 1000; + greater.setdSfactors(vv2); + EXPECT_TRUE(smaller < greater); +} + +TEST (PolynomialTest, PolynomialSum) { + std::vector<int> v1, v2, v3; + v1.resize(3); + v2.resize(3); + v3.resize(3); + for (int i = 0; i < 3; i++) { + v1[i] = i + 1; + v2[i] = (i + 1) * (i + 1); + v3[i] = 0; + } + std::vector<std::vector<int>> vec1, vec2; + vec1.resize(2); + vec2.resize(3); + vec1[0] = v1; + vec1[1] = v2; + vec2[0] = v3; + vec2[1] = v2; + vec2[2] = v1; + polynomial::TwoPolynomial poly1, poly2(vec1), poly3(vec2); + polynomial::TwoPolynomial poly4(poly2), poly5; + poly5 = poly3; + poly1.setCoefficient(6, 2, 0); + polynomial::PolynomialSum sum1, sum2(poly2), sum3(poly3); + polynomial::PolynomialSum sum4(sum2), sum5; + sum5 = sum3; + EXPECT_TRUE(sum1.isPolynomialZero(1)); + EXPECT_TRUE(sum2.isPolynomialZero(1)); + EXPECT_TRUE(sum3.isPolynomialZero(2)); + EXPECT_TRUE(sum4.isPolynomialZero(6)); + EXPECT_TRUE(sum5.isPolynomialZero(1)); + EXPECT_NEAR(sum2.evaluatePolynomial(0, 2, 1), 34.0, 1e-6); + EXPECT_NEAR(sum2.evaluatePolynomial(0, 1, 2), 62.0, 1e-6); + EXPECT_NEAR(sum2.evaluatePolynomial(0, 2, 2), 107.0, 1e-6); + EXPECT_NEAR(sum3.evaluatePolynomial(0, 2, 1), 52.0, 1e-6); + EXPECT_NEAR(sum3.evaluatePolynomial(0, 1, 2), 62.0, 1e-6); + EXPECT_NEAR(sum3.evaluatePolynomial(0, 2, 2), 158.0, 1e-6); + sum2.differentiateX(); + sum3.differentiateS(); + EXPECT_NEAR(sum2.evaluatePolynomial(0, 2, 1), 14.0, 1e-6); + EXPECT_NEAR(sum2.evaluatePolynomial(0, 1, 2), 45.0, 1e-6); + EXPECT_NEAR(sum2.evaluatePolynomial(0, 2, 2), 45.0, 1e-6); + EXPECT_NEAR(sum3.evaluatePolynomial(0, 2, 1), 76.0, 1e-6); + EXPECT_NEAR(sum3.evaluatePolynomial(0, 1, 2), 54.0, 1e-6); + EXPECT_NEAR(sum3.evaluatePolynomial(0, 2, 2), 136.0, 1e-6); + EXPECT_EQ(sum1.numberOfTerms(), 0); + EXPECT_EQ(sum2.numberOfTerms(), 1); + EXPECT_EQ(sum3.numberOfTerms(), 1); + EXPECT_EQ(sum4.numberOfTerms(), 1); + EXPECT_EQ(sum5.numberOfTerms(), 1); + EXPECT_TRUE(sum2.isPolynomialZero(1)); + EXPECT_FALSE(sum2.isPolynomialZero(0)); + EXPECT_TRUE(sum2.isPolynomialZero(3)); + EXPECT_TRUE(sum3.isPolynomialZero(1)); + EXPECT_FALSE(sum3.isPolynomialZero(0)); + std::vector<std::size_t> v4 = sum3.getdSfactors(0); + EXPECT_EQ(v4[0], 1); + sum3.differentiateS(); + v4 = sum3.getdSfactors(0); + EXPECT_EQ(v4[0], 2); + v4 = sum3.getdSfactors(1); + EXPECT_EQ(v4[0], 0); + EXPECT_EQ(v4[1], 1); + sum4.multiplyPolynomial(poly1); + EXPECT_NEAR(sum4.evaluatePolynomial(0, 2, 1), 816.0, 1e-6); + EXPECT_NEAR(sum4.evaluatePolynomial(0, 1, 2), 372.0, 1e-6); + EXPECT_NEAR(sum4.evaluatePolynomial(0, 2, 2), 2568.0, 1e-6); + sum4.truncate(2); + EXPECT_NEAR(sum4.evaluatePolynomial(0, 2, 1), 144.0, 1e-6); + EXPECT_NEAR(sum4.evaluatePolynomial(0, 1, 2), 102.0, 1e-6); + EXPECT_NEAR(sum4.evaluatePolynomial(0, 2, 2), 408.0, 1e-6); + sum4.addPolynomial(sum5); + EXPECT_EQ(sum4.numberOfTerms(), 2); + EXPECT_NEAR(sum4.evaluatePolynomial(1, 2, 1), 52.0, 1e-6); + EXPECT_NEAR(sum4.evaluatePolynomial(1, 1, 2), 62.0, 1e-6); + EXPECT_NEAR(sum4.evaluatePolynomial(1, 2, 2), 158.0, 1e-6); +} + +TEST (PolynomialTest, DifferentialOperatorTwo) { + std::vector<double> dSvalues; + dSvalues.resize(20, 1.0); + std::vector<int> v1, v2, v3; + v1.resize(3); + v2.resize(3); + v3.resize(3); + for (int i = 0; i < 3; i++) { + v1[i] = i + 1; + v2[i] = (i + 1) * (i + 1); + v3[i] = 0; + } + std::vector<std::vector<int>> vec1, vec2; + vec1.resize(2); + vec2.resize(3); + vec1[0] = v1; + vec1[1] = v2; + vec2[0] = v3; + vec2[1] = v2; + vec2[2] = v1; + polynomial::TwoPolynomial poly1, poly2(vec1), poly3(vec2); + polynomial::TwoPolynomial poly4(poly2), poly5; + poly5 = poly3; + poly1.setCoefficient(6, 2, 0); + polynomial::PolynomialSum sum1, sum2(poly2), sum3(poly3); + polynomial::PolynomialSum sum4(sum2), sum5; + sum5 = sum3; + polynomial::DifferentialOperatorTwo op1, op2(1, 1), op3(1, 1); + polynomial::DifferentialOperatorTwo op4(op3), op5; + op5 = op4; + for (int i = 0; i < 2; i++) { + EXPECT_EQ(op1.getXDerivatives(), 0); + EXPECT_EQ(op1.getSDerivatives(), 0); + EXPECT_EQ(op2.getXDerivatives(), 1); + EXPECT_EQ(op2.getXDerivatives(), 1); + EXPECT_EQ(op3.getXDerivatives(), 1); + EXPECT_EQ(op3.getXDerivatives(), 1); + EXPECT_EQ(op4.getXDerivatives(), 1); + EXPECT_EQ(op4.getXDerivatives(), 1); + EXPECT_EQ(op5.getXDerivatives(), 1); + EXPECT_EQ(op5.getXDerivatives(), 1); + } + op3.resizeX(5); + EXPECT_EQ(op3.getXDerivatives(), 5); + op3.resizeS(6); + EXPECT_EQ(op3.getSDerivatives(), 6); + op2.setPolynomial(poly2, 1, 1); + op2.setPolynomial(poly3, 0, 0); + op3.setPolynomial(poly3, 1, 1); + op3.setPolynomial(poly2, 0, 0); + op4.setPolynomial(poly3, 1, 0); + op4.setPolynomial(poly2, 0, 1); + op5.setPolynomial(poly3, 1, 1); + op5.setPolynomial(poly2, 0, 0); + EXPECT_TRUE(op2.isPolynomialZero(1, 0, 0)); + EXPECT_TRUE(op2.isPolynomialZero(0, 1, 0)); + EXPECT_EQ(op2.numberOfTerms(0, 0), 1); + EXPECT_EQ(op2.numberOfTerms(0, 1), 0); + EXPECT_EQ(op2.numberOfTerms(1, 0), 0); + EXPECT_EQ(op2.numberOfTerms(1, 1), 1); + EXPECT_NEAR(op2.evaluatePolynomial(2, 1, 0, 0, dSvalues), 52.0, 1e-6); + EXPECT_NEAR(op2.evaluatePolynomial(1, 2, 0, 0, dSvalues), 62.0, 1e-6); + EXPECT_NEAR(op2.evaluatePolynomial(2, 2, 0, 0, dSvalues), 158.0, 1e-6); + EXPECT_NEAR(op2.evaluatePolynomial(2, 1, 1, 1, dSvalues), 34.0, 1e-6); + EXPECT_NEAR(op2.evaluatePolynomial(1, 2, 1, 1, dSvalues), 62.0, 1e-6); + EXPECT_NEAR(op2.evaluatePolynomial(2, 2, 1, 1, dSvalues), 107.0, 1e-6); + op2.differentiateX(); + op3.differentiateS(); + EXPECT_NEAR(op2.evaluatePolynomial(2, 1, 0, 0, dSvalues), 38.0, 1e-6); + EXPECT_NEAR(op2.evaluatePolynomial(1, 2, 0, 0, dSvalues), 79.0, 1e-6); + EXPECT_NEAR(op2.evaluatePolynomial(2, 2, 0, 0, dSvalues), 113.0, 1e-6); + EXPECT_NEAR(op2.evaluatePolynomial(2, 1, 1, 1, dSvalues), 14.0, 1e-6); + EXPECT_NEAR(op2.evaluatePolynomial(1, 2, 1, 1, dSvalues), 45.0, 1e-6); + EXPECT_NEAR(op2.evaluatePolynomial(2, 2, 1, 1, dSvalues), 45.0, 1e-6); + EXPECT_NEAR(op3.evaluatePolynomial(2, 1, 0, 0, dSvalues), 52.0, 1e-6); + EXPECT_NEAR(op3.evaluatePolynomial(1, 2, 0, 0, dSvalues), 54.0, 1e-6); + EXPECT_NEAR(op3.evaluatePolynomial(2, 2, 0, 0, dSvalues), 94.0, 1e-6); + EXPECT_NEAR(op3.evaluatePolynomial(2, 1, 1, 1, dSvalues), 76.0, 1e-6); + EXPECT_NEAR(op3.evaluatePolynomial(1, 2, 1, 1, dSvalues), 54.0, 1e-6); + EXPECT_NEAR(op3.evaluatePolynomial(2, 2, 1, 1, dSvalues), 136.0, 1e-6); + op3.differentiateS(); + EXPECT_EQ(op3.numberOfTerms(0, 0), 2); + EXPECT_EQ(op3.numberOfTerms(0, 1), 2); + EXPECT_EQ(op3.numberOfTerms(1, 0), 0); + EXPECT_EQ(op3.numberOfTerms(1, 1), 2); + std::vector<std::size_t> v4 = op3.getdSFactors(0, 0, 0); + EXPECT_EQ(v4[0], 2); + v4 = op3.getdSFactors(0, 0, 1); + EXPECT_EQ(v4[0], 0); + EXPECT_EQ(v4[1], 1); + v4 = op3.getdSFactors(0, 1, 0); + EXPECT_EQ(v4[0], 1); + op5.addOperator(op4); + EXPECT_NEAR(op4.evaluatePolynomial(2, 1, 1, 0, dSvalues), 52.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(1, 2, 1, 0, dSvalues), 62.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(2, 2, 1, 0, dSvalues), 158.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(2, 1, 0, 1, dSvalues), 34.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(1, 2, 0, 1, dSvalues), 62.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(2, 2, 0, 1, dSvalues), 107.0, 1e-6); + EXPECT_NEAR(op5.evaluatePolynomial(2, 1, 1, 1, dSvalues), 52.0, 1e-6); + EXPECT_NEAR(op5.evaluatePolynomial(1, 2, 1, 1, dSvalues), 62.0, 1e-6); + EXPECT_NEAR(op5.evaluatePolynomial(2, 2, 1, 1, dSvalues), 158.0, 1e-6); + EXPECT_NEAR(op5.evaluatePolynomial(2, 1, 0, 0, dSvalues), 34.0, 1e-6); + EXPECT_NEAR(op5.evaluatePolynomial(1, 2, 0, 0, dSvalues), 62.0, 1e-6); + EXPECT_NEAR(op5.evaluatePolynomial(2, 2, 0, 0, dSvalues), 107.0, 1e-6); + op4.multiplyPolynomial(poly1); + EXPECT_NEAR(op4.evaluatePolynomial(2, 1, 0, 1, dSvalues), 816.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(1, 2, 0, 1, dSvalues), 372.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(2, 2, 0, 1, dSvalues), 2568.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(2, 1, 1, 0, dSvalues), 1248.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(1, 2, 1, 0, dSvalues), 372.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(2, 2, 1, 0, dSvalues), 3792.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(2, 1, 0, 0, dSvalues), 0.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(1, 2, 0, 0, dSvalues), 0.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(2, 2, 0, 0, dSvalues), 0.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(2, 1, 1, 1, dSvalues), 0.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(1, 2, 1, 1, dSvalues), 0.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(2, 2, 1, 1, dSvalues), 0.0, 1e-6); + op4.truncate(2); + EXPECT_NEAR(op4.evaluatePolynomial(2, 1, 0, 1, dSvalues), 144.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(1, 2, 0, 1, dSvalues), 102.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(2, 2, 0, 1, dSvalues), 408.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(2, 1, 1, 0, dSvalues), 0.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(1, 2, 1, 0, dSvalues), 0.0, 1e-6); + EXPECT_NEAR(op4.evaluatePolynomial(2, 2, 1, 0, dSvalues), 0.0, 1e-6); +} + +TEST (PolynomialTest, RecursionRelationTwo) { + std::vector<double> dSvalues; + dSvalues.resize(20, 1.0); + polynomial::RecursionRelationTwo r1, r2(1, 4); + polynomial::RecursionRelationTwo r3(r2), r4, r5(1, 4); + r4 = r3; + r5.applyOperator(); + EXPECT_EQ(r1.getMaxXDerivatives(), 0); + EXPECT_EQ(r1.getMaxSDerivatives(), 0); + EXPECT_EQ(r2.getMaxXDerivatives(), 2); + EXPECT_EQ(r2.getMaxSDerivatives(), 2); + EXPECT_EQ(r3.getMaxXDerivatives(), 2); + EXPECT_EQ(r3.getMaxSDerivatives(), 2); + EXPECT_EQ(r4.getMaxXDerivatives(), 2); + EXPECT_EQ(r4.getMaxSDerivatives(), 2); + EXPECT_EQ(r5.getMaxXDerivatives(), 4); + EXPECT_EQ(r5.getMaxSDerivatives(), 4); + r4.truncate(4); + EXPECT_NEAR(r4.evaluatePolynomial(2, 1, 0, 0, dSvalues), 0.0, 1e-6); + EXPECT_NEAR(r4.evaluatePolynomial(2, 1, 1, 0, dSvalues), 11.0, 1e-6); + EXPECT_NEAR(r4.evaluatePolynomial(2, 1, 0, 1, dSvalues), 122.0, 1e-6); + EXPECT_NEAR(r4.evaluatePolynomial(2, 1, 1, 1, dSvalues), 0.0, 1e-6); + EXPECT_NEAR(r4.evaluatePolynomial(2, 1, 2, 0, dSvalues), 1.0, 1e-6); + EXPECT_NEAR(r4.evaluatePolynomial(2, 1, 0, 2, dSvalues), 57.0, 1e-6); + EXPECT_NEAR(r4.evaluatePolynomial(2, 1, 1, 2, dSvalues), 0.0, 1e-6); + EXPECT_NEAR(r4.evaluatePolynomial(2, 1, 2, 1, dSvalues), 0.0, 1e-6); + EXPECT_NEAR(r4.evaluatePolynomial(1, 2, 0, 0, dSvalues), 0.0, 1e-6); + EXPECT_NEAR(r4.evaluatePolynomial(1, 2, 1, 0, dSvalues), 22.0, 1e-6); + EXPECT_NEAR(r4.evaluatePolynomial(1, 2, 0, 1, dSvalues), 61.0, 1e-6); + EXPECT_NEAR(r4.evaluatePolynomial(1, 2, 1, 1, dSvalues), 0.0, 1e-6); + EXPECT_NEAR(r4.evaluatePolynomial(1, 2, 2, 0, dSvalues), 1.0, 1e-6); + EXPECT_NEAR(r4.evaluatePolynomial(1, 2, 0, 2, dSvalues), 57.0, 1e-6); + EXPECT_NEAR(r4.evaluatePolynomial(1, 2, 1, 2, dSvalues), 0.0, 1e-6); + EXPECT_NEAR(r4.evaluatePolynomial(1, 2, 2, 1, dSvalues), 0.0, 1e-6); + std::vector<std::size_t> v4 = r4.getdSfactors(2, 0, 0); + EXPECT_EQ((int)v4.size(), 0); + v4 = r4.getdSfactors(0, 1, 0); + EXPECT_EQ((int)v4.size(), 1); + EXPECT_EQ(v4[0], 1); + r4.truncate(2); + EXPECT_NEAR(r4.evaluatePolynomial(0.2, 1, 1, 0, dSvalues), 0.84, 1e-6); + r5.truncate(2); + EXPECT_NEAR(r5.evaluatePolynomial(0.2, 1, 4, 0, dSvalues), 1.0, 1e-6); + r5.truncate(1); + EXPECT_NEAR(r5.evaluatePolynomial(0.2, 1, 2, 2, dSvalues), 1.2, 1e-6); + EXPECT_TRUE(r2.isPolynomialZero(0, 0, 0)); + EXPECT_TRUE(r2.isPolynomialZero(2, 1, 0)); + EXPECT_TRUE(r2.isPolynomialZero(1, 1, 0)); + r2.resizeX(4); + EXPECT_EQ(r2.getMaxXDerivatives(), 4); + r2.resizeS(7); + EXPECT_EQ(r2.getMaxSDerivatives(), 7); + r3.differentiateX(); + EXPECT_NEAR(r3.evaluatePolynomial(0.2, 1, 3, 0, dSvalues), 1.0, 1e-6); + EXPECT_NEAR(r3.evaluatePolynomial(0.2, 1, 1, 2, dSvalues), 0.696, 1e-6); + EXPECT_NEAR(r3.evaluatePolynomial(1, 0.2, 1, 2, dSvalues), 0.696, 1e-6); + r4.differentiateS(); + EXPECT_EQ(r4.numberOfTerms(0, 1), 3); +} + +TEST (PolynomialTest, TanhDeriv) { + EXPECT_NEAR(tanhderiv::integrate(1.0, 2.2, 0.3, 0.3, 2), -0.0148895, 1e-7); + EXPECT_NEAR(tanhderiv::integrate(1.0, 2.2, 0.3, 0.3, 3), -0.0991297, 1e-7); + EXPECT_NEAR(tanhderiv::integrate(1.0, 2.2, 0.3, 0.3, 4), -0.659093, 1e-6); + EXPECT_NEAR(tanhderiv::integrate(1.0, 2.2, 0.3, 0.3, 5), -4.37031, 1e-5); + EXPECT_NEAR(tanhderiv::integrate(1.0, 2.2, 0.3, 0.3, 6), -28.8209, 1e-4); + EXPECT_NEAR(tanhderiv::integrate(1.0, 2.2, 0.3, 0.3, 7), -187.956, 1e-3); + EXPECT_NEAR(tanhderiv::integrate(1.0, 2.2, 0.3, 0.3, 8), -1197.48, 1e-2); + EXPECT_NEAR(tanhderiv::integrate(1.0, 2.2, 0.3, 0.3, 9), -7246.6, 1e-1); + EXPECT_NEAR(tanhderiv::integrate(1.0, 2.2, 0.3, 0.3, 10), -38575, 1e0); +} -- GitLab