diff --git a/examples/performance/apply-boundary/apply-boundary.cxx b/examples/performance/apply-boundary/apply-boundary.cxx new file mode 100644 index 0000000000..cd5431bed6 --- /dev/null +++ b/examples/performance/apply-boundary/apply-boundary.cxx @@ -0,0 +1,132 @@ +#include +#include + +class BoundaryOp_timing : public PhysicsModel { + int init(bool restarting); + int rhs(BoutReal UNUSED(t)) { return 1; } + Field3D f_dirichlet, f_neumann, f_dirichlet_o3; + Field3D f_dirichlet_val, f_neumann_val, f_dirichlet_o3_val; + Field3D f_dirichlet_expr, f_neumann_expr, f_dirichlet_o3_expr; +}; + +int BoundaryOp_timing::init(bool UNUSED(restarting)) { + SOLVE_FOR3(f_dirichlet, f_neumann, f_dirichlet_o3); + SOLVE_FOR3(f_dirichlet_val, f_neumann_val, f_dirichlet_o3_val); + SOLVE_FOR3(f_dirichlet_expr, f_neumann_expr, f_dirichlet_o3_expr); + + Options* opt = Options::getRoot(); + int ntests; + OPTION(opt, ntests, 10000); + + + // test Dirichlet_O3 + + { + Timer timer("dirichlet_o3"); + for (int i=0; i +using BoundaryRegionOp = typename std::conditional::value, BoundaryOpPar, BoundaryOp>::type; + /// Create BoundaryOp objects on demand /*! * This implements a simple string parser, used to match boundary condition @@ -21,6 +24,10 @@ using std::map; * * Modifiers: Simple modifications of boundary conditions can be performed, * for example transforming the coordinate system + * + * Keywords can also be passed, e.g. "dirichlet(3., width=4)" will set a + * Dirichlet boundary condition to a constant value 3, and force 4 guard cells + * to be used (regardless of the values of xstart/ystart/xend/yend). * * This is a singleton, so only one instance can exist. This is * enforced by making the constructor private, and having a getInstance() method @@ -55,7 +62,7 @@ using std::map; * Subsequent calls to create() or createFromOptions() can make use * of the boundary type "myboundary". * - * BoundaryOpBase *bndry = bf->create("myboundary()", new BoundaryRegionXOut("xout", 0, 10, localmesh)); + * auto *bndry = bf->create("myboundary()", new BoundaryRegionXOut("xout", 0, 10, localmesh)); * * where the region is defined in boundary_region.hxx * @@ -69,12 +76,16 @@ class BoundaryFactory { static void cleanup(); ///< Frees all memory /// Create a boundary operation object - BoundaryOpBase* create(const string &name, BoundaryRegionBase *region); - BoundaryOpBase* create(const char* name, BoundaryRegionBase *region); + template + BoundaryRegionOp* create(const string &name, T* region); + template + BoundaryRegionOp* create(const char* name, T* region); /// Create a boundary object using the options file - BoundaryOpBase* createFromOptions(const string &varname, BoundaryRegionBase *region); - BoundaryOpBase* createFromOptions(const char* varname, BoundaryRegionBase *region); + template + BoundaryRegionOp* createFromOptions(const string &varname, T* region); + template + BoundaryRegionOp* createFromOptions(const char* varname, T* region); /*! * Add available boundary conditions and modifiers @@ -119,14 +130,18 @@ class BoundaryFactory { // map par_modmap; // Functions to look up operations and modifiers - BoundaryOp* findBoundaryOp(const string &s); + // Standard or parallel boundary conditions + template + T* findBoundaryOp(const string &s); + // Boundary modifiers BoundaryModifier* findBoundaryMod(const string &s); - // Parallel boundary conditions - BoundaryOpPar* findBoundaryOpPar(const string &s); - // To be implemented... - // BoundaryModifier* findBoundaryMod(const string &s); }; +template<> +BoundaryOp* BoundaryFactory::findBoundaryOp(const string &s); +template<> +BoundaryOpPar* BoundaryFactory::findBoundaryOp(const string &s); + #endif // __BNDRY_FACTORY_H__ diff --git a/include/boundary_op.hxx b/include/boundary_op.hxx index ce232163e1..c6bf80a641 100644 --- a/include/boundary_op.hxx +++ b/include/boundary_op.hxx @@ -8,27 +8,39 @@ class BoundaryModifier; #include "boundary_region.hxx" #include "field2d.hxx" #include "field3d.hxx" +#include "unused.hxx" #include "vector2d.hxx" #include "vector3d.hxx" -#include "unused.hxx" #include -#include #include #include +#include using std::string; using std::list; -class BoundaryOpBase { +/// An operation on a boundary +class BoundaryOp { public: - BoundaryOpBase() {} - virtual ~BoundaryOpBase() {} + BoundaryOp(bool apply_ddt = false) + : bndry(nullptr), apply_to_ddt(apply_ddt), val(0.), gen(nullptr) {} + BoundaryOp(BoundaryRegion *region, bool apply_ddt = false) + : bndry(region), apply_to_ddt(apply_ddt), val(0.), gen(nullptr) {} + BoundaryOp(BoundaryRegion *region, BoutReal val_in, std::shared_ptr g) + : bndry(region), apply_to_ddt(false), val(val_in), gen(std::move(g)) {} + virtual ~BoundaryOp() {} + + // Note: All methods must implement clone, except for modifiers (see below) + virtual BoundaryOp *clone(BoundaryRegion *UNUSED(region), + const list &UNUSED(args), + const std::map &UNUSED(keywords)) { + throw BoutException("BoundaryOp::clone not implemented"); + return nullptr; + } /// Apply a boundary condition on field f - virtual void apply(Field2D &f) = 0; - virtual void apply(Field2D &f,BoutReal UNUSED(t)){return apply(f);}//JMAD - virtual void apply(Field3D &f) = 0; - virtual void apply(Field3D &f,BoutReal UNUSED(t)){return apply(f);}//JMAD + virtual void apply(Field2D &f, BoutReal t = 0.) = 0; + virtual void apply(Field3D &f, BoutReal t = 0.) = 0; virtual void apply(Vector2D &f) { apply(f.x); @@ -41,58 +53,54 @@ public: apply(f.y); apply(f.z); } + + /// Apply a boundary condition on ddt(f) + virtual void apply_ddt(Field2D &f); + virtual void apply_ddt(Field3D &f); + virtual void apply_ddt(Vector2D &f) { apply(ddt(f)); } + virtual void apply_ddt(Vector3D &f) { apply(ddt(f)); } + + BoundaryRegion *bndry; + const bool apply_to_ddt; // True if this boundary condition should be applied on the + // time derivatives, false if it should be applied to the field + // values +protected: + const BoutReal val; // constant value for boundary condition + std::shared_ptr gen; // Generator }; /// An operation on a boundary -class BoundaryOp : public BoundaryOpBase { +template +class BoundaryOpWithApply : public BoundaryOp { public: - BoundaryOp() { - bndry = nullptr; - apply_to_ddt = false; - } - BoundaryOp(BoundaryRegion *region) {bndry = region; apply_to_ddt=false;} - ~BoundaryOp() override {} + using BoundaryOp::BoundaryOp; // Note: All methods must implement clone, except for modifiers (see below) - virtual BoundaryOp* clone(BoundaryRegion *UNUSED(region), const list &UNUSED(args)) { - throw BoutException("BoundaryOp::clone not implemented"); - } - - /// Clone using positional args and keywords - /// If not implemented, check if keywords are passed, then call two-argument version - virtual BoundaryOp *clone(BoundaryRegion *region, const list &args, - const std::map &keywords) { - if (!keywords.empty()) { - // Given keywords, but not using - throw BoutException("Keywords ignored in boundary : %s", keywords.begin()->first.c_str()); - } - - return clone(region, args); + virtual BoundaryOp *clone(BoundaryRegion *UNUSED(region), + const list &UNUSED(args)) { + ASSERT1(false); // this implementation should never get called + return nullptr; } - /// Apply a boundary condition on ddt(f) - virtual void apply_ddt(Field2D &f) { - apply(ddt(f)); - } - virtual void apply_ddt(Field3D &f) { - apply(ddt(f)); - } - virtual void apply_ddt(Vector2D &f) { - apply(ddt(f)); - } - virtual void apply_ddt(Vector3D &f) { - apply(ddt(f)); - } + /// Apply a boundary condition on field f + void apply(Field2D &f, BoutReal t = 0.) override { applyTemplate(f, t); } + void apply(Field3D &f, BoutReal t = 0.) override { applyTemplate(f, t); } - BoundaryRegion *bndry; - bool apply_to_ddt; // True if this boundary condition should be applied on the time derivatives, false if it should be applied to the field values +private: + template void applyTemplate(T &f, BoutReal t); }; class BoundaryModifier : public BoundaryOp { public: - BoundaryModifier() : op(nullptr) {} - BoundaryModifier(BoundaryOp *operation) : BoundaryOp(operation->bndry), op(operation) {} - virtual BoundaryOp* cloneMod(BoundaryOp *op, const list &args) = 0; + BoundaryModifier(bool apply_ddt = false) : BoundaryOp(apply_ddt), op(nullptr) {} + BoundaryModifier(BoundaryOp *operation, bool apply_ddt = false) + : BoundaryOp(operation->bndry, apply_ddt), op(operation) {} + virtual BoundaryOp *cloneMod(BoundaryOp *op, const list &args) = 0; + virtual BoundaryOpPar *cloneMod(BoundaryOpPar *UNUSED(op), + const list &UNUSED(args)) { + throw BoutException("BoundaryModifier should not be called on a BoundaryOpPar."); + } + protected: BoundaryOp *op; }; diff --git a/include/boundary_region.hxx b/include/boundary_region.hxx index 0121fc4c64..13fbacffe5 100644 --- a/include/boundary_region.hxx +++ b/include/boundary_region.hxx @@ -4,6 +4,8 @@ class BoundaryRegion; #ifndef __BNDRY_REGION_H__ #define __BNDRY_REGION_H__ +#include + #include #include using std::string; @@ -20,23 +22,33 @@ enum BndryLoc {BNDRY_XIN=1, BNDRY_PAR_FWD=16, // Don't include parallel boundaries BNDRY_PAR_BKWD=32}; -class BoundaryRegionBase { +/// Describes a region of the boundary, and a means of iterating over it +class BoundaryRegion { public: + BoundaryRegion() = delete; + BoundaryRegion(std::string name, int xd, int yd, BndryLoc loc, int wid, Mesh *passmesh = nullptr) + : bx(xd), by(yd), width(wid), localmesh(passmesh ? passmesh : mesh), + label(std::move(name)), location(loc) { + ASSERT1(!(bx == 0 && by == 0) && !(bx != 0 && by != 0)); + } + virtual ~BoundaryRegion() {} - BoundaryRegionBase() = delete; - BoundaryRegionBase(std::string name, Mesh *passmesh = nullptr) - : localmesh(passmesh ? passmesh : mesh), label(std::move(name)) {} - BoundaryRegionBase(std::string name, BndryLoc loc, Mesh *passmesh = nullptr) - : localmesh(passmesh ? passmesh : mesh), label(std::move(name)), location(loc) {} + virtual BoundaryRegion* copy(int wid=-1) = 0; - virtual ~BoundaryRegionBase() {} + int x,y; ///< Indices of the point in the boundary + const int bx, by; ///< Direction of the boundary [x+dx][y+dy] is going outwards + + const int width; ///< Width of the boundary + + virtual void next1d() = 0; ///< Loop over the innermost elements + virtual void nextX() = 0; ///< Just loop over X + virtual void nextY() = 0; ///< Just loop over Y Mesh* localmesh; ///< Mesh does this boundary region belongs to - string label; ///< Label for this boundary region + const string label; ///< Label for this boundary region BndryLoc location; ///< Which side of the domain is it on? - bool isParallel = false; ///< Is this a parallel boundary? virtual void first() = 0; ///< Move the region iterator to the start virtual void next() = 0; ///< Get the next element in the loop @@ -45,29 +57,11 @@ public: virtual bool isDone() = 0; ///< Returns true if outside domain. Can use this with nested nextX, nextY }; -/// Describes a region of the boundary, and a means of iterating over it -class BoundaryRegion : public BoundaryRegionBase { -public: - BoundaryRegion() = delete; - BoundaryRegion(std::string name, BndryLoc loc, Mesh *passmesh = nullptr) - : BoundaryRegionBase(name, loc, passmesh) {} - BoundaryRegion(std::string name, int xd, int yd, Mesh *passmesh = nullptr) - : BoundaryRegionBase(name, passmesh), bx(xd), by(yd), width(2) {} - ~BoundaryRegion() override {} - - int x,y; ///< Indices of the point in the boundary - int bx, by; ///< Direction of the boundary [x+dx][y+dy] is going outwards - - int width; ///< Width of the boundary - - virtual void next1d() = 0; ///< Loop over the innermost elements - virtual void nextX() = 0; ///< Just loop over X - virtual void nextY() = 0; ///< Just loop over Y -}; - class BoundaryRegionXIn : public BoundaryRegion { public: - BoundaryRegionXIn(std::string name, int ymin, int ymax, Mesh* passmesh = nullptr); + BoundaryRegionXIn(std::string name, int ymin, int ymax, Mesh* passmesh = nullptr, int wid = -1); + + BoundaryRegion* copy(int wid = -1) override; void first() override; void next() override; @@ -82,7 +76,9 @@ private: class BoundaryRegionXOut : public BoundaryRegion { public: - BoundaryRegionXOut(std::string name, int ymin, int ymax, Mesh* passmesh = nullptr); + BoundaryRegionXOut(std::string name, int ymin, int ymax, Mesh* passmesh = nullptr, int wid = -1); + + BoundaryRegion* copy(int wid = -1) override; void first() override; void next() override; @@ -97,7 +93,9 @@ private: class BoundaryRegionYDown : public BoundaryRegion { public: - BoundaryRegionYDown(std::string name, int xmin, int xmax, Mesh* passmesh = nullptr); + BoundaryRegionYDown(std::string name, int xmin, int xmax, Mesh* passmesh = nullptr, int wid = -1); + + BoundaryRegion* copy(int wid = -1) override; void first() override; void next() override; @@ -112,7 +110,9 @@ private: class BoundaryRegionYUp : public BoundaryRegion { public: - BoundaryRegionYUp(std::string name, int xmin, int xmax, Mesh* passmesh = nullptr); + BoundaryRegionYUp(std::string name, int xmin, int xmax, Mesh* passmesh = nullptr, int wid = -1); + + BoundaryRegion* copy(int wid = -1) override; void first() override; void next() override; diff --git a/include/boundary_standard.hxx b/include/boundary_standard.hxx index ca9a05fc11..325b2fd0be 100644 --- a/include/boundary_standard.hxx +++ b/include/boundary_standard.hxx @@ -5,443 +5,534 @@ #include "boundary_op.hxx" #include "bout_types.hxx" -#include #include "unused.hxx" +#include #include -/// Dirichlet boundary condition set half way between guard cell and grid cell at 2nd order accuracy -class BoundaryDirichlet_2ndOrder : public BoundaryOp { - public: - BoundaryDirichlet_2ndOrder() : val(0.) {} - BoundaryDirichlet_2ndOrder(BoutReal setval ): val(setval) {} - BoundaryDirichlet_2ndOrder(BoundaryRegion *region, BoutReal setval=0.):BoundaryOp(region),val(setval) { } - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; - - using BoundaryOp::apply; - void apply(Field2D &f) override; - void apply(Field3D &f) override; - - using BoundaryOp::apply_ddt; - void apply_ddt(Field2D &f) override; - void apply_ddt(Field3D &f) override; - private: - BoutReal val; -}; - /// Dirichlet (set to zero) boundary condition class BoundaryDirichlet : public BoundaryOp { - public: - BoundaryDirichlet() : gen(nullptr) {} - BoundaryDirichlet(BoundaryRegion *region, std::shared_ptr g) - : BoundaryOp(region), gen(std::move(g)) {} - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; - - using BoundaryOp::apply; - void apply(Field2D &f) override; - void apply(Field2D &f,BoutReal t) override; - void apply(Field3D &f) override; - void apply(Field3D &f,BoutReal t) override; - - using BoundaryOp::apply_ddt; - void apply_ddt(Field2D &f) override; - void apply_ddt(Field3D &f) override; - private: - std::shared_ptr gen; // Generator +public: + using BoundaryOp::BoundaryOp; // inherit BoundaryOp constructors + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + void apply(Field2D &f, BoutReal t = 0.) final { applyTemplate(f, t); } + void apply(Field3D &f, BoutReal t = 0.) final { applyTemplate(f, t); } + +private: + template void applyTemplate(T &f, BoutReal t); }; BoutReal default_func(BoutReal t, int x, int y, int z); /// 3nd-order boundary condition -class BoundaryDirichlet_O3 : public BoundaryOp { - public: - BoundaryDirichlet_O3() : gen(nullptr) {} - BoundaryDirichlet_O3(BoundaryRegion *region, std::shared_ptr g) - : BoundaryOp(region), gen(std::move(g)) {} - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; - - using BoundaryOp::apply; - void apply(Field2D &f) override; - void apply(Field2D &f,BoutReal t) override; - void apply(Field3D &f) override; - void apply(Field3D &f,BoutReal t) override; - - using BoundaryOp::apply_ddt; - void apply_ddt(Field2D &f) override; - void apply_ddt(Field3D &f) override; - private: - std::shared_ptr gen; // Generator +class BoundaryDirichlet_O3 : public BoundaryOpWithApply { +public: + using BoundaryOpWithApply< + BoundaryDirichlet_O3>::BoundaryOpWithApply; // inherit BoundaryOp constructors + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + static void applyAtPoint(Field2D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPoint(Field3D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPointStaggered(Field2D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void applyAtPointStaggered(Field3D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void extrapolateFurther(Field2D &f, int x, int bx, int y, int by, int z); + static void extrapolateFurther(Field3D &f, int x, int bx, int y, int by, int z); }; /// 4th-order boundary condition -class BoundaryDirichlet_O4 : public BoundaryOp { - public: - BoundaryDirichlet_O4() : gen(nullptr) {} - BoundaryDirichlet_O4(BoundaryRegion *region, std::shared_ptr g) - : BoundaryOp(region), gen(std::move(g)) {} - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; - - using BoundaryOp::apply; - void apply(Field2D &f) override; - void apply(Field2D &f,BoutReal t) override; - void apply(Field3D &f) override; - void apply(Field3D &f,BoutReal t) override; - - using BoundaryOp::apply_ddt; - void apply_ddt(Field2D &f) override; - void apply_ddt(Field3D &f) override; - private: - std::shared_ptr gen; // Generator +class BoundaryDirichlet_O4 : public BoundaryOpWithApply { +public: + using BoundaryOpWithApply:: + BoundaryOpWithApply; // inherit BoundaryOpWithApply constructors + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + static void applyAtPoint(Field2D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPoint(Field3D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPointStaggered(Field2D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void applyAtPointStaggered(Field3D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void extrapolateFurther(Field2D &f, int x, int bx, int y, int by, int z); + static void extrapolateFurther(Field3D &f, int x, int bx, int y, int by, int z); +}; + +/// Dirichlet boundary condition, tries to smooth out grid-scale oscillations at the +/// boundary +class BoundaryDirichlet_smooth : public BoundaryOpWithApply { +public: + using BoundaryOpWithApply:: + BoundaryOpWithApply; // inherit BoundaryOpWithApply constructors + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + static void applyAtPoint(Field2D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPoint(Field3D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPointStaggered(Field2D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void applyAtPointStaggered(Field3D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void extrapolateFurther(Field2D &f, int x, int bx, int y, int by, int z); + static void extrapolateFurther(Field3D &f, int x, int bx, int y, int by, int z); }; -/// Dirichlet boundary condition set half way between guard cell and grid cell at 4th order accuracy -class BoundaryDirichlet_4thOrder : public BoundaryOp { - public: - BoundaryDirichlet_4thOrder() : val(0.) {} - BoundaryDirichlet_4thOrder(BoutReal setval ): val(setval) {} - BoundaryDirichlet_4thOrder(BoundaryRegion *region, BoutReal setval=0.):BoundaryOp(region),val(setval) { } - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; - - using BoundaryOp::apply; - void apply(Field2D &f) override; - void apply(Field3D &f) override; - - using BoundaryOp::apply_ddt; - void apply_ddt(Field2D &f) override; - void apply_ddt(Field3D &f) override; - private: - BoutReal val; +/// Dirichlet boundary condition set half way between guard cell and grid cell at 2nd +/// order accuracy +class BoundaryDirichlet_2ndOrder + : public BoundaryOpWithApply { +public: + using BoundaryOpWithApply:: + BoundaryOpWithApply; // inherit BoundaryOpWithApply constructors + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + static void applyAtPoint(Field2D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPoint(Field3D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPointStaggered(Field2D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void applyAtPointStaggered(Field3D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void extrapolateFurther(Field2D &f, int x, int bx, int y, int by, int z); + static void extrapolateFurther(Field3D &f, int x, int bx, int y, int by, int z); +}; + +/// Dirichlet boundary condition set half way between guard cell and grid cell at 4th +/// order accuracy +class BoundaryDirichlet_O5 : public BoundaryOpWithApply { +public: + using BoundaryOpWithApply:: + BoundaryOpWithApply; // inherit BoundaryOpWithApply constructors + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + static void applyAtPoint(Field2D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPoint(Field3D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPointStaggered(Field2D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void applyAtPointStaggered(Field3D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void extrapolateFurther(Field2D &f, int x, int bx, int y, int by, int z); + static void extrapolateFurther(Field3D &f, int x, int bx, int y, int by, int z); }; /// Neumann (zero-gradient) boundary condition for non-orthogonal meshes class BoundaryNeumann_NonOrthogonal : public BoundaryOp { - public: - BoundaryNeumann_NonOrthogonal(): val(0.) {} - BoundaryNeumann_NonOrthogonal(BoutReal setval ): val(setval) {} - BoundaryNeumann_NonOrthogonal(BoundaryRegion *region, BoutReal setval=0.):BoundaryOp(region),val(setval) { } - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; - - using BoundaryOp::apply; - void apply(Field2D &f) override; - void apply(Field3D &f) override; - private: - BoutReal val; +public: + BoundaryNeumann_NonOrthogonal() {} + BoundaryNeumann_NonOrthogonal(BoutReal setval) + : BoundaryOp(nullptr, setval, nullptr) {} + BoundaryNeumann_NonOrthogonal(BoundaryRegion *region, BoutReal setval = 0.) + : BoundaryOp(region, setval, nullptr) {} + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + void apply(Field2D &f, BoutReal t = 0.) final { applyTemplate(f, t); } + void apply(Field3D &f, BoutReal t = 0.) final { applyTemplate(f, t); } + +private: + template void applyTemplate(T &f, BoutReal t); }; /// Neumann (zero-gradient) boundary condition, using 2nd order on boundary -class BoundaryNeumann2 : public BoundaryOp { - public: - BoundaryNeumann2() {} - BoundaryNeumann2(BoundaryRegion *region):BoundaryOp(region) { } - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; - - using BoundaryOp::apply; - void apply(Field2D &f) override; - void apply(Field3D &f) override; +class BoundaryNeumann2 : public BoundaryOpWithApply { +public: + using BoundaryOpWithApply< + BoundaryNeumann2>::BoundaryOpWithApply; // inherit BoundaryOpWithApply constructors + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + static void applyAtPoint(Field2D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPoint(Field3D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPointStaggered(Field2D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void applyAtPointStaggered(Field3D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void extrapolateFurther(Field2D &f, int x, int bx, int y, int by, int z); + static void extrapolateFurther(Field3D &f, int x, int bx, int y, int by, int z); }; -/// Neumann boundary condition set half way between guard cell and grid cell at 2nd order accuracy -class BoundaryNeumann_2ndOrder : public BoundaryOp { - public: - BoundaryNeumann_2ndOrder() : val(0.) {} - BoundaryNeumann_2ndOrder(BoutReal setval ): val(setval) {} - BoundaryNeumann_2ndOrder(BoundaryRegion *region, BoutReal setval=0.):BoundaryOp(region),val(setval) { } - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; - - using BoundaryOp::apply; - void apply(Field2D &f) override; - void apply(Field3D &f) override; - - using BoundaryOp::apply_ddt; - void apply_ddt(Field2D &f) override; - void apply_ddt(Field3D &f) override; - private: - BoutReal val; +/// Neumann boundary condition set half way between guard cell and grid cell at 2nd order +/// accuracy +class BoundaryNeumann_2ndOrder + : public BoundaryOpWithApply { +public: + using BoundaryOpWithApply:: + BoundaryOpWithApply; // inherit BoundaryOpWithApply constructors + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + static void applyAtPoint(Field2D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPoint(Field3D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPointStaggered(Field2D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void applyAtPointStaggered(Field3D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void extrapolateFurther(Field2D &f, int x, int bx, int y, int by, int z); + static void extrapolateFurther(Field3D &f, int x, int bx, int y, int by, int z); }; -// Neumann boundary condition set half way between guard cell and grid cell at 2nd order accuracy -class BoundaryNeumann : public BoundaryOp { - public: - BoundaryNeumann() : gen(nullptr) {} - BoundaryNeumann(BoundaryRegion *region, std::shared_ptr g) - : BoundaryOp(region), gen(std::move(g)) {} - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; - - using BoundaryOp::apply; - void apply(Field2D &f) override; - void apply(Field2D &f, BoutReal t) override; - void apply(Field3D &f) override; - void apply(Field3D &f,BoutReal t) override; - - using BoundaryOp::apply_ddt; - void apply_ddt(Field2D &f) override; - void apply_ddt(Field3D &f) override; - private: - std::shared_ptr gen; +// Neumann boundary condition set half way between guard cell and grid cell at 2nd order +// accuracy +class BoundaryNeumann : public BoundaryOpWithApply { +public: + using BoundaryOpWithApply:: + BoundaryOpWithApply; // inherit BoundaryOpWithApply constructors + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + static void applyAtPoint(Field2D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPoint(Field3D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPointStaggered(Field2D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void applyAtPointStaggered(Field3D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void extrapolateFurther(Field2D &f, int x, int bx, int y, int by, int z); + static void extrapolateFurther(Field3D &f, int x, int bx, int y, int by, int z); }; -/// Neumann boundary condition set half way between guard cell and grid cell at 4th order accuracy -class BoundaryNeumann_4thOrder : public BoundaryOp { - public: - BoundaryNeumann_4thOrder() : val(0.) {} - BoundaryNeumann_4thOrder(BoutReal setval ): val(setval) {} - BoundaryNeumann_4thOrder(BoundaryRegion *region, BoutReal setval=0.):BoundaryOp(region),val(setval) { } - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; - - using BoundaryOp::apply; - void apply(Field2D &f) override; - void apply(Field3D &f) override; - - using BoundaryOp::apply_ddt; - void apply_ddt(Field2D &f) override; - void apply_ddt(Field3D &f) override; - private: - BoutReal val; +/// Neumann boundary condition set half way between guard cell and grid cell at 4th order +/// accuracy +class BoundaryNeumann_O4 : public BoundaryOpWithApply { +public: + using BoundaryOpWithApply:: + BoundaryOpWithApply; // inherit BoundaryOpWithApply constructors + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + static void applyAtPoint(Field2D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPoint(Field3D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPointStaggered(Field2D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void applyAtPointStaggered(Field3D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void extrapolateFurther(Field2D &f, int x, int bx, int y, int by, int z); + static void extrapolateFurther(Field3D &f, int x, int bx, int y, int by, int z); }; -/// Neumann boundary condition set half way between guard cell and grid cell at 4th order accuracy -class BoundaryNeumann_O4 : public BoundaryOp { - public: - BoundaryNeumann_O4() : gen(nullptr) {} - BoundaryNeumann_O4(BoundaryRegion *region, std::shared_ptr g) - : BoundaryOp(region), gen(std::move(g)) {} - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; - - using BoundaryOp::apply; - void apply(Field2D &f) override; - void apply(Field2D &f, BoutReal t) override; - void apply(Field3D &f) override; - void apply(Field3D &f,BoutReal t) override; - - using BoundaryOp::apply_ddt; - void apply_ddt(Field2D &f) override; - void apply_ddt(Field3D &f) override; - private: - std::shared_ptr gen; +/// Neumann boundary condition set half way between guard cell and grid cell at 4th order +/// accuracy +class BoundaryNeumann_4thOrder + : public BoundaryOpWithApply { +public: + using BoundaryOpWithApply:: + BoundaryOpWithApply; // inherit BoundaryOpWithApply constructors + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + static void applyAtPoint(Field2D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPoint(Field3D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPointStaggered(Field2D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void applyAtPointStaggered(Field3D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void extrapolateFurther(Field2D &f, int x, int bx, int y, int by, int z); + static void extrapolateFurther(Field3D &f, int x, int bx, int y, int by, int z); }; /// NeumannPar (zero-gradient) boundary condition on /// the variable / sqrt(g_22) class BoundaryNeumannPar : public BoundaryOp { - public: - BoundaryNeumannPar() {} - BoundaryNeumannPar(BoundaryRegion *region):BoundaryOp(region) { } - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; - - using BoundaryOp::apply; - void apply(Field2D &f) override; - void apply(Field3D &f) override; +public: + using BoundaryOp::BoundaryOp; // inherit BoundaryOp constructors + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + void apply(Field2D &f, BoutReal t = 0.) final { applyTemplate(f, t); } + void apply(Field3D &f, BoutReal t = 0.) final { applyTemplate(f, t); } + +private: + template void applyTemplate(T &f, BoutReal t); }; /// Robin (mix of Dirichlet and Neumann) class BoundaryRobin : public BoundaryOp { - public: +public: BoundaryRobin() : aval(0.), bval(0.), gval(0.) {} - BoundaryRobin(BoundaryRegion *region, BoutReal a, BoutReal b, BoutReal g):BoundaryOp(region), aval(a), bval(b), gval(g) { } - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; + BoundaryRobin(BoundaryRegion *region, BoutReal a, BoutReal b, BoutReal g) + : BoundaryOp(region), aval(a), bval(b), gval(g) {} + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + void apply(Field2D &f, BoutReal t = 0.) final { applyTemplate(f, t); } + void apply(Field3D &f, BoutReal t = 0.) final { applyTemplate(f, t); } - using BoundaryOp::apply; - void apply(Field2D &f) override; - void apply(Field3D &f) override; private: - BoutReal aval, bval, gval; + const BoutReal aval, bval, gval; + + template void applyTemplate(T &f, BoutReal t); }; /// Constant gradient (zero second derivative) -class BoundaryConstGradient : public BoundaryOp { - public: - BoundaryConstGradient() {} - BoundaryConstGradient(BoundaryRegion *region):BoundaryOp(region) { } - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; - - using BoundaryOp::apply; - void apply(Field2D &f) override; - void apply(Field3D &f) override; +class BoundaryConstGradient : public BoundaryOpWithApply { +public: + using BoundaryOpWithApply:: + BoundaryOpWithApply; // inherit BoundaryOpWithApply constructors + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + static void applyAtPoint(Field2D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPoint(Field3D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPointStaggered(Field2D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void applyAtPointStaggered(Field3D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void extrapolateFurther(Field2D &f, int x, int bx, int y, int by, int z); + static void extrapolateFurther(Field3D &f, int x, int bx, int y, int by, int z); }; /// Zero Laplacian, decaying solution class BoundaryZeroLaplace : public BoundaryOp { - public: - BoundaryZeroLaplace() {} - BoundaryZeroLaplace(BoundaryRegion *region):BoundaryOp(region) { } - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; - - using BoundaryOp::apply; - void apply(Field2D &f) override; - void apply(Field3D &f) override; +public: + using BoundaryOp::BoundaryOp; + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + void apply(Field2D &f, BoutReal t) final; + void apply(Field3D &f, BoutReal t) final; }; /// Zero Laplacian class BoundaryZeroLaplace2 : public BoundaryOp { - public: - BoundaryZeroLaplace2() {} - BoundaryZeroLaplace2(BoundaryRegion *region):BoundaryOp(region) { } - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; - - using BoundaryOp::apply; - void apply(Field2D &f) override; - void apply(Field3D &f) override; +public: + using BoundaryOp::BoundaryOp; + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + void apply(Field2D &f, BoutReal t) final; + void apply(Field3D &f, BoutReal t) final; }; /// Constant Laplacian, decaying solution class BoundaryConstLaplace : public BoundaryOp { - public: - BoundaryConstLaplace() {} - BoundaryConstLaplace(BoundaryRegion *region):BoundaryOp(region) { } - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; - - using BoundaryOp::apply; - void apply(Field2D &f) override; - void apply(Field3D &f) override; +public: + using BoundaryOp::BoundaryOp; + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + void apply(Field2D &f, BoutReal t) final; + void apply(Field3D &f, BoutReal t) final; }; /// Vector boundary condition Div(B) = 0, Curl(B) = 0 class BoundaryDivCurl : public BoundaryOp { - public: - BoundaryDivCurl() {} - BoundaryDivCurl(BoundaryRegion *region):BoundaryOp(region) { } - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; - - using BoundaryOp::apply; - void apply(Field2D &UNUSED(f)) override { throw BoutException("ERROR: DivCurl boundary only for vectors"); } - void apply(Field3D &UNUSED(f)) override { throw BoutException("ERROR: DivCurl boundary only for vectors"); } - void apply(Vector2D &f) override; - void apply(Vector3D &f) override; +public: + using BoundaryOp::BoundaryOp; + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + void apply(Field2D &UNUSED(f), BoutReal UNUSED(t)) final { + throw BoutException("ERROR: DivCurl boundary only for vectors"); + } + void apply(Field3D &UNUSED(f), BoutReal UNUSED(t)) final { + throw BoutException("ERROR: DivCurl boundary only for vectors"); + } + void apply(Vector2D &f) final; + void apply(Vector3D &f) final; }; -/// Free boundary condition (evolve the field in the guard cells, using non-centred derivatives to calculate the ddt) +/// Free boundary condition (evolve the field in the guard cells, using non-centred +/// derivatives to calculate the ddt) class BoundaryFree : public BoundaryOp { - public: - BoundaryFree() : val(0.) {apply_to_ddt = true;} - BoundaryFree(BoutReal setval): val(setval) {} - BoundaryFree(BoundaryRegion *region, BoutReal setval=0.):BoundaryOp(region),val(setval) { } - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; - - using BoundaryOp::apply; - void apply(Field2D &f) override; - void apply(Field3D &f) override; - - using BoundaryOp::apply_ddt; - void apply_ddt(Field2D &f) override; - void apply_ddt(Field3D &f) override; - private: - BoutReal val; -}; - -// L. Easy -/// Alternative free boundary condition (evolve the field in the guard cells, using non-centred derivatives to calculate the ddt) -class BoundaryFree_O2 : public BoundaryOp { public: - BoundaryFree_O2() {} - BoundaryFree_O2(BoundaryRegion *region):BoundaryOp(region) { } - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; + using BoundaryOp::BoundaryOp; // inherit BoundaryOpWithApply constructors + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; - using BoundaryOp::apply; - void apply(Field2D &f) override; - void apply(Field3D &f) override; + void apply(Field2D &f, BoutReal t) final; + void apply(Field3D &f, BoutReal t) final; - using BoundaryOp::apply_ddt; - void apply_ddt(Field2D &f) override; - void apply_ddt(Field3D &f) override; + void apply_ddt(Field2D &f) final; + void apply_ddt(Field3D &f) final; }; -class BoundaryFree_O3 : public BoundaryOp { +// L. Easy +/// Alternative free boundary condition (evolve the field in the guard cells, using +/// non-centred derivatives to calculate the ddt) +class BoundaryFree_O2 : public BoundaryOpWithApply { public: - BoundaryFree_O3() {} - BoundaryFree_O3(BoundaryRegion *region):BoundaryOp(region) { } - BoundaryOp* clone(BoundaryRegion *region, const list &args) override; - - using BoundaryOp::apply; - void apply(Field2D &f) override; - void apply(Field3D &f) override; - - using BoundaryOp::apply_ddt; - void apply_ddt(Field2D &f) override; - void apply_ddt(Field3D &f) override; + using BoundaryOpWithApply< + BoundaryFree_O2>::BoundaryOpWithApply; // inherit BoundaryOpWithApply constructors + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + static void applyAtPoint(Field2D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPoint(Field3D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPointStaggered(Field2D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void applyAtPointStaggered(Field3D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void extrapolateFurther(Field2D &f, int x, int bx, int y, int by, int z); + static void extrapolateFurther(Field3D &f, int x, int bx, int y, int by, int z); +}; +class BoundaryFree_O3 : public BoundaryOpWithApply { +public: + using BoundaryOpWithApply< + BoundaryFree_O3>::BoundaryOpWithApply; // inherit BoundaryOpWithApply constructors + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + static void applyAtPoint(Field2D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPoint(Field3D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPointStaggered(Field2D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void applyAtPointStaggered(Field3D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void extrapolateFurther(Field2D &f, int x, int bx, int y, int by, int z); + static void extrapolateFurther(Field3D &f, int x, int bx, int y, int by, int z); }; // End L.Easy +class BoundaryFree_O4 : public BoundaryOpWithApply { +public: + using BoundaryOpWithApply< + BoundaryFree_O4>::BoundaryOpWithApply; // inherit BoundaryOpWithApply constructors + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + static void applyAtPoint(Field2D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPoint(Field3D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPointStaggered(Field2D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void applyAtPointStaggered(Field3D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void extrapolateFurther(Field2D &f, int x, int bx, int y, int by, int z); + static void extrapolateFurther(Field3D &f, int x, int bx, int y, int by, int z); +}; + +class BoundaryFree_O5 : public BoundaryOpWithApply { +public: + using BoundaryOpWithApply< + BoundaryFree_O5>::BoundaryOpWithApply; // inherit BoundaryOpWithApply constructors + BoundaryOp *clone(BoundaryRegion *region, const list &args, + const std::map &keywords) override; + + static void applyAtPoint(Field2D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPoint(Field3D &f, BoutReal val, int x, int bx, int y, int by, int z, + BoutReal delta); + static void applyAtPointStaggered(Field2D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void applyAtPointStaggered(Field3D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta); + static void extrapolateFurther(Field2D &f, int x, int bx, int y, int by, int z); + static void extrapolateFurther(Field3D &f, int x, int bx, int y, int by, int z); +}; ///////////////////////////////////////////////////////// /// Convert a boundary condition to a relaxing one class BoundaryRelax : public BoundaryModifier { - public: - BoundaryRelax() : r(10.) {apply_to_ddt = true;} // Set default rate - BoundaryRelax(BoundaryOp *operation, BoutReal rate) : BoundaryModifier(operation) {r = fabs(rate); apply_to_ddt = true;} - BoundaryOp* cloneMod(BoundaryOp *op, const list &args) override; - - using BoundaryModifier::apply; - void apply(Field2D &f) override {apply(f, 0.);}; - void apply(Field2D &f, BoutReal t) override; - void apply(Field3D &f) override {apply(f, 0.);}; - void apply(Field3D &f, BoutReal t) override; - - using BoundaryModifier::apply_ddt; - void apply_ddt(Field2D &f) override; - void apply_ddt(Field3D &f) override; - private: +public: + BoundaryRelax() : BoundaryModifier(true), r(10.) {} // Set default rate + BoundaryRelax(BoundaryOp *operation, BoutReal rate) + : BoundaryModifier(operation, true) { + r = fabs(rate); + } + BoundaryOp *cloneMod(BoundaryOp *op, const list &args) final; + + void apply(Field2D &f, BoutReal t) final; + void apply(Field3D &f, BoutReal t) final; + + void apply_ddt(Field2D &f) final; + void apply_ddt(Field3D &f) final; + +private: BoutReal r; }; /// Increase the width of a boundary class BoundaryWidth : public BoundaryModifier { public: - BoundaryWidth() : width(2) {} - BoundaryWidth(BoundaryOp *operation, int wid) : BoundaryModifier(operation), width(wid) {} - BoundaryOp* cloneMod(BoundaryOp *op, const list &args) override; - - using BoundaryModifier::apply; - void apply(Field2D &f) override {apply(f, 0.);}; - void apply(Field2D &f, BoutReal t) override; - void apply(Field3D &f) override {apply(f, 0.);}; - void apply(Field3D &f, BoutReal t) override; - - using BoundaryModifier::apply_ddt; - void apply_ddt(Field2D &f) override; - void apply_ddt(Field3D &f) override; + BoundaryWidth() : bndry(nullptr) {} + BoundaryWidth(BoundaryOp *operation, int wid); + BoundaryOp *cloneMod(BoundaryOp *op, + const list &args) final; + + void apply(Field2D &f, BoutReal t) final { + op->apply(f, t); + } + void apply(Field3D &f, BoutReal t) final { + op->apply(f, t); + } + + void apply_ddt(Field2D &f) final { + op->apply_ddt(f); + } + void apply_ddt(Field3D &f) final { + op->apply_ddt(f); + } + private: - int width; + std::unique_ptr bndry; }; -/// Convert input field fromFieldAligned, apply boundary and then convert back toFieldAligned +/// Convert input field fromFieldAligned, apply boundary and then convert back +/// toFieldAligned /// Equivalent to converting the boundary condition to "Field Aligned" from "orthogonal" class BoundaryToFieldAligned : public BoundaryModifier { public: - BoundaryToFieldAligned(){} - BoundaryToFieldAligned(BoundaryOp *operation) : BoundaryModifier(operation){} - BoundaryOp* cloneMod(BoundaryOp *op, const list &args) override; - - using BoundaryModifier::apply; - void apply(Field2D &f) override {apply(f, 0.);}; - void apply(Field2D &f, BoutReal t) override; - void apply(Field3D &f) override {apply(f, 0.);}; - void apply(Field3D &f, BoutReal t) override; - - using BoundaryModifier::apply_ddt; - void apply_ddt(Field2D &f) override; - void apply_ddt(Field3D &f) override; + BoundaryToFieldAligned() {} + BoundaryToFieldAligned(BoundaryOp *operation) : BoundaryModifier(operation) {} + BoundaryOp *cloneMod(BoundaryOp *op, const list &args) final; + + void apply(Field2D &f, BoutReal t) final; + void apply(Field3D &f, BoutReal t) final; + + void apply_ddt(Field2D &f) final; + void apply_ddt(Field3D &f) final; + private: }; -/// Convert input field toFieldAligned, apply boundary and then convert back fromFieldAligned +/// Convert input field toFieldAligned, apply boundary and then convert back +/// fromFieldAligned /// Equivalent to converting the boundary condition from "Field Aligned" to "orthogonal" class BoundaryFromFieldAligned : public BoundaryModifier { public: - BoundaryFromFieldAligned(){} - BoundaryFromFieldAligned(BoundaryOp *operation) : BoundaryModifier(operation){} - BoundaryOp* cloneMod(BoundaryOp *op, const list &args) override; - - using BoundaryModifier::apply; - void apply(Field2D &f) override {apply(f, 0.);}; - void apply(Field2D &f, BoutReal t) override; - void apply(Field3D &f) override {apply(f, 0.);}; - void apply(Field3D &f, BoutReal t) override; - - using BoundaryModifier::apply_ddt; - void apply_ddt(Field2D &f) override; - void apply_ddt(Field3D &f) override; + BoundaryFromFieldAligned() {} + BoundaryFromFieldAligned(BoundaryOp *operation) : BoundaryModifier(operation) {} + BoundaryOp *cloneMod(BoundaryOp *op, const list &args) final; + + void apply(Field2D &f, BoutReal t) final; + void apply(Field3D &f, BoutReal t) final; + + void apply_ddt(Field2D &f) final; + void apply_ddt(Field3D &f) final; + private: }; diff --git a/include/bout/mesh.hxx b/include/bout/mesh.hxx index 056294bbe0..9a0978a4a0 100644 --- a/include/bout/mesh.hxx +++ b/include/bout/mesh.hxx @@ -387,24 +387,30 @@ class Mesh { // Boundary regions /// Return a vector containing all the boundary regions on this processor - virtual vector getBoundaries() = 0; + virtual vector< std::unique_ptr >& getBoundaries() = 0; /// Add a boundary region to this processor - virtual void addBoundary(BoundaryRegion* UNUSED(bndry)) {} + virtual void addBoundary(BoundaryRegion* UNUSED(bndry)) { + throw BoutException("Mesh::addBoundary() is not implemented"); + } /// Get all the parallel (Y) boundaries on this processor - virtual vector getBoundariesPar() = 0; + virtual vector< std::unique_ptr >& getBoundariesPar() = 0; /// Add a parallel(Y) boundary to this processor - virtual void addBoundaryPar(BoundaryRegionPar* UNUSED(bndry)) {} + virtual void addBoundaryPar(BoundaryRegionPar* UNUSED(bndry)) { + throw BoutException("Mesh::addBoundaryRegionPar() is not implemented"); + } /// Branch-cut special handling (experimental) virtual const Field3D smoothSeparatrix(const Field3D &f) {return f;} virtual BoutReal GlobalX(int jx) const = 0; ///< Continuous X index between 0 and 1 virtual BoutReal GlobalY(int jy) const = 0; ///< Continuous Y index (0 -> 1) + virtual BoutReal GlobalZ(int jz) const = 0; ///< Continuous Z index (0 -> 1) virtual BoutReal GlobalX(BoutReal jx) const = 0; ///< Continuous X index between 0 and 1 virtual BoutReal GlobalY(BoutReal jy) const = 0; ///< Continuous Y index (0 -> 1) + virtual BoutReal GlobalZ(BoutReal jz) const = 0; ///< Continuous Z index (0 -> 1) ////////////////////////////////////////////////////////// diff --git a/include/field_data.hxx b/include/field_data.hxx index c4fadd4e95..ac03758bae 100644 --- a/include/field_data.hxx +++ b/include/field_data.hxx @@ -62,12 +62,23 @@ class FieldVisitor; */ class FieldData { public: - FieldData(); - virtual ~FieldData(); + FieldData(Mesh *datamesh = nullptr) + : fielddatamesh(datamesh != nullptr ? datamesh : mesh), + boundaryIsSet(true) {} + + virtual ~FieldData() {}; // Visitor pattern support virtual void accept(FieldVisitor &v) = 0; - + + Mesh *getDataMesh() const { + if (fielddatamesh != nullptr) { + return fielddatamesh; + } else { + return mesh; + } + } + // Defines interface which must be implemented virtual bool isReal() const = 0; ///< Returns true if field consists of BoutReal values virtual bool is3D() const = 0; ///< True if variable is 3D @@ -92,11 +103,15 @@ public: FieldGeneratorPtr getBndryGenerator(BndryLoc location); protected: - vector bndry_op; ///< Boundary conditions - bool boundaryIsCopy; ///< True if bndry_op is a copy + Mesh* fielddatamesh; + + // use shared_ptr for bndry_op because if boundary conditions are copied from + // another field we want to share the pointers to the BoundaryOp objects in + // that field's bndry_op + vector< std::shared_ptr > bndry_op; ///< Boundary conditions bool boundaryIsSet; ///< Set to true when setBoundary called // Parallel boundaries - vector bndry_op_par; ///< Boundary conditions + vector< std::shared_ptr > bndry_op_par; ///< Boundary conditions std::map bndry_generator; }; diff --git a/include/parallel_boundary_op.hxx b/include/parallel_boundary_op.hxx index ba4729ea71..4abfc5286b 100644 --- a/include/parallel_boundary_op.hxx +++ b/include/parallel_boundary_op.hxx @@ -12,7 +12,7 @@ ////////////////////////////////////////////////// // Base class -class BoundaryOpPar : public BoundaryOpBase { +class BoundaryOpPar { public: BoundaryOpPar() : bndry(nullptr), real_value(0.), value_type(REAL) {} BoundaryOpPar(BoundaryRegionPar *region, std::shared_ptr value) @@ -25,23 +25,37 @@ public: bndry(region), real_value(value), value_type(REAL) {} - ~BoundaryOpPar() override {} + virtual ~BoundaryOpPar() {} // Note: All methods must implement clone, except for modifiers (see below) virtual BoundaryOpPar* clone(BoundaryRegionPar *UNUSED(region), const list &UNUSED(args)) {return nullptr; } virtual BoundaryOpPar* clone(BoundaryRegionPar *UNUSED(region), Field3D *UNUSED(f)) {return nullptr; } - virtual BoundaryOpPar* clone(BoundaryRegionPar *region, const list &args, const std::map& UNUSED(keywords)) { - // If not implemented, call two-argument version + virtual BoundaryOpPar* clone(BoundaryRegionPar *region, const list &args, const std::map& keywords) { + // If not implemented, check if keywords are passed, then call two-argument version + if (!keywords.empty()) { + // Given keywords, but not using + throw BoutException("Keywords ignored in parallel boundary : %s", keywords.begin()->first.c_str()); + } return clone(region, args); } - - using BoundaryOpBase::apply; - void apply(Field2D &UNUSED(f)) override { + + /// Apply a boundary condition on field f + virtual void apply(Field3D &f,BoutReal t = 0.) = 0; + void apply(Field2D &UNUSED(f), BoutReal UNUSED(t) = 0.) { throw BoutException("Can't apply parallel boundary conditions to Field2D!"); } - void apply(Field2D &UNUSED(f), BoutReal UNUSED(t)) override { - throw BoutException("Can't apply parallel boundary conditions to Field2D!"); + + virtual void apply(Vector2D &f) { + apply(f.x); + apply(f.y); + apply(f.z); + } + + virtual void apply(Vector3D &f) { + apply(f.x); + apply(f.y); + apply(f.z); } BoundaryRegionPar *bndry; @@ -80,7 +94,6 @@ public: BoundaryOpPar* clone(BoundaryRegionPar *region, Field3D *f) override; using BoundaryOpPar::apply; - void apply(Field3D &f) override {return apply(f, 0);} void apply(Field3D &f, BoutReal t) override; }; @@ -100,7 +113,6 @@ public: BoundaryOpPar* clone(BoundaryRegionPar *region, Field3D *f) override; using BoundaryOpPar::apply; - void apply(Field3D &f) override {return apply(f, 0);} void apply(Field3D &f, BoutReal t) override; }; @@ -120,7 +132,6 @@ public: BoundaryOpPar* clone(BoundaryRegionPar *region, Field3D *f) override; using BoundaryOpPar::apply; - void apply(Field3D &f) override {return apply(f, 0);} void apply(Field3D &f, BoutReal t) override; }; @@ -140,7 +151,6 @@ public: BoundaryOpPar* clone(BoundaryRegionPar *region, Field3D *f) override; using BoundaryOpPar::apply; - void apply(Field3D &f) override {return apply(f, 0);} void apply(Field3D &f, BoutReal t) override; }; diff --git a/include/parallel_boundary_region.hxx b/include/parallel_boundary_region.hxx index 2025ad9abb..827e563735 100644 --- a/include/parallel_boundary_region.hxx +++ b/include/parallel_boundary_region.hxx @@ -10,7 +10,7 @@ * inside the boundary. * */ -class BoundaryRegionPar : public BoundaryRegionBase { +class BoundaryRegionPar { struct IndexPoint { int jx; @@ -45,20 +45,26 @@ class BoundaryRegionPar : public BoundaryRegionBase { public: BoundaryRegionPar(const string &name, int dir, Mesh* passmesh) : - BoundaryRegionBase(name, passmesh), dir(dir) { - BoundaryRegionBase::isParallel = true;} + localmesh(passmesh ? passmesh : mesh), label(std::move(name)), dir(dir) {} BoundaryRegionPar(const string &name, BndryLoc loc,int dir, Mesh* passmesh) : - BoundaryRegionBase(name, loc, passmesh), dir(dir) { - BoundaryRegionBase::isParallel = true;} + localmesh(passmesh ? passmesh : mesh), label(std::move(name)), location(loc), dir(dir) {} /// Add a point to the boundary void add_point(int jx,int jy,int jz, const BoutReal x,BoutReal y,BoutReal z, const BoutReal length,BoutReal angle); - void first() override; - void next() override; - bool isDone() override; + void first(); ///< Move the region iterator to the start + void next(); ///< Get the next element in the loop + /// over every element from inside out (in + /// X or Y first) + bool isDone(); ///< Returns true if outside domain. Can use this with nested nextX, nextY + + Mesh* localmesh; ///< Mesh does this boundary region belongs to + + string label; ///< Label for this boundary region + + BndryLoc location; ///< Which side of the domain is it on? /// Index of the point in the boundary int x, y, z; diff --git a/manual/sphinx/user_docs/boundary_options.rst b/manual/sphinx/user_docs/boundary_options.rst index e9f4933d07..bb61cfa5ff 100644 --- a/manual/sphinx/user_docs/boundary_options.rst +++ b/manual/sphinx/user_docs/boundary_options.rst @@ -37,7 +37,9 @@ brackets. Currently implemented boundary conditions are: - ``dirichlet()`` - Set to some number e.g. ``dirichlet(1)`` sets the boundary to :math:`1.0` -- ``neumann`` - Zero gradient +- ``neumann()`` - Set gradient to some number (default zero). Gradient + is in internal coordinates, i.e. using dx/dy for grid spacing with no metric + terms. - ``robin`` - A combination of zero-gradient and zero-value :math:`a f + b{{\frac{\partial f}{\partial x}}} = g` where the @@ -54,6 +56,11 @@ brackets. Currently implemented boundary conditions are: - ``constlaplace`` - Laplacian = const, decaying solution (X boundaries only) +Keyword arguments can also be given. Currently only ``width`` is implemented, +which reproduces the functionality of the ``width`` boundary modifier described +below. For example, ``dirichlet(3., width=4)`` is equivalent to +``width(dirichlet(3.), 4)``. + The zero- or constant-Laplacian boundary conditions works as follows: .. math:: @@ -75,9 +82,10 @@ which has the solution Assuming that the solution should decay away from the domain, on the inner :math:`x` boundary :math:`B = 0`, and on the outer boundary -:math:`A = 0`. Boundary modifiers change the behaviour of boundary -conditions, and more than one modifier can be used. Currently the -following are available: +:math:`A = 0`. + +Boundary modifiers change the behaviour of boundary conditions, and more than +one modifier can be used. Currently the following are available: - ``relax`` - Relaxing boundaries. Evolve the variable towards the given boundary condition at a given rate @@ -137,10 +145,11 @@ the core boundary. Changing the width of boundaries -------------------------------- -To change the width of a boundary region, the ``width`` modifier changes -the width of a boundary region before applying the boundary condition, -then changes the width back afterwards. To use, specify the boundary -condition and the width, for example +To change the width of a boundary region, the ``width`` modifier creates a copy +of the BoundaryRegion object with a different ``width`` parameter. This copy is +stored in the BoundaryWidth object, and a pointer to the BoundaryRegion passed +to the BoundaryOp. To use, specify the boundary condition and the width, for +example :: @@ -165,9 +174,6 @@ width. Limitations: -#. Because it modifies then restores a globally-used BoundaryRegion, - this code is not thread safe. - #. Boundary conditions can’t be applied across processors, and no checks are done that the width asked for fits within a single processor. diff --git a/src/field/field2d.cxx b/src/field/field2d.cxx index a675039078..d2531e6164 100644 --- a/src/field/field2d.cxx +++ b/src/field/field2d.cxx @@ -45,7 +45,8 @@ #include -Field2D::Field2D(Mesh *localmesh) : Field(localmesh), deriv(nullptr) { +Field2D::Field2D(Mesh *localmesh) : + Field(localmesh), FieldData(localmesh), deriv(nullptr) { boundaryIsSet = false; @@ -66,10 +67,13 @@ Field2D::Field2D(Mesh *localmesh) : Field(localmesh), deriv(nullptr) { } Field2D::Field2D(const Field2D& f) : Field(f.fieldmesh), // The mesh containing array sizes + FieldData(f.fielddatamesh), data(f.data), // This handles references to the data array deriv(nullptr) { TRACE("Field2D(Field2D&)"); + ASSERT1(fieldmesh == fielddatamesh); // Check consistency between Field::fieldmesh and FieldData::fielddatamesh + #ifdef TRACK name = f.name; #endif @@ -95,7 +99,9 @@ Field2D::Field2D(const Field2D& f) : Field(f.fieldmesh), // The mesh containing boundaryIsSet = false; } -Field2D::Field2D(BoutReal val, Mesh *localmesh) : Field(localmesh), deriv(nullptr) { +Field2D::Field2D(BoutReal val, Mesh *localmesh) : + Field(localmesh), FieldData(localmesh), deriv(nullptr) { + boundaryIsSet = false; nx = fieldmesh->LocalNx; @@ -114,6 +120,7 @@ void Field2D::allocate() { if(!fieldmesh) { /// If no mesh, use the global fieldmesh = mesh; + fielddatamesh = mesh; nx = fieldmesh->LocalNx; ny = fieldmesh->LocalNy; } @@ -198,7 +205,9 @@ Field2D &Field2D::operator=(const Field2D &rhs) { #endif // Copy the data and data sizes - fieldmesh = rhs.fieldmesh; + fieldmesh = rhs.getMesh(); + fielddatamesh = rhs.getDataMesh(); + ASSERT1(fieldmesh == fielddatamesh); // Check consistency between Field::fieldmesh and FieldData::fielddatamesh nx = rhs.nx; ny = rhs.ny; @@ -262,7 +271,7 @@ void Field2D::applyBoundary(const string &condition) { /// Loop over the mesh boundary regions for(const auto& reg : fieldmesh->getBoundaries()) { - BoundaryOp* op = static_cast(bfact->create(condition, reg)); + BoundaryOp* op = static_cast(bfact->create(condition, reg.get())); op->apply(*this); delete op; } @@ -298,7 +307,7 @@ void Field2D::applyBoundary(const string ®ion, const string &condition) { for (const auto ® : fieldmesh->getBoundaries()) { if (reg->label.compare(region) == 0) { region_found = true; - BoundaryOp *op = static_cast(bfact->create(condition, reg)); + BoundaryOp *op = static_cast(bfact->create(condition, reg.get())); op->apply(*this); delete op; break; @@ -340,7 +349,7 @@ void Field2D::applyTDerivBoundary() { } void Field2D::setBoundaryTo(const Field2D &f2d) { - TRACE("Field2D::setBoundary(const Field2D&)"); + TRACE("Field2D::setBoundaryTo(const Field2D&)"); checkData(f2d); diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index 7d9dda7cea..72cccf05f6 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -45,8 +45,8 @@ /// Constructor Field3D::Field3D(Mesh *localmesh) - : Field(localmesh), background(nullptr), deriv(nullptr), yup_field(nullptr), - ydown_field(nullptr) { + : Field(localmesh), FieldData(localmesh), background(nullptr), + deriv(nullptr), yup_field(nullptr), ydown_field(nullptr) { #ifdef TRACK name = ""; #endif @@ -71,11 +71,14 @@ Field3D::Field3D(Mesh *localmesh) /// later) Field3D::Field3D(const Field3D &f) : Field(f.fieldmesh), // The mesh containing array sizes + FieldData(f.fielddatamesh), background(nullptr), data(f.data), // This handles references to the data array deriv(nullptr), yup_field(nullptr), ydown_field(nullptr) { TRACE("Field3D(Field3D&)"); + ASSERT1(fieldmesh == fielddatamesh); // Check consistency between Field::fieldmesh and FieldData::fielddatamesh + #if CHECK > 2 checkData(f); #endif @@ -100,11 +103,13 @@ Field3D::Field3D(const Field3D &f) } Field3D::Field3D(const Field2D &f) - : Field(f.getMesh()), background(nullptr), deriv(nullptr), yup_field(nullptr), - ydown_field(nullptr) { + : Field(f.getMesh()), FieldData(f.getDataMesh()), background(nullptr), + deriv(nullptr), yup_field(nullptr), ydown_field(nullptr) { TRACE("Field3D: Copy constructor from Field2D"); + ASSERT1(fieldmesh == fielddatamesh); // Check consistency between Field::fieldmesh and FieldData::fielddatamesh + boundaryIsSet = false; nx = fieldmesh->LocalNx; @@ -118,8 +123,8 @@ Field3D::Field3D(const Field2D &f) } Field3D::Field3D(const BoutReal val, Mesh *localmesh) - : Field(localmesh), background(nullptr), deriv(nullptr), yup_field(nullptr), - ydown_field(nullptr) { + : Field(localmesh), FieldData(localmesh), background(nullptr), + deriv(nullptr), yup_field(nullptr), ydown_field(nullptr) { TRACE("Field3D: Copy constructor from value"); @@ -159,6 +164,7 @@ void Field3D::allocate() { if(!fieldmesh) { /// If no mesh, use the global fieldmesh = mesh; + fielddatamesh = mesh; nx = fieldmesh->LocalNx; ny = fieldmesh->LocalNy; nz = fieldmesh->LocalNz; @@ -300,7 +306,9 @@ Field3D & Field3D::operator=(const Field3D &rhs) { checkData(rhs); // Copy the data and data sizes - fieldmesh = rhs.fieldmesh; + fieldmesh = rhs.getMesh(); + fielddatamesh = rhs.getDataMesh(); + ASSERT1(fieldmesh == fielddatamesh); // Check consistency between Field::fieldmesh and FieldData::fielddatamesh nx = rhs.nx; ny = rhs.ny; nz = rhs.nz; data = rhs.data; @@ -312,13 +320,17 @@ Field3D & Field3D::operator=(const Field3D &rhs) { Field3D & Field3D::operator=(const Field2D &rhs) { TRACE("Field3D = Field2D"); - + /// Check that the data is valid checkData(rhs); - + /// Make sure there's a unique array to copy data into allocate(); + ASSERT1(fieldmesh == rhs.getMesh()); + ASSERT1(fielddatamesh == rhs.getDataMesh()); + ASSERT1(fieldmesh == fielddatamesh); // Check consistency between Field::fieldmesh and FieldData::fielddatamesh + /// Copy data const Region ®ion_all = fieldmesh->getRegion3D("RGN_ALL"); @@ -442,7 +454,7 @@ void Field3D::applyBoundary(const string &condition) { /// Loop over the mesh boundary regions for(const auto& reg : fieldmesh->getBoundaries()) { - BoundaryOp* op = static_cast(bfact->create(condition, reg)); + BoundaryOp* op = bfact->create(condition, reg.get()); op->apply(*this); delete op; } @@ -462,7 +474,7 @@ void Field3D::applyBoundary(const string ®ion, const string &condition) { for (const auto ® : fieldmesh->getBoundaries()) { if (reg->label.compare(region) == 0) { region_found = true; - BoundaryOp *op = static_cast(bfact->create(condition, reg)); + BoundaryOp *op = bfact->create(condition, reg.get()); op->apply(*this); delete op; break; @@ -494,7 +506,7 @@ void Field3D::applyTDerivBoundary() { } void Field3D::setBoundaryTo(const Field3D &f3d) { - TRACE("Field3D::setBoundary(const Field3D&)"); + TRACE("Field3D::setBoundaryTo(const Field3D&)"); checkData(f3d); @@ -569,7 +581,7 @@ void Field3D::applyParallelBoundary(const string &condition) { /// Loop over the mesh boundary regions for(const auto& reg : fieldmesh->getBoundariesPar()) { - BoundaryOpPar* op = static_cast(bfact->create(condition, reg)); + BoundaryOpPar* op = bfact->create(condition, reg.get()); op->apply(*this); delete op; } @@ -594,7 +606,7 @@ void Field3D::applyParallelBoundary(const string ®ion, const string &conditio /// Loop over the mesh boundary regions for(const auto& reg : fieldmesh->getBoundariesPar()) { if(reg->label.compare(region) == 0) { - BoundaryOpPar* op = static_cast(bfact->create(condition, reg)); + BoundaryOpPar* op = bfact->create(condition, reg.get()); op->apply(*this); delete op; break; @@ -623,9 +635,9 @@ void Field3D::applyParallelBoundary(const string ®ion, const string &conditio if(reg->label.compare(region) == 0) { // BoundaryFactory can't create boundaries using Field3Ds, so get temporary // boundary of the right type - BoundaryOpPar* tmp = static_cast(bfact->create(condition, reg)); + BoundaryOpPar* tmp = bfact->create(condition, reg.get()); // then clone that with the actual argument - BoundaryOpPar* op = tmp->clone(reg, f); + BoundaryOpPar* op = tmp->clone(reg.get(), f); op->apply(*this); delete tmp; delete op; diff --git a/src/field/field_data.cxx b/src/field/field_data.cxx index 6ab19709c7..49a20e543b 100644 --- a/src/field/field_data.cxx +++ b/src/field/field_data.cxx @@ -6,65 +6,72 @@ #include #include "unused.hxx" -FieldData::FieldData() : boundaryIsCopy(false), boundaryIsSet(true) { - -} - -FieldData::~FieldData() { - if(!boundaryIsCopy) { - // Delete the boundary operations - for(const auto& bndry : bndry_op) - delete bndry; - } -} - void FieldData::setBoundary(const string &name) { /// Get the boundary factory (singleton) BoundaryFactory *bfact = BoundaryFactory::getInstance(); output_info << "Setting boundary for variable " << name << endl; + + /// Get rid of existing boundary ops + bndry_op.clear(); + /// Loop over the mesh boundary regions - for(const auto& reg : mesh->getBoundaries()) { - BoundaryOp* op = static_cast(bfact->createFromOptions(name, reg)); + for(const auto& reg : getDataMesh()->getBoundaries()) { + BoundaryOp* op = bfact->createFromOptions(name, reg.get()); if (op != nullptr) - bndry_op.push_back(op); + bndry_op.push_back(std::shared_ptr(op)); output_info << endl; } - /// Get the mesh boundary regions - vector par_reg = mesh->getBoundariesPar(); + /// Get rid of existing parallel boundary ops + bndry_op_par.clear(); + /// Loop over the mesh parallel boundary regions - for(const auto& reg : mesh->getBoundariesPar()) { - BoundaryOpPar* op = static_cast(bfact->createFromOptions(name, reg)); - if (op != nullptr) - bndry_op_par.push_back(op); + for(const auto& reg : getDataMesh()->getBoundariesPar()) { + BoundaryOpPar* op = bfact->createFromOptions(name, reg.get()); + if (op != nullptr) { + bndry_op_par.push_back(std::shared_ptr(op)); + } output_info << endl; } boundaryIsSet = true; - boundaryIsCopy = false; } -void FieldData::setBoundary(const string &UNUSED(region), BoundaryOp *op) { - /// Get the mesh boundary regions - vector reg = mesh->getBoundaries(); - - /// Find the region +void FieldData::setBoundary(const string ®ion, BoundaryOp *op) { + output_info << "Setting " << region << " boundary for some variable" << endl; + + /// Find the region + BoundaryRegion* region_ptr = nullptr; + for (const auto& bndry : getDataMesh()->getBoundaries()) { + if (bndry->label == region) { + region_ptr = bndry.get(); + } + } /// Find if we're replacing an existing boundary - for(const auto& bndry : bndry_op) { - if( bndry->bndry == op->bndry ) { + for(auto it = bndry_op.begin(); it != bndry_op.end(); it++) { + if( (*it)->bndry == region_ptr ) { // Replacing this boundary - output << "Replacing "; + + output << "Replacing " << region_ptr->label<clone(region_ptr, {}, {}); + + bndry_op.push_back(std::shared_ptr(new_op)); } void FieldData::copyBoundary(const FieldData &f) { bndry_op = f.bndry_op; bndry_op_par = f.bndry_op_par; - boundaryIsCopy = true; boundaryIsSet = true; } @@ -76,7 +83,7 @@ void FieldData::addBndryFunction(FuncPtr userfunc, BndryLoc location){ void FieldData::addBndryGenerator(FieldGeneratorPtr gen, BndryLoc location) { if(location == BNDRY_ALL){ - for(const auto& reg : mesh->getBoundaries()) { + for(const auto& reg : getDataMesh()->getBoundaries()) { bndry_generator[reg->location] = gen; } } else { diff --git a/src/field/vector2d.cxx b/src/field/vector2d.cxx index 541715b15b..cdaf62dbd1 100644 --- a/src/field/vector2d.cxx +++ b/src/field/vector2d.cxx @@ -36,11 +36,12 @@ #include Vector2D::Vector2D(Mesh *localmesh) - : x(localmesh), y(localmesh), z(localmesh), covariant(true), deriv(nullptr), location(CELL_CENTRE) {} + : FieldData(localmesh), x(localmesh), y(localmesh), z(localmesh), + covariant(true), deriv(nullptr), location(CELL_CENTRE) {} Vector2D::Vector2D(const Vector2D &f) - : x(f.x), y(f.y), z(f.z), covariant(f.covariant), deriv(nullptr), - location(f.getLocation()) {} + : FieldData(f.fielddatamesh), x(f.x), y(f.y), z(f.z), covariant(f.covariant), + deriv(nullptr), location(f.getLocation()) {} Vector2D::~Vector2D() { if (deriv != nullptr) { @@ -148,6 +149,8 @@ Vector2D* Vector2D::timeDeriv() { /////////////////// ASSIGNMENT //////////////////// Vector2D & Vector2D::operator=(const Vector2D &rhs) { + fielddatamesh = rhs.fielddatamesh; + x = rhs.x; y = rhs.y; z = rhs.z; diff --git a/src/field/vector3d.cxx b/src/field/vector3d.cxx index 000e5e0fec..e7d254f205 100644 --- a/src/field/vector3d.cxx +++ b/src/field/vector3d.cxx @@ -37,11 +37,12 @@ #include Vector3D::Vector3D(Mesh *localmesh) - : x(localmesh), y(localmesh), z(localmesh), covariant(true), deriv(nullptr), location(CELL_CENTRE) {} + : FieldData(localmesh), x(localmesh), y(localmesh), z(localmesh), + covariant(true), deriv(nullptr), location(CELL_CENTRE) {} Vector3D::Vector3D(const Vector3D &f) - : x(f.x), y(f.y), z(f.z), covariant(f.covariant), deriv(nullptr), - location(f.getLocation()) {} + : FieldData(f.fielddatamesh), x(f.x), y(f.y), z(f.z), covariant(f.covariant), + deriv(nullptr), location(f.getLocation()) {} Vector3D::~Vector3D() { if (deriv != nullptr) { @@ -149,6 +150,8 @@ Vector3D* Vector3D::timeDeriv() { /////////////////// ASSIGNMENT //////////////////// Vector3D & Vector3D::operator=(const Vector3D &rhs) { + fielddatamesh = rhs.fielddatamesh; + x = rhs.x; y = rhs.y; z = rhs.z; @@ -160,6 +163,8 @@ Vector3D & Vector3D::operator=(const Vector3D &rhs) { } Vector3D & Vector3D::operator=(const Vector2D &rhs) { + fielddatamesh = rhs.x.getDataMesh(); + x = rhs.x; y = rhs.y; z = rhs.z; diff --git a/src/mesh/boundary_factory.cxx b/src/mesh/boundary_factory.cxx index 4824561320..6478ca4a0f 100644 --- a/src/mesh/boundary_factory.cxx +++ b/src/mesh/boundary_factory.cxx @@ -19,12 +19,14 @@ BoundaryFactory::BoundaryFactory() { add(new BoundaryDirichlet_2ndOrder(), "dirichlet_2ndorder"); // Deprecated add(new BoundaryDirichlet_O3(), "dirichlet_o3"); add(new BoundaryDirichlet_O4(), "dirichlet_o4"); - add(new BoundaryDirichlet_4thOrder(), "dirichlet_4thorder"); + add(new BoundaryDirichlet_O5(), "dirichlet_o5"); + add(new BoundaryDirichlet_O5(), "dirichlet_4thorder"); // Synonym for "dirichlet_o5" + add(new BoundaryDirichlet_smooth(), "dirichlet_smooth"); add(new BoundaryNeumann(), "neumann"); add(new BoundaryNeumann(), "neumann_O2"); // Synonym for "neumann" add(new BoundaryNeumann2(), "neumann2"); // Deprecated add(new BoundaryNeumann_2ndOrder(), "neumann_2ndorder"); // Deprecated - add(new BoundaryNeumann_4thOrder(), "neumann_4thorder"); + add(new BoundaryNeumann_4thOrder(), "neumann_4thorder"); // Deprecated: Less good version of neumann_O4 add(new BoundaryNeumann_O4(), "neumann_O4"); add(new BoundaryNeumannPar(), "neumannpar"); add(new BoundaryNeumann_NonOrthogonal(), "neumann_nonorthogonal"); @@ -36,6 +38,8 @@ BoundaryFactory::BoundaryFactory() { add(new BoundaryFree(), "free"); add(new BoundaryFree_O2(), "free_o2"); add(new BoundaryFree_O3(), "free_o3"); + add(new BoundaryFree_O4(), "free_o4"); + add(new BoundaryFree_O5(), "free_o5"); addMod(new BoundaryRelax(), "relax"); addMod(new BoundaryWidth(), "width"); @@ -79,7 +83,8 @@ void BoundaryFactory::cleanup() { instance = nullptr; } -BoundaryOpBase* BoundaryFactory::create(const string &name, BoundaryRegionBase *region) { +template +BoundaryRegionOp* BoundaryFactory::create(const string &name, T* region) { // Search for a string of the form: modifier(operation) auto pos = name.find('('); @@ -90,28 +95,17 @@ BoundaryOpBase* BoundaryFactory::create(const string &name, BoundaryRegionBase * if( (name == "null") || (name == "none") ) return nullptr; - if(region->isParallel) { - // Parallel boundary - BoundaryOpPar *pop = findBoundaryOpPar(trim(name)); - if (pop == nullptr) - throw BoutException("Could not find parallel boundary condition '%s'", name.c_str()); - - // Clone the boundary operation, passing the region to operate over, - // an empty args list and empty keyword map - list args; - return pop->clone(static_cast(region), args, {}); - } else { - // Perpendicular boundary - BoundaryOp *op = findBoundaryOp(trim(name)); - if (op == nullptr) - throw BoutException("Could not find boundary condition '%s'", name.c_str()); - - // Clone the boundary operation, passing the region to operate over, - // an empty args list and empty keyword map - list args; - return op->clone(static_cast(region), args, {}); + BoundaryRegionOp *op = findBoundaryOp< BoundaryRegionOp >(trim(name)); + if (op == nullptr) { + throw BoutException("Could not find boundary condition '%s'", name.c_str()); } + + // Clone the boundary operation, passing the region to operate over, + // an empty args list and empty keyword map + list args; + return op->clone(region, args, {}); } + // Contains a bracket. Find the last bracket and remove auto pos2 = name.rfind(')'); if(pos2 == string::npos) { @@ -168,34 +162,34 @@ BoundaryOpBase* BoundaryFactory::create(const string &name, BoundaryRegionBase * arglist.push_back(trim(s)); } - // Test if func is a modifier - BoundaryModifier *mod = findBoundaryMod(func); - if (mod != nullptr) { - // The first argument should be an operation - BoundaryOp *op = static_cast(create(arglist.front(), region)); - if (op == nullptr) - return nullptr; + if (std::is_same, BoundaryOp>::value) { + // Test if func is a modifier + BoundaryModifier *mod = findBoundaryMod(func); + if (mod != nullptr) { + // The first argument should be an operation + BoundaryRegionOp *op = create(arglist.front(), region); + if (op == nullptr) { + return nullptr; + } - // Remove the first element (name of operation) - arglist.pop_front(); + // Remove the first element (name of operation) + arglist.pop_front(); - // Clone the modifier, passing in the operator and remaining strings as argument - return mod->cloneMod(op, arglist); - } + // Clone the modifier, passing in the operator and remaining strings as argument + return mod->cloneMod(op, arglist); + } - if(region->isParallel) { - // Parallel boundary - BoundaryOpPar *pop = findBoundaryOpPar(trim(func)); - if (pop != nullptr) { + BoundaryRegionOp *op = findBoundaryOp< BoundaryRegionOp >(trim(func)); + if (op != nullptr) { // An operation with arguments - return pop->clone(static_cast(region), arglist, keywords); + return op->clone(region, arglist, keywords); } } else { - // Perpendicular boundary - BoundaryOp *op = findBoundaryOp(trim(func)); - if (op != nullptr) { + // Parallel boundary + BoundaryRegionOp *pop = findBoundaryOp< BoundaryRegionOp >(trim(func)); + if (pop != nullptr) { // An operation with arguments - return op->clone(static_cast(region), arglist, keywords); + return pop->clone(region, arglist, keywords); } } @@ -205,11 +199,19 @@ BoundaryOpBase* BoundaryFactory::create(const string &name, BoundaryRegionBase * return nullptr; } -BoundaryOpBase* BoundaryFactory::create(const char* name, BoundaryRegionBase *region) { +template +BoundaryRegionOp* BoundaryFactory::create(const char* name, T* region) { return create(string(name), region); } - -BoundaryOpBase* BoundaryFactory::createFromOptions(const string &varname, BoundaryRegionBase *region) { +// const char* version calls the string version, so this should instantiate +// both: +template +BoundaryOp* BoundaryFactory::create(const char* name, BoundaryRegion* region); +template +BoundaryOpPar* BoundaryFactory::create(const char* name, BoundaryRegionPar* region); + +template +BoundaryRegionOp* BoundaryFactory::createFromOptions(const string &varname, T* region) { if (region == nullptr) return nullptr; @@ -269,7 +271,7 @@ BoundaryOpBase* BoundaryFactory::createFromOptions(const string &varname, Bounda } /// Then (var, all) - if(region->isParallel) { + if(std::is_same, BoundaryOpPar>::value) { if(varOpts->isSet(prefix+"par_all")) { varOpts->get(prefix+"par_all", set, ""); return create(set, region); @@ -297,7 +299,7 @@ BoundaryOpBase* BoundaryFactory::createFromOptions(const string &varname, Bounda } /// Then (all, all) - if(region->isParallel) { + if(std::is_same, BoundaryOpPar>::value) { // Different default for parallel boundary regions varOpts->get(prefix+"par_all", set, "parallel_dirichlet"); } else { @@ -308,12 +310,19 @@ BoundaryOpBase* BoundaryFactory::createFromOptions(const string &varname, Bounda // values. If a user want to override, specify "none" or "null" } -BoundaryOpBase* BoundaryFactory::createFromOptions(const char* varname, BoundaryRegionBase *region) { +template +BoundaryRegionOp* BoundaryFactory::createFromOptions(const char* varname, T* region) { return createFromOptions(string(varname), region); } +// const char* version calls the string version, so this should instantiate +// both: +template +BoundaryOp* BoundaryFactory::createFromOptions(const char* name, BoundaryRegion* region); +template +BoundaryOpPar* BoundaryFactory::createFromOptions(const char* name, BoundaryRegionPar* region); void BoundaryFactory::add(BoundaryOp* bop, const string &name) { - if ((findBoundaryMod(name) != nullptr) || (findBoundaryOp(name) != nullptr)) { + if ((findBoundaryMod(name) != nullptr) || (findBoundaryOp(name) != nullptr)) { // error - already exists output_error << "ERROR: Trying to add an already existing boundary: " << name << endl; return; @@ -326,7 +335,7 @@ void BoundaryFactory::add(BoundaryOp* bop, const char *name) { } void BoundaryFactory::add(BoundaryOpPar* bop, const string &name) { - if (findBoundaryOpPar(name) != nullptr) { + if (findBoundaryOp(name) != nullptr) { // error - already exists output_error << "ERROR: Trying to add an already existing boundary: " << name << endl; return; @@ -339,7 +348,7 @@ void BoundaryFactory::add(BoundaryOpPar* bop, const char *name) { } void BoundaryFactory::addMod(BoundaryModifier* bmod, const string &name) { - if ((findBoundaryMod(name) != nullptr) || (findBoundaryOp(name) != nullptr)) { + if ((findBoundaryMod(name) != nullptr) || (findBoundaryOp(name) != nullptr)) { // error - already exists output_error << "ERROR: Trying to add an already existing boundary modifier: " << name << endl; return; @@ -351,7 +360,8 @@ void BoundaryFactory::addMod(BoundaryModifier* bmod, const char *name) { addMod(bmod, string(name)); } -BoundaryOp* BoundaryFactory::findBoundaryOp(const string &s) { +template<> +BoundaryOp* BoundaryFactory::findBoundaryOp(const string &s) { map::iterator it; it = opmap.find(lowercase(s)); if(it == opmap.end()) @@ -359,18 +369,19 @@ BoundaryOp* BoundaryFactory::findBoundaryOp(const string &s) { return it->second; } -BoundaryModifier* BoundaryFactory::findBoundaryMod(const string &s) { - map::iterator it; - it = modmap.find(lowercase(s)); - if(it == modmap.end()) +template<> +BoundaryOpPar* BoundaryFactory::findBoundaryOp(const string &s) { + map::iterator it; + it = par_opmap.find(lowercase(s)); + if(it == par_opmap.end()) return nullptr; return it->second; } -BoundaryOpPar* BoundaryFactory::findBoundaryOpPar(const string &s) { - map::iterator it; - it = par_opmap.find(lowercase(s)); - if(it == par_opmap.end()) +BoundaryModifier* BoundaryFactory::findBoundaryMod(const string &s) { + map::iterator it; + it = modmap.find(lowercase(s)); + if(it == modmap.end()) return nullptr; return it->second; } diff --git a/src/mesh/boundary_op.cxx b/src/mesh/boundary_op.cxx new file mode 100644 index 0000000000..c4576b872b --- /dev/null +++ b/src/mesh/boundary_op.cxx @@ -0,0 +1,39 @@ +/*************************************************************************** + * Copyright 2018 B.D. Dudson, J.T. Omotani + * + * Contact: Ben Dudson, bd512@york.ac.uk + * + * This file is part of BOUT++. + * + * BOUT++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * BOUT++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with BOUT++. If not, see . + * + **************************************************************************/ + +#include +#include +#include + +void BoundaryOp::apply_ddt(Field2D &f) { + Field2D *dt = f.timeDeriv(); + for (bndry->first(); !bndry->isDone(); bndry->next()) + for (int z = 0; z < f.getNz(); z++) + (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero +} + +void BoundaryOp::apply_ddt(Field3D &f) { + Field3D *dt = f.timeDeriv(); + for (bndry->first(); !bndry->isDone(); bndry->next()) + for (int z = 0; z < f.getNz(); z++) + (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero +} diff --git a/src/mesh/boundary_region.cxx b/src/mesh/boundary_region.cxx index 0ddd615e1d..ce3aa9ec9d 100644 --- a/src/mesh/boundary_region.cxx +++ b/src/mesh/boundary_region.cxx @@ -3,16 +3,21 @@ #include #include -BoundaryRegionXIn::BoundaryRegionXIn(std::string name, int ymin, int ymax, Mesh* passmesh) - : BoundaryRegion(name, -1, 0, passmesh), ys(ymin), ye(ymax) +BoundaryRegionXIn::BoundaryRegionXIn(std::string name, int ymin, int ymax, Mesh* passmesh, int wid) + : BoundaryRegion(name, -1, 0, BNDRY_XIN, + wid < 0 ? (passmesh == nullptr ? mesh : passmesh)->xstart : wid, + passmesh), + ys(ymin), ye(ymax) { - location = BNDRY_XIN; - width = localmesh->xstart; x = width-1; // First point inside the boundary if(ye < ys) swap(ys, ye); } +BoundaryRegion* BoundaryRegionXIn::copy(int wid) { + return new BoundaryRegionXIn(label, ys, ye, localmesh, wid); +} + void BoundaryRegionXIn::first() { x = width-1; @@ -57,16 +62,21 @@ bool BoundaryRegionXIn::isDone() /////////////////////////////////////////////////////////////// -BoundaryRegionXOut::BoundaryRegionXOut(std::string name, int ymin, int ymax, Mesh* passmesh) - : BoundaryRegion(name, 1, 0, passmesh), ys(ymin), ye(ymax) +BoundaryRegionXOut::BoundaryRegionXOut(std::string name, int ymin, int ymax, Mesh* passmesh, int wid) + : BoundaryRegion(name, 1, 0, BNDRY_XOUT, + wid < 0 ? (passmesh == nullptr ? mesh : passmesh)->LocalNx - (passmesh == nullptr ? mesh : passmesh)->xend - 1 : wid, + passmesh), + ys(ymin), ye(ymax) { - location = BNDRY_XOUT; - width = localmesh->LocalNx - localmesh->xend - 1; x = localmesh->LocalNx - width; // First point inside the boundary if(ye < ys) swap(ys, ye); } +BoundaryRegion* BoundaryRegionXOut::copy(int wid) { + return new BoundaryRegionXOut(label, ys, ye, localmesh, wid); +} + void BoundaryRegionXOut::first() { x = localmesh->LocalNx - width; @@ -111,16 +121,21 @@ bool BoundaryRegionXOut::isDone() /////////////////////////////////////////////////////////////// -BoundaryRegionYDown::BoundaryRegionYDown(std::string name, int xmin, int xmax, Mesh* passmesh) - : BoundaryRegion(name, 0, -1, passmesh), xs(xmin), xe(xmax) +BoundaryRegionYDown::BoundaryRegionYDown(std::string name, int xmin, int xmax, Mesh* passmesh, int wid) + : BoundaryRegion(name, 0, -1, BNDRY_YDOWN, + wid < 0 ? (passmesh == nullptr ? mesh : passmesh)->ystart : wid, + passmesh), + xs(xmin), xe(xmax) { - location = BNDRY_YDOWN; - width = localmesh->ystart; y = width-1; // First point inside the boundary if(xe < xs) swap(xs, xe); } +BoundaryRegion* BoundaryRegionYDown::copy(int wid) { + return new BoundaryRegionYDown(label, xs, xe, localmesh, wid); +} + void BoundaryRegionYDown::first() { x = xs; @@ -166,16 +181,21 @@ bool BoundaryRegionYDown::isDone() /////////////////////////////////////////////////////////////// -BoundaryRegionYUp::BoundaryRegionYUp(std::string name, int xmin, int xmax, Mesh* passmesh) - : BoundaryRegion(name, 0, 1, passmesh), xs(xmin), xe(xmax) +BoundaryRegionYUp::BoundaryRegionYUp(std::string name, int xmin, int xmax, Mesh* passmesh, int wid) + : BoundaryRegion(name, 0, 1, BNDRY_YUP, + wid < 0 ? (passmesh == nullptr ? mesh : passmesh)->LocalNy - (passmesh == nullptr ? mesh : passmesh)->yend - 1 : wid, + passmesh), + xs(xmin), xe(xmax) { - location = BNDRY_YUP; - width = localmesh->LocalNy - localmesh->yend - 1; y = localmesh->LocalNy - width; // First point inside the boundary if(xe < xs) swap(xs, xe); } +BoundaryRegion* BoundaryRegionYUp::copy(int wid) { + return new BoundaryRegionYUp(label, xs, xe, localmesh, wid); +} + void BoundaryRegionYUp::first() { x = xs; diff --git a/src/mesh/boundary_standard.cxx b/src/mesh/boundary_standard.cxx index 44f910d74d..1282a4df4e 100644 --- a/src/mesh/boundary_standard.cxx +++ b/src/mesh/boundary_standard.cxx @@ -1,21 +1,19 @@ -#include #include -#include -#include -#include -#include -#include -#include #include +#include #include - -// #define BOUNDARY_CONDITIONS_UPGRADE_EXTRAPOLATE_FOR_2ND_ORDER +#include +#include +#include +#include +#include +#include /////////////////////////////////////////////////////////////// // Helpers /** \brief Check that there are sufficient non-boundary points for desired B.C. - + Checks both the size of the global grid (i.e. if this B.C. could be ok for some parallel setup or not) and the local grid. @@ -23,1565 +21,1467 @@ lead to an out of bounds access error later but we add it here to provide a more explanatory message. */ +namespace { void verifyNumPoints(BoundaryRegion *region, int ptsRequired) { TRACE("Verifying number of points available for BC"); #ifndef CHECK - return; //No checking so just return + return; // No checking so just return #else + Mesh *localmesh = region->localmesh; + int ptsAvailGlobal, ptsAvailLocal, ptsAvail; string side, gridType; - - //Initialise var in case of no match and CHECK<=2 - ptsAvail = ptsRequired; //Ensures test passes without exception - - switch(region->location) { - case BNDRY_XIN: + + // Initialise var in case of no match and CHECK<=2 + ptsAvail = ptsRequired; // Ensures test passes without exception + + switch (region->location) { + case BNDRY_XIN: case BNDRY_XOUT: { side = "x"; - //Here 2*mesh->xstart is the total number of guard/boundary cells - ptsAvailGlobal = mesh->GlobalNx - 2*mesh->xstart; + // Here 2*localmesh->xstart is the total number of guard/boundary cells + ptsAvailGlobal = localmesh->GlobalNx - 2 * localmesh->xstart; - //Work out how many processor local points we have excluding boundaries - //but including ghost/guard cells - ptsAvailLocal = mesh->LocalNx; - if(mesh->firstX()) ptsAvailLocal -= mesh->xstart; - if(mesh->lastX()) ptsAvailLocal -= mesh->xstart; + // Work out how many processor local points we have excluding boundaries + // but including ghost/guard cells + ptsAvailLocal = localmesh->LocalNx; + if (localmesh->firstX()) + ptsAvailLocal -= localmesh->xstart; + if (localmesh->lastX()) + ptsAvailLocal -= localmesh->xstart; - //Now decide if it's a local or global limit, prefer global if a tie - if(ptsAvailGlobal <= ptsAvailLocal){ + // Now decide if it's a local or global limit, prefer global if a tie + if (ptsAvailGlobal <= ptsAvailLocal) { ptsAvail = ptsAvailGlobal; gridType = "global"; - }else{ + } else { ptsAvail = ptsAvailLocal; gridType = "local"; } break; } - case BNDRY_YUP: + case BNDRY_YUP: case BNDRY_YDOWN: { side = "y"; - //Here 2*mesh->ystart is the total number of guard/boundary cells - ptsAvailGlobal = mesh->GlobalNy - 2*mesh->ystart; + // Here 2*localmesh->ystart is the total number of guard/boundary cells + ptsAvailGlobal = localmesh->GlobalNy - 2 * localmesh->ystart; - //Work out how many processor local points we have excluding boundaries - //but including ghost/guard cells - ptsAvailLocal = mesh->LocalNy; - if(mesh->firstY()) ptsAvailLocal -= mesh->ystart; - if(mesh->lastY()) ptsAvailLocal -= mesh->ystart; + // Work out how many processor local points we have excluding boundaries + // but including ghost/guard cells + ptsAvailLocal = localmesh->LocalNy; + if (localmesh->firstY()) + ptsAvailLocal -= localmesh->ystart; + if (localmesh->lastY()) + ptsAvailLocal -= localmesh->ystart; - //Now decide if it's a local or global limit, prefer global if a tie - if(ptsAvailGlobal <= ptsAvailLocal){ + // Now decide if it's a local or global limit, prefer global if a tie + if (ptsAvailGlobal <= ptsAvailLocal) { ptsAvail = ptsAvailGlobal; gridType = "global"; - }else{ + } else { ptsAvail = ptsAvailLocal; gridType = "local"; } break; } -#if CHECK > 2 //Only fail on Unrecognised boundary for extreme checking - default : { - throw BoutException("Unrecognised boundary region (%s) for verifyNumPoints.",region->location); + default: { + throw BoutException("Unrecognised boundary region (%s) for verifyNumPoints.", + region->location); } -#endif } - //Now check we have enough points and if not throw an exception - if(ptsAvail < ptsRequired){ - throw BoutException("Too few %s grid points for %s boundary, have %d but need at least %d", - gridType.c_str(),side.c_str(),ptsAvail,ptsRequired); + // Now check we have enough points and if not throw an exception + if (ptsAvail < ptsRequired) { + throw BoutException( + "Too few %s grid points for %s boundary, have %d but need at least %d", + gridType.c_str(), side.c_str(), ptsAvail, ptsRequired); } #endif } -/////////////////////////////////////////////////////////////// +// 2nd order extrapolation to a point +template void extrapolate2nd(T &f, int x, int bx, int y, int by, int z) { + f(x, y, z) = 2 * f(x - bx, y - by, z) - f(x - 2 * bx, y - 2 * by, z); +} + +// 3rd order extrapolation to a point +template void extrapolate3rd(T &f, int x, int bx, int y, int by, int z) { + f(x, y, z) = 3.0 * f(x - bx, y - by, z) - 3.0 * f(x - 2 * bx, y - 2 * by, z) + + f(x - 3 * bx, y - 3 * by, z); +} + +// 4th order extrapolation to a point +template void extrapolate4th(T &f, int x, int bx, int y, int by, int z) { + f(x, y, z) = 4.0 * f(x - bx, y - by, z) - 6.0 * f(x - 2 * bx, y - 2 * by, z) + + 4.0 * f(x - 3 * bx, y - 3 * by, z) - f(x - 4 * bx, y - 4 * by, z); +} + +// 5th order extrapolation to a point +template void extrapolate5th(T &f, int x, int bx, int y, int by, int z) { + f(x, y, z) = 5.0 * f(x - bx, y - by, z) - 10.0 * f(x - 2 * bx, y - 2 * by, z) + + 10.0 * f(x - 3 * bx, y - 3 * by, z) - 5.0 * f(x - 4 * bx, y - 4 * by, z) + + f(x - 5 * bx, y - 5 * by, z); +} -BoundaryOp* BoundaryDirichlet::clone(BoundaryRegion *region, const list &args){ - verifyNumPoints(region,1); +template +BoundaryOp *boundaryClone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + verifyNumPoints(region, numpoints); - std::shared_ptr newgen; - if(!args.empty()) { - // First argument should be an expression - newgen = FieldFactory::get()->parse(args.front()); + std::shared_ptr newgen = nullptr; + BoutReal val = 0.; + if (!args.empty()) { + // First argument should be a value or expression + std::string expr = args.front(); + try { + val = stringToReal(expr); + } catch (const BoutException&) { + val = 0.; + newgen = FieldFactory::get()->parse(expr); + } + } + for (const auto &it : keywords) { + if (it.first == "width") { + int width = stringToInt(it.second); + + // remove this keyword from keywords that we pass through + auto new_keywords = keywords; + new_keywords.erase("width"); + + // Need to use BoundaryWidth modifier to implement this keyword. + // Clone a version of this boundary condition without 'width' and pass + // to a BoundaryWidth modifier. + return new BoundaryWidth(boundaryClone(region, args, new_keywords), width); + } else { + throw BoutException("Unrecognized boundary condition keyword %s for %s boundary", + it.first.c_str(), region->label.c_str()); + } } - return new BoundaryDirichlet(region, newgen); + return new T(region, val, newgen); } -void BoundaryDirichlet::apply(Field2D &f){ - BoundaryDirichlet::apply(f,0.); +template +BoundaryOp *boundaryCloneNoArguments(BoundaryRegion *region, const list &args, + const std::map &keywords) { + verifyNumPoints(region, numpoints); + + if (!args.empty()) { + output << "WARNING: Ignoring arguments to BoundaryOp for " << region->label + << " region\n"; + } + for (const auto &it : keywords) { + if (it.first == "width") { + int width = stringToInt(it.second); + + // remove this keyword from keywords that we pass through + auto new_keywords = keywords; + new_keywords.erase("width"); + + // Need to use BoundaryWidth modifier to implement this keyword. + // Clone a version of this boundary condition without 'width' and pass + // to a BoundaryWidth modifier. + return new BoundaryWidth(boundaryClone(region, args, new_keywords), width); + } else { + throw BoutException("Unrecognized boundary condition keyword %s for %s boundary", + it.first.c_str(), region->label.c_str()); + } + } + return new T(region); +} } -void BoundaryDirichlet::apply(Field2D &f,BoutReal t) { - // Set (at 2nd order) the value at the mid-point between the guard cell and the grid cell to be val - // N.B. Only first guard cells (closest to the grid) should ever be used - +/////////////////////////////////////////////////////////////// + +// Apply method for templated (CRTP pattern) BoundaryOpWithApply base class. +// Included here so it's in the same 'translation unit' as the implementations, +// so will be instantiated for each 'Derived'. + +template +template +void BoundaryOpWithApply::applyTemplate(T &f, BoutReal t) { + + Mesh *localmesh = f.getMesh(); + Coordinates *metric = f.getCoordinates(); + + CELL_LOC loc = f.getLocation(); + bndry->first(); + int nz = f.getNz(); + // Decide which generator to use std::shared_ptr fg = gen; - if(!fg) + if (!fg) { fg = f.getBndryGenerator(bndry->location); + } - BoutReal val = 0.0; - + if (fg) { + BoutReal generated_val = 0.0; + + if (loc == CELL_CENTRE) { + // no staggering + for (; !bndry->isDone(); bndry->next1d()) { + // Calculate the X and Y normalised values half-way between the guard cell and + // grid cell + BoutReal xnorm = + 0.5 * (localmesh->GlobalX(bndry->x) // In the guard cell + + localmesh->GlobalX(bndry->x - bndry->bx)); // the grid cell + + BoutReal ynorm = + 0.5 * (localmesh->GlobalY(bndry->y) // In the guard cell + + localmesh->GlobalY(bndry->y - bndry->by)); // the grid cell + + BoutReal delta; + if (needs_delta) { + delta = bndry->bx * metric->dx(bndry->x, bndry->y) + + bndry->by * metric->dy(bndry->x, bndry->y); + } else { + delta = 0.; + } - // Check for staggered grids - - CELL_LOC loc = f.getLocation(); - if(mesh->StaggerGrids && loc != CELL_CENTRE) { - // Staggered. Need to apply slightly differently - - if( loc == CELL_XLOW ) { - // shifted in X - - if(bndry->bx > 0) { - // Outer x boundary - - for(; !bndry->isDone(); bndry->next1d()) { - if(fg) { - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) - + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = mesh->GlobalY(bndry->y); - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); - } - - f(bndry->x,bndry->y) = val; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y ; - - f(xi, yi) = 2*f(xi - bndry->bx, yi) - f(xi - 2*bndry->bx, yi); - } + for (int z = 0; z < nz; z++) { + // Calculate the Z normalized value (at either guard cell or grid cell + BoutReal znorm = BoutReal(z) / BoutReal(nz); + generated_val = fg->generate(xnorm, TWOPI * ynorm, TWOPI * znorm, t); + + Derived::applyAtPoint(f, generated_val, bndry->x, bndry->bx, bndry->y, + bndry->by, z, delta); + } + + // Need to set second guard cell, as may be used for interpolation or upwinding + // derivatives + for (int i = 1; i < bndry->width; i++) { + int x = bndry->x + i * bndry->bx; + int y = bndry->y + i * bndry->by; + for (int z = 0; z < nz; z++) { + Derived::extrapolateFurther(f, x, bndry->bx, y, bndry->by, z); + } } } - if(bndry->bx < 0) { + } else if (loc == CELL_XLOW) { + // field is shifted in X + if (bndry->bx > 0) { + // Outer x boundary + for (; !bndry->isDone(); bndry->next1d()) { + BoutReal xnorm = 0.5 * (localmesh->GlobalX(bndry->x) + + localmesh->GlobalX(bndry->x - bndry->bx)); + BoutReal ynorm = localmesh->GlobalY(bndry->y); + + BoutReal delta; + if (needs_delta) { + delta = bndry->bx * metric->dx(bndry->x, bndry->y) + + bndry->by * metric->dy(bndry->x, bndry->y); + } else { + delta = 0.; + } + + for (int z = 0; z < nz; z++) { + BoutReal znorm = BoutReal(z) / BoutReal(nz); + generated_val = fg->generate(xnorm, TWOPI * ynorm, TWOPI * znorm, t); + + Derived::applyAtPointStaggered(f, generated_val, bndry->x, bndry->bx, + bndry->y, 0, z, delta); + + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 1; i < bndry->width; i++) { + int x = bndry->x + i * bndry->bx; + Derived::extrapolateFurther(f, x, bndry->bx, bndry->y, 0, z); + } + } + } + } else if (bndry->bx < 0) { // Inner x boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - if(fg) { - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) - + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = mesh->GlobalY(bndry->y); - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); + for (; !bndry->isDone(); bndry->next1d()) { + + BoutReal xnorm = 0.5 * (localmesh->GlobalX(bndry->x) + + localmesh->GlobalX(bndry->x - bndry->bx)); + BoutReal ynorm = localmesh->GlobalY(bndry->y); + + BoutReal delta; + if (needs_delta) { + delta = bndry->bx * metric->dx(bndry->x + 1, bndry->y) + + bndry->by * metric->dy(bndry->x + 1, bndry->y); + } else { + delta = 0.; + } + + for (int z = 0; z < nz; z++) { + BoutReal znorm = BoutReal(z) / BoutReal(nz); + generated_val = fg->generate(xnorm, TWOPI * ynorm, TWOPI * znorm, t); + + // Set one point inwards + Derived::applyAtPointStaggered(f, generated_val, bndry->x + 1, bndry->bx, + bndry->y, 0, z, delta); + + // Need to set second and third guard cells, as may be used for interpolation + // or upwinding derivatives + for (int i = 0; i < bndry->width; i++) { + int x = bndry->x + i * bndry->bx; + Derived::extrapolateFurther(f, x, bndry->bx, bndry->y, 0, z); + } } - - f(bndry->x - bndry->bx,bndry->y) = val; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y ; - - f(xi, yi) = 2*f(xi - bndry->bx, yi) - f(xi - 2*bndry->bx, yi); - } } - } - if(bndry->by !=0){ + } else if (bndry->by != 0) { // y boundaries - for(; !bndry->isDone(); bndry->next1d()) { - if(fg) { - // x norm is shifted by half a grid point because it is staggered. - // y norm is located half way between first grid cell and guard cell. - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - 1) ); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - bndry->by) ); - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); + for (; !bndry->isDone(); bndry->next1d()) { + // x norm is shifted bndry->by half a grid point because it is staggered. + // y norm is located half way between first grid cell and guard cell. + BoutReal xnorm = + 0.5 * (localmesh->GlobalX(bndry->x) + localmesh->GlobalX(bndry->x - 1)); + BoutReal ynorm = 0.5 * (localmesh->GlobalY(bndry->y) + + localmesh->GlobalY(bndry->y - bndry->by)); + + BoutReal delta; + if (needs_delta) { + delta = bndry->bx * metric->dx(bndry->x, bndry->y) + + bndry->by * metric->dy(bndry->x, bndry->y); + } else { + delta = 0.; + } + + for (int z = 0; z < nz; z++) { + BoutReal znorm = BoutReal(z) / BoutReal(nz); + generated_val = fg->generate(xnorm, TWOPI * ynorm, TWOPI * znorm, t); + + Derived::applyAtPoint(f, generated_val, bndry->x, 0, bndry->y, bndry->by, z, + delta); + + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 1; i < bndry->width; i++) { + int y = bndry->y + i * bndry->by; + Derived::extrapolateFurther(f, bndry->x, 0, y, bndry->by, z); + } } - f(bndry->x,bndry->y) = 2*val - f(bndry->x-bndry->bx, bndry->y-bndry->by); - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x ; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 2*f(xi, yi - bndry->by) - f(xi, yi - 2*bndry->by); - } } } - } - else if( loc == CELL_YLOW ) { - // Y boundary, and field is shifted in Y - - if(bndry->by > 0) { - // Upper y boundary - - for(; !bndry->isDone(); bndry->next1d()) { - if(fg) { - BoutReal xnorm = mesh->GlobalX(bndry->x); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) - + mesh->GlobalY(bndry->y - bndry->by) ); - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); + } else if (loc == CELL_YLOW) { + // Shifted in Y + if (bndry->by > 0) { + // Upper y boundary boundary + for (; !bndry->isDone(); bndry->next1d()) { + + BoutReal xnorm = localmesh->GlobalX(bndry->x); + BoutReal ynorm = 0.5 * (localmesh->GlobalY(bndry->y) + + localmesh->GlobalY(bndry->y - bndry->by)); + + BoutReal delta; + if (needs_delta) { + delta = bndry->bx * metric->dx(bndry->x, bndry->y) + + bndry->by * metric->dy(bndry->x, bndry->y); + } else { + delta = 0.; + } + + for (int z = 0; z < nz; z++) { + BoutReal znorm = BoutReal(z) / BoutReal(nz); + generated_val = fg->generate(xnorm, TWOPI * ynorm, TWOPI * znorm, t); + + Derived::applyAtPointStaggered(f, generated_val, bndry->x, 0, bndry->y, + bndry->by, z, delta); + + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 1; i < bndry->width; i++) { + int y = bndry->y + i * bndry->by; + Derived::extrapolateFurther(f, bndry->x, 0, y, bndry->by, z); + } } - - f(bndry->x,bndry->y) = val; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x ; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 2*f(xi, yi - bndry->by) - f(xi, yi - 2*bndry->by); - } } - } - if(bndry->by < 0) { + } else if (bndry->by < 0) { // Lower y boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - if(fg) { - BoutReal xnorm = mesh->GlobalX(bndry->x); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) - + mesh->GlobalY(bndry->y - bndry->by) ); - - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); + for (; !bndry->isDone(); bndry->next1d()) { + + BoutReal xnorm = localmesh->GlobalX(bndry->x); + BoutReal ynorm = 0.5 * (localmesh->GlobalY(bndry->y) + + localmesh->GlobalY(bndry->y - bndry->by)); + + BoutReal delta; + if (needs_delta) { + delta = bndry->bx * metric->dx(bndry->x, bndry->y + 1) + + bndry->by * metric->dy(bndry->x, bndry->y + 1); + } else { + delta = 0.; + } + + for (int z = 0; z < nz; z++) { + BoutReal znorm = BoutReal(z) / BoutReal(nz); + generated_val = fg->generate(xnorm, TWOPI * ynorm, TWOPI * znorm, t); + + Derived::applyAtPointStaggered(f, generated_val, bndry->x, 0, bndry->y + 1, + bndry->by, z, delta); + + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 0; i < bndry->width; i++) { + int y = bndry->y + i * bndry->by; + Derived::extrapolateFurther(f, bndry->x, 0, y, bndry->by, z); + } } - - f(bndry->x,bndry->y - bndry->by) = val; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=0;iwidth;i++) { - int xi = bndry->x ; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 2*f(xi, yi - bndry->by) - f(xi, yi - 2*bndry->by); - } } - } - if (bndry->bx !=0){ + } else if (bndry->bx != 0) { // x boundaries - for(; !bndry->isDone(); bndry->next1d()) { - - if(fg) { - // x norm is located half way between first grid cell and guard cell. - // y norm is shifted by half a grid point because it is staggered. - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - 1) ); - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); + for (; !bndry->isDone(); bndry->next1d()) { + // x norm is located half way between first grid cell and guard cell. + // y norm is shifted by half a grid point because it is staggered. + BoutReal xnorm = 0.5 * (localmesh->GlobalX(bndry->x) + + localmesh->GlobalX(bndry->x - bndry->bx)); + BoutReal ynorm = + 0.5 * (localmesh->GlobalY(bndry->y) + localmesh->GlobalY(bndry->y - 1)); + + BoutReal delta; + if (needs_delta) { + delta = bndry->bx * metric->dx(bndry->x, bndry->y) + + bndry->by * metric->dy(bndry->x, bndry->y); + } else { + delta = 0.; } - f(bndry->x,bndry->y) = 2*val - f(bndry->x-bndry->bx, bndry->y-bndry->by); - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y ; - f(xi, yi) = 2*f(xi - bndry->bx, yi) - f(xi - 2*bndry->bx, yi); - } - } - } - } - } else { - // Non-staggered, standard case - - for(; !bndry->isDone(); bndry->next1d()) { - - if(fg) { - // Calculate the X and Y normalised values half-way between the guard cell and grid cell - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) // In the guard cell - + mesh->GlobalX(bndry->x - bndry->bx) ); // the grid cell - - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) // In the guard cell - + mesh->GlobalY(bndry->y - bndry->by) ); // the grid cell - - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); - } - - f(bndry->x,bndry->y) = 2*val - f(bndry->x-bndry->bx, bndry->y-bndry->by); - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->bx; - f(xi, yi) = 2*f(xi - bndry->bx, yi - bndry->by) - f(xi - 2*bndry->bx, yi - 2*bndry->by); - } - } - } -} + for (int z = 0; z < nz; z++) { + BoutReal znorm = BoutReal(z) / BoutReal(nz); + generated_val = fg->generate(xnorm, TWOPI * ynorm, TWOPI * znorm, t); -void BoundaryDirichlet::apply(Field3D &f) { - BoundaryDirichlet::apply(f,0.); -} + Derived::applyAtPoint(f, generated_val, bndry->x, bndry->bx, bndry->y, 0, z, + delta); -void BoundaryDirichlet::apply(Field3D &f,BoutReal t) { - // Set (at 2nd order) the value at the mid-point between the guard cell and the grid cell to be val - // N.B. Only first guard cells (closest to the grid) should ever be used + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 1; i < bndry->width; i++) { + int x = bndry->x + i * bndry->bx; + Derived::extrapolateFurther(f, x, bndry->bx, bndry->y, 0, z); + } + } + } + } + } else if (loc == CELL_ZLOW) { + // Staggered in Z. Note there are no z-boundaries. + for (; !bndry->isDone(); bndry->next1d()) { + // Calculate the X and Y normalised values half-way between the guard cell and + // grid cell + BoutReal xnorm = + 0.5 * (localmesh->GlobalX(bndry->x) // In the guard cell + + localmesh->GlobalX(bndry->x - bndry->bx)); // the grid cell + + BoutReal ynorm = + 0.5 * (localmesh->GlobalY(bndry->y) // In the guard cell + + localmesh->GlobalY(bndry->y - bndry->by)); // the grid cell + + BoutReal delta; + if (needs_delta) { + delta = bndry->bx * metric->dx(bndry->x, bndry->y) + + bndry->by * metric->dy(bndry->x, bndry->y); + } else { + delta = 0.; + } - bndry->first(); + for (int z = 0; z < nz; z++) { + // It shouldn't matter if znorm<0 because the expression in fg->generate should + // be periodic in z - // Decide which generator to use - std::shared_ptr fg = gen; - if(!fg) - fg = f.getBndryGenerator(bndry->location); + // znorm is shifted by half a grid point because it is staggered + BoutReal znorm = (BoutReal(z) - 0.5) / BoutReal(nz); + generated_val = fg->generate(xnorm, TWOPI * ynorm, TWOPI * znorm, t); - BoutReal val = 0.0; + Derived::applyAtPoint(f, generated_val, bndry->x, bndry->bx, bndry->y, + bndry->by, z, delta); - // Check for staggered grids - - CELL_LOC loc = f.getLocation(); - if(mesh->StaggerGrids && loc != CELL_CENTRE) { - // Staggered. Need to apply slightly differently - - if( loc == CELL_XLOW ) { - // X boundary, and field is shifted in X - - if(bndry->bx > 0) { - // Outer x boundary - - for(; !bndry->isDone(); bndry->next1d()) { - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) - + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = mesh->GlobalY(bndry->y); - - for(int zk=0;zkLocalNz;zk++) { - if(fg){ - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - } - f(bndry->x,bndry->y, zk) = val; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y ; - - f(xi, yi, zk) = 2*f(xi - bndry->bx, yi , zk) - f(xi- 2*bndry->bx, yi , zk); - } - } - } - } - if (bndry->bx < 0){ - // Inner x boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) - + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = mesh->GlobalY(bndry->y); - - for(int zk=0;zkLocalNz;zk++) { - if(fg){ - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - } - f(bndry->x - bndry->bx,bndry->y, zk) = val; - f(bndry->x,bndry->y, zk) = f(bndry->x - bndry->bx,bndry->y, zk); - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y ; - - f(xi, yi, zk) = 2*f(xi - bndry->bx, yi , zk) - f(xi- 2*bndry->bx, yi , zk); - } - } - } - } - if(bndry->by !=0){ - // y boundaries - for(; !bndry->isDone(); bndry->next1d()) { - // x norm is shifted by half a grid point because it is staggered. - // y norm is located half way between first grid cell and guard cell. - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - 1) ); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - bndry->by) ); - - for(int zk=0;zkLocalNz;zk++) { - if(fg){ - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - } - f(bndry->x,bndry->y,zk) = 2*val - f(bndry->x-bndry->bx, bndry->y-bndry->by, zk); - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x ; - int yi = bndry->y + i*bndry->by; - - f(xi, yi, zk) = 2*f(xi, yi - bndry->by, zk) - f(xi, yi - 2*bndry->by, zk); - } - } - } - } - } - else if( loc == CELL_YLOW ) { - // Shifted in Y - - if(bndry->by > 0) { - // Upper y boundary boundary - - for(; !bndry->isDone(); bndry->next1d()) { - BoutReal xnorm = mesh->GlobalX(bndry->x); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - bndry->by) ); - for(int zk=0;zkLocalNz;zk++) { - if(fg){ - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - } - f(bndry->x,bndry->y,zk) = val; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x ; - int yi = bndry->y + i*bndry->by; - - f(xi, yi, zk) = 2.0*f(xi, yi - bndry->by, zk) - f(xi, yi - 2*bndry->by, zk); - } - } - } - } - if(bndry->by < 0){ - // Lower y boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - - BoutReal xnorm = mesh->GlobalX(bndry->x); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - bndry->by) ); - - for(int zk=0;zkLocalNz;zk++) { - if(fg){ - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - } - f(bndry->x,bndry->y - bndry->by, zk) = val; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=0;iwidth;i++) { - int xi = bndry->x ; - int yi = bndry->y + i*bndry->by; - - f(xi, yi, zk) = 2*f(xi, yi - bndry->by, zk) - f(xi, yi - 2*bndry->by, zk); - } - } - } - } - if(bndry->bx != 0){ - // x boundaries - for(; !bndry->isDone(); bndry->next1d()) { - // x norm is located half way between first grid cell and guard cell. - // y norm is shifted by half a grid point because it is staggered. - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - 1) ); - - for(int zk=0;zkLocalNz;zk++) { - if(fg) - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - - f(bndry->x,bndry->y,zk) = 2*val - f(bndry->x-bndry->bx, bndry->y-bndry->by, zk); - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y ; - - f(xi, yi, zk) = 2*f(xi - bndry->bx, yi , zk) - f(xi - 2*bndry->bx, yi, zk); - } - } - } + // Need to set second guard cell, as may be used for interpolation or upwinding + // derivatives + for (int i = 1; i < bndry->width; i++) { + int x = bndry->x + i * bndry->bx; + int y = bndry->y + i * bndry->by; + Derived::extrapolateFurther(f, x, bndry->bx, y, bndry->by, z); + } + } } } - } - else { - // Standard (non-staggered) case - for(; !bndry->isDone(); bndry->next1d()) { - // Calculate the X and Y normalised values half-way between the guard cell and grid cell - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) // In the guard cell - + mesh->GlobalX(bndry->x - bndry->bx) ); // the grid cell - - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) // In the guard cell - + mesh->GlobalY(bndry->y - bndry->by) ); // the grid cell - - for(int zk=0;zkLocalNz;zk++) { - if(fg){ - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - } - f(bndry->x,bndry->y,zk) = 2*val - f(bndry->x-bndry->bx, bndry->y-bndry->by, zk); - - // We've set the first boundary point using extrapolation in - // the line above. The below block of code is attempting to - // set the rest of the boundary cells also using - // extrapolation. Whilst this choice doesn't impact 2nd order - // methods it has been observed that with higher order - // methods, which actually use these points, the use of - // extrapolation can be unstable. For this reason we have - // commented out the below block and replaced it with the loop - // several lines below, which just sets all the rest of the - // boundary points to be the specified value. We've not - // removed the commented out code as we may wish to revisit - // this in the future, however it may be that this is - // eventually removed. It can be noted that we *don't* apply - // this treatment for other boundary treatments, - // i.e. elsewhere we tend to extrapolate. - - // // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - // for(int i=1;iwidth;i++) { - // int xi = bndry->x + i*bndry->bx; - // int yi = bndry->y + i*bndry->by; - - // f(xi, yi, zk) = 2*f(xi - bndry->bx, yi - bndry->by, zk) - f(xi - 2*bndry->bx, yi - 2*bndry->by, zk); - // // f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) + f(xi - 3*bndry->bx, yi - 3*bndry->by, zk); - - // } - } + } else { + if (loc == CELL_CENTRE) { + // no staggering + for (; !bndry->isDone(); bndry->next1d()) { + BoutReal delta; + if (needs_delta) { + delta = bndry->bx * metric->dx(bndry->x, bndry->y) + + bndry->by * metric->dy(bndry->x, bndry->y); + } else { + delta = 0.; + } + + for (int z = 0; z < nz; z++) { + Derived::applyAtPoint(f, val, bndry->x, bndry->bx, bndry->y, bndry->by, z, + delta); + } - // This loop is our alternative approach to setting the rest of the boundary - // points. Instead of extrapolating we just use the generated values. This - // can help with the stability of higher order methods. - for (int i = 1; i < bndry->width; i++) { - // Set any other guard cells using the values on the cells - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - xnorm = mesh->GlobalX(xi); - ynorm = mesh->GlobalY(yi); - for(int zk=0;zkLocalNz;zk++) { - if(fg) { - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); + // Need to set second guard cell, as may be used for interpolation or upwinding + // derivatives + for (int i = 1; i < bndry->width; i++) { + int x = bndry->x + i * bndry->bx; + int y = bndry->y + i * bndry->by; + for (int z = 0; z < nz; z++) { + Derived::extrapolateFurther(f, x, bndry->bx, y, bndry->by, z); } - f(xi, yi, zk) = val; } } - } - } -} + } else if (loc == CELL_XLOW) { + // field is shifted in X + if (bndry->bx > 0) { + // Outer x boundary + for (; !bndry->isDone(); bndry->next1d()) { + BoutReal delta; + if (needs_delta) { + delta = bndry->bx * metric->dx(bndry->x, bndry->y) + + bndry->by * metric->dy(bndry->x, bndry->y); + } else { + delta = 0.; + } + for (int z = 0; z < nz; z++) { + Derived::applyAtPointStaggered(f, val, bndry->x, bndry->bx, bndry->y, 0, z, + delta); -void BoundaryDirichlet::apply_ddt(Field2D &f) { - Field2D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - (*dt)(bndry->x,bndry->y) = 0.; // Set time derivative to zero -} + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 1; i < bndry->width; i++) { + int x = bndry->x + i * bndry->bx; + Derived::extrapolateFurther(f, x, bndry->bx, bndry->y, 0, z); + } + } + } + } else if (bndry->bx < 0) { + // Inner x boundary. Set one point inwards + for (; !bndry->isDone(); bndry->next1d()) { + BoutReal delta; + if (needs_delta) { + delta = bndry->bx * metric->dx(bndry->x + 1, bndry->y) + + bndry->by * metric->dy(bndry->x + 1, bndry->y); + } else { + delta = 0.; + } -void BoundaryDirichlet::apply_ddt(Field3D &f) { - Field3D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - for(int z=0;zLocalNz;z++) - (*dt)(bndry->x,bndry->y,z) = 0.; // Set time derivative to zero -} + for (int z = 0; z < nz; z++) { + // Set one point inwards + Derived::applyAtPointStaggered(f, val, bndry->x + 1, bndry->bx, bndry->y, 0, + z, delta); + // Need to set second and third guard cells, as may be used for interpolation + // or upwinding derivatives + for (int i = 0; i < bndry->width; i++) { + int x = bndry->x + i * bndry->bx; + Derived::extrapolateFurther(f, x, bndry->bx, bndry->y, 0, z); + } + } + } + } else if (bndry->by != 0) { + // y boundaries + for (; !bndry->isDone(); bndry->next1d()) { + BoutReal delta; + if (needs_delta) { + delta = bndry->bx * metric->dx(bndry->x, bndry->y) + + bndry->by * metric->dy(bndry->x, bndry->y); + } else { + delta = 0.; + } -/////////////////////////////////////////////////////////////// -// New implementation, accurate to higher order + for (int z = 0; z < nz; z++) { + Derived::applyAtPoint(f, val, bndry->x, 0, bndry->y, bndry->by, z, delta); -BoundaryOp* BoundaryDirichlet_O3::clone(BoundaryRegion *region, const list &args){ - verifyNumPoints(region,2); - std::shared_ptr newgen = nullptr; - if(!args.empty()) { - // First argument should be an expression - newgen = FieldFactory::get()->parse(args.front()); - } - return new BoundaryDirichlet_O3(region, newgen); -} + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 1; i < bndry->width; i++) { + int y = bndry->y + i * bndry->by; + Derived::extrapolateFurther(f, bndry->x, 0, y, bndry->by, z); + } + } + } + } + } else if (loc == CELL_YLOW) { + // Shifted in Y + if (bndry->by > 0) { + // Upper y boundary boundary + for (; !bndry->isDone(); bndry->next1d()) { + + BoutReal delta; + if (needs_delta) { + delta = bndry->bx * metric->dx(bndry->x, bndry->y) + + bndry->by * metric->dy(bndry->x, bndry->y); + } else { + delta = 0.; + } -void BoundaryDirichlet_O3::apply(Field2D &f){ - BoundaryDirichlet_O3::apply(f,0.); -} + for (int z = 0; z < nz; z++) { + Derived::applyAtPointStaggered(f, val, bndry->x, 0, bndry->y, bndry->by, z, + delta); -void BoundaryDirichlet_O3::apply(Field2D &f,BoutReal t) { - // Set (at 2nd order) the value at the mid-point between the guard cell and the grid cell to be val - // N.B. Only first guard cells (closest to the grid) should ever be used - - bndry->first(); + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 1; i < bndry->width; i++) { + int y = bndry->y + i * bndry->by; + Derived::extrapolateFurther(f, bndry->x, 0, y, bndry->by, z); + } + } + } + } else if (bndry->by < 0) { + // Lower y boundary. Set one point inwards + for (; !bndry->isDone(); bndry->next1d()) { + BoutReal delta; + if (needs_delta) { + delta = bndry->bx * metric->dx(bndry->x, bndry->y + 1) + + bndry->by * metric->dy(bndry->x, bndry->y + 1); + } else { + delta = 0.; + } - // Decide which generator to use - std::shared_ptr fg = gen; - if(!fg) - fg = f.getBndryGenerator(bndry->location); + for (int z = 0; z < nz; z++) { + Derived::applyAtPointStaggered(f, val, bndry->x, 0, bndry->y + 1, bndry->by, + z, delta); - BoutReal val = 0.0; - + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 0; i < bndry->width; i++) { + int y = bndry->y + i * bndry->by; + Derived::extrapolateFurther(f, bndry->x, 0, y, bndry->by, z); + } + } + } + } else if (bndry->bx != 0) { + // x boundaries + for (; !bndry->isDone(); bndry->next1d()) { + BoutReal delta; + if (needs_delta) { + delta = bndry->bx * metric->dx(bndry->x, bndry->y) + + bndry->by * metric->dy(bndry->x, bndry->y); + } else { + delta = 0.; + } - // Check for staggered grids - - CELL_LOC loc = f.getLocation(); - if(mesh->StaggerGrids && loc != CELL_CENTRE) { - // Staggered. Need to apply slightly differently - - if( loc == CELL_XLOW) { - // Field is shifted in X - - if(bndry->bx > 0) { - // Outer x boundary - for(; !bndry->isDone(); bndry->next1d()) { - if(fg) { - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = mesh->GlobalY(bndry->y); - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); - } - - f(bndry->x,bndry->y) = val; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 3.0*f(xi - bndry->bx, yi - bndry->by) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) + f(xi - 3*bndry->bx, yi - 3*bndry->by); - } - } - } - if(bndry->bx < 0) { - // Inner x boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - if(fg) { - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = mesh->GlobalY(bndry->y); - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); - } - f(bndry->x - bndry->bx,bndry->y) = val; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 3.0*f(xi - bndry->bx, yi - bndry->by) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) + f(xi - 3*bndry->bx, yi - 3*bndry->by); - } - } - } - if(bndry->by != 0){ - for(; !bndry->isDone(); bndry->next1d()) { - if(fg) { - // x norm is shifted by half a grid point because it is staggered. - // y norm is located half way between first grid cell and guard cell. - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - 1) ); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - bndry->by) ); - - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); - } - - f(bndry->x,bndry->y) = (8./3)*val - 2.*f(bndry->x-bndry->bx, bndry->y-bndry->by) + f(bndry->x-2*bndry->bx, bndry->y-2*bndry->by)/3.; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 3.0*f(xi - bndry->bx, yi - bndry->by) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) + f(xi - 3*bndry->bx, yi - 3*bndry->by); - } - - } - } - } - else if( loc == CELL_YLOW ) { - // Field is shifted in Y - - if(bndry->by > 0) { - // Upper y boundary - - for(; !bndry->isDone(); bndry->next1d()) { - if(fg) { - BoutReal xnorm = mesh->GlobalX(bndry->x); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - bndry->by) ); - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); - } - - f(bndry->x,bndry->y) = val; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 3.0*f(xi - bndry->bx, yi - bndry->by) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) + f(xi - 3*bndry->bx, yi - 3*bndry->by); - } - - } - } - if(bndry->by < 0) { - // Lower y boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - if(fg) { - BoutReal xnorm = mesh->GlobalX(bndry->x); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - bndry->by) ); - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); - } - - f(bndry->x,bndry->y - bndry->by) = val; - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 3.0*f(xi - bndry->bx, yi - bndry->by) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) + f(xi - 3*bndry->bx, yi - 3*bndry->by); - } - - } - } - if(bndry->bx != 0){ - // x boundaries - for(; !bndry->isDone(); bndry->next1d()) { - - if(fg) { - // x norm is located half way between first grid cell and guard cell. - // y norm is shifted by half a grid point because it is staggered. - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - 1) ); - - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); - } - - f(bndry->x,bndry->y) = (8./3)*val - 2.*f(bndry->x-bndry->bx, bndry->y-bndry->by) + f(bndry->x-2*bndry->bx, bndry->y-2*bndry->by)/3.; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 3.0*f(xi - bndry->bx, yi - bndry->by) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) + f(xi - 3*bndry->bx, yi - 3*bndry->by); - } - } + for (int z = 0; z < nz; z++) { + Derived::applyAtPoint(f, val, bndry->x, bndry->bx, bndry->y, 0, z, delta); + + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 1; i < bndry->width; i++) { + int x = bndry->x + i * bndry->bx; + Derived::extrapolateFurther(f, x, bndry->bx, bndry->y, 0, z); + } + } + } } - } - } - else { - // Non-staggered, standard case - - for(; !bndry->isDone(); bndry->next1d()) { - - if(fg) { - // Calculate the X and Y normalised values half-way between the guard cell and grid cell - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) // In the guard cell - + mesh->GlobalX(bndry->x - bndry->bx) ); // the grid cell - - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) // In the guard cell - + mesh->GlobalY(bndry->y - bndry->by) ); // the grid cell - - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); + } else if (loc == CELL_ZLOW) { + // Staggered in Z. Note there are no z-boundaries. + for (; !bndry->isDone(); bndry->next1d()) { + BoutReal delta; + if (needs_delta) { + delta = bndry->bx * metric->dx(bndry->x, bndry->y) + + bndry->by * metric->dy(bndry->x, bndry->y); + } else { + delta = 0.; + } + + for (int z = 0; z < nz; z++) { + Derived::applyAtPoint(f, val, bndry->x, bndry->bx, bndry->y, bndry->by, z, + delta); + + // Need to set second guard cell, as may be used for interpolation or upwinding + // derivatives + for (int i = 1; i < bndry->width; i++) { + int x = bndry->x + i * bndry->bx; + int y = bndry->y + i * bndry->by; + Derived::extrapolateFurther(f, x, bndry->bx, y, bndry->by, z); + } + } } - - f(bndry->x,bndry->y) = (8./3)*val - 2.*f(bndry->x-bndry->bx, bndry->y-bndry->by) + f(bndry->x-2*bndry->bx, bndry->y-2*bndry->by)/3.; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 3.0*f(xi - bndry->bx, yi - bndry->by) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) + f(xi - 3*bndry->bx, yi - 3*bndry->by); - } } } } +/////////////////////////////////////////////////////////////// -void BoundaryDirichlet_O3::apply(Field3D &f) { - BoundaryDirichlet_O3::apply(f,0.); +BoundaryOp *BoundaryDirichlet::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + return boundaryClone(region, args, keywords); } - -void BoundaryDirichlet_O3::apply(Field3D &f,BoutReal t) { - // Set (at 2nd order) the value at the mid-point between the guard cell and the grid cell to be val +// Override apply(), using this private method to provide both Field2D and +// Field3D versions, for BoundaryDirichlet because we apply a funny hack to the +// extra guard cells +template void BoundaryDirichlet::applyTemplate(T &f, BoutReal t) { + // Set (at 2nd order) the value at the mid-point between the guard cell and the grid + // cell to be val // N.B. Only first guard cells (closest to the grid) should ever be used + Mesh *localmesh = f.getMesh(); + + // Check for staggered grids + CELL_LOC loc = f.getLocation(); + ASSERT1(localmesh->StaggerGrids || loc == CELL_CENTRE); + bndry->first(); + int nz = f.getNz(); + // Decide which generator to use - std::shared_ptr fg = gen; - if(!fg) + std::shared_ptr fg = gen; + if (!fg) { fg = f.getBndryGenerator(bndry->location); + } - BoutReal val = 0.0; + if (fg) { + BoutReal generated_val = 0.; + + if (loc == CELL_CENTRE) { + // Unstaggered case + for (; !bndry->isDone(); bndry->next1d()) { + // Calculate the X and Y normalised values half-way between the guard cell and + // grid cell + BoutReal xnorm = + 0.5 * (localmesh->GlobalX(bndry->x) // In the guard cell + + localmesh->GlobalX(bndry->x - bndry->bx)); // the grid cell + + BoutReal ynorm = + 0.5 * (localmesh->GlobalY(bndry->y) // In the guard cell + + localmesh->GlobalY(bndry->y - bndry->by)); // the grid cell + + for (int zk = 0; zk < nz; zk++) { + BoutReal znorm = BoutReal(zk) / BoutReal(nz); + generated_val = fg->generate(xnorm, TWOPI * ynorm, TWOPI * znorm, t); + + f(bndry->x, bndry->y, zk) = + 2 * generated_val - f(bndry->x - bndry->bx, bndry->y - bndry->by, zk); + + // We've set the first boundary point using extrapolation in + // the line above. The below block of code is attempting to + // set the rest of the boundary cells also using + // extrapolation. Whilst this choice doesn't impact 2nd order + // methods it has been observed that with higher order + // methods, which actually use these points, the use of + // extrapolation can be unstable. For this reason we have + // commented out the below block and replaced it with the loop + // several lines below, which just sets all the rest of the + // boundary points to be the specified value. We've not + // removed the commented out code as we may wish to revisit + // this in the future, however it may be that this is + // eventually removed. It can be noted that we *don't* apply + // this treatment for other boundary treatments, + // i.e. elsewhere we tend to extrapolate. + + // // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + // for(int i=1;ix + i*bndry->bx; + // int yi = bndry->y + i*bndry->by; + + // f(xi, yi, zk) = 2*f(xi - bndry->bx, yi - bndry->by, zk) - f(xi - + // 2*bndry->bx, yi - 2*bndry->by, zk); + // // f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - + // 2*bndry->bx, yi - 2*bndry->by, zk) + f(xi - 3*bndry->bx, yi - 3*bndry->by, + // zk); + + // } + } - // Check for staggered grids - - CELL_LOC loc = f.getLocation(); - if(mesh->StaggerGrids && loc != CELL_CENTRE) { - // Staggered. Need to apply slightly differently - - if( loc == CELL_XLOW ) { + // This loop is our alternative approach to setting the rest of the boundary + // points. Instead of extrapolating we just use the generated values. This + // can help with the stability of higher order methods. + for (int i = 1; i < bndry->width; i++) { + // Set any other guard cells using the values on the cells + int xi = bndry->x + i * bndry->bx; + int yi = bndry->y + i * bndry->by; + xnorm = localmesh->GlobalX(xi); + ynorm = localmesh->GlobalY(yi); + for (int zk = 0; zk < nz; zk++) { + BoutReal znorm = BoutReal(zk) / BoutReal(nz); + generated_val = fg->generate(xnorm, TWOPI * ynorm, TWOPI * znorm, t); + + f(xi, yi, zk) = generated_val; + } + } + } + } else if (loc == CELL_XLOW) { // Field is shifted in X - - if(bndry->bx > 0) { - // Outer x boundary - - for(; !bndry->isDone(); bndry->next1d()) { - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = mesh->GlobalY(bndry->y); - - for(int zk=0;zkLocalNz;zk++) { - if(fg){ - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - } - f(bndry->x,bndry->y, zk) = val; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + f(xi - 3*bndry->bx, yi - 3*bndry->by, zk); - } - } - } + if (bndry->bx > 0) { + // Outer x boundary + for (; !bndry->isDone(); bndry->next1d()) { + BoutReal xnorm = 0.5 * (localmesh->GlobalX(bndry->x) + + localmesh->GlobalX(bndry->x - bndry->bx)); + BoutReal ynorm = localmesh->GlobalY(bndry->y); + + for (int zk = 0; zk < nz; zk++) { + BoutReal znorm = BoutReal(zk) / BoutReal(nz); + generated_val = fg->generate(xnorm, TWOPI * ynorm, TWOPI * znorm, t); + + f(bndry->x, bndry->y, zk) = generated_val; + + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 1; i < bndry->width; i++) { + int xi = bndry->x + i * bndry->bx; + int yi = bndry->y; + + f(xi, yi, zk) = + 2 * f(xi - bndry->bx, yi, zk) - f(xi - 2 * bndry->bx, yi, zk); + } + } + } + } else if (bndry->bx < 0) { + // Inner x boundary. Set one point inwards + for (; !bndry->isDone(); bndry->next1d()) { + BoutReal xnorm = 0.5 * (localmesh->GlobalX(bndry->x) + + localmesh->GlobalX(bndry->x - bndry->bx)); + BoutReal ynorm = localmesh->GlobalY(bndry->y); + + for (int zk = 0; zk < nz; zk++) { + BoutReal znorm = BoutReal(zk) / BoutReal(nz); + generated_val = fg->generate(xnorm, TWOPI * ynorm, TWOPI * znorm, t); + + f(bndry->x - bndry->bx, bndry->y, zk) = generated_val; + + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 0; i < bndry->width; i++) { + int xi = bndry->x + i * bndry->bx; + int yi = bndry->y; + + f(xi, yi, zk) = + 2 * f(xi - bndry->bx, yi, zk) - f(xi - 2 * bndry->bx, yi, zk); + } + } + } + } else if (bndry->by != 0) { + // y boundaries + for (; !bndry->isDone(); bndry->next1d()) { + // x norm is shifted bndry->by half a grid point because it is staggered. + // y norm is located half way between first grid cell and guard cell. + BoutReal xnorm = + 0.5 * (localmesh->GlobalX(bndry->x) + localmesh->GlobalX(bndry->x - 1)); + BoutReal ynorm = 0.5 * (localmesh->GlobalY(bndry->y) + + localmesh->GlobalY(bndry->y - bndry->by)); + + for (int zk = 0; zk < nz; zk++) { + BoutReal znorm = BoutReal(zk) / BoutReal(nz); + generated_val = fg->generate(xnorm, TWOPI * ynorm, TWOPI * znorm, t); + + f(bndry->x, bndry->y, zk) = + 2 * generated_val - f(bndry->x - bndry->bx, bndry->y - bndry->by, zk); + + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 1; i < bndry->width; i++) { + int xi = bndry->x; + int yi = bndry->y + i * bndry->by; + + f(xi, yi, zk) = + 2 * f(xi, yi - bndry->by, zk) - f(xi, yi - 2 * bndry->by, zk); + } + } + } } - if(bndry->bx < 0) { - // Inner x boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = mesh->GlobalY(bndry->y); - - for(int zk=0;zkLocalNz;zk++) { - if(fg){ - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - } - f(bndry->x - bndry->bx,bndry->y, zk) = val; - - // Need to set remaining guard cells, as may be used for interpolation or upwinding derivatives - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + f(xi - 3*bndry->bx, yi - 3*bndry->by, zk); - } - } - } + } else if (loc == CELL_YLOW) { + // Shifted in Y + if (bndry->by > 0) { + // Upper y boundary boundary + for (; !bndry->isDone(); bndry->next1d()) { + BoutReal xnorm = localmesh->GlobalX(bndry->x); + BoutReal ynorm = 0.5 * (localmesh->GlobalY(bndry->y) + + localmesh->GlobalY(bndry->y - bndry->by)); + + for (int zk = 0; zk < nz; zk++) { + BoutReal znorm = BoutReal(zk) / BoutReal(nz); + generated_val = fg->generate(xnorm, TWOPI * ynorm, TWOPI * znorm, t); + + f(bndry->x, bndry->y, zk) = generated_val; + + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 1; i < bndry->width; i++) { + int xi = bndry->x; + int yi = bndry->y + i * bndry->by; + + f(xi, yi, zk) = + 2.0 * f(xi, yi - bndry->by, zk) - f(xi, yi - 2 * bndry->by, zk); + } + } + } + } else if (bndry->by < 0) { + // Lower y boundary. Set one point inwards + for (; !bndry->isDone(); bndry->next1d()) { + BoutReal xnorm = localmesh->GlobalX(bndry->x); + BoutReal ynorm = 0.5 * (localmesh->GlobalY(bndry->y) + + localmesh->GlobalY(bndry->y - bndry->by)); + + for (int zk = 0; zk < nz; zk++) { + BoutReal znorm = BoutReal(zk) / BoutReal(nz); + generated_val = fg->generate(xnorm, TWOPI * ynorm, TWOPI * znorm, t); + + f(bndry->x, bndry->y - bndry->by, zk) = generated_val; + + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 0; i < bndry->width; i++) { + int xi = bndry->x; + int yi = bndry->y + i * bndry->by; + + f(xi, yi, zk) = + 2 * f(xi, yi - bndry->by, zk) - f(xi, yi - 2 * bndry->by, zk); + } + } + } + } else if (bndry->bx != 0) { + // x boundaries + for (; !bndry->isDone(); bndry->next1d()) { + // x norm is located half way between first grid cell and guard cell. + // y norm is shifted by half a grid point because it is staggered. + BoutReal xnorm = 0.5 * (localmesh->GlobalX(bndry->x) + + localmesh->GlobalX(bndry->x - bndry->bx)); + BoutReal ynorm = + 0.5 * (localmesh->GlobalY(bndry->y) + localmesh->GlobalY(bndry->y - 1)); + + for (int zk = 0; zk < nz; zk++) { + BoutReal znorm = BoutReal(zk) / BoutReal(nz); + generated_val = fg->generate(xnorm, TWOPI * ynorm, TWOPI * znorm, t); + + f(bndry->x, bndry->y, zk) = + 2 * generated_val - f(bndry->x - bndry->bx, bndry->y - bndry->by, zk); + + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 1; i < bndry->width; i++) { + int xi = bndry->x + i * bndry->bx; + int yi = bndry->y; + + f(xi, yi, zk) = + 2 * f(xi - bndry->bx, yi, zk) - f(xi - 2 * bndry->bx, yi, zk); + } + } + } } - if(bndry->by != 0){ - //y boundaries - - for(; !bndry->isDone(); bndry->next1d()) { - // x norm is shifted by half a grid point because it is staggered. - // y norm is located half way between first grid cell and guard cell. - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - 1) ); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - bndry->by) ); - - for(int zk=0;zkLocalNz;zk++) { - if(fg) - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - - f(bndry->x,bndry->y,zk) = (8./3)*val - 2.*f(bndry->x-bndry->bx, bndry->y-bndry->by,zk) + f(bndry->x-2*bndry->bx, bndry->y-2*bndry->by,zk)/3.; - - // Need to set remaining guard cells, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + f(xi - 3*bndry->bx, yi - 3*bndry->by, zk); - } - } - } + } else if (loc == CELL_ZLOW) { + // Staggered in Z. Note there are no z-boundaries. + for (; !bndry->isDone(); bndry->next1d()) { + // Calculate the X and Y normalised values half-way between the guard cell and + // grid cell + BoutReal xnorm = + 0.5 * (localmesh->GlobalX(bndry->x) // In the guard cell + + localmesh->GlobalX(bndry->x - bndry->bx)); // the grid cell + + BoutReal ynorm = + 0.5 * (localmesh->GlobalY(bndry->y) // In the guard cell + + localmesh->GlobalY(bndry->y - bndry->by)); // the grid cell + + for (int zk = 0; zk < nz; zk++) { + // It shouldn't matter if znorm<0 because the expression in fg->generate should + // be periodic in z + BoutReal znorm = + (BoutReal(zk) - 0.5) / + BoutReal( + nz); // znorm is shifted by half a grid point because it is staggered + generated_val = fg->generate(xnorm, TWOPI * ynorm, TWOPI * znorm, t); + + f(bndry->x, bndry->y, zk) = + 2 * generated_val - f(bndry->x - bndry->bx, bndry->y - bndry->by, zk); + + // Need to set second guard cell, as may be used for interpolation or upwinding + // derivatives + for (int i = 1; i < bndry->width; i++) { + int xi = bndry->x + i * bndry->bx; + int yi = bndry->y; + + f(xi, yi, zk) = 2 * f(xi - bndry->bx, yi, zk) - f(xi - 2 * bndry->bx, yi, zk); + } + } } } - else if( loc == CELL_YLOW ) { - // Field is shifted in Y - - if(bndry->by > 0) { - // Upper y boundary - - for(; !bndry->isDone(); bndry->next1d()) { - BoutReal xnorm = mesh->GlobalX(bndry->x); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - bndry->by) ); - for(int zk=0;zkLocalNz;zk++) { - if(fg){ - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - } - f(bndry->x,bndry->y,zk) = val; - - // Need to set remaining guard cells, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + f(xi - 3*bndry->bx, yi - 3*bndry->by, zk); - } - } - } - } - if(bndry->by < 0) { - // Lower y boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - - BoutReal xnorm = mesh->GlobalX(bndry->x); - BoutReal ynorm = 0.5*(mesh->GlobalY(bndry->y)+ mesh->GlobalY(bndry->y - bndry->by) ); - - for(int zk=0;zkLocalNz;zk++) { - if(fg){ - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - } - f(bndry->x,bndry->y - bndry->by, zk) = val; - - // Need to set remaining guard cells, as may be used for interpolation or upwinding derivatives - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + f(xi - 3*bndry->bx, yi - 3*bndry->by, zk); - } - } - } + } else { + if (loc == CELL_CENTRE) { + // Unstaggered case + for (; !bndry->isDone(); bndry->next1d()) { + for (int zk = 0; zk < nz; zk++) { + f(bndry->x, bndry->y, zk) = + 2 * val - f(bndry->x - bndry->bx, bndry->y - bndry->by, zk); + + // We've set the first boundary point using extrapolation in + // the line above. The below block of code is attempting to + // set the rest of the boundary cells also using + // extrapolation. Whilst this choice doesn't impact 2nd order + // methods it has been observed that with higher order + // methods, which actually use these points, the use of + // extrapolation can be unstable. For this reason we have + // commented out the below block and replaced it with the loop + // several lines below, which just sets all the rest of the + // boundary points to be the specified value. We've not + // removed the commented out code as we may wish to revisit + // this in the future, however it may be that this is + // eventually removed. It can be noted that we *don't* apply + // this treatment for other boundary treatments, + // i.e. elsewhere we tend to extrapolate. + + // // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + // for(int i=1;ix + i*bndry->bx; + // int yi = bndry->y + i*bndry->by; + + // f(xi, yi, zk) = 2*f(xi - bndry->bx, yi - bndry->by, zk) - f(xi - + // 2*bndry->bx, yi - 2*bndry->by, zk); + // // f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - + // 2*bndry->bx, yi - 2*bndry->by, zk) + f(xi - 3*bndry->bx, yi - 3*bndry->by, + // zk); + + // } + } + + // This loop is our alternative approach to setting the rest of the boundary + // points. Instead of extrapolating we just use the generated values. This + // can help with the stability of higher order methods. + for (int i = 1; i < bndry->width; i++) { + // Set any other guard cells using the values on the cells + int xi = bndry->x + i * bndry->bx; + int yi = bndry->y + i * bndry->by; + for (int zk = 0; zk < nz; zk++) { + f(xi, yi, zk) = val; + } + } } - if(bndry->bx != 0){ - // x boundaries - for(; !bndry->isDone(); bndry->next1d()) { - // x norm is located half way between first grid cell and guard cell. - // y norm is shifted by half a grid point because it is staggered. - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - 1) ); - - for(int zk=0;zkLocalNz;zk++) { - if(fg) - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - - f(bndry->x,bndry->y,zk) = (8./3)*val - 2.*f(bndry->x-bndry->bx, bndry->y-bndry->by,zk) + f(bndry->x-2*bndry->bx, bndry->y-2*bndry->by,zk)/3.; - - // Need to set remaining guard cells, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + f(xi - 3*bndry->bx, yi - 3*bndry->by, zk); - } - } - } + } else if (loc == CELL_XLOW) { + // Field is shifted in X + if (bndry->bx > 0) { + // Outer x boundary + for (; !bndry->isDone(); bndry->next1d()) { + for (int zk = 0; zk < nz; zk++) { + f(bndry->x, bndry->y, zk) = val; + + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 1; i < bndry->width; i++) { + int xi = bndry->x + i * bndry->bx; + int yi = bndry->y; + + f(xi, yi, zk) = + 2 * f(xi - bndry->bx, yi, zk) - f(xi - 2 * bndry->bx, yi, zk); + } + } + } + } else if (bndry->bx < 0) { + // Inner x boundary. Set one point inwards + for (; !bndry->isDone(); bndry->next1d()) { + for (int zk = 0; zk < nz; zk++) { + f(bndry->x - bndry->bx, bndry->y, zk) = val; + + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 0; i < bndry->width; i++) { + int xi = bndry->x + i * bndry->bx; + int yi = bndry->y; + + f(xi, yi, zk) = + 2 * f(xi - bndry->bx, yi, zk) - f(xi - 2 * bndry->bx, yi, zk); + } + } + } + } else if (bndry->by != 0) { + // y boundaries + for (; !bndry->isDone(); bndry->next1d()) { + for (int zk = 0; zk < nz; zk++) { + f(bndry->x, bndry->y, zk) = + 2 * val - f(bndry->x - bndry->bx, bndry->y - bndry->by, zk); + + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 1; i < bndry->width; i++) { + int xi = bndry->x; + int yi = bndry->y + i * bndry->by; + + f(xi, yi, zk) = + 2 * f(xi, yi - bndry->by, zk) - f(xi, yi - 2 * bndry->by, zk); + } + } + } } - } - } - else { - // Standard (non-staggered) case - for(; !bndry->isDone(); bndry->next1d()) { - // Calculate the X and Y normalised values half-way between the guard cell and grid cell - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) // In the guard cell - + mesh->GlobalX(bndry->x - bndry->bx) ); // the grid cell - - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) // In the guard cell - + mesh->GlobalY(bndry->y - bndry->by) ); // the grid cell - - for(int zk=0;zkLocalNz;zk++) { - if(fg) - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - - f(bndry->x,bndry->y,zk) = (8./3)*val - 2.*f(bndry->x-bndry->bx, bndry->y-bndry->by,zk) + f(bndry->x-2*bndry->bx, bndry->y-2*bndry->by,zk)/3.; - - // Need to set remaining guard cells, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + f(xi - 3*bndry->bx, yi - 3*bndry->by, zk); - } + } else if (loc == CELL_YLOW) { + // Shifted in Y + if (bndry->by > 0) { + // Upper y boundary boundary + for (; !bndry->isDone(); bndry->next1d()) { + for (int zk = 0; zk < nz; zk++) { + f(bndry->x, bndry->y, zk) = val; + + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 1; i < bndry->width; i++) { + int xi = bndry->x; + int yi = bndry->y + i * bndry->by; + + f(xi, yi, zk) = + 2.0 * f(xi, yi - bndry->by, zk) - f(xi, yi - 2 * bndry->by, zk); + } + } + } + } else if (bndry->by < 0) { + // Lower y boundary. Set one point inwards + for (; !bndry->isDone(); bndry->next1d()) { + for (int zk = 0; zk < nz; zk++) { + f(bndry->x, bndry->y - bndry->by, zk) = val; + + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 0; i < bndry->width; i++) { + int xi = bndry->x; + int yi = bndry->y + i * bndry->by; + + f(xi, yi, zk) = + 2 * f(xi, yi - bndry->by, zk) - f(xi, yi - 2 * bndry->by, zk); + } + } + } + } else if (bndry->bx != 0) { + // x boundaries + for (; !bndry->isDone(); bndry->next1d()) { + for (int zk = 0; zk < nz; zk++) { + f(bndry->x, bndry->y, zk) = + 2 * val - f(bndry->x - bndry->bx, bndry->y - bndry->by, zk); + + // Need to set second guard cell, as may be used for interpolation or + // upwinding derivatives + for (int i = 1; i < bndry->width; i++) { + int xi = bndry->x + i * bndry->bx; + int yi = bndry->y; + + f(xi, yi, zk) = + 2 * f(xi - bndry->bx, yi, zk) - f(xi - 2 * bndry->bx, yi, zk); + } + } + } } - } - } -} - -void BoundaryDirichlet_O3::apply_ddt(Field2D &f) { - Field2D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - (*dt)(bndry->x,bndry->y) = 0.; // Set time derivative to zero -} + } else if (loc == CELL_ZLOW) { + // Staggered in Z. Note there are no z-boundaries. + for (; !bndry->isDone(); bndry->next1d()) { + for (int zk = 0; zk < nz; zk++) { + f(bndry->x, bndry->y, zk) = + 2 * val - f(bndry->x - bndry->bx, bndry->y - bndry->by, zk); -void BoundaryDirichlet_O3::apply_ddt(Field3D &f) { - Field3D *dt = f.timeDeriv(); + // Need to set second guard cell, as may be used for interpolation or upwinding + // derivatives + for (int i = 1; i < bndry->width; i++) { + int xi = bndry->x + i * bndry->bx; + int yi = bndry->y; - bndry->first() ; - for(bndry->first(); !bndry->isDone(); bndry->next()){ - for(int z=0;zLocalNz;z++){ - (*dt)(bndry->x,bndry->y,z) = 0.; // Set time derivative to zero + f(xi, yi, zk) = 2 * f(xi - bndry->bx, yi, zk) - f(xi - 2 * bndry->bx, yi, zk); + } + } + } } } } /////////////////////////////////////////////////////////////// -// Extrapolate to calculate boundary cell to 4th-order +// New implementation, accurate to higher order -BoundaryOp* BoundaryDirichlet_O4::clone(BoundaryRegion *region, const list &args){ - verifyNumPoints(region,3); - std::shared_ptr newgen = nullptr; - if(!args.empty()) { - // First argument should be an expression - newgen = FieldFactory::get()->parse(args.front()); - } - return new BoundaryDirichlet_O4(region, newgen); +BoundaryOp * +BoundaryDirichlet_O3::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + return boundaryClone(region, args, keywords); } -void BoundaryDirichlet_O4::apply(Field2D &f){ - BoundaryDirichlet_O4::apply(f,0.); +inline void BoundaryDirichlet_O3::applyAtPoint(Field2D &f, BoutReal val, int x, int bx, + int y, int by, int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = + (8. / 3) * val - 2. * f(x - bx, y - by, z) + f(x - 2 * bx, y - 2 * by, z) / 3.; } - -void BoundaryDirichlet_O4::apply(Field2D &f,BoutReal t) { - // Set (at 2nd order) the value at the mid-point between the guard cell and the grid cell to be val - // N.B. Only first guard cells (closest to the grid) should ever be used - - bndry->first(); - - // Decide which generator to use - std::shared_ptr fg = gen; - if(!fg) - fg = f.getBndryGenerator(bndry->location); - - BoutReal val = 0.0; - - - // Check for staggered grids - - CELL_LOC loc = f.getLocation(); - if(mesh->StaggerGrids && loc != CELL_CENTRE) { - // Staggered. Need to apply slightly differently - - if(loc == CELL_XLOW ) { - // Field is shifted in X - - if(bndry->bx > 0) { - // Outer x boundary - - for(; !bndry->isDone(); bndry->next1d()) { - if(fg) { - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = mesh->GlobalY(bndry->y); - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); - } - f(bndry->x,bndry->y) = val; - - // Need to set remaining guard cells, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 4.0*f(xi - bndry->bx, yi - bndry->by) - 6.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) - + 4.0*f(xi - 3*bndry->bx, yi - 3*bndry->by) - f(xi - 4*bndry->bx, yi - 4*bndry->by); - } - } - } - - if(bndry->bx < 0) { - // Inner boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - if(fg) { - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) - + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = mesh->GlobalY(bndry->y); - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); - } - - f(bndry->x - bndry->bx,bndry->y) = val; - - // Need to set remaining guard cells, as may be used for interpolation or upwinding derivatives - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 4.0*f(xi - bndry->bx, yi - bndry->by) - 6.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) - + 4.0*f(xi - 3*bndry->bx, yi - 3*bndry->by) - f(xi - 4*bndry->bx, yi - 4*bndry->by); - } - } - } - if (bndry->by != 0){ - // y boundaries - for(; !bndry->isDone(); bndry->next1d()) { - - if(fg) { - // x norm is shifted by half a grid point because it is staggered. - // y norm is located half way between first grid cell and guard cell. - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - 1) ); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - bndry->by) ); - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); - } - - f(bndry->x,bndry->y) = (16./5)*val - 3.*f(bndry->x-bndry->bx, bndry->y-bndry->by) + f(bndry->x-2*bndry->bx, bndry->y-2*bndry->by) - (1./5)*f(bndry->x-3*bndry->bx, bndry->y-3*bndry->by); - - // Need to set remaining guard cells, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 4.0*f(xi - bndry->bx, yi - bndry->by) - 6.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) - + 4.0*f(xi - 3*bndry->bx, yi - 3*bndry->by) - f(xi - 4*bndry->bx, yi - 4*bndry->by); - } - } - } - } - else if( loc == CELL_YLOW ) { - // Field is shifted in Y - - if(bndry->by > 0) { - // Outer y boundary - - for(; !bndry->isDone(); bndry->next1d()) { - if(fg) { - BoutReal xnorm = mesh->GlobalX(bndry->x); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) - + mesh->GlobalY(bndry->y - bndry->by) ); - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); - } - f(bndry->x,bndry->y) = val; - - // Need to set remaining guard cells, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 4.0*f(xi - bndry->bx, yi - bndry->by) - 6.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) - + 4.0*f(xi - 3*bndry->bx, yi - 3*bndry->by) - f(xi - 4*bndry->bx, yi - 4*bndry->by); - } - } - } - if(bndry->by < 0) { - // Inner y boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - if(fg) { - BoutReal xnorm = mesh->GlobalX(bndry->x); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) - + mesh->GlobalY(bndry->y - bndry->by) ); - - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); - } - - f(bndry->x,bndry->y - bndry->by) = val; - - // Need to set remaining guard cells, as may be used for interpolation or upwinding derivatives - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 4.0*f(xi - bndry->bx, yi - bndry->by) - 6.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) - + 4.0*f(xi - 3*bndry->bx, yi - 3*bndry->by) - f(xi - 4*bndry->bx, yi - 4*bndry->by); - } - } - } - if(bndry->bx !=0){ - // x boundaries. - - for(; !bndry->isDone(); bndry->next1d()) { - - if(fg) { - // x norm is located half way between first grid cell and guard cell. - // y norm is shifted by half a grid point because it is staggered. - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - 1) ); - - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); - } - - f(bndry->x,bndry->y) = (16./5)*val - 3.*f(bndry->x-bndry->bx, bndry->y-bndry->by) + f(bndry->x-2*bndry->bx, bndry->y-2*bndry->by) - (1./5)*f(bndry->x-3*bndry->bx, bndry->y-3*bndry->by); - - // Need to set remaining guard cells, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 4.0*f(xi - bndry->bx, yi - bndry->by) - 6.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) - + 4.0*f(xi - 3*bndry->bx, yi - 3*bndry->by) - f(xi - 4*bndry->bx, yi - 4*bndry->by); - } - } - } - } - } - else { - // Non-staggered, standard case - - for(; !bndry->isDone(); bndry->next1d()) { - - if(fg) { - // Calculate the X and Y normalised values half-way between the guard cell and grid cell - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) // In the guard cell - + mesh->GlobalX(bndry->x - bndry->bx) ); // the grid cell - - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) // In the guard cell - + mesh->GlobalY(bndry->y - bndry->by) ); // the grid cell - - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t); - } - - f(bndry->x,bndry->y) = (16./5)*val - 3.*f(bndry->x-bndry->bx, bndry->y-bndry->by) + f(bndry->x-2*bndry->bx, bndry->y-2*bndry->by) - (1./5)*f(bndry->x-3*bndry->bx, bndry->y-3*bndry->by); - - // Need to set remaining guard cells, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 4.0*f(xi - bndry->bx, yi - bndry->by) - 6.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) - + 4.0*f(xi - 3*bndry->bx, yi - 3*bndry->by) - f(xi - 4*bndry->bx, yi - 4*bndry->by); - } - } - } +inline void BoundaryDirichlet_O3::applyAtPoint(Field3D &f, BoutReal val, int x, int bx, + int y, int by, int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = + (8. / 3) * val - 2. * f(x - bx, y - by, z) + f(x - 2 * bx, y - 2 * by, z) / 3.; } +inline void BoundaryDirichlet_O3::applyAtPointStaggered(Field2D &f, BoutReal val, int x, + int UNUSED(bx), int y, + int UNUSED(by), int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = val; +} +inline void BoundaryDirichlet_O3::applyAtPointStaggered(Field3D &f, BoutReal val, int x, + int UNUSED(bx), int y, + int UNUSED(by), int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = val; +} -void BoundaryDirichlet_O4::apply(Field3D &f) { - BoundaryDirichlet_O4::apply(f,0.); +inline void BoundaryDirichlet_O3::extrapolateFurther(Field2D &f, int x, int bx, int y, + int by, int z) { + extrapolate3rd(f, x, bx, y, by, z); +} +inline void BoundaryDirichlet_O3::extrapolateFurther(Field3D &f, int x, int bx, int y, + int by, int z) { + extrapolate3rd(f, x, bx, y, by, z); } +/////////////////////////////////////////////////////////////// +// Extrapolate to calculate boundary cell to 4th-order -void BoundaryDirichlet_O4::apply(Field3D &f,BoutReal t) { - // Set (at 2nd order) the value at the mid-point between the guard cell and the grid cell to be val - // N.B. Only first guard cells (closest to the grid) should ever be used +BoundaryOp * +BoundaryDirichlet_O4::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + return boundaryClone(region, args, keywords); +} - bndry->first(); +inline void BoundaryDirichlet_O4::applyAtPoint(Field2D &f, BoutReal val, int x, int bx, + int y, int by, int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = (16. / 5) * val - 3. * f(x - bx, y - by, z) + + f(x - 2 * bx, y - 2 * by, z) - (1. / 5) * f(x - 3 * bx, y - 3 * by, z); +} +inline void BoundaryDirichlet_O4::applyAtPoint(Field3D &f, BoutReal val, int x, int bx, + int y, int by, int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = (16. / 5) * val - 3. * f(x - bx, y - by, z) + + f(x - 2 * bx, y - 2 * by, z) - (1. / 5) * f(x - 3 * bx, y - 3 * by, z); +} - // Decide which generator to use - std::shared_ptr fg = gen; - if(!fg) - fg = f.getBndryGenerator(bndry->location); +inline void BoundaryDirichlet_O4::applyAtPointStaggered(Field2D &f, BoutReal val, int x, + int UNUSED(bx), int y, + int UNUSED(by), int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = val; +} +inline void BoundaryDirichlet_O4::applyAtPointStaggered(Field3D &f, BoutReal val, int x, + int UNUSED(bx), int y, + int UNUSED(by), int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = val; +} - BoutReal val = 0.0; +inline void BoundaryDirichlet_O4::extrapolateFurther(Field2D &f, int x, int bx, int y, + int by, int z) { + extrapolate4th(f, x, bx, y, by, z); +} +inline void BoundaryDirichlet_O4::extrapolateFurther(Field3D &f, int x, int bx, int y, + int by, int z) { + extrapolate4th(f, x, bx, y, by, z); +} - // Check for staggered grids - - CELL_LOC loc = f.getLocation(); - if(mesh->StaggerGrids && loc != CELL_CENTRE) { - // Staggered. Need to apply slightly differently - - if( loc == CELL_XLOW ) { - // Field is shifted in X - - if(bndry->bx > 0) { - // Outer x boundary - - for(; !bndry->isDone(); bndry->next1d()) { - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) - + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = mesh->GlobalY(bndry->y); - - for(int zk=0;zkLocalNz;zk++) { - if(fg){ - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - } - f(bndry->x,bndry->y, zk) = val; - - // Need to set remaining guard cells, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 4.0*f(xi - bndry->bx, yi - bndry->by, zk) - 6.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + 4.0*f(xi - 3*bndry->bx, yi - 3*bndry->by, zk) - f(xi - 4*bndry->bx, yi - 4*bndry->by, zk); - } - } - } - } - if(bndry->bx < 0) { - // Inner x boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) - + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = mesh->GlobalY(bndry->y); - - for(int zk=0;zkLocalNz;zk++) { - if(fg) - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - - f(bndry->x - bndry->bx,bndry->y, zk) = val; - - // Need to set remaining guard cells, as may be used for interpolation or upwinding derivatives - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 4.0*f(xi - bndry->bx, yi - bndry->by, zk) - 6.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + 4.0*f(xi - 3*bndry->bx, yi - 3*bndry->by, zk) - f(xi - 4*bndry->bx, yi - 4*bndry->by, zk); - } - } - } - } - if (bndry->by != 0){ - // y boundaries +/////////////////////////////////////////////////////////////// - for(; !bndry->isDone(); bndry->next1d()) { - // x norm is shifted by half a grid point because it is staggered. - // y norm is located half way between first grid cell and guard cell. - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - 1) ); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - bndry->by) ); +BoundaryOp * +BoundaryDirichlet_smooth::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + return boundaryClone(region, args, keywords); +} - for(int zk=0;zkLocalNz;zk++) { - if(fg) { - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - } - f(bndry->x,bndry->y,zk) = (16./5)*val - 3.*f(bndry->x-bndry->bx, bndry->y-bndry->by,zk) + f(bndry->x-2*bndry->bx, bndry->y-2*bndry->by,zk) - (1./5)*f(bndry->x-3*bndry->bx, bndry->y-3*bndry->by,zk); - - // Need to set remaining guard cells, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 4.0*f(xi - bndry->bx, yi - bndry->by, zk) - 6.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + 4.0*f(xi - 3*bndry->bx, yi - 3*bndry->by, zk) - f(xi - 4*bndry->bx, yi - 4*bndry->by, zk); - } - } - } - } - } - else if( loc == CELL_YLOW ) { - // Y boundary, and field is shifted in Y - - if(bndry->by > 0) { - // Outer y boundary - - for(; !bndry->isDone(); bndry->next1d()) { - BoutReal xnorm = mesh->GlobalX(bndry->x); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) - + mesh->GlobalY(bndry->y - bndry->by) ); - for(int zk=0;zkLocalNz;zk++) { - if(fg) - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - - f(bndry->x,bndry->y,zk) = val; - - // Need to set remaining guard cells, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 4.0*f(xi - bndry->bx, yi - bndry->by, zk) - 6.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + 4.0*f(xi - 3*bndry->bx, yi - 3*bndry->by, zk) - f(xi - 4*bndry->bx, yi - 4*bndry->by, zk); - } - } - } - } - if(bndry->by < 0) { - // Inner y boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - - BoutReal xnorm = mesh->GlobalX(bndry->x); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) - + mesh->GlobalY(bndry->y - bndry->by) ); - - for(int zk=0;zkLocalNz;zk++) { - if(fg) - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - - f(bndry->x,bndry->y - bndry->by, zk) = val; - - // Need to set remaining guard cells, as may be used for interpolation or upwinding derivatives - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 4.0*f(xi - bndry->bx, yi - bndry->by, zk) - 6.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + 4.0*f(xi - 3*bndry->bx, yi - 3*bndry->by, zk) - f(xi - 4*bndry->bx, yi - 4*bndry->by, zk); - } - } - } - } - if(bndry->bx !=0){ - // x boundaries - - for(; !bndry->isDone(); bndry->next1d()) { - // x norm is located half way between first grid cell and guard cell. - // y norm is shifted by half a grid point because it is staggered. - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - 1) ); - - for(int zk=0;zkLocalNz;zk++) { - if(fg) - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - - f(bndry->x,bndry->y,zk) = (16./5)*val - 3.*f(bndry->x-bndry->bx, bndry->y-bndry->by,zk) + f(bndry->x-2*bndry->bx, bndry->y-2*bndry->by,zk) - (1./5)*f(bndry->x-3*bndry->bx, bndry->y-3*bndry->by,zk); - - // Need to set remaining guard cells, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 4.0*f(xi - bndry->bx, yi - bndry->by, zk) - 6.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + 4.0*f(xi - 3*bndry->bx, yi - 3*bndry->by, zk) - f(xi - 4*bndry->bx, yi - 4*bndry->by, zk); - } - } - } - } - } - } - else { - // Standard (non-staggered) case - for(; !bndry->isDone(); bndry->next1d()) { - // Calculate the X and Y normalised values half-way between the guard cell and grid cell - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) // In the guard cell - + mesh->GlobalX(bndry->x - bndry->bx) ); // the grid cell - - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) // In the guard cell - + mesh->GlobalY(bndry->y - bndry->by) ); // the grid cell - - for(int zk=0;zkLocalNz;zk++) { - if(fg) - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz), t); - - f(bndry->x,bndry->y,zk) = (16./5)*val - 3.*f(bndry->x-bndry->bx, bndry->y-bndry->by,zk) + f(bndry->x-2*bndry->bx, bndry->y-2*bndry->by,zk) - (1./5)*f(bndry->x-3*bndry->bx, bndry->y-3*bndry->by,zk); - - // Need to set remaining guard cells, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 4.0*f(xi - bndry->bx, yi - bndry->by, zk) - 6.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + 4.0*f(xi - 3*bndry->bx, yi - 3*bndry->by, zk) - f(xi - 4*bndry->bx, yi - 4*bndry->by, zk); - } - } - } - } +inline void BoundaryDirichlet_smooth::applyAtPoint(Field2D &f, BoutReal val, int x, + int bx, int y, int by, int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = + 5. / 3. * val - 0.5 * f(x - bx, y - by, z) - 1. / 6. * f(x - 2 * bx, y - 2 * by, z); +} +inline void BoundaryDirichlet_smooth::applyAtPoint(Field3D &f, BoutReal val, int x, + int bx, int y, int by, int z, + BoutReal UNUSED(delta)) { + // Dirichlet bc using val and first grid point would be + // fb = 2*val - f0 + // using val and second grid point would be + // fb = 4/3*val - 1/3*f1 + // Here we apply the bc using the average of the two, to try and suppress + // grid-scale oscillations at the boundary + f(x, y, z) = + 5. / 3. * val - 0.5 * f(x - bx, y - by, z) - 1. / 6. * f(x - 2 * bx, y - 2 * by, z); } -void BoundaryDirichlet_O4::apply_ddt(Field2D &f) { - Field2D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - (*dt)(bndry->x,bndry->y) = 0.; // Set time derivative to zero +inline void BoundaryDirichlet_smooth::applyAtPointStaggered(Field2D &f, BoutReal val, + int x, int UNUSED(bx), int y, + int UNUSED(by), int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = val; +} +inline void BoundaryDirichlet_smooth::applyAtPointStaggered(Field3D &f, BoutReal val, + int x, int UNUSED(bx), int y, + int UNUSED(by), int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = val; } -void BoundaryDirichlet_O4::apply_ddt(Field3D &f) { - Field3D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - for(int z=0;zLocalNz;z++) - (*dt)(bndry->x,bndry->y,z) = 0.; // Set time derivative to zero +inline void BoundaryDirichlet_smooth::extrapolateFurther(Field2D &f, int x, int bx, int y, + int by, int z) { + extrapolate2nd(f, x, bx, y, by, z); +} +inline void BoundaryDirichlet_smooth::extrapolateFurther(Field3D &f, int x, int bx, int y, + int by, int z) { + extrapolate2nd(f, x, bx, y, by, z); } /////////////////////////////////////////////////////////////// -BoundaryOp* BoundaryDirichlet_2ndOrder::clone(BoundaryRegion *region, const list &args) { +BoundaryOp * +BoundaryDirichlet_2ndOrder::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { output << "WARNING: Use of boundary condition \"dirichlet_2ndorder\" is deprecated!\n"; output << " Consider using \"dirichlet\" instead\n"; - verifyNumPoints(region,2); - if(!args.empty()) { - // First argument should be a value - val = stringToReal(args.front()); - return new BoundaryDirichlet_2ndOrder(region, val); - } - return new BoundaryDirichlet_2ndOrder(region); + return boundaryClone(region, args, keywords); } -void BoundaryDirichlet_2ndOrder::apply(Field2D &f) { - // Set (at 2nd order) the value at the mid-point between the guard cell and the grid cell to be val - // N.B. Only first guard cells (closest to the grid) should ever be used - for(bndry->first(); !bndry->isDone(); bndry->next1d()) { - f(bndry->x,bndry->y) = 8./3.*val - 2.*f(bndry->x-bndry->bx,bndry->y-bndry->by) + 1./3.*f(bndry->x-2*bndry->bx,bndry->y-2*bndry->by); -#ifdef BOUNDARY_CONDITIONS_UPGRADE_EXTRAPOLATE_FOR_2ND_ORDER - f(bndry->x+bndry->bx,bndry->y+bndry->by) = 3.*f(bndry->x,bndry->y) - 3.*f(bndry->x-bndry->bx,bndry->y-bndry->by) + f(bndry->x-2*bndry->bx,bndry->y-2*bndry->by); -#elif defined(CHECK) - f(bndry->x+bndry->bx,bndry->y+bndry->by) = 1.e60; -#endif - } +// Set (at 2nd order) the value at the mid-point between the guard cell and the grid cell +// to be val +// N.B. Only first guard cells (closest to the grid) should ever be used +inline void BoundaryDirichlet_2ndOrder::applyAtPoint(Field2D &f, BoutReal val, int x, + int bx, int y, int by, int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = + 8. / 3. * val - 2. * f(x - bx, y - by, z) + 1. / 3. * f(x - 2 * bx, y - 2 * by, z); } - -void BoundaryDirichlet_2ndOrder::apply(Field3D &f) { - // Set (at 2nd order) the value at the mid-point between the guard cell and the grid cell to be val - // N.B. Only first guard cells (closest to the grid) should ever be used - for(bndry->first(); !bndry->isDone(); bndry->next1d()) - for(int z=0;zLocalNz;z++) { - f(bndry->x,bndry->y,z) = 8./3.*val - 2.*f(bndry->x-bndry->bx,bndry->y-bndry->by,z) + 1./3.*f(bndry->x-2*bndry->bx,bndry->y-2*bndry->by,z); -#ifdef BOUNDARY_CONDITIONS_UPGRADE_EXTRAPOLATE_FOR_2ND_ORDER - f(bndry->x+bndry->bx,bndry->y+bndry->by,z) = 3.*f(bndry->x,bndry->y,z) - 3.*f(bndry->x-bndry->bx,bndry->y-bndry->by,z) + f(bndry->x-2*bndry->bx,bndry->y-2*bndry->by,z); -#elif defined(CHECK) - f(bndry->x+bndry->bx,bndry->y+bndry->by,z) = 1.e60; -#endif - } +inline void BoundaryDirichlet_2ndOrder::applyAtPoint(Field3D &f, BoutReal val, int x, + int bx, int y, int by, int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = + 8. / 3. * val - 2. * f(x - bx, y - by, z) + 1. / 3. * f(x - 2 * bx, y - 2 * by, z); } -void BoundaryDirichlet_2ndOrder::apply_ddt(Field2D &f) { - Field2D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - (*dt)(bndry->x,bndry->y) = 0.; // Set time derivative to zero +inline void BoundaryDirichlet_2ndOrder::applyAtPointStaggered(Field2D &f, BoutReal val, + int x, int UNUSED(bx), + int y, int UNUSED(by), + int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = val; +} +inline void BoundaryDirichlet_2ndOrder::applyAtPointStaggered(Field3D &f, BoutReal val, + int x, int UNUSED(bx), + int y, int UNUSED(by), + int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = val; } -void BoundaryDirichlet_2ndOrder::apply_ddt(Field3D &f) { - Field3D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - for(int z=0;zLocalNz;z++) - (*dt)(bndry->x,bndry->y,z) = 0.; // Set time derivative to zero +inline void BoundaryDirichlet_2ndOrder::extrapolateFurther(Field2D &f, int x, int bx, + int y, int by, int z) { + extrapolate2nd(f, x, bx, y, by, z); +} +inline void BoundaryDirichlet_2ndOrder::extrapolateFurther(Field3D &f, int x, int bx, + int y, int by, int z) { + extrapolate2nd(f, x, bx, y, by, z); } /////////////////////////////////////////////////////////////// -BoundaryOp* BoundaryDirichlet_4thOrder::clone(BoundaryRegion *region, const list &args) { - verifyNumPoints(region,4); - if(!args.empty()) { - // First argument should be a value - val = stringToReal(args.front()); - return new BoundaryDirichlet_4thOrder(region, val); - } - return new BoundaryDirichlet_4thOrder(region); +BoundaryOp * +BoundaryDirichlet_O5::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + return boundaryClone(region, args, keywords); } -void BoundaryDirichlet_4thOrder::apply(Field2D &f) { - // Set (at 4th order) the value at the mid-point between the guard cell and the grid cell to be val - for(bndry->first(); !bndry->isDone(); bndry->next1d()) { - f(bndry->x,bndry->y) = 128./35.*val - 4.*f(bndry->x-bndry->bx,bndry->y-bndry->by) + 2.*f(bndry->x-2*bndry->bx,bndry->y-2*bndry->by) - 4./3.*f(bndry->x-3*bndry->bx,bndry->y-3*bndry->by) + 1./7.*f(bndry->x-4*bndry->bx,bndry->y-4*bndry->by); - f(bndry->x+bndry->bx,bndry->y+bndry->by) = -128./5.*val + 9.*f(bndry->x,bndry->y) + 18.*f(bndry->x-bndry->bx,bndry->y-bndry->by) -4.*f(bndry->x-2*bndry->bx,bndry->y-2*bndry->by) + 3./5.*f(bndry->x-3*bndry->bx,bndry->y-3*bndry->by); - } +inline void BoundaryDirichlet_O5::applyAtPoint(Field2D &f, BoutReal val, int x, int bx, + int y, int by, int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = + 128. / 35. * val - 4. * f(x - bx, y - by, z) + 2. * f(x - 2 * bx, y - 2 * by, z) - + 4. / 5. * f(x - 3 * bx, y - 3 * by, z) + 1. / 7. * f(x - 4 * bx, y - 4 * by, z); } - -void BoundaryDirichlet_4thOrder::apply(Field3D &f) { - // Set (at 4th order) the value at the mid-point between the guard cell and the grid cell to be val - for(bndry->first(); !bndry->isDone(); bndry->next1d()) - for(int z=0;zLocalNz;z++) { - f(bndry->x,bndry->y,z) = 128./35.*val - 4.*f(bndry->x-bndry->bx,bndry->y-bndry->by,z) + 2.*f(bndry->x-2*bndry->bx,bndry->y-2*bndry->by,z) - 4./3.*f(bndry->x-3*bndry->bx,bndry->y-3*bndry->by,z) + 1./7.*f(bndry->x-4*bndry->bx,bndry->y-4*bndry->by,z); - f(bndry->x+bndry->bx,bndry->y+bndry->by,z) = -128./5.*val + 9.*f(bndry->x,bndry->y,z) + 18.*f(bndry->x-bndry->bx,bndry->y-bndry->by,z) -4.*f(bndry->x-2*bndry->bx,bndry->y-2*bndry->by,z) + 3./5.*f(bndry->x-3*bndry->bx,bndry->y-3*bndry->by,z); - } +inline void BoundaryDirichlet_O5::applyAtPoint(Field3D &f, BoutReal val, int x, int bx, + int y, int by, int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = + 128. / 35. * val - 4. * f(x - bx, y - by, z) + 2. * f(x - 2 * bx, y - 2 * by, z) - + 4. / 5. * f(x - 3 * bx, y - 3 * by, z) + 1. / 7. * f(x - 4 * bx, y - 4 * by, z); } -void BoundaryDirichlet_4thOrder::apply_ddt(Field2D &f) { - Field2D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - (*dt)(bndry->x,bndry->y) = 0.; // Set time derivative to zero +inline void BoundaryDirichlet_O5::applyAtPointStaggered(Field2D &f, BoutReal val, int x, + int UNUSED(bx), int y, + int UNUSED(by), int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = val; +} +inline void BoundaryDirichlet_O5::applyAtPointStaggered(Field3D &f, BoutReal val, int x, + int UNUSED(bx), int y, + int UNUSED(by), int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = val; } -void BoundaryDirichlet_4thOrder::apply_ddt(Field3D &f) { - Field3D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - for(int z=0;zLocalNz;z++) - (*dt)(bndry->x,bndry->y,z) = 0.; // Set time derivative to zero +inline void BoundaryDirichlet_O5::extrapolateFurther(Field2D &f, int x, int bx, int y, + int by, int z) { + // Changing this extrapolation to not depend on val, so just using grid point + // values. JTO 16/10/2018 + extrapolate5th(f, x, bx, y, by, z); +} +inline void BoundaryDirichlet_O5::extrapolateFurther(Field3D &f, int x, int bx, int y, + int by, int z) { + // Changing this extrapolation to not depend on val, so just using grid point + // values. Not sure if this is the correct order... JTO 16/10/2018 + extrapolate5th(f, x, bx, y, by, z); } /////////////////////////////////////////////////////////////// -BoundaryOp* BoundaryNeumann_NonOrthogonal::clone(BoundaryRegion *region, const list &args) { - verifyNumPoints(region,1); - if(!args.empty()) { - output << "WARNING: arguments is set to BoundaryNeumann None Zero Gradient\n"; +BoundaryOp * +BoundaryNeumann_NonOrthogonal::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + verifyNumPoints(region, 1); + if (!args.empty()) { + output << "WARNING: argument is set to BoundaryNeumann_NonOrthogonal\n"; // First argument should be a value - val = stringToReal(args.front()); - return new BoundaryNeumann_NonOrthogonal(region, val); + BoutReal val_in = stringToReal(args.front()); + return new BoundaryNeumann_NonOrthogonal(region, val_in); } - return new BoundaryNeumann_NonOrthogonal(region); -} - -void BoundaryNeumann_NonOrthogonal::apply(Field2D &f) { - Coordinates *metric = f.getCoordinates(); - // Calculate derivatives for metric use - mesh->communicate(f); - Field2D dfdy = DDY(f); - // Loop over all elements and set equal to the next point in - for(bndry->first(); !bndry->isDone(); bndry->next1d()) { - // Interpolate (linearly) metrics to halfway between last cell and boundary cell - BoutReal g11shift = 0.5*(metric->g11(bndry->x,bndry->y) + metric->g11(bndry->x-bndry->bx,bndry->y)); - BoutReal g12shift = 0.5*(metric->g12(bndry->x,bndry->y) + metric->g12(bndry->x-bndry->bx,bndry->y)); - // Have to use derivatives at last gridpoint instead of derivatives on boundary layer - // because derivative values don't exist in boundary region - // NOTE: should be fixed to interpolate to boundary line - BoutReal xshift = g12shift*dfdy(bndry->x-bndry->bx,bndry->y); - - if(bndry->bx != 0 && bndry->by == 0) { - // x boundaries only - BoutReal delta = bndry->bx*metric->dx(bndry->x, bndry->y); - f(bndry->x, bndry->y) = f(bndry->x - bndry->bx, bndry->y) + delta/g11shift*(val - xshift); - if (bndry->bx == 2){ - f(bndry->x + bndry->bx, bndry->y) = f(bndry->x - 2*bndry->bx, bndry->y) + 3.0*delta/g11shift*(val - xshift); - } - } else if(bndry->by != 0 && bndry->bx == 0) { - // y boundaries only - // no need to shift this b/c we want parallel nuemann not theta - BoutReal delta = bndry->by*metric->dy(bndry->x, bndry->y); - f(bndry->x, bndry->y) = f(bndry->x, bndry->y - bndry->by) + delta*val; - if (bndry->width == 2){ - f(bndry->x, bndry->y + bndry->by) = f(bndry->x, bndry->y - 2*bndry->by) + 3.0*delta*val; - } - } else { - // set corners to zero - f(bndry->x, bndry->y) = 0.0; - if (bndry->width == 2){ - f(bndry->x + bndry->bx, bndry->y + bndry->by) = 0.0; - } - } + if (!keywords.empty()) { + // Given keywords, but not using + throw BoutException("Keywords ignored in boundary : %s", + keywords.begin()->first.c_str()); } + return new BoundaryNeumann_NonOrthogonal(region); } -void BoundaryNeumann_NonOrthogonal::apply(Field3D &f) { +template +void BoundaryNeumann_NonOrthogonal::applyTemplate(T &f, BoutReal UNUSED(t)) { + Mesh *localmesh = f.getMesh(); Coordinates *metric = f.getCoordinates(); // Calculate derivatives for metric use - mesh->communicate(f); - Field3D dfdy = DDY(f); - Field3D dfdz = DDZ(f); + localmesh->communicate(f); + T dfdy = DDY(f); + T dfdz = DDZ(f); // Loop over all elements and set equal to the next point in - for(bndry->first(); !bndry->isDone(); bndry->next1d()) { + for (bndry->first(); !bndry->isDone(); bndry->next1d()) { // Interpolate (linearly) metrics to halfway between last cell and boundary cell - BoutReal g11shift = 0.5*(metric->g11(bndry->x,bndry->y) + metric->g11(bndry->x-bndry->bx,bndry->y)); - BoutReal g12shift = 0.5*(metric->g12(bndry->x,bndry->y) + metric->g12(bndry->x-bndry->bx,bndry->y)); - BoutReal g13shift = 0.5*(metric->g13(bndry->x,bndry->y) + metric->g13(bndry->x-bndry->bx,bndry->y)); + BoutReal g11shift = 0.5 * (metric->g11(bndry->x, bndry->y) + + metric->g11(bndry->x - bndry->bx, bndry->y)); + BoutReal g12shift = 0.5 * (metric->g12(bndry->x, bndry->y) + + metric->g12(bndry->x - bndry->bx, bndry->y)); + BoutReal g13shift = 0.5 * (metric->g13(bndry->x, bndry->y) + + metric->g13(bndry->x - bndry->bx, bndry->y)); // Have to use derivatives at last gridpoint instead of derivatives on boundary layer // because derivative values don't exist in boundary region // NOTE: should be fixed to interpolate to boundary line - for(int z=0;zLocalNz;z++) { - BoutReal xshift = g12shift*dfdy(bndry->x-bndry->bx,bndry->y,z) - + g13shift*dfdz(bndry->x-bndry->bx,bndry->y,z); - if(bndry->bx != 0 && bndry->by == 0) { + for (int z = 0; z < localmesh->LocalNz; z++) { + BoutReal xshift = g12shift * dfdy(bndry->x - bndry->bx, bndry->y, z) + + g13shift * dfdz(bndry->x - bndry->bx, bndry->y, z); + if (bndry->bx != 0 && bndry->by == 0) { // x boundaries only - BoutReal delta = bndry->bx*metric->dx(bndry->x, bndry->y); - f(bndry->x, bndry->y, z) = f(bndry->x - bndry->bx, bndry->y, z) + delta/g11shift*(val - xshift); - if (bndry->width == 2){ - f(bndry->x + bndry->bx, bndry->y, z) = f(bndry->x - 2*bndry->bx, bndry->y, z) + 3.0*delta/g11shift*(val - xshift); + BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y); + f(bndry->x, bndry->y, z) = + f(bndry->x - bndry->bx, bndry->y, z) + delta / g11shift * (val - xshift); + if (bndry->width == 2) { + f(bndry->x + bndry->bx, bndry->y, z) = + f(bndry->x - 2 * bndry->bx, bndry->y, z) + + 3.0 * delta / g11shift * (val - xshift); } - } else if(bndry->by != 0 && bndry->bx == 0) { + } else if (bndry->by != 0 && bndry->bx == 0) { // y boundaries only // no need to shift this b/c we want parallel nuemann not theta - BoutReal delta = bndry->by*metric->dy(bndry->x, bndry->y); - f(bndry->x, bndry->y, z) = f(bndry->x, bndry->y - bndry->by, z) + delta*val; - if (bndry->width == 2){ - f(bndry->x, bndry->y + bndry->by, z) = f(bndry->x, bndry->y - 2*bndry->by, z) + 3.0*delta*val; + BoutReal delta = bndry->by * metric->dy(bndry->x, bndry->y); + f(bndry->x, bndry->y, z) = f(bndry->x, bndry->y - bndry->by, z) + delta * val; + if (bndry->width == 2) { + f(bndry->x, bndry->y + bndry->by, z) = + f(bndry->x, bndry->y - 2 * bndry->by, z) + 3.0 * delta * val; } } else { // set corners to zero f(bndry->x, bndry->y, z) = 0.0; - if (bndry->width == 2){ + if (bndry->width == 2) { f(bndry->x + bndry->bx, bndry->y + bndry->by, z) = 0.0; } } @@ -1591,845 +1491,400 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D &f) { /////////////////////////////////////////////////////////////// -BoundaryOp* BoundaryNeumann2::clone(BoundaryRegion *region, const list &args) { +BoundaryOp *BoundaryNeumann2::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { output << "WARNING: Use of boundary condition \"neumann2\" is deprecated!\n"; output << " Consider using \"neumann\" instead\n"; - verifyNumPoints(region,2); - if(!args.empty()) { - output << "WARNING: Ignoring arguments to BoundaryNeumann2\n"; - } - return new BoundaryNeumann2(region); + return boundaryCloneNoArguments(region, args, keywords); } -void BoundaryNeumann2::apply(Field2D &f) { - // Loop over all elements and use one-sided differences - for(bndry->first(); !bndry->isDone(); bndry->next()) - f(bndry->x, bndry->y) = (4.*f(bndry->x - bndry->bx, bndry->y - bndry->by) - f(bndry->x - 2*bndry->bx, bndry->y - 2*bndry->by))/3.; +inline void BoundaryNeumann2::applyAtPoint(Field2D &f, BoutReal UNUSED(val), int x, + int bx, int y, int by, int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = (4. * f(x - bx, y - by, z) - f(x - 2 * bx, y - 2 * by, z)) / 3.; } - -void BoundaryNeumann2::apply(Field3D &f) { - for(bndry->first(); !bndry->isDone(); bndry->next()) - for(int z=0;zLocalNz;z++) - f(bndry->x, bndry->y, z) = (4.*f(bndry->x - bndry->bx, bndry->y - bndry->by, z) - f(bndry->x - 2*bndry->bx, bndry->y - 2*bndry->by, z))/3.; +inline void BoundaryNeumann2::applyAtPoint(Field3D &f, BoutReal UNUSED(val), int x, + int bx, int y, int by, int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = (4. * f(x - bx, y - by, z) - f(x - 2 * bx, y - 2 * by, z)) / 3.; } -/////////////////////////////////////////////////////////////// - -BoundaryOp* BoundaryNeumann_2ndOrder::clone(BoundaryRegion *region, const list &args) { - output << "WARNING: Use of boundary condition \"neumann_2ndorder\" is deprecated!\n"; - output << " Consider using \"neumann\" instead\n"; -#ifdef BOUNDARY_CONDITIONS_UPGRADE_EXTRAPOLATE_FOR_2ND_ORDER - verifyNumPoints(region,2); -#else - verifyNumPoints(region,1); -#endif - if(!args.empty()) { - // First argument should be a value - val = stringToReal(args.front()); - return new BoundaryNeumann_2ndOrder(region, val); - } - return new BoundaryNeumann_2ndOrder(region); +void BoundaryNeumann2::applyAtPointStaggered(Field2D &UNUSED(f), BoutReal UNUSED(val), + int UNUSED(x), int UNUSED(bx), int UNUSED(y), + int UNUSED(by), int UNUSED(z), + BoutReal UNUSED(delta)) { + throw BoutException("BoundaryNeumann2 not implemented for staggered grids"); } - -void BoundaryNeumann_2ndOrder::apply(Field2D &f) { - Coordinates *metric = f.getCoordinates(); - - // Set (at 2nd order) the gradient at the mid-point between the guard cell and the grid cell to be val - // This sets the value of the co-ordinate derivative, i.e. DDX/DDY not Grad_par/Grad_perp.x - // N.B. Only first guard cells (closest to the grid) should ever be used - for(bndry->first(); !bndry->isDone(); bndry->next1d()) { - f(bndry->x,bndry->y) = f(bndry->x-bndry->bx,bndry->y-bndry->by) + val*(bndry->bx*metric->dx(bndry->x,bndry->y)+bndry->by*metric->dy(bndry->x,bndry->y)); -#ifdef BOUNDARY_CONDITIONS_UPGRADE_EXTRAPOLATE_FOR_2ND_ORDER - f(bndry->x+bndry->bx,bndry->y+bndry->by) = 3.*f(bndry->x,bndry->y) - 3.*f(bndry->x-bndry->bx,bndry->y-bndry->by) + f(bndry->x-2*bndry->bx,bndry->y-2*bndry->by); -#elif defined(CHECK) - f(bndry->x+bndry->bx,bndry->y+bndry->by) = 1.e60; -#endif - } -} - -void BoundaryNeumann_2ndOrder::apply(Field3D &f) { - Coordinates *metric = f.getCoordinates(); - // Set (at 2nd order) the gradient at the mid-point between the guard cell and the grid cell to be val - // This sets the value of the co-ordinate derivative, i.e. DDX/DDY not Grad_par/Grad_perp.x - // N.B. Only first guard cells (closest to the grid) should ever be used - for(bndry->first(); !bndry->isDone(); bndry->next1d()) - for(int z=0;zLocalNz;z++) { - BoutReal delta = bndry->bx*metric->dx(bndry->x,bndry->y)+bndry->by*metric->dy(bndry->x,bndry->y); - f(bndry->x,bndry->y,z) = f(bndry->x-bndry->bx,bndry->y-bndry->by,z) + val*delta; -#ifdef BOUNDARY_CONDITIONS_UPGRADE_EXTRAPOLATE_FOR_2ND_ORDER - f(bndry->x+bndry->bx,bndry->y+bndry->by,z) = 3.*f(bndry->x,bndry->y,z) - 3.*f(bndry->x-bndry->bx,bndry->y-bndry->by,z) + f(bndry->x-2*bndry->bx,bndry->y-2*bndry->by,z); -#elif defined(CHECK) - f(bndry->x+bndry->bx,bndry->y+bndry->by,z) = 1.e60; -#endif - } +void BoundaryNeumann2::applyAtPointStaggered(Field3D &UNUSED(f), BoutReal UNUSED(val), + int UNUSED(x), int UNUSED(bx), int UNUSED(y), + int UNUSED(by), int UNUSED(z), + BoutReal UNUSED(delta)) { + throw BoutException("BoundaryNeumann2 not implemented for staggered grids"); } -void BoundaryNeumann_2ndOrder::apply_ddt(Field2D &f) { - Field2D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - (*dt)(bndry->x,bndry->y) = 0.; // Set time derivative to zero +inline void BoundaryNeumann2::extrapolateFurther(Field2D &f, int x, int bx, int y, int by, + int z) { + extrapolate2nd(f, x, bx, y, by, z); } - -void BoundaryNeumann_2ndOrder::apply_ddt(Field3D &f) { - Field3D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - for(int z=0;zLocalNz;z++) - (*dt)(bndry->x,bndry->y,z) = 0.; // Set time derivative to zero +inline void BoundaryNeumann2::extrapolateFurther(Field3D &f, int x, int bx, int y, int by, + int z) { + extrapolate2nd(f, x, bx, y, by, z); } /////////////////////////////////////////////////////////////// -BoundaryOp* BoundaryNeumann::clone(BoundaryRegion *region, const list &args){ - verifyNumPoints(region,1); - std::shared_ptr newgen = nullptr; - if(!args.empty()) { - // First argument should be an expression - newgen = FieldFactory::get()->parse(args.front()); - } - return new BoundaryNeumann(region, newgen); -} - -void BoundaryNeumann::apply(Field2D &f) { - BoundaryNeumann::apply(f,0.); -} - - -void BoundaryNeumann::apply(Field2D &f,BoutReal t) { - // Set (at 2nd order) the value at the mid-point between the guard cell and the grid cell to be val - // N.B. Only first guard cells (closest to the grid) should ever be used - - Coordinates *metric = f.getCoordinates(); - - bndry->first(); - - // Decide which generator to use - std::shared_ptr fg = gen; - if(!fg) - fg = f.getBndryGenerator(bndry->location); - - BoutReal val = 0.0; - - // Check for staggered grids - - CELL_LOC loc = f.getLocation(); - if(mesh->StaggerGrids && loc != CELL_CENTRE) { - // Staggered. Need to apply slightly differently - // Use one-sided differencing. Cell is now on - // the boundary, so use one-sided differencing - - if( loc == CELL_XLOW ) { - // Field is shifted in X - - if(bndry->bx > 0) { - // Outer x boundary - - for(; !bndry->isDone(); bndry->next1d()) { - - if(fg) { - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) - + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = mesh->GlobalY(bndry->y); - - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t) * metric->dx(bndry->x, bndry->y); - } - - f(bndry->x,bndry->y) = (4.*f(bndry->x - bndry->bx, bndry->y) - f(bndry->x - 2*bndry->bx, bndry->y) + 2.*val)/3.; - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - // Use third order extrapolation because boundary point is set to third order, and these points - // may be used be used by 2nd order upwinding type schemes, which require 3rd order - f(xi, yi) = 3.0*f(xi - bndry->bx, yi - bndry->by) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) + f(xi - 3*bndry->bx, yi - 3*bndry->by); - } - } - } - if(bndry->bx < 0) { - // Inner x boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - - if(fg) { - - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) - + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = mesh->GlobalY(bndry->y); - - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t) * metric->dx(bndry->x, bndry->y); - } - - f(bndry->x - bndry->bx,bndry->y) = (4.*f(bndry->x - 2*bndry->bx, bndry->y) - f(bndry->x - 3*bndry->bx, bndry->y) - 2.*val)/3.; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - // Use third order extrapolation because boundary point is set to third order, and these points - // may be used be used by 2nd order upwinding type schemes, which require 3rd order - f(xi, yi) = 3.0*f(xi - bndry->bx, yi - bndry->by) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) + f(xi - 3*bndry->bx, yi - 3*bndry->by); - } - } - } - if(bndry->by !=0 ){ - // y boundaries - - for(bndry->first(); !bndry->isDone(); bndry->next1d()) { - BoutReal delta = bndry->bx*metric->dx(bndry->x,bndry->y)+bndry->by*metric->dy(bndry->x,bndry->y); - - if(fg) { - // x norm is shifted by half a grid point because it is staggered. - // y norm is located half way between first grid cell and guard cell. - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - 1) ); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - bndry->by) ); - - val = fg->generate(xnorm, TWOPI*ynorm, 0.0, t); - } - - f(bndry->x,bndry->y) = f(bndry->x-bndry->bx, bndry->y-bndry->by) + delta*val; - if (bndry->width == 2){ - f(bndry->x + bndry->bx, bndry->y + bndry->by) = f(bndry->x - 2*bndry->bx, bndry->y - 2*bndry->by) + 3.0*delta*val; - } - } - } - } - else if(loc == CELL_YLOW) { - // Y boundary, and field is shifted in Y - - if(bndry->by > 0) { - // Outer y boundary - - for(; !bndry->isDone(); bndry->next1d()) { - if(fg) { - BoutReal xnorm = mesh->GlobalX(bndry->x); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) - + mesh->GlobalY(bndry->y - bndry->by) ); - - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t) * metric->dx(bndry->x, bndry->y); - } - f(bndry->x,bndry->y) = (4.*f(bndry->x, bndry->y - bndry->by) - f(bndry->x, bndry->y - 2*bndry->by) + 2.*val)/3.; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - // Use third order extrapolation because boundary point is set to third order, and these points - // may be used be used by 2nd order upwinding type schemes, which require 3rd order - f(xi, yi) = 3.0*f(xi - bndry->bx, yi - bndry->by) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) + f(xi - 3*bndry->bx, yi - 3*bndry->by); - } - } - } - if(bndry->by < 0) { - // Inner y boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - - if(fg) { - - BoutReal xnorm = mesh->GlobalX(bndry->x); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) - + mesh->GlobalY(bndry->y - bndry->by) ); - - val = fg->generate(xnorm,TWOPI*ynorm,0.0, t) * metric->dx(bndry->x, bndry->y - bndry->by); - } - f(bndry->x,bndry->y - bndry->by) = (4.*f(bndry->x, bndry->y - 2*bndry->by) - f(bndry->x, bndry->y - 3*bndry->by) - 2.*val)/3.; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - // Use third order extrapolation because boundary point is set to third order, and these points - // may be used be used by 2nd order upwinding type schemes, which require 3rd order - f(xi, yi) = 3.0*f(xi - bndry->bx, yi - bndry->by) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) + f(xi - 3*bndry->bx, yi - 3*bndry->by); - } - } - } - if(bndry->bx != 0){ - // x boundaries - for(bndry->first(); !bndry->isDone(); bndry->next1d()) { - BoutReal delta = bndry->bx*metric->dx(bndry->x,bndry->y)+bndry->by*metric->dy(bndry->x,bndry->y); - - if(fg) { - // x norm is located half way between first grid cell and guard cell. - // y norm is shifted by half a grid point because it is staggered. - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - 1) ); - - val = fg->generate(xnorm, TWOPI*ynorm, 0.0, t); - } - - f(bndry->x,bndry->y) = f(bndry->x-bndry->bx, bndry->y-bndry->by) + delta*val; - if (bndry->width == 2){ - f(bndry->x + bndry->bx, bndry->y + bndry->by) = f(bndry->x - 2*bndry->bx, bndry->y - 2*bndry->by) + 3.0*delta*val; - } - } - } - } - } - else { - // Non-staggered, standard case - - for(bndry->first(); !bndry->isDone(); bndry->next1d()) { - BoutReal delta = bndry->bx*metric->dx(bndry->x,bndry->y)+bndry->by*metric->dy(bndry->x,bndry->y); - - if(fg) { - // Calculate the X and Y normalised values half-way between the guard cell and grid cell - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) // In the guard cell - + mesh->GlobalX(bndry->x - bndry->bx) ); // the grid cell - - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) // In the guard cell - + mesh->GlobalY(bndry->y - bndry->by) ); // the grid cell - - val = fg->generate(xnorm, TWOPI*ynorm, 0.0, t); - } - - f(bndry->x,bndry->y) = f(bndry->x-bndry->bx, bndry->y-bndry->by) + delta*val; - if (bndry->width == 2){ - f(bndry->x + bndry->bx, bndry->y + bndry->by) = f(bndry->x - 2*bndry->bx, bndry->y - 2*bndry->by) + 3.0*delta*val; - } - } - } -} - - -void BoundaryNeumann::apply(Field3D &f) { - BoundaryNeumann::apply(f,0.); -} - - -void BoundaryNeumann::apply(Field3D &f,BoutReal t) { - Coordinates *metric = f.getCoordinates(); - - bndry->first(); - - // Decide which generator to use - std::shared_ptr fg = gen; - if(!fg) - fg = f.getBndryGenerator(bndry->location); - - BoutReal val = 0.0; - - // Check for staggered grids - - CELL_LOC loc = f.getLocation(); - if(mesh->StaggerGrids && loc != CELL_CENTRE) { - // Staggered. Need to apply slightly differently - // Use one-sided differencing. Cell is now on - // the boundary, so use one-sided differencing - - if( loc == CELL_XLOW ) { - // Field is shifted in X - - if(bndry->bx > 0) { - // Outer x boundary - for(; !bndry->isDone(); bndry->next1d()) { - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) - + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = mesh->GlobalY(bndry->y); - - for(int zk=0;zkLocalNz;zk++) { - if(fg) - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz),t) * metric->dx(bndry->x, bndry->y); - - f(bndry->x,bndry->y, zk) = (4.*f(bndry->x - bndry->bx, bndry->y,zk) - f(bndry->x - 2*bndry->bx, bndry->y,zk) + 2.*val)/3.; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - // Use third order extrapolation because boundary point is set to third order, and these points - // may be used be used by 2nd order upwinding type schemes, which require 3rd order - f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + f(xi - 3*bndry->bx, yi - 3*bndry->by, zk); - } - } - } - } - if(bndry->bx < 0) { - // Inner x boundary - for(; !bndry->isDone(); bndry->next1d()) { - - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) - + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = mesh->GlobalY(bndry->y); - - for(int zk=0;zkLocalNz;zk++) { - - if(fg) - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz),t) * metric->dx(bndry->x - bndry->bx, bndry->y); - - f(bndry->x - bndry->bx,bndry->y, zk) = (4.*f(bndry->x - 2*bndry->bx, bndry->y,zk) - f(bndry->x - 3*bndry->bx, bndry->y,zk) - 2.*val)/3.; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - // Use third order extrapolation because boundary point is set to third order, and these points - // may be used be used by 2nd order upwinding type schemes, which require 3rd order - f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + f(xi - 3*bndry->bx, yi - 3*bndry->by, zk); - } - } - } - } - if(bndry->by != 0) { - for(; !bndry->isDone(); bndry->next1d()) { - // x norm is shifted by half a grid point because it is staggered. - // y norm is located half way between first grid cell and guard cell. - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - 1) ); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - bndry->by) ); - - BoutReal delta = bndry->bx*metric->dx(bndry->x,bndry->y)+bndry->by*metric->dy(bndry->x,bndry->y); - - for(int zk=0;zkLocalNz;zk++) { - if(fg){ - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz),t); - } - f(bndry->x,bndry->y, zk) = f(bndry->x-bndry->bx, bndry->y-bndry->by, zk) + delta*val; - if (bndry->width == 2){ - f(bndry->x + bndry->bx, bndry->y + bndry->by, zk) = f(bndry->x - 2*bndry->bx, bndry->y - 2*bndry->by, zk) + 3.0*delta*val; - } - } - } - } - } - else if( loc == CELL_YLOW ) { - // Field is shifted in Y - - if(bndry->by > 0) { - // Outer y boundary - for(; !bndry->isDone(); bndry->next1d()) { - - BoutReal xnorm = mesh->GlobalX(bndry->x); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) - + mesh->GlobalY(bndry->y - bndry->by) ); - for(int zk=0;zkLocalNz;zk++) { - - if(fg){ - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz),t) * metric->dy(bndry->x, bndry->y); - } - f(bndry->x,bndry->y,zk) = (4.*f(bndry->x, bndry->y - bndry->by,zk) - f(bndry->x, bndry->y - 2*bndry->by,zk) + 2.*val)/3.; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - // Use third order extrapolation because boundary point is set to third order, and these points - // may be used be used by 2nd order upwinding type schemes, which require 3rd order - f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + f(xi - 3*bndry->bx, yi - 3*bndry->by, zk); - } - } - } - } - if(bndry->by < 0) { - // Inner y boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - - BoutReal xnorm = mesh->GlobalX(bndry->x); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) - + mesh->GlobalY(bndry->y - bndry->by) ); - for(int zk=0;zkLocalNz;zk++) { - if(fg) - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz),t) * metric->dy(bndry->x, bndry->y - bndry->by); - - f(bndry->x,bndry->y - bndry->by,zk) = (4.*f(bndry->x, bndry->y - 2*bndry->by,zk) - f(bndry->x, bndry->y - 3*bndry->by,zk) - 2.*val)/3.; - - // Need to set second guard cell, as may be used for interpolation or upwinding derivatives - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - // Use third order extrapolation because boundary point is set to third order, and these points - // may be used be used by 2nd order upwinding type schemes, which require 3rd order - f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + f(xi - 3*bndry->bx, yi - 3*bndry->by, zk); - } - } - } - } - if(bndry->bx !=0 ){ - // x boundaries. - for(; !bndry->isDone(); bndry->next1d()) { - // x norm is located half way between first grid cell and guard cell. - // y norm is shifted by half a grid point because it is staggered. - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) + mesh->GlobalX(bndry->x - bndry->bx) ); - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) + mesh->GlobalY(bndry->y - 1) ); - - BoutReal delta = bndry->bx*metric->dx(bndry->x,bndry->y)+bndry->by*metric->dy(bndry->x,bndry->y); - - for(int zk=0;zkLocalNz;zk++) { - if(fg){ - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz),t); - } - f(bndry->x,bndry->y, zk) = f(bndry->x-bndry->bx, bndry->y-bndry->by, zk) + delta*val; - if (bndry->width == 2){ - f(bndry->x + bndry->bx, bndry->y + bndry->by, zk) = f(bndry->x - 2*bndry->bx, bndry->y - 2*bndry->by, zk) + 3.0*delta*val; - } - } - } - } - } - } - else { - for(; !bndry->isDone(); bndry->next1d()) { - // Calculate the X and Y normalised values half-way between the guard cell and grid cell - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) // In the guard cell - + mesh->GlobalX(bndry->x - bndry->bx) ); // the grid cell - - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) // In the guard cell - + mesh->GlobalY(bndry->y - bndry->by) ); // the grid cell - - BoutReal delta = bndry->bx*metric->dx(bndry->x,bndry->y)+bndry->by*metric->dy(bndry->x,bndry->y); - - for(int zk=0;zkLocalNz;zk++) { - if(fg){ - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz),t); - } - f(bndry->x,bndry->y, zk) = f(bndry->x-bndry->bx, bndry->y-bndry->by, zk) + delta*val; - if (bndry->width == 2){ - f(bndry->x + bndry->bx, bndry->y + bndry->by, zk) = f(bndry->x - 2*bndry->bx, bndry->y - 2*bndry->by, zk) + 3.0*delta*val; - } - } - } - } -} - -void BoundaryNeumann::apply_ddt(Field2D &f) { - Field2D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - (*dt)(bndry->x,bndry->y) = 0.; // Set time derivative to zero -} - -void BoundaryNeumann::apply_ddt(Field3D &f) { - Field3D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - for(int z=0;zLocalNz;z++) - (*dt)(bndry->x,bndry->y,z) = 0.; // Set time derivative to zero -} - -/////////////////////////////////////////////////////////////// - -BoundaryOp* BoundaryNeumann_O4::clone(BoundaryRegion *region, const list &args){ - std::shared_ptr newgen = nullptr; - if(!args.empty()) { - // First argument should be an expression - newgen = FieldFactory::get()->parse(args.front()); - } - return new BoundaryNeumann_O4(region, newgen); -} - -void BoundaryNeumann_O4::apply(Field2D &f) { - BoundaryNeumann_O4::apply(f,0.); -} - -void BoundaryNeumann_O4::apply(Field2D &f,BoutReal t) { - - // Set (at 4th order) the value at the mid-point between the guard cell and the grid cell to be val - // N.B. Only first guard cells (closest to the grid) should ever be used - bndry->first(); - - // Decide which generator to use - std::shared_ptr fg = gen; - if(!fg) - fg = f.getBndryGenerator(bndry->location); +BoundaryOp * +BoundaryNeumann_2ndOrder::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + output << "WARNING: Use of boundary condition \"neumann_2ndorder\" is deprecated!\n"; + output << " Consider using \"neumann\" instead\n"; + return boundaryClone(region, args, keywords); +} - BoutReal val = 0.0; - - // Check for staggered grids - CELL_LOC loc = f.getLocation(); - if(mesh->StaggerGrids && loc != CELL_CENTRE) { - throw BoutException("neumann_o4 not implemented with staggered grid yet"); - } - else { - // Non-staggered, standard case - - Coordinates *coords = f.getCoordinates(); - - for(bndry->first(); !bndry->isDone(); bndry->next1d()) { - BoutReal delta = bndry->bx*coords->dx(bndry->x,bndry->y)+bndry->by*coords->dy(bndry->x,bndry->y); - - if(fg) { - // Calculate the X and Y normalised values half-way between the guard cell and grid cell - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) // In the guard cell - + mesh->GlobalX(bndry->x - bndry->bx) ); // the grid cell - - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) // In the guard cell - + mesh->GlobalY(bndry->y - bndry->by) ); // the grid cell - - val = fg->generate(xnorm, TWOPI*ynorm, 0.0, t); - } - - f(bndry->x, bndry->y) = 12.*delta*val/11. - + - ( - + 17.*f(bndry->x- bndry->bx, bndry->y- bndry->by) - + 9.*f(bndry->x-2*bndry->bx, bndry->y-2*bndry->by) - - 5.*f(bndry->x-3*bndry->bx, bndry->y-3*bndry->by) - + f(bndry->x-4*bndry->bx, bndry->y-4*bndry->by) - )/22.; - - if (bndry->width == 2){ - throw BoutException("neumann_o4 with a boundary width of 2 not implemented yet"); - } - } - } +inline void BoundaryNeumann_2ndOrder::applyAtPoint(Field2D &f, BoutReal val, int x, + int bx, int y, int by, int z, + BoutReal delta) { + f(x, y, z) = f(x - bx, y - by, z) + val * delta; +} +inline void BoundaryNeumann_2ndOrder::applyAtPoint(Field3D &f, BoutReal val, int x, + int bx, int y, int by, int z, + BoutReal delta) { + f(x, y, z) = f(x - bx, y - by, z) + val * delta; } -void BoundaryNeumann_O4::apply(Field3D &f) { - BoundaryNeumann_O4::apply(f,0.); +inline void BoundaryNeumann_2ndOrder::applyAtPointStaggered(Field2D &f, BoutReal val, + int x, int bx, int y, int by, + int z, BoutReal delta) { + f(x, y, z) = + (4. * f(x - bx, y - by, z) - f(x - 2 * bx, y - 2 * by, z) + 2. * delta * val) / 3.; +} +inline void BoundaryNeumann_2ndOrder::applyAtPointStaggered(Field3D &f, BoutReal val, + int x, int bx, int y, int by, + int z, BoutReal delta) { + f(x, y, z) = + (4. * f(x - bx, y - by, z) - f(x - 2 * bx, y - 2 * by, z) + 2. * delta * val) / 3.; } -void BoundaryNeumann_O4::apply(Field3D &f,BoutReal t) { - bndry->first(); +inline void BoundaryNeumann_2ndOrder::extrapolateFurther(Field2D &f, int x, int bx, int y, + int by, int z) { + extrapolate2nd(f, x, bx, y, by, z); +} +inline void BoundaryNeumann_2ndOrder::extrapolateFurther(Field3D &f, int x, int bx, int y, + int by, int z) { + extrapolate2nd(f, x, bx, y, by, z); +} - // Decide which generator to use - std::shared_ptr fg = gen; - if(!fg) - fg = f.getBndryGenerator(bndry->location); +/////////////////////////////////////////////////////////////// - BoutReal val = 0.0; - - // Check for staggered grids - CELL_LOC loc = f.getLocation(); - if(mesh->StaggerGrids && loc != CELL_CENTRE) { - throw BoutException("neumann_o4 not implemented with staggered grid yet"); - } - else { - Coordinates *coords = f.getCoordinates(); - for(; !bndry->isDone(); bndry->next1d()) { - // Calculate the X and Y normalised values half-way between the guard cell and grid cell - BoutReal xnorm = 0.5*( mesh->GlobalX(bndry->x) // In the guard cell - + mesh->GlobalX(bndry->x - bndry->bx) ); // the grid cell - - BoutReal ynorm = 0.5*( mesh->GlobalY(bndry->y) // In the guard cell - + mesh->GlobalY(bndry->y - bndry->by) ); // the grid cell - - BoutReal delta = bndry->bx*coords->dx(bndry->x,bndry->y)+bndry->by*coords->dy(bndry->x,bndry->y); - - for(int zk=0;zkLocalNz;zk++) { - if(fg){ - val = fg->generate(xnorm,TWOPI*ynorm,TWOPI*zk/(mesh->LocalNz),t); - } +BoundaryOp *BoundaryNeumann::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + return boundaryClone(region, args, keywords); +} - f(bndry->x,bndry->y, zk) = 12.*delta*val/11. - + - ( - + 17.*f(bndry->x- bndry->bx, bndry->y- bndry->by, zk) - + 9.*f(bndry->x-2*bndry->bx, bndry->y-2*bndry->by, zk) - - 5.*f(bndry->x-3*bndry->bx, bndry->y-3*bndry->by, zk) - + f(bndry->x-4*bndry->bx, bndry->y-4*bndry->by, zk) - )/22.; - - if (bndry->width == 2){ - throw BoutException("neumann_o4 with a boundary width of 2 not implemented yet"); - } - } - } - } +inline void BoundaryNeumann::applyAtPoint(Field2D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta) { + f(x, y, z) = f(x - bx, y - by, z) + delta * val; +} +inline void BoundaryNeumann::applyAtPoint(Field3D &f, BoutReal val, int x, int bx, int y, + int by, int z, BoutReal delta) { + f(x, y, z) = f(x - bx, y - by, z) + delta * val; } -void BoundaryNeumann_O4::apply_ddt(Field2D &f) { - Field2D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - (*dt)(bndry->x,bndry->y) = 0.; // Set time derivative to zero +// For staggered case need to apply slightly differently Use one-sided +// differencing. Cell is now on the boundary, so use one-sided differencing +inline void BoundaryNeumann::applyAtPointStaggered(Field2D &f, BoutReal val, int x, + int bx, int y, int by, int z, + BoutReal delta) { + f(x, y, z) = + (4. * f(x - bx, y - by, z) - f(x - 2 * bx, y - 2 * by, z) + 2. * delta * val) / 3.; +} +inline void BoundaryNeumann::applyAtPointStaggered(Field3D &f, BoutReal val, int x, + int bx, int y, int by, int z, + BoutReal delta) { + f(x, y, z) = + (4. * f(x - bx, y - by, z) - f(x - 2 * bx, y - 2 * by, z) + 2. * delta * val) / 3.; } -void BoundaryNeumann_O4::apply_ddt(Field3D &f) { - Field3D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - for(int z=0;zLocalNz;z++) - (*dt)(bndry->x,bndry->y,z) = 0.; // Set time derivative to zero +inline void BoundaryNeumann::extrapolateFurther(Field2D &f, int x, int bx, int y, int by, + int z) { + extrapolate2nd(f, x, bx, y, by, z); +} +inline void BoundaryNeumann::extrapolateFurther(Field3D &f, int x, int bx, int y, int by, + int z) { + extrapolate2nd(f, x, bx, y, by, z); } /////////////////////////////////////////////////////////////// -BoundaryOp* BoundaryNeumann_4thOrder::clone(BoundaryRegion *region, const list &args) { - verifyNumPoints(region,4); - if(!args.empty()) { - // First argument should be a value - val = stringToReal(args.front()); - return new BoundaryNeumann_4thOrder(region, val); - } - return new BoundaryNeumann_4thOrder(region); +BoundaryOp * +BoundaryNeumann_O4::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + return boundaryClone(region, args, keywords); } -void BoundaryNeumann_4thOrder::apply(Field2D &f) { - Coordinates *metric = f.getCoordinates(); - // Set (at 4th order) the gradient at the mid-point between the guard cell and the grid cell to be val - // This sets the value of the co-ordinate derivative, i.e. DDX/DDY not Grad_par/Grad_perp.x - for(bndry->first(); !bndry->isDone(); bndry->next1d()) { - BoutReal delta = -(bndry->bx*metric->dx(bndry->x,bndry->y)+bndry->by*metric->dy(bndry->x,bndry->y)); - f(bndry->x,bndry->y) = 12.*delta/11.*val + 17./22.*f(bndry->x-bndry->bx,bndry->y-bndry->by) + 9./22.*f(bndry->x-2*bndry->bx,bndry->y-2*bndry->by) - 5./22.*f(bndry->x-3*bndry->bx,bndry->y-3*bndry->by) + 1./22.*f(bndry->x-4*bndry->bx,bndry->y-4*bndry->by); - f(bndry->x+bndry->bx,bndry->y+bndry->by) = -24.*delta*val + 27.*f(bndry->x,bndry->y) - 27.*f(bndry->x-bndry->bx,bndry->y-bndry->by) + f(bndry->x-2*bndry->bx,bndry->y-2*bndry->by); // The f(bndry->x-4*bndry->bx,bndry->y-4*bndry->by) term vanishes, so that this sets to zero the 4th order central difference first derivative at the point half way between the guard cell and the grid cell - } +inline void BoundaryNeumann_O4::applyAtPoint(Field2D &f, BoutReal val, int x, int bx, + int y, int by, int z, BoutReal delta) { + f(x, y, z) = 12. * delta * val / 11. + + (17. * f(x - bx, y - by, z) + 9. * f(x - 2 * bx, y - 2 * by, z) - + 5. * f(x - 3 * bx, y - 3 * by, z) + f(x - 4 * bx, y - 4 * by, z)) / + 22.; } - -void BoundaryNeumann_4thOrder::apply(Field3D &f) { - Coordinates *metric = f.getCoordinates(); - // Set (at 4th order) the gradient at the mid-point between the guard cell and the grid cell to be val - // This sets the value of the co-ordinate derivative, i.e. DDX/DDY not Grad_par/Grad_perp.x - for(bndry->first(); !bndry->isDone(); bndry->next1d()) - for(int z=0;zLocalNz;z++) { - BoutReal delta = -(bndry->bx*metric->dx(bndry->x,bndry->y)+bndry->by*metric->dy(bndry->x,bndry->y)); - f(bndry->x,bndry->y,z) = 12.*delta/11.*val + 17./22.*f(bndry->x-bndry->bx,bndry->y-bndry->by,z) + 9./22.*f(bndry->x-2*bndry->bx,bndry->y-2*bndry->by,z) - 5./22.*f(bndry->x-3*bndry->bx,bndry->y-3*bndry->by,z) + 1./22.*f(bndry->x-4*bndry->bx,bndry->y-4*bndry->by,z); - f(bndry->x+bndry->bx,bndry->y+bndry->by,z) = -24.*delta*val + 27.*f(bndry->x,bndry->y,z) - 27.*f(bndry->x-bndry->bx,bndry->y-bndry->by,z) + f(bndry->x-2*bndry->bx,bndry->y-2*bndry->by,z); // The f(bndry->x-4*bndry->bx,bndry->y-4*bndry->by,z) term vanishes, so that this sets to zero the 4th order central difference first derivative at the point half way between the guard cell and the grid cell - } +inline void BoundaryNeumann_O4::applyAtPoint(Field3D &f, BoutReal val, int x, int bx, + int y, int by, int z, BoutReal delta) { + f(x, y, z) = 12. * delta * val / 11. + + (17. * f(x - bx, y - by, z) + 9. * f(x - 2 * bx, y - 2 * by, z) - + 5. * f(x - 3 * bx, y - 3 * by, z) + f(x - 4 * bx, y - 4 * by, z)) / + 22.; } -void BoundaryNeumann_4thOrder::apply_ddt(Field2D &f) { - Field2D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - (*dt)(bndry->x,bndry->y) = 0.; // Set time derivative to zero +inline void BoundaryNeumann_O4::applyAtPointStaggered(Field2D &f, BoutReal val, int x, + int bx, int y, int by, int z, + BoutReal delta) { + f(x, y, z) = + 12. / 25. * + (delta * val + 4. * f(x - bx, y - by, z) - 3. * f(x - 2 * bx, y - 2 * by, z) + + 4. / 3. * f(x - 3 * bx, y - 3 * by, z) - 1. / 4. * f(x - 4 * bx, y - 4 * by, z)); +} +inline void BoundaryNeumann_O4::applyAtPointStaggered(Field3D &f, BoutReal val, int x, + int bx, int y, int by, int z, + BoutReal delta) { + f(x, y, z) = + 12. / 25. * + (delta * val + 4. * f(x - bx, y - by, z) - 3. * f(x - 2 * bx, y - 2 * by, z) + + 4. / 3. * f(x - 3 * bx, y - 3 * by, z) - 1. / 4. * f(x - 4 * bx, y - 4 * by, z)); } -void BoundaryNeumann_4thOrder::apply_ddt(Field3D &f) { - Field3D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - for(int z=0;zLocalNz;z++) - (*dt)(bndry->x,bndry->y,z) = 0.; // Set time derivative to zero +inline void BoundaryNeumann_O4::extrapolateFurther(Field2D &f, int x, int bx, int y, + int by, int z) { + extrapolate5th(f, x, bx, y, by, z); +} +inline void BoundaryNeumann_O4::extrapolateFurther(Field3D &f, int x, int bx, int y, + int by, int z) { + extrapolate5th(f, x, bx, y, by, z); } /////////////////////////////////////////////////////////////// -BoundaryOp* BoundaryNeumannPar::clone(BoundaryRegion *region, const list &args) { - verifyNumPoints(region,1); - if(!args.empty()) { - output << "WARNING: Ignoring arguments to BoundaryNeumann2\n"; - } - return new BoundaryNeumannPar(region); +BoundaryOp * +BoundaryNeumann_4thOrder::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + return boundaryClone(region, args, keywords); +} + +inline void BoundaryNeumann_4thOrder::applyAtPoint(Field2D &f, BoutReal val, int x, + int bx, int y, int by, int z, + BoutReal delta) { + f(x, y, z) = 12. * delta / 11. * val + 17. / 22. * f(x - bx, y - by, z) + + 9. / 22. * f(x - 2 * bx, y - 2 * by, z) - + 5. / 22. * f(x - 3 * bx, y - 3 * by, z) + + 1. / 22. * f(x - 4 * bx, y - 4 * by, z); +} +inline void BoundaryNeumann_4thOrder::applyAtPoint(Field3D &f, BoutReal val, int x, + int bx, int y, int by, int z, + BoutReal delta) { + f(x, y, z) = 12. * delta / 11. * val + 17. / 22. * f(x - bx, y - by, z) + + 9. / 22. * f(x - 2 * bx, y - 2 * by, z) - + 5. / 22. * f(x - 3 * bx, y - 3 * by, z) + + 1. / 22. * f(x - 4 * bx, y - 4 * by, z); +} + +void BoundaryNeumann_4thOrder::applyAtPointStaggered(Field2D &UNUSED(f), + BoutReal UNUSED(val), int UNUSED(x), + int UNUSED(bx), int UNUSED(y), + int UNUSED(by), int UNUSED(z), + BoutReal UNUSED(delta)) { + throw BoutException("BoundaryNeumann_4thOrder is not implemented for staggered grids."); +} +void BoundaryNeumann_4thOrder::applyAtPointStaggered(Field3D &UNUSED(f), + BoutReal UNUSED(val), int UNUSED(x), + int UNUSED(bx), int UNUSED(y), + int UNUSED(by), int UNUSED(z), + BoutReal UNUSED(delta)) { + throw BoutException("BoundaryNeumann_4thOrder is not implemented for staggered grids."); +} + +inline void BoundaryNeumann_4thOrder::extrapolateFurther(Field2D &f, int x, int bx, int y, + int by, int z) { + // Changing this extrapolation to not depend on val, so just using grid point + // values. Previously was: + // f(x+bx,y+by,z) = -24.*delta*val + 27.*f(x,y,z) - 27.*f(x-bx,y-by,z) + + // f(x-2*bx,y-2*by,z); // The f(x-4*bx,y-4*by,z) term vanishes, so that this sets to + // zero the 4th order central difference first derivative at the point half way between + // the guard cell and the grid cell + // - JTO 16/10/2018 + extrapolate5th(f, x, bx, y, by, z); +} +inline void BoundaryNeumann_4thOrder::extrapolateFurther(Field3D &f, int x, int bx, int y, + int by, int z) { + // Changing this extrapolation to not depend on val, so just using grid point + // values. Previously was + // f(x+bx,y+by,z) = -24.*delta*val + 27.*f(x,y,z) - 27.*f(x-bx,y-by,z) + + // f(x-2*bx,y-2*by,z); // The f(x-4*bx,y-4*by,z) term vanishes, so that this sets to + // zero the 4th order central difference first derivative at the point half way between + // the guard cell and the grid cell + // - JTO 16/10/2018 + extrapolate5th(f, x, bx, y, by, z); } +/////////////////////////////////////////////////////////////// -void BoundaryNeumannPar::apply(Field2D &f) { - Coordinates *metric = f.getCoordinates(); - // Loop over all elements and set equal to the next point in - for(bndry->first(); !bndry->isDone(); bndry->next()) - f(bndry->x, bndry->y) = f(bndry->x - bndry->bx, bndry->y - bndry->by)*sqrt(metric->g_22(bndry->x, bndry->y)/metric->g_22(bndry->x - bndry->bx, bndry->y - bndry->by)); +BoundaryOp * +BoundaryNeumannPar::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + return boundaryCloneNoArguments(region, args, keywords); } -void BoundaryNeumannPar::apply(Field3D &f) { +template void BoundaryNeumannPar::applyTemplate(T &f, BoutReal UNUSED(t)) { + ASSERT1(f.getLocation() == + CELL_CENTRE); // BoundaryNeumannPar not implemented for staggered fields Coordinates *metric = f.getCoordinates(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - for(int z=0;zLocalNz;z++) - f(bndry->x,bndry->y,z) = f(bndry->x - bndry->bx,bndry->y - bndry->by,z)*sqrt(metric->g_22(bndry->x, bndry->y)/metric->g_22(bndry->x - bndry->bx, bndry->y - bndry->by)); + for (bndry->first(); !bndry->isDone(); bndry->next()) + for (int z = 0; z < mesh->LocalNz; z++) + f(bndry->x, bndry->y, z) = + f(bndry->x - bndry->bx, bndry->y - bndry->by, z) * + sqrt(metric->g_22(bndry->x, bndry->y) / + metric->g_22(bndry->x - bndry->bx, bndry->y - bndry->by)); } /////////////////////////////////////////////////////////////// -BoundaryOp* BoundaryRobin::clone(BoundaryRegion *region, const list &args) { - verifyNumPoints(region,1); +BoundaryOp *BoundaryRobin::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + verifyNumPoints(region, 1); BoutReal a = 0.5, b = 1.0, g = 0.; - + list::const_iterator it = args.begin(); - - if(it != args.end()) { + + if (!keywords.empty()) { + // Given keywords, but not using + throw BoutException("Keywords ignored in boundary : %s", + keywords.begin()->first.c_str()); + } + + if (it != args.end()) { // First argument is 'a' a = stringToReal(*it); it++; - - if(it != args.end()) { + + if (it != args.end()) { // Second is 'b' b = stringToReal(*it); it++; - - if(it != args.end()) { - // Third is 'g' - g = stringToReal(*it); - it++; - if(it != args.end()) { - output << "WARNING: BoundaryRobin takes maximum of 3 arguments. Ignoring extras\n"; - } + + if (it != args.end()) { + // Third is 'g' + g = stringToReal(*it); + it++; + if (it != args.end()) { + output + << "WARNING: BoundaryRobin takes maximum of 3 arguments. Ignoring extras\n"; + } } } } - - return new BoundaryRobin(region, a, b, g); -} -void BoundaryRobin::apply(Field2D &f) { - if(fabs(bval) < 1.e-12) { - // No derivative term so just constant value - for(bndry->first(); !bndry->isDone(); bndry->next()) - f(bndry->x, bndry->y) = gval / aval; - }else { - BoutReal sign = 1.; - if( (bndry->bx < 0) || (bndry->by < 0)) - sign = -1.; - for(bndry->first(); !bndry->isDone(); bndry->next()) - f(bndry->x, bndry->y) = f(bndry->x - bndry->bx,bndry->y - bndry->by) + sign*(gval - aval*f(bndry->x - bndry->bx,bndry->y - bndry->by) ) / bval; - } + return new BoundaryRobin(region, a, b, g); } -void BoundaryRobin::apply(Field3D &f) { - if(fabs(bval) < 1.e-12) { - for(bndry->first(); !bndry->isDone(); bndry->next()) - for(int z=0;zLocalNz;z++) - f(bndry->x, bndry->y, z) = gval / aval; - }else { - BoutReal sign = 1.; - if( (bndry->bx < 0) || (bndry->by < 0)) - sign = -1.; - for(bndry->first(); !bndry->isDone(); bndry->next()) - for(int z=0;zLocalNz;z++) - f(bndry->x, bndry->y, z) = f(bndry->x - bndry->bx, bndry->y - bndry->by, z) + sign*(gval - aval*f(bndry->x - bndry->bx, bndry->y - bndry->by, z) ) / bval; +template void BoundaryRobin::applyTemplate(T &f, BoutReal UNUSED(t)) { + if (fabs(bval) < 1.e-12) { + for (bndry->first(); !bndry->isDone(); bndry->next()) + for (int z = 0; z < f.getNz(); z++) + f(bndry->x, bndry->y, z) = gval / aval; + } else { + Coordinates *metric = f.getCoordinates(); + for (bndry->first(); !bndry->isDone(); bndry->next()) { + BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y) + + bndry->by * metric->dy(bndry->x, bndry->y); + for (int z = 0; z < f.getNz(); z++) { + f(bndry->x, bndry->y, z) = + f(bndry->x - bndry->bx, bndry->y - bndry->by, z) + + (gval - aval * f(bndry->x - bndry->bx, bndry->y - bndry->by, z)) * delta / + bval; + } + } } } /////////////////////////////////////////////////////////////// -void BoundaryConstGradient::apply(Field2D &f){ - // Loop over all elements and set equal to the next point in - for(bndry->first(); !bndry->isDone(); bndry->next()) - f(bndry->x, bndry->y) = 2.*f(bndry->x - bndry->bx,bndry->y - bndry->by) - f(bndry->x - 2*bndry->bx,bndry->y - 2*bndry->by); +BoundaryOp * +BoundaryConstGradient::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + return boundaryCloneNoArguments(region, args, keywords); } -void BoundaryConstGradient::apply(Field3D &f) { - for(bndry->first(); !bndry->isDone(); bndry->next()) - for(int z=0;zLocalNz;z++) - f(bndry->x, bndry->y, z) = 2.*f(bndry->x - bndry->bx, bndry->y - bndry->by, z) - f(bndry->x - 2*bndry->bx,bndry->y - 2*bndry->by,z); +inline void BoundaryConstGradient::applyAtPoint(Field2D &f, BoutReal UNUSED(val), int x, + int bx, int y, int by, int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = 2. * f(x - bx, y - by, z) - f(x - 2 * bx, y - 2 * by, z); +} +inline void BoundaryConstGradient::applyAtPoint(Field3D &f, BoutReal UNUSED(val), int x, + int bx, int y, int by, int z, + BoutReal UNUSED(delta)) { + f(x, y, z) = 2. * f(x - bx, y - by, z) - f(x - 2 * bx, y - 2 * by, z); } -/////////////////////////////////////////////////////////////// +void BoundaryConstGradient::applyAtPointStaggered(Field2D &UNUSED(f), + BoutReal UNUSED(val), int UNUSED(x), + int UNUSED(bx), int UNUSED(y), + int UNUSED(by), int UNUSED(z), + BoutReal UNUSED(delta)) { + throw BoutException("BoundaryConstGradient is not implemented for staggered grids."); +} +void BoundaryConstGradient::applyAtPointStaggered(Field3D &UNUSED(f), + BoutReal UNUSED(val), int UNUSED(x), + int UNUSED(bx), int UNUSED(y), + int UNUSED(by), int UNUSED(z), + BoutReal UNUSED(delta)) { + throw BoutException("BoundaryConstGradient is not implemented for staggered grids."); +} -BoundaryOp* BoundaryConstGradient::clone(BoundaryRegion *region, const list &args) { - verifyNumPoints(region,2); - if(!args.empty()) { - output << "WARNING: Ignoring arguments to BoundaryConstGradient\n"; - } - return new BoundaryConstGradient(region); +inline void BoundaryConstGradient::extrapolateFurther(Field2D &f, int x, int bx, int y, + int by, int z) { + extrapolate2nd(f, x, bx, y, by, z); +} +inline void BoundaryConstGradient::extrapolateFurther(Field3D &f, int x, int bx, int y, + int by, int z) { + extrapolate2nd(f, x, bx, y, by, z); } /////////////////////////////////////////////////////////////// -BoundaryOp* BoundaryZeroLaplace::clone(BoundaryRegion *region, const list &args) { - verifyNumPoints(region,2); - if(!args.empty()) { - output << "WARNING: Ignoring arguments to BoundaryZeroLaplace\n"; - } - return new BoundaryZeroLaplace(region); +BoundaryOp * +BoundaryZeroLaplace::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + return boundaryCloneNoArguments(region, args, keywords); } -void BoundaryZeroLaplace::apply(Field2D &f) { +void BoundaryZeroLaplace::apply(Field2D &f, BoutReal UNUSED(t)) { Coordinates *metric = f.getCoordinates(); - if((bndry->location != BNDRY_XIN) && (bndry->location != BNDRY_XOUT)) { + if ((bndry->location != BNDRY_XIN) && (bndry->location != BNDRY_XOUT)) { // Can't apply this boundary condition to non-X boundaries - throw BoutException("ERROR: Can't apply Zero Laplace condition to non-X boundaries\n"); + throw BoutException( + "ERROR: Can't apply Zero Laplace condition to non-X boundaries\n"); } // Constant X derivative int bx = bndry->bx; // Loop over the Y dimension - for(bndry->first(); !bndry->isDone(); bndry->nextY()) { + for (bndry->first(); !bndry->isDone(); bndry->nextY()) { int x = bndry->x; int y = bndry->y; - BoutReal g = (f(x-bx,y) - f(x-2*bx,y)) / metric->dx(x-bx,y); + BoutReal g = (f(x - bx, y) - f(x - 2 * bx, y)) / metric->dx(x - bx, y); // Loop in X towards edge of domain do { - f(x,y) = f(x-bx,y) + g*metric->dx(x,y); + f(x, y) = f(x - bx, y) + g * metric->dx(x, y); bndry->nextX(); - x = bndry->x; y = bndry->y; - }while(!bndry->isDone()); + x = bndry->x; + y = bndry->y; + } while (!bndry->isDone()); } } -void BoundaryZeroLaplace::apply(Field3D &f) { - int ncz = mesh->LocalNz; +void BoundaryZeroLaplace::apply(Field3D &f, BoutReal UNUSED(t)) { + Mesh *localmesh = f.getMesh(); + + int ncz = localmesh->LocalNz; Coordinates *metric = f.getCoordinates(); @@ -2452,8 +1907,8 @@ void BoundaryZeroLaplace::apply(Field3D &f) { int y = bndry->y; // Take FFT of last 2 points in domain - rfft(f(x - bx, y), mesh->LocalNz, c0.begin()); - rfft(f(x - 2 * bx, y), mesh->LocalNz, c1.begin()); + rfft(f(x - bx, y), localmesh->LocalNz, c0.begin()); + rfft(f(x - 2 * bx, y), localmesh->LocalNz, c1.begin()); c1[0] = c0[0] - c1[0]; // Only need gradient // Solve metric->g11*d2f/dx2 - metric->g33*kz^2f = 0 @@ -2472,7 +1927,7 @@ void BoundaryZeroLaplace::apply(Field3D &f) { c0[jz] *= exp(coef * kwave); // The decaying solution only } // Reverse FFT - irfft(c0.begin(), mesh->LocalNz, f(x, y)); + irfft(c0.begin(), localmesh->LocalNz, f(x, y)); bndry->nextX(); x = bndry->x; @@ -2483,16 +1938,13 @@ void BoundaryZeroLaplace::apply(Field3D &f) { /////////////////////////////////////////////////////////////// -BoundaryOp *BoundaryZeroLaplace2::clone(BoundaryRegion *region, - const list &args) { - verifyNumPoints(region, 3); - if (!args.empty()) { - output << "WARNING: Ignoring arguments to BoundaryZeroLaplace2\n"; - } - return new BoundaryZeroLaplace2(region); +BoundaryOp * +BoundaryZeroLaplace2::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + return boundaryCloneNoArguments(region, args, keywords); } -void BoundaryZeroLaplace2::apply(Field2D &f) { +void BoundaryZeroLaplace2::apply(Field2D &f, BoutReal UNUSED(t)) { if ((bndry->location != BNDRY_XIN) && (bndry->location != BNDRY_XOUT)) { // Can't apply this boundary condition to non-X boundaries throw BoutException( @@ -2518,11 +1970,13 @@ void BoundaryZeroLaplace2::apply(Field2D &f) { } } -void BoundaryZeroLaplace2::apply(Field3D &f) { - int ncz = mesh->LocalNz; +void BoundaryZeroLaplace2::apply(Field3D &f, BoutReal UNUSED(t)) { + Mesh *localmesh = f.getMesh(); + + int ncz = localmesh->LocalNz; ASSERT0(ncz % 2 == 0); // Allocation assumes even number - + // allocate memory Array c0(ncz / 2 + 1), c1(ncz / 2 + 1), c2(ncz / 2 + 1); @@ -2571,77 +2025,81 @@ void BoundaryZeroLaplace2::apply(Field3D &f) { /////////////////////////////////////////////////////////////// -BoundaryOp* BoundaryConstLaplace::clone(BoundaryRegion *region, const list &args) { - verifyNumPoints(region,2); - if(!args.empty()) { - output << "WARNING: Ignoring arguments to BoundaryConstLaplace\n"; - } - return new BoundaryConstLaplace(region); +BoundaryOp * +BoundaryConstLaplace::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + return boundaryCloneNoArguments(region, args, keywords); } -void BoundaryConstLaplace::apply(Field2D &f) { - if((bndry->location != BNDRY_XIN) && (bndry->location != BNDRY_XOUT)) { +void BoundaryConstLaplace::apply(Field2D &f, BoutReal UNUSED(t)) { + if ((bndry->location != BNDRY_XIN) && (bndry->location != BNDRY_XOUT)) { // Can't apply this boundary condition to non-X boundaries - throw BoutException("ERROR: Can't apply Zero Laplace condition to non-X boundaries\n"); + throw BoutException( + "ERROR: Can't apply Zero Laplace condition to non-X boundaries\n"); } - + // Constant X second derivative int bx = bndry->bx; // Loop over the Y dimension - for(bndry->first(); !bndry->isDone(); bndry->nextY()) { + for (bndry->first(); !bndry->isDone(); bndry->nextY()) { int x = bndry->x; int y = bndry->y; // Calculate the Laplacian on the last point - dcomplex la,lb,lc; - laplace_tridag_coefs(x-2*bx, y, 0, la, lb, lc); - dcomplex val = la*f(x-bx-1,y) + lb*f(x-2*bx,y) + lc*f(x-2*bx+1,y); + dcomplex la, lb, lc; + laplace_tridag_coefs(x - 2 * bx, y, 0, la, lb, lc); + dcomplex val = + la * f(x - bx - 1, y) + lb * f(x - 2 * bx, y) + lc * f(x - 2 * bx + 1, y); // Loop in X towards edge of domain do { - laplace_tridag_coefs(x-bx, y, 0, la, lb, lc); - if(bx < 0) { // Lower X - f(x,y) = ((val - lb*f(x-bx,y) + lc*f(x-2*bx,y)) / la).real(); - }else // Upper X - f(x,y) = ((val - lb*f(x-bx,y) + la*f(x-2*bx,y)) / lc).real(); - + laplace_tridag_coefs(x - bx, y, 0, la, lb, lc); + if (bx < 0) { // Lower X + f(x, y) = ((val - lb * f(x - bx, y) + lc * f(x - 2 * bx, y)) / la).real(); + } else // Upper X + f(x, y) = ((val - lb * f(x - bx, y) + la * f(x - 2 * bx, y)) / lc).real(); + bndry->nextX(); - x = bndry->x; y = bndry->y; - }while(!bndry->isDone()); + x = bndry->x; + y = bndry->y; + } while (!bndry->isDone()); } } -void BoundaryConstLaplace::apply(Field3D &f) { - if((bndry->location != BNDRY_XIN) && (bndry->location != BNDRY_XOUT)) { +void BoundaryConstLaplace::apply(Field3D &f, BoutReal UNUSED(t)) { + if ((bndry->location != BNDRY_XIN) && (bndry->location != BNDRY_XOUT)) { // Can't apply this boundary condition to non-X boundaries - throw BoutException("ERROR: Can't apply Zero Laplace condition to non-X boundaries\n"); + throw BoutException( + "ERROR: Can't apply Zero Laplace condition to non-X boundaries\n"); } - + + Mesh *localmesh = f.getMesh(); + Coordinates *metric = f.getCoordinates(); - - int ncz = mesh->LocalNz; + + int ncz = localmesh->LocalNz; // Allocate memory - Array c0(ncz/2 + 1), c1(ncz/2 + 1), c2(ncz/2 + 1); - + Array c0(ncz / 2 + 1), c1(ncz / 2 + 1), c2(ncz / 2 + 1); + int bx = bndry->bx; // Loop over the Y dimension - for(bndry->first(); !bndry->isDone(); bndry->nextY()) { + for (bndry->first(); !bndry->isDone(); bndry->nextY()) { int x = bndry->x; int y = bndry->y; - + // Take FFT of last 3 points in domain - rfft(f(x-bx,y), ncz, c0.begin()); - rfft(f(x-2*bx,y), ncz, c1.begin()); - rfft(f(x-3*bx,y), ncz, c2.begin()); - dcomplex k0lin = (c1[0] - c0[0])/metric->dx(x-bx,y); // for kz=0 solution - + rfft(f(x - bx, y), ncz, c0.begin()); + rfft(f(x - 2 * bx, y), ncz, c1.begin()); + rfft(f(x - 3 * bx, y), ncz, c2.begin()); + dcomplex k0lin = (c1[0] - c0[0]) / metric->dx(x - bx, y); // for kz=0 solution + // Calculate Delp2 on point MXG+1 (and put into c1) - for(int jz=0;jz<=ncz/2;jz++) { - dcomplex la,lb,lc; - laplace_tridag_coefs(x-2*bx, y, jz, la, lb, lc); - if(bx < 0) { // Inner X - c1[jz] = la*c0[jz] + lb*c1[jz] + lc*c2[jz]; - }else { // Outer X - c1[jz] = la*c2[jz] + lb*c1[jz] + lc*c0[jz]; + for (int jz = 0; jz <= ncz / 2; jz++) { + dcomplex la, lb, lc; + laplace_tridag_coefs(x - 2 * bx, y, jz, la, lb, lc); + if (bx < 0) { // Inner X + c1[jz] = la * c0[jz] + lb * c1[jz] + lc * c2[jz]; + } else { // Outer X + c1[jz] = la * c2[jz] + lb * c1[jz] + lc * c0[jz]; } } // Solve metric->g11*d2f/dx2 - metric->g33*kz^2f = 0 @@ -2650,32 +2108,32 @@ void BoundaryConstLaplace::apply(Field3D &f) { // Loop in X towards edge of domain do { // kz = 0 solution - xpos -= metric->dx(x,y); - c2[0] = c0[0] + k0lin*xpos + 0.5*c1[0]*xpos*xpos/metric->g11(x-bx,y); + xpos -= metric->dx(x, y); + c2[0] = c0[0] + k0lin * xpos + 0.5 * c1[0] * xpos * xpos / metric->g11(x - bx, y); // kz != 0 solution - BoutReal coef = -1.0*sqrt(metric->g33(x-bx,y) / metric->g11(x-bx,y))*metric->dx(x-bx,y); - for(int jz=1;jz<=ncz/2;jz++) { - BoutReal kwave=jz*2.0*PI/metric->zlength(); // wavenumber in [rad^-1] - c0[jz] *= exp(coef*kwave); // The decaying solution only - // Add the particular solution - c2[jz] = c0[jz] - c1[jz]/(metric->g33(x-bx,y)*kwave*kwave); + BoutReal coef = -1.0 * sqrt(metric->g33(x - bx, y) / metric->g11(x - bx, y)) * + metric->dx(x - bx, y); + for (int jz = 1; jz <= ncz / 2; jz++) { + BoutReal kwave = jz * 2.0 * PI / metric->zlength(); // wavenumber in [rad^-1] + c0[jz] *= exp(coef * kwave); // The decaying solution only + // Add the particular solution + c2[jz] = c0[jz] - c1[jz] / (metric->g33(x - bx, y) * kwave * kwave); } // Reverse FFT - irfft(c2.begin(), ncz, f(x,y)); - + irfft(c2.begin(), ncz, f(x, y)); + bndry->nextX(); - x = bndry->x; y = bndry->y; - }while(!bndry->isDone()); + x = bndry->x; + y = bndry->y; + } while (!bndry->isDone()); } } /////////////////////////////////////////////////////////////// -BoundaryOp* BoundaryDivCurl::clone(BoundaryRegion *region, const list &args) { - if(!args.empty()) { - output << "WARNING: Ignoring arguments to BoundaryDivCurl\n"; - } - return new BoundaryDivCurl(region); +BoundaryOp *BoundaryDivCurl::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + return boundaryCloneNoArguments(region, args, keywords); } void BoundaryDivCurl::apply(Vector2D &UNUSED(f)) { @@ -2685,83 +2143,105 @@ void BoundaryDivCurl::apply(Vector2D &UNUSED(f)) { void BoundaryDivCurl::apply(Vector3D &var) { int jx, jy, jz, jzp, jzm; BoutReal tmp; - - Coordinates *metric = mesh->getCoordinates(var.getLocation()); - - int ncz = mesh->LocalNz; - - if(bndry->location != BNDRY_XOUT) { + + Mesh *localmesh = var.x.getMesh(); + + Coordinates *metric = localmesh->getCoordinates(var.getLocation()); + + int ncz = localmesh->LocalNz; + + if (bndry->location != BNDRY_XOUT) { throw BoutException("ERROR: DivCurl boundary only works for outer X currently\n"); } var.toCovariant(); - - if(mesh->xstart > 2) { - throw BoutException("Error: Div = Curl = 0 boundary condition doesn't work for MXG > 2. Sorry\n"); + + if (localmesh->xstart > 2) { + throw BoutException( + "Error: Div = Curl = 0 boundary condition doesn't work for MXG > 2. Sorry\n"); } - jx = mesh->xend+1; - for(jy=1;jyLocalNy-1;jy++) { - for(jz=0;jzxend + 1; + for (jy = 1; jy < localmesh->LocalNy - 1; jy++) { + for (jz = 0; jz < ncz; jz++) { + jzp = (jz + 1) % ncz; jzm = (jz - 1 + ncz) % ncz; // dB_y / dx = dB_x / dy - + // dB_x / dy - tmp = (var.x(jx-1,jy+1,jz) - var.x(jx-1,jy-1,jz)) / (metric->dy(jx-1,jy-1) + metric->dy(jx-1,jy)); - - var.y(jx,jy,jz) = var.y(jx-2,jy,jz) + (metric->dx(jx-2,jy) + metric->dx(jx-1,jy)) * tmp; - if(mesh->xstart == 2) - // 4th order to get last point - var.y(jx+1,jy,jz) = var.y(jx-3,jy,jz) + 4.*metric->dx(jx,jy)*tmp; - + tmp = (var.x(jx - 1, jy + 1, jz) - var.x(jx - 1, jy - 1, jz)) / + (metric->dy(jx - 1, jy - 1) + metric->dy(jx - 1, jy)); + + var.y(jx, jy, jz) = + var.y(jx - 2, jy, jz) + (metric->dx(jx - 2, jy) + metric->dx(jx - 1, jy)) * tmp; + if (localmesh->xstart == 2) + // 4th order to get last point + var.y(jx + 1, jy, jz) = var.y(jx - 3, jy, jz) + 4. * metric->dx(jx, jy) * tmp; + // dB_z / dx = dB_x / dz - - tmp = (var.x(jx-1,jy,jzp) - var.x(jx-1,jy,jzm)) / (2.*metric->dz); - - var.z(jx,jy,jz) = var.z(jx-2,jy,jz) + (metric->dx(jx-2,jy) + metric->dx(jx-1,jy)) * tmp; - if(mesh->xstart == 2) - var.z(jx+1,jy,jz) = var.z(jx-3,jy,jz) + 4.*metric->dx(jx,jy)*tmp; - - // d/dx( Jmetric->g11 B_x ) = - d/dx( Jmetric->g12 B_y + Jmetric->g13 B_z) + + tmp = (var.x(jx - 1, jy, jzp) - var.x(jx - 1, jy, jzm)) / (2. * metric->dz); + + var.z(jx, jy, jz) = + var.z(jx - 2, jy, jz) + (metric->dx(jx - 2, jy) + metric->dx(jx - 1, jy)) * tmp; + if (localmesh->xstart == 2) + var.z(jx + 1, jy, jz) = var.z(jx - 3, jy, jz) + 4. * metric->dx(jx, jy) * tmp; + + // d/dx( Jmetric->g11 B_x ) = - d/dx( Jmetric->g12 B_y + Jmetric->g13 B_z) // - d/dy( JB^y ) - d/dz( JB^z ) - - tmp = -( metric->J(jx,jy)*metric->g12(jx,jy)*var.y(jx,jy,jz) + metric->J(jx,jy)*metric->g13(jx,jy)*var.z(jx,jy,jz) - - metric->J(jx-2,jy)*metric->g12(jx-2,jy)*var.y(jx-2,jy,jz) + metric->J(jx-2,jy)*metric->g13(jx-2,jy)*var.z(jx-2,jy,jz) ) - / (metric->dx(jx-2,jy) + metric->dx(jx-1,jy)); // First term (d/dx) using vals calculated above - tmp -= (metric->J(jx-1,jy+1)*metric->g12(jx-1,jy+1)*var.x(jx-1,jy+1,jz) - metric->J(jx-1,jy-1)*metric->g12(jx-1,jy-1)*var.x(jx-1,jy-1,jz) - + metric->J(jx-1,jy+1)*metric->g22(jx-1,jy+1)*var.y(jx-1,jy+1,jz) - metric->J(jx-1,jy-1)*metric->g22(jx-1,jy-1)*var.y(jx-1,jy-1,jz) - + metric->J(jx-1,jy+1)*metric->g23(jx-1,jy+1)*var.z(jx-1,jy+1,jz) - metric->J(jx-1,jy-1)*metric->g23(jx-1,jy-1)*var.z(jx-1,jy-1,jz)) - / (metric->dy(jx-1,jy-1) + metric->dy(jx-1,jy)); // second (d/dy) - tmp -= (metric->J(jx-1,jy)*metric->g13(jx-1,jy)*(var.x(jx-1,jy,jzp) - var.x(jx-1,jy,jzm)) + - metric->J(jx-1,jy)*metric->g23(jx-1,jy)*(var.y(jx-1,jy,jzp) - var.y(jx-1,jy,jzm)) + - metric->J(jx-1,jy)*metric->g33(jx-1,jy)*(var.z(jx-1,jy,jzp) - var.z(jx-1,jy,jzm))) / (2.*metric->dz); - - var.x(jx,jy,jz) = ( metric->J(jx-2,jy)*metric->g11(jx-2,jy)*var.x(jx-2,jy,jz) + - (metric->dx(jx-2,jy) + metric->dx(jx-1,jy)) * tmp ) / metric->J(jx,jy)*metric->g11(jx,jy); - if(mesh->xstart == 2) - var.x(jx+1,jy,jz) = ( metric->J(jx-3,jy)*metric->g11(jx-3,jy)*var.x(jx-3,jy,jz) + - 4.*metric->dx(jx,jy)*tmp ) / metric->J(jx+1,jy)*metric->g11(jx+1,jy); + + tmp = -(metric->J(jx, jy) * metric->g12(jx, jy) * var.y(jx, jy, jz) + + metric->J(jx, jy) * metric->g13(jx, jy) * var.z(jx, jy, jz) - + metric->J(jx - 2, jy) * metric->g12(jx - 2, jy) * var.y(jx - 2, jy, jz) + + metric->J(jx - 2, jy) * metric->g13(jx - 2, jy) * var.z(jx - 2, jy, jz)) / + (metric->dx(jx - 2, jy) + + metric->dx(jx - 1, jy)); // First term (d/dx) using vals calculated above + tmp -= (metric->J(jx - 1, jy + 1) * metric->g12(jx - 1, jy + 1) * + var.x(jx - 1, jy + 1, jz) - + metric->J(jx - 1, jy - 1) * metric->g12(jx - 1, jy - 1) * + var.x(jx - 1, jy - 1, jz) + + metric->J(jx - 1, jy + 1) * metric->g22(jx - 1, jy + 1) * + var.y(jx - 1, jy + 1, jz) - + metric->J(jx - 1, jy - 1) * metric->g22(jx - 1, jy - 1) * + var.y(jx - 1, jy - 1, jz) + + metric->J(jx - 1, jy + 1) * metric->g23(jx - 1, jy + 1) * + var.z(jx - 1, jy + 1, jz) - + metric->J(jx - 1, jy - 1) * metric->g23(jx - 1, jy - 1) * + var.z(jx - 1, jy - 1, jz)) / + (metric->dy(jx - 1, jy - 1) + metric->dy(jx - 1, jy)); // second (d/dy) + tmp -= (metric->J(jx - 1, jy) * metric->g13(jx - 1, jy) * + (var.x(jx - 1, jy, jzp) - var.x(jx - 1, jy, jzm)) + + metric->J(jx - 1, jy) * metric->g23(jx - 1, jy) * + (var.y(jx - 1, jy, jzp) - var.y(jx - 1, jy, jzm)) + + metric->J(jx - 1, jy) * metric->g33(jx - 1, jy) * + (var.z(jx - 1, jy, jzp) - var.z(jx - 1, jy, jzm))) / + (2. * metric->dz); + + var.x(jx, jy, jz) = + (metric->J(jx - 2, jy) * metric->g11(jx - 2, jy) * var.x(jx - 2, jy, jz) + + (metric->dx(jx - 2, jy) + metric->dx(jx - 1, jy)) * tmp) / + metric->J(jx, jy) * metric->g11(jx, jy); + if (localmesh->xstart == 2) + var.x(jx + 1, jy, jz) = + (metric->J(jx - 3, jy) * metric->g11(jx - 3, jy) * var.x(jx - 3, jy, jz) + + 4. * metric->dx(jx, jy) * tmp) / + metric->J(jx + 1, jy) * metric->g11(jx + 1, jy); } } } /////////////////////////////////////////////////////////////// -BoundaryOp* BoundaryFree::clone(BoundaryRegion *region, const list &args) { - if(!args.empty()) { - // First argument should be a value - val = stringToReal(args.front()); - return new BoundaryFree(region, val); - } - return new BoundaryFree(region); +BoundaryOp *BoundaryFree::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + return boundaryCloneNoArguments(region, args, keywords); } -void BoundaryFree::apply(Field2D &UNUSED(f)) { +void BoundaryFree::apply(Field2D &UNUSED(f), BoutReal UNUSED(t)) { // Do nothing for free boundary } -void BoundaryFree::apply(Field3D &UNUSED(f)) { +void BoundaryFree::apply(Field3D &UNUSED(f), BoutReal UNUSED(t)) { // Do nothing for free boundary } @@ -2772,500 +2252,162 @@ void BoundaryFree::apply_ddt(Field2D &UNUSED(f)) { void BoundaryFree::apply_ddt(Field3D &UNUSED(f)) { // Do nothing for free boundary } + /////////////////////////////////////////////////////////////// -// New free boundary implementation. Uses last grid points to extrapolate into the guard cells. -// Written by L. Easy. +// New free boundary implementation. Uses last grid points to extrapolate into the guard +// cells. +// Written by L. Easy. /////////////////////////////////////////////////////////////// // 2nd order extrapolation: -BoundaryOp* BoundaryFree_O2::clone(BoundaryRegion *region, const list &args){ - verifyNumPoints(region,2); - if(!args.empty()) { - output << "WARNING: Ignoring arguments to BoundaryFree\n"; - } - return new BoundaryFree_O2(region) ; +BoundaryOp *BoundaryFree_O2::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + return boundaryCloneNoArguments(region, args, keywords); } -void BoundaryFree_O2::apply(Field2D &f) { - // Set (at 2nd order) the value at the mid-point between the guard cell and the grid cell to be val - // N.B. Only first guard cells (closest to the grid) should ever be used - - bndry->first(); - - // Check for staggered grids - - CELL_LOC loc = f.getLocation(); - if(mesh->StaggerGrids && loc != CELL_CENTRE) { - // Staggered. Need to apply slightly differently - - if( loc == CELL_XLOW) { - // Field is shifted in X - - if(bndry->bx > 0) { - // Outer x boundary - for(; !bndry->isDone(); bndry->next1d()) { - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 2*f(xi - bndry->bx, yi -bndry->by) - f(xi- 2*bndry->bx, yi - 2*bndry->by); - } - } - } - if(bndry->bx < 0) { - // Inner x boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - for(int i=-1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 2*f(xi - bndry->bx, yi -bndry->by) - f(xi- 2*bndry->bx, yi - 2*bndry->by); - } - } - } - if(bndry->by != 0){ - // y boundaries - for(; !bndry->isDone(); bndry->next1d()) { - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 2*f(xi - bndry->bx, yi -bndry->by) - f(xi- 2*bndry->bx, yi - 2*bndry->by); - } - - } - } - } - else if( loc == CELL_YLOW ) { - // Field is shifted in Y - - if(bndry->by > 0) { - // Upper y boundary - - for(; !bndry->isDone(); bndry->next1d()) { - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 2*f(xi - bndry->bx, yi -bndry->by) - f(xi- 2*bndry->bx, yi - 2*bndry->by); - } - } - } - if(bndry->by < 0) { - // Lower y boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - for(int i=-1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 2*f(xi - bndry->bx, yi -bndry->by) - f(xi- 2*bndry->bx, yi - 2*bndry->by); - } - } - } - if(bndry->bx != 0){ - // x boundaries - for(; !bndry->isDone(); bndry->next1d()) { - - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 2*f(xi - bndry->bx, yi -bndry->by) - f(xi- 2*bndry->bx, yi - 2*bndry->by); - } - } - } - } - } - else { - // Non-staggered, standard case - - for(; !bndry->isDone(); bndry->next1d()) { - - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 2*f(xi - bndry->bx, yi -bndry->by) - f(xi- 2*bndry->bx, yi - 2*bndry->by); - } - } - } +inline void BoundaryFree_O2::applyAtPoint(Field2D &f, BoutReal UNUSED(val), int x, int bx, + int y, int by, int z, BoutReal UNUSED(delta)) { + extrapolate2nd(f, x, bx, y, by, z); } - -void BoundaryFree_O2::apply(Field3D &f) { - // Extrapolate from the last evolved simulation cells into the guard cells at 3rd order. - - bndry->first(); - - - // Check for staggered grids - - CELL_LOC loc = f.getLocation(); - if(mesh->StaggerGrids && loc != CELL_CENTRE) { - // Staggered. Need to apply slightly differently - - if( loc == CELL_XLOW ) { - // Field is shifted in X - - if(bndry->bx > 0) { - // Outer x boundary - - for(; !bndry->isDone(); bndry->next1d()) { - - for(int zk=0;zkLocalNz;zk++) { - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 2*f(xi - bndry->bx, yi -bndry->by, zk) - f(xi- 2*bndry->bx, yi - 2*bndry->by, zk); - } - } - } - } - if(bndry->bx < 0) { - // Inner x boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - - for(int zk=0;zkLocalNz;zk++) { - for(int i=-1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 2*f(xi - bndry->bx, yi -bndry->by, zk) - f(xi- 2*bndry->bx, yi - 2*bndry->by, zk); - } - } - } - } - if(bndry->by != 0){ - //y boundaries - - for(; !bndry->isDone(); bndry->next1d()) { - - for(int zk=0;zkLocalNz;zk++) { - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 2*f(xi - bndry->bx, yi -bndry->by, zk) - f(xi- 2*bndry->bx, yi - 2*bndry->by, zk); - } - } - } - } - } - else if( loc == CELL_YLOW ) { - // Field is shifted in Y - - if(bndry->by > 0) { - // Upper y boundary - for(; !bndry->isDone(); bndry->next1d()) { - for(int zk=0;zkLocalNz;zk++) { - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 2*f(xi - bndry->bx, yi -bndry->by, zk) - f(xi- 2*bndry->bx, yi - 2*bndry->by, zk); - } - } - } - } - if(bndry->by < 0) { - // Lower y boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - - for(int zk=0;zkLocalNz;zk++) { - for(int i=-1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 2*f(xi - bndry->bx, yi -bndry->by, zk) - f(xi- 2*bndry->bx, yi - 2*bndry->by, zk); - } - } - } - } - if(bndry->bx != 0){ - // x boundaries - for(; !bndry->isDone(); bndry->next1d()) { - - for(int zk=0;zkLocalNz;zk++) { - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 2*f(xi - bndry->bx, yi -bndry->by, zk) - f(xi- 2*bndry->bx, yi - 2*bndry->by, zk); - } - } - } - } - } - } - else { - // Standard (non-staggered) case - for(; !bndry->isDone(); bndry->next1d()) { - - for(int zk=0;zkLocalNz;zk++) { - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 2*f(xi - bndry->bx, yi -bndry->by, zk) - f(xi- 2*bndry->bx, yi - 2*bndry->by, zk); - } - } - } - } +inline void BoundaryFree_O2::applyAtPoint(Field3D &f, BoutReal UNUSED(val), int x, int bx, + int y, int by, int z, BoutReal UNUSED(delta)) { + extrapolate2nd(f, x, bx, y, by, z); } -void BoundaryFree_O2::apply_ddt(Field2D &f) { - Field2D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - (*dt)(bndry->x,bndry->y) = 0.; // Set time derivative to zero +inline void BoundaryFree_O2::applyAtPointStaggered(Field2D &f, BoutReal UNUSED(val), + int x, int bx, int y, int by, int z, + BoutReal UNUSED(delta)) { + extrapolate2nd(f, x, bx, y, by, z); +} +inline void BoundaryFree_O2::applyAtPointStaggered(Field3D &f, BoutReal UNUSED(val), + int x, int bx, int y, int by, int z, + BoutReal UNUSED(delta)) { + extrapolate2nd(f, x, bx, y, by, z); } -void BoundaryFree_O2::apply_ddt(Field3D &f) { - Field3D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - for(int z=0;zLocalNz;z++) - (*dt)(bndry->x,bndry->y,z) = 0.; // Set time derivative to zero - +inline void BoundaryFree_O2::extrapolateFurther(Field2D &f, int x, int bx, int y, int by, + int z) { + extrapolate2nd(f, x, bx, y, by, z); +} +inline void BoundaryFree_O2::extrapolateFurther(Field3D &f, int x, int bx, int y, int by, + int z) { + extrapolate2nd(f, x, bx, y, by, z); } ////////////////////////////////// // Third order extrapolation: ////////////////////////////////// -BoundaryOp* BoundaryFree_O3::clone(BoundaryRegion *region, const list &args){ - verifyNumPoints(region,3); +BoundaryOp *BoundaryFree_O3::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + return boundaryCloneNoArguments(region, args, keywords); +} - if(!args.empty()) { - output << "WARNING: Ignoring arguments to BoundaryConstLaplace\n"; - } - return new BoundaryFree_O3(region) ; +inline void BoundaryFree_O3::applyAtPoint(Field2D &f, BoutReal UNUSED(val), int x, int bx, + int y, int by, int z, BoutReal UNUSED(delta)) { + extrapolate3rd(f, x, bx, y, by, z); +} +inline void BoundaryFree_O3::applyAtPoint(Field3D &f, BoutReal UNUSED(val), int x, int bx, + int y, int by, int z, BoutReal UNUSED(delta)) { + extrapolate3rd(f, x, bx, y, by, z); } -void BoundaryFree_O3::apply(Field2D &f) { +inline void BoundaryFree_O3::applyAtPointStaggered(Field2D &f, BoutReal UNUSED(val), + int x, int bx, int y, int by, int z, + BoutReal UNUSED(delta)) { + extrapolate3rd(f, x, bx, y, by, z); +} +inline void BoundaryFree_O3::applyAtPointStaggered(Field3D &f, BoutReal UNUSED(val), + int x, int bx, int y, int by, int z, + BoutReal UNUSED(delta)) { + extrapolate3rd(f, x, bx, y, by, z); +} - bndry->first(); +inline void BoundaryFree_O3::extrapolateFurther(Field2D &f, int x, int bx, int y, int by, + int z) { + extrapolate3rd(f, x, bx, y, by, z); +} +inline void BoundaryFree_O3::extrapolateFurther(Field3D &f, int x, int bx, int y, int by, + int z) { + extrapolate3rd(f, x, bx, y, by, z); +} - // Check for staggered grids - - CELL_LOC loc = f.getLocation(); - if(mesh->StaggerGrids && loc != CELL_CENTRE) { - // Staggered. Need to apply slightly differently - - if( loc == CELL_XLOW) { - // Field is shifted in X - - if(bndry->bx > 0) { - // Outer x boundary - for(; !bndry->isDone(); bndry->next1d()) { - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 3.0*f(xi - bndry->bx, yi - bndry->by) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) + f(xi - 3*bndry->bx, yi - 3*bndry->by); - } - } - } - if(bndry->bx < 0) { - // Inner x boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - for(int i=-1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 3.0*f(xi - bndry->bx, yi - bndry->by) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) + f(xi - 3*bndry->bx, yi - 3*bndry->by); - } - } - } - if(bndry->by != 0){ - // y boundaries - for(; !bndry->isDone(); bndry->next1d()) { - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 3.0*f(xi - bndry->bx, yi - bndry->by) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) + f(xi - 3*bndry->bx, yi - 3*bndry->by); - } - - } - } - } - else if( loc == CELL_YLOW ) { - // Field is shifted in Y - - if(bndry->by > 0) { - // Upper y boundary - - for(; !bndry->isDone(); bndry->next1d()) { - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 3.0*f(xi - bndry->bx, yi - bndry->by) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) + f(xi - 3*bndry->bx, yi - 3*bndry->by); - } - } - } - if(bndry->by < 0) { - // Lower y boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - for(int i=-1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 3.0*f(xi - bndry->bx, yi - bndry->by) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) + f(xi - 3*bndry->bx, yi - 3*bndry->by); - } - - } - } - if(bndry->bx != 0){ - // x boundaries - for(; !bndry->isDone(); bndry->next1d()) { - - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 3.0*f(xi - bndry->bx, yi - bndry->by) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) + f(xi - 3*bndry->bx, yi - 3*bndry->by); - } - } - } - } - } - else { - // Non-staggered, standard case - - for(; !bndry->isDone(); bndry->next1d()) { - - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi) = 3.0*f(xi - bndry->bx, yi - bndry->by) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by) + f(xi - 3*bndry->bx, yi - 3*bndry->by); - } - } - } +// Fourth order extrapolation: +BoundaryOp *BoundaryFree_O4::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + return boundaryCloneNoArguments(region, args, keywords); } -void BoundaryFree_O3::apply(Field3D &f) { - // Extrapolate from the last evolved simulation cells into the guard cells at 3rd order. +inline void BoundaryFree_O4::applyAtPoint(Field2D &f, BoutReal UNUSED(val), int x, int bx, + int y, int by, int z, BoutReal UNUSED(delta)) { + extrapolate4th(f, x, bx, y, by, z); +} +inline void BoundaryFree_O4::applyAtPoint(Field3D &f, BoutReal UNUSED(val), int x, int bx, + int y, int by, int z, BoutReal UNUSED(delta)) { + extrapolate4th(f, x, bx, y, by, z); +} - bndry->first(); +inline void BoundaryFree_O4::applyAtPointStaggered(Field2D &f, BoutReal UNUSED(val), + int x, int bx, int y, int by, int z, + BoutReal UNUSED(delta)) { + extrapolate4th(f, x, bx, y, by, z); +} +inline void BoundaryFree_O4::applyAtPointStaggered(Field3D &f, BoutReal UNUSED(val), + int x, int bx, int y, int by, int z, + BoutReal UNUSED(delta)) { + extrapolate4th(f, x, bx, y, by, z); +} +inline void BoundaryFree_O4::extrapolateFurther(Field2D &f, int x, int bx, int y, int by, + int z) { + extrapolate4th(f, x, bx, y, by, z); +} +inline void BoundaryFree_O4::extrapolateFurther(Field3D &f, int x, int bx, int y, int by, + int z) { + extrapolate4th(f, x, bx, y, by, z); +} - // Check for staggered grids - - CELL_LOC loc = f.getLocation(); - if(mesh->StaggerGrids && loc != CELL_CENTRE) { - // Staggered. Need to apply slightly differently - - if( loc == CELL_XLOW ) { - // Field is shifted in X - - if(bndry->bx > 0) { - // Outer x boundary - - for(; !bndry->isDone(); bndry->next1d()) { - - for(int zk=0;zkLocalNz;zk++) { - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + f(xi - 3*bndry->bx, yi - 3*bndry->by, zk); - } - } - } - } - if(bndry->bx < 0) { - // Inner x boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - - for(int zk=0;zkLocalNz;zk++) { - for(int i=-1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + f(xi - 3*bndry->bx, yi - 3*bndry->by, zk); - } - } - } - } - if(bndry->by != 0){ - //y boundaries - - for(; !bndry->isDone(); bndry->next1d()) { - - for(int zk=0;zkLocalNz;zk++) { - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + f(xi - 3*bndry->bx, yi - 3*bndry->by, zk); - } - } - } - } - } - else if( loc == CELL_YLOW ) { - // Field is shifted in Y - - if(bndry->by > 0) { - // Upper y boundary - for(; !bndry->isDone(); bndry->next1d()) { - for(int zk=0;zkLocalNz;zk++) { - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + f(xi - 3*bndry->bx, yi - 3*bndry->by, zk); - } - } - } - } - if(bndry->by < 0) { - // Lower y boundary. Set one point inwards - for(; !bndry->isDone(); bndry->next1d()) { - - for(int zk=0;zkLocalNz;zk++) { - for(int i=-1;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + f(xi - 3*bndry->bx, yi - 3*bndry->by, zk); - } - } - } - } - if(bndry->bx != 0){ - // x boundaries - for(; !bndry->isDone(); bndry->next1d()) { - - for(int zk=0;zkLocalNz;zk++) { - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + f(xi - 3*bndry->bx, yi - 3*bndry->by, zk); - } - - } - } - } - } - } - else { - // Standard (non-staggered) case - for(; !bndry->isDone(); bndry->next1d()) { - - for(int zk=0;zkLocalNz;zk++) { - for(int i=0;iwidth;i++) { - int xi = bndry->x + i*bndry->bx; - int yi = bndry->y + i*bndry->by; - f(xi, yi, zk) = 3.0*f(xi - bndry->bx, yi - bndry->by, zk) - 3.0*f(xi - 2*bndry->bx, yi - 2*bndry->by, zk) - + f(xi - 3*bndry->bx, yi - 3*bndry->by, zk); - } - } - } - } +// Fifth order extrapolation: +BoundaryOp *BoundaryFree_O5::clone(BoundaryRegion *region, const list &args, + const std::map &keywords) { + return boundaryCloneNoArguments(region, args, keywords); } -void BoundaryFree_O3::apply_ddt(Field2D &f) { - Field2D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - (*dt)(bndry->x,bndry->y) = 0.; // Set time derivative to zero +inline void BoundaryFree_O5::applyAtPoint(Field2D &f, BoutReal UNUSED(val), int x, int bx, + int y, int by, int z, BoutReal UNUSED(delta)) { + extrapolate5th(f, x, bx, y, by, z); +} +inline void BoundaryFree_O5::applyAtPoint(Field3D &f, BoutReal UNUSED(val), int x, int bx, + int y, int by, int z, BoutReal UNUSED(delta)) { + extrapolate5th(f, x, bx, y, by, z); } -void BoundaryFree_O3::apply_ddt(Field3D &f) { - Field3D *dt = f.timeDeriv(); - for(bndry->first(); !bndry->isDone(); bndry->next()) - for(int z=0;zLocalNz;z++) - (*dt)(bndry->x,bndry->y,z) = 0.; // Set time derivative to zero +inline void BoundaryFree_O5::applyAtPointStaggered(Field2D &f, BoutReal UNUSED(val), + int x, int bx, int y, int by, int z, + BoutReal UNUSED(delta)) { + extrapolate5th(f, x, bx, y, by, z); +} +inline void BoundaryFree_O5::applyAtPointStaggered(Field3D &f, BoutReal UNUSED(val), + int x, int bx, int y, int by, int z, + BoutReal UNUSED(delta)) { + extrapolate5th(f, x, bx, y, by, z); +} +inline void BoundaryFree_O5::extrapolateFurther(Field2D &f, int x, int bx, int y, int by, + int z) { + extrapolate5th(f, x, bx, y, by, z); +} +inline void BoundaryFree_O5::extrapolateFurther(Field3D &f, int x, int bx, int y, int by, + int z) { + extrapolate5th(f, x, bx, y, by, z); } /////////////////////////////////////////////////////////////// -BoundaryOp* BoundaryRelax::cloneMod(BoundaryOp *operation, const list &args) { - BoundaryRelax* result = new BoundaryRelax(operation, r); - - if(!args.empty()) { +BoundaryOp *BoundaryRelax::cloneMod(BoundaryOp *operation, const list &args) { + BoundaryRelax *result = new BoundaryRelax(operation, r); + + if (!args.empty()) { // First argument should be the rate BoutReal val = stringToReal(args.front()); val = fabs(val); // Should always be positive @@ -3280,9 +2422,9 @@ void BoundaryRelax::apply(Field2D &f, BoutReal t) { op->apply(f, t); } -void BoundaryRelax::apply(Field3D &f, BoutReal UNUSED(t)) { +void BoundaryRelax::apply(Field3D &f, BoutReal t) { // Just apply the original boundary condition to f - op->apply(f); + op->apply(f, t); } void BoundaryRelax::apply_ddt(Field2D &f) { @@ -3292,155 +2434,141 @@ void BoundaryRelax::apply_ddt(Field2D &f) { Field2D g = f; // Apply the boundary to g op->apply(g); - + bndry->first(); - + // Set time-derivatives - for(bndry->first(); !bndry->isDone(); bndry->next()) { + for (bndry->first(); !bndry->isDone(); bndry->next()) { ddt(f)(bndry->x, bndry->y) = r * (g(bndry->x, bndry->y) - f(bndry->x, bndry->y)); } } void BoundaryRelax::apply_ddt(Field3D &f) { TRACE("BoundaryRelax::apply_ddt(Field3D)"); - + + Mesh *localmesh = f.getMesh(); + // Make a copy of f Field3D g = f; // NOTE: This is not very efficient... copying entire field // Apply the boundary to g op->apply(g); // Set time-derivatives - for(bndry->first(); !bndry->isDone(); bndry->next()) - for(int z=0;zLocalNz;z++) { - ddt(f)(bndry->x, bndry->y, z) = r * (g(bndry->x, bndry->y, z) - f(bndry->x, bndry->y, z)); + for (bndry->first(); !bndry->isDone(); bndry->next()) + for (int z = 0; z < localmesh->LocalNz; z++) { + ddt(f)(bndry->x, bndry->y, z) = + r * (g(bndry->x, bndry->y, z) - f(bndry->x, bndry->y, z)); } } /////////////////////////////////////////////////////////////// -BoundaryOp* BoundaryWidth::cloneMod(BoundaryOp *operation, const list &args) { - BoundaryWidth* result = new BoundaryWidth(operation, width); - +BoundaryWidth::BoundaryWidth(BoundaryOp *operation, int width) + : BoundaryModifier(operation) { + + // create a new BoundaryRegion, copied from the input one but with a + // different width + bndry = std::unique_ptr(op->bndry->copy(width)); + + // set BoundaryOp op to use the new bndry + op->bndry = bndry.get(); +} + +BoundaryOp* BoundaryWidth::cloneMod(BoundaryOp *operation, + const list &args) { + int width = -1; if(args.empty()) { output << "WARNING: BoundaryWidth expected 1 argument\n"; }else { // First argument should be the rate - int val = stringToInt(args.front()); - result->width = val; + width = stringToInt(args.front()); } - - return result; -} - -void BoundaryWidth::apply(Field2D &f, BoutReal t) { - // Pointer to boundary region shared between all BoundaryOp, BoundaryModifiers - int oldwid = bndry->width; - bndry->width = width; - op->apply(f, t); - bndry->width = oldwid; -} -void BoundaryWidth::apply(Field3D &f, BoutReal t) { - int oldwid = bndry->width; - bndry->width = width; - op->apply(f, t); - bndry->width = oldwid; -} - -void BoundaryWidth::apply_ddt(Field2D &f) { - int oldwid = bndry->width; - bndry->width = width; - op->apply_ddt(f); - bndry->width = oldwid; -} - -void BoundaryWidth::apply_ddt(Field3D &f) { - int oldwid = bndry->width; - bndry->width = width; - op->apply_ddt(f); - bndry->width = oldwid; + return new BoundaryWidth(operation, width); } /////////////////////////////////////////////////////////////// -BoundaryOp* BoundaryToFieldAligned::cloneMod(BoundaryOp *operation, const list &args) { - BoundaryToFieldAligned* result = new BoundaryToFieldAligned(operation); - - if(!args.empty()) { + +BoundaryOp *BoundaryToFieldAligned::cloneMod(BoundaryOp *operation, + const list &args) { + BoundaryToFieldAligned *result = new BoundaryToFieldAligned(operation); + + if (!args.empty()) { output << "WARNING: BoundaryToFieldAligned expected no argument\n"; - //Shouldn't we throw ? + // Shouldn't we throw ? } - + return result; } -void BoundaryToFieldAligned::apply(Field2D &f, BoutReal t) { - op->apply(f, t); -} +void BoundaryToFieldAligned::apply(Field2D &f, BoutReal t) { op->apply(f, t); } void BoundaryToFieldAligned::apply(Field3D &f, BoutReal t) { - //NOTE: This is not very efficient... updating entire field - f = mesh->fromFieldAligned(f); + Mesh *localmesh = f.getMesh(); + + // NOTE: This is not very efficient... updating entire field + f = localmesh->fromFieldAligned(f); // Apply the boundary to shifted field op->apply(f, t); - //Shift back - f = mesh->toFieldAligned(f); + // Shift back + f = localmesh->toFieldAligned(f); - //This is inefficient -- could instead use the shiftZ just in the bndry - //but this is not portable to other parallel transforms -- we could instead - //have a flag to define the region in which we want to apply to/fromFieldAligned -} - -void BoundaryToFieldAligned::apply_ddt(Field2D &f) { - op->apply_ddt(f); + // This is inefficient -- could instead use the shiftZ just in the bndry + // but this is not portable to other parallel transforms -- we could instead + // have a flag to define the region in which we want to apply to/fromFieldAligned } +void BoundaryToFieldAligned::apply_ddt(Field2D &f) { op->apply_ddt(f); } + void BoundaryToFieldAligned::apply_ddt(Field3D &f) { - f = mesh->fromFieldAligned(f); - ddt(f) = mesh->fromFieldAligned(ddt(f)); + Mesh *localmesh = f.getMesh(); + + f = localmesh->fromFieldAligned(f); + ddt(f) = localmesh->fromFieldAligned(ddt(f)); op->apply_ddt(f); - ddt(f) = mesh->toFieldAligned(ddt(f)); + ddt(f) = localmesh->toFieldAligned(ddt(f)); } - /////////////////////////////////////////////////////////////// -BoundaryOp* BoundaryFromFieldAligned::cloneMod(BoundaryOp *operation, const list &args) { - BoundaryFromFieldAligned* result = new BoundaryFromFieldAligned(operation); - - if(!args.empty()) { +BoundaryOp *BoundaryFromFieldAligned::cloneMod(BoundaryOp *operation, + const list &args) { + BoundaryFromFieldAligned *result = new BoundaryFromFieldAligned(operation); + + if (!args.empty()) { output << "WARNING: BoundaryFromFieldAligned expected no argument\n"; - //Shouldn't we throw ? + // Shouldn't we throw ? } - + return result; } -void BoundaryFromFieldAligned::apply(Field2D &f, BoutReal t) { - op->apply(f, t); -} +void BoundaryFromFieldAligned::apply(Field2D &f, BoutReal t) { op->apply(f, t); } void BoundaryFromFieldAligned::apply(Field3D &f, BoutReal t) { - //NOTE: This is not very efficient... shifting entire field - f = mesh->toFieldAligned(f); + Mesh *localmesh = f.getMesh(); + + // NOTE: This is not very efficient... shifting entire field + f = localmesh->toFieldAligned(f); // Apply the boundary to shifted field op->apply(f, t); - //Shift back - f = mesh->fromFieldAligned(f); + // Shift back + f = localmesh->fromFieldAligned(f); - //This is inefficient -- could instead use the shiftZ just in the bndry - //but this is not portable to other parallel transforms -- we could instead - //have a flag to define the region in which we want to apply to/fromFieldAligned -} - -void BoundaryFromFieldAligned::apply_ddt(Field2D &f) { - op->apply_ddt(f); + // This is inefficient -- could instead use the shiftZ just in the bndry + // but this is not portable to other parallel transforms -- we could instead + // have a flag to define the region in which we want to apply to/fromFieldAligned } +void BoundaryFromFieldAligned::apply_ddt(Field2D &f) { op->apply_ddt(f); } + void BoundaryFromFieldAligned::apply_ddt(Field3D &f) { - f = mesh->toFieldAligned(f); - ddt(f) = mesh->toFieldAligned(ddt(f)); + Mesh *localmesh = f.getMesh(); + + f = localmesh->toFieldAligned(f); + ddt(f) = localmesh->toFieldAligned(ddt(f)); op->apply_ddt(f); - ddt(f) = mesh->fromFieldAligned(ddt(f)); + ddt(f) = localmesh->fromFieldAligned(ddt(f)); } diff --git a/src/mesh/coordinates.cxx b/src/mesh/coordinates.cxx index 81c0e921e4..368bac7836 100644 --- a/src/mesh/coordinates.cxx +++ b/src/mesh/coordinates.cxx @@ -187,7 +187,7 @@ namespace { // Note: cannot use applyBoundary("neumann") here because applyBoundary() // would try to create a new Coordinates object since we have not finished // initializing yet, leading to an infinite recursion - for (auto bndry : localmesh->getBoundaries()) { + for (auto &bndry : localmesh->getBoundaries()) { if (bndry->bx != 0) { // If bx!=0 we are on an x-boundary, inner if bx>0 and outer if bx<0 for(bndry->first(); !bndry->isDone(); bndry->next1d()) { diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index e4f3c487a0..fc0b00cb21 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -71,12 +71,6 @@ BoutMesh::~BoutMesh() { // Delete the communication handles clear_handles(); - // Delete the boundary regions - for (const auto &bndry : boundary) - delete bndry; - for (const auto &bndry : par_boundary) - delete bndry; - if (comm_x != MPI_COMM_NULL) MPI_Comm_free(&comm_x); if (comm_inner != MPI_COMM_NULL) @@ -796,15 +790,15 @@ int BoutMesh::load() { if (((yg > jyseps1_1) && (yg <= jyseps2_1)) || ((yg > jyseps1_2) && (yg <= jyseps2_2))) { // Core - boundary.push_back(new BoundaryRegionXIn("core", ystart, yend, this)); + boundary.push_back(std::unique_ptr(new BoundaryRegionXIn("core", ystart, yend, this))); } else { // PF region - boundary.push_back(new BoundaryRegionXIn("pf", ystart, yend, this)); + boundary.push_back(std::unique_ptr(new BoundaryRegionXIn("pf", ystart, yend, this))); } } if (PE_XIND == (NXPE - 1)) { // Outer SOL - boundary.push_back(new BoundaryRegionXOut("sol", ystart, yend, this)); + boundary.push_back(std::unique_ptr(new BoundaryRegionXOut("sol", ystart, yend, this))); } } @@ -812,15 +806,15 @@ int BoutMesh::load() { // Need boundaries in Y if ((UDATA_INDEST < 0) && (UDATA_XSPLIT > xstart)) - boundary.push_back(new BoundaryRegionYUp("upper_target", xstart, UDATA_XSPLIT - 1, this)); + boundary.push_back(std::unique_ptr(new BoundaryRegionYUp("upper_target", xstart, UDATA_XSPLIT - 1, this))); if ((UDATA_OUTDEST < 0) && (UDATA_XSPLIT <= xend)) - boundary.push_back(new BoundaryRegionYUp("upper_target", UDATA_XSPLIT, xend, this)); + boundary.push_back(std::unique_ptr(new BoundaryRegionYUp("upper_target", UDATA_XSPLIT, xend, this))); if ((DDATA_INDEST < 0) && (DDATA_XSPLIT > xstart)) boundary.push_back( - new BoundaryRegionYDown("lower_target", xstart, DDATA_XSPLIT - 1, this)); + std::unique_ptr(new BoundaryRegionYDown("lower_target", xstart, DDATA_XSPLIT - 1, this))); if ((DDATA_OUTDEST < 0) && (DDATA_XSPLIT <= xend)) - boundary.push_back(new BoundaryRegionYDown("lower_target", DDATA_XSPLIT, xend, this)); + boundary.push_back(std::unique_ptr(new BoundaryRegionYDown("lower_target", DDATA_XSPLIT, xend, this))); } if (!boundary.empty()) { @@ -2379,13 +2373,13 @@ const RangeIterator BoutMesh::iterateBndryUpperY() const { return RangeIterator(xs, xe); } -vector BoutMesh::getBoundaries() { return boundary; } +vector< std::unique_ptr >& BoutMesh::getBoundaries() { return boundary; } -vector BoutMesh::getBoundariesPar() { return par_boundary; } +vector< std::unique_ptr >& BoutMesh::getBoundariesPar() { return par_boundary; } void BoutMesh::addBoundaryPar(BoundaryRegionPar *bndry) { output_info << "Adding new parallel boundary: " << bndry->label << endl; - par_boundary.push_back(bndry); + par_boundary.push_back(std::unique_ptr(bndry)); } const Field3D BoutMesh::smoothSeparatrix(const Field3D &f) { @@ -2533,6 +2527,14 @@ BoutReal BoutMesh::GlobalY(BoutReal jy) const { return yglo / static_cast(nycore); } +BoutReal BoutMesh::GlobalZ(int jz) const { + return static_cast(jz) / static_cast(GlobalNz); +} + +BoutReal BoutMesh::GlobalZ(BoutReal jz) const { + return jz / static_cast(GlobalNz); +} + void BoutMesh::outputVars(Datafile &file) { file.add(zperiod, "zperiod", false); file.add(MXSUB, "MXSUB", false); diff --git a/src/mesh/impls/bout/boutmesh.hxx b/src/mesh/impls/bout/boutmesh.hxx index 8b8fb97ee1..8fa0f1d8a1 100644 --- a/src/mesh/impls/bout/boutmesh.hxx +++ b/src/mesh/impls/bout/boutmesh.hxx @@ -138,8 +138,8 @@ class BoutMesh : public Mesh { // Boundary regions - vector getBoundaries(); - vector getBoundariesPar(); + vector< std::unique_ptr >& getBoundaries(); + vector< std::unique_ptr >& getBoundariesPar(); void addBoundaryPar(BoundaryRegionPar* bndry); const Field3D smoothSeparatrix(const Field3D &f); @@ -147,10 +147,12 @@ class BoutMesh : public Mesh { int getNx() const {return nx;} int getNy() const {return ny;} - BoutReal GlobalX(int jx) const; - BoutReal GlobalY(int jy) const; - BoutReal GlobalX(BoutReal jx) const; - BoutReal GlobalY(BoutReal jy) const; + BoutReal GlobalX(int jx) const override; + BoutReal GlobalY(int jy) const override; + BoutReal GlobalZ(int jz) const override; + BoutReal GlobalX(BoutReal jx) const override; + BoutReal GlobalY(BoutReal jy) const override; + BoutReal GlobalZ(BoutReal jz) const override; BoutReal getIxseps1() const {return ixseps1;} BoutReal getIxseps2() const {return ixseps2;} @@ -219,8 +221,8 @@ class BoutMesh : public Mesh { void addBoundaryRegions(); ///< Adds 2D and 3D regions for boundaries - vector boundary; // Vector of boundary regions - vector par_boundary; // Vector of parallel boundary regions + vector< std::unique_ptr > boundary; // Vector of boundary regions + vector< std::unique_ptr > par_boundary; // Vector of parallel boundary regions ////////////////////////////////////////////////// // Communications diff --git a/src/mesh/makefile b/src/mesh/makefile index f5e19763a8..597d8afc53 100644 --- a/src/mesh/makefile +++ b/src/mesh/makefile @@ -3,11 +3,11 @@ BOUT_TOP = ../.. DIRS = impls parallel data interpolation -SOURCEC = difops.cxx interpolation.cxx mesh.cxx boundary_standard.cxx \ - boundary_factory.cxx boundary_region.cxx meshfactory.cxx \ - surfaceiter.cxx coordinates.cxx index_derivs.cxx \ - parallel_boundary_region.cxx parallel_boundary_op.cxx fv_ops.cxx -SOURCEH = $(SOURCEC:%.cxx=%.hxx) -TARGET = lib +SOURCEC = difops.cxx interpolation.cxx mesh.cxx boundary_op.cxx \ + boundary_standard.cxx boundary_factory.cxx boundary_region.cxx \ + meshfactory.cxx surfaceiter.cxx coordinates.cxx index_derivs.cxx \ + parallel_boundary_region.cxx parallel_boundary_op.cxx fv_ops.cxx +SOURCEH = $(SOURCEC:%.cxx=%.hxx) +TARGET = lib include $(BOUT_TOP)/make.config diff --git a/tests/MMS/derivatives3/data/BOUT.inp b/tests/MMS/derivatives3/data/BOUT.inp index 13db66011d..c346c7626f 100644 --- a/tests/MMS/derivatives3/data/BOUT.inp +++ b/tests/MMS/derivatives3/data/BOUT.inp @@ -35,7 +35,8 @@ n=6 dy=2*Pi/ny MXG=0 MYG=2 - +ixseps1 = -1 +ixseps2 = -1 [meshx] staggergrids=true diff --git a/tests/MMS/derivatives3/runtest b/tests/MMS/derivatives3/runtest index c3f64624cb..2d5176b792 100755 --- a/tests/MMS/derivatives3/runtest +++ b/tests/MMS/derivatives3/runtest @@ -16,25 +16,135 @@ boutcore.init("-d data -q -q -q".split(" ")) def runtests(functions,derivatives,directions,stag,msg): global errorlist for direction in directions: - direction, fac,guards, diff_func = direction + direction, fac,guards, diff_func, diff_order = direction locations=['CENTRE'] if stag: locations.append(direction.upper()+"LOW") - for funcs, derivative , inloc, outloc in itertools.product(functions, - derivatives, locations,locations): - infunc, outfunc = funcs + for funcs, derivative , inloc, outloc, testBoundaries \ + in itertools.product(functions, derivatives, locations, locations, [0,1,2]): + infunc, outfunc, difffunc = funcs order, diff = derivative + expected_order = order errors=[] + errors_L2=[] for nz in nzs: + dirnfac=direction+"*"+fac + this_infunc = infunc.replace("%s",dirnfac) + this_outfunc = outfunc.replace("%s",dirnfac) + this_difffunc = difffunc.replace("%s",dirnfac) boutcore.setOption("meshD:nD".replace("D",direction) ,"%d"% (nz+ (2*guards if direction == "x" else 0)),force=True) boutcore.setOption("meshD:dD".replace("D",direction,) ,"2*pi/(%d)"%(nz),force=True) - dirnfac=direction+"*"+fac mesh=boutcore.Mesh(section="mesh"+direction) - f=boutcore.create3D(infunc.replace("%s",dirnfac),mesh + f=boutcore.create3D(this_infunc,mesh ,outloc=inloc) + if testBoundaries == 0: + # test derivative operators without relying on boundary conditions + pass + if testBoundaries == 1: + if diff_order == 1: + if order==2: + if direction == "x": + f.applyBoundary(boundary="dirichlet_o3(%s)"%(this_infunc), region="core") + f.applyBoundary(boundary="neumann_o2(%s)"%(this_difffunc), region="sol") + if direction == "y": + f.applyBoundary(boundary="neumann_o2(%s)"%(this_difffunc), region="lower_target") + f.applyBoundary(boundary="dirichlet_o3(%s)"%(this_infunc), region="upper_target") + elif order==3: + if direction == "x": + f.applyBoundary(boundary="dirichlet_o4(%s)"%(this_infunc), region="core") + f.applyBoundary(boundary="neumann_o4(%s)"%(this_difffunc), region="sol") + if direction == "y": + f.applyBoundary(boundary="neumann_o4(%s)"%(this_difffunc), region="lower_target") + f.applyBoundary(boundary="dirichlet_o4(%s)"%(this_infunc), region="upper_target") + elif order==4: + if direction == "x": + f.applyBoundary(boundary="dirichlet_o5(%s)"%(this_infunc), region="core") + f.applyBoundary(boundary="neumann_o4(%s)"%(this_difffunc), region="sol") + if direction == "y": + f.applyBoundary(boundary="neumann_o4(%s)"%(this_difffunc), region="lower_target") + f.applyBoundary(boundary="dirichlet_o5(%s)"%(this_infunc), region="upper_target") + elif diff_order == 2: + if order==2: + if direction == "x": + f.applyBoundary(boundary="dirichlet_o4(%s)"%(this_infunc), region="core") + f.applyBoundary(boundary="neumann_o4(%s)"%(this_difffunc), region="sol") # there is no neumann_o3 + if direction == "y": + f.applyBoundary(boundary="neumann_o4(%s)"%(this_difffunc), region="lower_target") # there is no neumann_o3 + f.applyBoundary(boundary="dirichlet_o4(%s)"%(this_infunc), region="upper_target") + elif order==3: + if direction == "x": + f.applyBoundary(boundary="dirichlet_o5(%s)"%(this_infunc), region="core") + f.applyBoundary(boundary="neumann_o4(%s)"%(this_difffunc), region="sol") + if direction == "y": + f.applyBoundary(boundary="neumann_o4(%s)"%(this_difffunc), region="lower_target") + f.applyBoundary(boundary="dirichlet_o5(%s)"%(this_infunc), region="upper_target") + elif order==4: + if direction == "x": + f.applyBoundary(boundary="dirichlet_o5(%s)"%(this_infunc), region="core") + f.applyBoundary(boundary="neumann_o4(%s)"%(this_difffunc), region="sol") + if direction == "y": + f.applyBoundary(boundary="dirichlet_o5(%s)"%(this_infunc), region="upper_target") + f.applyBoundary(boundary="neumann_o4(%s)"%(this_difffunc), region="lower_target") + # don't have accurate enough boundary conditions, so reduce expected order + expected_order = 3 + else: + raise ValueError("don't know how to test a derivatives higher than d2/d*2") + if testBoundaries == 2: + if diff_order == 1: + if order==2: + if direction == "x": + f.applyBoundary(boundary="dirichlet(%s)"%(this_infunc), region="core") + f.applyBoundary(boundary="free_o2", region="sol") + expected_order = 1 # reduce expected order because free_o2 is not accurate enough for order=2 + if direction == "y": + f.applyBoundary(boundary="free_o3", region="lower_target") + f.applyBoundary(boundary="free_o3", region="upper_target") + elif order==3: + if direction == "x": + f.applyBoundary(boundary="free_o4", region="core") + f.applyBoundary(boundary="free_o4", region="sol") + if direction == "y": + f.applyBoundary(boundary="free_o4", region="lower_target") + f.applyBoundary(boundary="free_o4", region="upper_target") + elif order==4: + if direction == "x": + f.applyBoundary(boundary="free_o4", region="core") + f.applyBoundary(boundary="free_o4", region="sol") + expected_order = 3 + if direction == "y": + f.applyBoundary(boundary="free_o5", region="lower_target") + f.applyBoundary(boundary="free_o5", region="upper_target") + elif diff_order == 2: + if order==2: + if direction == "x": + f.applyBoundary(boundary="free_o4", region="core") + f.applyBoundary(boundary="free_o4", region="sol") # there is no neumann_o3 + if direction == "y": + f.applyBoundary(boundary="free_o4", region="lower_target") # there is no neumann_o3 + f.applyBoundary(boundary="free_o4", region="upper_target") + elif order==3: + if direction == "x": + f.applyBoundary(boundary="free_o5", region="core") + f.applyBoundary(boundary="free_o5", region="sol") + if direction == "y": + f.applyBoundary(boundary="free_o5", region="lower_target") + f.applyBoundary(boundary="free_o5", region="upper_target") + elif order==4: + if direction == "x": + f.applyBoundary(boundary="free_o5", region="core") + f.applyBoundary(boundary="free_o5", region="sol") + if direction == "y": + f.applyBoundary(boundary="free_o5", region="upper_target") + f.applyBoundary(boundary="free_o5", region="lower_target") + # don't have accurate enough boundary conditions, so reduce expected order + expected_order = 3 + else: + raise ValueError("don't know how to test a derivatives higher than d2/d*2") + #endif testBoundaries + sim=diff_func(f,method=diff,outloc=outloc) if sim.getLocation() != outloc: cent=['CENTRE','CENTER'] @@ -42,37 +152,50 @@ def runtests(functions,derivatives,directions,stag,msg): pass else: errorlist.append("Location does not match - expected %s but got %s"%(outloc,sim.getLocation())) - ana=boutcore.create3D(outfunc.replace("%s",dirnfac),mesh, outloc=outloc) + ana=boutcore.create3D(this_outfunc, mesh, outloc=outloc) err=sim-ana err=err.getAll().flatten() if guards: err=err[guards:-guards] - err=np.max(np.abs(err)) - errors.append(err) + if ("LOW" in inloc) and ("LOW" in outloc) and guards: + # first point is the one where the boundary condition is set on staggered fields + # the derivative at this point does not necessarily have to be + # accurate, since it is effectively an 'extra' guard cell + err=err[1:] + err_max=np.max(np.abs(err)) + errors.append(err_max) + err_L2=np.sqrt(np.mean(err**2)) + errors_L2.append(err_L2) errc=np.log(errors[-2]/errors[-1]) difc=np.log(nzs[-1]/nzs[-2]) conv=errc/difc - if order-.1 < conv < order+.1: + errc_L2=np.log(errors_L2[-2]/errors_L2[-1]) + conv_L2=errc_L2/difc + if expected_order-.2 < conv < expected_order+.2: pass else: info="%s - %s - %s - %s -> %s "%(infunc,diff, direction,inloc,outloc) - error="%s: %s is not working. Expected %f got %f"%(msg,info,order,conv) + error="%s: %s is not working with testBoundaries=%i. Expected %f got max error %f, RMS error %f"%(msg,info,testBoundaries,expected_order,conv,conv_L2) + print(error) errorlist.append(error) if doPlot: from matplotlib import pyplot as plt plt.plot((ana).getAll().flatten()) plt.plot((sim).getAll().flatten()) + plt.figure() + plt.legend() plt.show() -mmax=7 -start=6 +mmax=8 +start=7 doPlot=False nzs=np.logspace(start,mmax,num=mmax-start+1,base=2) +# functions contains list of triples of (function, derivative_operator(function), first_derivative(function) functions=[ - ["sin(%s)","cos(%s)"] , - ["cos(%s)", "-sin(%s)"] + ["sin(%s+1.)", "cos(%s+1.)", "cos(%s+1.)"] , + ["cos(%s+1.)", "-sin(%s+1.)", "-sin(%s+1.)"] ] derivatives=[ @@ -84,9 +207,9 @@ derivatives=[ ] directions=[ - ["x","2*pi",2 ,boutcore.DDX], - ["y","1" ,2 ,boutcore.DDY], -# ["z","1" ,0 ,boutcore.DDZ] + ["x","2*pi",2 ,boutcore.DDX, 1], + ["y","1" ,2 ,boutcore.DDY, 1], +# ["z","1" ,0 ,boutcore.DDZ, 1] ] runtests(functions,derivatives,directions,stag=False,msg="DD") @@ -100,8 +223,8 @@ runtests(functions,derivatives,directions,stag=True,msg="DD") functions=[ - ["sin(%s)","-sin(%s)"], - ["cos(%s)" , "-cos(%s)"] + ["sin(%s+1.)","-sin(%s+1.)", "cos(%s+1.)"], + ["cos(%s+1.)" , "-cos(%s+1.)", "-sin(%s+1.)"] ] derivatives=[ @@ -109,9 +232,9 @@ derivatives=[ [4,"C4"] ] directions=[ - ["x","2*pi",2 ,boutcore.D2DX2], - ["y","1" ,2 ,boutcore.D2DY2], -# ["z","1" ,0 ,boutcore.D2DZ2] + ["x","2*pi",2 ,boutcore.D2DX2, 2], + ["y","1" ,2 ,boutcore.D2DY2, 2], +# ["z","1" ,0 ,boutcore.D2DZ2, 2] ] runtests(functions,derivatives,directions,False,"D2D2") diff --git a/tests/MMS/diffusion/data/BOUT.inp b/tests/MMS/diffusion/data/BOUT.inp index b4ec84fb8f..6989c8a4fe 100644 --- a/tests/MMS/diffusion/data/BOUT.inp +++ b/tests/MMS/diffusion/data/BOUT.inp @@ -67,6 +67,7 @@ mxstep = 1000000 [cyto] dis = 1 +neumann_boundaries = false [all] @@ -78,7 +79,5 @@ zs_opt = 0 [N] -bndry_all = dirichlet_o2 -bndry_xin = neumann_o2 ################################ diff --git a/tests/MMS/diffusion/diffusion.cxx b/tests/MMS/diffusion/diffusion.cxx index b89a0b0e6a..a3aa592d81 100644 --- a/tests/MMS/diffusion/diffusion.cxx +++ b/tests/MMS/diffusion/diffusion.cxx @@ -42,6 +42,8 @@ int physics_init(bool restarting) { Options *cytooptions = Options::getRoot()->getSection("cyto"); cytooptions->get("dis", mu_N, 1); + bool neumann_boundaries; + cytooptions->get("neumann_boundaries", neumann_boundaries, false); SAVE_ONCE(mu_N); @@ -61,12 +63,11 @@ int physics_init(bool restarting) { coord->g_23 = 0.0; coord->geometry(); - //Dirichlet everywhere except inner x-boundary Neumann - N.addBndryFunction(MS,BNDRY_ALL); - N.addBndryFunction(dxMS,BNDRY_XIN); - - //Dirichlet boundary conditions everywhere - //N.addBndryFunction(MS,BNDRY_ALL); + if (neumann_boundaries) { + N.addBndryFunction(dxMS,BNDRY_ALL); + } else { + N.addBndryFunction(MS,BNDRY_ALL); + } // Tell BOUT++ to solve N SOLVE_FOR(N); diff --git a/tests/MMS/diffusion/runtest b/tests/MMS/diffusion/runtest index 5c8b35eb85..a74eec2b8f 100755 --- a/tests/MMS/diffusion/runtest +++ b/tests/MMS/diffusion/runtest @@ -21,6 +21,8 @@ shell_safe("make > make.log") # List of NX values to use nxlist = [4, 8, 16, 32, 64, 128] +opts_list = [" cyto:neumann_boundaries=false n:bndry_xin=dirichlet n:bndry_xout=dirichlet", + " cyto:neumann_boundaries=true N:bndry_xin=neumann N:bndry_xout=neumann"] nout = 1 timestep = 0.1 @@ -30,70 +32,71 @@ nproc = 1 error_2 = [] # The L2 error (RMS) error_inf = [] # The maximum error -for nx in nxlist: - args = "mesh:nx="+str(nx)+" nout="+str(nout)+" timestep="+str(timestep) - - print("Running with " + args) +for i,opts in enumerate(opts_list): + for nx in nxlist: + args = "mesh:nx="+str(nx)+" nout="+str(nout)+" timestep="+str(timestep)+opts - # Delete old data - shell("rm data/BOUT.dmp.*.nc") - - # Command to run - cmd = "./cyto "+args - # Launch using MPI - s, out = launch_safe(cmd, runcmd=MPIRUN, nproc=nproc, pipe=True) + print("Running with " + args) - # Save output to log file - f = open("run.log."+str(nx), "w") - f.write(out) - f.close() + # Delete old data + shell("rm data/BOUT.dmp.*.nc") - # Collect data - E_N = collect("E_N", tind=[nout,nout], path="data", info=False) + # Command to run + cmd = "./cyto "+args + # Launch using MPI + s, out = launch_safe(cmd, runcmd=MPIRUN, nproc=nproc, pipe=True) - E_N = E_N[0,:,0,0] + # Save output to log file + f = open("run.log."+str(nx), "w") + f.write(out) + f.close() - # Average error over domain, not including guard cells - l2 = sqrt(mean(E_N[1:-1]**2)) - linf = max(abs( E_N[1:-1] )) - - error_2.append( l2 ) - error_inf.append( linf ) + # Collect data + E_N = collect("E_N", tind=[nout,nout], path="data", info=False) - print("Error norm: l-2 %f l-inf %f" % (l2, linf)) + E_N = E_N[0,:,0,0] -# Calculate grid spacing -dx = 1. / (array(nxlist) - 2.) + # Average error over domain, not including guard cells + l2 = sqrt(mean(E_N[1:-1]**2)) + linf = max(abs( E_N[1:-1] )) -order = log(error_2[-1] / error_2[-2]) / log(dx[-1] / dx[-2]) -print("Convergence order = %f" % (order)) + error_2.append( l2 ) + error_inf.append( linf ) -# Attempt to plot errors -try: - import matplotlib.pyplot as plt + print("Error norm: l-2 %f l-inf %f" % (l2, linf)) - plt.plot(dx, error_2, '-o', label=r'$l^2$') - plt.plot(dx, error_inf, '-x', label=r'$l^\infty$') - plt.plot(dx, error_2[-1]*(dx/dx[-1])**order, '--', label="Order %.1f"%(order)) + # Calculate grid spacing + dx = 1. / (array(nxlist) - 2.) - plt.legend(loc="upper left") - plt.grid() + order = log(error_2[-1] / error_2[-2]) / log(dx[-1] / dx[-2]) + print("Convergence order = %f" % (order)) - plt.yscale('log') - plt.xscale('log') + # Attempt to plot errors + try: + import matplotlib.pyplot as plt - plt.xlabel(r'Mesh spacing $\delta x$') - plt.ylabel("Error norm") + plt.plot(dx, error_2, '-o', label=r'$l^2$') + plt.plot(dx, error_inf, '-x', label=r'$l^\infty$') + plt.plot(dx, error_2[-1]*(dx/dx[-1])**order, '--', label="Order %.1f"%(order)) - plt.savefig("norm.pdf") + plt.legend(loc="upper left") + plt.grid() - #plt.show() - plt.close() -except: - # Plotting could fail for any number of reasons, and the actual - # error raised may depend on, among other things, the current - # matplotlib backend, so catch everything - pass + plt.yscale('log') + plt.xscale('log') + + plt.xlabel(r'Mesh spacing $\delta x$') + plt.ylabel("Error norm") + + plt.savefig("norm.pdf") + + #plt.show() + plt.close() + except: + # Plotting could fail for any number of reasons, and the actual + # error raised may depend on, among other things, the current + # matplotlib backend, so catch everything + pass if order > 1.8 and order < 2.2: # test for success diff --git a/tests/MMS/wave-1d-y/data/BOUT.inp b/tests/MMS/wave-1d-y/data/BOUT.inp index d47b8d9df8..f375e905a6 100644 --- a/tests/MMS/wave-1d-y/data/BOUT.inp +++ b/tests/MMS/wave-1d-y/data/BOUT.inp @@ -67,15 +67,14 @@ solution = y - sin(t)*cos(0.5*y) + cos(y) ddy = 0.5*sin(t)*sin(0.5*y) - sin(y) + 1 source = 0.2*y*sin(0.1*y^2)*cos(t) - 2*y - cos(t)*cos(0.5*y) - cos(y) -bndry_all = neumann_o2(f:ddy) -bndry_yup = dirichlet_o2(f:solution) +bndry_all = none [g] solution = y^2 + sin(y) + cos(t)*cos(0.1*y^2) ddy = -0.2*y*sin(0.1*y^2)*cos(t) + 2*y + cos(y) source = -0.5*sin(t)*sin(0.5*y) - sin(t)*cos(0.1*y^2) + sin(y) - 1 -bndry_all = dirichlet_o2(g:solution) +bndry_all = none ################################ diff --git a/tests/MMS/wave-1d-y/runtest b/tests/MMS/wave-1d-y/runtest index 168cea6807..6d5dde19cb 100755 --- a/tests/MMS/wave-1d-y/runtest +++ b/tests/MMS/wave-1d-y/runtest @@ -24,9 +24,16 @@ MPIRUN = getmpirun() print("Making MMS wave test") shell_safe("make > make.log") -# List of NX values to use +# List of NY values to use nylist = [8, 16, 32, 64, 128, 256] +# Options to test +opts_list = [' mesh:staggergrids=true f:bndry_ydown="dirichlet_smooth(f:solution)" f:bndry_yup="dirichlet_smooth(f:solution)" g:bndry_ydown="dirichlet_smooth(g:solution)" g:bndry_yup="dirichlet_smooth(g:solution)"', + ' mesh:staggergrids=true f:bndry_ydown="neumann(f:ddy)" f:bndry_yup="neumann(f:ddy)" g:bndry_ydown="neumann(g:ddy)" g:bndry_yup="neumann(g:ddy)"', + ' mesh:staggergrids=false f:bndry_ydown="dirichlet_smooth(f:solution)" f:bndry_yup="dirichlet_smooth(f:solution)" g:bndry_ydown="dirichlet_smooth(g:solution)" g:bndry_yup="dirichlet_smooth(g:solution)"', + ' mesh:staggergrids=false f:bndry_ydown="neumann(f:ddy)" f:bndry_yup="neumann(f:ddy)" g:bndry_ydown="neumann(g:ddy)" g:bndry_yup="neumann(g:ddy)"'] + + nout = 1 timestep = 1 @@ -36,91 +43,92 @@ varlist = ["f", "g"] markers = ['bo', 'r^'] labels = ["f", "g"] -error_2 = {} -error_inf = {} -for var in varlist: - error_2[var] = [] # The L2 error (RMS) - error_inf[var] = [] # The maximum error - -for ny in nylist: - dy = 2.*pi / ny - args = "mesh:ny="+str(ny)+" mesh:dy="+str(dy)+" nout="+str(nout)+" timestep="+str(timestep) - - print("Running with " + args) - - # Delete old data - shell("rm data/BOUT.dmp.*.nc") - - # Command to run - cmd = "./wave "+args - # Launch using MPI - s, out = launch_safe(cmd, runcmd=MPIRUN, nproc=nproc, pipe=True) - - # Save output to log file - with open("run.log."+str(ny), "w") as f: - f.write(out) - - for var in varlist: - # Collect data - E = collect("E_"+var, tind=[nout,nout], info=False, path="data") - E = E[0,0,:,0] - - # Average error over domain - - l2 = sqrt(mean(E**2)) - linf = max(abs(E)) - - error_2[var].append( l2 ) - error_inf[var].append( linf ) - - print("Error norm %s: l-2 %f l-inf %f" % (var, l2, linf)) - -# Save data -with open("wave.pkl", "wb") as output: - pickle.dump(nylist, output) - pickle.dump(error_2, output) - pickle.dump(error_inf, output) - -# Calculate grid spacing -dy = 1. / array(nylist) - -# Calculate convergence order success = True -for var in varlist: - order = log(error_2[var][-1] / error_2[var][-2]) / log(dy[-1] / dy[-2]) - stdout.write("%s Convergence order = %f" % (var, order)) - - if 1.8 < order < 2.2: # Should be second order accurate - print("............ PASS") - else: - success = False - print("............ FAIL") +for i,opts in enumerate(opts_list): + error_2 = {} + error_inf = {} + for var in varlist: + error_2[var] = [] # The L2 error (RMS) + error_inf[var] = [] # The maximum error -# plot errors -try: - import matplotlib.pyplot as plt - for var,mark,label in zip(varlist, markers, labels): - plt.plot(dy, error_2[var], '-'+mark, label="%s order=%.2f" % (label, order)) - plt.plot(dy, error_inf[var], '--'+mark) + for ny in nylist: + dy = 2.*pi / ny + args = "mesh:ny="+str(ny)+" mesh:dy="+str(dy)+" nout="+str(nout)+" timestep="+str(timestep)+opts + + print("Running with " + args) - plt.legend(loc="upper left") - plt.grid() + # Delete old data + shell("rm data/BOUT.dmp.*.nc") + + # Command to run + cmd = "./wave "+args + # Launch using MPI + s, out = launch_safe(cmd, runcmd=MPIRUN, nproc=nproc, pipe=True) + + # Save output to log file + with open("run.log."+str(ny), "w") as f: + f.write(out) + + for var in varlist: + # Collect data + E = collect("E_"+var, tind=[nout,nout], info=False, path="data") + E = E[0,0,:,0] + + # Average error over domain + + l2 = sqrt(mean(E**2)) + linf = max(abs(E)) + + error_2[var].append( l2 ) + error_inf[var].append( linf ) - plt.yscale('log') - plt.xscale('log') + print("Error norm %s: l-2 %f l-inf %f" % (var, l2, linf)) - plt.xlabel(r'Mesh spacing $\delta y$') - plt.ylabel("Error norm") + ## Save data + #with open("wave.pkl", "wb") as output: + # pickle.dump(nylist, output) + # pickle.dump(error_2, output) + # pickle.dump(error_inf, output) - plt.savefig("norm.pdf") + # Calculate grid spacing + dy = 1. / array(nylist) - #plt.show() - plt.close() -except: - # Plotting could fail for any number of reasons, and the actual - # error raised may depend on, among other things, the current - # matplotlib backend, so catch everything - pass + # Calculate convergence order + for var in varlist: + order = log(error_2[var][-1] / error_2[var][-2]) / log(dy[-1] / dy[-2]) + stdout.write("%s Convergence order = %f" % (var, order)) + + if 1.8 < order < 2.2: # Should be second order accurate + print("............ PASS with", opts) + else: + success = False + print("............ FAIL with", opts) + + # plot errors + try: + import matplotlib.pyplot as plt + for var,mark,label in zip(varlist, markers, labels): + plt.plot(dy, error_2[var], '-'+mark, label="%s order=%.2f" % (label, order)) + plt.plot(dy, error_inf[var], '--'+mark) + + plt.legend(loc="upper left") + plt.grid() + + plt.yscale('log') + plt.xscale('log') + + plt.xlabel(r'Mesh spacing $\delta y$') + plt.ylabel("Error norm") + + plt.savefig("norm%i.pdf"%i) + + #plt.show() + plt.close() + except: + # Plotting could fail for any number of reasons, and the actual + # error raised may depend on, among other things, the current + # matplotlib backend, so catch everything + pass if success: print(" => Test passed") diff --git a/tests/MMS/wave-1d-y/wave.cxx b/tests/MMS/wave-1d-y/wave.cxx index db4c931665..6fe8896896 100644 --- a/tests/MMS/wave-1d-y/wave.cxx +++ b/tests/MMS/wave-1d-y/wave.cxx @@ -5,12 +5,19 @@ class Wave1D : public PhysicsModel { private: Field3D f, g; // Evolving variables + CELL_LOC maybe_ylow; protected: int init(bool restarting) { - g.setLocation(CELL_YLOW); // g staggered - + if(mesh->StaggerGrids) { + maybe_ylow = CELL_YLOW; + } else { + maybe_ylow = CELL_CENTRE; + } + + g.setLocation(maybe_ylow); // g staggered + // Tell BOUT++ to solve f and g bout_solve(f, "f"); bout_solve(g, "g"); @@ -23,7 +30,7 @@ class Wave1D : public PhysicsModel { // Central differencing ddt(f) = DDY(g, CELL_CENTRE); - ddt(g) = DDY(f, CELL_YLOW); + ddt(g) = DDY(f, maybe_ylow); return 0; } diff --git a/tests/MMS/wave-1d/data/BOUT.inp b/tests/MMS/wave-1d/data/BOUT.inp index bcadb4eab4..9388cce9d0 100644 --- a/tests/MMS/wave-1d/data/BOUT.inp +++ b/tests/MMS/wave-1d/data/BOUT.inp @@ -75,13 +75,11 @@ zs_opt = 0 [f] -bndry_all = neumann # Should be ignored -bndry_xin = neumann_o2 +bndry_all = none [g] -bndry_all = dirichlet_o2 -bndry_xin = neumann # Should have no effect +bndry_all = none ################################ diff --git a/tests/MMS/wave-1d/runtest b/tests/MMS/wave-1d/runtest index f400d919c2..d677428c6d 100755 --- a/tests/MMS/wave-1d/runtest +++ b/tests/MMS/wave-1d/runtest @@ -22,88 +22,104 @@ shell_safe("make > make.log") # List of NX values to use nxlist = [8, 12, 20, 36, 68, 132] +# switch staggered grids on/off, swap the boundary conditions in<->out +optslist = [" f:bndry_xout=dirichlet_smooth f:bndry_all=neumann g:bndry_xout=dirichlet_smooth g:bndry_all=neumann", + " mesh:staggergrids=false f:bndry_xout=dirichlet_smooth f:bndry_all=neumann g:bndry_xout=dirichlet_smooth g:bndry_all=neumann", + " swap_boundary_conditions=true f:bndry_xin=dirichlet_smooth f:bndry_all=neumann g:bndry_xin=dirichlet_smooth g:bndry_all=neumann", + " mesh:staggergrids=false, swap_boundary_conditions=true f:bndry_xin=dirichlet_smooth f:bndry_all=neumann g:bndry_xin=dirichlet_smooth g:bndry_all=neumann"] nout = 1 timestep = 0.1 nproc = 1 -error_2 = [] # The L2 error (RMS) -error_inf = [] # The maximum error +results = [] +for iopt,opts in enumerate(optslist): + error_2 = [] # The L2 error (RMS) + error_inf = [] # The maximum error -for nx in nxlist: - args = "mesh:nx="+str(nx)+" nout="+str(nout)+" timestep="+str(timestep) - - print("Running with " + args) + print("opts = '"+opts+"'\n") - # Delete old data - shell("rm data/BOUT.dmp.*.nc") - - # Command to run - cmd = "./wave "+args - # Launch using MPI - s, out = launch_safe(cmd, runcmd=MPIRUN, nproc=nproc, pipe=True) + for nx in nxlist: + args = "mesh:nx="+str(nx)+" nout="+str(nout)+" timestep="+str(timestep)+opts - # Save output to log file - f = open("run.log."+str(nx), "w") - f.write(out) - f.close() + print("Running with " + args) - # Collect data - E_f = collect("E_f", tind=[nout,nout], path="data", info=False) - E_f = E_f[0,:,0,0] - - E_g = collect("E_g", tind=[nout,nout], path="data", info=False) - E_g = E_g[0,:,0,0] + # Delete old data + shell("rm data/BOUT.dmp.*.nc") - # Average error over domain, not including guard cells - E = concatenate([E_f[1:-1], E_g[2:-1]]) + # Command to run + cmd = "./wave "+args + # Launch using MPI + s, out = launch_safe(cmd, runcmd=MPIRUN, nproc=nproc, pipe=True) - l2 = sqrt(mean(E**2)) - linf = max(abs(E)) - - error_2.append( l2 ) - error_inf.append( linf ) + # Save output to log file + f = open("run.log."+str(iopt)+"."+str(nx), "w") + f.write(out) + f.close() - print("Error norm: l-2 %f l-inf %f" % (l2, linf)) + # Collect data + E_f = collect("E_f", path="data", info=False) + E_f = E_f[-1,:,0,0] -# Calculate grid spacing -dx = 1. / (array(nxlist) - 2.) + E_g = collect("E_g", path="data", info=False) + E_g = E_g[-1,:,0,0] -order = log(error_2[-1] / error_2[-2]) / log(dx[-1] / dx[-2]) -print("Convergence order = %f" % (order)) + # Average error over domain, not including guard cells + E = concatenate([E_f[1:-1], E_g[2:-1]]) -# plot errors -try: - import matplotlib.pyplot as plt + l2 = sqrt(mean(E**2)) + linf = max(abs(E)) - plt.plot(dx, error_2, '-o', label=r'$l^2$') - plt.plot(dx, error_inf, '-x', label=r'$l^\infty$') - plt.plot(dx, error_2[-1]*(dx/dx[-1])**order, '--', label="Order %.1f"%(order)) + error_2.append( l2 ) + error_inf.append( linf ) - plt.legend(loc="upper left") - plt.grid() + print("Error norm: l-2 %f l-inf %f" % (l2, linf)) - plt.yscale('log') - plt.xscale('log') + # Calculate grid spacing + dx = 1. / (array(nxlist) - 2.) - plt.xlabel(r'Mesh spacing $\delta x$') - plt.ylabel("Error norm") + order = log(error_2[-1] / error_2[-2]) / log(dx[-1] / dx[-2]) + print("Convergence order = %f" % (order)) - plt.savefig("wave_norm.pdf") + # plot errors + try: + import matplotlib.pyplot as plt - #plt.show() - plt.close() -except: - # Plotting could fail for any number of reasons, and the actual - # error raised may depend on, among other things, the current - # matplotlib backend, so catch everything - pass + plt.plot(dx, error_2, '-o', label=r'$l^2$') + plt.plot(dx, error_inf, '-x', label=r'$l^\infty$') + plt.plot(dx, error_2[-1]*(dx/dx[-1])**order, '--', label="Order %.1f"%(order)) + + plt.legend(loc="upper left") + plt.grid() + + plt.yscale('log') + plt.xscale('log') + + plt.xlabel(r'Mesh spacing $\delta x$') + plt.ylabel("Error norm") + + plt.savefig("wave_norm.pdf") + + #plt.show() + plt.close() + except: + # Plotting could fail for any number of reasons, and the actual + # error raised may depend on, among other things, the current + # matplotlib backend, so catch everything + pass + + if 2.2 > order > 1.8: + # test for success + results.append(0) + else: + results.append(1) + + print("") -if 2.2 > order > 1.8: - # test for success - print(" => Test passed") - exit(0) +if all(r == 0 for r in results): + print(" => Test passed") + exit(0) else: - print(" => Test failed") - exit(1) + print(" => Test failed") + exit(1) diff --git a/tests/MMS/wave-1d/wave.cxx b/tests/MMS/wave-1d/wave.cxx index d2543a98b1..d37c0b12dc 100644 --- a/tests/MMS/wave-1d/wave.cxx +++ b/tests/MMS/wave-1d/wave.cxx @@ -42,7 +42,11 @@ class Wave1D : public PhysicsModel { Field3D f, g; // Evolving variables Field3D E_f, E_g; // Error vectors + bool swap_boundary_conditions; + Coordinates *coord; + + CELL_LOC maybe_xlow = mesh->StaggerGrids ? CELL_XLOW : CELL_CENTRE; const Field3D solution_f(BoutReal t); const Field3D source_f(BoutReal t); @@ -56,6 +60,8 @@ class Wave1D : public PhysicsModel { // Get the options Options *meshoptions = Options::getRoot()->getSection("mesh"); + + OPTION(Options::getRoot(), swap_boundary_conditions, false); meshoptions->get("Lx",Lx,1.0); meshoptions->get("Ly",Ly,1.0); @@ -83,14 +89,29 @@ class Wave1D : public PhysicsModel { coord->g_23 = 0.0; coord->geometry(); - g.setLocation(CELL_XLOW); // g staggered to the left of f + g.setLocation(maybe_xlow); // g staggered to the left of f - //Dirichlet everywhere except inner x-boundary Neumann - f.addBndryFunction(MS_f,BNDRY_ALL); - f.addBndryFunction(dxMS_f,BNDRY_XIN); + // Note, when staggergrids=true, only g's boundary conditions have any effect + + if (!swap_boundary_conditions) { + // Dirichlet at outer x-boundary, Neumann everywhere else + f.addBndryFunction(dxMS_f,BNDRY_ALL); + f.addBndryFunction(MS_f,BNDRY_XOUT); // note order matters, must add this after BNDRY_ALL + } else { + // Dirichlet at inner x-boundary, Neumann everywhere else + f.addBndryFunction(dxMS_f,BNDRY_ALL); + f.addBndryFunction(MS_f,BNDRY_XIN); // note order matters, must add this after BNDRY_ALL + } - g.addBndryFunction(MS_g,BNDRY_ALL); - g.addBndryFunction(dxMS_g,BNDRY_XIN); + if (!swap_boundary_conditions) { + // Dirichlet at outer x-boundary, Neumann everywhere else + g.addBndryFunction(dxMS_g,BNDRY_ALL); + g.addBndryFunction(MS_g,BNDRY_XOUT); // note order matters, must add this after BNDRY_ALL + } else { + // Dirichlet at inner x-boundary, Neumann everywhere else + g.addBndryFunction(dxMS_g,BNDRY_ALL); + g.addBndryFunction(MS_g,BNDRY_XIN); // note order matters, must add this after BNDRY_ALL + } // Tell BOUT++ to solve f and g bout_solve(f, "f"); @@ -101,8 +122,8 @@ class Wave1D : public PhysicsModel { for (int xi = mesh->xstart; xi < mesh->xend +1; xi++){ for (int yj = mesh->ystart; yj < mesh->yend + 1; yj++){ for (int zk = 0; zk < mesh->LocalNz; zk++) { - f(xi, yj, zk) = MS_f(0.,mesh->GlobalX(xi),mesh->GlobalY(yj),coord->dz*zk); - g(xi, yj, zk) = MS_g(0.,0.5*(mesh->GlobalX(xi)+mesh->GlobalX(xi-1)),mesh->GlobalY(yj),coord->dz*zk); + f(xi, yj, zk) = MS_f(0.,mesh->GlobalX(xi),TWOPI*mesh->GlobalY(yj),TWOPI*coord->dz*zk); + g(xi, yj, zk) = MS_g(0.,0.5*(mesh->GlobalX(xi)+mesh->GlobalX(xi-1)),TWOPI*mesh->GlobalY(yj),TWOPI*coord->dz*zk); } } } @@ -110,8 +131,8 @@ class Wave1D : public PhysicsModel { for (int xi = mesh->xstart; xi < mesh->xend +1; xi++){ for (int yj = mesh->ystart; yj < mesh->yend + 1; yj++){ for (int zk = 0; zk < mesh->LocalNz; zk++) { - f(xi, yj, zk) = MS_f(0.,mesh->GlobalX(xi),mesh->GlobalY(yj),coord->dz*zk); - g(xi, yj, zk) = MS_g(0.,mesh->GlobalX(xi),mesh->GlobalY(yj),coord->dz*zk); + f(xi, yj, zk) = MS_f(0.,mesh->GlobalX(xi),TWOPI*mesh->GlobalY(yj),TWOPI*coord->dz*zk); + g(xi, yj, zk) = MS_g(0.,mesh->GlobalX(xi),TWOPI*mesh->GlobalY(yj),TWOPI*coord->dz*zk); } } } @@ -135,8 +156,8 @@ class Wave1D : public PhysicsModel { //ddt(g) = HLL(-f, g, -1.0, 1.0); // Central differencing - ddt(f) = DDX(g, CELL_CENTRE);// + 20*SQ(coord->dx)*D2DX2(f); - ddt(g) = DDX(f, CELL_XLOW);// + 20*SQ(coord->dx)*D2DX2(g); + ddt(f) = DDX(g, CELL_CENTRE);// + 20*SQ(f.getCoordinates()->dx)*D2DX2(f); + ddt(g) = DDX(f, maybe_xlow);// + 20*SQ(g.getCoordinates()->dx)*D2DX2(g); //add MMS source term ddt(f) += source_f(t); @@ -198,10 +219,10 @@ const Field3D Wave1D::solution_f(BoutReal t) { for (int xi = mesh->xstart - bx; xi < mesh->xend + bx + 1; xi++){ for (int yj = mesh->ystart - by; yj < mesh->yend + by + 1; yj++){ BoutReal x = mesh->GlobalX(xi); - BoutReal y = mesh->GlobalY(yj);//GlobalY not fixed yet + BoutReal y = mesh->GlobalY(yj); for (int zk = 0; zk < mesh->LocalNz; zk++) { BoutReal z = coord->dz*zk; - S(xi, yj, zk) = MS_f(t,x,y,z); + S(xi, yj, zk) = MS_f(t,x,TWOPI*y,TWOPI*z); } } } @@ -249,7 +270,7 @@ const Field3D Wave1D::solution_g(BoutReal t) { Field3D S; S.allocate(); - S.setLocation(CELL_XLOW); + S.setLocation(maybe_xlow); int bx = (mesh->LocalNx - (mesh->xend - mesh->xstart + 1)) / 2; int by = (mesh->LocalNy - (mesh->yend - mesh->ystart + 1)) / 2; @@ -260,10 +281,10 @@ const Field3D Wave1D::solution_g(BoutReal t) { if(mesh->StaggerGrids) { x = 0.5*(mesh->GlobalX(xi-1) + mesh->GlobalX(xi)); } - BoutReal y = mesh->GlobalY(yj);//GlobalY not fixed yet + BoutReal y = mesh->GlobalY(yj); for (int zk = 0; zk < mesh->LocalNz; zk++) { BoutReal z = coord->dz*zk; - S(xi, yj, zk) = MS_g(t,x,y,z); + S(xi, yj, zk) = MS_g(t,x,TWOPI*y,TWOPI*z); } } } @@ -275,7 +296,7 @@ const Field3D Wave1D::source_g(BoutReal t) { Field3D result; result.allocate(); - result.setLocation(CELL_XLOW); + result.setLocation(maybe_xlow); int xi,yj,zk; diff --git a/tests/unit/field/test_vector2d.cxx b/tests/unit/field/test_vector2d.cxx index 7824f2b2cf..1f77eb17c4 100644 --- a/tests/unit/field/test_vector2d.cxx +++ b/tests/unit/field/test_vector2d.cxx @@ -18,11 +18,6 @@ class Vector2DTest : public ::testing::Test { static void SetUpTestCase() { // Delete any existing mesh if (mesh != nullptr) { - // Delete boundary regions - for (auto &r : mesh->getBoundaries()) { - delete r; - } - delete mesh; mesh = nullptr; } @@ -39,10 +34,6 @@ class Vector2DTest : public ::testing::Test { static void TearDownTestCase() { if (mesh != nullptr) { - // Delete boundary regions - for (auto &r : mesh->getBoundaries()) { - delete r; - } delete mesh; mesh = nullptr; } diff --git a/tests/unit/field/test_vector3d.cxx b/tests/unit/field/test_vector3d.cxx index fb77078781..cac1439dec 100644 --- a/tests/unit/field/test_vector3d.cxx +++ b/tests/unit/field/test_vector3d.cxx @@ -17,11 +17,6 @@ class Vector3DTest : public ::testing::Test { static void SetUpTestCase() { // Delete any existing mesh if (mesh != nullptr) { - // Delete boundary regions - for (auto &r : mesh->getBoundaries()) { - delete r; - } - delete mesh; mesh = nullptr; } @@ -37,12 +32,6 @@ class Vector3DTest : public ::testing::Test { } static void TearDownTestCase() { - if (mesh != nullptr) { - // Delete boundary regions - for (auto &r : mesh->getBoundaries()) { - delete r; - } - } delete mesh; mesh = nullptr; } diff --git a/tests/unit/mesh/test_boundary_factory.cxx b/tests/unit/mesh/test_boundary_factory.cxx index 850eadd2f4..c6421f6ad1 100644 --- a/tests/unit/mesh/test_boundary_factory.cxx +++ b/tests/unit/mesh/test_boundary_factory.cxx @@ -20,8 +20,8 @@ class TestBoundary : public BoundaryOp { std::list args; std::map keywords; - void apply(Field2D &UNUSED(f)) override {} - void apply(Field3D &UNUSED(f)) override {} + void apply(Field2D &UNUSED(f), BoutReal UNUSED(t)) override {} + void apply(Field3D &UNUSED(f), BoutReal UNUSED(t)) override {} }; TEST(BoundaryFactoryTests, IsSingleton) { @@ -41,7 +41,7 @@ TEST(BoundaryFactoryTests, CreateTestBoundary) { // Check no brackets - auto *boundary = fac->create("testboundary", ®ion); + auto *boundary = fac->create("testboundary", ®ion); EXPECT_TRUE( boundary != nullptr ); @@ -51,7 +51,7 @@ TEST(BoundaryFactoryTests, CreateTestBoundary) { // Positional arguments - boundary = fac->create("testboundary(a, 1)", ®ion); + boundary = fac->create("testboundary(a, 1)", ®ion); EXPECT_TRUE( boundary != nullptr ); TestBoundary *tb = dynamic_cast(boundary); @@ -64,7 +64,7 @@ TEST(BoundaryFactoryTests, CreateTestBoundary) { delete boundary; // Test keywords - boundary = fac->create("testboundary(key=1, b=value)", ®ion); + boundary = fac->create("testboundary(key=1, b=value)", ®ion); EXPECT_TRUE( boundary != nullptr ); tb = dynamic_cast(boundary); @@ -77,7 +77,7 @@ TEST(BoundaryFactoryTests, CreateTestBoundary) { delete boundary; // Mix of positional args and keywords - boundary = fac->create("testboundary(0.23, key =1+2 , something(),b=value ,a + sin(1.2))", ®ion); + boundary = fac->create("testboundary(0.23, key =1+2 , something(),b=value ,a + sin(1.2))", ®ion); EXPECT_TRUE( boundary != nullptr ); tb = dynamic_cast(boundary); diff --git a/tests/unit/test_extras.hxx b/tests/unit/test_extras.hxx index 6e6c9dc2ad..2e27d9f066 100644 --- a/tests/unit/test_extras.hxx +++ b/tests/unit/test_extras.hxx @@ -144,13 +144,15 @@ public: const RangeIterator iterateBndryLowerInnerY() const { return RangeIterator(); } const RangeIterator iterateBndryUpperOuterY() const { return RangeIterator(); } const RangeIterator iterateBndryUpperInnerY() const { return RangeIterator(); } - void addBoundary(BoundaryRegion* region) {boundaries.push_back(region);} - vector getBoundaries() { return boundaries; } - vector getBoundariesPar() { return vector(); } + void addBoundary(BoundaryRegion* region) {boundaries.push_back(std::unique_ptr(region));} + vector< std::unique_ptr >& getBoundaries() { return boundaries; } + vector< std::unique_ptr >& getBoundariesPar() { return par_boundaries; } BoutReal GlobalX(int UNUSED(jx)) const { return 0; } BoutReal GlobalY(int UNUSED(jy)) const { return 0; } + BoutReal GlobalZ(int UNUSED(jz)) const { return 0; } BoutReal GlobalX(BoutReal UNUSED(jx)) const { return 0; } BoutReal GlobalY(BoutReal UNUSED(jy)) const { return 0; } + BoutReal GlobalZ(BoutReal UNUSED(jz)) const { return 0; } int XGLOBAL(int UNUSED(xloc)) const { return 0; } int YGLOBAL(int UNUSED(yloc)) const { return 0; } @@ -159,7 +161,8 @@ public: derivs_init(opt); } private: - vector boundaries; + vector< std::unique_ptr > boundaries; + vector< std::unique_ptr > par_boundaries; }; diff --git a/tools/pylib/_boutcore_build/boutcore.pyx.in b/tools/pylib/_boutcore_build/boutcore.pyx.in index 63559f9786..2d69d4c666 100755 --- a/tools/pylib/_boutcore_build/boutcore.pyx.in +++ b/tools/pylib/_boutcore_build/boutcore.pyx.in @@ -572,7 +572,7 @@ EOF done cat <