diff --git a/.github/workflows/clang-tidy-review.yml b/.github/workflows/clang-tidy-review.yml index 348033fb8d..04f1bd7e38 100644 --- a/.github/workflows/clang-tidy-review.yml +++ b/.github/workflows/clang-tidy-review.yml @@ -48,7 +48,7 @@ jobs: with: build_dir: build apt_packages: "libfftw3-dev,libnetcdf-c++4-dev,libopenmpi-dev,petsc-dev,slepc-dev,liblapack-dev,libparpack2-dev,libsundials-dev,uuid-dev" - clang_tidy_checks: '-*,performance-*,readability-*,bugprone-*,clang-analyzer-*,cppcoreguidelines-*,mpi-*,misc-*,-readability-magic-numbers,-cppcoreguidelines-avoid-magic-numbers,-misc-non-private-member-variables-in-classes,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-type-vararg' + clang_tidy_checks: '-*,performance-*,readability-*,bugprone-*,clang-analyzer-*,cppcoreguidelines-*,mpi-*,misc-*,-readability-magic-numbers,-cppcoreguidelines-avoid-magic-numbers,-misc-non-private-member-variables-in-classes,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-type-vararg,-clang-analyzer-optin.mpi*' # Googletest triggers a _lot_ of clang-tidy warnings, so ignore all # the unit tests until they're fixed or ignored upstream exclude: "tests/unit/*cxx" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index eda7190bc0..d1dcfbb6ab 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -78,6 +78,19 @@ jobs: -DSUNDIALS_ROOT=/home/runner/local" omp_num_threads: 2 + - name: "CMake, shared, Ubuntu 20.04, 3D metrics" + os: ubuntu-20.04 + cmake_options: "-DBUILD_SHARED_LIBS=ON + -DBOUT_ENABLE_METRIC_3D=ON + -DBOUT_ENABLE_OPENMP=ON + -DBOUT_USE_PETSC=ON + -DBOUT_USE_SLEPC=ON + -DBOUT_USE_HDF5=ON + -DBOUT_USE_SUNDIALS=ON + -DBOUT_ENABLE_PYTHON=OFF + -DSUNDIALS_ROOT=/home/runner/local" + omp_num_threads: 2 + - name: "Coverage" os: ubuntu-18.04 configure_options: "--enable-shared diff --git a/CHANGELOG.md b/CHANGELOG.md index 54ba028a7b..4fe5f04532 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,19 @@ - `PhysicsModel` expects the options `datadir` and `dump_format` to have been set; this is only a problem if you don't call `BoutInitialise`. [\#2062](https://github.com/boutproject/BOUT-dev/pull/2062) +- `dz` is now a `Field2D`, and `Coordinates::zlength()` also returns a + `Field2D`. In most cases, wrapping these in a call to `getUniform` + in order to get a `BoutReal` will do the correct thing. If checks + are enabled, `getUniform` will throw an exception if its argument is + not uniform (that is, that all values are identical). + [\#2025](https://github.com/boutproject/BOUT-dev/pull/2025) +- The parallel boundaries `BNDRY_PAR_FWD` and `BNDRY_PAR_BKWD` have + been expanded to distinguish between the inner and outer boundaries + in x; the enums have been replaced with `BNDRY_PAR_*_XIN`, and + `BNDRY_PAR_*_XOUT` for both `FWD` and + `BKWD`. [\#2025](https://github.com/boutproject/BOUT-dev/pull/2025) +- Support for reading/writing HDF5 files has been removed ahead of completely + refactoring the I/O - Support for reading/writing HDF5 files has been removed ahead of completely refactoring the I/O systems. [\#2208](https://github.com/boutproject/BOUT-dev/pull/2208) diff --git a/CMakeLists.txt b/CMakeLists.txt index 36eb25bc61..cf9e258f39 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -552,6 +552,14 @@ endif() message(STATUS "Enable OpenMP: ${BOUT_ENABLE_OPENMP}") set(BOUT_USE_OPENMP ${BOUT_ENABLE_OPENMP}) +option(BOUT_ENABLE_METRIC_3D "Enable 3D metric support" OFF) +if(BOUT_ENABLE_METRIC_3D) + set(BOUT_METRIC_TYPE "3D") +else() + set(BOUT_METRIC_TYPE "2D") +endif() +set(BOUT_USE_METRIC_3D ${BOUT_ENABLE_METRIC_3D}) + # Optional dependencies option(BOUT_USE_PVODE "Enable support for bundled PVODE" ON) @@ -991,6 +999,7 @@ message(" Field name tracking : ${BOUT_USE_TRACK} Floating point exceptions: ${BOUT_USE_SIGFPE} Backtrace enabled : ${BOUT_USE_BACKTRACE} + Metric type : ${BOUT_METRIC_TYPE} Python API support : ${BOUT_USE_PYTHON} Sanitizers enabled : ${BOUT_USE_SANITIZERS} diff --git a/autoconf_build_defines.hxx.in b/autoconf_build_defines.hxx.in index 7927cb9cc8..0011864ef8 100644 --- a/autoconf_build_defines.hxx.in +++ b/autoconf_build_defines.hxx.in @@ -51,6 +51,9 @@ /* Use libuuid for UUID generation */ #undef BOUT_HAS_UUID_SYSTEM_GENERATOR +/* Type of the metric fields */ +#undef BOUT_METRIC_TYPE + /* OpenMP schedule */ #undef BOUT_OPENMP_SCHEDULE @@ -60,6 +63,9 @@ /* Enable color logs option */ #undef BOUT_USE_COLOR +/* Is the metric field 3D */ +#undef BOUT_USE_METRIC_3D + /* Enable MsgStack for traces */ #undef BOUT_USE_MSGSTACK diff --git a/bin/bout-config.in b/bin/bout-config.in index a354f6a1d6..1cea5e366e 100755 --- a/bin/bout-config.in +++ b/bin/bout-config.in @@ -44,6 +44,8 @@ has_fftw="@BOUT_HAS_FFTW@" petsc_has_sundials="@PETSC_HAS_SUNDIALS@" +metric_type="@BOUT_METRIC_TYPE@" + usage() { cat < $petsc_has_sundials" echo + echo " --metric-type -> $metric_type" } if test $# -eq 0; then @@ -233,6 +238,9 @@ while test $# -gt 0; do echo $has_nls ;; + --metric-type) + echo $metric_type + ;; --petsc-version) # TODO: Remove in next release diff --git a/bout++Config.cmake.in b/bout++Config.cmake.in index 45ec741aaf..904ac6aa81 100644 --- a/bout++Config.cmake.in +++ b/bout++Config.cmake.in @@ -10,6 +10,7 @@ set(BOUT_USE_BACKTRACE @BOUT_USE_BACKTRACE@) set(BOUT_USE_OPENMP @BOUT_USE_OPENMP@) set(BOUT_HAS_OUTPUT_DEBUG @BOUT_HAS_OUTPUT_DEBUG@) set(BOUT_CHECK_LEVEL @BOUT_CHECK_LEVEL@) +set(BOUT_USE_METRIC_3D @BOUT_USE_METRIC_3D@) set(BOUT_HAS_PVODE @BOUT_HAS_PVODE@) set(BOUT_HAS_NETCDF @BOUT_HAS_NETCDF@) diff --git a/cmake_build_defines.hxx.in b/cmake_build_defines.hxx.in index 07c99c7b63..ea5eb1c9b1 100644 --- a/cmake_build_defines.hxx.in +++ b/cmake_build_defines.hxx.in @@ -26,6 +26,8 @@ #cmakedefine01 BOUT_USE_SIGFPE #cmakedefine01 BOUT_USE_SIGNAL #cmakedefine01 BOUT_USE_TRACK +#cmakedefine BOUT_METRIC_TYPE @BOUT_METRIC_TYPE@ +#cmakedefine01 BOUT_USE_METRIC_3D #cmakedefine01 BOUT_USE_MSGSTACK // CMake build does not support legacy interface diff --git a/configure b/configure index 5db1dd0e26..2054dda2f8 100755 --- a/configure +++ b/configure @@ -629,6 +629,7 @@ PETSC_ARCH PETSC_DIR PETSC_MAKE_INCLUDE PETSC_HAS_SUNDIALS +BOUT_METRIC_TYPE BOUT_USE_MSGSTACK BOUT_USE_TRACK BOUT_USE_SIGNAL @@ -827,6 +828,7 @@ enable_static enable_openmp with_openmp_schedule enable_pvode_openmp +enable_metric_3d with_gcov enable_code_coverage enable_nls @@ -1487,6 +1489,7 @@ Optional Features: --enable-static Enable building bout++ into an static library --enable-openmp Enable building with OpenMP support --enable-pvode-openmp Enable building PVODE with OpenMP support + --enable-metric-3d Use Field3D to store coordinates metric data --disable-openmp do not use OpenMP --enable-code-coverage Whether to enable code coverage support --disable-nls do not use Native Language Support @@ -2588,6 +2591,13 @@ else enable_pvode_openmp=no fi +# Check whether --enable-metric_3d was given. +if test "${enable_metric_3d+set}" = set; then : + enableval=$enable_metric_3d; +else + enable_metric_3d=no +fi + @@ -6045,6 +6055,20 @@ fi fi +if test "x$enable_metric_3d" != "xno"; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Using Field3D to store coordinates data, this is experimental." >&5 +$as_echo "$as_me: WARNING: Using Field3D to store coordinates data, this is experimental." >&2;} + BOUT_METRIC_TYPE="3D" + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: Using Field2D to store coordinates data" >&5 +$as_echo "$as_me: Using Field2D to store coordinates data" >&6;} + BOUT_METRIC_TYPE="2D" + +fi + ############################################################# # Build into shared object (pic) ############################################################# @@ -15759,6 +15783,18 @@ fi +cat >>confdefs.h <<_ACEOF +#define BOUT_METRIC_TYPE $BOUT_METRIC_TYPE +_ACEOF + +BOUT_METRIC_3D=$(test $BOUT_METRIC_TYPE != 3D ; echo $?) + +cat >>confdefs.h <<_ACEOF +#define BOUT_USE_METRIC_3D $BOUT_METRIC_3D +_ACEOF + + + @@ -17299,6 +17335,8 @@ $as_echo "$as_me: Enable FP exceptions : $BOUT_USE_SIGFPE" >&6;} $as_echo "$as_me: Enable signal handlers : $BOUT_USE_SIGNAL" >&6;} { $as_echo "$as_me:${as_lineno-$LINENO}: Enable field names : $BOUT_USE_TRACK" >&5 $as_echo "$as_me: Enable field names : $BOUT_USE_TRACK" >&6;} +{ $as_echo "$as_me:${as_lineno-$LINENO}: Metric type : $BOUT_METRIC_TYPE" >&5 +$as_echo "$as_me: Metric type : $BOUT_METRIC_TYPE" >&6;} { $as_echo "$as_me:${as_lineno-$LINENO}: " >&5 $as_echo "$as_me: " >&6;} diff --git a/configure.ac b/configure.ac index 6faf5d510b..d1a268c277 100644 --- a/configure.ac +++ b/configure.ac @@ -97,6 +97,8 @@ AC_ARG_WITH(openmp_schedule,[AS_HELP_STRING([--with-openmp-schedule=static/dynam [Set OpenMP schedule (default: static)])],,[with_openmp_schedule=static]) AC_ARG_ENABLE(pvode_openmp, [AS_HELP_STRING([--enable-pvode-openmp], [Enable building PVODE with OpenMP support])],,[enable_pvode_openmp=no]) +AC_ARG_ENABLE(metric_3d, [AS_HELP_STRING([--enable-metric-3d], + [Use Field3D to store coordinates metric data])],,[enable_metric_3d=no]) AC_ARG_VAR(EXTRA_INCS,[Extra compile flags]) AC_ARG_VAR(EXTRA_LIBS,[Extra linking flags]) @@ -423,6 +425,14 @@ AS_IF([test "x$enable_backtrace" = "xyes" || test "x$enable_backtrace" = "xmaybe ]) ]) +AS_IF([test "x$enable_metric_3d" != "xno"], [ + AC_MSG_WARN([Using Field3D to store coordinates data, this is experimental.]) + BOUT_METRIC_TYPE="3D" +], [ + AC_MSG_NOTICE([Using Field2D to store coordinates data]) + BOUT_METRIC_TYPE="2D" +]) + ############################################################# # Build into shared object (pic) ############################################################# @@ -1372,6 +1382,10 @@ BOUT_DEFINE_SUBST(BOUT_USE_SIGFPE, [Enable floating point exceptions]) BOUT_DEFINE_SUBST(BOUT_USE_SIGNAL, [Enable signal handlers]) BOUT_DEFINE_SUBST(BOUT_USE_TRACK, [Enable field name tracking]) BOUT_DEFINE_SUBST(BOUT_USE_MSGSTACK, [Enable MsgStack for traces]) +AC_DEFINE_UNQUOTED([BOUT_METRIC_TYPE], $BOUT_METRIC_TYPE, [Type of the metric fields]) +BOUT_METRIC_3D=$(test $BOUT_METRIC_TYPE != 3D ; echo $?) +AC_DEFINE_UNQUOTED([BOUT_USE_METRIC_3D], $BOUT_METRIC_3D, [Is the metric field 3D]) +AC_SUBST(BOUT_METRIC_TYPE) AC_SUBST(PETSC_HAS_SUNDIALS) AC_SUBST(PETSC_MAKE_INCLUDE) @@ -1420,6 +1434,7 @@ AC_MSG_NOTICE([ Enable OpenMP : $BOUT_USE_OPENMP]) AC_MSG_NOTICE([ Enable FP exceptions : $BOUT_USE_SIGFPE]) AC_MSG_NOTICE([ Enable signal handlers : $BOUT_USE_SIGNAL]) AC_MSG_NOTICE([ Enable field names : $BOUT_USE_TRACK]) +AC_MSG_NOTICE([ Metric type : $BOUT_METRIC_TYPE]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([-------------------------------]) diff --git a/examples/conducting-wall-mode/cwm.cxx b/examples/conducting-wall-mode/cwm.cxx index 1ea3a5a19c..09ad7b780b 100644 --- a/examples/conducting-wall-mode/cwm.cxx +++ b/examples/conducting-wall-mode/cwm.cxx @@ -305,8 +305,8 @@ class CWM : public PhysicsModel { for (int jz = 0; jz < mesh->LocalNz; jz++) { var(xrup.ind, jy, jz) = var(xrup.ind, jy - 1, jz) - + coord->dy(xrup.ind, jy) - * sqrt(coord->g_22(xrup.ind, jy)) + + coord->dy(xrup.ind, jy, jz) + * sqrt(coord->g_22(xrup.ind, jy, jz)) * value(xrup.ind, jy, jz); } } @@ -322,8 +322,8 @@ class CWM : public PhysicsModel { for (int jz = 0; jz < mesh->LocalNz; jz++) { var(xrdn.ind, jy, jz) = var(xrdn.ind, jy + 1, jz) - - coord->dy(xrdn.ind, jy) - * sqrt(coord->g_22(xrdn.ind, jy)) + - coord->dy(xrdn.ind, jy, jz) + * sqrt(coord->g_22(xrdn.ind, jy, jz)) * value(xrdn.ind, jy, jz); } } diff --git a/examples/elm-pb/elm_pb.cxx b/examples/elm-pb/elm_pb.cxx index 8c16825009..b145240e47 100644 --- a/examples/elm-pb/elm_pb.cxx +++ b/examples/elm-pb/elm_pb.cxx @@ -26,9 +26,11 @@ class ELMpb : public PhysicsModel { // 2D inital profiles Field2D J0, P0; // Current and pressure Vector2D b0xcv; // Curvature term - Field2D beta, gradparB; // Used for Vpar terms + Field2D beta; // Used for Vpar terms + Coordinates::FieldMetric gradparB; Field2D phi0; // When diamagnetic terms used - Field2D U0, Psixy, x; // 0th vorticity of equilibrium flow, + Field2D Psixy, x; + Coordinates::FieldMetric U0; // 0th vorticity of equilibrium flow, // radial flux coordinate, normalized radial flux coordinate bool constn0; @@ -659,8 +661,9 @@ class ELMpb : public PhysicsModel { for (int jy = 0; jy < mesh->LocalNy; jy++) { for (int jz = 0; jz < mesh->LocalNz; jz++) { - BoutReal angle = rmp_m * pol_angle(jx, jy) - + rmp_n * ((BoutReal)jz) * mesh->getCoordinates()->dz; + BoutReal angle = + rmp_m * pol_angle(jx, jy) + + rmp_n * ((BoutReal)jz) * mesh->getCoordinates()->dz(jx, jy, jz); rmp_Psi0(jx, jy, jz) = (((BoutReal)(jx - 4)) / ((BoutReal)(mesh->LocalNx - 5))) * rmp_factor * cos(angle); diff --git a/examples/gyro-gem/gem.cxx b/examples/gyro-gem/gem.cxx index 9ede002aa6..c82f3f7ec0 100644 --- a/examples/gyro-gem/gem.cxx +++ b/examples/gyro-gem/gem.cxx @@ -58,7 +58,7 @@ class GEM : public PhysicsModel { Vector3D B0vec; // Equilibrium B field vector Field2D logB; // For curvature - Field2D Grad_par_logB; // Grad_par(log(B)) + Coordinates::FieldMetric Grad_par_logB; // Grad_par(log(B)) Field2D Ni0, Ne0; // Gyro-center densities Field2D Ti0, Te0; // Starting isotropic temperatures diff --git a/examples/lapd-drift/lapd_drift.cxx b/examples/lapd-drift/lapd_drift.cxx index 3e9588d72f..da7980bb95 100644 --- a/examples/lapd-drift/lapd_drift.cxx +++ b/examples/lapd-drift/lapd_drift.cxx @@ -20,7 +20,8 @@ class LAPDdrift : public PhysicsModel { private: // 2D initial profiles - Field2D Ni0, Ti0, Te0, Vi0, phi0, Ve0, rho0, Ajpar0, src_ni0; + Field2D Ni0, Ti0, Te0, Vi0, phi0, Ve0, Ajpar0, src_ni0; + Coordinates::FieldMetric rho0; Vector2D b0xcv; // for curvature terms // 3D evolving fields @@ -731,8 +732,8 @@ class LAPDdrift : public PhysicsModel { /****************SPECIAL DIFFERENTIAL OPERATORS******************/ - const Field2D Perp_Grad_dot_Grad(const Field2D &p, const Field2D &f) { - + Coordinates::FieldMetric Perp_Grad_dot_Grad(const Field2D& p, const Field2D& f) { + return DDX(p)*DDX(f)*mesh->getCoordinates()->g11; } @@ -741,8 +742,8 @@ class LAPDdrift : public PhysicsModel { // ExB terms. These routines allow comparisons with BOUT-06 // if bout_exb=true is set in BOUT.inp ///////////////////////////////////////////////////////////////// - const Field2D vE_Grad(const Field2D &f, const Field2D &p) { - Field2D result; + Coordinates::FieldMetric vE_Grad(const Field2D& f, const Field2D& p) { + Coordinates::FieldMetric result; if (bout_exb) { // Use a subset of terms for comparison to BOUT-06 result = 0.0; @@ -770,25 +771,27 @@ class LAPDdrift : public PhysicsModel { int jzm = (jz - 1 + ncz) % ncz; // J++ = DDZ(p)*DDX(f) - DDX(p)*DDZ(f) - BoutReal Jpp = 0.25*( (p(jx,jy,jzp) - p(jx,jy,jzm))* - (f(jx+1,jy) - f(jx-1,jy)) - - (p(jx+1,jy,jz) - p(jx-1,jy,jz))* - (f(jx,jy) - f(jx,jy)) ) - / (coord->dx(jx,jy) * coord->dz); - + BoutReal Jpp = + 0.25 + * ((p(jx, jy, jzp) - p(jx, jy, jzm)) * (f(jx + 1, jy) - f(jx - 1, jy)) + - (p(jx + 1, jy, jz) - p(jx - 1, jy, jz)) * (f(jx, jy) - f(jx, jy))) + / (coord->dx(jx, jy, jz) * coord->dz(jx, jy, jz)); + // J+x - BoutReal Jpx = 0.25*( f(jx+1,jy)*(p(jx+1,jy,jzp)-p(jx+1,jy,jzm)) - - f(jx-1,jy)*(p(jx-1,jy,jzp)-p(jx-1,jy,jzm)) - - f(jx,jy)*(p(jx+1,jy,jzp)-p(jx-1,jy,jzp)) + - f(jx,jy)*(p(jx+1,jy,jzm)-p(jx-1,jy,jzm))) - / (coord->dx(jx,jy) * coord->dz); + BoutReal Jpx = 0.25 + * (f(jx + 1, jy) * (p(jx + 1, jy, jzp) - p(jx + 1, jy, jzm)) + - f(jx - 1, jy) * (p(jx - 1, jy, jzp) - p(jx - 1, jy, jzm)) + - f(jx, jy) * (p(jx + 1, jy, jzp) - p(jx - 1, jy, jzp)) + + f(jx, jy) * (p(jx + 1, jy, jzm) - p(jx - 1, jy, jzm))) + / (coord->dx(jx, jy, jz) * coord->dz(jx, jy, jz)); // Jx+ - BoutReal Jxp = 0.25*( f(jx+1,jy)*(p(jx,jy,jzp)-p(jx+1,jy,jz)) - - f(jx-1,jy)*(p(jx-1,jy,jz)-p(jx,jy,jzm)) - - f(jx-1,jy)*(p(jx,jy,jzp)-p(jx-1,jy,jz)) + - f(jx+1,jy)*(p(jx+1,jy,jz)-p(jx,jy,jzm))) - / (coord->dx(jx,jy) * coord->dz); - + BoutReal Jxp = 0.25 + * (f(jx + 1, jy) * (p(jx, jy, jzp) - p(jx + 1, jy, jz)) + - f(jx - 1, jy) * (p(jx - 1, jy, jz) - p(jx, jy, jzm)) + - f(jx - 1, jy) * (p(jx, jy, jzp) - p(jx - 1, jy, jz)) + + f(jx + 1, jy) * (p(jx + 1, jy, jz) - p(jx, jy, jzm))) + / (coord->dx(jx, jy, jz) * coord->dz(jx, jy, jz)); + result(jx,jy,jz) = (Jpp + Jpx + Jxp) / 3.; } } @@ -833,25 +836,29 @@ class LAPDdrift : public PhysicsModel { int jzm = (jz - 1 + ncz) % ncz; // J++ = DDZ(p)*DDX(f) - DDX(p)*DDZ(f) - BoutReal Jpp = 0.25*( (p(jx,jy,jzp) - p(jx,jy,jzm))* - (f(jx+1,jy,jz) - f(jx-1,jy,jz)) - - (p(jx+1,jy,jz) - p(jx-1,jy,jz))* - (f(jx,jy,jzp) - f(jx,jy,jzm)) ) - / (coord->dx(jx,jy) * coord->dz); - + BoutReal Jpp = 0.25 + * ((p(jx, jy, jzp) - p(jx, jy, jzm)) + * (f(jx + 1, jy, jz) - f(jx - 1, jy, jz)) + - (p(jx + 1, jy, jz) - p(jx - 1, jy, jz)) + * (f(jx, jy, jzp) - f(jx, jy, jzm))) + / (coord->dx(jx, jy, jz) * coord->dz(jx, jy, jz)); + // J+x - BoutReal Jpx = 0.25*( f(jx+1,jy,jz)*(p(jx+1,jy,jzp)-p(jx+1,jy,jzm)) - - f(jx-1,jy,jz)*(p(jx-1,jy,jzp)-p(jx-1,jy,jzm)) - - f(jx,jy,jzp)*(p(jx+1,jy,jzp)-p(jx-1,jy,jzp)) + - f(jx,jy,jzm)*(p(jx+1,jy,jzm)-p(jx-1,jy,jzm))) - / (coord->dx(jx,jy) * coord->dz); + BoutReal Jpx = + 0.25 + * (f(jx + 1, jy, jz) * (p(jx + 1, jy, jzp) - p(jx + 1, jy, jzm)) + - f(jx - 1, jy, jz) * (p(jx - 1, jy, jzp) - p(jx - 1, jy, jzm)) + - f(jx, jy, jzp) * (p(jx + 1, jy, jzp) - p(jx - 1, jy, jzp)) + + f(jx, jy, jzm) * (p(jx + 1, jy, jzm) - p(jx - 1, jy, jzm))) + / (coord->dx(jx, jy, jz) * coord->dz(jx, jy, jz)); // Jx+ - BoutReal Jxp = 0.25*( f(jx+1,jy,jzp)*(p(jx,jy,jzp)-p(jx+1,jy,jz)) - - f(jx-1,jy,jzm)*(p(jx-1,jy,jz)-p(jx,jy,jzm)) - - f(jx-1,jy,jzp)*(p(jx,jy,jzp)-p(jx-1,jy,jz)) + - f(jx+1,jy,jzm)*(p(jx+1,jy,jz)-p(jx,jy,jzm))) - / (coord->dx(jx,jy) * coord->dz); - + BoutReal Jxp = 0.25 + * (f(jx + 1, jy, jzp) * (p(jx, jy, jzp) - p(jx + 1, jy, jz)) + - f(jx - 1, jy, jzm) * (p(jx - 1, jy, jz) - p(jx, jy, jzm)) + - f(jx - 1, jy, jzp) * (p(jx, jy, jzp) - p(jx - 1, jy, jz)) + + f(jx + 1, jy, jzm) * (p(jx + 1, jy, jz) - p(jx, jy, jzm))) + / (coord->dx(jx, jy, jz) * coord->dz(jx, jy, jz)); + result(jx,jy,jz) = (Jpp + Jpx + Jxp) / 3.; } } diff --git a/examples/laplacexy/laplace_perp/test.cxx b/examples/laplacexy/laplace_perp/test.cxx index dac59f6677..f76e999ca3 100644 --- a/examples/laplacexy/laplace_perp/test.cxx +++ b/examples/laplacexy/laplace_perp/test.cxx @@ -59,7 +59,7 @@ int main(int argc, char** argv) { mesh->communicate(solved); // Now differentiate using Laplace_perp - Field2D result = Laplace_perp(solved); + auto result = Laplace_perp(solved); // Write fields to output SAVE_ONCE3(input, solved, result); diff --git a/examples/orszag-tang/mhd.cxx b/examples/orszag-tang/mhd.cxx index 45d4286798..02e93efa36 100644 --- a/examples/orszag-tang/mhd.cxx +++ b/examples/orszag-tang/mhd.cxx @@ -47,8 +47,8 @@ class MHD : public PhysicsModel { solver->add(B, "B"); Coordinates *coord = mesh->getCoordinates(); - output.write("dx[0,0] = {:e}, dy[0,0] = {:e}, dz = {:e}\n", coord->dx(0, 0), - coord->dy(0, 0), coord->dz); + output.write("dx(0,0,0) = {:e}, dy(0,0,0) = {:e}, dz(0,0,0) = {:e}\n", + coord->dx(0, 0, 0), coord->dy(0, 0, 0), coord->dz(0, 0, 0)); SAVE_REPEAT(divB); diff --git a/include/boundary_region.hxx b/include/boundary_region.hxx index eda8c3944f..dd24f48d3a 100644 --- a/include/boundary_region.hxx +++ b/include/boundary_region.hxx @@ -20,15 +20,19 @@ enum class BndryLoc {xin, ydown, yup, all, - par_fwd, // Don't include parallel boundaries - par_bkwd}; + par_fwd_xin, // Don't include parallel boundaries + par_bkwd_xin, + par_fwd_xout, // Don't include parallel boundaries + par_bkwd_xout}; constexpr BndryLoc BNDRY_XIN = BndryLoc::xin; constexpr BndryLoc BNDRY_XOUT = BndryLoc::xout; constexpr BndryLoc BNDRY_YDOWN = BndryLoc::ydown; constexpr BndryLoc BNDRY_YUP = BndryLoc::yup; constexpr BndryLoc BNDRY_ALL = BndryLoc::all; -constexpr BndryLoc BNDRY_PAR_FWD = BndryLoc::par_fwd; -constexpr BndryLoc BNDRY_PAR_BKWD = BndryLoc::par_bkwd; +constexpr BndryLoc BNDRY_PAR_FWD_XIN = BndryLoc::par_fwd_xin; +constexpr BndryLoc BNDRY_PAR_BKWD_XIN = BndryLoc::par_bkwd_xin; +constexpr BndryLoc BNDRY_PAR_FWD_XOUT = BndryLoc::par_fwd_xout; +constexpr BndryLoc BNDRY_PAR_BKWD_XOUT = BndryLoc::par_bkwd_xout; class BoundaryRegionBase { public: diff --git a/include/bout/build_config.hxx b/include/bout/build_config.hxx index f1aedb681a..2362ec45e1 100644 --- a/include/bout/build_config.hxx +++ b/include/bout/build_config.hxx @@ -32,6 +32,7 @@ constexpr auto use_output_debug = static_cast(BOUT_USE_OUTPUT_DEBUG); constexpr auto use_sigfpe = static_cast(BOUT_USE_SIGFPE); constexpr auto use_signal = static_cast(BOUT_USE_SIGNAL); constexpr auto use_track = static_cast(BOUT_USE_TRACK); +constexpr auto use_metric_3d = static_cast(BOUT_USE_METRIC_3D); constexpr auto use_msgstack = static_cast(BOUT_USE_MSGSTACK); } // namespace build diff --git a/include/bout/coordinates.hxx b/include/bout/coordinates.hxx index 9998d6c2bb..516460d71f 100644 --- a/include/bout/coordinates.hxx +++ b/include/bout/coordinates.hxx @@ -40,6 +40,7 @@ #include "field2d.hxx" #include "field3d.hxx" +class Datafile; class Mesh; /*! @@ -49,6 +50,12 @@ class Mesh; */ class Coordinates { public: +#if BOUT_USE_METRIC_3D + using FieldMetric = Field3D; +#else + using FieldMetric = Field2D; +#endif + /// Standard constructor from input Coordinates(Mesh *mesh, Options* options = nullptr); @@ -64,11 +71,11 @@ public: /// A constructor useful for testing purposes. To use it, inherit /// from Coordinates. If \p calculate_geometry is true (default), /// calculate the non-uniform variables, Christoffel symbols - Coordinates(Mesh* mesh, Field2D dx, Field2D dy, BoutReal dz, Field2D J, Field2D Bxy, - Field2D g11, Field2D g22, Field2D g33, Field2D g12, Field2D g13, - Field2D g23, Field2D g_11, Field2D g_22, Field2D g_33, Field2D g_12, - Field2D g_13, Field2D g_23, Field2D ShiftTorsion, Field2D IntShiftTorsion, - bool calculate_geometry = true); + Coordinates(Mesh* mesh, FieldMetric dx, FieldMetric dy, FieldMetric dz, FieldMetric J, + FieldMetric Bxy, FieldMetric g11, FieldMetric g22, FieldMetric g33, + FieldMetric g12, FieldMetric g13, FieldMetric g23, FieldMetric g_11, + FieldMetric g_22, FieldMetric g_33, FieldMetric g_12, FieldMetric g_13, + FieldMetric g_23, FieldMetric ShiftTorsion, FieldMetric IntShiftTorsion); Coordinates& operator=(Coordinates&&) = default; @@ -80,36 +87,45 @@ public: * Must be a better way so that Coordinates doesn't depend on Datafile */ void outputVars(Datafile &file); - - Field2D dx, dy; ///< Mesh spacing in x and y - BoutReal dz; ///< Mesh spacing in Z - BoutReal zlength() const { return dz * nz; } ///< Length of the Z domain. Used for FFTs + FieldMetric dx, dy, dz; ///< Mesh spacing in x, y and z + + Field2D zlength() const { +#if BOUT_USE_METRIC_3D + Field2D result(0., localmesh); + BOUT_FOR_SERIAL(i, dz.getRegion("RGN_ALL")) { result[i] += dz[i]; } + return result; +#else + return dz * nz; +#endif + } ///< Length of the Z domain. Used for FFTs /// True if corrections for non-uniform mesh spacing should be included in operators bool non_uniform; - Field2D d1_dx, d1_dy; ///< 2nd-order correction for non-uniform meshes d/di(1/dx) and d/di(1/dy) - - Field2D J; ///< Coordinate system Jacobian, so volume of cell is J*dx*dy*dz + /// 2nd-order correction for non-uniform meshes d/di(1/dx), d/di(1/dy) and d/di(1/dz) + FieldMetric d1_dx, d1_dy, d1_dz; + + FieldMetric J; ///< Coordinate system Jacobian, so volume of cell is J*dx*dy*dz + + FieldMetric Bxy; ///< Magnitude of B = nabla z times nabla x - Field2D Bxy; ///< Magnitude of B = nabla z times nabla x - /// Contravariant metric tensor (g^{ij}) - Field2D g11, g22, g33, g12, g13, g23; - + FieldMetric g11, g22, g33, g12, g13, g23; + /// Covariant metric tensor - Field2D g_11, g_22, g_33, g_12, g_13, g_23; - + FieldMetric g_11, g_22, g_33, g_12, g_13, g_23; + /// Christoffel symbol of the second kind (connection coefficients) - Field2D G1_11, G1_22, G1_33, G1_12, G1_13, G1_23; - Field2D G2_11, G2_22, G2_33, G2_12, G2_13, G2_23; - Field2D G3_11, G3_22, G3_33, G3_12, G3_13, G3_23; - - Field2D G1, G2, G3; - - Field2D ShiftTorsion; ///< d pitch angle / dx. Needed for vector differentials (Curl) + FieldMetric G1_11, G1_22, G1_33, G1_12, G1_13, G1_23; + FieldMetric G2_11, G2_22, G2_33, G2_12, G2_13, G2_23; + FieldMetric G3_11, G3_22, G3_33, G3_12, G3_13, G3_23; + + FieldMetric G1, G2, G3; - Field2D IntShiftTorsion; ///< Integrated shear (I in BOUT notation) + /// d pitch angle / dx. Needed for vector differentials (Curl) + FieldMetric ShiftTorsion; + + FieldMetric IntShiftTorsion; ///< Integrated shear (I in BOUT notation) /// Calculate differential geometry quantities from the metric tensor int geometry(bool recalculate_staggered = true, @@ -120,7 +136,6 @@ public: int calcContravariant(const std::string& region = "RGN_ALL"); int jacobian(); ///< Calculate J and Bxy - /////////////////////////////////////////////////////////// // Parallel transforms /////////////////////////////////////////////////////////// @@ -144,64 +159,80 @@ public: #ifdef DERIV_FUNC_REGION_ENUM_TO_STRING #error This utility macro should not clash with another one #else -#define DERIV_FUNC_REGION_ENUM_TO_STRING(func, T) \ - [[deprecated("Please use Coordinates::#func(const #T& f, " \ - "CELL_LOC outloc = CELL_DEFAULT, const std::string& method = \"DEFAULT\", " \ - "const std::string& region = \"RGN_ALL\") instead")]] \ - inline T func(const T& f, CELL_LOC outloc, const std::string& method, \ - REGION region) { \ - return func(f, outloc, method, toString(region)); \ - } \ - [[deprecated("Please use Coordinates::#func(const #T& f, " \ - "CELL_LOC outloc = CELL_DEFAULT, const std::string& method = \"DEFAULT\", " \ - "const std::string& region = \"RGN_ALL\") instead")]] \ - inline T func(const T& f, CELL_LOC outloc, DIFF_METHOD method, \ - REGION region = RGN_NOBNDRY) { \ - return func(f, outloc, toString(method), toString(region)); \ +#define DERIV_FUNC_REGION_ENUM_TO_STRING(func, ResultType, T) \ + [[deprecated( \ + "Please use Coordinates::#func(const #T& f, " \ + "CELL_LOC outloc = CELL_DEFAULT, const std::string& method = \"DEFAULT\", " \ + "const std::string& region = \"RGN_ALL\") instead")]] inline ResultType \ + func(const T& f, CELL_LOC outloc, const std::string& method, REGION region) { \ + return func(f, outloc, method, toString(region)); \ + } \ + [[deprecated( \ + "Please use Coordinates::#func(const #T& f, " \ + "CELL_LOC outloc = CELL_DEFAULT, const std::string& method = \"DEFAULT\", " \ + "const std::string& region = \"RGN_ALL\") instead")]] inline ResultType \ + func(const T& f, CELL_LOC outloc, DIFF_METHOD method, REGION region = RGN_NOBNDRY) { \ + return func(f, outloc, toString(method), toString(region)); \ } #endif #ifdef GRAD_FUNC_REGION_ENUM_TO_STRING #error This utility macro should not clash with another one #else -#define GRAD_FUNC_REGION_ENUM_TO_STRING(func, T) \ - [[deprecated("Please use Coordinates::#func(const #T& f, " \ +#define GRAD_FUNC_REGION_ENUM_TO_STRING(func, ResultType, T) \ + [[deprecated( \ + "Please use Coordinates::#func(const #T& f, " \ "CELL_LOC outloc = CELL_DEFAULT, const std::string& method = \"DEFAULT\") " \ - "instead")]] \ - inline T func(const T& f, CELL_LOC outloc, DIFF_METHOD method) { \ - return func(f, outloc, toString(method)); \ + "instead")]] inline ResultType \ + func(const T& f, CELL_LOC outloc, DIFF_METHOD method) { \ + return func(f, outloc, toString(method)); \ } #endif - Field2D DDX(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); - DERIV_FUNC_REGION_ENUM_TO_STRING(DDX, Field2D); + FieldMetric DDX(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); + DERIV_FUNC_REGION_ENUM_TO_STRING(DDX, FieldMetric, Field2D); - Field2D DDY(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); - DERIV_FUNC_REGION_ENUM_TO_STRING(DDY, Field2D); + FieldMetric DDY(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); + DERIV_FUNC_REGION_ENUM_TO_STRING(DDY, FieldMetric, Field2D); - Field2D DDZ(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); - DERIV_FUNC_REGION_ENUM_TO_STRING(DDZ, Field2D); + FieldMetric DDZ(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); + DERIV_FUNC_REGION_ENUM_TO_STRING(DDZ, FieldMetric, Field2D); + + Field3D DDX(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); + + Field3D DDY(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); + + Field3D DDZ(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); /// Gradient along magnetic field b.Grad(f) - Field2D Grad_par(const Field2D& var, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT"); - GRAD_FUNC_REGION_ENUM_TO_STRING(Grad_par, Field2D); + FieldMetric Grad_par(const Field2D& var, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); + GRAD_FUNC_REGION_ENUM_TO_STRING(Grad_par, FieldMetric, Field2D); Field3D Grad_par(const Field3D& var, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT"); - GRAD_FUNC_REGION_ENUM_TO_STRING(Grad_par, Field3D); + GRAD_FUNC_REGION_ENUM_TO_STRING(Grad_par, Field3D, Field3D); /// Advection along magnetic field V*b.Grad(f) - Field2D Vpar_Grad_par(const Field2D& v, const Field2D& f, - CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT"); + FieldMetric Vpar_Grad_par(const Field2D& v, const Field2D& f, + CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); [[deprecated("Please use Coordinates::Vpar_Grad_par(const Field2D& v, " - "const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, " - "const std::string& method = \"DEFAULT\") instead")]] - inline Field2D Vpar_Grad_par(const Field2D& v, const Field2D& f, CELL_LOC outloc, - DIFF_METHOD method) { + "const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, " + "const std::string& method = \"DEFAULT\") instead")]] inline FieldMetric + Vpar_Grad_par(const Field2D& v, const Field2D& f, CELL_LOC outloc, DIFF_METHOD method) { return Vpar_Grad_par(v, f, outloc, toString(method)); } @@ -216,22 +247,22 @@ public: } /// Divergence along magnetic field Div(b*f) = B.Grad(f/B) - Field2D Div_par(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT"); - GRAD_FUNC_REGION_ENUM_TO_STRING(Div_par, Field2D); + FieldMetric Div_par(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); + GRAD_FUNC_REGION_ENUM_TO_STRING(Div_par, FieldMetric, Field2D); Field3D Div_par(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT"); - GRAD_FUNC_REGION_ENUM_TO_STRING(Div_par, Field3D); + GRAD_FUNC_REGION_ENUM_TO_STRING(Div_par, Field3D, Field3D); // Second derivative along magnetic field - Field2D Grad2_par2(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT"); - GRAD_FUNC_REGION_ENUM_TO_STRING(Grad2_par2, Field2D); + FieldMetric Grad2_par2(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); + GRAD_FUNC_REGION_ENUM_TO_STRING(Grad2_par2, FieldMetric, Field2D); Field3D Grad2_par2(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT"); - GRAD_FUNC_REGION_ENUM_TO_STRING(Grad2_par2, Field3D); + GRAD_FUNC_REGION_ENUM_TO_STRING(Grad2_par2, Field3D, Field3D); #undef DERIV_FUNC_REGION_ENUM_TO_STRING #undef GRAD_FUNC_REGION_ENUM_TO_STRING @@ -239,19 +270,19 @@ public: // Perpendicular Laplacian operator, using only X-Z derivatives // NOTE: This might be better bundled with the Laplacian inversion code // since it makes use of the same coefficients and FFT routines - Field2D Delp2(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, bool useFFT = true); + FieldMetric Delp2(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, bool useFFT = true); Field3D Delp2(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, bool useFFT = true); FieldPerp Delp2(const FieldPerp& f, CELL_LOC outloc = CELL_DEFAULT, bool useFFT = true); // Full parallel Laplacian operator on scalar field - // Laplace_par(f) = Div( b (b dot Grad(f)) ) - Field2D Laplace_par(const Field2D &f, CELL_LOC outloc=CELL_DEFAULT); + // Laplace_par(f) = Div( b (b dot Grad(f)) ) + FieldMetric Laplace_par(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT); Field3D Laplace_par(const Field3D &f, CELL_LOC outloc=CELL_DEFAULT); // Full Laplacian operator on scalar field - Field2D Laplace(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& dfdy_boundary_conditions = "free_o3", - const std::string& dfdy_dy_region = ""); + FieldMetric Laplace(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& dfdy_boundary_conditions = "free_o3", + const std::string& dfdy_dy_region = ""); Field3D Laplace(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& dfdy_boundary_conditions = "free_o3", const std::string& dfdy_dy_region = ""); diff --git a/include/bout/fv_ops.hxx b/include/bout/fv_ops.hxx index 9281aa9762..574681511c 100644 --- a/include/bout/fv_ops.hxx +++ b/include/bout/fv_ops.hxx @@ -231,11 +231,11 @@ namespace FV { for (int j = ys; j <= ye; j++) { // Pre-calculate factors which multiply fluxes - +#if not(BOUT_USE_METRIC_3D) // For right cell boundaries BoutReal common_factor = (coord->J(i, j) + coord->J(i, j + 1)) / (sqrt(coord->g_22(i, j)) + sqrt(coord->g_22(i, j + 1))); - + BoutReal flux_factor_rc = common_factor / (coord->dy(i, j) * coord->J(i, j)); BoutReal flux_factor_rp = common_factor / (coord->dy(i, j + 1) * coord->J(i, j + 1)); @@ -245,8 +245,28 @@ namespace FV { BoutReal flux_factor_lc = common_factor / (coord->dy(i, j) * coord->J(i, j)); BoutReal flux_factor_lm = common_factor / (coord->dy(i, j - 1) * coord->J(i, j - 1)); - +#endif for (int k = 0; k < mesh->LocalNz; k++) { +#if BOUT_USE_METRIC_3D + // For right cell boundaries + BoutReal common_factor = + (coord->J(i, j, k) + coord->J(i, j + 1, k)) + / (sqrt(coord->g_22(i, j, k)) + sqrt(coord->g_22(i, j + 1, k))); + + BoutReal flux_factor_rc = + common_factor / (coord->dy(i, j, k) * coord->J(i, j, k)); + BoutReal flux_factor_rp = + common_factor / (coord->dy(i, j + 1, k) * coord->J(i, j + 1, k)); + + // For left cell boundaries + common_factor = (coord->J(i, j, k) + coord->J(i, j - 1, k)) + / (sqrt(coord->g_22(i, j, k)) + sqrt(coord->g_22(i, j - 1, k))); + + BoutReal flux_factor_lc = + common_factor / (coord->dy(i, j, k) * coord->J(i, j, k)); + BoutReal flux_factor_lm = + common_factor / (coord->dy(i, j - 1, k) * coord->J(i, j - 1, k)); +#endif //////////////////////////////////////////// // Reconstruct f at the cell faces @@ -452,13 +472,13 @@ namespace FV { if (vU > 0.0) { BoutReal flux = vU * s.R; - result[i] += flux / (coord->J[i] * coord->dz); - result[i.zp()] -= flux / (coord->J[i.zp()] * coord->dz); + result[i] += flux / (coord->J[i] * coord->dz[i]); + result[i.zp()] -= flux / (coord->J[i.zp()] * coord->dz[i.zp()]); } if (vD < 0.0) { BoutReal flux = vD * s.L; - result[i] -= flux / (coord->J[i] * coord->dz); - result[i.zm()] += flux / (coord->J[i.zm()] * coord->dz); + result[i] -= flux / (coord->J[i] * coord->dz[i]); + result[i.zm()] += flux / (coord->J[i.zm()] * coord->dz[i.zm()]); } } @@ -473,6 +493,7 @@ namespace FV { Field3D yresult = 0.0; yresult.setDirectionY(YDirectionType::Aligned); + BOUT_FOR(i, result.getRegion("RGN_NOBNDRY")) { // Y velocities on y boundaries BoutReal vU = 0.25 * (vy[i] + vy[i.yp()]) * (coord->J[i] + coord->J[i.yp()]); @@ -487,6 +508,11 @@ namespace FV { } return result + fromFieldAligned(yresult, "RGN_NOBNDRY"); } -} + /*! + * X-Z Finite Volume diffusion operator + */ + Field3D Div_Perp_Lap(const Field3D& a, const Field3D& f, + CELL_LOC outloc = CELL_DEFAULT); +} // namespace FV #endif // __FV_OPS_H__ diff --git a/include/bout/mesh.hxx b/include/bout/mesh.hxx index 4e0789c90f..f21cbeccf7 100644 --- a/include/bout/mesh.hxx +++ b/include/bout/mesh.hxx @@ -193,9 +193,11 @@ class Mesh { /// @param[out] var This will be set to the value. Will be allocated if needed /// @param[in] name Name of the variable to read /// @param[in] def The default value if not found + /// @param[in] communicate Should the field be communicated to fill guard cells? /// /// @returns zero if successful, non-zero on failure - int get(Field2D &var, const std::string &name, BoutReal def=0.0, CELL_LOC location=CELL_DEFAULT); + int get(Field2D& var, const std::string& name, BoutReal def=0.0, + bool communicate = true, CELL_LOC location=CELL_DEFAULT); /// Get a Field3D from the input source /// @@ -225,11 +227,14 @@ class Mesh { /// By default all fields revert to zero /// /// @param[in] var This will be set to the value read - /// @param[in] name The name of the vector. Individual fields are read based on this name by appending. See above + /// @param[in] name The name of the vector. Individual fields are read based on this + /// name by appending. See above /// @param[in] def The default value if not found (used for all the components) + /// @param[in] communicate Should the field be communicated to fill guard cells? /// - /// @returns zero always. - int get(Vector2D &var, const std::string &name, BoutReal def=0.0); + /// @returns zero always. + int get(Vector2D& var, const std::string& name, BoutReal def = 0.0, + bool communicate = true); /// Get a Vector3D from the input source. /// If \p var is covariant then this gets three @@ -239,11 +244,14 @@ class Mesh { /// By default all fields revert to zero /// /// @param[in] var This will be set to the value read - /// @param[in] name The name of the vector. Individual fields are read based on this name by appending. See above + /// @param[in] name The name of the vector. Individual fields are read based on this + /// name by appending. See above /// @param[in] def The default value if not found (used for all the components) + /// @param[in] communicate Should the field be communicated to fill guard cells? /// - /// @returns zero always. - int get(Vector3D &var, const std::string &name, BoutReal def=0.0); + /// @returns zero always. + int get(Vector3D& var, const std::string& name, BoutReal def = 0.0, + bool communicate = true); /// Test if input source was a grid file bool isDataSourceGridFile() const; @@ -661,6 +669,7 @@ class Mesh { // (circular dependency between Mesh and Coordinates) auto inserted = coords_map.emplace(location, nullptr); inserted.first->second = createDefaultCoordinates(location); + inserted.first->second->geometry(false); return inserted.first->second; } diff --git a/include/bout/paralleltransform.hxx b/include/bout/paralleltransform.hxx index 0ceb6a5f62..71efe2da59 100644 --- a/include/bout/paralleltransform.hxx +++ b/include/bout/paralleltransform.hxx @@ -49,34 +49,49 @@ public: /// Convert a field into field-aligned coordinates /// so that the y index is along the magnetic field - virtual const Field3D toFieldAligned(const Field3D &f, const std::string& region = "RGN_ALL") = 0; + virtual Field3D toFieldAligned(const Field3D& f, + const std::string& region = "RGN_ALL") = 0; [[deprecated("Please use toFieldAligned(const Field3D& f, " - "const std::string& region = \"RGN_ALL\") instead")]] - const Field3D toFieldAligned(const Field3D &f, REGION region) { + "const std::string& region = \"RGN_ALL\") instead")]] Field3D + toFieldAligned(const Field3D& f, REGION region) { return toFieldAligned(f, toString(region)); } - virtual const FieldPerp toFieldAligned(const FieldPerp &f, const std::string& region = "RGN_ALL") = 0; + virtual FieldPerp toFieldAligned(const FieldPerp& f, + const std::string& region = "RGN_ALL") = 0; [[deprecated("Please use toFieldAligned(const FieldPerp& f, " - "const std::string& region = \"RGN_ALL\") instead")]] - const FieldPerp toFieldAligned(const FieldPerp &f, REGION region) { + "const std::string& region = \"RGN_ALL\") instead")]] FieldPerp + toFieldAligned(const FieldPerp& f, REGION region) { return toFieldAligned(f, toString(region)); } - + /// Convert back from field-aligned coordinates /// into standard form - virtual const Field3D fromFieldAligned(const Field3D &f, const std::string& region = "RGN_ALL") = 0; + virtual Field3D fromFieldAligned(const Field3D& f, + const std::string& region = "RGN_ALL") = 0; [[deprecated("Please use fromFieldAligned(const Field3D& f, " - "const std::string& region = \"RGN_ALL\") instead")]] - const Field3D fromFieldAligned(const Field3D &f, REGION region) { + "const std::string& region = \"RGN_ALL\") instead")]] Field3D + fromFieldAligned(const Field3D& f, REGION region) { return fromFieldAligned(f, toString(region)); } - virtual const FieldPerp fromFieldAligned(const FieldPerp &f, const std::string& region = "RGN_ALL") = 0; + virtual FieldPerp fromFieldAligned(const FieldPerp& f, + const std::string& region = "RGN_ALL") = 0; [[deprecated("Please use fromFieldAligned(const FieldPerp& f, " - "const std::string& region = \"RGN_ALL\") instead")]] - const FieldPerp fromFieldAligned(const FieldPerp &f, REGION region) { + "const std::string& region = \"RGN_ALL\") instead")]] FieldPerp + fromFieldAligned(const FieldPerp& f, REGION region) { return fromFieldAligned(f, toString(region)); } + /// Field2D are axisymmetric, so transformation to or from field-aligned coordinates is + /// a null operation. + virtual Field2D toFieldAligned(const Field2D& f, + const std::string& UNUSED(region) = "RGN_ALL") { + return f; + } + virtual Field2D fromFieldAligned(const Field2D& f, + const std::string& UNUSED(region) = "RGN_ALL") { + return f; + } + virtual bool canToFromFieldAligned() = 0; struct PositionsAndWeights { @@ -139,12 +154,14 @@ public: * The field is already aligned in Y, so this * does nothing */ - const Field3D toFieldAligned(const Field3D& f, const std::string& UNUSED(region) = "RGN_ALL") override { + Field3D toFieldAligned(const Field3D& f, + const std::string& UNUSED(region) = "RGN_ALL") override { ASSERT2(f.getDirectionY() == YDirectionType::Standard); Field3D result = f; return result.setDirectionY(YDirectionType::Aligned); } - const FieldPerp toFieldAligned(const FieldPerp& f, const std::string& UNUSED(region) = "RGN_ALL") override { + FieldPerp toFieldAligned(const FieldPerp& f, + const std::string& UNUSED(region) = "RGN_ALL") override { ASSERT2(f.getDirectionY() == YDirectionType::Standard); FieldPerp result = f; return result.setDirectionY(YDirectionType::Aligned); @@ -154,12 +171,14 @@ public: * The field is already aligned in Y, so this * does nothing */ - const Field3D fromFieldAligned(const Field3D& f, const std::string& UNUSED(region) = "RGN_ALL") override { + Field3D fromFieldAligned(const Field3D& f, + const std::string& UNUSED(region) = "RGN_ALL") override { ASSERT2(f.getDirectionY() == YDirectionType::Aligned); Field3D result = f; return result.setDirectionY(YDirectionType::Standard); } - const FieldPerp fromFieldAligned(const FieldPerp& f, const std::string& UNUSED(region) = "RGN_ALL") override { + FieldPerp fromFieldAligned(const FieldPerp& f, + const std::string& UNUSED(region) = "RGN_ALL") override { ASSERT2(f.getDirectionY() == YDirectionType::Aligned); FieldPerp result = f; return result.setDirectionY(YDirectionType::Standard); @@ -211,18 +230,19 @@ public: * in X-Z, and the metric tensor will need to be changed * if X derivatives are used. */ - const Field3D toFieldAligned(const Field3D& f, const std::string& region = "RGN_ALL") override; - const FieldPerp toFieldAligned(const FieldPerp& f, - const std::string& region = "RGN_ALL") override; + Field3D toFieldAligned(const Field3D& f, + const std::string& region = "RGN_ALL") override; + FieldPerp toFieldAligned(const FieldPerp& f, + const std::string& region = "RGN_ALL") override; /*! * Converts a field back to X-Z orthogonal coordinates * from field aligned coordinates. */ - const Field3D fromFieldAligned(const Field3D& f, - const std::string& region = "RGN_ALL") override; - const FieldPerp fromFieldAligned(const FieldPerp& f, - const std::string& region = "RGN_ALL") override; + Field3D fromFieldAligned(const Field3D& f, + const std::string& region = "RGN_ALL") override; + FieldPerp fromFieldAligned(const FieldPerp& f, + const std::string& region = "RGN_ALL") override; std::vector getWeightsForYApproximation(int UNUSED(i), int UNUSED(j), int UNUSED(k), @@ -284,14 +304,13 @@ private: * Shift a 2D field in Z. * Since 2D fields are constant in Z, this has no effect */ - const Field2D shiftZ(const Field2D& f, const Field2D& UNUSED(zangle), - const std::string UNUSED(region) = "RGN_NOX") const { + Field2D shiftZ(const Field2D& f, const Field2D& UNUSED(zangle), + const std::string UNUSED(region) = "RGN_NOX") const { return f; }; [[deprecated("Please use shiftZ(const Field2D& f, const Field2D& zangle, " - "const std::string& region = \"RGN_NOX\") instead")]] - const Field2D shiftZ(const Field2D& f, const Field2D& UNUSED(zangle), - REGION UNUSED(region)) const { + "const std::string& region = \"RGN_NOX\") instead")]] Field2D + shiftZ(const Field2D& f, const Field2D& UNUSED(zangle), REGION UNUSED(region)) const { return f; }; @@ -302,12 +321,11 @@ private: * @param[in] zangle Toroidal angle (z) * */ - const Field3D shiftZ(const Field3D& f, const Field2D& zangle, - const std::string& region = "RGN_NOX") const; + Field3D shiftZ(const Field3D& f, const Field2D& zangle, + const std::string& region = "RGN_NOX") const; [[deprecated("Please use shiftZ(const Field3D& f, const Field2D& zangle, " - "const std::string& region = \"RGN_NOX\") instead")]] - const Field3D shiftZ(const Field3D& f, const Field2D& zangle, - REGION region) const { + "const std::string& region = \"RGN_NOX\") instead")]] Field3D + shiftZ(const Field3D& f, const Field2D& zangle, REGION region) const { return shiftZ(f, zangle, toString(region)); }; @@ -321,12 +339,12 @@ private: * @param[in] phs The phase to shift by * @param[in] y_direction_out The value to set yDirectionType of the result to */ - const Field3D shiftZ(const Field3D& f, const Tensor& phs, - const YDirectionType y_direction_out, - const std::string& region = "RGN_NOX") const; - const FieldPerp shiftZ(const FieldPerp& f, const Tensor& phs, - const YDirectionType y_direction_out, - const std::string& region = "RGN_NOX") const; + Field3D shiftZ(const Field3D& f, const Tensor& phs, + const YDirectionType y_direction_out, + const std::string& region = "RGN_NOX") const; + FieldPerp shiftZ(const FieldPerp& f, const Tensor& phs, + const YDirectionType y_direction_out, + const std::string& region = "RGN_NOX") const; /*! * Shift a given 1D array, assumed to be in Z, by the given \p zangle diff --git a/include/derivs.hxx b/include/derivs.hxx index fa9acbec2e..0b77834499 100644 --- a/include/derivs.hxx +++ b/include/derivs.hxx @@ -39,21 +39,21 @@ #ifdef DERIV_FUNC_REGION_ENUM_TO_STRING #error This utility macro should not clash with another one #else -#define DERIV_FUNC_REGION_ENUM_TO_STRING(func, T) \ -[[deprecated("Please use #func(const #T& f, CELL_LOC outloc = CELL_DEFAULT, " \ - "const std::string& method = \"DEFAULT\", const std::string& region = \"RGN_ALL\") " \ - "instead")]] \ -inline T func(const T& f, CELL_LOC outloc, const std::string& method, \ - REGION region) { \ - return func(f, outloc, method, toString(region)); \ -} \ -[[deprecated("Please use #func(const #T& f, CELL_LOC outloc = CELL_DEFAULT, " \ - "const std::string& method = \"DEFAULT\", const std::string& region = \"RGN_ALL\") " \ - "instead")]] \ -inline T func(const T& f, CELL_LOC outloc, DIFF_METHOD method, \ - REGION region = RGN_NOBNDRY) { \ - return func(f, outloc, toString(method), toString(region)); \ -} +#define DERIV_FUNC_REGION_ENUM_TO_STRING(func, T, Tr) \ + [[deprecated("Please use #func(const #T& f, CELL_LOC outloc = CELL_DEFAULT, " \ + "const std::string& method = \"DEFAULT\", const std::string& region = " \ + "\"RGN_ALL\") " \ + "instead")]] inline Tr \ + func(const T& f, CELL_LOC outloc, const std::string& method, REGION region) { \ + return func(f, outloc, method, toString(region)); \ + } \ + [[deprecated("Please use #func(const #T& f, CELL_LOC outloc = CELL_DEFAULT, " \ + "const std::string& method = \"DEFAULT\", const std::string& region = " \ + "\"RGN_ALL\") " \ + "instead")]] inline Tr \ + func(const T& f, CELL_LOC outloc, DIFF_METHOD method, REGION region = RGN_NOBNDRY) { \ + return func(f, outloc, toString(method), toString(region)); \ + } #endif #ifdef VDERIV_FUNC_REGION_ENUM_TO_STRING @@ -92,7 +92,7 @@ inline T func(const T1& v, const T2& f, CELL_LOC outloc, DIFF_METHOD method, \ /// If not given, defaults to RGN_NOBNDRY Field3D DDX(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(DDX, Field3D) +DERIV_FUNC_REGION_ENUM_TO_STRING(DDX, Field3D, Field3D) /// Calculate first partial derivative in X /// @@ -106,9 +106,10 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(DDX, Field3D) /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field2D DDX(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& - method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(DDX, Field2D) +Coordinates::FieldMetric DDX(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); +DERIV_FUNC_REGION_ENUM_TO_STRING(DDX, Field2D, Coordinates::FieldMetric) /// Calculate first partial derivative in Y /// @@ -124,7 +125,7 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(DDX, Field2D) /// If not given, defaults to RGN_NOBNDRY Field3D DDY(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(DDY, Field3D) +DERIV_FUNC_REGION_ENUM_TO_STRING(DDY, Field3D, Field3D) /// Calculate first partial derivative in Y /// @@ -138,9 +139,10 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(DDY, Field3D) /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field2D DDY(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& - method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(DDY, Field2D) +Coordinates::FieldMetric DDY(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); +DERIV_FUNC_REGION_ENUM_TO_STRING(DDY, Field2D, Coordinates::FieldMetric) /// Calculate first partial derivative in Z /// @@ -156,7 +158,7 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(DDY, Field2D) /// If not given, defaults to RGN_NOBNDRY Field3D DDZ(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(DDZ, Field3D) +DERIV_FUNC_REGION_ENUM_TO_STRING(DDZ, Field3D, Field3D) /// Calculate first partial derivative in Z /// @@ -170,9 +172,10 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(DDZ, Field3D) /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field2D DDZ(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& - method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(DDZ, Field2D) +Coordinates::FieldMetric DDZ(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); +DERIV_FUNC_REGION_ENUM_TO_STRING(DDZ, Field2D, Coordinates::FieldMetric) /// Calculate first partial derivative in Z /// @@ -188,7 +191,7 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(DDZ, Field2D) /// If not given, defaults to RGN_NOBNDRY Vector3D DDZ(const Vector3D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(DDZ, Vector3D) +DERIV_FUNC_REGION_ENUM_TO_STRING(DDZ, Vector3D, Vector3D) /// Calculate first partial derivative in Z /// @@ -204,7 +207,7 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(DDZ, Vector3D) /// If not given, defaults to RGN_NOBNDRY Vector2D DDZ(const Vector2D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(DDZ, Vector2D) +DERIV_FUNC_REGION_ENUM_TO_STRING(DDZ, Vector2D, Vector2D) ////////// SECOND DERIVATIVES ////////// @@ -222,7 +225,7 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(DDZ, Vector2D) /// If not given, defaults to RGN_NOBNDRY Field3D D2DX2(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(D2DX2, Field3D) +DERIV_FUNC_REGION_ENUM_TO_STRING(D2DX2, Field3D, Field3D) /// Calculate second partial derivative in X /// @@ -236,9 +239,10 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(D2DX2, Field3D) /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field2D D2DX2(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& - method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(D2DX2, Field2D) +Coordinates::FieldMetric D2DX2(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); +DERIV_FUNC_REGION_ENUM_TO_STRING(D2DX2, Field2D, Coordinates::FieldMetric) /// Calculate second partial derivative in Y /// @@ -254,7 +258,7 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(D2DX2, Field2D) /// If not given, defaults to RGN_NOBNDRY Field3D D2DY2(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(D2DY2, Field3D) +DERIV_FUNC_REGION_ENUM_TO_STRING(D2DY2, Field3D, Field3D) /// Calculate second partial derivative in Y /// @@ -268,9 +272,10 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(D2DY2, Field3D) /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field2D D2DY2(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& - method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(D2DY2, Field2D) +Coordinates::FieldMetric D2DY2(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); +DERIV_FUNC_REGION_ENUM_TO_STRING(D2DY2, Field2D, Coordinates::FieldMetric) /// Calculate second partial derivative in Z /// @@ -286,7 +291,7 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(D2DY2, Field2D) /// If not given, defaults to RGN_NOBNDRY Field3D D2DZ2(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(D2DZ2, Field3D) +DERIV_FUNC_REGION_ENUM_TO_STRING(D2DZ2, Field3D, Field3D) /// Calculate second partial derivative in Z /// @@ -300,9 +305,10 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(D2DZ2, Field3D) /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field2D D2DZ2(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& - method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(D2DZ2, Field2D) +Coordinates::FieldMetric D2DZ2(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); +DERIV_FUNC_REGION_ENUM_TO_STRING(D2DZ2, Field2D, Coordinates::FieldMetric) ////////// FOURTH DERIVATIVES ////////// @@ -320,7 +326,7 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(D2DZ2, Field2D) /// If not given, defaults to RGN_NOBNDRY Field3D D4DX4(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(D4DX4, Field3D) +DERIV_FUNC_REGION_ENUM_TO_STRING(D4DX4, Field3D, Field3D) /// Calculate forth partial derivative in X /// @@ -334,9 +340,10 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(D4DX4, Field3D) /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field2D D4DX4(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& - method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(D4DX4, Field2D) +Coordinates::FieldMetric D4DX4(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); +DERIV_FUNC_REGION_ENUM_TO_STRING(D4DX4, Field2D, Coordinates::FieldMetric) /// Calculate forth partial derivative in Y /// @@ -352,7 +359,7 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(D4DX4, Field2D) /// If not given, defaults to RGN_NOBNDRY Field3D D4DY4(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(D4DY4, Field3D) +DERIV_FUNC_REGION_ENUM_TO_STRING(D4DY4, Field3D, Field3D) /// Calculate forth partial derivative in Y /// @@ -366,9 +373,10 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(D4DY4, Field3D) /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field2D D4DY4(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& - method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(D4DY4, Field2D) +Coordinates::FieldMetric D4DY4(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); +DERIV_FUNC_REGION_ENUM_TO_STRING(D4DY4, Field2D, Coordinates::FieldMetric) /// Calculate forth partial derivative in Z /// @@ -384,7 +392,7 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(D4DY4, Field2D) /// If not given, defaults to RGN_NOBNDRY Field3D D4DZ4(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(D4DZ4, Field3D) +DERIV_FUNC_REGION_ENUM_TO_STRING(D4DZ4, Field3D, Field3D) /// Calculate forth partial derivative in Z /// @@ -398,9 +406,10 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(D4DZ4, Field3D) /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field2D D4DZ4(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& - method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(D4DZ4, Field2D) +Coordinates::FieldMetric D4DZ4(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); +DERIV_FUNC_REGION_ENUM_TO_STRING(D4DZ4, Field2D, Coordinates::FieldMetric) /// For terms of form v * grad(f) /// @@ -432,9 +441,11 @@ VDERIV_FUNC_REGION_ENUM_TO_STRING(VDDX, Field3D, Field3D, Field3D) /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field2D VDDX(const Field2D& v, const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -VDERIV_FUNC_REGION_ENUM_TO_STRING(VDDX, Field2D, Field2D, Field2D) +Coordinates::FieldMetric VDDX(const Field2D& v, const Field2D& f, + CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); +VDERIV_FUNC_REGION_ENUM_TO_STRING(VDDX, Coordinates::FieldMetric, Field2D, Field2D) /// For terms of form v * grad(f) /// @@ -466,9 +477,11 @@ VDERIV_FUNC_REGION_ENUM_TO_STRING(VDDY, Field3D, Field3D, Field3D) /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field2D VDDY(const Field2D& v, const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -VDERIV_FUNC_REGION_ENUM_TO_STRING(VDDY, Field2D, Field2D, Field2D) +Coordinates::FieldMetric VDDY(const Field2D& v, const Field2D& f, + CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); +VDERIV_FUNC_REGION_ENUM_TO_STRING(VDDY, Coordinates::FieldMetric, Field2D, Field2D) /// For terms of form v * grad(f) /// @@ -500,9 +513,11 @@ VDERIV_FUNC_REGION_ENUM_TO_STRING(VDDZ, Field3D, Field3D, Field3D) /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field2D VDDZ(const Field2D& v, const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -VDERIV_FUNC_REGION_ENUM_TO_STRING(VDDZ, Field2D, Field2D, Field2D) +Coordinates::FieldMetric VDDZ(const Field2D& v, const Field2D& f, + CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); +VDERIV_FUNC_REGION_ENUM_TO_STRING(VDDZ, Coordinates::FieldMetric, Field2D, Field2D) /// For terms of form v * grad(f) /// @@ -517,9 +532,11 @@ VDERIV_FUNC_REGION_ENUM_TO_STRING(VDDZ, Field2D, Field2D, Field2D) /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field2D VDDZ(const Field3D& v, const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -VDERIV_FUNC_REGION_ENUM_TO_STRING(VDDZ, Field2D, Field3D, Field2D) +Coordinates::FieldMetric VDDZ(const Field3D& v, const Field2D& f, + CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); +VDERIV_FUNC_REGION_ENUM_TO_STRING(VDDZ, Coordinates::FieldMetric, Field3D, Field2D) /// for terms of form div(v * f) /// @@ -551,9 +568,11 @@ VDERIV_FUNC_REGION_ENUM_TO_STRING(FDDX, Field3D, Field3D, Field3D) /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field2D FDDX(const Field2D& v, const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -VDERIV_FUNC_REGION_ENUM_TO_STRING(FDDX, Field2D, Field2D, Field2D) +Coordinates::FieldMetric FDDX(const Field2D& v, const Field2D& f, + CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); +VDERIV_FUNC_REGION_ENUM_TO_STRING(FDDX, Coordinates::FieldMetric, Field2D, Field2D) /// for terms of form div(v * f) /// @@ -585,9 +604,11 @@ VDERIV_FUNC_REGION_ENUM_TO_STRING(FDDY, Field3D, Field3D, Field3D) /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field2D FDDY(const Field2D& v, const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -VDERIV_FUNC_REGION_ENUM_TO_STRING(FDDY, Field2D, Field2D, Field2D) +Coordinates::FieldMetric FDDY(const Field2D& v, const Field2D& f, + CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); +VDERIV_FUNC_REGION_ENUM_TO_STRING(FDDY, Coordinates::FieldMetric, Field2D, Field2D) /// for terms of form div(v * f) /// @@ -619,9 +640,11 @@ VDERIV_FUNC_REGION_ENUM_TO_STRING(FDDZ, Field3D, Field3D, Field3D) /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field2D FDDZ(const Field2D& v, const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -VDERIV_FUNC_REGION_ENUM_TO_STRING(FDDZ, Field2D, Field2D, Field2D) +Coordinates::FieldMetric FDDZ(const Field2D& v, const Field2D& f, + CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); +VDERIV_FUNC_REGION_ENUM_TO_STRING(FDDZ, Coordinates::FieldMetric, Field2D, Field2D) /// Calculate mixed partial derivative in x and y /// @@ -684,28 +707,31 @@ D2DXDY(const Field3D& f, CELL_LOC outloc, DIFF_METHOD method, REGION region = RG /// (default) then the same as the region for the calculation as a /// whole. If dfdy_region < region in size then this will cause /// errors. -Field2D D2DXDY(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT", - const std::string& region = "RGN_NOBNDRY", - const std::string& dfdy_boundary_condition = "free_o3", - const std::string& dfdy_region = ""); +Coordinates::FieldMetric +D2DXDY(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY", + const std::string& dfdy_boundary_condition = "free_o3", + const std::string& dfdy_region = ""); [[deprecated( "Please use D2DXDY(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, " "const std::string& method = \"DEFAULT\", const std::string& region = \"RGN_ALL\", " - "const std::string& dfdy_boundary_condition) instead")]] inline Field2D -D2DXDY(const Field2D& f, CELL_LOC outloc, const std::string& method, REGION region, - const std::string& dfdy_boundary_condition = "free_o3", - const std::string& dfdy_region = "") { + "const std::string& dfdy_boundary_condition) instead")]] inline Coordinates:: + FieldMetric + D2DXDY(const Field2D& f, CELL_LOC outloc, const std::string& method, REGION region, + const std::string& dfdy_boundary_condition = "free_o3", + const std::string& dfdy_region = "") { return D2DXDY(f, outloc, method, toString(region), dfdy_boundary_condition, dfdy_region); } [[deprecated( "Please use D2DXDY(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, " "const std::string& method = \"DEFAULT\", const std::string& region = \"RGN_ALL\", " - "const std::string& dfdy_boundary_condition) instead")]] inline Field2D -D2DXDY(const Field2D& f, CELL_LOC outloc, DIFF_METHOD method, REGION region = RGN_NOBNDRY, - const std::string& dfdy_boundary_condition = "free_o3", - const std::string& dfdy_region = "") { + "const std::string& dfdy_boundary_condition) instead")]] inline Coordinates:: + FieldMetric + D2DXDY(const Field2D& f, CELL_LOC outloc, DIFF_METHOD method, + REGION region = RGN_NOBNDRY, + const std::string& dfdy_boundary_condition = "free_o3", + const std::string& dfdy_region = "") { return D2DXDY(f, outloc, toString(method), toString(region), dfdy_boundary_condition, dfdy_region); }; @@ -724,7 +750,7 @@ D2DXDY(const Field2D& f, CELL_LOC outloc, DIFF_METHOD method, REGION region = RG /// If not given, defaults to RGN_NOBNDRY Field3D D2DXDZ(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(D2DXDZ, Field3D) +DERIV_FUNC_REGION_ENUM_TO_STRING(D2DXDZ, Field3D, Field3D) /// Calculate mixed partial derivative in x and z /// @@ -738,9 +764,10 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(D2DXDZ, Field3D) /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field2D D2DXDZ(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& - method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(D2DXDZ, Field2D) +Coordinates::FieldMetric D2DXDZ(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); +DERIV_FUNC_REGION_ENUM_TO_STRING(D2DXDZ, Field2D, Coordinates::FieldMetric) /// Calculate mixed partial derivative in y and z /// @@ -756,7 +783,7 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(D2DXDZ, Field2D) /// If not given, defaults to RGN_NOBNDRY Field3D D2DYDZ(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(D2DYDZ, Field3D) +DERIV_FUNC_REGION_ENUM_TO_STRING(D2DYDZ, Field3D, Field3D) /// Calculate mixed partial derivative in y and z /// @@ -770,9 +797,10 @@ DERIV_FUNC_REGION_ENUM_TO_STRING(D2DYDZ, Field3D) /// If not given, defaults to DIFF_DEFAULT /// @param[in] region What region is expected to be calculated /// If not given, defaults to RGN_NOBNDRY -Field2D D2DYDZ(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& - method = "DEFAULT", const std::string& region = "RGN_NOBNDRY"); -DERIV_FUNC_REGION_ENUM_TO_STRING(D2DYDZ, Field2D) +Coordinates::FieldMetric D2DYDZ(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT", + const std::string& region = "RGN_NOBNDRY"); +DERIV_FUNC_REGION_ENUM_TO_STRING(D2DYDZ, Field2D, Coordinates::FieldMetric) #undef DERIV_FUNC_REGION_ENUM_TO_STRING #undef VDERIV_FUNC_REGION_ENUM_TO_STRING diff --git a/include/difops.hxx b/include/difops.hxx index 437cb75136..5644793c09 100644 --- a/include/difops.hxx +++ b/include/difops.hxx @@ -53,28 +53,31 @@ * enabled) * @param[in] method The method to use. The default is set in the options. */ -const Field2D Grad_par(const Field2D& var, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT"); -DEPRECATED(const Field2D Grad_par(const Field2D& var, const std::string& method, - CELL_LOC outloc = CELL_DEFAULT)); -inline const Field2D Grad_par(const Field2D& var, CELL_LOC outloc, DIFF_METHOD method) { +Coordinates::FieldMetric Grad_par(const Field2D& var, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); +DEPRECATED(Coordinates::FieldMetric Grad_par(const Field2D& var, + const std::string& method, + CELL_LOC outloc = CELL_DEFAULT)); +inline Coordinates::FieldMetric Grad_par(const Field2D& var, CELL_LOC outloc, + DIFF_METHOD method) { return Grad_par(var, outloc, toString(method)); } -DEPRECATED(inline const Field2D Grad_par(const Field2D& var, DIFF_METHOD method, - CELL_LOC outloc)) { +DEPRECATED(inline Coordinates::FieldMetric Grad_par(const Field2D& var, + DIFF_METHOD method, + CELL_LOC outloc)) { return Grad_par(var, outloc, toString(method)); } -const Field3D Grad_par(const Field3D& var, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT"); -DEPRECATED(const Field3D Grad_par(const Field3D& var, const std::string& method, - CELL_LOC outloc = CELL_DEFAULT)); -inline const DEPRECATED(Field3D Grad_par(const Field3D& var, CELL_LOC outloc, - DIFF_METHOD method)) { +Field3D Grad_par(const Field3D& var, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); +DEPRECATED(Field3D Grad_par(const Field3D& var, const std::string& method, + CELL_LOC outloc = CELL_DEFAULT)); +inline DEPRECATED(Field3D Grad_par(const Field3D& var, CELL_LOC outloc, + DIFF_METHOD method)) { return Grad_par(var, outloc, toString(method)); }; -DEPRECATED(inline const - Field3D Grad_par(const Field3D& var, DIFF_METHOD method, CELL_LOC outloc)) { +DEPRECATED(inline Field3D Grad_par(const Field3D& var, DIFF_METHOD method, + CELL_LOC outloc)) { return Grad_par(var, outloc, toString(method)); } @@ -88,7 +91,7 @@ DEPRECATED(inline const * Combines the parallel and perpendicular calculation to include * grid-points at the corners. */ -const Field3D Grad_parP(const Field3D& apar, const Field3D& f); +Field3D Grad_parP(const Field3D& apar, const Field3D& f); /*! * vpar times parallel derivative along unperturbed B-field (upwinding) @@ -104,33 +107,32 @@ const Field3D Grad_parP(const Field3D& apar, const Field3D& f); * @param[in] method The numerical method to use. The default is set in the options * */ -const Field2D Vpar_Grad_par(const Field2D& v, const Field2D& f, - CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT"); -DEPRECATED(const Field2D Vpar_Grad_par(const Field2D& v, const Field2D& f, - const std::string& method, - CELL_LOC outloc = CELL_DEFAULT)); -inline const Field2D Vpar_Grad_par(const Field2D& v, const Field2D& f, CELL_LOC outloc, - DIFF_METHOD method) { +Coordinates::FieldMetric Vpar_Grad_par(const Field2D& v, const Field2D& f, + CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); +DEPRECATED(Coordinates::FieldMetric Vpar_Grad_par(const Field2D& v, const Field2D& f, + const std::string& method, + CELL_LOC outloc = CELL_DEFAULT)); +inline Coordinates::FieldMetric Vpar_Grad_par(const Field2D& v, const Field2D& f, + CELL_LOC outloc, DIFF_METHOD method) { return Vpar_Grad_par(v, f, outloc, toString(method)); } -DEPRECATED(inline const Field2D Vpar_Grad_par(const Field2D& v, const Field2D& f, - DIFF_METHOD method, CELL_LOC outloc)) { +DEPRECATED(inline Coordinates::FieldMetric Vpar_Grad_par( + const Field2D& v, const Field2D& f, DIFF_METHOD method, CELL_LOC outloc)) { return Vpar_Grad_par(v, f, outloc, toString(method)); } -const Field3D Vpar_Grad_par(const Field3D& v, const Field3D& f, - CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT"); -DEPRECATED(const Field3D Vpar_Grad_par(const Field3D& v, const Field3D& f, - const std::string& method, - CELL_LOC outloc = CELL_DEFAULT)); -inline const Field3D Vpar_Grad_par(const Field3D& v, const Field3D& f, CELL_LOC outloc, - DIFF_METHOD method) { +Field3D Vpar_Grad_par(const Field3D& v, const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); +DEPRECATED(Field3D Vpar_Grad_par(const Field3D& v, const Field3D& f, + const std::string& method, + CELL_LOC outloc = CELL_DEFAULT)); +inline Field3D Vpar_Grad_par(const Field3D& v, const Field3D& f, CELL_LOC outloc, + DIFF_METHOD method) { return Vpar_Grad_par(v, f, outloc, toString(method)); } -DEPRECATED(inline const Field3D Vpar_Grad_par(const Field3D& v, const Field3D& f, - DIFF_METHOD method, CELL_LOC outloc)) { +DEPRECATED(inline Field3D Vpar_Grad_par(const Field3D& v, const Field3D& f, + DIFF_METHOD method, CELL_LOC outloc)) { return Vpar_Grad_par(v, f, outloc, toString(method)); } @@ -146,27 +148,28 @@ DEPRECATED(inline const Field3D Vpar_Grad_par(const Field3D& v, const Field3D& f * @param[in] method The numerical method to use * */ -const Field2D Div_par(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT"); -DEPRECATED(const Field2D Div_par(const Field2D& f, const std::string& method, - CELL_LOC outloc = CELL_DEFAULT)); -inline const Field2D Div_par(const Field2D& f, CELL_LOC outloc, DIFF_METHOD method) { +Coordinates::FieldMetric Div_par(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); +DEPRECATED(Coordinates::FieldMetric Div_par(const Field2D& f, const std::string& method, + CELL_LOC outloc = CELL_DEFAULT)); +inline Coordinates::FieldMetric Div_par(const Field2D& f, CELL_LOC outloc, + DIFF_METHOD method) { return Div_par(f, outloc, toString(method)); } -DEPRECATED(inline const Field2D Div_par(const Field2D& f, DIFF_METHOD method, - CELL_LOC outloc)) { +DEPRECATED(inline Coordinates::FieldMetric Div_par(const Field2D& f, DIFF_METHOD method, + CELL_LOC outloc)) { return Div_par(f, outloc, toString(method)); } -const Field3D Div_par(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT"); -DEPRECATED(const Field3D Div_par(const Field3D& f, const std::string& method, - CELL_LOC outloc = CELL_DEFAULT)); -inline const Field3D Div_par(const Field3D& f, CELL_LOC outloc, DIFF_METHOD method) { +Field3D Div_par(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); +DEPRECATED(Field3D Div_par(const Field3D& f, const std::string& method, + CELL_LOC outloc = CELL_DEFAULT)); +inline Field3D Div_par(const Field3D& f, CELL_LOC outloc, DIFF_METHOD method) { return Div_par(f, outloc, toString(method)); } -DEPRECATED(inline const Field3D Div_par(const Field3D& f, DIFF_METHOD method, - CELL_LOC outloc)) { +DEPRECATED(inline Field3D Div_par(const Field3D& f, DIFF_METHOD method, + CELL_LOC outloc)) { return Div_par(f, outloc, toString(method)); } @@ -174,23 +177,22 @@ DEPRECATED(inline const Field3D Div_par(const Field3D& f, DIFF_METHOD method, // Both f and v are interpolated onto cell boundaries // using 2nd order central difference, then multiplied together // to get the flux at the boundary. -const Field3D Div_par(const Field3D& f, const Field3D& v); +Field3D Div_par(const Field3D& f, const Field3D& v); // Flux methods. Model divergence of flux: df/dt = Div(v * f) // TODO : Should we add Field2D versions? -const Field3D Div_par_flux(const Field3D& v, const Field3D& f, - CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT"); -DEPRECATED(const Field3D Div_par_flux(const Field3D& v, const Field3D& f, - const std::string& method, - CELL_LOC outloc = CELL_DEFAULT)); -inline const Field3D Div_par_flux(const Field3D& v, const Field3D& f, CELL_LOC outloc, - DIFF_METHOD method) { +Field3D Div_par_flux(const Field3D& v, const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); +DEPRECATED(Field3D Div_par_flux(const Field3D& v, const Field3D& f, + const std::string& method, + CELL_LOC outloc = CELL_DEFAULT)); +inline Field3D Div_par_flux(const Field3D& v, const Field3D& f, CELL_LOC outloc, + DIFF_METHOD method) { return Div_par_flux(v, f, outloc, toString(method)); } -DEPRECATED(inline const Field3D Div_par_flux(const Field3D& v, const Field3D& f, - DIFF_METHOD method, - CELL_LOC outloc = CELL_DEFAULT)) { +DEPRECATED(inline Field3D Div_par_flux(const Field3D& v, const Field3D& f, + DIFF_METHOD method, + CELL_LOC outloc = CELL_DEFAULT)) { return Div_par_flux(v, f, outloc, toString(method)); } @@ -205,15 +207,16 @@ DEPRECATED(inline const Field3D Div_par_flux(const Field3D& v, const Field3D& f, * @param[in] f The field to be differentiated * @param[in] outloc The cell location of the result */ -const Field2D Grad2_par2(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT"); -inline const Field2D Grad2_par2(const Field2D& f, CELL_LOC outloc, DIFF_METHOD method) { +Coordinates::FieldMetric Grad2_par2(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); +inline Coordinates::FieldMetric Grad2_par2(const Field2D& f, CELL_LOC outloc, + DIFF_METHOD method) { return Grad2_par2(f, outloc, toString(method)); } -const Field3D Grad2_par2(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT"); -inline const Field3D Grad2_par2(const Field3D& f, CELL_LOC outloc, DIFF_METHOD method) { +Field3D Grad2_par2(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); +inline Field3D Grad2_par2(const Field3D& f, CELL_LOC outloc, DIFF_METHOD method) { return Grad2_par2(f, outloc, toString(method)); } @@ -221,67 +224,66 @@ inline const Field3D Grad2_par2(const Field3D& f, CELL_LOC outloc, DIFF_METHOD m * Parallel derivatives, converting between cell-centred and lower cell boundary * These are a simple way to do staggered differencing */ -[[deprecated( - "Grad_par_CtoL is deprecated. Staggering is now supported in Grad_par.")]] -inline const Field3D Grad_par_CtoL(const Field3D &var) { +[[deprecated("Grad_par_CtoL is deprecated. Staggering is now supported in " + "Grad_par.")]] inline Field3D +Grad_par_CtoL(const Field3D& var) { ASSERT2(var.getLocation() == CELL_CENTRE); return Grad_par(var, CELL_YLOW); } -[[deprecated( - "Grad_par_CtoL is deprecated. Staggering is now supported in Grad_par.")]] -inline const Field2D Grad_par_CtoL(const Field2D &var) { +[[deprecated("Grad_par_CtoL is deprecated. Staggering is now supported in " + "Grad_par.")]] inline Coordinates::FieldMetric +Grad_par_CtoL(const Field2D& var) { ASSERT2(var.getLocation() == CELL_CENTRE); return Grad_par(var, CELL_YLOW); } -[[deprecated( - "Vpar_Grad_par_LCtoC is deprecated. Staggering is now supported in Vpar_Grad_par.")]] -inline const Field3D Vpar_Grad_par_LCtoC(const Field3D& v, const Field3D& f, - const std::string& region="RGN_NOBNDRY") { +[[deprecated("Vpar_Grad_par_LCtoC is deprecated. Staggering is now supported in " + "Vpar_Grad_par.")]] inline Field3D +Vpar_Grad_par_LCtoC(const Field3D& v, const Field3D& f, + const std::string& region = "RGN_NOBNDRY") { ASSERT2(v.getLocation() == CELL_YLOW); ASSERT2(f.getLocation() == CELL_CENTRE); return Vpar_Grad_par(v, f, CELL_CENTRE, region); } -[[deprecated( - "Vpar_Grad_par_LCtoC is deprecated. Staggering is now supported in Vpar_Grad_par.")]] -inline const Field3D Vpar_Grad_par_LCtoC(const Field3D& v, const Field3D& f, - REGION region=RGN_NOBNDRY) { +[[deprecated("Vpar_Grad_par_LCtoC is deprecated. Staggering is now supported in " + "Vpar_Grad_par.")]] inline Field3D +Vpar_Grad_par_LCtoC(const Field3D& v, const Field3D& f, REGION region = RGN_NOBNDRY) { ASSERT2(v.getLocation() == CELL_YLOW); ASSERT2(f.getLocation() == CELL_CENTRE); return Vpar_Grad_par(v, f, CELL_CENTRE, toString(region)); } -[[deprecated( - "Grad_par_LtoC is deprecated. Staggering is now supported in Grad_par.")]] -inline const Field3D Grad_par_LtoC(const Field3D &var) { +[[deprecated("Grad_par_LtoC is deprecated. Staggering is now supported in " + "Grad_par.")]] inline Field3D +Grad_par_LtoC(const Field3D& var) { ASSERT2(var.getLocation() == CELL_YLOW); return Grad_par(var, CELL_CENTRE); } -[[deprecated( - "Grad_par_LtoC is deprecated. Staggering is now supported in Grad_par.")]] -inline const Field2D Grad_par_LtoC(const Field2D &var) { +[[deprecated("Grad_par_LtoC is deprecated. Staggering is now supported in " + "Grad_par.")]] inline Coordinates::FieldMetric +Grad_par_LtoC(const Field2D& var) { ASSERT2(var.getLocation() == CELL_YLOW); return Grad_par(var, CELL_CENTRE); } -[[deprecated( - "Div_par_LtoC is deprecated. Staggering is now supported in Grad_par.")]] -inline const Field3D Div_par_LtoC(const Field3D &var) { +[[deprecated("Div_par_LtoC is deprecated. Staggering is now supported in " + "Grad_par.")]] inline Field3D +Div_par_LtoC(const Field3D& var) { ASSERT2(var.getLocation() == CELL_YLOW); return Div_par(var, CELL_CENTRE); } -[[deprecated( - "Div_par_LtoC is deprecated. Staggering is now supported in Grad_par.")]] -inline const Field2D Div_par_LtoC(const Field2D &var) { +[[deprecated("Div_par_LtoC is deprecated. Staggering is now supported in " + "Grad_par.")]] inline Coordinates::FieldMetric +Div_par_LtoC(const Field2D& var) { ASSERT2(var.getLocation() == CELL_YLOW); return Div_par(var, CELL_CENTRE); } -[[deprecated( - "Div_par_CtoL is deprecated. Staggering is now supported in Grad_par.")]] -inline const Field3D Div_par_CtoL(const Field3D &var) { +[[deprecated("Div_par_CtoL is deprecated. Staggering is now supported in " + "Grad_par.")]] inline Field3D +Div_par_CtoL(const Field3D& var) { ASSERT2(var.getLocation() == CELL_CENTRE); return Div_par(var, CELL_YLOW); } -[[deprecated( - "Div_par_CtoL is deprecated. Staggering is now supported in Grad_par.")]] -inline const Field2D Div_par_CtoL(const Field2D &var) { +[[deprecated("Div_par_CtoL is deprecated. Staggering is now supported in " + "Grad_par.")]] inline Coordinates::FieldMetric +Div_par_CtoL(const Field2D& var) { ASSERT2(var.getLocation() == CELL_CENTRE); return Div_par(var, CELL_YLOW); } @@ -296,12 +298,17 @@ inline const Field2D Div_par_CtoL(const Field2D &var) { * @param[in] kY The diffusion coefficient * @param[in] f The field whose gradient drives a flux */ -const Field2D Div_par_K_Grad_par(BoutReal kY, const Field2D &f, CELL_LOC outloc=CELL_DEFAULT); -const Field3D Div_par_K_Grad_par(BoutReal kY, const Field3D &f, CELL_LOC outloc=CELL_DEFAULT); -const Field2D Div_par_K_Grad_par(const Field2D &kY, const Field2D &f, CELL_LOC outloc=CELL_DEFAULT); -const Field3D Div_par_K_Grad_par(const Field2D &kY, const Field3D &f, CELL_LOC outloc=CELL_DEFAULT); -const Field3D Div_par_K_Grad_par(const Field3D &kY, const Field2D &f, CELL_LOC outloc=CELL_DEFAULT); -const Field3D Div_par_K_Grad_par(const Field3D &kY, const Field3D &f, CELL_LOC outloc=CELL_DEFAULT); +Coordinates::FieldMetric Div_par_K_Grad_par(BoutReal kY, const Field2D& f, + CELL_LOC outloc = CELL_DEFAULT); +Field3D Div_par_K_Grad_par(BoutReal kY, const Field3D& f, CELL_LOC outloc = CELL_DEFAULT); +Coordinates::FieldMetric Div_par_K_Grad_par(const Field2D& kY, const Field2D& f, + CELL_LOC outloc = CELL_DEFAULT); +Field3D Div_par_K_Grad_par(const Field2D& kY, const Field3D& f, + CELL_LOC outloc = CELL_DEFAULT); +Field3D Div_par_K_Grad_par(const Field3D& kY, const Field2D& f, + CELL_LOC outloc = CELL_DEFAULT); +Field3D Div_par_K_Grad_par(const Field3D& kY, const Field3D& f, + CELL_LOC outloc = CELL_DEFAULT); /*! * Perpendicular Laplacian operator @@ -312,50 +319,52 @@ const Field3D Div_par_K_Grad_par(const Field3D &kY, const Field3D &f, CELL_LOC o * * For the full perpendicular Laplacian, use Laplace_perp */ -const Field2D Delp2(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, bool useFFT = true); -const Field3D Delp2(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, bool useFFT = true); -const FieldPerp Delp2(const FieldPerp& f, CELL_LOC outloc = CELL_DEFAULT, - bool useFFT = true); +Coordinates::FieldMetric Delp2(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + bool useFFT = true); +Field3D Delp2(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, bool useFFT = true); +FieldPerp Delp2(const FieldPerp& f, CELL_LOC outloc = CELL_DEFAULT, bool useFFT = true); /*! * Perpendicular Laplacian, keeping y derivatives * * */ -const Field2D Laplace_perp(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& dfdy_boundary_condition = "free_o3", - const std::string& dfdy_region = ""); -const Field3D Laplace_perp(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& dfdy_boundary_condition = "free_o3", - const std::string& dfdy_region = ""); +Coordinates::FieldMetric +Laplace_perp(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& dfdy_boundary_condition = "free_o3", + const std::string& dfdy_region = ""); +Field3D Laplace_perp(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& dfdy_boundary_condition = "free_o3", + const std::string& dfdy_region = ""); /*! * Parallel Laplacian operator * */ -const Field2D Laplace_par(const Field2D &f, CELL_LOC outloc=CELL_DEFAULT); -const Field3D Laplace_par(const Field3D &f, CELL_LOC outloc=CELL_DEFAULT); +Coordinates::FieldMetric Laplace_par(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT); +Field3D Laplace_par(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT); /*! * Full Laplacian operator (par + perp) */ -const Field2D Laplace(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& dfdy_boundary_condition = "free_o3", - const std::string& dfdy_region = ""); -const Field3D Laplace(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& dfdy_boundary_condition = "free_o3", - const std::string& dfdy_region = ""); +Coordinates::FieldMetric Laplace(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& dfdy_boundary_condition = "free_o3", + const std::string& dfdy_region = ""); +Field3D Laplace(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& dfdy_boundary_condition = "free_o3", + const std::string& dfdy_region = ""); /*! * Inverse of Laplacian operator in LaplaceXY solver */ -const Field2D Laplace_perpXY(const Field2D& A, const Field2D& f); +Field2D Laplace_perpXY(const Field2D& A, const Field2D& f); /*! * Terms of form b0 x Grad(phi) dot Grad(A) * */ -const Field2D b0xGrad_dot_Grad(const Field2D &phi, const Field2D &A, CELL_LOC outloc=CELL_DEFAULT); +Coordinates::FieldMetric b0xGrad_dot_Grad(const Field2D& phi, const Field2D& A, + CELL_LOC outloc = CELL_DEFAULT); /*! * Terms of form @@ -368,9 +377,12 @@ const Field2D b0xGrad_dot_Grad(const Field2D &phi, const Field2D &A, CELL_LOC ou * @param[in] A The field being advected * @param[in] outloc The cell location where the result is defined. By default the same as A. */ -const Field3D b0xGrad_dot_Grad(const Field3D &phi, const Field2D &A, CELL_LOC outloc=CELL_DEFAULT); -const Field3D b0xGrad_dot_Grad(const Field2D &phi, const Field3D &A, CELL_LOC outloc=CELL_DEFAULT); -const Field3D b0xGrad_dot_Grad(const Field3D &phi, const Field3D &A, CELL_LOC outloc=CELL_DEFAULT); +Field3D b0xGrad_dot_Grad(const Field3D& phi, const Field2D& A, + CELL_LOC outloc = CELL_DEFAULT); +Field3D b0xGrad_dot_Grad(const Field2D& phi, const Field3D& A, + CELL_LOC outloc = CELL_DEFAULT); +Field3D b0xGrad_dot_Grad(const Field3D& phi, const Field3D& A, + CELL_LOC outloc = CELL_DEFAULT); /*! * Poisson bracket methods @@ -404,17 +416,15 @@ constexpr BRACKET_METHOD BRACKET_ARAKAWA_OLD = BRACKET_METHOD::arakawa_old; * @param[in] solver Pointer to the time integration solver * */ -const Field2D bracket(const Field2D &f, const Field2D &g, - BRACKET_METHOD method = BRACKET_STD, CELL_LOC outloc = CELL_DEFAULT, - Solver *solver = nullptr); -const Field3D bracket(const Field2D &f, const Field3D &g, - BRACKET_METHOD method = BRACKET_STD, CELL_LOC outloc = CELL_DEFAULT, - Solver *solver = nullptr); -const Field3D bracket(const Field3D &f, const Field2D &g, - BRACKET_METHOD method = BRACKET_STD, CELL_LOC outloc = CELL_DEFAULT, - Solver *solver = nullptr); -const Field3D bracket(const Field3D &f, const Field3D &g, - BRACKET_METHOD method = BRACKET_STD, CELL_LOC outloc = CELL_DEFAULT, - Solver *solver = nullptr); +Coordinates::FieldMetric bracket(const Field2D& f, const Field2D& g, + BRACKET_METHOD method = BRACKET_STD, + CELL_LOC outloc = CELL_DEFAULT, + Solver* solver = nullptr); +Field3D bracket(const Field2D& f, const Field3D& g, BRACKET_METHOD method = BRACKET_STD, + CELL_LOC outloc = CELL_DEFAULT, Solver* solver = nullptr); +Field3D bracket(const Field3D& f, const Field2D& g, BRACKET_METHOD method = BRACKET_STD, + CELL_LOC outloc = CELL_DEFAULT, Solver* solver = nullptr); +Field3D bracket(const Field3D& f, const Field3D& g, BRACKET_METHOD method = BRACKET_STD, + CELL_LOC outloc = CELL_DEFAULT, Solver* solver = nullptr); #endif /* __DIFOPS_H__ */ diff --git a/include/field.hxx b/include/field.hxx index 76abd899ac..9ddce61113 100644 --- a/include/field.hxx +++ b/include/field.hxx @@ -273,7 +273,9 @@ inline void checkPositive(const T& f, const std::string& name="field", const std BOUT_FOR_SERIAL(i, f.getRegion(rgn)) { if (f[i] <= 0.) { - throw BoutException("{:s} is not positive at {:s}", name, toString(i)); + throw BoutException("{:s} ({:s} {:s}) is {:e} (not positive) at {:s}", name, + toString(f.getLocation()), toString(f.getDirections()), f[i], + toString(i)); } } } @@ -350,6 +352,52 @@ inline BoutReal min(const T& f, bool allpe, REGION rgn) { return min(f, allpe, toString(rgn)); } +/// Returns true if all elements of \p f over \p region are equal. By +/// default only checks the local processor, use \p allpe to check +/// globally +/// +/// @param[in] f The field to check +/// @param[in] allpe Check over all processors +/// @param[in] region The region to check for uniformity over +template > +inline bool isUniform(const T& f, bool allpe = false, + const std::string& region = "RGN_ALL") { + bool result = true; + auto element = f[*f.getRegion(region).begin()]; + // TODO: maybe parallise this loop, as the early return is unlikely + BOUT_FOR_SERIAL(i, f.getRegion(region)) { + if (f[i] != element) { + result = false; + break; + } + } + if (allpe) { + bool localresult = result; + MPI_Allreduce(&localresult, &result, 1, MPI_C_BOOL, MPI_LOR, BoutComm::get()); + } + return result; +} + +/// Returns the value of the first element of \p f (in the region \p +/// region if given). If checks are enabled, then throws an exception +/// if \p f is not uniform over \p region. By default only checks the +/// local processor, use \p allpe to check globally +/// +/// @param[in] f The field to check +/// @param[in] allpe Check over all processors +/// @param[in] region The region to assume is uniform +template > +inline BoutReal getUniform(const T& f, bool allpe = false, + const std::string& region = "RGN_ALL") { +#if CHECK > 1 + if (not isUniform(f, allpe, region)) { + throw BoutException("Requested getUniform({}, {}, {}) but Field is not const", f.name, + allpe, region); + } +#endif + return f[*f.getRegion(region).begin()]; +} + /// Maximum of \p r, excluding the boundary/guard cells by default /// (can be changed with \p rgn argument). /// diff --git a/include/field2d.hxx b/include/field2d.hxx index dcf89be672..73868bd413 100644 --- a/include/field2d.hxx +++ b/include/field2d.hxx @@ -312,24 +312,6 @@ Field2D operator-(const Field2D &f); // Non-member functions -inline Field2D toFieldAligned(const Field2D& f, const std::string& UNUSED(region) = "RGN_ALL") { - return f; -} -[[deprecated("Please use toFieldAligned(const Field2D& f, " - "const std::string& region = \"RGN_ALL\") instead")]] -inline Field2D toFieldAligned(const Field2D& f, REGION region) { - return toFieldAligned(f, toString(region)); -} - -inline Field2D fromFieldAligned(const Field2D& f, const std::string& UNUSED(region) = "RGN_ALL") { - return f; -} -[[deprecated("Please use fromFieldAligned(const Field2D& f, " - "const std::string& region = \"RGN_ALL\") instead")]] -inline Field2D fromFieldAligned(const Field2D& f, REGION region) { - return fromFieldAligned(f, toString(region)); -} - #if CHECK > 0 /// Throw an exception if \p f is not allocated or if any /// elements are non-finite (for CHECK > 2). @@ -355,6 +337,11 @@ void invalidateGuards(Field2D &var); inline void invalidateGuards(Field2D &UNUSED(var)) {} #endif +/// Average in the Z direction +/// Field2D has no Z direction -- return input +/// @param[in] f Variable to average +inline Field2D DC(const Field2D& f) { return f; } + /// Returns a reference to the time-derivative of a field \p f /// /// Wrapper around member function f.timeDeriv() diff --git a/include/field3d.hxx b/include/field3d.hxx index 82d48f20c0..3e88985a81 100644 --- a/include/field3d.hxx +++ b/include/field3d.hxx @@ -463,6 +463,7 @@ class Field3D : public Field { #endif friend class Vector3D; + friend class Vector2D; Field3D& calcParallelSlices(); diff --git a/include/interpolation.hxx b/include/interpolation.hxx index 60b38dd864..0486672dac 100644 --- a/include/interpolation.hxx +++ b/include/interpolation.hxx @@ -119,7 +119,10 @@ const T interp_to(const T& var, CELL_LOC loc, const std::string region = "RGN_AL ASSERT0(fieldmesh->ystart >= 2); // We can't interpolate in y unless we're field-aligned - const bool is_unaligned = (var.getDirectionY() == YDirectionType::Standard); + // Field2D doesn't need to shift to/from field-aligned because it is axisymmetric, + // so always set is_unaligned=false for Field2D. + const bool is_unaligned = std::is_same::value ? false + : (var.getDirectionY() == YDirectionType::Standard); const T var_fa = is_unaligned ? toFieldAligned(var, "RGN_NOX") : var; if (not std::is_base_of::value) { diff --git a/include/invert_parderiv.hxx b/include/invert_parderiv.hxx index 2d438fd204..a4e2ad756f 100644 --- a/include/invert_parderiv.hxx +++ b/include/invert_parderiv.hxx @@ -69,6 +69,8 @@ public: } }; +using RegisterUnavailableInvertPar = RegisterUnavailableInFactory; + /// Base class for parallel inversion solvers /*! * diff --git a/include/msg_stack.hxx b/include/msg_stack.hxx index 0295877516..0b309c9585 100644 --- a/include/msg_stack.hxx +++ b/include/msg_stack.hxx @@ -223,6 +223,6 @@ private: * * } // Scope ends, message popped */ -#define AUTO_TRACE() TRACE(__thefunc__) +#define AUTO_TRACE() TRACE(__thefunc__) // NOLINT #endif // __MSG_STACK_H__ diff --git a/include/vecops.hxx b/include/vecops.hxx index 52c8d7b584..2ce88fdb66 100644 --- a/include/vecops.hxx +++ b/include/vecops.hxx @@ -29,13 +29,21 @@ #ifndef __VECOPS_H__ #define __VECOPS_H__ +class Field2D; +class Field3D; +class Vector2D; +class Vector3D; + +#include "bout_types.hxx" +#include "bout/coordinates.hxx" +#include "bout/deprecated.hxx" +// Those are needed because we implement functions here. +// They can be dropped if we remove the deprecated wrappers. #include "field2d.hxx" #include "field3d.hxx" #include "vector2d.hxx" #include "vector3d.hxx" -#include "bout/deprecated.hxx" - /// Gradient of scalar field \p f, returning a covariant vector /// /// All locations supported @@ -44,10 +52,10 @@ /// @param[in] outloc The location where the result is desired /// By default this is the same location as the input \p f /// @param[in] method The method to use. The default is set in the options. -const Vector2D Grad(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT"); -const Vector3D Grad(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT"); +Vector2D Grad(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); +Vector3D Grad(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); /// Perpendicular gradient of scalar field \p f /// @@ -61,13 +69,13 @@ const Vector3D Grad(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, /// @param[in] outloc The cell location where the result is desired /// @param[in] method The method to use. The default is set in the options. /// -const Vector3D Grad_perp(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT"); -inline const Vector3D Grad_perp(const Field3D& f, CELL_LOC outloc, DIFF_METHOD method) { +Vector3D Grad_perp(const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); +inline Vector3D Grad_perp(const Field3D& f, CELL_LOC outloc, DIFF_METHOD method) { return Grad_perp(f, outloc, toString(method)); } -const Vector2D Grad_perp(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT"); +Vector2D Grad_perp(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); /// Divergence of a vector \p v, returning a scalar /// @@ -78,26 +86,28 @@ const Vector2D Grad_perp(const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, /// @param[in] outloc The cell location where the result is desired /// @param[in] method The method to use. The default is set in the options. /// -const Field2D Div(const Vector2D& v, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT"); -const Field3D Div(const Vector3D& v, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT"); +Coordinates::FieldMetric Div(const Vector2D& v, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); +Field3D Div(const Vector3D& v, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); + +Coordinates::FieldMetric Div(const Vector2D& v, const Field2D& f, + CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); -const Field2D Div(const Vector2D& v, const Field2D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT"); -const Field3D Div(const Vector3D& v, const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, - const std::string& method = "DEFAULT"); +Field3D Div(const Vector3D& v, const Field3D& f, CELL_LOC outloc = CELL_DEFAULT, + const std::string& method = "DEFAULT"); DEPRECATED(inline const Field3D Div(const Vector3D& v, const Field3D& f, const std::string& method, CELL_LOC outloc = CELL_DEFAULT)) { return Div(v, f, outloc, method); } -inline const Field3D Div(const Vector3D& v, const Field3D& f, CELL_LOC outloc, - DIFF_METHOD method = DIFF_DEFAULT) { +inline Field3D Div(const Vector3D& v, const Field3D& f, CELL_LOC outloc, + DIFF_METHOD method = DIFF_DEFAULT) { return Div(v, f, outloc, toString(method)); } -DEPRECATED(inline const Field3D Div(const Vector3D& v, const Field3D& f, - DIFF_METHOD method, CELL_LOC outloc = CELL_DEFAULT)) { +DEPRECATED(inline Field3D Div(const Vector3D& v, const Field3D& f, DIFF_METHOD method, + CELL_LOC outloc = CELL_DEFAULT)) { return Div(v, f, outloc, toString(method)); } @@ -113,8 +123,8 @@ DEPRECATED(inline const Field3D Div(const Vector3D& v, const Field3D& f, /// /// @param[in] v The vector to differentiate /// -const Vector2D Curl(const Vector2D &v); -const Vector3D Curl(const Vector3D &v); +Vector2D Curl(const Vector2D& v); +Vector3D Curl(const Vector3D& v); // Upwinding routines @@ -122,17 +132,17 @@ const Vector3D Curl(const Vector3D &v); /// /// The vector and the field must be at the same location, which /// cannot be CELL_VSHIFT -const Field2D V_dot_Grad(const Vector2D &v, const Field2D &f); -const Field3D V_dot_Grad(const Vector2D &v, const Field3D &f); -const Field3D V_dot_Grad(const Vector3D &v, const Field2D &f); -const Field3D V_dot_Grad(const Vector3D &v, const Field3D &f); +Coordinates::FieldMetric V_dot_Grad(const Vector2D& v, const Field2D& f); +Field3D V_dot_Grad(const Vector2D& v, const Field3D& f); +Field3D V_dot_Grad(const Vector3D& v, const Field2D& f); +Field3D V_dot_Grad(const Vector3D& v, const Field3D& f); /// Advection of a vector field \p a by a velocity vector \p v /// /// Both vectors must be at the same location, which cannot be CELL_VSHIFT -const Vector2D V_dot_Grad(const Vector2D &v, const Vector2D &a); -const Vector3D V_dot_Grad(const Vector2D &v, const Vector3D &a); -const Vector3D V_dot_Grad(const Vector3D &v, const Vector2D &a); -const Vector3D V_dot_Grad(const Vector3D &v, const Vector3D &a); +Vector2D V_dot_Grad(const Vector2D& v, const Vector2D& a); +Vector3D V_dot_Grad(const Vector2D& v, const Vector3D& a); +Vector3D V_dot_Grad(const Vector3D& v, const Vector2D& a); +Vector3D V_dot_Grad(const Vector3D& v, const Vector3D& a); #endif // __VECOPS_H__ diff --git a/include/vector2d.hxx b/include/vector2d.hxx index 73373ddd90..3a16396cb9 100644 --- a/include/vector2d.hxx +++ b/include/vector2d.hxx @@ -37,10 +37,12 @@ class Vector2D; #ifndef __VECTOR2D_H__ #define __VECTOR2D_H__ -#include "field2d.hxx" -class Field3D; //#include "field3d.hxx" +class Field2D; +class Field3D; class Vector3D; //#include "vector3d.hxx" +#include + /*! * A vector with three components (x,y,z) which only vary in 2D * (x and y). Implemented as a collection of three Field2D objects. @@ -55,7 +57,7 @@ public: ~Vector2D() override; - Field2D x, y, z; ///< components + Coordinates::FieldMetric x, y, z; ///< components bool covariant{true}; ///< true if the components are covariant (default) @@ -126,7 +128,7 @@ public: const Vector2D operator/(const Field2D &rhs) const; ///< Divides all components by \p rhs const Vector3D operator/(const Field3D &rhs) const; ///< Divides all components by \p rhs - const Field2D operator*(const Vector2D &rhs) const; ///< Dot product + const Coordinates::FieldMetric operator*(const Vector2D& rhs) const; ///< Dot product const Field3D operator*(const Vector3D &rhs) const; ///< Dot product /// Set component locations consistently @@ -170,10 +172,13 @@ const Vector3D cross(const Vector2D & lhs, const Vector3D &rhs); * * |v| = sqrt( v dot v ) */ -const Field2D abs(const Vector2D& v, const std::string& region = "RGN_ALL"); -[[deprecated("Please use Vector2D abs(const Vector2D& f, " - "const std::string& region = \"RGN_ALL\") instead")]] -inline const Field2D abs(const Vector2D &v, REGION region) { +const Coordinates::FieldMetric abs(const Vector2D& v, + const std::string& region = "RGN_ALL"); +[[deprecated( + "Please use Vector2D abs(const Vector2D& f, " + "const std::string& region = \"RGN_ALL\") instead")]] inline const Coordinates:: + FieldMetric + abs(const Vector2D& v, REGION region) { return abs(v, toString(region)); } diff --git a/makefile b/makefile index 6dea1287b8..10949da529 100644 --- a/makefile +++ b/makefile @@ -1,4 +1,4 @@ -BOUT_TOP = . +BOUT_TOP = $(PWD) DIRS = src diff --git a/manual/sphinx/developer_docs/mesh.rst b/manual/sphinx/developer_docs/mesh.rst index 396e49229b..d21fd729a3 100644 --- a/manual/sphinx/developer_docs/mesh.rst +++ b/manual/sphinx/developer_docs/mesh.rst @@ -261,13 +261,16 @@ index. Differencing ------------ -The mesh spacing is given by the public members `Mesh::dx`, `Mesh::dy` -and `Mesh::dx`:: +The mesh spacing is given by the public members `Coordinates::dx`, +`Coordinates::dy` and `Coordinates::dz`:: // These used for differential operators - Field2D dx, dy; - Field2D d2x, d2y; // 2nd-order correction for non-uniform meshes - BoutReal zlength, dz; // Derived from options (in radians) + FieldMetric dx, dy, dz; + FieldMetric d2x, d2y; // 2nd-order correction for non-uniform meshes + Field2D zlength(); // Computed from dz + +`Coordinates::FieldMetric` can be either `Field2D` or if BOUT++ has +been configured with `--enable-metric-3d` then a `Field3D`. Metrics ------- @@ -277,66 +280,37 @@ details are handled by `Coordinates`. The contravariant and covariant metric tensor components are public members of `Coordinates`:: // Contravariant metric tensor (g^{ij}) - Field2D g11, g22, g33, g12, g13, g23; // These are read in grid.cxx + FieldMetric g11, g22, g33, g12, g13, g23; // These are read in grid.cxx // Covariant metric tensor - Field2D g_11, g_22, g_33, g_12, g_13, g_23; + FieldMetric g_11, g_22, g_33, g_12, g_13, g_23; int calcCovariant(); // Invert contravatiant metric to get covariant int calcContravariant(); // Invert covariant metric to get contravariant If only one of these sets is modified by an external code, then -`Coordinates::calcCovariant` and `Coordinates::calcContravariant` can -be used to calculate the other (uses Gauss-Jordan currently). +`Coordinates::calcCovariant()` and `Coordinates::calcContravariant()` +can be used to calculate the other (uses Gauss-Jordan currently). From the metric tensor components, `Coordinates` calculates several other useful quantities:: int jacobian(); // Calculate J and Bxy - Field2D J; // Jacobian - Field2D Bxy; // Magnitude of B = nabla z times nabla x + FieldMetric J; // Jacobian + FieldMetric Bxy; // Magnitude of B = nabla z times nabla x /// Calculate differential geometry quantities from the metric tensor int geometry(); // Christoffel symbol of the second kind (connection coefficients) - Field2D G1_11, G1_22, G1_33, G1_12, G1_13; - Field2D G2_11, G2_22, G2_33, G2_12, G2_23; - Field2D G3_11, G3_22, G3_33, G3_13, G3_23; + FieldMetric G1_11, G1_22, G1_33, G1_12, G1_13; + FieldMetric G2_11, G2_22, G2_33, G2_12, G2_23; + FieldMetric G3_11, G3_22, G3_33, G3_13, G3_23; - Field2D G1, G2, G3; + FieldMetric G1, G2, G3; These quantities are public and accessible everywhere, but this is because they are needed in a lot of the code. They shouldn’t change -after initialisation, unless the physics model starts doing fancy things -with deforming meshes. - -Miscellaneous -------------- - -There are some public members of `Mesh` which are there for some -specific task and don’t really go anywhere else (yet). - -To perform radial derivatives in tokamak geometry, interpolation is -needed in the Z direction. This is done by shifting in Z by a phase -factor, performing the derivatives, then shifting back. The following -public variables are currently used for this:: - - bool ShiftXderivs; // Use shifted X derivatives - int ShiftOrder; // Order of shifted X derivative interpolation - Field2D zShift; // Z shift for each point (radians) - - Field2D ShiftTorsion; // d / dx. Needed for vector differentials (Curl) - Field2D IntShiftTorsion; // Integrated shear (I in BOUT notation) - bool IncIntShear; // Include integrated shear (if shifting X) - - int TwistOrder; // Order of twist-shift interpolation - -This determines what order method to use for the interpolation at the -twist-shift location, with ``0`` meaning FFT during communication. Since -this must be 0 at the moment it’s fairly redundant and should be -removed. - -A (currently experimental) feature is:: - - bool StaggerGrids; ///< Enable staggered grids (Centre, Lower). Otherwise all vars are cell centred (default). +after initialisation, unless the physics model starts doing fancy +things with deforming meshes. In that case it is up to the user to +ensure they are updated. diff --git a/manual/sphinx/index.rst b/manual/sphinx/index.rst index 2460b30025..79402cef63 100644 --- a/manual/sphinx/index.rst +++ b/manual/sphinx/index.rst @@ -28,6 +28,7 @@ The documentation is divided into the following sections: user_docs/installing user_docs/advanced_install user_docs/running_bout + user_docs/new_in_v5 .. toctree:: :maxdepth: 2 diff --git a/manual/sphinx/user_docs/input_grids.rst b/manual/sphinx/user_docs/input_grids.rst index e4b63696f9..c1e3347eec 100644 --- a/manual/sphinx/user_docs/input_grids.rst +++ b/manual/sphinx/user_docs/input_grids.rst @@ -20,11 +20,12 @@ a simple mesh can be created using options. dx = 0.1 # X mesh spacing dy = 0.1 # Y mesh spacing -The above options will create a :math:`260\times 256` mesh in X and Y -(MZ option sets Z resolution), with mesh spacing of :math:`0.1` in both -directions. By default the coordinate system is Cartesian (metric tensor -is the identity matrix), but this can be changed by specifying the -metric tensor components. +The above options will create a :math:`256\times 256` mesh in X and Y, +assuming there are 2 guard cells in X direction. The Z resolution can +be specified with MZ. The mesh spacing is :math:`0.1` in both +directions. By default the coordinate system is Cartesian (metric +tensor is the identity matrix), but this can be changed by specifying +the metric tensor components. Integer quantities such as ``nx`` can be numbers (like “260”), or expressions (like “256 + 2\*MXG”). @@ -40,8 +41,9 @@ boundary but ``z`` does not (since it is usually periodic): mxg = 2 -Note that the variable ``nz`` can be used before its definition; all -variables are first read, and then processed afterwards. +Note that the order of the defintion within a section isn't important, +variables can be used before they are defined. All variables are first +read, and only processed if they are used. Expressions are always calculated in floating point; When expressions are used to set integer quantities (such as the number of grid @@ -95,8 +97,8 @@ variables are defined doesn’t matter, so ``L`` could be defined below section :ref:`sec-recursive-functions`). If the variables are defined in the same section (as ``dy`` and ``L``) or a parent section, then no section prefix is required. To refer to a variable in a different -section, prefix the variable with the section name -e.g. “``section:variable``”. +section, prefix the variable with the section name, for example, +``section:variable`` or ``mesh:dx``. More complex meshes can be created by supplying an input grid file to describe the grid points, geometry, and starting profiles. Currently @@ -106,20 +108,27 @@ found, a warning will be printed and the default values used. - X and Y grid sizes (integers) ``nx`` and ``ny`` **REQUIRED** -- Differencing quantities in 2D arrays ``dx[nx][ny]`` and - ``dy[nx][ny]``. If these are not found they will be set to 1. +- Differencing quantities in 2D/3D arrays ``dx(nx,ny[,nz])``, + ``dy(nx,ny[,nz])`` and ``dz(nx,ny[,nz])``. If these are not found + they will be set to 1. To allow variation in ``z`` direction, BOUT++ + has to be configured ``--enable-metric-3d``, otherwise 2D fields are + used for the metric fields. Note that prior to BOUT++ version 5 + ``dz`` was a constant. -- Diagonal terms of the metric tensor :math:`g^{ij}` ``g11[nx][ny]``, - ``g22[nx][ny]``, and ``g33[nx][ny]``. If not found, these will be set +- Diagonal terms of the metric tensor :math:`g^{ij}` ``g11(nx,ny[,nz])``, + ``g22(nx,ny[,nz])``, and ``g33(nx,ny[,nz])``. If not found, these will be set to 1. -- Off-diagonal metric tensor :math:`g^{ij}` elements ``g12[nx][ny]``, - ``g13[nx][ny]``, and ``g23[nx][ny]``. If not found, these will be set +- Off-diagonal metric tensor :math:`g^{ij}` elements ``g12(nx,ny[,nz])``, + ``g13(nx,ny[,nz])``, and ``g23(nx,ny[,nz])``. If not found, these will be set to 0. -- Z shift for interpolation between the base and field-aligned grids, see - :ref:`sec-parallel-transforms`. The shifts must be provided in the gridfile - in a field ``zShift(nx, ny)``. If not found, ``zShift`` is set to zero. +- Z shift for interpolation between field-aligned coordinates and + non-aligned coordinates (see :ref:`sec-field-aligned-coordinates`). Parallel + differential operators are calculated using a shift to field-aligned + values when ``paralleltransform:type = shifted`` (or ``shiftedinterp``). + The shifts must be provided in the gridfile in a field ``zShift(nx,ny)``. + If not found, ``zShift`` is set to zero. The remaining quantities determine the topology of the grid. These are based on tokamak single/double-null configurations, but can be adapted @@ -150,9 +159,8 @@ This section describes how to generate inputs for tokamak equilibria. If you’re not interested in tokamaks then you can skip to the next section. The directory ``tokamak_grids`` contains code to generate input grid -files for tokamaks. These can be used by the ``2fluid`` and -``highbeta_reduced`` modules, and are (mostly) compatible with inputs to -the BOUT-06 code. +files for tokamaks. These can be used by, for example, the ``2fluid`` and +``highbeta_reduced`` modules. .. _sec-bout-topology: @@ -428,7 +436,7 @@ the magnetic field is followed along the Y coordinate from each 2D grid to where it either intersects the forward and backward grid, or hits a boundary. -The simplest code which creates an output file is:: +A simple code which creates an output file is:: import zoidberg @@ -438,8 +446,8 @@ The simplest code which creates an output file is:: grid = zoidberg.grid.rectangular_grid(10,10,10) # Follow magnetic fields from each point maps = zoidberg.make_maps(grid, field) - # Write everything to file - zoidberg.write_maps(grid, field, maps, gridfile="grid.fci.nc") + # Write everything to file - with default option for gridfile and metric2d + zoidberg.write_maps(grid, field, maps, gridfile="grid.fci.nc", metric2d=True) As in the above code, creating an output file consists of the following steps: diff --git a/manual/sphinx/user_docs/new_in_v5.rst b/manual/sphinx/user_docs/new_in_v5.rst new file mode 100644 index 0000000000..8a1f5b0128 --- /dev/null +++ b/manual/sphinx/user_docs/new_in_v5.rst @@ -0,0 +1,117 @@ +.. _sec-newv5: + +============================= + New Features in BOUT++ v5.0 +============================= + +BOUT++ v5.0 is a new major release, adding tons of new features, improving +existing code, as well as removing some old and deprecated things. There are +some breaking changes which will require modifications to your physics model, +but for the vast majority we have provided tooling to automate this as much as +possible. + + +3D Metrics +========== + +Up until now, BOUT++ has been limited to varying the metric components only in +the XY plane. This release now introduces 3D metrics as a compile-time option, +allow simulations of devices such as stellarators. + +To enable 3D metrics, build BOUT++ like: + +.. code-block:: console + + ./configure --enable-metric-3D + +or, with CMake: + +.. code-block:: console + + cmake . -B build -DBOUT_ENABLE_METRIC_3D=ON + +Changes +------- + +Types +~~~~~ + +Adding 3D metrics to BOUT++ has been a substantial effort, requiring many +changes to a significant amount of the source code. The main change is that the +metric components, ``g11``, ``g22``, and so on, as well as the grid spacing, +``dx``, ``dy``, ``dz``, have changed from `Field2D` to +`Coordinates::FieldMetric`: a type alias for either `Field3D` or `Field2D` +depending on if BOUT++ was built with or without 3D metrics respectively. + +.. note:: + `Coordinates::dz` has also changed to be `Field2D` even without 3D + metrics. This is a breaking change, in that it may be necessary to change + user code in order to keep working. If you don't use 3D metrics, wrapping the + use of ``dz``, and similarly `Coordinates::zlength()`, in a call to + `getUniform` will return a ``BoutReal``. + + +The use of `Coordinates::FieldMetric` has been followed through the rest of the +code base. If a metric component enters an expression that previously contained +only `Field2D` and `BoutReal` types, the result is now a +`Coordinates::FieldMetric`. This means that functions that previously both took +and returned a `Field2D` now return a `Coordinates::FieldMetric` (we could have +chosen to make the return type ``auto`` instead and rely on the compiler to +deduce the correct type, but we have chosen to make the dependence on the metric +dimensionality more explicit). + +Because almost any operation on a vector involves the metric, the individual +components of `Vector2D` are now also of type +`Coordinates::FieldMetric`. Realistically, the use of `Vector2D` in a model +making use of 3D metrics is probably ill-advised. + +Indexing +~~~~~~~~ + +3D metrics also requires changes in how fields are indexed. In `BOUT_FOR` loops, +generally no changes are required, as they already do The Right Thing. In other +cases, simply changing, for example, ``dx(x, y)`` to ``dx(x, y, z)`` is +sufficient: in the 2D metric case, the third index is accepted and discarded. + +Many methods and operators have been upgraded to deal with 3D metrics. For +example, the `LaplaceXZpetsc` implementation has been modified to deal with +non-zero ``g_{xz}`` terms. + +FIXME WHEN COORDINATES REFACTORED +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to simplify a lot of code, the call to `Coordinates::geometry`, which +calculates the connection coefficients `Coordinates::G1_11` and so on, has been +moved out of the `Coordinates` constructor. This is because computing the +coefficients involves derivatives which requires `Coordinates` and causes all +sorts of headaches and shims. As most users do not call the constructor +themselves anyway, this change should not be much of an issue. + + +Incompatibilities +----------------- + +Many features of BOUT++ have been written assuming an axisymmetric coordinate +system. Once 3D metrics are enabled, this is no longer (necessarily) true which +breaks several features. For instance, many of the Laplacian inversion solvers +use intrinsically 2D methods, and so are not available when using 3D +metrics. Most of these features are runtime options, and therefore will throw an +exception if you try to use them. To get a list of available Laplacian solvers, +for example, you can pass the ``--list-laplacians`` flag to a compiled BOUT++ +executable, which will print all the Laplacian solvers, noting which are +unavailable and why. + +Several boundary conditions are also incompatible with 3D metrics, unfortunately +at the time of writing there is no easy way to list those that are. Several of +these, such as ``zerolaplace`` have no alternative implementations, so this may +mean it is not possible to run a given model with 3D metrics. + +There are a few tests that don't work with 3D metrics, mostly because they rely +on one of the above incompatible methods or operators. + +There is a preprocessor macro, ``BOUT_USE_METRIC_3D``, and a ``constexpr bool``, +`bout::build::use_metric_3d`, which can be used to guard code that doesn't +compile or work with 3D metrics, or perhaps needs to be handled differently. + +Caution should be exercised with FFT-based methods. Technically, FFTs do work +with 3D metrics, but will not give the correct answer with non-constant ``dz``. diff --git a/src/field/field.cxx b/src/field/field.cxx index b164adef15..91b8d72b0b 100644 --- a/src/field/field.cxx +++ b/src/field/field.cxx @@ -25,12 +25,13 @@ //#include +#include +#include +#include #include -#include #include -#include +#include #include -#include Field::Field(Mesh* localmesh, CELL_LOC location_in, DirectionTypes directions_in) : FieldData(localmesh, location_in), directions(directions_in) {} diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index 8b328db338..8b35d3c0f8 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -202,6 +202,13 @@ Field3D &Field3D::ynext(int dir) { } bool Field3D::requiresTwistShift(bool twist_shift_enabled) { + // Workaround for 3D coordinates. + // We need to communicate in the coordinates constructor in that + // case a Field3D, but coordinates isn't valid yet. As such we + // disable twist-shift in that case. + if (getCoordinates() == nullptr) { + return false; + } return getCoordinates()->getParallelTransform().requiresTwistShift(twist_shift_enabled, getDirectionY()); } @@ -775,7 +782,7 @@ void shiftZ(Field3D &var, int jx, int jy, double zangle) { rfft(&(var(jx,jy,0)), ncz, v.begin()); // Forward FFT - BoutReal zlength = var.getCoordinates()->zlength(); + BoutReal zlength = var.getCoordinates()->zlength()(jx, jy); // Apply phase shift for(int jz=1;jz<=ncz/2;jz++) { diff --git a/src/field/field_factory.cxx b/src/field/field_factory.cxx index 1fea66379b..9c1f0304a5 100644 --- a/src/field/field_factory.cxx +++ b/src/field/field_factory.cxx @@ -216,14 +216,19 @@ Field3D FieldFactory::create3D(FieldGeneratorPtr gen, Mesh* localmesh, CELL_LOC if (transform_from_field_aligned) { auto coords = result.getCoordinates(); if (coords == nullptr) { - throw BoutException("Unable to transform result: Mesh does not have Coordinates set"); - } - if (coords->getParallelTransform().canToFromFieldAligned()) { - // Transform from field aligned coordinates, to be compatible with - // older BOUT++ inputs. This is not a particularly "nice" solution. - result = fromFieldAligned(result, "RGN_ALL"); + // Should not lead to issues. If called from the coordinates + // constructor, then this is expected, and the result will be + // transformed. Otherwise, if the field is used untransformed, + // the inconsistency will be detected. + output_warn.write("Skipping parallel transformation - coordinates not set!\n"); } else { - result.setDirectionY(YDirectionType::Standard); + if (coords->getParallelTransform().canToFromFieldAligned()) { + // Transform from field aligned coordinates, to be compatible with + // older BOUT++ inputs. This is not a particularly "nice" solution. + result = fromFieldAligned(result, "RGN_ALL"); + } else { + result.setDirectionY(YDirectionType::Standard); + } } } @@ -262,12 +267,17 @@ FieldPerp FieldFactory::createPerp(FieldGeneratorPtr gen, Mesh* localmesh, CELL_ if (transform_from_field_aligned) { auto coords = result.getCoordinates(); if (coords == nullptr) { - throw BoutException("Unable to transform result: Mesh does not have Coordinates set"); - } - if (coords->getParallelTransform().canToFromFieldAligned()) { - // Transform from field aligned coordinates, to be compatible with - // older BOUT++ inputs. This is not a particularly "nice" solution. - result = fromFieldAligned(result, "RGN_ALL"); + // Should not lead to issues. If called from the coordinates + // constructor, then this is expected, and the result will be + // transformed. Otherwise, if the field is used untransformed, + // the inconsistency will be detected. + output_warn.write("Skipping parallel transformation - coordinates not set!\n"); + } else { + if (coords->getParallelTransform().canToFromFieldAligned()) { + // Transform from field aligned coordinates, to be compatible with + // older BOUT++ inputs. This is not a particularly "nice" solution. + result = fromFieldAligned(result, "RGN_ALL"); + } } } diff --git a/src/field/fieldgenerators.cxx b/src/field/fieldgenerators.cxx index fa12244bd7..bd369d1ab2 100644 --- a/src/field/fieldgenerators.cxx +++ b/src/field/fieldgenerators.cxx @@ -81,19 +81,21 @@ BoutReal FieldBallooning::generate(const Context& ctx) { / (localmesh->xend - localmesh->xstart); int jx = ROUND((ctx.x() - localmesh->GlobalX(0)) / dx); + const BoutReal zlength = getUniform(coords->zlength()); + if (localmesh->periodicY(jx, ts)) { // Start with the value at this point BoutReal value = arg->generate(ctx); for (int i = 1; i <= ball_n; i++) { // y - i * 2pi - value += arg->generate(Context(ctx).set( - "y", ctx.y() - i * TWOPI, - "z", ctx.z() + i * ts * TWOPI / coords->zlength())); + // clang-format off + value += arg->generate(Context(ctx).set("y", ctx.y() - i * TWOPI, + "z", ctx.z() + i * ts * TWOPI / zlength)); - value += arg->generate(Context(ctx).set( - "y", ctx.y() + i * TWOPI, - "z", ctx.z() - i * ts * TWOPI / coords->zlength())); + value += arg->generate(Context(ctx).set("y", ctx.y() + i * TWOPI, + "z", ctx.z() - i * ts * TWOPI / zlength)); + // clang-format on } return value; } diff --git a/src/field/vecops.cxx b/src/field/vecops.cxx index 8c3be73b8c..e1499084b8 100644 --- a/src/field/vecops.cxx +++ b/src/field/vecops.cxx @@ -38,7 +38,7 @@ * Gradient operators **************************************************************************/ -const Vector2D Grad(const Field2D& f, CELL_LOC outloc, const std::string& method) { +Vector2D Grad(const Field2D& f, CELL_LOC outloc, const std::string& method) { TRACE("Grad( Field2D )"); SCOREP0(); CELL_LOC outloc_x, outloc_y, outloc_z; @@ -67,7 +67,7 @@ const Vector2D Grad(const Field2D& f, CELL_LOC outloc, const std::string& method return result; } -const Vector3D Grad(const Field3D &f, CELL_LOC outloc, const std::string& method) { +Vector3D Grad(const Field3D& f, CELL_LOC outloc, const std::string& method) { TRACE("Grad( Field3D )"); SCOREP0(); CELL_LOC outloc_x, outloc_y, outloc_z; @@ -96,7 +96,7 @@ const Vector3D Grad(const Field3D &f, CELL_LOC outloc, const std::string& method return result; } -const Vector3D Grad_perp(const Field3D &f, CELL_LOC outloc, const std::string& method) { +Vector3D Grad_perp(const Field3D& f, CELL_LOC outloc, const std::string& method) { TRACE("Grad_perp( Field3D )"); SCOREP0(); ASSERT1(outloc == CELL_DEFAULT || outloc == f.getLocation()); @@ -118,7 +118,7 @@ const Vector3D Grad_perp(const Field3D &f, CELL_LOC outloc, const std::string& m return result; } -const Vector2D Grad_perp(const Field2D &f, CELL_LOC outloc, const std::string& method) { +Vector2D Grad_perp(const Field2D& f, CELL_LOC outloc, const std::string& method) { AUTO_TRACE(); SCOREP0(); ASSERT1(outloc == CELL_DEFAULT || outloc == f.getLocation()); @@ -143,7 +143,8 @@ const Vector2D Grad_perp(const Field2D &f, CELL_LOC outloc, const std::string& m * Divergence operators **************************************************************************/ -const Field2D Div(const Vector2D& v, CELL_LOC outloc, const std::string& method) { +Coordinates::FieldMetric Div(const Vector2D& v, CELL_LOC outloc, + const std::string& method) { TRACE("Div( Vector2D )"); SCOREP0(); if (outloc == CELL_DEFAULT) { @@ -159,8 +160,8 @@ const Field2D Div(const Vector2D& v, CELL_LOC outloc, const std::string& method) // get contravariant components of v Vector2D vcn = v; vcn.toContravariant(); - - Field2D result = DDX(metric->J*vcn.x, outloc, method); + + Coordinates::FieldMetric result = DDX(metric->J * vcn.x, outloc, method); result += DDY(metric->J*vcn.y, outloc, method); result += DDZ(metric->J*vcn.z, outloc, method); result /= metric->J; @@ -168,7 +169,7 @@ const Field2D Div(const Vector2D& v, CELL_LOC outloc, const std::string& method) return result; } -const Field3D Div(const Vector3D& v, CELL_LOC outloc, const std::string& method) { +Field3D Div(const Vector3D& v, CELL_LOC outloc, const std::string& method) { TRACE("Div( Vector3D )"); SCOREP0(); if (outloc == CELL_DEFAULT) { @@ -186,8 +187,16 @@ const Field3D Div(const Vector3D& v, CELL_LOC outloc, const std::string& method) Vector3D vcn = v; vcn.toContravariant(); - Field3D result = DDX(vcn.x.getCoordinates()->J * vcn.x, outloc, method); - result += DDY(vcn.y.getCoordinates()->J * vcn.y, outloc, method); + auto vcnJy = vcn.y.getCoordinates()->J * vcn.y; + if (v.y.hasParallelSlices()) { + // If v.y has parallel slices then we are using ShiftedMetric (with + // mesh:calcParallelSlices_on_communicate=true) or FCI, so we should calculate + // parallel slices for vcnJy in order to calculate the parallel derivative DDY + vcnJy.calcParallelSlices(); + } + auto result = DDY(vcnJy, outloc, method); + + result += DDX(vcn.x.getCoordinates()->J * vcn.x, outloc, method); result += DDZ(vcn.z.getCoordinates()->J * vcn.z, outloc, method); result /= metric->J; @@ -198,8 +207,8 @@ const Field3D Div(const Vector3D& v, CELL_LOC outloc, const std::string& method) * Divergence operators for flux methods **************************************************************************/ -const Field2D Div(const Vector2D& v, const Field2D& f, CELL_LOC outloc, - const std::string& method) { +Coordinates::FieldMetric Div(const Vector2D& v, const Field2D& f, CELL_LOC outloc, + const std::string& method) { TRACE("Div( Vector2D, Field2D )"); SCOREP0(); if (outloc == CELL_DEFAULT) { @@ -216,7 +225,8 @@ const Field2D Div(const Vector2D& v, const Field2D& f, CELL_LOC outloc, Vector2D vcn = v; vcn.toContravariant(); - Field2D result = FDDX(vcn.x.getCoordinates()->J * vcn.x, f, outloc, method); + Coordinates::FieldMetric result = + FDDX(vcn.x.getCoordinates()->J * vcn.x, f, outloc, method); result += FDDY(vcn.y.getCoordinates()->J * vcn.y, f, outloc, method); result += FDDZ(vcn.z.getCoordinates()->J * vcn.z, f, outloc, method); result /= metric->J; @@ -224,8 +234,8 @@ const Field2D Div(const Vector2D& v, const Field2D& f, CELL_LOC outloc, return result; } -const Field3D Div(const Vector3D& v, const Field3D& f, CELL_LOC outloc, - const std::string& method) { +Field3D Div(const Vector3D& v, const Field3D& f, CELL_LOC outloc, + const std::string& method) { TRACE("Div( Vector3D, Field3D )"); if (outloc == CELL_DEFAULT) { @@ -253,7 +263,7 @@ const Field3D Div(const Vector3D& v, const Field3D& f, CELL_LOC outloc, * Curl operators **************************************************************************/ -const Vector2D Curl(const Vector2D &v) { +Vector2D Curl(const Vector2D& v) { TRACE("Curl( Vector2D )"); @@ -281,7 +291,7 @@ const Vector2D Curl(const Vector2D &v) { return result; } -const Vector3D Curl(const Vector3D &v) { +Vector3D Curl(const Vector3D& v) { TRACE("Curl( Vector3D )"); SCOREP0(); ASSERT1(v.getLocation() != CELL_VSHIFT); @@ -312,7 +322,7 @@ const Vector3D Curl(const Vector3D &v) { /************************************************************************** * Upwinding operators **************************************************************************/ -const Field2D V_dot_Grad(const Vector2D &v, const Field2D &f) { +Coordinates::FieldMetric V_dot_Grad(const Vector2D& v, const Field2D& f) { TRACE("V_dot_Grad( Vector2D , Field2D )"); SCOREP0(); @@ -323,7 +333,7 @@ const Field2D V_dot_Grad(const Vector2D &v, const Field2D &f) { return VDDX(vcn.x, f) + VDDY(vcn.y, f) + VDDZ(vcn.z, f); } -const Field3D V_dot_Grad(const Vector2D &v, const Field3D &f) { +Field3D V_dot_Grad(const Vector2D& v, const Field3D& f) { TRACE("V_dot_Grad( Vector2D , Field3D )"); SCOREP0(); @@ -334,7 +344,7 @@ const Field3D V_dot_Grad(const Vector2D &v, const Field3D &f) { return VDDX(vcn.x, f) + VDDY(vcn.y, f) + VDDZ(vcn.z, f); } -const Field3D V_dot_Grad(const Vector3D &v, const Field2D &f) { +Field3D V_dot_Grad(const Vector3D& v, const Field2D& f) { TRACE("V_dot_Grad( Vector3D , Field2D )"); SCOREP0(); @@ -345,7 +355,7 @@ const Field3D V_dot_Grad(const Vector3D &v, const Field2D &f) { return VDDX(vcn.x, f) + VDDY(vcn.y, f) + VDDZ(vcn.z, f); } -const Field3D V_dot_Grad(const Vector3D &v, const Field3D &f) { +Field3D V_dot_Grad(const Vector3D& v, const Field3D& f) { TRACE("V_dot_Grad( Vector3D , Field3D )"); SCOREP0(); @@ -430,15 +440,15 @@ R V_dot_Grad(const T &v, const F &a) { } // Implement vector-vector operation in terms of templated routine above -const Vector2D V_dot_Grad(const Vector2D &v, const Vector2D &a) { +Vector2D V_dot_Grad(const Vector2D& v, const Vector2D& a) { return V_dot_Grad(v, a); } -const Vector3D V_dot_Grad(const Vector2D &v, const Vector3D &a) { +Vector3D V_dot_Grad(const Vector2D& v, const Vector3D& a) { return V_dot_Grad(v, a); } -const Vector3D V_dot_Grad(const Vector3D &v, const Vector2D &a) { +Vector3D V_dot_Grad(const Vector3D& v, const Vector2D& a) { return V_dot_Grad(v, a); } -const Vector3D V_dot_Grad(const Vector3D &v, const Vector3D &a) { +Vector3D V_dot_Grad(const Vector3D& v, const Vector3D& a) { return V_dot_Grad(v, a); } diff --git a/src/field/vector2d.cxx b/src/field/vector2d.cxx index f3c75c33dc..3c567a12fb 100644 --- a/src/field/vector2d.cxx +++ b/src/field/vector2d.cxx @@ -85,7 +85,7 @@ void Vector2D::toCovariant() { const auto y_at_z = interp_to(y, z.getLocation()); // multiply by g_{ij} - BOUT_FOR(i, localmesh->getRegion2D("RGN_ALL")){ + BOUT_FOR(i, x.getRegion("RGN_ALL")) { x[i] = metric_x->g_11[i]*x[i] + metric_x->g_12[i]*y_at_x[i] + metric_x->g_13[i]*z_at_x[i]; y[i] = metric_y->g_22[i]*y[i] + metric_y->g_12[i]*x_at_y[i] + metric_y->g_23[i]*z_at_y[i]; z[i] = metric_z->g_33[i]*z[i] + metric_z->g_13[i]*x_at_z[i] + metric_z->g_23[i]*y_at_z[i]; @@ -94,9 +94,9 @@ void Vector2D::toCovariant() { const auto metric = localmesh->getCoordinates(location); // Need to use temporary arrays to store result - Field2D gx{emptyFrom(x)}, gy{emptyFrom(y)}, gz{emptyFrom(z)}; + Coordinates::FieldMetric gx{emptyFrom(x)}, gy{emptyFrom(y)}, gz{emptyFrom(z)}; - BOUT_FOR(i, localmesh->getRegion2D("RGN_ALL")){ + BOUT_FOR(i, x.getRegion("RGN_ALL")) { gx[i] = metric->g_11[i]*x[i] + metric->g_12[i]*y[i] + metric->g_13[i]*z[i]; gy[i] = metric->g_22[i]*y[i] + metric->g_12[i]*x[i] + metric->g_23[i]*z[i]; gz[i] = metric->g_33[i]*z[i] + metric->g_13[i]*x[i] + metric->g_23[i]*y[i]; @@ -136,7 +136,7 @@ void Vector2D::toContravariant() { const auto y_at_z = interp_to(y, z.getLocation()); // multiply by g_{ij} - BOUT_FOR(i, localmesh->getRegion2D("RGN_ALL")){ + BOUT_FOR(i, x.getRegion("RGN_ALL")) { x[i] = metric_x->g11[i]*x[i] + metric_x->g12[i]*y_at_x[i] + metric_x->g13[i]*z_at_x[i]; y[i] = metric_y->g22[i]*y[i] + metric_y->g12[i]*x_at_y[i] + metric_y->g23[i]*z_at_y[i]; z[i] = metric_z->g33[i]*z[i] + metric_z->g13[i]*x_at_z[i] + metric_z->g23[i]*y_at_z[i]; @@ -146,9 +146,9 @@ void Vector2D::toContravariant() { const auto metric = localmesh->getCoordinates(location); // Need to use temporary arrays to store result - Field2D gx{emptyFrom(x)}, gy{emptyFrom(y)}, gz{emptyFrom(z)}; + Coordinates::FieldMetric gx{emptyFrom(x)}, gy{emptyFrom(y)}, gz{emptyFrom(z)}; - BOUT_FOR(i, localmesh->getRegion2D("RGN_ALL")){ + BOUT_FOR(i, x.getRegion("RGN_ALL")) { gx[i] = metric->g11[i]*x[i] + metric->g12[i]*y[i] + metric->g13[i]*z[i]; gy[i] = metric->g22[i]*y[i] + metric->g12[i]*x[i] + metric->g23[i]*z[i]; gz[i] = metric->g33[i]*z[i] + metric->g13[i]*x[i] + metric->g23[i]*y[i]; @@ -371,11 +371,11 @@ const Vector3D Vector2D::operator/(const Field3D &rhs) const { ////////////////// DOT PRODUCT /////////////////// -const Field2D Vector2D::operator*(const Vector2D &rhs) const { +const Coordinates::FieldMetric Vector2D::operator*(const Vector2D& rhs) const { ASSERT2(location == rhs.getLocation()); Mesh* localmesh = getMesh(); - Field2D result{emptyFrom(x)}; + Coordinates::FieldMetric result{emptyFrom(x)}; if(rhs.covariant ^ covariant) { // Both different - just multiply components @@ -476,7 +476,7 @@ const Vector3D operator*(const Field3D &lhs, const Vector2D &rhs) { ***************************************************************/ // Return the magnitude of a vector -const Field2D abs(const Vector2D &v, const std::string& region) { +const Coordinates::FieldMetric abs(const Vector2D& v, const std::string& region) { return sqrt(v*v, region); } diff --git a/src/fileio/datafile.cxx b/src/fileio/datafile.cxx index e094030a88..cc5ddd13e1 100644 --- a/src/fileio/datafile.cxx +++ b/src/fileio/datafile.cxx @@ -980,6 +980,18 @@ void Datafile::add(Vector2D &f, const char *name, bool save_repeat, const std::s // Add variables to file auto dname = d.covar ? d.name + "_" : d.name; +#if BOUT_USE_METRIC_3D + // Add variables to file + if (!file->addVarField3D(dname + "x", save_repeat)) { + throw BoutException("Failed to add Vector2D variable {:s} to Datafile", dname); + } + if (!file->addVarField3D(dname + "y", save_repeat)) { + throw BoutException("Failed to add Vector2D variable {:s} to Datafile", dname); + } + if (!file->addVarField3D(dname + "z", save_repeat)) { + throw BoutException("Failed to add Vector2D variable {:s} to Datafile", dname); + } +#else if (!file->addVarField2D(dname + "x", save_repeat)) { throw BoutException("Failed to add Vector2D variable {:s} to Datafile", dname); } @@ -989,6 +1001,7 @@ void Datafile::add(Vector2D &f, const char *name, bool save_repeat, const std::s if (!file->addVarField2D(dname + "z", save_repeat)) { throw BoutException("Failed to add Vector2D variable {:s} to Datafile", dname); } +#endif if(openclose) { file->close(); @@ -1266,6 +1279,19 @@ bool Datafile::read() { } // 2D vectors +#if BOUT_USE_METRIC_3D + for (const auto& var : v2d_arr) { + if (var.covar) { + // Reading covariant vector + read_f3d(var.name + "_x", &(var.ptr->x), var.save_repeat); + read_f3d(var.name + "_y", &(var.ptr->y), var.save_repeat); + read_f3d(var.name + "_z", &(var.ptr->z), var.save_repeat); + } else { + read_f3d(var.name + "x", &(var.ptr->x), var.save_repeat); + read_f3d(var.name + "y", &(var.ptr->y), var.save_repeat); + read_f3d(var.name + "z", &(var.ptr->z), var.save_repeat); + } +#else for(const auto& var : v2d_arr) { if(var.covar) { // Reading covariant vector @@ -1277,7 +1303,7 @@ bool Datafile::read() { read_f2d(var.name + "y", &(var.ptr->y), var.save_repeat); read_f2d(var.name + "z", &(var.ptr->z), var.save_repeat); } - +#endif var.ptr->covariant = var.covar; } @@ -1493,9 +1519,15 @@ bool Datafile::write() { v.toContravariant(); } +#if BOUT_USE_METRIC_3D + write_f3d(name + "x", &(v.x), var.save_repeat); + write_f3d(name + "y", &(v.y), var.save_repeat); + write_f3d(name + "z", &(v.z), var.save_repeat); +#else write_f2d(name+"x", &(v.x), var.save_repeat); write_f2d(name+"y", &(v.y), var.save_repeat); write_f2d(name+"z", &(v.z), var.save_repeat); +#endif } // 3D vectors diff --git a/src/fileio/impls/netcdf/nc_format.cxx b/src/fileio/impls/netcdf/nc_format.cxx index a9ebb520a0..89d136703b 100644 --- a/src/fileio/impls/netcdf/nc_format.cxx +++ b/src/fileio/impls/netcdf/nc_format.cxx @@ -28,10 +28,10 @@ #include #include -#include #include "bout/build_config.hxx" -#include +#include #include +#include using std::string; using std::vector; diff --git a/src/fileio/impls/netcdf4/ncxx4.cxx b/src/fileio/impls/netcdf4/ncxx4.cxx index dc085c4040..755f023427 100644 --- a/src/fileio/impls/netcdf4/ncxx4.cxx +++ b/src/fileio/impls/netcdf4/ncxx4.cxx @@ -1417,4 +1417,3 @@ std::vector Ncxx4::getRecDimVec(int nd) { } #endif // BOUT_HAS_NETCDF - diff --git a/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx b/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx index 209253950b..7d32e1fe96 100644 --- a/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx +++ b/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx @@ -33,6 +33,11 @@ * */ +#include "cyclic_laplace.hxx" +#include "bout/build_config.hxx" + +#if not BOUT_USE_METRIC_3D + #include #include #include @@ -42,10 +47,9 @@ #include #include -#include "cyclic_laplace.hxx" - LaplaceCyclic::LaplaceCyclic(Options *opt, const CELL_LOC loc, Mesh *mesh_in) : Laplacian(opt, loc, mesh_in), Acoef(0.0), C1coef(1.0), C2coef(1.0), Dcoef(1.0) { + Acoef.setLocation(location); C1coef.setLocation(location); C2coef.setLocation(location); @@ -141,9 +145,9 @@ FieldPerp LaplaceCyclic::solve(const FieldPerp& rhs, const FieldPerp& x0) { // Get elements of the tridiagonal matrix // including boundary conditions + BoutReal zlen = getUniform(coords->dz) * (localmesh->LocalNz - 3); BOUT_OMP(for nowait) for (int kz = 0; kz < nmode; kz++) { - BoutReal zlen = coords->dz * (localmesh->LocalNz - 3); BoutReal kwave = kz * 2.0 * PI / (2. * zlen); // wave number is 1/[rad]; DST has extra 2. @@ -209,9 +213,10 @@ FieldPerp LaplaceCyclic::solve(const FieldPerp& rhs, const FieldPerp& x0) { // Get elements of the tridiagonal matrix // including boundary conditions + const BoutReal zlength = getUniform(coords->zlength()); BOUT_OMP(for nowait) for (int kz = 0; kz < nmode; kz++) { - BoutReal kwave = kz * 2.0 * PI / (coords->zlength()); // wave number is 1/[rad] + BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] tridagMatrix(&a(kz, 0), &b(kz, 0), &c(kz, 0), &bcmplx(kz, 0), jy, kz, // True for the component constant (DC) in Z kwave, // Z wave number @@ -342,13 +347,13 @@ Field3D LaplaceCyclic::solve(const Field3D& rhs, const Field3D& x0) { // Get elements of the tridiagonal matrix // including boundary conditions + const BoutReal zlen = getUniform(coords->dz) * (localmesh->LocalNz - 3); BOUT_OMP(for nowait) for (int ind = 0; ind < nsys; ind++) { // ind = (iy - ys) * nmode + kz int iy = ys + ind / nmode; int kz = ind % nmode; - BoutReal zlen = coords->dz * (localmesh->LocalNz - 3); BoutReal kwave = kz * 2.0 * PI / (2. * zlen); // wave number is 1/[rad]; DST has extra 2. @@ -423,13 +428,14 @@ Field3D LaplaceCyclic::solve(const Field3D& rhs, const Field3D& x0) { // Get elements of the tridiagonal matrix // including boundary conditions + const BoutReal zlength = getUniform(coords->zlength()); BOUT_OMP(for nowait) for (int ind = 0; ind < nsys; ind++) { // ind = (iy - ys) * nmode + kz int iy = ys + ind / nmode; int kz = ind % nmode; - BoutReal kwave = kz * 2.0 * PI / (coords->zlength()); // wave number is 1/[rad] + BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] tridagMatrix(&a3D(ind, 0), &b3D(ind, 0), &c3D(ind, 0), &bcmplx3D(ind, 0), iy, kz, // True for the component constant (DC) in Z kwave, // Z wave number @@ -559,3 +565,5 @@ void LaplaceCyclic ::verify_solution(const Matrix& a_ver, } output.write("max abs error {}\n", max_error); } + +#endif // BOUT_USE_METRIC_3D diff --git a/src/invert/laplace/impls/cyclic/cyclic_laplace.hxx b/src/invert/laplace/impls/cyclic/cyclic_laplace.hxx index 80e39b395c..cd05918747 100644 --- a/src/invert/laplace/impls/cyclic/cyclic_laplace.hxx +++ b/src/invert/laplace/impls/cyclic/cyclic_laplace.hxx @@ -31,7 +31,18 @@ class LaplaceCyclic; #ifndef __LAP_CYCLIC_H__ #define __LAP_CYCLIC_H__ -#include +#include "invert_laplace.hxx" +#include "bout/build_config.hxx" + +#if BOUT_USE_METRIC_3D + +namespace { +RegisterUnavailableLaplace registerlaplacecycle(LAPLACE_CYCLIC, + "BOUT++ was configured with 3D metrics"); +} + +#else + #include #include #include @@ -111,4 +122,6 @@ private: CyclicReduce *cr; ///< Tridiagonal solver }; +#endif // BOUT_USE_METRIC_3D + #endif // __SPT_H__ diff --git a/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx b/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx index fc69261f3f..29a6bcb5cf 100644 --- a/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx +++ b/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx @@ -25,6 +25,10 @@ **************************************************************************/ #include "iterative_parallel_tri.hxx" +#include "bout/build_config.hxx" + +#if not BOUT_USE_METRIC_3D + #include "globals.hxx" #include @@ -249,7 +253,7 @@ FieldPerp LaplaceIPT::solve(const FieldPerp& b, const FieldPerp& x0) { xs = localmesh->xstart; // First interior point xe = localmesh->xend; // Last interior point - const BoutReal kwaveFactor = 2.0 * PI / coords->zlength(); + const BoutReal kwaveFactor = 2.0 * PI / getUniform(coords->zlength()); // Setting the width of the boundary. // NOTE: The default is a width of 2 guard cells @@ -1450,3 +1454,5 @@ void LaplaceIPT::Level::synchronize_reduced_field(const LaplaceIPT& l, l.nmode, MPI_DOUBLE_COMPLEX, proc_out, 1, comm, MPI_STATUS_IGNORE); } } + +#endif // BOUT_USE_METRIC_3D diff --git a/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.hxx b/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.hxx index 3c3bac73e5..062daa575a 100644 --- a/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.hxx +++ b/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.hxx @@ -29,8 +29,19 @@ class LaplaceIPT; #ifndef __IPT_H__ #define __IPT_H__ +#include "invert_laplace.hxx" +#include "bout/build_config.hxx" + +#if BOUT_USE_METRIC_3D + +namespace { +RegisterUnavailableLaplace registerlaplaceipt(LAPLACE_IPT, + "BOUT++ was configured with 3D metrics"); +} + +#else + #include -#include #include #include @@ -233,4 +244,6 @@ private: } }; +#endif // BOUT_USE_METRIC_3D + #endif // __IPT_H__ diff --git a/src/invert/laplace/impls/multigrid/multigrid_alg.cxx b/src/invert/laplace/impls/multigrid/multigrid_alg.cxx index 6cfdee1b47..10ac88ad2c 100644 --- a/src/invert/laplace/impls/multigrid/multigrid_alg.cxx +++ b/src/invert/laplace/impls/multigrid/multigrid_alg.cxx @@ -27,6 +27,10 @@ * **************************************************************************/ +#include "bout/build_config.hxx" + +#if not BOUT_USE_METRIC_3D + #include "multigrid_laplace.hxx" #include #include "unused.hxx" @@ -760,3 +764,5 @@ BOUT_OMP(for) fflush(stdout); } } + +#endif diff --git a/src/invert/laplace/impls/multigrid/multigrid_laplace.cxx b/src/invert/laplace/impls/multigrid/multigrid_laplace.cxx index 6ab7564287..03926fa1c6 100644 --- a/src/invert/laplace/impls/multigrid/multigrid_laplace.cxx +++ b/src/invert/laplace/impls/multigrid/multigrid_laplace.cxx @@ -28,6 +28,10 @@ **************************************************************************/ #include "multigrid_laplace.hxx" +#include "bout/build_config.hxx" + +#if not BOUT_USE_METRIC_3D + #include #include #include @@ -549,7 +553,6 @@ BOUT_OMP(for) } void LaplaceMultigrid::generateMatrixF(int level) { - TRACE("LaplaceMultigrid::generateMatrixF(int)"); // Set (fine-level) matrix entries @@ -567,20 +570,23 @@ BOUT_OMP(for collapse(2)) int k2 = k-1; int k2p = (k2+1)%Nz_global; int k2m = (k2+Nz_global-1)%Nz_global; - + + BoutReal dz = coords->dz(i2, yindex); BoutReal ddx_C = (C2(i2+1, yindex, k2) - C2(i2-1, yindex, k2))/2./coords->dx(i2, yindex)/C1(i2, yindex, k2); - BoutReal ddz_C = (C2(i2, yindex, k2p) - C2(i2, yindex, k2m)) /2./coords->dz/C1(i2, yindex, k2); - + BoutReal ddz_C = + (C2(i2, yindex, k2p) - C2(i2, yindex, k2m)) / 2. / dz / C1(i2, yindex, k2); + BoutReal ddx = D(i2, yindex, k2)*coords->g11(i2, yindex)/coords->dx(i2, yindex)/coords->dx(i2, yindex); // coefficient of 2nd derivative stencil (x-direction) - - BoutReal ddz = D(i2, yindex, k2)*coords->g33(i2, yindex)/coords->dz/coords->dz; - // coefficient of 2nd derivative stencil (z-direction) - - BoutReal dxdz = D(i2, yindex, k2)*2.*coords->g13(i2, yindex)/coords->dx(i2, yindex)/coords->dz; - // coefficient of mixed derivative stencil (could assume zero, at least initially, - // if easier; then check this is true in constructor) - + + BoutReal ddz = D(i2, yindex, k2) * coords->g33(i2, yindex) / SQ(dz); + // coefficient of 2nd derivative stencil (z-direction) + + BoutReal dxdz = + D(i2, yindex, k2) * 2. * coords->g13(i2, yindex) / coords->dx(i2, yindex) / dz; + // coefficient of mixed derivative stencil (could assume zero, at least initially, + // if easier; then check this is true in constructor) + BoutReal dxd = (D(i2, yindex, k2)*coords->G1(i2, yindex) + coords->g11(i2, yindex)*ddx_C + coords->g13(i2, yindex)*ddz_C // (could assume zero, at least initially, if easier; then check this is true in constructor) @@ -589,12 +595,15 @@ BOUT_OMP(for collapse(2)) // add correction for non-uniform dx dxd += D(i2, yindex, k2)*coords->d1_dx(i2, yindex); } - - BoutReal dzd = (D(i2, yindex, k2)*coords->G3(i2, yindex) - + coords->g33(i2, yindex)*ddz_C - + coords->g13(i2, yindex)*ddx_C // (could assume zero, at least initially, if easier; then check this is true in constructor) - )/coords->dz; // coefficient of 1st derivative stencil (z-direction) - + + BoutReal dzd = + (D(i2, yindex, k2) * coords->G3(i2, yindex) + coords->g33(i2, yindex) * ddz_C + + coords->g13(i2, yindex) + * ddx_C // (could assume zero, at least initially, if easier; then check + // this is true in constructor) + ) + / dz; // coefficient of 1st derivative stencil (z-direction) + int ic = i*(llz+2)+k; mat[ic*9] = dxdz/4.; mat[ic*9+1] = ddx - dxd/2.; @@ -684,3 +693,4 @@ BOUT_OMP(for) } } +#endif // BOUT_USE_METRIC_3D diff --git a/src/invert/laplace/impls/multigrid/multigrid_laplace.hxx b/src/invert/laplace/impls/multigrid/multigrid_laplace.hxx index 3a14cabe1b..aa2b675082 100644 --- a/src/invert/laplace/impls/multigrid/multigrid_laplace.hxx +++ b/src/invert/laplace/impls/multigrid/multigrid_laplace.hxx @@ -31,12 +31,23 @@ #ifndef __MULTIGRID_LAPLACE_H__ #define __MULTIGRID_LAPLACE_H__ +#include "invert_laplace.hxx" +#include "bout/build_config.hxx" + +#if BOUT_USE_METRIC_3D + +namespace { +RegisterUnavailableLaplace + registerlaplacemultigrid(LAPLACE_MULTIGRID, "BOUT++ was configured with 3D metrics"); +} + +#else + #include #include #include #include -#include #include #include @@ -239,4 +250,6 @@ namespace { RegisterLaplace registerlaplacemultigrid(LAPLACE_MULTIGRID); } +#endif // BOUT_USE_METRIC_3D + #endif // __MULTIGRID_LAPLACE_H__ diff --git a/src/invert/laplace/impls/multigrid/multigrid_solver.cxx b/src/invert/laplace/impls/multigrid/multigrid_solver.cxx index 8a477a1569..a1892f09cd 100644 --- a/src/invert/laplace/impls/multigrid/multigrid_solver.cxx +++ b/src/invert/laplace/impls/multigrid/multigrid_solver.cxx @@ -27,6 +27,10 @@ * **************************************************************************/ +#include "bout/build_config.hxx" + +#if not BOUT_USE_METRIC_3D + #include "multigrid_laplace.hxx" #include "unused.hxx" #include @@ -723,3 +727,5 @@ MultigridSerial::MultigridSerial(int level, int gx, int gz, MPI_Comm comm, int c } } } + +#endif diff --git a/src/invert/laplace/impls/pcr/pcr.cxx b/src/invert/laplace/impls/pcr/pcr.cxx index 508bc57041..373abb730c 100644 --- a/src/invert/laplace/impls/pcr/pcr.cxx +++ b/src/invert/laplace/impls/pcr/pcr.cxx @@ -158,6 +158,7 @@ FieldPerp LaplacePCR::solve(const FieldPerp& rhs, const FieldPerp& x0) { } if (dst) { + const BoutReal zlen = getUniform(coords->dz) * (localmesh->LocalNz - 3); BOUT_OMP(parallel) { /// Create a local thread-scope working array auto k1d = Array( @@ -189,7 +190,6 @@ FieldPerp LaplacePCR::solve(const FieldPerp& rhs, const FieldPerp& x0) { // including boundary conditions BOUT_OMP(for nowait) for (int kz = 0; kz < nmode; kz++) { - BoutReal zlen = coords->dz * (localmesh->LocalNz - 3); BoutReal kwave = kz * 2.0 * PI / (2. * zlen); // wave number is 1/[rad]; DST has extra 2. @@ -200,7 +200,7 @@ FieldPerp LaplacePCR::solve(const FieldPerp& rhs, const FieldPerp& x0) { &C1coef, &C2coef, &Dcoef, false); // Don't include guard cells in arrays } - } + } // BOUT_OMP(parallel) // Solve tridiagonal systems cr_pcr_solver(a, b, c, bcmplx, xcmplx); @@ -228,6 +228,7 @@ FieldPerp LaplacePCR::solve(const FieldPerp& rhs, const FieldPerp& x0) { } } } else { + const BoutReal zlength = getUniform(coords->zlength()); BOUT_OMP(parallel) { /// Create a local thread-scope working array auto k1d = Array((localmesh->LocalNz) / 2 @@ -259,7 +260,7 @@ FieldPerp LaplacePCR::solve(const FieldPerp& rhs, const FieldPerp& x0) { // including boundary conditions BOUT_OMP(for nowait) for (int kz = 0; kz < nmode; kz++) { - BoutReal kwave = kz * 2.0 * PI / (coords->zlength()); // wave number is 1/[rad] + BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] tridagMatrix(&a(kz, 0), &b(kz, 0), &c(kz, 0), &bcmplx(kz, 0), jy, kz, // True for the component constant (DC) in Z kwave, // Z wave number @@ -267,7 +268,7 @@ FieldPerp LaplacePCR::solve(const FieldPerp& rhs, const FieldPerp& x0) { &C1coef, &C2coef, &Dcoef, false); // Don't include guard cells in arrays } - } + } // BOUT_OMP(parallel) // Solve tridiagonal systems cr_pcr_solver(a, b, c, bcmplx, xcmplx); @@ -363,6 +364,7 @@ Field3D LaplacePCR::solve(const Field3D& rhs, const Field3D& x0) { auto bcmplx3D = Matrix(nsys, nx); if (dst) { + const BoutReal zlen = getUniform(coords->dz) * (localmesh->LocalNz - 3); BOUT_OMP(parallel) { /// Create a local thread-scope working array auto k1d = Array( @@ -402,7 +404,6 @@ Field3D LaplacePCR::solve(const Field3D& rhs, const Field3D& x0) { int iy = ys + ind / nmode; int kz = ind % nmode; - BoutReal zlen = coords->dz * (localmesh->LocalNz - 3); BoutReal kwave = kz * 2.0 * PI / (2. * zlen); // wave number is 1/[rad]; DST has extra 2. @@ -413,7 +414,7 @@ Field3D LaplacePCR::solve(const Field3D& rhs, const Field3D& x0) { &C1coef, &C2coef, &Dcoef, false); // Don't include guard cells in arrays } - } + } // BOUT_OMP(parallel) // Solve tridiagonal systems cr_pcr_solver(a3D, b3D, c3D, bcmplx3D, xcmplx3D); @@ -445,6 +446,7 @@ Field3D LaplacePCR::solve(const Field3D& rhs, const Field3D& x0) { } } } else { + const BoutReal zlength = getUniform(coords->zlength()); BOUT_OMP(parallel) { /// Create a local thread-scope working array auto k1d = Array(localmesh->LocalNz / 2 @@ -485,7 +487,7 @@ Field3D LaplacePCR::solve(const Field3D& rhs, const Field3D& x0) { int iy = ys + ind / nmode; int kz = ind % nmode; - BoutReal kwave = kz * 2.0 * PI / (coords->zlength()); // wave number is 1/[rad] + BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] tridagMatrix(&a3D(ind, 0), &b3D(ind, 0), &c3D(ind, 0), &bcmplx3D(ind, 0), iy, kz, // True for the component constant (DC) in Z kwave, // Z wave number @@ -493,7 +495,7 @@ Field3D LaplacePCR::solve(const Field3D& rhs, const Field3D& x0) { &C1coef, &C2coef, &Dcoef, false); // Don't include guard cells in arrays } - } + } // BOUT_OMP(parallel) // Solve tridiagonal systems cr_pcr_solver(a3D, b3D, c3D, bcmplx3D, xcmplx3D); diff --git a/src/invert/laplace/impls/pdd/pdd.cxx b/src/invert/laplace/impls/pdd/pdd.cxx index 9a5806a845..9576087d43 100644 --- a/src/invert/laplace/impls/pdd/pdd.cxx +++ b/src/invert/laplace/impls/pdd/pdd.cxx @@ -169,7 +169,7 @@ void LaplacePDD::start(const FieldPerp &b, PDD_data &data) { /// Create the matrices to be inverted (one for each z point) - BoutReal kwaveFactor = 2.0 * PI / coords->zlength(); + BoutReal kwaveFactor = 2.0 * PI / getUniform(coords->zlength()); /// Set matrix elements for (int kz = 0; kz <= maxmode; kz++) { diff --git a/src/invert/laplace/impls/petsc/petsc_laplace.cxx b/src/invert/laplace/impls/petsc/petsc_laplace.cxx index 51f43a10d2..e48ce938f2 100644 --- a/src/invert/laplace/impls/petsc/petsc_laplace.cxx +++ b/src/invert/laplace/impls/petsc/petsc_laplace.cxx @@ -403,25 +403,45 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { if(inner_boundary_flags & INVERT_AC_GRAD) { // Set values corresponding to nodes adjacent in x if( fourth_order ) { - // Fourth Order Accuracy on Boundary - Element(i,x,z, 0, 0, -25.0 / (12.0*coords->dx(x,y)) / sqrt(coords->g_11(x,y)), MatA ); - Element(i,x,z, 1, 0, 4.0 / coords->dx(x,y) / sqrt(coords->g_11(x,y)), MatA ); - Element(i,x,z, 2, 0, -3.0 / coords->dx(x,y) / sqrt(coords->g_11(x,y)), MatA ); - Element(i,x,z, 3, 0, 4.0 / (3.0*coords->dx(x,y)) / sqrt(coords->g_11(x,y)), MatA ); - Element(i,x,z, 4, 0, -1.0 / (4.0*coords->dx(x,y)) / sqrt(coords->g_11(x,y)), MatA ); + // Fourth Order Accuracy on Boundary + Element(i, x, z, 0, 0, + -25.0 / (12.0 * coords->dx(x, y, z)) + / sqrt(coords->g_11(x, y, z)), + MatA); + Element(i, x, z, 1, 0, + 4.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), + MatA); + Element(i, x, z, 2, 0, + -3.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), + MatA); + Element(i, x, z, 3, 0, + 4.0 / (3.0 * coords->dx(x, y, z)) + / sqrt(coords->g_11(x, y, z)), + MatA); + Element(i, x, z, 4, 0, + -1.0 / (4.0 * coords->dx(x, y, z)) + / sqrt(coords->g_11(x, y, z)), + MatA); } else { -// // Second Order Accuracy on Boundary -// Element(i,x,z, 0, 0, -3.0 / (2.0*coords->dx(x,y)), MatA ); -// Element(i,x,z, 1, 0, 2.0 / coords->dx(x,y), MatA ); -// Element(i,x,z, 2, 0, -1.0 / (2.0*coords->dx(x,y)), MatA ); -// // Element(i,x,z, 3, 0, 0.0, MatA ); // Reset these elements to 0 in case 4th order flag was used previously: not allowed now -// // Element(i,x,z, 4, 0, 0.0, MatA ); - // Second Order Accuracy on Boundary, set half-way between grid points - Element(i,x,z, 0, 0, -1.0 / coords->dx(x,y) / sqrt(coords->g_11(x,y)), MatA ); - Element(i,x,z, 1, 0, 1.0 / coords->dx(x,y) / sqrt(coords->g_11(x,y)), MatA ); - Element(i,x,z, 2, 0, 0.0, MatA ); -// Element(i,x,z, 3, 0, 0.0, MatA ); // Reset these elements to 0 in case 4th order flag was used previously: not allowed now -// Element(i,x,z, 4, 0, 0.0, MatA ); + // Second Order Accuracy on Boundary + // Element(i,x,z, 0, 0, -3.0 / (2.0*coords->dx(x,y)), MatA ); + // Element(i,x,z, 1, 0, 2.0 / coords->dx(x,y), MatA ); + // Element(i,x,z, 2, 0, -1.0 / (2.0*coords->dx(x,y)), MatA ); + // Element(i,x,z, 3, 0, 0.0, MatA ); // Reset these elements to 0 + // in case 4th order flag was used previously: not allowed now + // Element(i,x,z, 4, 0, 0.0, MatA ); + // Second Order Accuracy on Boundary, set half-way between grid points + Element(i, x, z, 0, 0, + -1.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), + MatA); + Element(i, x, z, 1, 0, + 1.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), + MatA); + Element(i, x, z, 2, 0, 0.0, MatA); + // Element(i,x,z, 3, 0, 0.0, MatA ); // Reset + // these elements to 0 in case 4th order flag was + // used previously: not allowed now + // Element(i,x,z, 4, 0, 0.0, MatA ); } } else { if (fourth_order) { @@ -475,12 +495,12 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { // Set the matrix coefficients Coeffs( x, y, z, A1, A2, A3, A4, A5 ); - BoutReal dx = coords->dx(x,y); - BoutReal dx2 = SQ(coords->dx(x,y)); - BoutReal dz = coords->dz; - BoutReal dz2 = SQ(coords->dz); - BoutReal dxdz = coords->dx(x,y) * coords->dz; - + BoutReal dx = coords->dx(x, y, z); + BoutReal dx2 = SQ(dx); + BoutReal dz = coords->dz(x, y, z); + BoutReal dz2 = SQ(dz); + BoutReal dxdz = dx * dz; + ASSERT3(finite(A1)); ASSERT3(finite(A2)); ASSERT3(finite(A3)); @@ -653,25 +673,45 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { // Set values corresponding to nodes adjacent in x if( fourth_order ) { // Fourth Order Accuracy on Boundary - Element(i,x,z, 0, 0, 25.0 / (12.0*coords->dx(x,y)) / sqrt(coords->g_11(x,y)), MatA ); - Element(i,x,z, -1, 0, -4.0 / coords->dx(x,y) / sqrt(coords->g_11(x,y)), MatA ); - Element(i,x,z, -2, 0, 3.0 / coords->dx(x,y) / sqrt(coords->g_11(x,y)), MatA ); - Element(i,x,z, -3, 0, -4.0 / (3.0*coords->dx(x,y)) / sqrt(coords->g_11(x,y)), MatA ); - Element(i,x,z, -4, 0, 1.0 / (4.0*coords->dx(x,y)) / sqrt(coords->g_11(x,y)), MatA ); + Element(i, x, z, 0, 0, + 25.0 / (12.0 * coords->dx(x, y, z)) + / sqrt(coords->g_11(x, y, z)), + MatA); + Element(i, x, z, -1, 0, + -4.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), + MatA); + Element(i, x, z, -2, 0, + 3.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), + MatA); + Element(i, x, z, -3, 0, + -4.0 / (3.0 * coords->dx(x, y, z)) + / sqrt(coords->g_11(x, y, z)), + MatA); + Element(i, x, z, -4, 0, + 1.0 / (4.0 * coords->dx(x, y, z)) + / sqrt(coords->g_11(x, y, z)), + MatA); } else { -// // Second Order Accuracy on Boundary -// Element(i,x,z, 0, 0, 3.0 / (2.0*coords->dx(x,y)), MatA ); -// Element(i,x,z, -1, 0, -2.0 / coords->dx(x,y), MatA ); -// Element(i,x,z, -2, 0, 1.0 / (2.0*coords->dx(x,y)), MatA ); -// // Element(i,x,z, -3, 0, 0.0, MatA ); // Reset these elements to 0 in case 4th order flag was used previously: not allowed now -// // Element(i,x,z, -4, 0, 0.0, MatA ); - // Second Order Accuracy on Boundary, set half-way between grid points - Element(i,x,z, 0, 0, 1.0 / coords->dx(x,y) / sqrt(coords->g_11(x,y)), MatA ); - Element(i,x,z, -1, 0, -1.0 / coords->dx(x,y) / sqrt(coords->g_11(x,y)), MatA ); - Element(i,x,z, -2, 0, 0.0, MatA ); -// Element(i,x,z, -3, 0, 0.0, MatA ); // Reset these elements to 0 in case 4th order flag was used previously: not allowed now -// Element(i,x,z, -4, 0, 0.0, MatA ); + // // Second Order Accuracy on Boundary + // Element(i,x,z, 0, 0, 3.0 / (2.0*coords->dx(x,y)), MatA ); + // Element(i,x,z, -1, 0, -2.0 / coords->dx(x,y), MatA ); + // Element(i,x,z, -2, 0, 1.0 / (2.0*coords->dx(x,y)), MatA ); + // Element(i,x,z, -3, 0, 0.0, MatA ); // Reset these elements to 0 + // in case 4th order flag was used previously: not allowed now + // Element(i,x,z, -4, 0, 0.0, MatA ); + // Second Order Accuracy on Boundary, set half-way between grid + // points + Element(i, x, z, 0, 0, + 1.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), + MatA); + Element(i, x, z, -1, 0, + -1.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), + MatA); + Element(i, x, z, -2, 0, 0.0, MatA); + // Element(i,x,z, -3, 0, 0.0, MatA ); // Reset these elements to 0 + // in case 4th order flag was used previously: not allowed now + // Element(i,x,z, -4, 0, 0.0, MatA ); } } else { @@ -943,16 +983,16 @@ void LaplacePetsc::Element(int i, int x, int z, */ void LaplacePetsc::Coeffs( int x, int y, int z, BoutReal &coef1, BoutReal &coef2, BoutReal &coef3, BoutReal &coef4, BoutReal &coef5 ) { - coef1 = coords->g11(x,y); // X 2nd derivative coefficient - coef2 = coords->g33(x,y); // Z 2nd derivative coefficient - coef3 = 2.*coords->g13(x,y); // X-Z mixed derivative coefficient + coef1 = coords->g11(x, y, z); // X 2nd derivative coefficient + coef2 = coords->g33(x, y, z); // Z 2nd derivative coefficient + coef3 = 2. * coords->g13(x, y, z); // X-Z mixed derivative coefficient coef4 = 0.0; coef5 = 0.0; // If global flag all_terms are set (true by default) if (all_terms) { - coef4 = coords->G1(x,y); // X 1st derivative - coef5 = coords->G3(x,y); // Z 1st derivative + coef4 = coords->G1(x, y, z); // X 1st derivative + coef5 = coords->G3(x, y, z); // Z 1st derivative ASSERT3(finite(coef4)); ASSERT3(finite(coef5)); @@ -961,13 +1001,17 @@ void LaplacePetsc::Coeffs( int x, int y, int z, BoutReal &coef1, BoutReal &coef2 if(nonuniform) { // non-uniform mesh correction if((x != 0) && (x != (localmesh->LocalNx-1))) { - coef4 -= 0.5 * ( ( coords->dx(x+1,y) - coords->dx(x-1,y) ) / SQ(coords->dx(x,y)) ) * coef1; // BOUT-06 term + coef4 -= 0.5 + * ((coords->dx(x + 1, y, z) - coords->dx(x - 1, y, z)) + / SQ(coords->dx(x, y, z))) + * coef1; // BOUT-06 term } } if(localmesh->IncIntShear) { // d2dz2 term - coef2 += coords->g11(x,y) * coords->IntShiftTorsion(x,y) * coords->IntShiftTorsion(x,y); + coef2 += coords->g11(x, y, z) * coords->IntShiftTorsion(x, y, z) + * coords->IntShiftTorsion(x, y, z); // Mixed derivative coef3 = 0.0; // This cancels out } @@ -997,19 +1041,25 @@ void LaplacePetsc::Coeffs( int x, int y, int z, BoutReal &coef1, BoutReal &coef2 int zmm = z-2; // z minus 1 minus 1 if (zmm<0) zmm += meshz; // Fourth order discretization of C in x - ddx_C = (-C2(x+2,y,z) + 8.*C2(x+1,y,z) - 8.*C2(x-1,y,z) + C2(x-2,y,z)) / (12.*coords->dx(x,y)*(C1(x,y,z))); + ddx_C = (-C2(x + 2, y, z) + 8. * C2(x + 1, y, z) - 8. * C2(x - 1, y, z) + + C2(x - 2, y, z)) + / (12. * coords->dx(x, y, z) * (C1(x, y, z))); // Fourth order discretization of C in z - ddz_C = (-C2(x,y,zpp) + 8.*C2(x,y,zp) - 8.*C2(x,y,zm) + C2(x,y,zmm)) / (12.*coords->dz*(C1(x,y,z))); + ddz_C = + (-C2(x, y, zpp) + 8. * C2(x, y, zp) - 8. * C2(x, y, zm) + C2(x, y, zmm)) + / (12. * coords->dz(x, y, z) * (C1(x, y, z))); } else { // Second order discretization of C in x - ddx_C = (C2(x+1,y,z) - C2(x-1,y,z)) / (2.*coords->dx(x,y)*(C1(x,y,z))); + ddx_C = (C2(x + 1, y, z) - C2(x - 1, y, z)) + / (2. * coords->dx(x, y, z) * (C1(x, y, z))); // Second order discretization of C in z - ddz_C = (C2(x,y,zp) - C2(x,y,zm)) / (2.*coords->dz*(C1(x,y,z))); + ddz_C = (C2(x, y, zp) - C2(x, y, zm)) + / (2. * coords->dz(x, y, z) * (C1(x, y, z))); } - coef4 += coords->g11(x,y) * ddx_C + coords->g13(x,y) * ddz_C; - coef5 += coords->g13(x,y) * ddx_C + coords->g33(x,y) * ddz_C; + coef4 += coords->g11(x, y, z) * ddx_C + coords->g13(x, y, z) * ddz_C; + coef5 += coords->g13(x, y, z) * ddx_C + coords->g33(x, y, z) * ddz_C; } } diff --git a/src/invert/laplace/impls/petsc3damg/petsc3damg.cxx b/src/invert/laplace/impls/petsc3damg/petsc3damg.cxx index 29a673ded6..404b8fc3fd 100644 --- a/src/invert/laplace/impls/petsc3damg/petsc3damg.cxx +++ b/src/invert/laplace/impls/petsc3damg/petsc3damg.cxx @@ -302,8 +302,8 @@ void LaplacePetsc3dAmg::updateMatrix3D() { const Field3D dc_dx = issetC ? DDX(C2) : Field3D(); const Field3D dc_dy = issetC ? DDY(C2) : Field3D(); const Field3D dc_dz = issetC ? DDZ(C2) : Field3D(); - const Field2D dJ_dy = DDY(coords->J/coords->g_22); - + const auto dJ_dy = DDY(coords->J / coords->g_22); + // Set up the matrix for the internal points on the grid. // Boundary conditions were set in the constructor. BOUT_FOR_SERIAL(l, indexer->getRegionNobndry()) { @@ -346,13 +346,13 @@ void LaplacePetsc3dAmg::updateMatrix3D() { C_df_dx += C_d2f_dx2 * coords->d1_dx[l]; } C_df_dx /= 2 * coords->dx[l]; - C_df_dz /= 2 * coords->dz; + C_df_dz /= 2 * coords->dz[l]; C_d2f_dx2 /= SQ(coords->dx[l]); C_d2f_dy2 /= SQ(coords->dy[l]); - C_d2f_dz2 /= SQ(coords->dz); + C_d2f_dz2 /= SQ(coords->dz[l]); - C_d2f_dxdz /= 4 * coords->dx[l] * coords->dz; + C_d2f_dxdz /= 4 * coords->dx[l] * coords->dz[l]; operator3D(l, l) = -2 * (C_d2f_dx2 + C_d2f_dy2 + C_d2f_dz2) + A[l]; operator3D(l, l.xp()) = C_df_dx + C_d2f_dx2; @@ -414,7 +414,7 @@ void LaplacePetsc3dAmg::updateMatrix3D() { C_d2f_dxdy /= 4*coords->dx[l]; // NOTE: This value is not completed here. It needs to // be divide by dx(i +/- 1, j, k) when using to set a // matrix element - C_d2f_dydz /= 4*coords->dy[l]*coords->dz; + C_d2f_dydz /= 4 * coords->dy[l] * coords->dz[l]; // The values stored in the y-boundary are already interpolated // up/down, so we don't want the matrix to do any such diff --git a/src/invert/laplace/impls/serial_band/serial_band.cxx b/src/invert/laplace/impls/serial_band/serial_band.cxx index 32ba521b6f..66ccc15ecb 100644 --- a/src/invert/laplace/impls/serial_band/serial_band.cxx +++ b/src/invert/laplace/impls/serial_band/serial_band.cxx @@ -24,16 +24,19 @@ * **************************************************************************/ -#include #include "serial_band.hxx" +#include "bout/build_config.hxx" + +#if not BOUT_USE_METRIC_3D +#include #include -#include -#include +#include #include +#include +#include #include -#include -#include +#include #include @@ -117,15 +120,15 @@ FieldPerp LaplaceSerialBand::solve(const FieldPerp& b, const FieldPerp& x0) { xend = localmesh->LocalNx-2; } + const auto kwave_fac = TWOPI / coords->zlength(); // wave number is 1/[rad] for(int iz=0;iz<=maxmode;iz++) { // solve differential equation in x - - BoutReal coef1=0.0, coef2=0.0, coef3=0.0, coef4=0.0, - coef5=0.0, coef6=0.0, kwave; + + BoutReal coef1 = 0.0, coef2 = 0.0, coef3 = 0.0, coef4 = 0.0, coef5 = 0.0, coef6 = 0.0; ///////// PERFORM INVERSION ///////// // shift freqs according to FFT convention - kwave=iz*2.0*PI/coords->zlength(); // wave number is 1/[rad] + const Field2D kwave_ = iz * kwave_fac; // set bk1d for(int ix=0;ixLocalNx;ix++) @@ -134,6 +137,7 @@ FieldPerp LaplaceSerialBand::solve(const FieldPerp& b, const FieldPerp& x0) { // Fill in interior points for(int ix=xstart;ix<=xend;ix++) { + BoutReal kwave = kwave_(ix, jy); #ifdef SECONDORDER // Use second-order differencing. Useful for testing the tridiagonal solver // with different boundary conditions @@ -195,6 +199,7 @@ FieldPerp LaplaceSerialBand::solve(const FieldPerp& b, const FieldPerp& x0) { int ix = 1; + auto kwave = kwave_(ix, jy); coef1=coords->g11(ix,jy)/(SQ(coords->dx(ix,jy))); coef2=coords->g33(ix,jy); coef3= kwave * coords->g13(ix,jy)/(2. * coords->dx(ix,jy)); @@ -315,7 +320,8 @@ FieldPerp LaplaceSerialBand::solve(const FieldPerp& b, const FieldPerp& x0) { // Enforce zero laplacian for 2nd and 4th-order int ix = 1; - + + auto kwave = kwave_(ix, jy); coef1=coords->g11(ix,jy)/(12.* SQ(coords->dx(ix,jy))); coef2=coords->g33(ix,jy); @@ -361,7 +367,8 @@ FieldPerp LaplaceSerialBand::solve(const FieldPerp& b, const FieldPerp& x0) { coef1=coords->g11(ix,jy)/(12.* SQ(coords->dx(ix,jy))); coef2=coords->g33(ix,jy); - + + auto kwave = kwave_(ix, jy); coef3= kwave * coords->g13(ix,jy)/(2. * coords->dx(ix,jy)); coef4 = Acoef(ix,jy); @@ -420,6 +427,7 @@ FieldPerp LaplaceSerialBand::solve(const FieldPerp& b, const FieldPerp& x0) { } checkData(x); - return x; } + +#endif // BOUT_USE_METRIC_3D diff --git a/src/invert/laplace/impls/serial_band/serial_band.hxx b/src/invert/laplace/impls/serial_band/serial_band.hxx index 99842f3e27..a856a0566f 100644 --- a/src/invert/laplace/impls/serial_band/serial_band.hxx +++ b/src/invert/laplace/impls/serial_band/serial_band.hxx @@ -29,7 +29,18 @@ class LaplaceSerialBand; #ifndef __SERIAL_BAND_H__ #define __SERIAL_BAND_H__ -#include +#include "invert_laplace.hxx" +#include "bout/build_config.hxx" + +#if BOUT_USE_METRIC_3D + +namespace { +RegisterUnavailableLaplace + registerlaplaceserialband(LAPLACE_BAND, "BOUT++ was configured with 3D metrics"); +} + +#else + #include #include #include @@ -80,4 +91,6 @@ private: Array bk1d, xk1d; }; +#endif // BOUT_USE_METRIC_3D + #endif // __SERIAL_BAND_H__ diff --git a/src/invert/laplace/impls/serial_tri/serial_tri.cxx b/src/invert/laplace/impls/serial_tri/serial_tri.cxx index 444c4f40bc..b3e02659a6 100644 --- a/src/invert/laplace/impls/serial_tri/serial_tri.cxx +++ b/src/invert/laplace/impls/serial_tri/serial_tri.cxx @@ -83,7 +83,7 @@ FieldPerp LaplaceSerialTri::solve(const FieldPerp& b, const FieldPerp& x0) { int ncz = localmesh->LocalNz; // No of z pnts int ncx = localmesh->LocalNx; // No of x pnts - BoutReal kwaveFactor = 2.0 * PI / coords->zlength(); + BoutReal kwaveFactor = 2.0 * PI / getUniform(coords->zlength()); // Setting the width of the boundary. // NOTE: The default is a width of 2 guard cells diff --git a/src/invert/laplace/impls/spt/spt.cxx b/src/invert/laplace/impls/spt/spt.cxx index 2fac129057..6b39e1da44 100644 --- a/src/invert/laplace/impls/spt/spt.cxx +++ b/src/invert/laplace/impls/spt/spt.cxx @@ -293,7 +293,7 @@ int LaplaceSPT::start(const FieldPerp &b, SPT_data &data) { data.bk(kz, ix) = dc1d[kz]; } - BoutReal kwaveFactor = 2.0 * PI / coords->zlength(); + BoutReal kwaveFactor = 2.0 * PI / getUniform(coords->zlength()); /// Set matrix elements for (int kz = 0; kz <= maxmode; kz++) { diff --git a/src/invert/laplace/invert_laplace.cxx b/src/invert/laplace/invert_laplace.cxx index 213ffe6c38..2836158598 100644 --- a/src/invert/laplace/invert_laplace.cxx +++ b/src/invert/laplace/invert_laplace.cxx @@ -260,14 +260,21 @@ void Laplacian::tridagCoefs(int jx, int jy, int jz, ASSERT1(ccoef == nullptr || ccoef->getLocation() == loc); ASSERT1(d == nullptr || d->getLocation() == loc); - - BoutReal kwave=jz*2.0*PI/coords->zlength(); // wave number is 1/[rad] + BoutReal kwave = jz * 2.0 * PI / coords->zlength()(jx, jy); // wave number is 1/[rad] tridagCoefs(jx, jy, kwave, a, b, c, ccoef, d, loc); } +#if BOUT_USE_METRIC_3D +void Laplacian::tridagCoefs(int /* jx */, int /* jy */, BoutReal /* kwave */, + dcomplex& /* a */, dcomplex& /* b */, dcomplex& /* c */, + const Field2D* /* c1coef */, const Field2D* /* c2coef */, + const Field2D* /* d */, CELL_LOC /* loc */) { + throw BoutException("Laplacian::tridagCoefs() does not support 3d metrics."); +} +#else void Laplacian::tridagCoefs(int jx, int jy, BoutReal kwave, dcomplex &a, dcomplex &b, dcomplex &c, const Field2D *c1coef, const Field2D *c2coef, @@ -363,6 +370,7 @@ void Laplacian::tridagCoefs(int jx, int jy, BoutReal kwave, b = dcomplex(-2.0*coef1 - SQ(kwave)*coef2,kwave*coef5); c = dcomplex(coef1 + coef4,kwave*coef3); } +#endif /*! * Set the matrix components of A in Ax=b @@ -401,12 +409,26 @@ void Laplacian::tridagCoefs(int jx, int jy, BoutReal kwave, * \param[out] cvec The upper diagonal. * DO NOT CONFUSE WITH "C" (called ccoef here) */ +#if BOUT_USE_METRIC_3D +void Laplacian::tridagMatrix(dcomplex* /*avec*/, dcomplex* /*bvec*/, dcomplex* /*cvec*/, + dcomplex* /*bk*/, int /*jy*/, int /*kz*/, BoutReal /*kwave*/, + int /*global_flags*/, int /*inner_boundary_flags*/, + int /*outer_boundary_flags*/, const Field2D* /*a*/, + const Field2D* /*c1coef*/, const Field2D* /*c2coef*/, + const Field2D* /*d*/, bool /*includeguards*/) { + throw BoutException("Error: tridagMatrix does not yet work with 3D metric."); +} +#else void Laplacian::tridagMatrix(dcomplex *avec, dcomplex *bvec, dcomplex *cvec, dcomplex *bk, int jy, int kz, BoutReal kwave, int global_flags, int inner_boundary_flags, int outer_boundary_flags, const Field2D *a, const Field2D *c1coef, const Field2D *c2coef, const Field2D *d, bool includeguards) { + ASSERT1(a->getLocation() == location); + ASSERT1(c1coef->getLocation() == location); + ASSERT1(c2coef->getLocation() == location); + ASSERT1(d->getLocation() == location); // Better have either both or neither C coefficients ASSERT3((c1coef == nullptr and c2coef == nullptr) @@ -723,6 +745,7 @@ void Laplacian::tridagMatrix(dcomplex *avec, dcomplex *bvec, dcomplex *cvec, } } } +#endif /********************************************************************************** * LEGACY INTERFACE diff --git a/src/invert/laplacexy/laplacexy.cxx b/src/invert/laplacexy/laplacexy.cxx index 380ce4b34c..c194721108 100644 --- a/src/invert/laplacexy/laplacexy.cxx +++ b/src/invert/laplacexy/laplacexy.cxx @@ -896,6 +896,13 @@ void LaplaceXY::setMatrixElementsFiniteVolume(const Field2D &A, const Field2D &B // (1/J) d/dx ( J * g11 d/dx ) + (1/J) d/dy ( J * g22 d/dy ) auto coords = localmesh->getCoordinates(location); + const Field2D J_DC = DC(coords->J); + const Field2D g11_DC = DC(coords->g11); + const Field2D dx_DC = DC(coords->dx); + const Field2D dy_DC = DC(coords->dy); + const Field2D g_22_DC = DC(coords->g_22); + const Field2D g_23_DC = DC(coords->g_23); + const Field2D g23_DC = DC(coords->g23); for(int x=localmesh->xstart; x <= localmesh->xend; x++) { for(int y=localmesh->ystart;y<=localmesh->yend;y++) { @@ -905,22 +912,22 @@ void LaplaceXY::setMatrixElementsFiniteVolume(const Field2D &A, const Field2D &B // XX component // Metrics on x+1/2 boundary - BoutReal J = 0.5*(coords->J(x,y) + coords->J(x+1,y)); - BoutReal g11 = 0.5*(coords->g11(x,y) + coords->g11(x+1,y)); - BoutReal dx = 0.5*(coords->dx(x,y) + coords->dx(x+1,y)); + BoutReal J = 0.5 * (J_DC(x, y) + J_DC(x + 1, y)); + BoutReal g11 = 0.5 * (g11_DC(x, y) + g11_DC(x + 1, y)); + BoutReal dx = 0.5 * (dx_DC(x, y) + dx_DC(x + 1, y)); BoutReal Acoef = 0.5*(A(x,y) + A(x+1,y)); - BoutReal val = Acoef * J * g11 / (coords->J(x,y) * dx * coords->dx(x,y)); + BoutReal val = Acoef * J * g11 / (J_DC(x, y) * dx * dx_DC(x, y)); xp = val; c = -val; // Metrics on x-1/2 boundary - J = 0.5*(coords->J(x,y) + coords->J(x-1,y)); - g11 = 0.5*(coords->g11(x,y) + coords->g11(x-1,y)); - dx = 0.5*(coords->dx(x,y) + coords->dx(x-1,y)); + J = 0.5 * (J_DC(x, y) + J_DC(x - 1, y)); + g11 = 0.5 * (g11_DC(x, y) + g11_DC(x - 1, y)); + dx = 0.5 * (dx_DC(x, y) + dx_DC(x - 1, y)); Acoef = 0.5*(A(x,y) + A(x-1,y)); - val = Acoef * J * g11 / (coords->J(x,y) * dx * coords->dx(x,y)); + val = Acoef * J * g11 / (J_DC(x, y) * dx * dx_DC(x, y)); xm = val; c -= val; @@ -934,26 +941,26 @@ void LaplaceXY::setMatrixElementsFiniteVolume(const Field2D &A, const Field2D &B if( include_y_derivs ) { // YY component // Metrics at y+1/2 - J = 0.5*(coords->J(x,y) + coords->J(x,y+1)); - BoutReal g_22 = 0.5*(coords->g_22(x,y) + coords->g_22(x,y+1)); - BoutReal g23 = 0.5*(coords->g23(x,y) + coords->g23(x,y+1)); - BoutReal g_23 = 0.5*(coords->g_23(x,y) + coords->g_23(x,y+1)); - BoutReal dy = 0.5*(coords->dy(x,y) + coords->dy(x,y+1)); + J = 0.5 * (J_DC(x, y) + J_DC(x, y + 1)); + BoutReal g_22 = 0.5 * (g_22_DC(x, y) + g_22_DC(x, y + 1)); + BoutReal g23 = 0.5 * (g23_DC(x, y) + g23_DC(x, y + 1)); + BoutReal g_23 = 0.5 * (g_23_DC(x, y) + g_23_DC(x, y + 1)); + BoutReal dy = 0.5 * (dy_DC(x, y) + dy_DC(x, y + 1)); Acoef = 0.5*(A(x,y+1) + A(x,y)); - val = -Acoef * J * g23 * g_23 / (g_22 * coords->J(x,y) * dy * coords->dy(x,y)); + val = -Acoef * J * g23 * g_23 / (g_22 * J_DC(x, y) * dy * dy_DC(x, y)); yp = val; c -= val; // Metrics at y-1/2 - J = 0.5*(coords->J(x,y) + coords->J(x,y-1)); - g_22 = 0.5*(coords->g_22(x,y) + coords->g_22(x,y-1)); - g23 = 0.5*(coords->g23(x,y) + coords->g23(x,y-1)); - g_23 = 0.5*(coords->g_23(x,y) + coords->g_23(x,y-1)); - dy = 0.5*(coords->dy(x,y) + coords->dy(x,y-1)); + J = 0.5 * (J_DC(x, y) + J_DC(x, y - 1)); + g_22 = 0.5 * (g_22_DC(x, y) + g_22_DC(x, y - 1)); + g23 = 0.5 * (g23_DC(x, y) + g23_DC(x, y - 1)); + g_23 = 0.5 * (g_23_DC(x, y) + g_23_DC(x, y - 1)); + dy = 0.5 * (dy_DC(x, y) + dy_DC(x, y - 1)); Acoef = 0.5*(A(x,y-1) + A(x,y)); - val = -Acoef * J * g23 * g_23 / (g_22 * coords->J(x,y) * dy * coords->dy(x,y)); + val = -Acoef * J * g23 * g_23 / (g_22 * J_DC(x, y) * dy * dy_DC(x, y)); ym = val; c -= val; } @@ -999,33 +1006,44 @@ void LaplaceXY::setMatrixElementsFiniteDifference(const Field2D &A, const Field2 // + B*f auto coords = localmesh->getCoordinates(location); - - Field2D coef_dfdy = coords->G2 - DDY(coords->J/coords->g_22)/coords->J; + const Field2D G1_2D = DC(coords->G1); + const Field2D G2_2D = DC(coords->G2); + const Field2D J_2D = DC(coords->J); + const Field2D g11_2D = DC(coords->g11); + const Field2D g_22_2D = DC(coords->g_22); + const Field2D g22_2D = DC(coords->g22); + const Field2D g12_2D = DC(coords->g12); + const Field2D d1_dx_2D = DC(coords->d1_dx); + const Field2D d1_dy_2D = DC(coords->d1_dy); + const Field2D dx_2D = DC(coords->dx); + const Field2D dy_2D = DC(coords->dy); + + const Field2D coef_dfdy = G2_2D - DC(DDY(J_2D / g_22_2D) / J_2D); for(int x = localmesh->xstart; x <= localmesh->xend; x++) { for(int y = localmesh->ystart; y <= localmesh->yend; y++) { // stencil entries PetscScalar c, xm, xp, ym, yp, xpyp, xpym, xmyp, xmym; - BoutReal dx = coords->dx(x,y); + BoutReal dx = dx_2D(x, y); // A*G1*dfdx - BoutReal val = A(x, y)*coords->G1(x, y)/(2.*dx); + BoutReal val = A(x, y) * G1_2D(x, y) / (2. * dx); xp = val; xm = -val; // A*g11*d2fdx2 - val = A(x, y)*coords->g11(x, y)/SQ(dx); + val = A(x, y) * g11_2D(x, y) / SQ(dx); xp += val; c = -2.*val; xm += val; // Non-uniform grid correction - val = A(x, y)*coords->g11(x, y)*coords->d1_dx(x, y)/(2.*dx); + val = A(x, y) * g11_2D(x, y) * d1_dx_2D(x, y) / (2. * dx); xp += val; xm -= val; // g11*dAdx*dfdx - val = coords->g11(x, y)*(A(x+1, y) - A(x-1, y))/(4.*SQ(dx)); + val = g11_2D(x, y) * (A(x + 1, y) - A(x - 1, y)) / (4. * SQ(dx)); xp += val; xm -= val; @@ -1038,7 +1056,7 @@ void LaplaceXY::setMatrixElementsFiniteDifference(const Field2D &A, const Field2 ccoef(y - localmesh->ystart, x - xstart) = xp; if(include_y_derivs) { - BoutReal dy = coords->dy(x,y); + BoutReal dy = dy_2D(x, y); BoutReal dAdx = (A(x+1, y) - A(x-1, y))/(2.*dx); BoutReal dAdy = (A(x, y+1) - A(x, y-1))/(2.*dy); @@ -1048,33 +1066,32 @@ void LaplaceXY::setMatrixElementsFiniteDifference(const Field2D &A, const Field2 ym = -val; // A*(g22-1/g_22)*d2fdy2 - val = A(x, y)*(coords->g22(x, y) - 1./coords->g_22(x,y))/SQ(dy); + val = A(x, y) * (g22_2D(x, y) - 1. / g_22_2D(x, y)) / SQ(dy); yp += val; c -= 2.*val; ym += val; // Non-uniform mesh correction - val = A(x, y)*(coords->g22(x, y) - 1./coords->g_22(x,y)) - *coords->d1_dy(x, y)/(2.*dy); + val = A(x, y) * (g22_2D(x, y) - 1. / g_22_2D(x, y)) * d1_dy_2D(x, y) / (2. * dy); yp += val; ym -= val; // 2*A*g12*d2dfdxdy - val = A(x, y)*coords->g12(x, y)/(2.*dx*dy); + val = A(x, y) * g12_2D(x, y) / (2. * dx * dy); xpyp = val; xpym = -val; xmyp = -val; xmym = val; // g22*dAdy*dfdy - val = (coords->g22(x, y) - 1./coords->g_22(x,y))*dAdy/(2.*dy); + val = (g22_2D(x, y) - 1. / g_22_2D(x, y)) * dAdy / (2. * dy); yp += val; ym -= val; // g12*(dAdx*dfdy + dAdy*dfdx) - val = coords->g12(x, y)*dAdx/(2.*dy); + val = g12_2D(x, y) * dAdx / (2. * dy); yp += val; ym -= val; - val = coords->g12(x, y)*dAdy/(2.*dx); + val = g12_2D(x, y) * dAdy / (2. * dx); xp += val; xm -= val; } diff --git a/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx b/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx index 72c63e5fc5..e8c925fcf2 100644 --- a/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx +++ b/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx @@ -1,4 +1,7 @@ #include "laplacexz-cyclic.hxx" +#include "bout/build_config.hxx" + +#if not BOUT_USE_METRIC_3D #include #include @@ -9,7 +12,6 @@ #include LaplaceXZcyclic::LaplaceXZcyclic(Mesh *m, Options *options, const CELL_LOC loc) : LaplaceXZ(m, options, loc) { - // Number of Z Fourier modes, including DC nmode = (m->LocalNz) / 2 + 1; @@ -72,9 +74,10 @@ void LaplaceXZcyclic::setCoefs(const Field2D &A2D, const Field2D &B2D) { ASSERT2(max(abs(coord->g13)) < 1e-5); int ind = 0; + const BoutReal zlength = getUniform(coord->zlength()); for(int y=localmesh->ystart; y <= localmesh->yend; y++) { for(int kz = 0; kz < nmode; kz++) { - BoutReal kwave=kz*2.0*PI/(coord->zlength()); + BoutReal kwave = kz * 2.0 * PI / zlength; if(localmesh->firstX()) { // Inner X boundary @@ -267,3 +270,5 @@ Field3D LaplaceXZcyclic::solve(const Field3D &rhs, const Field3D &x0) { return result; } + +#endif // BOUT_USE_METRIC_3D diff --git a/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.hxx b/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.hxx index 5a66afa11e..0b6a24156b 100644 --- a/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.hxx +++ b/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.hxx @@ -1,4 +1,18 @@ -#include +#ifndef LAPLACEXZ_CYCLIC_HXX +#define LAPLACEXZ_CYCLIC_HXX + +#include "bout/build_config.hxx" +#include "bout/invert/laplacexz.hxx" + +#if BOUT_USE_METRIC_3D + +namespace { +RegisterUnavailableLaplaceXZ registerlaplacexzcylic{ + "cyclic", "BOUT++ was configured with 3D metrics"}; +} // namespace + +#else + #include #include #include @@ -29,3 +43,7 @@ private: namespace { RegisterLaplaceXZ registerlaplacexzcylic{"cyclic"}; } // namespace + +#endif // BOUT_USE_METRIC_3D + +#endif // LAPLACEXZ_CYCLIC_HXX diff --git a/src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx b/src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx index 641d825b7e..162daae707 100644 --- a/src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx +++ b/src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx @@ -146,7 +146,7 @@ LaplaceXZpetsc::LaplaceXZpetsc(Mesh *m, Options *opt, const CELL_LOC loc) // Get KSP Solver Type const std::string ksptype = (*opt)["ksptype"].doc("KSP solver type").withDefault("gmres"); - + // Get PC type const std::string pctype = (*opt)["pctype"].doc("Preconditioner type").withDefault("none"); @@ -191,9 +191,9 @@ LaplaceXZpetsc::LaplaceXZpetsc(Mesh *m, Options *opt, const CELL_LOC loc) for (int i=0;igetCoordinates(location); - // NOTE: For now the X-Z terms are omitted, so check that they are small - ASSERT2(max(abs(coords->g13)) < 1e-5); - for(int x=localmesh->xstart; x <= localmesh->xend; x++) { for(int z=0; z < localmesh->LocalNz; z++) { // stencil entries PetscScalar c, xm, xp, zm, zp; + // Diagonal entries + PetscScalar xpzp{0.0}, xpzm{0.0}, xmzp{0.0}, xmzm{0.0}; // XX component + { + // Metrics on x+1/2 boundary + const BoutReal J = 0.5 * (coords->J(x, y, z) + coords->J(x + 1, y, z)); + const BoutReal g11 = 0.5 * (coords->g11(x, y, z) + coords->g11(x + 1, y, z)); + const BoutReal dx = 0.5 * (coords->dx(x, y, z) + coords->dx(x + 1, y, z)); + const BoutReal Acoef = 0.5 * (A(x, y, z) + A(x + 1, y, z)); + + const BoutReal val = + Acoef * J * g11 / (coords->J(x, y, z) * dx * coords->dx(x, y, z)); + xp = val; + c = -val; + } - // Metrics on x+1/2 boundary - BoutReal J = 0.5*(coords->J(x,y) + coords->J(x+1,y)); - BoutReal g11 = 0.5*(coords->g11(x,y) + coords->g11(x+1,y)); - BoutReal dx = 0.5*(coords->dx(x,y) + coords->dx(x+1,y)); - BoutReal Acoef = 0.5*(A(x,y,z) + A(x+1,y,z)); - - BoutReal val = Acoef * J * g11 / (coords->J(x,y) * dx * coords->dx(x,y)); - xp = val; - c = -val; - - // Metrics on x-1/2 boundary - J = 0.5*(coords->J(x,y) + coords->J(x-1,y)); - g11 = 0.5*(coords->g11(x,y) + coords->g11(x-1,y)); - dx = 0.5*(coords->dx(x,y) + coords->dx(x-1,y)); - Acoef = 0.5*(A(x,y,z) + A(x-1,y,z)); - - val = Acoef * J * g11 / (coords->J(x,y) * dx * coords->dx(x,y)); - xm = val; - c -= val; + { + // Metrics on x-1/2 boundary + const BoutReal J = 0.5 * (coords->J(x, y, z) + coords->J(x - 1, y, z)); + const BoutReal g11 = 0.5 * (coords->g11(x, y, z) + coords->g11(x - 1, y, z)); + const BoutReal dx = 0.5 * (coords->dx(x, y, z) + coords->dx(x - 1, y, z)); + const BoutReal Acoef = 0.5 * (A(x, y, z) + A(x - 1, y, z)); + + const BoutReal val = + Acoef * J * g11 / (coords->J(x, y, z) * dx * coords->dx(x, y, z)); + xm = val; + c -= val; + } // ZZ component - // Note that because metrics are constant in Z many terms cancel - // Wrap around z-1 and z+1 indices - int zminus = (z - 1 + (localmesh->LocalNz)) % (localmesh->LocalNz); - int zplus = (z + 1) % (localmesh->LocalNz); - - // Metrics on z+1/2 boundary - Acoef = 0.5*(A(x,y,z) + A(x,y,zplus)); - - val = Acoef * coords->g33(x,y) / (coords->dz*coords->dz); - zp = val; - c -= val; + const int zminus = (z - 1 + (localmesh->LocalNz)) % (localmesh->LocalNz); + const int zplus = (z + 1) % (localmesh->LocalNz); + + { + const BoutReal J = 0.5 * (coords->J(x, y, z) + coords->J(x, y, zplus)); + const BoutReal g33 = 0.5 * (coords->g33(x, y, z) + coords->g33(x, y, zplus)); + const BoutReal dz = 0.5 * (coords->dz(x, y, z) + coords->dz(x, y, zplus)); + // Metrics on z+1/2 boundary + const BoutReal Acoef = 0.5 * (A(x, y, z) + A(x, y, zplus)); + + const BoutReal val = Acoef * J * g33 / (coords->J(x, y, z) * dz * dz); + zp = val; + c -= val; + } + { + // Metrics on z-1/2 boundary + const BoutReal J = 0.5 * (coords->J(x, y, z) + coords->J(x, y, zminus)); + const BoutReal g33 = 0.5 * (coords->g33(x, y, z) + coords->g33(x, y, zminus)); + const BoutReal Acoef = 0.5 * (A(x, y, z) + A(x, y, zminus)); + const BoutReal dz = 0.5 * (coords->dz(x, y, z) + coords->dz(x, y, zminus)); + + const BoutReal val = Acoef * J * g33 / (coords->J(x, y, z) * dz * dz); + zm = val; + c -= val; + } - // Metrics on z-1/2 boundary - Acoef = 0.5*(A(x,y,z) + A(x,y,zminus)); + // XZ components + // (1/J) d/dx ( A * J * g13 d/dz ) + { + // + // x+1/2 + // + // --o------x-- + // | | z + // | c -> flux ^ + // | | | + // --o------x-- -> x + // + // Taking derivative in z (between corners marked x) + // so metrics at (x+1/2,z) + + // Metrics + const BoutReal J = 0.5 * (coords->J(x, y, z) + coords->J(x + 1, y, z)); + const BoutReal g13 = 0.5 * (coords->g13(x, y, z) + coords->g13(x + 1, y, z)); + const BoutReal dz = 0.5 * (coords->dz(x, y, z) + coords->dz(x + 1, y, z)); + const BoutReal Acoef = 0.5 * (A(x, y, z) + A(x + 1, y, z)); + + const BoutReal val = + Acoef * J * g13 / (coords->J(x, y, z) * dz * coords->dx(x, y, z)); + + // This val coefficient is multiplied by the (x+1/2,z+1/2) corner + // and (x+1/2,z-1/2) corner + + // (x+1/2,z+1/2) + //xp += 0.25 * val; Note cancels + xpzp += 0.25 * val; + zp += 0.25 * val; + //c += 0.25 * val; Note cancels + + // (x+1/2,z-1/2) + //xp -= 0.25 * val; Note cancels + xpzm -= 0.25 * val; + zm -= 0.25 * val; + //c -= 0.25 * val; Note cancels + } + { + // + // x-1/2 + // + // --x------o-- + // | | z + // flux -> c | ^ + // | | | + // --x------o-- -> x + // + // Taking derivative in z (between corners marked x) + // so metrics at (x-1/2,z) + + const BoutReal J = 0.5 * (coords->J(x, y, z) + coords->J(x - 1, y, z)); + const BoutReal g13 = 0.5 * (coords->g13(x, y, z) + coords->g13(x - 1, y, z)); + const BoutReal dz = 0.5 * (coords->dz(x, y, z) + coords->dz(x - 1, y, z)); + const BoutReal Acoef = 0.5 * (A(x, y, z) + A(x - 1, y, z)); + + const BoutReal val = + -Acoef * J * g13 / (coords->J(x, y, z) * dz * coords->dx(x, y, z)); + + // (x+1/2,z+1/2) + xpzp += 0.25 * val; + zp += 0.25 * val; + + // (x+1/2,z-1/2) + xpzm -= 0.25 * val; + zm -= 0.25 * val; + } - val = Acoef * coords->g33(x,y) / (coords->dz*coords->dz); - zm = val; - c -= val; + // ZX components + // (1/J) d/dz ( A * J * g13 d/dx ) + { + // z+1/2 + // + // flux + // ^ + // --x---|--x-- + // | | z + // | c | ^ + // | | | + // --o------o-- -> x + // + // Taking derivative in x (between corners marked x) + // so metrics at (x,z+1/2) + + const BoutReal J = 0.5 * (coords->J(x, y, z) + coords->J(x, y, zplus)); + const BoutReal g13 = 0.5 * (coords->g13(x, y, z) + coords->g13(x, y, zplus)); + const BoutReal dx = 0.5 * (coords->dx(x, y, z) + coords->dx(x, y, zplus)); + const BoutReal Acoef = 0.5 * (A(x, y, z) + A(x, y, zplus)); + + const BoutReal val = + Acoef * J * g13 / (coords->J(x, y, z) * dx * coords->dz(x, y, z)); + + // (x+1/2,z+1/2) + //zp += 0.25 * val; Note cancels + xpzp += 0.25 * val; + xp += 0.25 * val; + //c += 0.25 * val; Note cancels + + // (x-1/2,z+1/2) + //zp -= 0.25 * val; Note cancels + //c -= 0.25 * val; Note cancels + xm -= 0.25 * val; + xmzp -= 0.25 * val; + } + { + // z-1/2 + // + // --o------o-- + // | | z + // | c | ^ + // | ^ | | + // --x---|--x-- -> x + // flux + // + // Taking derivative in x (between corners marked x) + // so metrics at (x,z-1/2) + + const BoutReal J = 0.5 * (coords->J(x, y, z) + coords->J(x, y, zminus)); + const BoutReal g13 = 0.5 * (coords->g13(x, y, z) + coords->g13(x, y, zminus)); + const BoutReal dx = 0.5 * (coords->dx(x, y, z) + coords->dx(x, y, zminus)); + const BoutReal Acoef = 0.5 * (A(x, y, z) + A(x, y, zminus)); + + const BoutReal val = + -Acoef * J * g13 / (coords->J(x, y, z) * dx * coords->dz(x, y, z)); + + // (x+1/2,z-1/2) + xpzm += 0.25 * val; + xp += 0.25 * val; + + // (x-1/2,z+1/2) + xm -= 0.25 * val; + xmzm -= 0.25 * val; + } // B term c += B(x,y,z); ///////////////////////////////////////////////// - // Now have a 5-point stencil for the Laplacian + // Now have a 9-point stencil for the Laplacian + // + // o------o------o------o + // | xmzp | zp | xpzp | + // o------o------o------o + // | xm | c | xp | + // o------o------o------o + // | xmzm | zm | xpzm | + // o------o------o------o + // // Set the centre (diagonal) MatSetValues(it.MatA, 1, &row, 1, &row, &c, INSERT_VALUES); @@ -462,8 +616,17 @@ void LaplaceXZpetsc::setCoefs(const Field3D &Ain, const Field3D &Bin) { if(z == localmesh->LocalNz-1) { col -= localmesh->LocalNz; // Wrap around } + MatSetValues(it.MatA, 1, &row, 1, &col, &zp, INSERT_VALUES); + // X + 1, Z + 1 + const int xpzp_col = col + localmesh->LocalNz; + MatSetValues(it.MatA, 1, &row, 1, &xpzp_col, &xpzp, INSERT_VALUES); + + // X - 1, Z + 1 + const int xmzp_col = col - localmesh->LocalNz; + MatSetValues(it.MatA, 1, &row, 1, &xmzp_col, &xmzp, INSERT_VALUES); + // Z - 1 col = row - 1; if(z == 0) { @@ -471,6 +634,14 @@ void LaplaceXZpetsc::setCoefs(const Field3D &Ain, const Field3D &Bin) { } MatSetValues(it.MatA, 1, &row, 1, &col, &zm, INSERT_VALUES); + // X + 1, Z - 1 + const int xpzm_col = col + localmesh->LocalNz; + MatSetValues(it.MatA, 1, &row, 1, &xpzm_col, &xpzm, INSERT_VALUES); + + // X - 1, Z - 1 + const int xmzm_col = col - localmesh->LocalNz; + MatSetValues(it.MatA, 1, &row, 1, &xmzm_col, &xmzm, INSERT_VALUES); + row++; } } diff --git a/src/invert/parderiv/impls/cyclic/cyclic.cxx b/src/invert/parderiv/impls/cyclic/cyclic.cxx index 9634b511d9..3b27fee955 100644 --- a/src/invert/parderiv/impls/cyclic/cyclic.cxx +++ b/src/invert/parderiv/impls/cyclic/cyclic.cxx @@ -35,10 +35,14 @@ * ************************************************************************/ +#include "cyclic.hxx" +#include "bout/build_config.hxx" + +#if not BOUT_USE_METRIC_3D + #include #include #include -#include "cyclic.hxx" #include #include #include @@ -103,6 +107,7 @@ const Field3D InvertParCR::solve(const Field3D &f) { auto b = Matrix(nsys, size); auto c = Matrix(nsys, size); + const auto zlength = getUniform(coord->zlength()); // Loop over flux-surfaces for (surf.first(); !surf.isDone(); surf.next()) { int x = surf.xpos; @@ -142,7 +147,7 @@ const Field3D InvertParCR::solve(const Field3D &f) { // Set up tridiagonal system for (int k = 0; k < nsys; k++) { - BoutReal kwave=k*2.0*PI/coord->zlength(); // wave number is 1/length + BoutReal kwave = k * 2.0 * PI / zlength; // wave number is 1/length for (int y = 0; y < localmesh->LocalNy - localmesh->ystart - local_ystart; y++) { BoutReal acoef = A(x, y + local_ystart); // Constant @@ -177,16 +182,17 @@ const Field3D InvertParCR::solve(const Field3D &f) { int rank, np; bout::globals::mpi->MPI_Comm_rank(surf.communicator(), &rank); bout::globals::mpi->MPI_Comm_size(surf.communicator(), &np); + if (rank == 0) { for (int k = 0; k < nsys; k++) { - BoutReal kwave=k*2.0*PI/coord->zlength(); // wave number is 1/[rad] + BoutReal kwave = k * 2.0 * PI / zlength; // wave number is 1/[rad] dcomplex phase(cos(kwave*ts) , -sin(kwave*ts)); a(k, 0) *= phase; } } if (rank == np - 1) { for (int k = 0; k < nsys; k++) { - BoutReal kwave=k*2.0*PI/coord->zlength(); // wave number is 1/[rad] + BoutReal kwave = k * 2.0 * PI / zlength; // wave number is 1/[rad] dcomplex phase(cos(kwave*ts) , sin(kwave*ts)); c(k, localmesh->LocalNy - 2 * localmesh->ystart - 1) *= phase; } @@ -235,3 +241,4 @@ const Field3D InvertParCR::solve(const Field3D &f) { return fromFieldAligned(result, "RGN_NOBNDRY"); } +#endif // BOUT_USE_METRIC_3D diff --git a/src/invert/parderiv/impls/cyclic/cyclic.hxx b/src/invert/parderiv/impls/cyclic/cyclic.hxx index e838515b35..d7db822adb 100644 --- a/src/invert/parderiv/impls/cyclic/cyclic.hxx +++ b/src/invert/parderiv/impls/cyclic/cyclic.hxx @@ -43,6 +43,17 @@ #define __INV_PAR_CR_H__ #include "invert_parderiv.hxx" +#include "bout/build_config.hxx" + +#if BOUT_USE_METRIC_3D + +namespace { +RegisterUnavailableInvertPar registerinvertparcyclic{ + PARDERIVCYCLIC, "BOUT++ was configured with 3D metrics"}; +} + +#else + #include "dcomplex.hxx" #include #include "utils.hxx" @@ -97,4 +108,6 @@ namespace { RegisterInvertPar registerinvertparcyclic{PARDERIVCYCLIC}; } +#endif // BOUT_USE_METRIC_3D + #endif // __INV_PAR_CR_H__ diff --git a/src/mesh/boundary_factory.cxx b/src/mesh/boundary_factory.cxx index c55f2087c2..3d259d30c3 100644 --- a/src/mesh/boundary_factory.cxx +++ b/src/mesh/boundary_factory.cxx @@ -234,12 +234,20 @@ BoundaryOpBase* BoundaryFactory::createFromOptions(const string &varname, Bounda side = "yup"; break; } - case BNDRY_PAR_FWD: { - side = "par_yup"; + case BNDRY_PAR_FWD_XIN: { + side = "par_yup_xin"; break; } - case BNDRY_PAR_BKWD: { - side = "par_ydown"; + case BNDRY_PAR_FWD_XOUT: { + side = "par_yup_xout"; + break; + } + case BNDRY_PAR_BKWD_XIN: { + side = "par_ydown_xin"; + break; + } + case BNDRY_PAR_BKWD_XOUT: { + side = "par_ydown_xout"; break; } default: { diff --git a/src/mesh/boundary_standard.cxx b/src/mesh/boundary_standard.cxx index ec8df787b2..ad95a4ab6a 100644 --- a/src/mesh/boundary_standard.cxx +++ b/src/mesh/boundary_standard.cxx @@ -1551,6 +1551,7 @@ BoundaryOp* BoundaryNeumann_NonOrthogonal::clone(BoundaryRegion* region, } void BoundaryNeumann_NonOrthogonal::apply(Field2D& f) { +#if not(BOUT_USE_METRIC_3D) Mesh* mesh = bndry->localmesh; ASSERT1(mesh == f.getMesh()); Coordinates* metric = f.getCoordinates(); @@ -1598,6 +1599,10 @@ void BoundaryNeumann_NonOrthogonal::apply(Field2D& f) { } } } +#else + throw BoutException("Applying boundary condition 'neumann_4thorder' to Field2D not " + "compatible with 3D metrics in all cases."); +#endif } void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { @@ -1611,302 +1616,119 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { Field3D dfdz = DDZ(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)); - 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 +#if BOUT_USE_METRIC_3D for (int z = 0; z < mesh->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); - } - } 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; - } - } else { - // set corners to zero - f(bndry->x, bndry->y, z) = 0.0; - if (bndry->width == 2) { - f(bndry->x + bndry->bx, bndry->y + bndry->by, z) = 0.0; - } - } - } - } -} - -/////////////////////////////////////////////////////////////// - -BoundaryOp* BoundaryNeumann::clone(BoundaryRegion* region, - const std::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 / 3rd order) the value at the mid-point between - // the guard cell and the grid cell to be val - // N.B. First guard cells (closest to the grid) is 2nd order, while - // 2nd is 3rd order - - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); - 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 and (loc == CELL_XLOW or loc == CELL_YLOW)) { - // 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) { - val = fg->generate(Context(bndry, loc, t, mesh)) * 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; i < bndry->width; 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) { - val = fg->generate(Context(bndry, loc, t, mesh)) * 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; i < bndry->width; 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) { - val = fg->generate(Context(bndry, loc, t, mesh)); - } - - f(bndry->x, bndry->y) = - f(bndry->x - bndry->bx, bndry->y - bndry->by) + delta * val; +#else + int z = 0; +#endif + // Interpolate (linearly) metrics to halfway between last cell and boundary cell + BoutReal g11shift = 0.5 + * (metric->g11(bndry->x, bndry->y, z) + + metric->g11(bndry->x - bndry->bx, bndry->y, z)); + BoutReal g12shift = 0.5 + * (metric->g12(bndry->x, bndry->y, z) + + metric->g12(bndry->x - bndry->bx, bndry->y, z)); + BoutReal g13shift = 0.5 + * (metric->g13(bndry->x, bndry->y, z) + + metric->g13(bndry->x - bndry->bx, bndry->y, z)); + // 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 +#if not(BOUT_USE_METRIC_3D) + for (int z = 0; z < mesh->LocalNz; z++) { +#endif + 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, z); + 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 + 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) { - val = fg->generate(Context(bndry, loc, t, mesh)) * metric->dy(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; i < bndry->width; 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) { - val = fg->generate(Context(bndry, loc, t, mesh)) * metric->dy(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; i < bndry->width; 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) { - val = fg->generate(Context(bndry, loc, t, mesh)); + 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) { + // 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, z); + 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; } - - f(bndry->x, bndry->y) = - f(bndry->x - bndry->bx, bndry->y - bndry->by) + delta * val; + } else { + // set corners to zero + f(bndry->x, bndry->y, z) = 0.0; 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; + f(bndry->x + bndry->bx, bndry->y + bndry->by, z) = 0.0; } } } } - } 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) { - val = fg->generate(Context(bndry, loc, t, mesh)); - } + /////////////////////////////////////////////////////////////// - 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; - } + BoundaryOp* BoundaryNeumann::clone(BoundaryRegion * region, + const std::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(Field3D& f) { BoundaryNeumann::apply(f, 0.); } + void BoundaryNeumann::apply(Field2D & f) { BoundaryNeumann::apply(f, 0.); } -void BoundaryNeumann::apply(Field3D& f, BoutReal t) { - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); - Coordinates* metric = f.getCoordinates(); + void BoundaryNeumann::apply(Field2D & f, BoutReal t) { + // Set (at 2nd order / 3rd order) the value at the mid-point between + // the guard cell and the grid cell to be val + // N.B. First guard cells (closest to the grid) is 2nd order, while + // 2nd is 3rd order - bndry->first(); +#if not(BOUT_USE_METRIC_3D) + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); + Coordinates* metric = f.getCoordinates(); - // Decide which generator to use - std::shared_ptr fg = gen; - if (!fg) - fg = f.getBndryGenerator(bndry->location); + bndry->first(); - BoutReal val = 0.0; + // Decide which generator to use + std::shared_ptr fg = gen; + if (!fg) + fg = f.getBndryGenerator(bndry->location); - // Check for staggered grids + BoutReal val = 0.0; - 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 + // Check for staggered grids - if (loc == CELL_XLOW) { - // Field is shifted in X + CELL_LOC loc = f.getLocation(); + if (mesh->StaggerGrids and (loc == CELL_XLOW or loc == CELL_YLOW)) { + // 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 (bndry->bx > 0) { - // Outer x boundary - for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { if (fg) { - val = fg->generate(Context(bndry, zk, loc, t, mesh)) * metric->dx(bndry->x, bndry->y); + val = fg->generate(Context(bndry, loc, t, mesh)) + * 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.; - + 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; i < bndry->width; i++) { @@ -1915,24 +1737,24 @@ void BoundaryNeumann::apply(Field3D& f, BoutReal t) { // 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); + 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 - for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + if (bndry->bx < 0) { + // Inner x boundary. Set one point inwards + for (; !bndry->isDone(); bndry->next1d()) { + if (fg) { - val = fg->generate(Context(bndry, zk, loc, t, mesh)) * metric->dx(bndry->x - bndry->bx, bndry->y); + val = fg->generate(Context(bndry, loc, t, mesh)) + * metric->dx(bndry->x, 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) + 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 @@ -1943,46 +1765,46 @@ void BoundaryNeumann::apply(Field3D& f, BoutReal t) { // 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); + 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()) { - BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y) - + bndry->by * metric->dy(bndry->x, bndry->y); + 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); - for (int zk = 0; zk < mesh->LocalNz; zk++) { if (fg) { - val = fg->generate(Context(bndry, zk, loc, t, mesh)); + val = fg->generate(Context(bndry, loc, t, mesh)); } - f(bndry->x, bndry->y, zk) = - f(bndry->x - bndry->bx, bndry->y - bndry->by, zk) + delta * val; + + 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, zk) = - f(bndry->x - 2 * bndry->bx, bndry->y - 2 * bndry->by, zk) + 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) { - // Field is shifted in Y + } 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()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + if (bndry->by > 0) { + // Outer y boundary + + for (; !bndry->isDone(); bndry->next1d()) { if (fg) { - val = fg->generate(Context(bndry, zk, loc, t, mesh)) * metric->dy(bndry->x, bndry->y); + val = fg->generate(Context(bndry, loc, t, mesh)) + * 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.; + 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 @@ -1992,24 +1814,23 @@ void BoundaryNeumann::apply(Field3D& f, BoutReal t) { // 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); + 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()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + if (bndry->by < 0) { + // Inner y boundary. Set one point inwards + for (; !bndry->isDone(); bndry->next1d()) { + if (fg) { - val = fg->generate(Context(bndry, zk, loc, t, mesh)) * metric->dy(bndry->x, bndry->y - bndry->by); + val = fg->generate(Context(bndry, loc, t, mesh)) + * 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) + 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 @@ -2020,190 +1841,379 @@ void BoundaryNeumann::apply(Field3D& f, BoutReal t) { // 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); + 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()) { - BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y) - + bndry->by * metric->dy(bndry->x, bndry->y); + 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); - for (int zk = 0; zk < mesh->LocalNz; zk++) { if (fg) { - val = fg->generate(Context(bndry, zk, loc, t, mesh)); + val = fg->generate(Context(bndry, loc, t, mesh)); } - f(bndry->x, bndry->y, zk) = - f(bndry->x - bndry->bx, bndry->y - bndry->by, zk) + delta * val; + + 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, zk) = - f(bndry->x - 2 * bndry->bx, bndry->y - 2 * bndry->by, zk) + 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_ZLOW) { - // Shifted in Z - 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 - 0.5)/(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 { - throw BoutException("Unrecognized location"); - } - } else { - for (; !bndry->isDone(); bndry->next1d()) { - BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y) - + bndry->by * metric->dy(bndry->x, bndry->y); + // 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); - for (int zk = 0; zk < mesh->LocalNz; zk++) { if (fg) { - val = fg->generate(Context(bndry, zk, loc, t, mesh)); + val = fg->generate(Context(bndry, loc, t, mesh)); } - f(bndry->x, bndry->y, zk) = - f(bndry->x - bndry->bx, bndry->y - bndry->by, zk) + delta * val; + + 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, zk) = - f(bndry->x - 2 * bndry->bx, bndry->y - 2 * bndry->by, zk) - + 3.0 * delta * val; + 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 + throw BoutException("Applying boundary condition 'neumann' to Field2D " + "not compatible with 3D metrics in all cases."); +#endif } -} -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) { - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); - Field3D* dt = f.timeDeriv(); - for (bndry->first(); !bndry->isDone(); bndry->next()) - for (int z = 0; z < mesh->LocalNz; z++) - (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero -} + void BoundaryNeumann::apply(Field3D & f) { BoundaryNeumann::apply(f, 0.); } + + void BoundaryNeumann::apply(Field3D & f, BoutReal t) { + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); + 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()) { + for (int zk = 0; zk < mesh->LocalNz; zk++) { + if (fg) { + val = fg->generate(Context(bndry, zk, loc, t, mesh)) + * metric->dx(bndry->x, bndry->y, zk); + } + + 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; i < bndry->width; 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()) { + for (int zk = 0; zk < mesh->LocalNz; zk++) { + if (fg) { + val = fg->generate(Context(bndry, zk, loc, t, mesh)) + * metric->dx(bndry->x - bndry->bx, bndry->y, zk); + } + + 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; i < bndry->width; 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()) { +#if not(BOUT_USE_METRIC_3D) + BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y) + + bndry->by * metric->dy(bndry->x, bndry->y); +#endif + for (int zk = 0; zk < mesh->LocalNz; zk++) { +#if BOUT_USE_METRIC_3D + BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y, zk) + + bndry->by * metric->dy(bndry->x, bndry->y, zk); +#endif + if (fg) { + val = fg->generate(Context(bndry, zk, loc, t, mesh)); + } + 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()) { + for (int zk = 0; zk < mesh->LocalNz; zk++) { + if (fg) { + val = fg->generate(Context(bndry, zk, loc, t, mesh)) + * metric->dy(bndry->x, bndry->y, zk); + } + 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; i < bndry->width; 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()) { + for (int zk = 0; zk < mesh->LocalNz; zk++) { + if (fg) { + val = fg->generate(Context(bndry, zk, loc, t, mesh)) + * metric->dy(bndry->x, bndry->y - bndry->by, zk); + } + + 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; i < bndry->width; 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()) { +#if not(BOUT_USE_METRIC_3D) + int zk = 0; + BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y, zk) + + bndry->by * metric->dy(bndry->x, bndry->y, zk); +#endif + for (int zk = 0; zk < mesh->LocalNz; zk++) { +#if BOUT_USE_METRIC_3D + BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y, zk) + + bndry->by * metric->dy(bndry->x, bndry->y, zk); +#endif + if (fg) { + val = fg->generate(Context(bndry, zk, loc, t, mesh)); + } + 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_ZLOW) { + // Shifted in Z + 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 -BoundaryOp* BoundaryNeumann_O4::clone(BoundaryRegion* region, - const std::list& args) { - std::shared_ptr newgen = nullptr; - if (!args.empty()) { - // First argument should be an expression - newgen = FieldFactory::get()->parse(args.front()); + for (int zk = 0; zk < mesh->LocalNz; zk++) { + BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y, zk) + + bndry->by * metric->dy(bndry->x, bndry->y, zk); + if (fg) { + val = fg->generate(xnorm, TWOPI * ynorm, + TWOPI * (zk - 0.5) / (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 { + throw BoutException("Unrecognized location"); + } + } else { + for (; !bndry->isDone(); bndry->next1d()) { +#if BOUT_USE_METRIC_3D + for (int zk = 0; zk < mesh->LocalNz; zk++) { + BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y, zk) + + bndry->by * metric->dy(bndry->x, bndry->y, zk); +#else + BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y) + + bndry->by * metric->dy(bndry->x, bndry->y); + for (int zk = 0; zk < mesh->LocalNz; zk++) { +#endif + if (fg) { + val = fg->generate(Context(bndry, zk, loc, t, mesh)); + } + 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; + } + } + } + } } - 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) { - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); + 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 + } - // 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(); + void BoundaryNeumann::apply_ddt(Field3D & f) { + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); + Field3D* dt = f.timeDeriv(); + for (bndry->first(); !bndry->isDone(); bndry->next()) + for (int z = 0; z < mesh->LocalNz; z++) + (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero + } - // Decide which generator to use - std::shared_ptr fg = gen; - if (!fg) - fg = f.getBndryGenerator(bndry->location); + /////////////////////////////////////////////////////////////// - BoutReal val = 0.0; + BoundaryOp* BoundaryNeumann_O4::clone(BoundaryRegion * region, + const std::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); + } - // 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 + void BoundaryNeumann_O4::apply(Field2D & f) { BoundaryNeumann_O4::apply(f, 0.); } - Coordinates* coords = f.getCoordinates(); + void BoundaryNeumann_O4::apply(Field2D & f, BoutReal t) { +#if not(BOUT_USE_METRIC_3D) + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); - 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); + // 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(); - if (fg) { - val = fg->generate(Context(bndry, loc, t, mesh)); - } + // Decide which generator to use + std::shared_ptr fg = gen; + if (!fg) + fg = f.getBndryGenerator(bndry->location); - 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.; + BoutReal val = 0.0; - if (bndry->width == 2) { - throw BoutException("neumann_o4 with a boundary width of 2 not implemented yet"); - } - } - } -} - -void BoundaryNeumann_O4::apply(Field3D& f) { BoundaryNeumann_O4::apply(f, 0.); } - -void BoundaryNeumann_O4::apply(Field3D& f, BoutReal t) { - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); - bndry->first(); - - // Decide which generator to use - std::shared_ptr fg = gen; - if (!fg) - fg = f.getBndryGenerator(bndry->location); + // 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 - BoutReal val = 0.0; + Coordinates* coords = f.getCoordinates(); - // 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()) { - BoutReal delta = bndry->bx * coords->dx(bndry->x, bndry->y) - + bndry->by * coords->dy(bndry->x, bndry->y); + 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); - for (int zk = 0; zk < mesh->LocalNz; zk++) { if (fg) { - val = fg->generate(Context(bndry, zk, loc, t, mesh)); + val = fg->generate(Context(bndry, loc, t, mesh)); } - f(bndry->x, bndry->y, zk) = + f(bndry->x, bndry->y) = 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)) + + (+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) { @@ -2212,1354 +2222,1468 @@ void BoundaryNeumann_O4::apply(Field3D& f, BoutReal t) { } } } +#else + throw BoutException("Applying boundary condition 'neumann_O4' to Field2D not " + "compatible with 3D metrics in all cases."); +#endif } -} - -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 -} - -void BoundaryNeumann_O4::apply_ddt(Field3D& f) { - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); - Field3D *dt = f.timeDeriv(); - for (bndry->first(); !bndry->isDone(); bndry->next()) - for (int z = 0; z < mesh->LocalNz; z++) - (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero -} - -/////////////////////////////////////////////////////////////// - -BoundaryOp* BoundaryNeumann_4thOrder::clone(BoundaryRegion* region, - const std::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); -} -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 - } -} + void BoundaryNeumann_O4::apply(Field3D & f) { BoundaryNeumann_O4::apply(f, 0.); } -void BoundaryNeumann_4thOrder::apply(Field3D& f) { - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); - 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;z < mesh->LocalNz; 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 - } -} + void BoundaryNeumann_O4::apply(Field3D & f, BoutReal t) { + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); + bndry->first(); -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 -} + // Decide which generator to use + std::shared_ptr fg = gen; + if (!fg) + fg = f.getBndryGenerator(bndry->location); -void BoundaryNeumann_4thOrder::apply_ddt(Field3D& f) { - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); - Field3D *dt = f.timeDeriv(); - for (bndry->first(); !bndry->isDone(); bndry->next()) - for (int z = 0;z < mesh->LocalNz; z++) - (*dt)(bndry->x,bndry->y,z) = 0.; // Set time derivative to zero -} + BoutReal val = 0.0; -/////////////////////////////////////////////////////////////// - -BoundaryOp* BoundaryNeumannPar::clone(BoundaryRegion* region, - const std::list& args) { - verifyNumPoints(region, 1); - if (!args.empty()) { - output << "WARNING: Ignoring arguments to BoundaryNeumann2\n"; - } - return new BoundaryNeumannPar(region); -} - -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)); -} - -void BoundaryNeumannPar::apply(Field3D& f) { - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); - Coordinates *metric = f.getCoordinates(); - 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 std::list& args) { - verifyNumPoints(region, 1); - BoutReal a = 0.5, b = 1.0, g = 0.; - - auto it = args.begin(); - - if (it != args.end()) { - // First argument is 'a' - a = stringToReal(*it); - it++; + // 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()) { + for (int zk = 0; zk < mesh->LocalNz; zk++) { + BoutReal delta = bndry->bx * coords->dx(bndry->x, bndry->y, zk) + + bndry->by * coords->dy(bndry->x, bndry->y, zk); + if (fg) { + val = fg->generate(Context(bndry, zk, loc, t, mesh)); + } - if (it != args.end()) { - // Second is 'b' - b = stringToReal(*it); - it++; + 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 (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 (bndry->width == 2) { + throw BoutException( + "neumann_o4 with a boundary width of 2 not implemented yet"); + } } } } } - 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 + void BoundaryNeumann_O4::apply_ddt(Field2D & f) { + Field2D* dt = f.timeDeriv(); 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; + (*dt)(bndry->x, bndry->y) = 0.; // Set time derivative to zero } -} -void BoundaryRobin::apply(Field3D& f) { - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); - if (fabs(bval) < 1.e-12) { - for (bndry->first(); !bndry->isDone(); bndry->next()) - for (int z = 0;z < mesh->LocalNz; z++) - f(bndry->x, bndry->y, z) = gval / aval; - } else { - BoutReal sign = 1.; - if ((bndry->bx < 0) || (bndry->by < 0)) - sign = -1.; + void BoundaryNeumann_O4::apply_ddt(Field3D & f) { + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); + Field3D* dt = f.timeDeriv(); 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) - + sign * (gval - aval * f(bndry->x - bndry->bx, bndry->y - bndry->by, z)) - / bval; + (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } -} - -/////////////////////////////////////////////////////////////// - -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); -} -void BoundaryConstGradient::apply(Field3D& f) { - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); - for (bndry->first(); !bndry->isDone(); bndry->next()) - for (int z = 0;z < mesh->LocalNz; 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); -} + /////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////// - -BoundaryOp* BoundaryConstGradient::clone(BoundaryRegion* region, - const std::list& args) { - verifyNumPoints(region, 2); - if (!args.empty()) { - output << "WARNING: Ignoring arguments to BoundaryConstGradient\n"; + BoundaryOp* BoundaryNeumann_4thOrder::clone(BoundaryRegion * region, + const std::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); } - return new BoundaryConstGradient(region); -} -/////////////////////////////////////////////////////////////// + void BoundaryNeumann_4thOrder::apply(Field2D & f) { +#if not(BOUT_USE_METRIC_3D) + 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 + } +#else + throw BoutException("void BoundaryNeumann_4thOrder::apply(Field2D& f) not " + "implemented with 3D metrics"); +#endif + } -BoundaryOp* BoundaryZeroLaplace::clone(BoundaryRegion* region, - const std::list& args) { - verifyNumPoints(region, 2); - if (!args.empty()) { - output << "WARNING: Ignoring arguments to BoundaryZeroLaplace\n"; + void BoundaryNeumann_4thOrder::apply(Field3D & f) { + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); + 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; z < mesh->LocalNz; z++) { + BoutReal delta = -(bndry->bx * metric->dx(bndry->x, bndry->y, z) + + bndry->by * metric->dy(bndry->x, bndry->y, z)); + 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 + } + } + + 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 } - return new BoundaryZeroLaplace(region); -} -void BoundaryZeroLaplace::apply(Field2D& f) { - Coordinates* metric = f.getCoordinates(); - 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"); - } - // Constant X derivative - int bx = bndry->bx; - // Loop over the Y dimension - 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); - // Loop in X towards edge of domain - do { - f(x, y) = f(x - bx, y) + g * metric->dx(x, y); - bndry->nextX(); - x = bndry->x; - y = bndry->y; - } while (!bndry->isDone()); + void BoundaryNeumann_4thOrder::apply_ddt(Field3D & f) { + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); + Field3D* dt = f.timeDeriv(); + for (bndry->first(); !bndry->isDone(); bndry->next()) + for (int z = 0; z < mesh->LocalNz; z++) + (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } -} -void BoundaryZeroLaplace::apply(Field3D& f) { - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); - int ncz = mesh->LocalNz; + /////////////////////////////////////////////////////////////// - Coordinates* metric = f.getCoordinates(); + BoundaryOp* BoundaryNeumannPar::clone(BoundaryRegion * region, + const std::list& args) { + verifyNumPoints(region, 1); + if (!args.empty()) { + output << "WARNING: Ignoring arguments to BoundaryNeumann2\n"; + } + return new BoundaryNeumannPar(region); + } - Array c0(ncz / 2 + 1); - Array c1(ncz / 2 + 1); + void BoundaryNeumannPar::apply(Field2D & f) { +#if not(BOUT_USE_METRIC_3D) + 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)); +#else + throw BoutException("Applying boundary condition 'neumannpar' to Field2D not " + "compatible with 3D metrics in all cases."); +#endif + } - 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"); + void BoundaryNeumannPar::apply(Field3D & f) { + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); + Coordinates* metric = f.getCoordinates(); + 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, z) + / metric->g_22(bndry->x - bndry->bx, bndry->y - bndry->by, z)); } - int bx = bndry->bx; - // Loop over the Y dimension - for (bndry->first(); !bndry->isDone(); bndry->nextY()) { - // bndry->(x,y) is the first point in the boundary - // bndry->(x-bx,y) is the last "real" point in the domain + /////////////////////////////////////////////////////////////// - int x = bndry->x; - int y = bndry->y; + BoundaryOp* BoundaryRobin::clone(BoundaryRegion * region, + const std::list& args) { + verifyNumPoints(region, 1); + BoutReal a = 0.5, b = 1.0, g = 0.; - // 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()); - c1[0] = c0[0] - c1[0]; // Only need gradient + auto it = args.begin(); - // Solve metric->g11*d2f/dx2 - metric->g33*kz^2f = 0 - // Assume metric->g11, metric->g33 constant -> exponential growth or decay + if (it != args.end()) { + // First argument is 'a' + a = stringToReal(*it); + it++; - // Loop in X towards edge of domain - do { - // kz = 0 solution - c0[0] += c1[0]; // Straight line + if (it != args.end()) { + // Second is 'b' + b = stringToReal(*it); + it++; - // kz != 0 solution - BoutReal coef = - -1.0 * sqrt(metric->g33(x, y) / metric->g11(x, y)) * metric->dx(x, 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 + 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"; + } + } } - // Reverse FFT - irfft(c0.begin(), mesh->LocalNz, f(x, y)); + } - bndry->nextX(); - x = bndry->x; - y = bndry->y; - } while (!bndry->isDone()); + return new BoundaryRobin(region, a, b, g); } -} - -/////////////////////////////////////////////////////////////// -BoundaryOp* BoundaryZeroLaplace2::clone(BoundaryRegion* region, - const std::list& args) { - verifyNumPoints(region, 3); - if (!args.empty()) { - output << "WARNING: Ignoring arguments to BoundaryZeroLaplace2\n"; + 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 BoundaryZeroLaplace2(region); -} -void BoundaryZeroLaplace2::apply(Field2D& f) { - 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"); + void BoundaryRobin::apply(Field3D & f) { + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); + if (fabs(bval) < 1.e-12) { + for (bndry->first(); !bndry->isDone(); bndry->next()) + for (int z = 0; z < mesh->LocalNz; 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; z < mesh->LocalNz; 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; + } } - Coordinates* metric = f.getCoordinates(); + /////////////////////////////////////////////////////////////// - // Constant X derivative - int bx = bndry->bx; - // Loop over the Y dimension - 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); - // Loop in X towards edge of domain - do { - f(x, y) = f(x - bx, y) + g * metric->dx(x, y); - bndry->nextX(); - x = bndry->x; - y = bndry->y; - } while (!bndry->isDone()); + 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); } -} -void BoundaryZeroLaplace2::apply(Field3D& f) { - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); - int ncz = mesh->LocalNz; - - ASSERT0(ncz % 2 == 0); // Allocation assumes even number + void BoundaryConstGradient::apply(Field3D & f) { + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); + for (bndry->first(); !bndry->isDone(); bndry->next()) + for (int z = 0; z < mesh->LocalNz; 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); + } - // allocate memory - Array c0(ncz / 2 + 1), c1(ncz / 2 + 1), c2(ncz / 2 + 1); + /////////////////////////////////////////////////////////////// - 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"); + BoundaryOp* BoundaryConstGradient::clone(BoundaryRegion * region, + const std::list& args) { + verifyNumPoints(region, 2); + if (!args.empty()) { + output << "WARNING: Ignoring arguments to BoundaryConstGradient\n"; + } + return new BoundaryConstGradient(region); } - int bx = bndry->bx; - // Loop over the Y dimension - for (bndry->first(); !bndry->isDone(); bndry->nextY()) { - // bndry->(x,y) is the first point in the boundary - // bndry->(x-bx,y) is the last "real" point in the domain + /////////////////////////////////////////////////////////////// - int x = bndry->x; - int y = bndry->y; + BoundaryOp* BoundaryZeroLaplace::clone(BoundaryRegion * region, + const std::list& args) { + verifyNumPoints(region, 2); + if (!args.empty()) { + output << "WARNING: Ignoring arguments to BoundaryZeroLaplace\n"; + } + return new BoundaryZeroLaplace(region); + } - // Take FFT of last 2 points in domain - rfft(f(x - bx, y), ncz, c1.begin()); - rfft(f(x - 2 * bx, y), ncz, c2.begin()); + void BoundaryZeroLaplace::apply(Field2D & f) { +#if not(BOUT_USE_METRIC_3D) + Coordinates* metric = f.getCoordinates(); + 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"); + } + // Constant X derivative + int bx = bndry->bx; + // Loop over the Y dimension + 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); + // Loop in X towards edge of domain + do { + f(x, y) = f(x - bx, y) + g * metric->dx(x, y); + bndry->nextX(); + x = bndry->x; + y = bndry->y; + } while (!bndry->isDone()); + } +#else + throw BoutException("Applying boundary condition 'zerolaplace' to Field2D not " + "compatible with 3D metrics in all cases."); +#endif + } - // Loop in X towards edge of domain - do { - for (int jz = 0; jz <= ncz / 2; jz++) { - dcomplex la, lb, lc; - laplace_tridag_coefs(x - bx, y, jz, la, lb, lc); - if (bx > 0) { - // Outer boundary - swap(la, lc); - } - c0[jz] = -(lb * c1[jz] + lc * c2[jz]) / la; - } - // Reverse FFT - irfft(c0.begin(), ncz, f(x, y)); - // cycle c0 -> c1 -> c2 -> c0 - swap(c0, c2); - swap(c2, c1); + void BoundaryZeroLaplace::apply(Field3D & f) { +#if not(BOUT_USE_METRIC_3D) + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); + int ncz = mesh->LocalNz; - bndry->nextX(); - x = bndry->x; - y = bndry->y; - } while (!bndry->isDone()); - } -} + Coordinates* metric = f.getCoordinates(); -/////////////////////////////////////////////////////////////// + Array c0(ncz / 2 + 1); + Array c1(ncz / 2 + 1); -BoundaryOp* BoundaryConstLaplace::clone(BoundaryRegion* region, - const std::list& args) { - verifyNumPoints(region, 2); - if (!args.empty()) { - output << "WARNING: Ignoring arguments to BoundaryConstLaplace\n"; - } - return new BoundaryConstLaplace(region); -} + 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"); + } -void BoundaryConstLaplace::apply(Field2D& f) { - 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"); - } - - // Constant X second derivative - int bx = bndry->bx; - // Loop over the Y dimension - 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); - // 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(); - - bndry->nextX(); - x = bndry->x; - y = bndry->y; - } while (!bndry->isDone()); + int bx = bndry->bx; + // Loop over the Y dimension + for (bndry->first(); !bndry->isDone(); bndry->nextY()) { + // bndry->(x,y) is the first point in the boundary + // bndry->(x-bx,y) is the last "real" point in the domain + + int x = bndry->x; + 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()); + c1[0] = c0[0] - c1[0]; // Only need gradient + + // Solve metric->g11*d2f/dx2 - metric->g33*kz^2f = 0 + // Assume metric->g11, metric->g33 constant -> exponential growth or decay + + // Loop in X towards edge of domain + do { + // kz = 0 solution + c0[0] += c1[0]; // Straight line + + // kz != 0 solution + BoutReal coef = + -1.0 * sqrt(metric->g33(x, y) / metric->g11(x, y)) * metric->dx(x, y); + for (int jz = 1; jz <= ncz / 2; jz++) { + BoutReal kwave = + jz * 2.0 * PI / metric->zlength()(x, y); // wavenumber in [rad^-1] + c0[jz] *= exp(coef * kwave); // The decaying solution only + } + // Reverse FFT + irfft(c0.begin(), mesh->LocalNz, f(x, y)); + + bndry->nextX(); + x = bndry->x; + y = bndry->y; + } while (!bndry->isDone()); + } +#else + throw BoutException( + "Applying boundary to Field3D not compatible with 3D metrics in " + "ZeroLaplace case."); +#endif } -} -void BoundaryConstLaplace::apply(Field3D& f) { - 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"); - } + /////////////////////////////////////////////////////////////// - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); - Coordinates *metric = f.getCoordinates(); - - int ncz = mesh->LocalNz; - - // Allocate memory - 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()) { - 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 - - // 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]; - } + BoundaryOp* BoundaryZeroLaplace2::clone(BoundaryRegion * region, + const std::list& args) { + verifyNumPoints(region, 3); + if (!args.empty()) { + output << "WARNING: Ignoring arguments to BoundaryZeroLaplace2\n"; } - // Solve metric->g11*d2f/dx2 - metric->g33*kz^2f = 0 - // Assume metric->g11, metric->g33 constant -> exponential growth or decay - BoutReal xpos = 0.0; - // 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); - // 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); - } - // Reverse FFT - irfft(c2.begin(), ncz, f(x, y)); - - bndry->nextX(); - x = bndry->x; - y = bndry->y; - } while (!bndry->isDone()); + return new BoundaryZeroLaplace2(region); } -} -/////////////////////////////////////////////////////////////// + void BoundaryZeroLaplace2::apply(Field2D & f) { +#if not(BOUT_USE_METRIC_3D) + 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"); + } -BoundaryOp* BoundaryDivCurl::clone(BoundaryRegion* region, - const std::list& args) { - if (!args.empty()) { - output << "WARNING: Ignoring arguments to BoundaryDivCurl\n"; + Coordinates* metric = f.getCoordinates(); + + // Constant X derivative + int bx = bndry->bx; + // Loop over the Y dimension + 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); + // Loop in X towards edge of domain + do { + f(x, y) = f(x - bx, y) + g * metric->dx(x, y); + bndry->nextX(); + x = bndry->x; + y = bndry->y; + } while (!bndry->isDone()); + } +#else + throw BoutException("Applying boundary condition 'zerolaplace2' to Field2D not " + "compatible with 3D metrics in all cases."); +#endif } - return new BoundaryDivCurl(region); -} - -void BoundaryDivCurl::apply(Vector2D& UNUSED(f)) { - throw BoutException("ERROR: DivCurl boundary not yet implemented for 2D vectors\n"); -} -void BoundaryDivCurl::apply(Vector3D& var) { - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == var.getMesh()); + void BoundaryZeroLaplace2::apply(Field3D & f) { +#if not(BOUT_USE_METRIC_3D) + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); + const int ncz = mesh->LocalNz; - int jx, jy, jz, jzp, jzm; - BoutReal tmp; + ASSERT0(ncz % 2 == 0); // Allocation assumes even number - Coordinates* metric = mesh->getCoordinates(var.getLocation()); + // allocate memory + Array c0(ncz / 2 + 1), c1(ncz / 2 + 1), c2(ncz / 2 + 1); - int ncz = mesh->LocalNz; + 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"); + } - if (bndry->location != BNDRY_XOUT) { - throw BoutException("ERROR: DivCurl boundary only works for outer X currently\n"); + int bx = bndry->bx; + // Loop over the Y dimension + for (bndry->first(); !bndry->isDone(); bndry->nextY()) { + // bndry->(x,y) is the first point in the boundary + // bndry->(x-bx,y) is the last "real" point in the domain + + int x = bndry->x; + int y = bndry->y; + + // Take FFT of last 2 points in domain + rfft(f(x - bx, y), ncz, c1.begin()); + rfft(f(x - 2 * bx, y), ncz, c2.begin()); + + // Loop in X towards edge of domain + do { + for (int jz = 0; jz <= ncz / 2; jz++) { + dcomplex la, lb, lc; + laplace_tridag_coefs(x - bx, y, jz, la, lb, lc); + if (bx > 0) { + // Outer boundary + swap(la, lc); + } + c0[jz] = -(lb * c1[jz] + lc * c2[jz]) / la; + } + // Reverse FFT + irfft(c0.begin(), ncz, f(x, y)); + // cycle c0 -> c1 -> c2 -> c0 + swap(c0, c2); + swap(c2, c1); + + bndry->nextX(); + x = bndry->x; + y = bndry->y; + } while (!bndry->isDone()); + } +#else + throw BoutException( + "Applying boundary to Field3D not compatible with 3D metrics in " + "ZeroLaplace2 case."); +#endif } - var.toCovariant(); - if (mesh->xstart > 2) { - throw BoutException( - "Error: Div = Curl = 0 boundary condition doesn't work for MXG > 2. Sorry\n"); + /////////////////////////////////////////////////////////////// + + BoundaryOp* BoundaryConstLaplace::clone(BoundaryRegion * region, + const std::list& args) { + verifyNumPoints(region, 2); + if (!args.empty()) { + output << "WARNING: Ignoring arguments to BoundaryConstLaplace\n"; + } + return new BoundaryConstLaplace(region); } - jx = mesh->xend + 1; - for (jy = 1; jy < mesh->LocalNy - 1; jy++) { - for (jz = 0; jz < ncz; jz++) { - jzp = (jz + 1) % ncz; - jzm = (jz - 1 + ncz) % ncz; + void BoundaryConstLaplace::apply(Field2D & f) { + 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"); + } - // dB_y / dx = dB_x / dy + // Constant X second derivative + int bx = bndry->bx; + // Loop over the Y dimension + 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); + // 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(); + + bndry->nextX(); + x = bndry->x; + y = bndry->y; + } while (!bndry->isDone()); + } + } - // 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)); + void BoundaryConstLaplace::apply(Field3D & f) { +#if not(BOUT_USE_METRIC_3D) + 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"); + } - 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; + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); + Coordinates* metric = f.getCoordinates(); - // dB_z / dx = dB_x / dz + int ncz = mesh->LocalNz; - tmp = (var.x(jx - 1, jy, jzp) - var.x(jx - 1, jy, jzm)) / (2. * metric->dz); + // Allocate memory + Array c0(ncz / 2 + 1), c1(ncz / 2 + 1), c2(ncz / 2 + 1); - 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; + int bx = bndry->bx; + // Loop over the Y dimension + for (bndry->first(); !bndry->isDone(); bndry->nextY()) { + int x = bndry->x; + int y = bndry->y; - // d/dx( Jmetric->g11 B_x ) = - d/dx( Jmetric->g12 B_y + Jmetric->g13 B_z) - // - d/dy( JB^y ) - d/dz( JB^z ) + // 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 - 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); + // 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]; + } + } + // Solve metric->g11*d2f/dx2 - metric->g33*kz^2f = 0 + // Assume metric->g11, metric->g33 constant -> exponential growth or decay + BoutReal xpos = 0.0; + // 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); + // 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 / getUniform(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)); + + bndry->nextX(); + x = bndry->x; + y = bndry->y; + } while (!bndry->isDone()); } +#else + throw BoutException( + "Applying boundary to Field3D not compatible with 3D metrics in " + "ConstLaplace case."); +#endif } -} -/////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////// -BoundaryOp* BoundaryFree::clone(BoundaryRegion* region, - const std::list& args) { - if (!args.empty()) { - // First argument should be a value - val = stringToReal(args.front()); - return new BoundaryFree(region, val); + BoundaryOp* BoundaryDivCurl::clone(BoundaryRegion * region, + const std::list& args) { + if (!args.empty()) { + output << "WARNING: Ignoring arguments to BoundaryDivCurl\n"; + } + return new BoundaryDivCurl(region); } - return new BoundaryFree(region); -} -void BoundaryFree::apply(Field2D& UNUSED(f)) { - // Do nothing for free boundary -} + void BoundaryDivCurl::apply(Vector2D & UNUSED(f)) { + throw BoutException("ERROR: DivCurl boundary not yet implemented for 2D vectors\n"); + } -void BoundaryFree::apply(Field3D& UNUSED(f)) { - // Do nothing for free boundary -} + void BoundaryDivCurl::apply(Vector3D & var) { +#if not(BOUT_USE_METRIC_3D) + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == var.getMesh()); -void BoundaryFree::apply_ddt(Field2D& UNUSED(f)) { - // Do nothing for free boundary -} + int jx, jy, jz, jzp, jzm; + BoutReal tmp; -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. -/////////////////////////////////////////////////////////////// + Coordinates* metric = mesh->getCoordinates(var.getLocation()); -// 2nd order extrapolation: + int ncz = mesh->LocalNz; -BoundaryOp* BoundaryFree_O2::clone(BoundaryRegion* region, - const std::list& args) { - verifyNumPoints(region, 2); - if (!args.empty()) { - output << "WARNING: Ignoring arguments to BoundaryFree\n"; - } - return new BoundaryFree_O2(region); -} + if (bndry->location != BNDRY_XOUT) { + throw BoutException("ERROR: DivCurl boundary only works for outer X currently\n"); + } + var.toCovariant(); -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 + if (mesh->xstart > 2) { + throw BoutException( + "Error: Div = Curl = 0 boundary condition doesn't work for MXG > 2. Sorry\n"); + } - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); - bndry->first(); + jx = mesh->xend + 1; + for (jy = 1; jy < mesh->LocalNy - 1; jy++) { + for (jz = 0; jz < ncz; jz++) { + jzp = (jz + 1) % ncz; + jzm = (jz - 1 + ncz) % ncz; - // Check for staggered grids + // dB_y / dx = dB_x / dy - CELL_LOC loc = f.getLocation(); - if (mesh->StaggerGrids and (loc == CELL_XLOW or loc == CELL_YLOW)) { - // Staggered. Need to apply slightly differently + // 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)); - if (loc == CELL_XLOW) { - // Field is shifted in X + 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; - if (bndry->bx > 0) { - // Outer x boundary - for (; !bndry->isDone(); bndry->next1d()) { - for (int i = 0; i < bndry->width; 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; i < bndry->width; 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; i < bndry->width; 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 + // dB_z / dx = dB_x / dz - if (bndry->by > 0) { - // Upper y boundary + tmp = (var.x(jx - 1, jy, jzp) - var.x(jx - 1, jy, jzm)) + / (2. * metric->dz(jx - 1, jy)); - for (; !bndry->isDone(); bndry->next1d()) { - for (int i = 0; i < bndry->width; 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; i < bndry->width; 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()) { + 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; - for (int i = 0; i < bndry->width; 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); - } - } + // 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(jx - 1, jy)); + + 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); } } - } else { - // Non-staggered, standard case +#else + throw BoutException( + "Applying boundary to Vector3D not compatible with 3D metrics in DivCurl."); +#endif + } - for (; !bndry->isDone(); bndry->next1d()) { + /////////////////////////////////////////////////////////////// - for (int i = 0; i < bndry->width; 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); - } + BoundaryOp* BoundaryFree::clone(BoundaryRegion * region, + const std::list& args) { + if (!args.empty()) { + // First argument should be a value + val = stringToReal(args.front()); + return new BoundaryFree(region, val); } + return new BoundaryFree(region); } -} -void BoundaryFree_O2::apply(Field3D& f) { - // Extrapolate from the last evolved simulation cells into the guard cells at 3rd order. + void BoundaryFree::apply(Field2D & UNUSED(f)) { + // Do nothing for free boundary + } - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); - bndry->first(); + void BoundaryFree::apply(Field3D & UNUSED(f)) { + // Do nothing for free boundary + } - // Check for staggered grids + void BoundaryFree::apply_ddt(Field2D & UNUSED(f)) { + // Do nothing for free boundary + } - CELL_LOC loc = f.getLocation(); - if (mesh->StaggerGrids and (loc == CELL_XLOW or loc == CELL_YLOW)) { - // Staggered. Need to apply slightly differently + 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. + /////////////////////////////////////////////////////////////// - if (loc == CELL_XLOW) { - // Field is shifted in X + // 2nd order extrapolation: - if (bndry->bx > 0) { - // Outer x boundary + BoundaryOp* BoundaryFree_O2::clone(BoundaryRegion * region, + const std::list& args) { + verifyNumPoints(region, 2); + if (!args.empty()) { + output << "WARNING: Ignoring arguments to BoundaryFree\n"; + } + return new BoundaryFree_O2(region); + } - for (; !bndry->isDone(); bndry->next1d()) { + 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 - for (int zk = 0; zk < mesh->LocalNz; zk++) { + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); + bndry->first(); + + // Check for staggered grids + + CELL_LOC loc = f.getLocation(); + if (mesh->StaggerGrids and (loc == CELL_XLOW or loc == CELL_YLOW)) { + // 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; i < bndry->width; 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) = 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 zk = 0; zk < mesh->LocalNz; zk++) { + if (bndry->bx < 0) { + // Inner x boundary. Set one point inwards + for (; !bndry->isDone(); bndry->next1d()) { for (int i = -1; i < bndry->width; 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) = 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 zk = 0; zk < mesh->LocalNz; zk++) { + if (bndry->by != 0) { + // y boundaries + for (; !bndry->isDone(); bndry->next1d()) { for (int i = 0; i < bndry->width; 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) = 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 + } 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; zk < mesh->LocalNz; zk++) { + if (bndry->by > 0) { + // Upper y boundary + + for (; !bndry->isDone(); bndry->next1d()) { for (int i = 0; i < bndry->width; 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) = 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 zk = 0; zk < mesh->LocalNz; zk++) { + if (bndry->by < 0) { + // Lower y boundary. Set one point inwards + for (; !bndry->isDone(); bndry->next1d()) { for (int i = -1; i < bndry->width; 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) = 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()) { + if (bndry->bx != 0) { + // x boundaries + for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { for (int i = 0; i < bndry->width; 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) = 2 * f(xi - bndry->bx, yi - bndry->by) + - f(xi - 2 * bndry->bx, yi - 2 * bndry->by); } } } } - } - } else { - // Standard (non-staggered) case - for (; !bndry->isDone(); bndry->next1d()) { + } else { + // Non-staggered, standard case + + for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { for (int i = 0; i < bndry->width; 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) = 2 * f(xi - bndry->bx, yi - bndry->by) + - f(xi - 2 * bndry->bx, yi - 2 * bndry->by); } } } } -} - -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 -} -void BoundaryFree_O2::apply_ddt(Field3D& f) { - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); - Field3D *dt = f.timeDeriv(); - for (bndry->first(); !bndry->isDone(); bndry->next()) - for (int z = 0; z < mesh->LocalNz; z++) - (*dt)(bndry->x,bndry->y,z) = 0.; // Set time derivative to zero -} - -////////////////////////////////// -// Third order extrapolation: -////////////////////////////////// -BoundaryOp* BoundaryFree_O3::clone(BoundaryRegion* region, - const std::list& args) { - verifyNumPoints(region, 3); + void BoundaryFree_O2::apply(Field3D & f) { + // Extrapolate from the last evolved simulation cells into the guard cells at 3rd + // order. - if (!args.empty()) { - output << "WARNING: Ignoring arguments to BoundaryConstLaplace\n"; - } - return new BoundaryFree_O3(region); -} + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); + bndry->first(); -void BoundaryFree_O3::apply(Field2D& f) { + // Check for staggered grids - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); - bndry->first(); + CELL_LOC loc = f.getLocation(); + if (mesh->StaggerGrids and (loc == CELL_XLOW or loc == CELL_YLOW)) { + // Staggered. Need to apply slightly differently - // Check for staggered grids + if (loc == CELL_XLOW) { + // Field is shifted in X - CELL_LOC loc = f.getLocation(); - if (mesh->StaggerGrids and (loc == CELL_XLOW or loc == CELL_YLOW)) { - // Staggered. Need to apply slightly differently + if (bndry->bx > 0) { + // Outer x boundary - if (loc == CELL_XLOW) { - // Field is shifted in X + for (; !bndry->isDone(); bndry->next1d()) { - if (bndry->bx > 0) { - // Outer x boundary - for (; !bndry->isDone(); bndry->next1d()) { - for (int i = 0; i < bndry->width; 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 zk = 0; zk < mesh->LocalNz; zk++) { + for (int i = 0; i < bndry->width; 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 i = -1; i < bndry->width; 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 zk = 0; zk < mesh->LocalNz; zk++) { + for (int i = -1; i < bndry->width; 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 i = 0; i < bndry->width; 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 zk = 0; zk < mesh->LocalNz; zk++) { + for (int i = 0; i < bndry->width; 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 + } else if (loc == CELL_YLOW) { + // Field is shifted in Y - if (bndry->by > 0) { - // Upper y boundary + if (bndry->by > 0) { + // Upper y boundary + for (; !bndry->isDone(); bndry->next1d()) { + for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int i = 0; i < bndry->width; 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 (; !bndry->isDone(); bndry->next1d()) { - for (int i = 0; i < bndry->width; 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 zk = 0; zk < mesh->LocalNz; zk++) { + for (int i = -1; i < bndry->width; 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 i = -1; i < bndry->width; 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 zk = 0; zk < mesh->LocalNz; zk++) { + for (int i = 0; i < bndry->width; 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()) { + } else { + // Standard (non-staggered) case + for (; !bndry->isDone(); bndry->next1d()) { + for (int zk = 0; zk < mesh->LocalNz; zk++) { for (int i = 0; i < bndry->width; 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); + f(xi, yi, zk) = 2 * f(xi - bndry->bx, yi - bndry->by, zk) + - f(xi - 2 * bndry->bx, yi - 2 * bndry->by, zk); } } } } - } else { - // Non-staggered, standard case + } - for (; !bndry->isDone(); bndry->next1d()) { + 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 + } - for (int i = 0; i < bndry->width; 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 BoundaryFree_O2::apply_ddt(Field3D & f) { + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); + Field3D* dt = f.timeDeriv(); + for (bndry->first(); !bndry->isDone(); bndry->next()) + for (int z = 0; z < mesh->LocalNz; z++) + (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } -} -void BoundaryFree_O3::apply(Field3D& f) { - // Extrapolate from the last evolved simulation cells into the guard cells at 3rd order. + ////////////////////////////////// + // Third order extrapolation: + ////////////////////////////////// + BoundaryOp* BoundaryFree_O3::clone(BoundaryRegion * region, + const std::list& args) { + verifyNumPoints(region, 3); - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); - bndry->first(); + if (!args.empty()) { + output << "WARNING: Ignoring arguments to BoundaryConstLaplace\n"; + } + return new BoundaryFree_O3(region); + } - // Check for staggered grids + void BoundaryFree_O3::apply(Field2D & f) { - CELL_LOC loc = f.getLocation(); - if (mesh->StaggerGrids and (loc == CELL_XLOW or loc == CELL_YLOW)) { - // Staggered. Need to apply slightly differently + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); + bndry->first(); - if (loc == CELL_XLOW) { - // Field is shifted in X + // Check for staggered grids - if (bndry->bx > 0) { - // Outer x boundary + CELL_LOC loc = f.getLocation(); + if (mesh->StaggerGrids and (loc == CELL_XLOW or loc == CELL_YLOW)) { + // Staggered. Need to apply slightly differently - for (; !bndry->isDone(); bndry->next1d()) { + if (loc == CELL_XLOW) { + // Field is shifted in X - for (int zk = 0; zk < mesh->LocalNz; zk++) { + if (bndry->bx > 0) { + // Outer x boundary + for (; !bndry->isDone(); bndry->next1d()) { for (int i = 0; i < bndry->width; 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); + 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 zk = 0; zk < mesh->LocalNz; zk++) { + if (bndry->bx < 0) { + // Inner x boundary. Set one point inwards + for (; !bndry->isDone(); bndry->next1d()) { for (int i = -1; i < bndry->width; 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); + 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 zk = 0; zk < mesh->LocalNz; zk++) { + if (bndry->by != 0) { + // y boundaries + for (; !bndry->isDone(); bndry->next1d()) { for (int i = 0; i < bndry->width; 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); + 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 + } 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; zk < mesh->LocalNz; zk++) { + if (bndry->by > 0) { + // Upper y boundary + + for (; !bndry->isDone(); bndry->next1d()) { for (int i = 0; i < bndry->width; 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); + 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 zk = 0; zk < mesh->LocalNz; zk++) { + if (bndry->by < 0) { + // Lower y boundary. Set one point inwards + for (; !bndry->isDone(); bndry->next1d()) { for (int i = -1; i < bndry->width; 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); + 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 (bndry->bx != 0) { + // x boundaries + for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { for (int i = 0; i < bndry->width; 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); + 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 { - // Standard (non-staggered) case - for (; !bndry->isDone(); bndry->next1d()) { + } else { + // Non-staggered, standard case + + for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { for (int i = 0; i < bndry->width; 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); + 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 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 -} + void BoundaryFree_O3::apply(Field3D & f) { + // Extrapolate from the last evolved simulation cells into the guard cells at 3rd + // order. -void BoundaryFree_O3::apply_ddt(Field3D& f) { - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); - Field3D *dt = f.timeDeriv(); - for (bndry->first(); !bndry->isDone(); bndry->next()) - for (int z = 0; z < mesh->LocalNz; z++) - (*dt)(bndry->x,bndry->y,z) = 0.; // Set time derivative to zero -} + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); + bndry->first(); -/////////////////////////////////////////////////////////////// + // Check for staggered grids -BoundaryOp* BoundaryRelax::cloneMod(BoundaryOp* operation, - const std::list& args) { - auto* result = new BoundaryRelax(operation, r); + CELL_LOC loc = f.getLocation(); + if (mesh->StaggerGrids and (loc == CELL_XLOW or loc == CELL_YLOW)) { + // Staggered. Need to apply slightly differently - if (!args.empty()) { - // First argument should be the rate - BoutReal val = stringToReal(args.front()); - val = fabs(val); // Should always be positive - result->r = val; + 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 < mesh->LocalNz; zk++) { + for (int i = 0; i < bndry->width; 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; zk < mesh->LocalNz; zk++) { + for (int i = -1; i < bndry->width; 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; zk < mesh->LocalNz; zk++) { + for (int i = 0; i < bndry->width; 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; zk < mesh->LocalNz; zk++) { + for (int i = 0; i < bndry->width; 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; zk < mesh->LocalNz; zk++) { + for (int i = -1; i < bndry->width; 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; zk < mesh->LocalNz; zk++) { + for (int i = 0; i < bndry->width; 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; zk < mesh->LocalNz; zk++) { + for (int i = 0; i < bndry->width; 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); + } + } + } + } } - return result; -} + 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 + } -void BoundaryRelax::apply(Field2D& f, BoutReal t) { - // Just apply the original boundary condition to f - op->apply(f, t); -} + void BoundaryFree_O3::apply_ddt(Field3D & f) { + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); + Field3D* dt = f.timeDeriv(); + for (bndry->first(); !bndry->isDone(); bndry->next()) + for (int z = 0; z < mesh->LocalNz; z++) + (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero + } -void BoundaryRelax::apply(Field3D& f, BoutReal UNUSED(t)) { - // Just apply the original boundary condition to f - op->apply(f); -} + /////////////////////////////////////////////////////////////// -void BoundaryRelax::apply_ddt(Field2D& f) { - TRACE("BoundaryRelax::apply_ddt(Field2D)"); + BoundaryOp* BoundaryRelax::cloneMod(BoundaryOp * operation, + const std::list& args) { + auto* result = new BoundaryRelax(operation, r); - // Make a copy of f - Field2D g = f; - // Apply the boundary to g - op->apply(g); + if (!args.empty()) { + // First argument should be the rate + BoutReal val = stringToReal(args.front()); + val = fabs(val); // Should always be positive + result->r = val; + } - bndry->first(); + return result; + } - // Set time-derivatives - 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(Field2D & f, BoutReal t) { + // Just apply the original boundary condition to f + op->apply(f, t); } -} -void BoundaryRelax::apply_ddt(Field3D& f) { - TRACE("BoundaryRelax::apply_ddt(Field3D)"); + void BoundaryRelax::apply(Field3D & f, BoutReal UNUSED(t)) { + // Just apply the original boundary condition to f + op->apply(f); + } - Mesh* mesh = bndry->localmesh; - ASSERT1(mesh == f.getMesh()); + void BoundaryRelax::apply_ddt(Field2D & f) { + TRACE("BoundaryRelax::apply_ddt(Field2D)"); - // 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; z < mesh->LocalNz; z++) { - ddt(f)(bndry->x, bndry->y, z) = - r * (g(bndry->x, bndry->y, z) - f(bndry->x, bndry->y, z)); + // Make a copy of f + Field2D g = f; + // Apply the boundary to g + op->apply(g); + + bndry->first(); + + // Set time-derivatives + 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)"); -BoundaryOp* BoundaryWidth::cloneMod(BoundaryOp* operation, - const std::list& args) { - auto* result = new BoundaryWidth(operation, width); + Mesh* mesh = bndry->localmesh; + ASSERT1(mesh == f.getMesh()); - 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; + // 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; z < mesh->LocalNz; z++) { + ddt(f)(bndry->x, bndry->y, z) = + r * (g(bndry->x, bndry->y, z) - f(bndry->x, bndry->y, z)); + } } - 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; -} + BoundaryOp* BoundaryWidth::cloneMod(BoundaryOp * operation, + const std::list& args) { + auto* result = new BoundaryWidth(operation, width); -void BoundaryWidth::apply(Field3D& f, BoutReal t) { - int oldwid = bndry->width; - bndry->width = width; - op->apply(f, t); - bndry->width = oldwid; -} + 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; + } -void BoundaryWidth::apply_ddt(Field2D& f) { - int oldwid = bndry->width; - bndry->width = width; - op->apply_ddt(f); - bndry->width = oldwid; -} + return result; + } -void BoundaryWidth::apply_ddt(Field3D& f) { - int oldwid = bndry->width; - bndry->width = width; - op->apply_ddt(f); - bndry->width = oldwid; -} + 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; + } -/////////////////////////////////////////////////////////////// -BoundaryOp* BoundaryToFieldAligned::cloneMod(BoundaryOp* operation, - const std::list& args) { - auto* result = new BoundaryToFieldAligned(operation); + void BoundaryWidth::apply(Field3D & f, BoutReal t) { + int oldwid = bndry->width; + bndry->width = width; + op->apply(f, t); + bndry->width = oldwid; + } - if (!args.empty()) { - output << "WARNING: BoundaryToFieldAligned expected no argument\n"; - // Shouldn't we throw ? + void BoundaryWidth::apply_ddt(Field2D & f) { + int oldwid = bndry->width; + bndry->width = width; + op->apply_ddt(f); + bndry->width = oldwid; } - return result; -} + void BoundaryWidth::apply_ddt(Field3D & f) { + int oldwid = bndry->width; + bndry->width = width; + op->apply_ddt(f); + bndry->width = oldwid; + } -void BoundaryToFieldAligned::apply(Field2D& f, BoutReal t) { op->apply(f, t); } + /////////////////////////////////////////////////////////////// + BoundaryOp* BoundaryToFieldAligned::cloneMod(BoundaryOp * operation, + const std::list& args) { + auto* result = new BoundaryToFieldAligned(operation); -void BoundaryToFieldAligned::apply(Field3D &f, BoutReal t) { - ASSERT1(bndry->localmesh == f.getMesh()); + if (!args.empty()) { + output << "WARNING: BoundaryToFieldAligned expected no argument\n"; + // Shouldn't we throw ? + } - // NOTE: This is not very efficient... updating entire field - f = fromFieldAligned(f); + return result; + } - // Apply the boundary to shifted field - op->apply(f, t); + void BoundaryToFieldAligned::apply(Field2D & f, BoutReal t) { op->apply(f, t); } - // Shift back - f = toFieldAligned(f); + void BoundaryToFieldAligned::apply(Field3D & f, BoutReal t) { + ASSERT1(bndry->localmesh == f.getMesh()); - // 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 -} + // NOTE: This is not very efficient... updating entire field + f = fromFieldAligned(f); -void BoundaryToFieldAligned::apply_ddt(Field2D &f) { - op->apply_ddt(f); -} + // Apply the boundary to shifted field + op->apply(f, t); -void BoundaryToFieldAligned::apply_ddt(Field3D &f) { - ASSERT1(bndry->localmesh == f.getMesh()); + // Shift back + f = toFieldAligned(f); - f = fromFieldAligned(f); - ddt(f) = fromFieldAligned(ddt(f)); - op->apply_ddt(f); - ddt(f) = toFieldAligned(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 + } -/////////////////////////////////////////////////////////////// -BoundaryOp* BoundaryFromFieldAligned::cloneMod(BoundaryOp* operation, - const std::list& args) { - auto* result = new BoundaryFromFieldAligned(operation); + void BoundaryToFieldAligned::apply_ddt(Field2D & f) { op->apply_ddt(f); } - if (!args.empty()) { - output << "WARNING: BoundaryFromFieldAligned expected no argument\n"; - // Shouldn't we throw ? + void BoundaryToFieldAligned::apply_ddt(Field3D & f) { + ASSERT1(bndry->localmesh == f.getMesh()); + + f = fromFieldAligned(f); + ddt(f) = fromFieldAligned(ddt(f)); + op->apply_ddt(f); + ddt(f) = toFieldAligned(ddt(f)); } - return result; -} + /////////////////////////////////////////////////////////////// + BoundaryOp* BoundaryFromFieldAligned::cloneMod(BoundaryOp * operation, + const std::list& args) { + auto* result = new BoundaryFromFieldAligned(operation); + + if (!args.empty()) { + output << "WARNING: BoundaryFromFieldAligned expected no argument\n"; + // Shouldn't we throw ? + } -void BoundaryFromFieldAligned::apply(Field2D& f, BoutReal t) { op->apply(f, t); } + return result; + } -void BoundaryFromFieldAligned::apply(Field3D &f, BoutReal t) { - ASSERT1(bndry->localmesh == f.getMesh()); + void BoundaryFromFieldAligned::apply(Field2D & f, BoutReal t) { op->apply(f, t); } - // NOTE: This is not very efficient... shifting entire field - f = toFieldAligned(f); + void BoundaryFromFieldAligned::apply(Field3D & f, BoutReal t) { + ASSERT1(bndry->localmesh == f.getMesh()); - // Apply the boundary to shifted field - op->apply(f, t); + // NOTE: This is not very efficient... shifting entire field + f = toFieldAligned(f); - // Shift back - f = fromFieldAligned(f); + // Apply the boundary to shifted field + op->apply(f, t); - // 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 -} + // Shift back + f = fromFieldAligned(f); -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(Field3D &f) { - ASSERT1(bndry->localmesh == f.getMesh()); + void BoundaryFromFieldAligned::apply_ddt(Field2D & f) { op->apply_ddt(f); } - f = toFieldAligned(f); - ddt(f) = toFieldAligned(ddt(f)); - op->apply_ddt(f); - ddt(f) = fromFieldAligned(ddt(f)); -} + void BoundaryFromFieldAligned::apply_ddt(Field3D & f) { + ASSERT1(bndry->localmesh == f.getMesh()); + + f = toFieldAligned(f); + ddt(f) = toFieldAligned(ddt(f)); + op->apply_ddt(f); + ddt(f) = fromFieldAligned(ddt(f)); + } diff --git a/src/mesh/coordinates.cxx b/src/mesh/coordinates.cxx index cf8ad5db7a..10a0073a4b 100644 --- a/src/mesh/coordinates.cxx +++ b/src/mesh/coordinates.cxx @@ -22,14 +22,25 @@ // use anonymous namespace so this utility function is not available outside this file namespace { +template +// Use sendY()/sendX() and wait() instead of Mesh::communicate() to ensure we +// don't try to calculate parallel slices as Coordinates are not constructed yet +void communicate(T& t, Ts&... ts) { + FieldGroup g(t, ts...); + auto h = t.getMesh()->sendY(g); + t.getMesh()->wait(h); + h = t.getMesh()->sendX(g); + t.getMesh()->wait(h); +} + /// Interpolate a Field2D to a new CELL_LOC with interp_to. /// Communicates to set internal guard cells. /// Boundary guard cells are set by extrapolating from the grid, like /// 'free_o3' boundary conditions /// Corner guard cells are set to BoutNaN -Field2D interpolateAndExtrapolate(const Field2D& f, CELL_LOC location, - bool extrapolate_x = true, bool extrapolate_y = true, - bool no_extra_interpolate = false) { +Field2D interpolateAndExtrapolate(const Field2D& f, CELL_LOC location, bool extrapolate_x, + bool extrapolate_y, bool no_extra_interpolate, + ParallelTransform* UNUSED(pt) = nullptr) { Mesh* localmesh = f.getMesh(); Field2D result = interp_to(f, location, "RGN_NOBNDRY"); @@ -41,7 +52,7 @@ Field2D interpolateAndExtrapolate(const Field2D& f, CELL_LOC location, // communicate f. We will sort out result's boundary guard cells below, but // not f's so we don't want to change f. result.allocate(); - localmesh->communicate(result); + communicate(result); // Extrapolate into boundaries (if requested) so that differential geometry // terms can be interpolated if necessary @@ -119,7 +130,6 @@ Field2D interpolateAndExtrapolate(const Field2D& f, CELL_LOC location, } } } - #if CHECK > 0 if (not ( // if include_corner_cells=true, then we extrapolate valid data into the @@ -146,6 +156,160 @@ Field2D interpolateAndExtrapolate(const Field2D& f, CELL_LOC location, return result; } +Field3D interpolateAndExtrapolate(const Field3D& f_, CELL_LOC location, + bool extrapolate_x, bool extrapolate_y, + bool no_extra_interpolate, ParallelTransform* pt_) { + + Mesh* localmesh = f_.getMesh(); + Field3D result; + Field3D f = f_; + ParallelTransform* pt_f; + if (f.getCoordinates() == nullptr) { + // if input f is member of the Coordinates we are currently constructing, it will not + // have Coordinates and needs to use the passed-in ParallelTransform + pt_f = pt_; + } else { + // if input f is from Coordinates at a different location, it will have its own + // Coordinates, and we should use its ParallelTransform + pt_f = &f.getCoordinates()->getParallelTransform(); + } + if (f.getDirectionY() != YDirectionType::Standard) { + if (pt_f->canToFromFieldAligned()) { + f = pt_f->fromFieldAligned(f); + } else { + f.setDirectionY(YDirectionType::Standard); + } + } + if (location == CELL_YLOW and f.getLocation() != CELL_YLOW) { + auto f_aligned = pt_f->toFieldAligned(f, "RGN_NOX"); + result = interp_to(f_aligned, location, "RGN_NOBNDRY"); + ParallelTransform* pt_result; + if (result.getCoordinates() == nullptr) { + pt_result = pt_; + } else { + pt_result = &result.getCoordinates()->getParallelTransform(); + } + result = pt_result->fromFieldAligned(result, "RGN_NOBNDRY"); + } else { + result = interp_to(f, location, "RGN_NOBNDRY"); + } + // Ensure result's data is unique. Otherwise result might be a duplicate of + // f (if no interpolation is needed, e.g. if interpolation is in the + // z-direction); then f would be communicated. Since this function is used + // on geometrical quantities that might not be periodic in y even on closed + // field lines (due to dependence on integrated shear), we don't want to + // communicate f. We will sort out result's boundary guard cells below, but + // not f's so we don't want to change f. + result.allocate(); + communicate(result); + + // Extrapolate into boundaries (if requested) so that differential geometry + // terms can be interpolated if necessary + // Note: cannot use applyBoundary("free_o3") here because applyBoundary() + // would try to create a new Coordinates object since we have not finished + // initializing yet, leading to an infinite recursion. + // Also, here we interpolate for the boundary points at xstart/ystart and + // (xend+1)/(yend+1) instead of extrapolating. + for (auto& bndry : localmesh->getBoundaries()) { + if ((extrapolate_x and bndry->bx != 0) or (extrapolate_y and bndry->by != 0)) { + int extrap_start = 0; + if (not no_extra_interpolate) { + // Can use no_extra_interpolate argument to skip the extra interpolation when we + // want to extrapolate the Christoffel symbol terms which come from derivatives so + // don't have the extra point set already + if ((location == CELL_XLOW) && (bndry->bx > 0)) { + extrap_start = 1; + } else if ((location == CELL_YLOW) && (bndry->by > 0)) { + extrap_start = 1; + } + } + for (bndry->first(); !bndry->isDone(); bndry->next1d()) { + // interpolate extra boundary point that is missed by interp_to, if + // necessary. + // Only interpolate this point if we are actually changing location. E.g. + // when we use this function to extrapolate J and Bxy on staggered grids, + // this point should already be set correctly because the metric + // components have been interpolated to here. + if (extrap_start > 0 and f.getLocation() != location) { + ASSERT1(bndry->bx == 0 or localmesh->xstart > 1); + ASSERT1(bndry->by == 0 or localmesh->ystart > 1); + // note that either bx or by is >0 here + for (int zi = 0; zi < localmesh->LocalNz; ++zi) { + result(bndry->x, bndry->y, zi) = + (9. + * (f(bndry->x - bndry->bx, bndry->y - bndry->by, zi) + + f(bndry->x, bndry->y, zi)) + - f(bndry->x - 2 * bndry->bx, bndry->y - 2 * bndry->by, zi) + - f(bndry->x + bndry->bx, bndry->y + bndry->by, zi)) + / 16.; + } + } + // set boundary guard cells + if ((bndry->bx != 0 && localmesh->GlobalNx - 2 * bndry->width >= 3) + || (bndry->by != 0 && localmesh->GlobalNy - 2 * bndry->width >= 3)) { + if (bndry->bx != 0 && localmesh->LocalNx == 1 && bndry->width == 1) { + throw BoutException( + "Not enough points in the x-direction on this " + "processor for extrapolation needed to use staggered grids. " + "Increase number of x-guard cells MXG or decrease number of " + "processors in the x-direction NXPE."); + } + if (bndry->by != 0 && localmesh->LocalNy == 1 && bndry->width == 1) { + throw BoutException( + "Not enough points in the y-direction on this " + "processor for extrapolation needed to use staggered grids. " + "Increase number of y-guard cells MYG or decrease number of " + "processors in the y-direction NYPE."); + } + // extrapolate into boundary guard cells if there are enough grid points + for (int i = extrap_start; i < bndry->width; i++) { + int xi = bndry->x + i * bndry->bx; + int yi = bndry->y + i * bndry->by; + for (int zi = 0; zi < localmesh->LocalNz; ++zi) { + result(xi, yi, zi) = + 3.0 * result(xi - bndry->bx, yi - bndry->by, zi) + - 3.0 * result(xi - 2 * bndry->bx, yi - 2 * bndry->by, zi) + + result(xi - 3 * bndry->bx, yi - 3 * bndry->by, zi); + } + } + } else { + // not enough grid points to extrapolate, set equal to last grid point + for (int i = extrap_start; i < bndry->width; i++) { + for (int zi = 0; zi < localmesh->LocalNz; ++zi) { + result(bndry->x + i * bndry->bx, bndry->y + i * bndry->by, zi) = + result(bndry->x - bndry->bx, bndry->y - bndry->by, zi); + } + } + } + } + } + } +#if CHECK > 0 + if (not( + // if include_corner_cells=true, then we extrapolate valid data into the + // corner cells if they are not already filled + localmesh->include_corner_cells + + // if we are not extrapolating at all, the corner cells should contain valid + // data + or (not extrapolate_x and not extrapolate_y))) { + // Invalidate corner guard cells + for (int i = 0; i < localmesh->xstart; i++) { + for (int j = 0; j < localmesh->ystart; j++) { + for (int k = 0; k < localmesh->LocalNz; ++k) { + result(i, j, k) = BoutNaN; + result(i, localmesh->LocalNy - 1 - j, k) = BoutNaN; + result(localmesh->LocalNx - 1 - i, j, k) = BoutNaN; + result(localmesh->LocalNx - 1 - i, localmesh->LocalNy - 1 - j, k) = BoutNaN; + } + } + } + } +#endif + + return result; +} + // If the CELL_CENTRE variable was read, the staggered version is required to // also exist for consistency void checkStaggeredGet(Mesh* mesh, const std::string& name, const std::string& suffix) { @@ -156,15 +320,26 @@ void checkStaggeredGet(Mesh* mesh, const std::string& name, const std::string& s } // convenience function for repeated code -int getAtLoc(Mesh* mesh, Field2D &var, const std::string& name, - const std::string& suffix, CELL_LOC location, BoutReal default_value = 0.) { +int getAtLoc(Mesh* mesh, Coordinates::FieldMetric& var, const std::string& name, + const std::string& suffix, CELL_LOC location, BoutReal default_value = 0.) { checkStaggeredGet(mesh, name, suffix); - int result = mesh->get(var, name+suffix, default_value, location); + int result = mesh->get(var, name + suffix, default_value, false, location); return result; } +int getAtLocAndFillGuards(Mesh* mesh, Coordinates::FieldMetric& var, + const std::string& name, const std::string& suffix, + CELL_LOC location, BoutReal default_value, bool extrapolate_x, + bool extrapolate_y, bool no_extra_interpolate, + ParallelTransform* pt) { + auto ret = getAtLoc(mesh, var, name, suffix, location, default_value); + var = interpolateAndExtrapolate(var, location, extrapolate_x, extrapolate_y, + no_extra_interpolate, pt); + return ret; +} + std::string getLocationSuffix(CELL_LOC location) { switch (location) { case CELL_CENTRE: { @@ -177,9 +352,8 @@ std::string getLocationSuffix(CELL_LOC location) { return "_ylow"; } case CELL_ZLOW: { - // geometrical quantities are Field2D, so CELL_ZLOW version is the same - // as CELL_CENTRE - return ""; + // in 2D metric, same as CELL_CENTRE + return bout::build::use_metric_3d ? "_zlow" : ""; } default: { throw BoutException("Incorrect location passed to " @@ -187,37 +361,33 @@ std::string getLocationSuffix(CELL_LOC location) { } } } -} - -Coordinates::Coordinates(Mesh* mesh, Field2D dx, Field2D dy, BoutReal dz, Field2D J, - Field2D Bxy, Field2D g11, Field2D g22, Field2D g33, Field2D g12, - Field2D g13, Field2D g23, Field2D g_11, Field2D g_22, - Field2D g_33, Field2D g_12, Field2D g_13, Field2D g_23, - Field2D ShiftTorsion, Field2D IntShiftTorsion, - bool calculate_geometry) +} // anonymous namespace + +Coordinates::Coordinates(Mesh* mesh, FieldMetric dx, FieldMetric dy, FieldMetric dz, + FieldMetric J, FieldMetric Bxy, FieldMetric g11, FieldMetric g22, + FieldMetric g33, FieldMetric g12, FieldMetric g13, + FieldMetric g23, FieldMetric g_11, FieldMetric g_22, + FieldMetric g_33, FieldMetric g_12, FieldMetric g_13, + FieldMetric g_23, FieldMetric ShiftTorsion, + FieldMetric IntShiftTorsion) : dx(std::move(dx)), dy(std::move(dy)), dz(dz), J(std::move(J)), Bxy(std::move(Bxy)), g11(std::move(g11)), g22(std::move(g22)), g33(std::move(g33)), g12(std::move(g12)), g13(std::move(g13)), g23(std::move(g23)), g_11(std::move(g_11)), g_22(std::move(g_22)), g_33(std::move(g_33)), g_12(std::move(g_12)), g_13(std::move(g_13)), g_23(std::move(g_23)), ShiftTorsion(std::move(ShiftTorsion)), IntShiftTorsion(std::move(IntShiftTorsion)), nz(mesh->LocalNz), localmesh(mesh), - location(CELL_CENTRE) { - if (calculate_geometry) { - if (geometry()) { - throw BoutException("Differential geometry failed\n"); - } - } -} + location(CELL_CENTRE) {} Coordinates::Coordinates(Mesh* mesh, Options* options) - : dx(1, mesh), dy(1, mesh), dz(1), d1_dx(mesh), d1_dy(mesh), J(1, mesh), Bxy(1, mesh), + : dx(1., mesh), dy(1., mesh), dz(1., mesh), d1_dx(mesh), d1_dy(mesh), d1_dz(mesh), + J(1., mesh), Bxy(1., mesh), // Identity metric tensor - g11(1, mesh), g22(1, mesh), g33(1, mesh), g12(0, mesh), g13(0, mesh), g23(0, mesh), - g_11(1, mesh), g_22(1, mesh), g_33(1, mesh), g_12(0, mesh), g_13(0, mesh), - g_23(0, mesh), G1_11(mesh), G1_22(mesh), G1_33(mesh), G1_12(mesh), G1_13(mesh), - G1_23(mesh), G2_11(mesh), G2_22(mesh), G2_33(mesh), G2_12(mesh), G2_13(mesh), - G2_23(mesh), G3_11(mesh), G3_22(mesh), G3_33(mesh), G3_12(mesh), G3_13(mesh), - G3_23(mesh), G1(mesh), G2(mesh), G3(mesh), ShiftTorsion(mesh), + g11(1., mesh), g22(1., mesh), g33(1., mesh), g12(0, mesh), g13(0, mesh), + g23(0, mesh), g_11(1., mesh), g_22(1., mesh), g_33(1., mesh), g_12(0, mesh), + g_13(0, mesh), g_23(0, mesh), G1_11(mesh), G1_22(mesh), G1_33(mesh), G1_12(mesh), + G1_13(mesh), G1_23(mesh), G2_11(mesh), G2_22(mesh), G2_33(mesh), G2_12(mesh), + G2_13(mesh), G2_23(mesh), G3_11(mesh), G3_22(mesh), G3_33(mesh), G3_12(mesh), + G3_13(mesh), G3_23(mesh), G1(mesh), G2(mesh), G3(mesh), ShiftTorsion(mesh), IntShiftTorsion(mesh), localmesh(mesh), location(CELL_CENTRE) { if (options == nullptr) { @@ -241,15 +411,8 @@ Coordinates::Coordinates(Mesh* mesh, Options* options) "cells. Set option extrapolate_y=false to disable this.\n")); } - mesh->get(dx, "dx", 1.0); - dx = interpolateAndExtrapolate(dx, location, extrapolate_x, extrapolate_y); - - if (mesh->periodicX) { - mesh->communicate(dx); - } - - mesh->get(dy, "dy", 1.0); - dy = interpolateAndExtrapolate(dy, location, extrapolate_x, extrapolate_y); + mesh->get(dx, "dx", 1.0, false); + mesh->get(dy, "dy", 1.0, false); nz = mesh->LocalNz; @@ -262,24 +425,52 @@ Coordinates::Coordinates(Mesh* mesh, Options* options) const auto default_dz = (zmax - zmin) * TWOPI / nz; - mesh->get(dz, "dz", default_dz); + mesh->get(dz, "dz", default_dz, false); } + // required early for differentiation. + setParallelTransform(options); + + dz = interpolateAndExtrapolate(dz, location, extrapolate_x, extrapolate_y, false, + transform.get()); + dx = interpolateAndExtrapolate(dx, location, extrapolate_x, extrapolate_y, false, + transform.get()); + + if (mesh->periodicX) { + communicate(dx); + } + + dy = interpolateAndExtrapolate(dy, location, extrapolate_x, extrapolate_y, false, + transform.get()); + + auto getUnaligned = [this](auto& field, const std::string& name, + BoutReal default_value) { + localmesh->get(field, name, default_value, false); + if (field.getDirectionY() == YDirectionType::Aligned + and transform->canToFromFieldAligned()) { + return transform->fromFieldAligned(field); + } else { + return field.setDirectionY(YDirectionType::Standard); + } + }; + + auto getUnalignedAtLocation = [this, extrapolate_x, extrapolate_y, + getUnaligned](auto& field, const std::string& name, + BoutReal default_value) { + field = getUnaligned(field, name, default_value); + return interpolateAndExtrapolate(field, location, extrapolate_x, extrapolate_y, false, + transform.get()); + }; + // Diagonal components of metric tensor g^{ij} (default to 1) - mesh->get(g11, "g11", 1.0); - g11 = interpolateAndExtrapolate(g11, location, extrapolate_x, extrapolate_y); - mesh->get(g22, "g22", 1.0); - g22 = interpolateAndExtrapolate(g22, location, extrapolate_x, extrapolate_y); - mesh->get(g33, "g33", 1.0); - g33 = interpolateAndExtrapolate(g33, location, extrapolate_x, extrapolate_y); + g11 = getUnalignedAtLocation(g11, "g11", 1.0); + g22 = getUnalignedAtLocation(g22, "g22", 1.0); + g33 = getUnalignedAtLocation(g33, "g33", 1.0); // Off-diagonal elements. Default to 0 - mesh->get(g12, "g12", 0.0); - g12 = interpolateAndExtrapolate(g12, location, extrapolate_x, extrapolate_y); - mesh->get(g13, "g13", 0.0); - g13 = interpolateAndExtrapolate(g13, location, extrapolate_x, extrapolate_y); - mesh->get(g23, "g23", 0.0); - g23 = interpolateAndExtrapolate(g23, location, extrapolate_x, extrapolate_y); + g12 = getUnalignedAtLocation(g12, "g12", 0.0); + g13 = getUnalignedAtLocation(g13, "g13", 0.0); + g23 = getUnalignedAtLocation(g23, "g23", 0.0); // Check input metrics // Diagonal metric components should be finite @@ -306,12 +497,12 @@ Coordinates::Coordinates(Mesh* mesh, Options* options) // Check that all components are present if (std::all_of(begin(covariant_component_names), end(covariant_component_names), source_has_component)) { - mesh->get(g_11, "g_11"); - mesh->get(g_22, "g_22"); - mesh->get(g_33, "g_33"); - mesh->get(g_12, "g_12"); - mesh->get(g_13, "g_13"); - mesh->get(g_23, "g_23"); + g_11 = getUnaligned(g_11, "g_11", 1.0); + g_22 = getUnaligned(g_22, "g_22", 1.0); + g_33 = getUnaligned(g_33, "g_33", 1.0); + g_12 = getUnaligned(g_12, "g_12", 0.0); + g_13 = getUnaligned(g_13, "g_13", 0.0); + g_23 = getUnaligned(g_23, "g_23", 0.0); output_warn.write("\tWARNING! Covariant components of metric tensor set manually. " "Contravariant components NOT recalculated\n"); @@ -332,12 +523,18 @@ Coordinates::Coordinates(Mesh* mesh, Options* options) } // More robust to extrapolate derived quantities directly, rather than // deriving from extrapolated covariant metric components - g_11 = interpolateAndExtrapolate(g_11, location, extrapolate_x, extrapolate_y); - g_22 = interpolateAndExtrapolate(g_22, location, extrapolate_x, extrapolate_y); - g_33 = interpolateAndExtrapolate(g_33, location, extrapolate_x, extrapolate_y); - g_12 = interpolateAndExtrapolate(g_12, location, extrapolate_x, extrapolate_y); - g_13 = interpolateAndExtrapolate(g_13, location, extrapolate_x, extrapolate_y); - g_23 = interpolateAndExtrapolate(g_23, location, extrapolate_x, extrapolate_y); + g_11 = interpolateAndExtrapolate(g_11, location, extrapolate_x, extrapolate_y, false, + transform.get()); + g_22 = interpolateAndExtrapolate(g_22, location, extrapolate_x, extrapolate_y, false, + transform.get()); + g_33 = interpolateAndExtrapolate(g_33, location, extrapolate_x, extrapolate_y, false, + transform.get()); + g_12 = interpolateAndExtrapolate(g_12, location, extrapolate_x, extrapolate_y, false, + transform.get()); + g_13 = interpolateAndExtrapolate(g_13, location, extrapolate_x, extrapolate_y, false, + transform.get()); + g_23 = interpolateAndExtrapolate(g_23, location, extrapolate_x, extrapolate_y, false, + transform.get()); // Check covariant metrics // Diagonal metric components should be finite @@ -359,16 +556,19 @@ Coordinates::Coordinates(Mesh* mesh, Options* options) // Attempt to read J from the grid file auto Jcalc = J; - if (mesh->get(J, "J")) { + if (mesh->get(J, "J", 0.0, false)) { output_warn.write( "\tWARNING: Jacobian 'J' not found. Calculating from metric tensor\n"); J = Jcalc; } else { - J = interpolateAndExtrapolate(J, location, extrapolate_x, extrapolate_y); + J = interpolateAndExtrapolate(J, location, extrapolate_x, extrapolate_y, false, + transform.get()); // Compare calculated and loaded values output_warn.write("\tMaximum difference in J is {:e}\n", max(abs(J - Jcalc))); + communicate(J); + // Re-evaluate Bxy using new J Bxy = sqrt(g_22) / J; } @@ -382,13 +582,14 @@ Coordinates::Coordinates(Mesh* mesh, Options* options) // Attempt to read Bxy from the grid file auto Bcalc = Bxy; - if (mesh->get(Bxy, "Bxy")) { + if (mesh->get(Bxy, "Bxy", 0.0, false)) { output_warn.write("\tWARNING: Magnitude of B field 'Bxy' not found. Calculating from " "metric tensor\n"); Bxy = Bcalc; } else { - Bxy = interpolateAndExtrapolate(Bxy, location, extrapolate_x, extrapolate_y); + Bxy = interpolateAndExtrapolate(Bxy, location, extrapolate_x, extrapolate_y, false, + transform.get()); output_warn.write("\tMaximum difference in Bxy is {:e}\n", max(abs(Bxy - Bcalc))); } @@ -396,53 +597,119 @@ Coordinates::Coordinates(Mesh* mesh, Options* options) bout::checkFinite(Bxy, "Bxy", "RGN_NOCORNERS"); bout::checkPositive(Bxy, "Bxy", "RGN_NOCORNERS"); - ////////////////////////////////////////////////////// - /// Calculate Christoffel symbols. Needs communication - if (geometry()) { - throw BoutException("Differential geometry failed\n"); - } - - if (mesh->get(ShiftTorsion, "ShiftTorsion")) { + if (mesh->get(ShiftTorsion, "ShiftTorsion", 0.0, false)) { output_warn.write( "\tWARNING: No Torsion specified for zShift. Derivatives may not be correct\n"); ShiftTorsion = 0.0; } - ShiftTorsion = interpolateAndExtrapolate(ShiftTorsion, location, extrapolate_x, extrapolate_y); + ShiftTorsion = interpolateAndExtrapolate(ShiftTorsion, location, extrapolate_x, + extrapolate_y, false, transform.get()); ////////////////////////////////////////////////////// if (mesh->IncIntShear) { - if (mesh->get(IntShiftTorsion, "IntShiftTorsion")) { + if (mesh->get(IntShiftTorsion, "IntShiftTorsion", 0.0, false)) { output_warn.write("\tWARNING: No Integrated torsion specified\n"); - IntShiftTorsion = 0.0; } - IntShiftTorsion = interpolateAndExtrapolate(IntShiftTorsion, location, extrapolate_x, extrapolate_y); + IntShiftTorsion = interpolateAndExtrapolate(IntShiftTorsion, location, extrapolate_x, + extrapolate_y, false, transform.get()); } else { // IntShiftTorsion will not be used, but set to zero to avoid uninitialized field IntShiftTorsion = 0.; } +} - setParallelTransform(options); +// use anonymous namespace so this utility function is not available outside this file +namespace { +/// Interpolate a FieldMetric to a new CELL_LOC with interp_to. +/// Communicates to set internal guard cells. +/// Boundary guard cells are set equal to the nearest grid point (equivalent to +/// 2nd order accurate Neumann boundary condition). +/// Corner guard cells are set to BoutNaN +Coordinates::FieldMetric +interpolateAndNeumann(MAYBE_UNUSED(const Coordinates::FieldMetric& f), + MAYBE_UNUSED(CELL_LOC location), + MAYBE_UNUSED(ParallelTransform* pt)) { + Mesh* localmesh = f.getMesh(); + Coordinates::FieldMetric result; +#if BOUT_USE_METRIC_3D + if (location == CELL_YLOW) { + auto f_aligned = f.getCoordinates() == nullptr ? pt->toFieldAligned(f, "RGN_NOX") + : toFieldAligned(f, "RGN_NOX"); + result = interp_to(f_aligned, location, "RGN_NOBNDRY"); + result = result.getCoordinates() == nullptr + ? pt->fromFieldAligned(result, "RGN_NOBNDRY") + : fromFieldAligned(result, "RGN_NOBNDRY"); + } else +#endif + { + result = interp_to(f, location, "RGN_NOBNDRY"); + } + communicate(result); + + // Copy nearest value into boundaries so that differential geometry terms can + // be interpolated if necessary + // 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()) { + 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()) { + for (int i = 0; i < localmesh->xstart; i++) { + for (int z = 0; z < result.getNz(); ++z) { + result(bndry->x + i * bndry->bx, bndry->y, z) = + result(bndry->x + (i - 1) * bndry->bx, bndry->y - bndry->by, z); + } + } + } + } + if (bndry->by != 0) { + // If by!=0 we are on a y-boundary, upper if by>0 and lower if by<0 + for (bndry->first(); !bndry->isDone(); bndry->next1d()) { + for (int i = 0; i < localmesh->ystart; i++) { + for (int z = 0; z < result.getNz(); ++z) { + result(bndry->x, bndry->y + i * bndry->by, z) = + result(bndry->x - bndry->bx, bndry->y + (i - 1) * bndry->by, z); + } + } + } + } + } + + // Set corner guard cells + for (int i = 0; i < localmesh->xstart; i++) { + for (int j = 0; j < localmesh->ystart; j++) { + for (int z = 0; z < result.getNz(); ++z) { + result(i, j, z) = BoutNaN; + result(i, localmesh->LocalNy - 1 - j, z) = BoutNaN; + result(localmesh->LocalNx - 1 - i, j, z) = BoutNaN; + result(localmesh->LocalNx - 1 - i, localmesh->LocalNy - 1 - j, z) = BoutNaN; + } + } + } + + return result; } +} // namespace Coordinates::Coordinates(Mesh* mesh, Options* options, const CELL_LOC loc, - const Coordinates* coords_in, bool force_interpolate_from_centre) - : dx(1, mesh), dy(1, mesh), dz(1), d1_dx(mesh), d1_dy(mesh), J(1, mesh), Bxy(1, mesh), + const Coordinates* coords_in, bool force_interpolate_from_centre) + : dx(1., mesh), dy(1., mesh), dz(1., mesh), d1_dx(mesh), d1_dy(mesh), d1_dz(mesh), + J(1., mesh), Bxy(1., mesh), // Identity metric tensor - g11(1, mesh), g22(1, mesh), g33(1, mesh), g12(0, mesh), g13(0, mesh), g23(0, mesh), - g_11(1, mesh), g_22(1, mesh), g_33(1, mesh), g_12(0, mesh), g_13(0, mesh), - g_23(0, mesh), G1_11(mesh), G1_22(mesh), G1_33(mesh), G1_12(mesh), G1_13(mesh), - G1_23(mesh), G2_11(mesh), G2_22(mesh), G2_33(mesh), G2_12(mesh), G2_13(mesh), - G2_23(mesh), G3_11(mesh), G3_22(mesh), G3_33(mesh), G3_12(mesh), G3_13(mesh), - G3_23(mesh), G1(mesh), G2(mesh), G3(mesh), ShiftTorsion(mesh), + g11(1., mesh), g22(1., mesh), g33(1., mesh), g12(0, mesh), g13(0, mesh), + g23(0, mesh), g_11(1., mesh), g_22(1., mesh), g_33(1., mesh), g_12(0, mesh), + g_13(0, mesh), g_23(0, mesh), G1_11(mesh), G1_22(mesh), G1_33(mesh), G1_12(mesh), + G1_13(mesh), G1_23(mesh), G2_11(mesh), G2_22(mesh), G2_33(mesh), G2_12(mesh), + G2_13(mesh), G2_23(mesh), G3_11(mesh), G3_22(mesh), G3_33(mesh), G3_12(mesh), + G3_13(mesh), G3_23(mesh), G1(mesh), G2(mesh), G3(mesh), ShiftTorsion(mesh), IntShiftTorsion(mesh), localmesh(mesh), location(loc) { std::string suffix = getLocationSuffix(location); nz = mesh->LocalNz; - dz = coords_in->dz; - // Default to true in case staggered quantities are not read from file bool extrapolate_x = true; bool extrapolate_y = true; @@ -462,30 +729,45 @@ Coordinates::Coordinates(Mesh* mesh, Options* options, const CELL_LOC loc, "cells\n")); } - getAtLoc(mesh, dx, "dx", suffix, location, 1.0); - dx = interpolateAndExtrapolate(dx, location, extrapolate_x, extrapolate_y); + { + auto& options = Options::root(); + const bool has_zperiod = options.isSet("zperiod"); + const auto zmin = has_zperiod ? 0.0 : options["ZMIN"].withDefault(0.0); + const auto zmax = has_zperiod ? 1.0 / options["zperiod"].withDefault(1.0) + : options["ZMAX"].withDefault(1.0); + + const auto default_dz = (zmax - zmin) * TWOPI / nz; + getAtLoc(mesh, dz, "dz", suffix, location, default_dz); + } + setParallelTransform(options); + + dz = interpolateAndExtrapolate(dz, location, extrapolate_x, extrapolate_y, false, + transform.get()); + + getAtLocAndFillGuards(mesh, dx, "dx", suffix, location, 1.0, extrapolate_x, + extrapolate_y, false, transform.get()); if (mesh->periodicX) { - mesh->communicate(dx); + communicate(dx); } - getAtLoc(mesh, dy, "dy", suffix, location, 1.0); - dy = interpolateAndExtrapolate(dy, location, extrapolate_x, extrapolate_y); + getAtLocAndFillGuards(mesh, dy, "dy", suffix, location, 1.0, extrapolate_x, + extrapolate_y, false, transform.get()); // grid data source has staggered fields, so read instead of interpolating // Diagonal components of metric tensor g^{ij} (default to 1) - getAtLoc(mesh, g11, "g11", suffix, location, 1.0); - g11 = interpolateAndExtrapolate(g11, location, extrapolate_x, extrapolate_y); - getAtLoc(mesh, g22, "g22", suffix, location, 1.0); - g22 = interpolateAndExtrapolate(g22, location, extrapolate_x, extrapolate_y); - getAtLoc(mesh, g33, "g33", suffix, location, 1.0); - g33 = interpolateAndExtrapolate(g33, location, extrapolate_x, extrapolate_y); - getAtLoc(mesh, g12, "g12", suffix, location, 0.0); - g12 = interpolateAndExtrapolate(g12, location, extrapolate_x, extrapolate_y); - getAtLoc(mesh, g13, "g13", suffix, location, 0.0); - g13 = interpolateAndExtrapolate(g13, location, extrapolate_x, extrapolate_y); - getAtLoc(mesh, g23, "g23", suffix, location, 0.0); - g23 = interpolateAndExtrapolate(g23, location, extrapolate_x, extrapolate_y); + getAtLocAndFillGuards(mesh, g11, "g11", suffix, location, 1.0, extrapolate_x, + extrapolate_y, false, transform.get()); + getAtLocAndFillGuards(mesh, g22, "g22", suffix, location, 1.0, extrapolate_x, + extrapolate_y, false, transform.get()); + getAtLocAndFillGuards(mesh, g33, "g33", suffix, location, 1.0, extrapolate_x, + extrapolate_y, false, transform.get()); + getAtLocAndFillGuards(mesh, g12, "g12", suffix, location, 0.0, extrapolate_x, + extrapolate_y, false, transform.get()); + getAtLocAndFillGuards(mesh, g13, "g13", suffix, location, 0.0, extrapolate_x, + extrapolate_y, false, transform.get()); + getAtLocAndFillGuards(mesh, g23, "g23", suffix, location, 0.0, extrapolate_x, + extrapolate_y, false, transform.get()); // Check input metrics // Diagonal metric components should be finite @@ -539,12 +821,18 @@ Coordinates::Coordinates(Mesh* mesh, Options* options, const CELL_LOC loc, } // More robust to extrapolate derived quantities directly, rather than // deriving from extrapolated covariant metric components - g_11 = interpolateAndExtrapolate(g_11, location, extrapolate_x, extrapolate_y); - g_22 = interpolateAndExtrapolate(g_22, location, extrapolate_x, extrapolate_y); - g_33 = interpolateAndExtrapolate(g_33, location, extrapolate_x, extrapolate_y); - g_12 = interpolateAndExtrapolate(g_12, location, extrapolate_x, extrapolate_y); - g_13 = interpolateAndExtrapolate(g_13, location, extrapolate_x, extrapolate_y); - g_23 = interpolateAndExtrapolate(g_23, location, extrapolate_x, extrapolate_y); + g_11 = interpolateAndExtrapolate(g_11, location, extrapolate_x, extrapolate_y, false, + transform.get()); + g_22 = interpolateAndExtrapolate(g_22, location, extrapolate_x, extrapolate_y, false, + transform.get()); + g_33 = interpolateAndExtrapolate(g_33, location, extrapolate_x, extrapolate_y, false, + transform.get()); + g_12 = interpolateAndExtrapolate(g_12, location, extrapolate_x, extrapolate_y, false, + transform.get()); + g_13 = interpolateAndExtrapolate(g_13, location, extrapolate_x, extrapolate_y, false, + transform.get()); + g_23 = interpolateAndExtrapolate(g_23, location, extrapolate_x, extrapolate_y, false, + transform.get()); // Check covariant metrics // Diagonal metric components should be finite @@ -570,11 +858,12 @@ Coordinates::Coordinates(Mesh* mesh, Options* options, const CELL_LOC loc, auto Jcalc = J; if (getAtLoc(mesh, J, "J", suffix, location)) { output_warn.write( - "\tWARNING: Jacobian 'J_%s' not found. Calculating from metric tensor\n", - suffix.c_str()); + "\tWARNING: Jacobian 'J_{:s}' not found. Calculating from metric tensor\n", + suffix); J = Jcalc; } else { - J = interpolateAndExtrapolate(J, location, extrapolate_x, extrapolate_y); + J = interpolateAndExtrapolate(J, location, extrapolate_x, extrapolate_y, false, + transform.get()); // Compare calculated and loaded values output_warn.write("\tMaximum difference in J is %e\n", max(abs(J - Jcalc))); @@ -587,17 +876,20 @@ Coordinates::Coordinates(Mesh* mesh, Options* options, const CELL_LOC loc, bout::checkFinite(J, "J" + suffix, "RGN_NOCORNERS"); bout::checkPositive(J, "J" + suffix, "RGN_NOCORNERS"); if (min(abs(J)) < 1.0e-10) { - throw BoutException("\tERROR: Jacobian%s becomes very small\n", suffix.c_str()); + throw BoutException("\tERROR: Jacobian{:s} becomes very small\n", suffix); } // Attempt to read Bxy from the grid file auto Bcalc = Bxy; if (getAtLoc(mesh, Bxy, "Bxy", suffix, location)) { - output_warn.write("\tWARNING: Magnitude of B field 'Bxy_%s' not found. Calculating " - " from metric tensor\n", suffix.c_str()); + output_warn.write( + "\tWARNING: Magnitude of B field 'Bxy_{:s}' not found. Calculating " + " from metric tensor\n", + suffix); Bxy = Bcalc; } else { - Bxy = interpolateAndExtrapolate(Bxy, location, extrapolate_x, extrapolate_y); + Bxy = interpolateAndExtrapolate(Bxy, location, extrapolate_x, extrapolate_y, false, + transform.get()); output_warn.write("\tMaximum difference in Bxy is %e\n", max(abs(Bxy - Bcalc))); } @@ -607,23 +899,26 @@ Coordinates::Coordinates(Mesh* mesh, Options* options, const CELL_LOC loc, bout::checkPositive(Bxy, "Bxy" + suffix, "RGN_NOCORNERS"); checkStaggeredGet(mesh, "ShiftTorsion", suffix); - if (mesh->get(ShiftTorsion, "ShiftTorsion"+suffix)) { + if (mesh->get(ShiftTorsion, "ShiftTorsion" + suffix, 0.0, false)) { output_warn.write("\tWARNING: No Torsion specified for zShift. Derivatives may not be correct\n"); ShiftTorsion = 0.0; } ShiftTorsion.setLocation(location); - ShiftTorsion = interpolateAndExtrapolate(ShiftTorsion, location, extrapolate_x, extrapolate_y); + ShiftTorsion = interpolateAndExtrapolate(ShiftTorsion, location, extrapolate_x, + extrapolate_y, false, transform.get()); ////////////////////////////////////////////////////// if (mesh->IncIntShear) { checkStaggeredGet(mesh, "IntShiftTorsion", suffix); - if (mesh->get(IntShiftTorsion, "IntShiftTorsion"+suffix)) { + if (mesh->get(IntShiftTorsion, "IntShiftTorsion" + suffix, 0.0, false)) { output_warn.write("\tWARNING: No Integrated torsion specified\n"); IntShiftTorsion = 0.0; } IntShiftTorsion.setLocation(location); - IntShiftTorsion = interpolateAndExtrapolate(IntShiftTorsion, location, extrapolate_x, extrapolate_y); + IntShiftTorsion = + interpolateAndExtrapolate(IntShiftTorsion, location, extrapolate_x, + extrapolate_y, false, transform.get()); } else { // IntShiftTorsion will not be used, but set to zero to avoid uninitialized field IntShiftTorsion = 0.; @@ -631,29 +926,55 @@ Coordinates::Coordinates(Mesh* mesh, Options* options, const CELL_LOC loc, } else { // Interpolate fields from coords_in - dx = interpolateAndExtrapolate(coords_in->dx, location); - dy = interpolateAndExtrapolate(coords_in->dy, location); + if (isUniform(coords_in->dz)) { + dz = coords_in->dz; + dz.setLocation(location); + } else { + throw BoutException( + "We are asked to transform dz to get dz before we have a transform, which " + "might require dz!\nPlease provide a dz for the staggered quantity!"); + } + setParallelTransform(options); + dx = interpolateAndExtrapolate(coords_in->dx, location, true, true, false, + transform.get()); + dy = interpolateAndExtrapolate(coords_in->dy, location, true, true, false, + transform.get()); + // not really needed - we have used dz already ... + dz = interpolateAndExtrapolate(coords_in->dz, location, true, true, false, + transform.get()); // Diagonal components of metric tensor g^{ij} - g11 = interpolateAndExtrapolate(coords_in->g11, location); - g22 = interpolateAndExtrapolate(coords_in->g22, location); - g33 = interpolateAndExtrapolate(coords_in->g33, location); + g11 = interpolateAndExtrapolate(coords_in->g11, location, true, true, false, + transform.get()); + g22 = interpolateAndExtrapolate(coords_in->g22, location, true, true, false, + transform.get()); + g33 = interpolateAndExtrapolate(coords_in->g33, location, true, true, false, + transform.get()); // Off-diagonal elements. - g12 = interpolateAndExtrapolate(coords_in->g12, location); - g13 = interpolateAndExtrapolate(coords_in->g13, location); - g23 = interpolateAndExtrapolate(coords_in->g23, location); + g12 = interpolateAndExtrapolate(coords_in->g12, location, true, true, false, + transform.get()); + g13 = interpolateAndExtrapolate(coords_in->g13, location, true, true, false, + transform.get()); + g23 = interpolateAndExtrapolate(coords_in->g23, location, true, true, false, + transform.get()); // 3x3 matrix inversion can exaggerate small interpolation errors, so it is // more robust to interpolate and extrapolate derived quantities directly, // rather than deriving from interpolated/extrapolated covariant metric // components - g_11 = interpolateAndExtrapolate(coords_in->g_11, location); - g_22 = interpolateAndExtrapolate(coords_in->g_22, location); - g_33 = interpolateAndExtrapolate(coords_in->g_33, location); - g_12 = interpolateAndExtrapolate(coords_in->g_12, location); - g_13 = interpolateAndExtrapolate(coords_in->g_13, location); - g_23 = interpolateAndExtrapolate(coords_in->g_23, location); + g_11 = interpolateAndExtrapolate(coords_in->g_11, location, true, true, false, + transform.get()); + g_22 = interpolateAndExtrapolate(coords_in->g_22, location, true, true, false, + transform.get()); + g_33 = interpolateAndExtrapolate(coords_in->g_33, location, true, true, false, + transform.get()); + g_12 = interpolateAndExtrapolate(coords_in->g_12, location, true, true, false, + transform.get()); + g_13 = interpolateAndExtrapolate(coords_in->g_13, location, true, true, false, + transform.get()); + g_23 = interpolateAndExtrapolate(coords_in->g_23, location, true, true, false, + transform.get()); // Check input metrics // Diagonal metric components should be finite @@ -678,28 +999,24 @@ Coordinates::Coordinates(Mesh* mesh, Options* options, const CELL_LOC loc, bout::checkFinite(g_13, "g_13", "RGN_NOCORNERS"); bout::checkFinite(g_23, "g_23", "RGN_NOCORNERS"); - J = interpolateAndExtrapolate(coords_in->J, location); - Bxy = interpolateAndExtrapolate(coords_in->Bxy, location); + J = interpolateAndExtrapolate(coords_in->J, location, true, true, false, + transform.get()); + Bxy = interpolateAndExtrapolate(coords_in->Bxy, location, true, true, false, + transform.get()); bout::checkFinite(J, "The Jacobian", "RGN_NOCORNERS"); bout::checkPositive(J, "The Jacobian", "RGN_NOCORNERS"); bout::checkFinite(Bxy, "Bxy", "RGN_NOCORNERS"); bout::checkPositive(Bxy, "Bxy", "RGN_NOCORNERS"); - ShiftTorsion = interpolateAndExtrapolate(coords_in->ShiftTorsion, location); + ShiftTorsion = interpolateAndExtrapolate(coords_in->ShiftTorsion, location, true, + true, false, transform.get()); if (mesh->IncIntShear) { - IntShiftTorsion = interpolateAndExtrapolate(coords_in->IntShiftTorsion, location); + IntShiftTorsion = interpolateAndExtrapolate(coords_in->IntShiftTorsion, location, + true, true, false, transform.get()); } } - - ////////////////////////////////////////////////////// - /// Calculate Christoffel symbols. Needs communication - if (geometry(false, force_interpolate_from_centre)) { - throw BoutException("Differential geometry failed while constructing staggered Coordinates"); - } - - setParallelTransform(options); } void Coordinates::outputVars(Datafile& file) { @@ -736,6 +1053,8 @@ void Coordinates::outputVars(Datafile& file) { int Coordinates::geometry(bool recalculate_staggered, bool force_interpolate_from_centre) { TRACE("Coordinates::geometry"); + communicate(dx, dy, dz, g11, g22, g33, g12, g13, g23, g_11, g_22, g_33, g_12, g_13, + g_23, J, Bxy); output_progress.write("Calculating differential geometry terms\n"); @@ -745,7 +1064,7 @@ int Coordinates::geometry(bool recalculate_staggered, if (min(abs(dy)) < 1e-8) throw BoutException("dy magnitude less than 1e-8"); - if (fabs(dz) < 1e-8) + if (min(abs(dz)) < 1e-8) throw BoutException("dz magnitude less than 1e-8"); // Check input metrics @@ -837,41 +1156,21 @@ int Coordinates::geometry(bool recalculate_staggered, G3_23 = 0.5 * g13 * (DDZ(g_12) + DDY(g_13) - DDX(g_23)) + 0.5 * g23 * DDZ(g_22) + 0.5 * g33 * DDY(g_33); - G1 = (DDX(J * g11) + DDY(J * g12) + DDZ(J * g13)) / J; - G2 = (DDX(J * g12) + DDY(J * g22) + DDZ(J * g23)) / J; - G3 = (DDX(J * g13) + DDY(J * g23) + DDZ(J * g33)) / J; + auto tmp = J * g12; + communicate(tmp); + G1 = (DDX(J * g11) + DDY(tmp) + DDZ(J * g13)) / J; + tmp = J * g22; + communicate(tmp); + G2 = (DDX(J * g12) + DDY(tmp) + DDZ(J * g23)) / J; + tmp = J * g23; + communicate(tmp); + G3 = (DDX(J * g13) + DDY(tmp) + DDZ(J * g33)) / J; // Communicate christoffel symbol terms output_progress.write("\tCommunicating connection terms\n"); - FieldGroup com; - - com.add(G1_11); - com.add(G1_22); - com.add(G1_33); - com.add(G1_12); - com.add(G1_13); - com.add(G1_23); - - com.add(G2_11); - com.add(G2_22); - com.add(G2_33); - com.add(G2_12); - com.add(G2_13); - com.add(G2_23); - - com.add(G3_11); - com.add(G3_22); - com.add(G3_33); - com.add(G3_12); - com.add(G3_13); - com.add(G3_23); - - com.add(G1); - com.add(G2); - com.add(G3); - - localmesh->communicate(com); + communicate(G1_11, G1_22, G1_33, G1_12, G1_13, G1_23, G2_11, G2_22, G2_33, G2_12, G2_13, + G2_23, G3_11, G3_22, G3_33, G3_12, G3_13, G3_23, G1, G2, G3); // Set boundary guard cells of Christoffel symbol terms // Ideally, when location is staggered, we would set the upper/outer boundary point @@ -883,37 +1182,39 @@ int Coordinates::geometry(bool recalculate_staggered, // CELL_YLOW grid is at a 'guard cell' location (yend+1). // However, the above would require lots of special handling, so just extrapolate for // now. - G1_11 = interpolateAndExtrapolate(G1_11, location, true, true, true); - G1_22 = interpolateAndExtrapolate(G1_22, location, true, true, true); - G1_33 = interpolateAndExtrapolate(G1_33, location, true, true, true); - G1_12 = interpolateAndExtrapolate(G1_12, location, true, true, true); - G1_13 = interpolateAndExtrapolate(G1_13, location, true, true, true); - G1_23 = interpolateAndExtrapolate(G1_23, location, true, true, true); - - G2_11 = interpolateAndExtrapolate(G2_11, location, true, true, true); - G2_22 = interpolateAndExtrapolate(G2_22, location, true, true, true); - G2_33 = interpolateAndExtrapolate(G2_33, location, true, true, true); - G2_12 = interpolateAndExtrapolate(G2_12, location, true, true, true); - G2_13 = interpolateAndExtrapolate(G2_13, location, true, true, true); - G2_23 = interpolateAndExtrapolate(G2_23, location, true, true, true); - - G3_11 = interpolateAndExtrapolate(G3_11, location, true, true, true); - G3_22 = interpolateAndExtrapolate(G3_22, location, true, true, true); - G3_33 = interpolateAndExtrapolate(G3_33, location, true, true, true); - G3_12 = interpolateAndExtrapolate(G3_12, location, true, true, true); - G3_13 = interpolateAndExtrapolate(G3_13, location, true, true, true); - G3_23 = interpolateAndExtrapolate(G3_23, location, true, true, true); - - G1 = interpolateAndExtrapolate(G1, location, true, true, true); - G2 = interpolateAndExtrapolate(G2, location, true, true, true); - G3 = interpolateAndExtrapolate(G3, location, true, true, true); + G1_11 = interpolateAndExtrapolate(G1_11, location, true, true, true, transform.get()); + G1_22 = interpolateAndExtrapolate(G1_22, location, true, true, true, transform.get()); + G1_33 = interpolateAndExtrapolate(G1_33, location, true, true, true, transform.get()); + G1_12 = interpolateAndExtrapolate(G1_12, location, true, true, true, transform.get()); + G1_13 = interpolateAndExtrapolate(G1_13, location, true, true, true, transform.get()); + G1_23 = interpolateAndExtrapolate(G1_23, location, true, true, true, transform.get()); + + G2_11 = interpolateAndExtrapolate(G2_11, location, true, true, true, transform.get()); + G2_22 = interpolateAndExtrapolate(G2_22, location, true, true, true, transform.get()); + G2_33 = interpolateAndExtrapolate(G2_33, location, true, true, true, transform.get()); + G2_12 = interpolateAndExtrapolate(G2_12, location, true, true, true, transform.get()); + G2_13 = interpolateAndExtrapolate(G2_13, location, true, true, true, transform.get()); + G2_23 = interpolateAndExtrapolate(G2_23, location, true, true, true, transform.get()); + + G3_11 = interpolateAndExtrapolate(G3_11, location, true, true, true, transform.get()); + G3_22 = interpolateAndExtrapolate(G3_22, location, true, true, true, transform.get()); + G3_33 = interpolateAndExtrapolate(G3_33, location, true, true, true, transform.get()); + G3_12 = interpolateAndExtrapolate(G3_12, location, true, true, true, transform.get()); + G3_13 = interpolateAndExtrapolate(G3_13, location, true, true, true, transform.get()); + G3_23 = interpolateAndExtrapolate(G3_23, location, true, true, true, transform.get()); + + G1 = interpolateAndExtrapolate(G1, location, true, true, true, transform.get()); + G2 = interpolateAndExtrapolate(G2, location, true, true, true, transform.get()); + G3 = interpolateAndExtrapolate(G3, location, true, true, true, transform.get()); ////////////////////////////////////////////////////// /// Non-uniform meshes. Need to use DDX, DDY OPTION(Options::getRoot(), non_uniform, true); - Field2D d2x(localmesh), d2y(localmesh); // d^2 x / d i^2 + Coordinates::FieldMetric d2x(localmesh), d2y(localmesh), + d2z(localmesh); // d^2 x / d i^2 + // Read correction for non-uniform meshes std::string suffix = getLocationSuffix(location); if (location == CELL_CENTRE or (!force_interpolate_from_centre @@ -921,64 +1222,110 @@ int Coordinates::geometry(bool recalculate_staggered, bool extrapolate_x = not localmesh->sourceHasXBoundaryGuards(); bool extrapolate_y = not localmesh->sourceHasYBoundaryGuards(); - if (localmesh->get(d2x, "d2x"+suffix, 0.0, location)) { + if (localmesh->get(d2x, "d2x" + suffix, 0.0, false, location)) { output_warn.write( "\tWARNING: differencing quantity 'd2x' not found. Calculating from dx\n"); d1_dx = bout::derivatives::index::DDX(1. / dx); // d/di(1/dx) - localmesh->communicate(d1_dx); - d1_dx = interpolateAndExtrapolate(d1_dx, location, true, true, true); + communicate(d1_dx); + d1_dx = + interpolateAndExtrapolate(d1_dx, location, true, true, true, transform.get()); } else { d2x.setLocation(location); // set boundary cells if necessary - d2x = interpolateAndExtrapolate(d2x, location, extrapolate_x, extrapolate_y); + d2x = interpolateAndExtrapolate(d2x, location, extrapolate_x, extrapolate_y, false, + transform.get()); d1_dx = -d2x / (dx * dx); } - if (localmesh->get(d2y, "d2y"+suffix, 0.0, location)) { + if (localmesh->get(d2y, "d2y" + suffix, 0.0, false, location)) { output_warn.write( "\tWARNING: differencing quantity 'd2y' not found. Calculating from dy\n"); - d1_dy = bout::derivatives::index::DDY(1. / dy); // d/di(1/dy) + d1_dy = DDY(1. / dy); // d/di(1/dy) - localmesh->communicate(d1_dy); - d1_dy = interpolateAndExtrapolate(d1_dy, location, true, true, true); + communicate(d1_dy); + d1_dy = + interpolateAndExtrapolate(d1_dy, location, true, true, true, transform.get()); } else { d2y.setLocation(location); // set boundary cells if necessary - d2y = interpolateAndExtrapolate(d2y, location, extrapolate_x, extrapolate_y); + d2y = interpolateAndExtrapolate(d2y, location, extrapolate_x, extrapolate_y, false, + transform.get()); d1_dy = -d2y / (dy * dy); } + +#if BOUT_USE_METRIC_3D + if (localmesh->get(d2z, "d2z" + suffix, 0.0, false)) { + output_warn.write( + "\tWARNING: differencing quantity 'd2z' not found. Calculating from dz\n"); + d1_dz = bout::derivatives::index::DDZ(1. / dz); + communicate(d1_dz); + d1_dz = + interpolateAndExtrapolate(d1_dz, location, true, true, true, transform.get()); + } else { + d2z.setLocation(location); + // set boundary cells if necessary + d2z = interpolateAndExtrapolate(d2z, location, extrapolate_x, extrapolate_y, false, + transform.get()); + + d1_dz = -d2z / (dz * dz); + } +#else + d1_dz = 0; +#endif } else { - if (localmesh->get(d2x, "d2x")) { + if (localmesh->get(d2x, "d2x", 0.0, false)) { output_warn.write( "\tWARNING: differencing quantity 'd2x' not found. Calculating from dx\n"); d1_dx = bout::derivatives::index::DDX(1. / dx); // d/di(1/dx) - localmesh->communicate(d1_dx); - d1_dx = interpolateAndExtrapolate(d1_dx, location, true, true, true); + communicate(d1_dx); + d1_dx = + interpolateAndExtrapolate(d1_dx, location, true, true, true, transform.get()); } else { // Shift d2x to our location - d2x = interpolateAndExtrapolate(d2x, location); + d2x = interpolateAndExtrapolate(d2x, location, true, true, false, transform.get()); d1_dx = -d2x / (dx * dx); } - if (localmesh->get(d2y, "d2y")) { + if (localmesh->get(d2y, "d2y", 0.0, false)) { output_warn.write( "\tWARNING: differencing quantity 'd2y' not found. Calculating from dy\n"); - d1_dy = bout::derivatives::index::DDY(1. / dy); // d/di(1/dy) + d1_dy = DDY(1. / dy); // d/di(1/dy) - localmesh->communicate(d1_dy); - d1_dy = interpolateAndExtrapolate(d1_dy, location, true, true, true); + communicate(d1_dy); + d1_dy = + interpolateAndExtrapolate(d1_dy, location, true, true, true, transform.get()); } else { // Shift d2y to our location - d2y = interpolateAndExtrapolate(d2y, location); + d2y = interpolateAndExtrapolate(d2y, location, true, true, false, transform.get()); d1_dy = -d2y / (dy * dy); } + +#if BOUT_USE_METRIC_3D + if (localmesh->get(d2z, "d2z", 0.0, false)) { + output_warn.write( + "\tWARNING: differencing quantity 'd2z' not found. Calculating from dz\n"); + d1_dz = bout::derivatives::index::DDZ(1. / dz); + + communicate(d1_dz); + d1_dz = + interpolateAndExtrapolate(d1_dz, location, true, true, true, transform.get()); + } else { + // Shift d2z to our location + d2z = interpolateAndExtrapolate(d2z, location, true, true, false, transform.get()); + + d1_dz = -d2z / (dz * dz); + } +#else + d1_dz = 0; +#endif } + communicate(d1_dx, d1_dy, d1_dz); if (location == CELL_CENTRE && recalculate_staggered) { // Re-calculate interpolated Coordinates at staggered locations @@ -1111,8 +1458,8 @@ int Coordinates::jacobian() { const bool extrapolate_x = not localmesh->sourceHasXBoundaryGuards(); const bool extrapolate_y = not localmesh->sourceHasYBoundaryGuards(); - Field2D g = g11 * g22 * g33 + 2.0 * g12 * g13 * g23 - g11 * g23 * g23 - g22 * g13 * g13 - - g33 * g12 * g12; + auto g = g11 * g22 * g33 + 2.0 * g12 * g13 * g23 - g11 * g23 * g23 - g22 * g13 * g13 + - g33 * g12 * g12; // Check that g is positive bout::checkPositive(g, "The determinant of g^ij", "RGN_NOBNDRY"); @@ -1120,10 +1467,12 @@ int Coordinates::jacobian() { J = 1. / sqrt(g); // More robust to extrapolate derived quantities directly, rather than // deriving from extrapolated covariant metric components - J = interpolateAndExtrapolate(J, location, extrapolate_x, extrapolate_y); + J = interpolateAndExtrapolate(J, location, extrapolate_x, extrapolate_y, false, + transform.get()); Bxy = sqrt(g_22) / J; - Bxy = interpolateAndExtrapolate(Bxy, location, extrapolate_x, extrapolate_y); + Bxy = interpolateAndExtrapolate(Bxy, location, extrapolate_x, extrapolate_y, false, + transform.get()); return 0; } @@ -1135,11 +1484,11 @@ void fixZShiftGuards(Field2D& zShift) { // extrapolate into boundary guard cells if necessary zShift = interpolateAndExtrapolate(zShift, zShift.getLocation(), - not localmesh->sourceHasXBoundaryGuards(), - not localmesh->sourceHasYBoundaryGuards()); + not localmesh->sourceHasXBoundaryGuards(), + not localmesh->sourceHasYBoundaryGuards(), false); // make sure zShift has been communicated - localmesh->communicate(zShift); + communicate(zShift); // Correct guard cells for discontinuity of zShift at poloidal branch cut for (int x = 0; x < localmesh->LocalNx; x++) { @@ -1184,33 +1533,39 @@ void Coordinates::setParallelTransform(Options* options) { if (localmesh->sourceHasVar("dx"+suffix)) { // Grid file has variables at this location, so should be able to read checkStaggeredGet(localmesh, "zShift", suffix); - if (localmesh->get(zShift, "zShift"+suffix, 0.0, location)) { + if (localmesh->get(zShift, "zShift" + suffix, 0.0, false, location)) { // No zShift variable. Try qinty in BOUT grid files - if (localmesh->get(zShift, "qinty"+suffix, 0.0, location)) { + if (localmesh->get(zShift, "qinty" + suffix, 0.0, false, location)) { // Failed to find either variable, cannot use ShiftedMetric throw BoutException("Could not read zShift"+suffix+" from grid file"); } } } else { + if (location == CELL_YLOW and bout::build::use_metric_3d) { + throw BoutException("Cannot interpolate zShift to construct ShiftedMetric when " + "using 3d metrics. You must provide zShift_ylow in the grid " + "file."); + } Field2D zShift_centre; - if (localmesh->get(zShift_centre, "zShift")) { + if (localmesh->get(zShift_centre, "zShift", 0.0, false)) { // No zShift variable. Try qinty in BOUT grid files - if (localmesh->get(zShift_centre, "qinty")) { + if (localmesh->get(zShift_centre, "qinty", 0.0, false)) { // Failed to find either variable, cannot use ShiftedMetric - throw BoutException("Could not read zShift"+suffix+" from grid file"); + throw BoutException("Could not read zShift from grid file"); } } fixZShiftGuards(zShift_centre); - zShift = interpolateAndExtrapolate(zShift_centre, location); + zShift = interpolateAndExtrapolate(zShift_centre, location, true, true, false, + transform.get()); } fixZShiftGuards(zShift); if (ptstr == "shifted") { transform = bout::utils::make_unique(*localmesh, location, zShift, - zlength()); + getUniform(zlength())); } else if (ptstr == "shiftedinterp") { transform = bout::utils::make_unique(*localmesh, location, zShift); @@ -1238,20 +1593,49 @@ void Coordinates::setParallelTransform(Options* options) { * *******************************************************************************/ -Field2D Coordinates::DDX(const Field2D& f, CELL_LOC loc, const std::string& method, - const std::string& region) { +Coordinates::FieldMetric Coordinates::DDX(const Field2D& f, CELL_LOC loc, + const std::string& method, + const std::string& region) { ASSERT1(location == loc || loc == CELL_DEFAULT); return bout::derivatives::index::DDX(f, loc, method, region) / dx; } +Field3D Coordinates::DDX(const Field3D& f, CELL_LOC outloc, const std::string& method, + const std::string& region) { + + auto result = bout::derivatives::index::DDX(f, outloc, method, region); + result /= dx; + + if (f.getMesh()->IncIntShear) { + // Using BOUT-06 style shifting + result += IntShiftTorsion * DDZ(f, outloc, method, region); + } + + return result; +}; -Field2D Coordinates::DDY(const Field2D& f, CELL_LOC loc, const std::string& method, - const std::string& region) { +Coordinates::FieldMetric Coordinates::DDY(const Field2D& f, CELL_LOC loc, + const std::string& method, + const std::string& region) { ASSERT1(location == loc || loc == CELL_DEFAULT); return bout::derivatives::index::DDY(f, loc, method, region) / dy; } -Field2D Coordinates::DDZ(MAYBE_UNUSED(const Field2D& f), CELL_LOC loc, - const std::string& UNUSED(method), const std::string& UNUSED(region)) { +Field3D Coordinates::DDY(const Field3D& f, CELL_LOC outloc, const std::string& method, + const std::string& region) { +#if BOUT_USE_METRIC_3D + if (! f.hasParallelSlices() and ! transform->canToFromFieldAligned()) { + Field3D f_parallel = f; + transform->calcParallelSlices(f_parallel); + f_parallel.applyParallelBoundary(); + return bout::derivatives::index::DDY(f_parallel, outloc, method, region); + } +#endif + return bout::derivatives::index::DDY(f, outloc, method, region) / dy; +}; + +Coordinates::FieldMetric Coordinates::DDZ(const Field2D& f, CELL_LOC loc, + const std::string& UNUSED(method), + const std::string& UNUSED(region)) { ASSERT1(location == loc || loc == CELL_DEFAULT); ASSERT1(f.getMesh() == localmesh); if (loc == CELL_DEFAULT) { @@ -1259,14 +1643,17 @@ Field2D Coordinates::DDZ(MAYBE_UNUSED(const Field2D& f), CELL_LOC loc, } return zeroFrom(f).setLocation(loc); } - -#include +Field3D Coordinates::DDZ(const Field3D& f, CELL_LOC outloc, const std::string& method, + const std::string& region) { + return bout::derivatives::index::DDZ(f, outloc, method, region) / dz; +}; ///////////////////////////////////////////////////////// // Parallel gradient -Field2D Coordinates::Grad_par(const Field2D& var, MAYBE_UNUSED(CELL_LOC outloc), - const std::string& UNUSED(method)) { +Coordinates::FieldMetric Coordinates::Grad_par(const Field2D& var, + MAYBE_UNUSED(CELL_LOC outloc), + const std::string& UNUSED(method)) { TRACE("Coordinates::Grad_par( Field2D )"); ASSERT1(location == outloc || (outloc == CELL_DEFAULT && location == var.getLocation())); @@ -1286,8 +1673,9 @@ Field3D Coordinates::Grad_par(const Field3D& var, CELL_LOC outloc, // Vpar_Grad_par // vparallel times the parallel derivative along unperturbed B-field -Field2D Coordinates::Vpar_Grad_par(const Field2D& v, const Field2D& f, - MAYBE_UNUSED(CELL_LOC outloc), const std::string& UNUSED(method)) { +Coordinates::FieldMetric Coordinates::Vpar_Grad_par(const Field2D& v, const Field2D& f, + MAYBE_UNUSED(CELL_LOC outloc), + const std::string& UNUSED(method)) { ASSERT1(location == outloc || (outloc == CELL_DEFAULT && location == f.getLocation())); return VDDY(v, f) / sqrt(g_22); } @@ -1301,14 +1689,14 @@ Field3D Coordinates::Vpar_Grad_par(const Field3D& v, const Field3D& f, CELL_LOC ///////////////////////////////////////////////////////// // Parallel divergence -Field2D Coordinates::Div_par(const Field2D& f, CELL_LOC outloc, - const std::string& method) { +Coordinates::FieldMetric Coordinates::Div_par(const Field2D& f, CELL_LOC outloc, + const std::string& method) { TRACE("Coordinates::Div_par( Field2D )"); ASSERT1(location == outloc || outloc == CELL_DEFAULT); // Need Bxy at location of f, which might be different from location of this // Coordinates object - Field2D Bxy_floc = f.getCoordinates()->Bxy; + auto Bxy_floc = f.getCoordinates()->Bxy; return Bxy * Grad_par(f / Bxy_floc, outloc, method); } @@ -1320,7 +1708,7 @@ Field3D Coordinates::Div_par(const Field3D& f, CELL_LOC outloc, // Need Bxy at location of f, which might be different from location of this // Coordinates object - Field2D Bxy_floc = f.getCoordinates()->Bxy; + auto Bxy_floc = f.getCoordinates()->Bxy; if (!f.hasParallelSlices()) { // No yup/ydown fields. The Grad_par operator will @@ -1342,14 +1730,16 @@ Field3D Coordinates::Div_par(const Field3D& f, CELL_LOC outloc, // second parallel derivative (b dot Grad)(b dot Grad) // Note: For parallel Laplacian use Laplace_par -Field2D Coordinates::Grad2_par2(const Field2D& f, CELL_LOC outloc, - const std::string& method) { +Coordinates::FieldMetric Coordinates::Grad2_par2(const Field2D& f, CELL_LOC outloc, + const std::string& method) { TRACE("Coordinates::Grad2_par2( Field2D )"); ASSERT1(location == outloc || (outloc == CELL_DEFAULT && location == f.getLocation())); - Field2D sg = sqrt(g_22); - Field2D result = DDY(1. / sg, outloc, method) * DDY(f, outloc, method) / sg - + D2DY2(f, outloc, method) / g_22; + auto invSg = 1.0 / sqrt(g_22); + // Communicate to get parallel slices + localmesh->communicate(invSg); + auto result = DDY(invSg, outloc, method) * DDY(f, outloc, method) * invSg + + D2DY2(f, outloc, method) / g_22; return result; } @@ -1362,8 +1752,10 @@ Field3D Coordinates::Grad2_par2(const Field3D& f, CELL_LOC outloc, } ASSERT1(location == outloc); - Field2D sg = sqrt(g_22); - sg = DDY(1. / sg, outloc, method) / sg; + auto invSg = 1.0 / sqrt(g_22); + // Communicate to get parallel slices + localmesh->communicate(invSg); + auto sg = DDY(invSg, outloc, method) * invSg; Field3D result = ::DDY(f, outloc, method); @@ -1381,11 +1773,12 @@ Field3D Coordinates::Grad2_par2(const Field3D& f, CELL_LOC outloc, #include // Delp2 uses same coefficients as inversion code -Field2D Coordinates::Delp2(const Field2D& f, CELL_LOC outloc, bool UNUSED(useFFT)) { +Coordinates::FieldMetric Coordinates::Delp2(const Field2D& f, CELL_LOC outloc, + bool UNUSED(useFFT)) { TRACE("Coordinates::Delp2( Field2D )"); ASSERT1(location == outloc || outloc == CELL_DEFAULT); - Field2D result = G1 * DDX(f, outloc) + g11 * D2DX2(f, outloc); + auto result = G1 * DDX(f, outloc) + g11 * D2DX2(f, outloc); return result; } @@ -1408,7 +1801,7 @@ Field3D Coordinates::Delp2(const Field3D& f, CELL_LOC outloc, bool useFFT) { Field3D result{emptyFrom(f).setLocation(outloc)}; - if (useFFT) { + if (useFFT and not bout::build::use_metric_3d) { int ncz = localmesh->LocalNz; // Allocate memory @@ -1517,7 +1910,7 @@ FieldPerp Coordinates::Delp2(const FieldPerp& f, CELL_LOC outloc, bool useFFT) { return result; } -Field2D Coordinates::Laplace_par(const Field2D& f, CELL_LOC outloc) { +Coordinates::FieldMetric Coordinates::Laplace_par(const Field2D& f, CELL_LOC outloc) { ASSERT1(location == outloc || outloc == CELL_DEFAULT); return D2DY2(f, outloc) / g_22 + DDY(J / g_22, outloc) * DDY(f, outloc) / J; } @@ -1529,18 +1922,17 @@ Field3D Coordinates::Laplace_par(const Field3D& f, CELL_LOC outloc) { // Full Laplacian operator on scalar field -Field2D Coordinates::Laplace(const Field2D& f, CELL_LOC outloc, - const std::string& dfdy_boundary_conditions, - const std::string& dfdy_dy_region) { +Coordinates::FieldMetric Coordinates::Laplace(const Field2D& f, CELL_LOC outloc, + const std::string& dfdy_boundary_conditions, + const std::string& dfdy_dy_region) { TRACE("Coordinates::Laplace( Field2D )"); ASSERT1(location == outloc || outloc == CELL_DEFAULT); - Field2D result = - G1 * DDX(f, outloc) + G2 * DDY(f, outloc) + g11 * D2DX2(f, outloc) - + g22 * D2DY2(f, outloc) - + 2.0 * g12 - * D2DXDY(f, outloc, "DEFAULT", "RGN_NOBNDRY", - dfdy_boundary_conditions, dfdy_dy_region); + auto result = G1 * DDX(f, outloc) + G2 * DDY(f, outloc) + g11 * D2DX2(f, outloc) + + g22 * D2DY2(f, outloc) + + 2.0 * g12 + * D2DXDY(f, outloc, "DEFAULT", "RGN_NOBNDRY", + dfdy_boundary_conditions, dfdy_dy_region); return result; } @@ -1565,9 +1957,10 @@ Field3D Coordinates::Laplace(const Field3D& f, CELL_LOC outloc, // Full perpendicular Laplacian, in form of inverse of Laplacian operator in LaplaceXY // solver -Field2D Coordinates::Laplace_perpXY(const Field2D& A, const Field2D& f) { +Field2D Coordinates::Laplace_perpXY(MAYBE_UNUSED(const Field2D& A), + MAYBE_UNUSED(const Field2D& f)) { TRACE("Coordinates::Laplace_perpXY( Field2D )"); - +#if not(BOUT_USE_METRIC_3D) Field2D result; result.allocate(); for (auto i : result.getRegion(RGN_NOBNDRY)) { @@ -1619,4 +2012,7 @@ Field2D Coordinates::Laplace_perpXY(const Field2D& A, const Field2D& f) { } return result; +#else + throw BoutException("Coordinates::Laplace_perpXY for 3D metric not implemented"); +#endif } diff --git a/src/mesh/data/gridfromfile.cxx b/src/mesh/data/gridfromfile.cxx index 6ed66bd503..b9c5435a8d 100644 --- a/src/mesh/data/gridfromfile.cxx +++ b/src/mesh/data/gridfromfile.cxx @@ -581,8 +581,8 @@ bool GridFile::readgrid_3dvar_fft(Mesh *m, const std::string &name, /// but don't do it yet as we don't assert that m == var.getMesh() /// Expect the assertion to be true, in which case we probably don't /// need to pass m as can just use var.getMesh() - BoutReal zlength = m->getCoordinates(var.getLocation())->zlength(); - + BoutReal zlength = getUniform(m->getCoordinates(var.getLocation())->zlength()); + int zperiod = ROUND(TWOPI / zlength); /// Number of periods in 2pi // Print out which modes are going to be read in @@ -712,7 +712,7 @@ bool GridFile::readgrid_perpvar_fft(Mesh *m, const std::string &name, /// but don't do it yet as we don't assert that m == var.getMesh() /// Expect the assertion to be true, in which case we probably don't /// need to pass m as can just use var.getMesh() - BoutReal zlength = m->getCoordinates(var.getLocation())->zlength(); + BoutReal zlength = getUniform(m->getCoordinates(var.getLocation())->zlength()); int zperiod = ROUND(TWOPI / zlength); /// Number of periods in 2pi diff --git a/src/mesh/difops.cxx b/src/mesh/difops.cxx index bc59932cfd..2fa2e36a81 100644 --- a/src/mesh/difops.cxx +++ b/src/mesh/difops.cxx @@ -1,27 +1,27 @@ /************************************************************************** -* Various differential operators defined on BOUT grid -* -************************************************************************** -* Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu -* -* 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 . -* -**************************************************************************/ + * Various differential operators defined on BOUT grid + * + ************************************************************************** + * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * + * 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 "bout/build_config.hxx" @@ -47,19 +47,21 @@ * The parallel derivative along unperturbed B-field *******************************************************************************/ -const Field2D Grad_par(const Field2D &var, CELL_LOC outloc, const std::string &method) { +Coordinates::FieldMetric Grad_par(const Field2D& var, CELL_LOC outloc, + const std::string& method) { return var.getCoordinates(outloc)->Grad_par(var, outloc, method); } -const Field2D Grad_par(const Field2D &var, const std::string &method, CELL_LOC outloc) { +Coordinates::FieldMetric Grad_par(const Field2D& var, const std::string& method, + CELL_LOC outloc) { return var.getCoordinates(outloc)->Grad_par(var, outloc, method); } -const Field3D Grad_par(const Field3D &var, CELL_LOC outloc, const std::string &method) { +Field3D Grad_par(const Field3D& var, CELL_LOC outloc, const std::string& method) { return var.getCoordinates(outloc)->Grad_par(var, outloc, method); } -const Field3D Grad_par(const Field3D &var, const std::string &method, CELL_LOC outloc) { +Field3D Grad_par(const Field3D& var, const std::string& method, CELL_LOC outloc) { return var.getCoordinates(outloc)->Grad_par(var, outloc, method); } @@ -74,14 +76,14 @@ const Field3D Grad_par(const Field3D &var, const std::string &method, CELL_LOC o * grid-points at the corners. *******************************************************************************/ -const Field3D Grad_parP(const Field3D &apar, const Field3D &f) { +Field3D Grad_parP(const Field3D& apar, const Field3D& f) { ASSERT1_FIELDS_COMPATIBLE(apar, f); ASSERT1(f.hasParallelSlices()); Mesh *mesh = apar.getMesh(); Field3D result{emptyFrom(f)}; - + int ncz = mesh->LocalNz; Coordinates *metric = apar.getCoordinates(); @@ -92,70 +94,92 @@ const Field3D Grad_parP(const Field3D &apar, const Field3D &f) { for(int x=1;x<=mesh->LocalNx-2;x++) for(int y=1;y<=mesh->LocalNy-2;y++) for(int z=0;zdy(x, y+1) + metric->dy(x, y) + 0.5*metric->dy(x, y-1)); + gys(x, y, z) = (f.yup()(x, y + 1, z) - f.ydown()(x, y - 1, z)) + / (0.5 * metric->dy(x, y + 1, z) + metric->dy(x, y, z) + + 0.5 * metric->dy(x, y - 1, z)); } - + for(int x=1;x<=mesh->LocalNx-2;x++) { for(int y=mesh->ystart;y<=mesh->yend;y++) { - BoutReal by = 1./sqrt(metric->g_22(x, y)); for(int z=0;zg_22(x, y, z)); // Z indices zm and zp int zm = (z - 1 + ncz) % ncz; int zp = (z + 1) % ncz; - + // bx = -DDZ(apar) - BoutReal bx = (apar(x, y, zm) - apar(x, y, zp))/(2.*metric->dz); + BoutReal bx = (apar(x, y, zm) - apar(x, y, zp)) + / (0.5 * metric->dz(x, y, zm) + metric->dz(x, y, z) + + 0.5 * metric->dz(x, y, zp)); // bz = DDX(f) - BoutReal bz = (apar(x+1, y, z) - apar(x-1, y, z))/(0.5*metric->dx(x-1, y) + metric->dx(x, y) + 0.5*metric->dx(x+1, y)); - - // Now calculate (bx*d/dx + by*d/dy + bz*d/dz) f - + BoutReal bz = (apar(x + 1, y, z) - apar(x - 1, y, z)) + / (0.5 * metric->dx(x - 1, y, z) + metric->dx(x, y, z) + + 0.5 * metric->dx(x + 1, y, z)); + + // Now calculate (bx*d/dx + by*d/dy + bz*d/dz) f + // Length dl for predictor - BoutReal dl = fabs(metric->dx(x, y)) / (fabs(bx) + 1e-16); - dl = BOUTMIN(dl, fabs(metric->dy(x, y)) / (fabs(by) + 1e-16)); - dl = BOUTMIN(dl, metric->dz / (fabs(bz) + 1e-16)); - - BoutReal fp, fm; - + BoutReal dl = fabs(metric->dx(x, y, z)) / (fabs(bx) + 1e-16); + dl = BOUTMIN(dl, fabs(metric->dy(x, y, z)) / (fabs(by) + 1e-16)); + dl = BOUTMIN(dl, fabs(metric->dz(x, y, z)) / (fabs(bz) + 1e-16)); + + BoutReal fp, fm; + // X differencing - fp = f(x+1, y, z) - + (0.25*dl/metric->dz) * bz * (f(x+1, y, zm) - f(x+1, y, zp)) - - 0.5*dl * by * gys(x+1, y, z); - - fm = f(x-1, y, z) - + (0.25*dl/metric->dz) * bz * (f(x-1, y, zm) - f(x-1, y, zp)) - - 0.5*dl * by * gys(x-1, y, z); - - result(x, y, z) = bx * (fp - fm) / (0.5*metric->dx(x-1, y) + metric->dx(x, y) + 0.5*metric->dx(x+1, y)); - - // Z differencing - - fp = f(x, y, zp) - + (0.25*dl/metric->dx(x, y)) * bx * (f(x-1, y, zp) - f(x+1, y, zp)) - - 0.5*dl * by * gys(x, y, zp); - - fm = f(x, y, zm) - + (0.25*dl/metric->dx(x, y)) * bx * (f(x-1,y,zm) - f(x+1, y, zm)) - - 0.5*dl * by * gys(x, y, zm); - - result(x, y, z) += bz * (fp - fm) / (2.*metric->dz); + fp = + f(x + 1, y, z) + + (0.25 * dl / metric->dz(x, y, z)) * bz * (f(x + 1, y, zm) - f(x + 1, y, zp)) + - 0.5 * dl * by * gys(x + 1, y, z); + + fm = + f(x - 1, y, z) + + (0.25 * dl / metric->dz(x, y, z)) * bz * (f(x - 1, y, zm) - f(x - 1, y, zp)) + - 0.5 * dl * by * gys(x - 1, y, z); + + result(x, y, z) = bx * (fp - fm) + / (0.5 * metric->dx(x - 1, y, z) + metric->dx(x, y, z) + + 0.5 * metric->dx(x + 1, y, z)); + + // Z differencing + + fp = + f(x, y, zp) + + (0.25 * dl / metric->dx(x, y, z)) * bx * (f(x - 1, y, zp) - f(x + 1, y, zp)) + - 0.5 * dl * by * gys(x, y, zp); + + fm = + f(x, y, zm) + + (0.25 * dl / metric->dx(x, y, z)) * bx * (f(x - 1, y, zm) - f(x + 1, y, zm)) + - 0.5 * dl * by * gys(x, y, zm); + + result(x, y, z) += bz * (fp - fm) + / (0.5 * metric->dz(x, y, zm) + metric->dz(x, y, z) + + 0.5 * metric->dz(x, y, zp)); // Y differencing - - fp = f.yup()(x,y+1,z) - - 0.5*dl * bx * (f.yup()(x+1, y+1, z) - f.yup()(x-1, y+1, z))/(0.5*metric->dx(x-1, y) + metric->dx(x, y) + 0.5*metric->dx(x+1, y)) - - + (0.25*dl/metric->dz) * bz * (f.yup()(x,y+1,zm) - f.yup()(x,y+1,zp)); - - fm = f.ydown()(x,y-1,z) - - 0.5*dl * bx * (f.ydown()(x+1, y-1, z) - f.ydown()(x-1, y-1, z))/(0.5*metric->dx(x-1, y) + metric->dx(x, y) + 0.5*metric->dx(x+1, y)) - + (0.25*dl/metric->dz) * bz * (f.ydown()(x,y-1,zm) - f.ydown()(x,y-1,zp)); - - result(x,y,z) += by * (fp - fm) / (0.5*metric->dy(x,y-1) + metric->dy(x,y) + 0.5*metric->dy(x,y+1)); + + fp = f.yup()(x, y + 1, z) + - 0.5 * dl * bx * (f.yup()(x + 1, y + 1, z) - f.yup()(x - 1, y + 1, z)) + / (0.5 * metric->dx(x - 1, y, z) + metric->dx(x, y, z) + + 0.5 * metric->dx(x + 1, y, z)) + + + (0.25 * dl / metric->dz(x, y, z)) * bz + * (f.yup()(x, y + 1, zm) - f.yup()(x, y + 1, zp)); + + fm = f.ydown()(x, y - 1, z) + - 0.5 * dl * bx * (f.ydown()(x + 1, y - 1, z) - f.ydown()(x - 1, y - 1, z)) + / (0.5 * metric->dx(x - 1, y, z) + metric->dx(x, y, z) + + 0.5 * metric->dx(x + 1, y, z)) + + (0.25 * dl / metric->dz(x, y, z)) * bz + * (f.ydown()(x, y - 1, zm) - f.ydown()(x, y - 1, zp)); + + result(x, y, z) += by * (fp - fm) + / (0.5 * metric->dy(x, y - 1, z) + metric->dy(x, y, z) + + 0.5 * metric->dy(x, y + 1, z)); } } } - + ASSERT2(result.getLocation() == f.getLocation()); return result; @@ -166,19 +190,23 @@ const Field3D Grad_parP(const Field3D &apar, const Field3D &f) { * vparallel times the parallel derivative along unperturbed B-field *******************************************************************************/ -const Field2D Vpar_Grad_par(const Field2D &v, const Field2D &f, CELL_LOC outloc, const std::string &method) { +Coordinates::FieldMetric Vpar_Grad_par(const Field2D& v, const Field2D& f, + CELL_LOC outloc, const std::string& method) { return f.getCoordinates(outloc)->Vpar_Grad_par(v, f, outloc, method); } -const Field2D Vpar_Grad_par(const Field2D &v, const Field2D &f, const std::string &method, CELL_LOC outloc) { +Coordinates::FieldMetric Vpar_Grad_par(const Field2D& v, const Field2D& f, + const std::string& method, CELL_LOC outloc) { return f.getCoordinates(outloc)->Vpar_Grad_par(v, f, outloc, method); } -const Field3D Vpar_Grad_par(const Field3D &v, const Field3D &f, CELL_LOC outloc, const std::string &method) { +Field3D Vpar_Grad_par(const Field3D& v, const Field3D& f, CELL_LOC outloc, + const std::string& method) { return f.getCoordinates(outloc)->Vpar_Grad_par(v, f, outloc, method); } -const Field3D Vpar_Grad_par(const Field3D &v, const Field3D &f, const std::string &method, CELL_LOC outloc) { +Field3D Vpar_Grad_par(const Field3D& v, const Field3D& f, const std::string& method, + CELL_LOC outloc) { return f.getCoordinates(outloc)->Vpar_Grad_par(v, f, outloc, method); } @@ -186,23 +214,25 @@ const Field3D Vpar_Grad_par(const Field3D &v, const Field3D &f, const std::strin * Div_par * parallel divergence operator B \partial_{||} (F/B) *******************************************************************************/ -const Field2D Div_par(const Field2D &f, CELL_LOC outloc, const std::string &method) { +Coordinates::FieldMetric Div_par(const Field2D& f, CELL_LOC outloc, + const std::string& method) { return f.getCoordinates(outloc)->Div_par(f, outloc, method); } -const Field2D Div_par(const Field2D &f, const std::string &method, CELL_LOC outloc) { +Coordinates::FieldMetric Div_par(const Field2D& f, const std::string& method, + CELL_LOC outloc) { return f.getCoordinates(outloc)->Div_par(f, outloc, method); } -const Field3D Div_par(const Field3D &f, CELL_LOC outloc, const std::string &method) { +Field3D Div_par(const Field3D& f, CELL_LOC outloc, const std::string& method) { return f.getCoordinates(outloc)->Div_par(f, outloc, method); } -const Field3D Div_par(const Field3D &f, const std::string &method, CELL_LOC outloc) { +Field3D Div_par(const Field3D& f, const std::string& method, CELL_LOC outloc) { return f.getCoordinates(outloc)->Div_par(f, outloc, method); } -const Field3D Div_par(const Field3D& f, const Field3D& v) { +Field3D Div_par(const Field3D& f, const Field3D& v) { ASSERT1_FIELDS_COMPATIBLE(f, v); ASSERT1(f.hasParallelSlices()); ASSERT1(v.hasParallelSlices()); @@ -226,14 +256,17 @@ const Field3D Div_par(const Field3D& f, const Field3D& v) { BoutReal vR = 0.5 * (v(i, j, k) + v.yup()(i, j + 1, k)); // Calculate flux at right boundary (y+1/2) - BoutReal fluxRight = fR * vR * (coord->J(i, j) + coord->J(i, j + 1)) - / (sqrt(coord->g_22(i, j)) + sqrt(coord->g_22(i, j + 1))); + BoutReal fluxRight = + fR * vR * (coord->J(i, j, k) + coord->J(i, j + 1, k)) + / (sqrt(coord->g_22(i, j, k)) + sqrt(coord->g_22(i, j + 1, k))); // Calculate at left boundary (y-1/2) - BoutReal fluxLeft = fL * vL * (coord->J(i, j) + coord->J(i, j - 1)) - / (sqrt(coord->g_22(i, j)) + sqrt(coord->g_22(i, j - 1))); + BoutReal fluxLeft = + fL * vL * (coord->J(i, j, k) + coord->J(i, j - 1, k)) + / (sqrt(coord->g_22(i, j, k)) + sqrt(coord->g_22(i, j - 1, k))); - result(i, j, k) = (fluxRight - fluxLeft) / (coord->dy(i, j) * coord->J(i, j)); + result(i, j, k) = + (fluxRight - fluxLeft) / (coord->dy(i, j, k) * coord->J(i, j, k)); } } @@ -242,10 +275,11 @@ const Field3D Div_par(const Field3D& f, const Field3D& v) { //////// Flux methods -const Field3D Div_par_flux(const Field3D &v, const Field3D &f, CELL_LOC outloc, const std::string &method) { +Field3D Div_par_flux(const Field3D& v, const Field3D& f, CELL_LOC outloc, + const std::string& method) { Coordinates *metric = f.getCoordinates(outloc); - Field2D Bxy_floc = f.getCoordinates()->Bxy; + auto Bxy_floc = f.getCoordinates()->Bxy; if (!f.hasParallelSlices()) { return metric->Bxy*FDDY(v, f/Bxy_floc, outloc, method)/sqrt(metric->g_22); @@ -260,7 +294,8 @@ const Field3D Div_par_flux(const Field3D &v, const Field3D &f, CELL_LOC outloc, return metric->Bxy*FDDY(v, f_B, outloc, method)/sqrt(metric->g_22); } -const Field3D Div_par_flux(const Field3D &v, const Field3D &f, const std::string &method, CELL_LOC outloc) { +Field3D Div_par_flux(const Field3D& v, const Field3D& f, const std::string& method, + CELL_LOC outloc) { return Div_par_flux(v,f, outloc, method); } @@ -273,11 +308,12 @@ const Field3D Div_par_flux(const Field3D &v, const Field3D &f, const std::string * Note: For parallel Laplacian use LaplacePar *******************************************************************************/ -const Field2D Grad2_par2(const Field2D &f, CELL_LOC outloc, const std::string &method) { +Coordinates::FieldMetric Grad2_par2(const Field2D& f, CELL_LOC outloc, + const std::string& method) { return f.getCoordinates(outloc)->Grad2_par2(f, outloc, method); } -const Field3D Grad2_par2(const Field3D &f, CELL_LOC outloc, const std::string &method) { +Field3D Grad2_par2(const Field3D& f, CELL_LOC outloc, const std::string& method) { return f.getCoordinates(outloc)->Grad2_par2(f, outloc, method); } @@ -286,30 +322,32 @@ const Field3D Grad2_par2(const Field3D &f, CELL_LOC outloc, const std::string &m * Parallel divergence of diffusive flux, K*Grad_par *******************************************************************************/ -const Field2D Div_par_K_Grad_par(BoutReal kY, const Field2D &f, CELL_LOC outloc) { +Coordinates::FieldMetric Div_par_K_Grad_par(BoutReal kY, const Field2D& f, + CELL_LOC outloc) { return kY*Grad2_par2(f, outloc); } -const Field3D Div_par_K_Grad_par(BoutReal kY, const Field3D &f, CELL_LOC outloc) { +Field3D Div_par_K_Grad_par(BoutReal kY, const Field3D& f, CELL_LOC outloc) { return kY*Grad2_par2(f, outloc); } -const Field2D Div_par_K_Grad_par(const Field2D &kY, const Field2D &f, CELL_LOC outloc) { +Coordinates::FieldMetric Div_par_K_Grad_par(const Field2D& kY, const Field2D& f, + CELL_LOC outloc) { if (outloc == CELL_DEFAULT) outloc = f.getLocation(); return interp_to(kY, outloc)*Grad2_par2(f, outloc) + Div_par(kY, outloc)*Grad_par(f, outloc); } -const Field3D Div_par_K_Grad_par(const Field2D &kY, const Field3D &f, CELL_LOC outloc) { +Field3D Div_par_K_Grad_par(const Field2D& kY, const Field3D& f, CELL_LOC outloc) { if (outloc == CELL_DEFAULT) outloc = f.getLocation(); return interp_to(kY, outloc)*Grad2_par2(f, outloc) + Div_par(kY, outloc)*Grad_par(f, outloc); } -const Field3D Div_par_K_Grad_par(const Field3D &kY, const Field2D &f, CELL_LOC outloc) { +Field3D Div_par_K_Grad_par(const Field3D& kY, const Field2D& f, CELL_LOC outloc) { if (outloc == CELL_DEFAULT) outloc = f.getLocation(); return interp_to(kY, outloc)*Grad2_par2(f, outloc) + Div_par(kY, outloc)*Grad_par(f, outloc); } -const Field3D Div_par_K_Grad_par(const Field3D &kY, const Field3D &f, CELL_LOC outloc) { +Field3D Div_par_K_Grad_par(const Field3D& kY, const Field3D& f, CELL_LOC outloc) { if (outloc == CELL_DEFAULT) outloc = f.getLocation(); return interp_to(kY, outloc)*Grad2_par2(f, outloc) + Div_par(kY, outloc)*Grad_par(f, outloc); } @@ -319,15 +357,15 @@ const Field3D Div_par_K_Grad_par(const Field3D &kY, const Field3D &f, CELL_LOC o * perpendicular Laplacian operator *******************************************************************************/ -const Field2D Delp2(const Field2D& f, CELL_LOC outloc, bool useFFT) { +Coordinates::FieldMetric Delp2(const Field2D& f, CELL_LOC outloc, bool useFFT) { return f.getCoordinates(outloc)->Delp2(f, outloc, useFFT); } -const Field3D Delp2(const Field3D& f, CELL_LOC outloc, bool useFFT) { +Field3D Delp2(const Field3D& f, CELL_LOC outloc, bool useFFT) { return f.getCoordinates(outloc)->Delp2(f, outloc, useFFT); } -const FieldPerp Delp2(const FieldPerp& f, CELL_LOC outloc, bool useFFT) { +FieldPerp Delp2(const FieldPerp& f, CELL_LOC outloc, bool useFFT) { return f.getCoordinates(outloc)->Delp2(f, outloc, useFFT); } @@ -338,33 +376,33 @@ const FieldPerp Delp2(const FieldPerp& f, CELL_LOC outloc, bool useFFT) { * Laplace_perp = Laplace - Laplace_par *******************************************************************************/ -const Field2D Laplace_perp(const Field2D& f, CELL_LOC outloc, - const std::string& dfdy_boundary_condition, - const std::string& dfdy_region) { +Coordinates::FieldMetric Laplace_perp(const Field2D& f, CELL_LOC outloc, + const std::string& dfdy_boundary_condition, + const std::string& dfdy_region) { return Laplace(f, outloc, dfdy_boundary_condition, dfdy_region) - Laplace_par(f, outloc); } -const Field3D Laplace_perp(const Field3D& f, CELL_LOC outloc, - const std::string& dfdy_boundary_condition, - const std::string& dfdy_region) { +Field3D Laplace_perp(const Field3D& f, CELL_LOC outloc, + const std::string& dfdy_boundary_condition, + const std::string& dfdy_region) { return Laplace(f, outloc, dfdy_boundary_condition, dfdy_region) - Laplace_par(f, outloc); } /******************************************************************************* -* LaplacePar -* Full parallel Laplacian operator on scalar field -* -* LaplacePar(f) = Div( b (b dot Grad(f)) ) -* -*******************************************************************************/ + * LaplacePar + * Full parallel Laplacian operator on scalar field + * + * LaplacePar(f) = Div( b (b dot Grad(f)) ) + * + *******************************************************************************/ -const Field2D Laplace_par(const Field2D &f, CELL_LOC outloc) { +Coordinates::FieldMetric Laplace_par(const Field2D& f, CELL_LOC outloc) { return f.getCoordinates(outloc)->Laplace_par(f, outloc); } -const Field3D Laplace_par(const Field3D &f, CELL_LOC outloc) { +Field3D Laplace_par(const Field3D& f, CELL_LOC outloc) { return f.getCoordinates(outloc)->Laplace_par(f, outloc); } @@ -373,16 +411,16 @@ const Field3D Laplace_par(const Field3D &f, CELL_LOC outloc) { * Full Laplacian operator on scalar field *******************************************************************************/ -const Field2D Laplace(const Field2D& f, CELL_LOC outloc, - const std::string& dfdy_boundary_condition, - const std::string& dfdy_region) { +Coordinates::FieldMetric Laplace(const Field2D& f, CELL_LOC outloc, + const std::string& dfdy_boundary_condition, + const std::string& dfdy_region) { return f.getCoordinates(outloc)->Laplace(f, outloc, dfdy_boundary_condition, dfdy_region); } -const Field3D Laplace(const Field3D& f, CELL_LOC outloc, - const std::string& dfdy_boundary_condition, - const std::string& dfdy_region) { +Field3D Laplace(const Field3D& f, CELL_LOC outloc, + const std::string& dfdy_boundary_condition, + const std::string& dfdy_region) { return f.getCoordinates(outloc)->Laplace(f, outloc, dfdy_boundary_condition, dfdy_region); } @@ -392,7 +430,7 @@ const Field3D Laplace(const Field3D& f, CELL_LOC outloc, * Inverse of Laplacian operator in LaplaceXY solver *******************************************************************************/ -const Field2D Laplace_perpXY(const Field2D& A, const Field2D& f) { +Field2D Laplace_perpXY(const Field2D& A, const Field2D& f) { return f.getCoordinates()->Laplace_perpXY(A, f); } @@ -402,10 +440,11 @@ const Field2D Laplace_perpXY(const Field2D& A, const Field2D& f) { * Used for ExB terms and perturbed B field using A_|| *******************************************************************************/ -const Field2D b0xGrad_dot_Grad(const Field2D &phi, const Field2D &A, CELL_LOC outloc) { - +Coordinates::FieldMetric b0xGrad_dot_Grad(const Field2D& phi, const Field2D& A, + CELL_LOC outloc) { + TRACE("b0xGrad_dot_Grad( Field2D , Field2D )"); - + if (outloc == CELL_DEFAULT) outloc = A.getLocation(); ASSERT1(phi.getMesh() == A.getMesh()); @@ -413,15 +452,15 @@ const Field2D b0xGrad_dot_Grad(const Field2D &phi, const Field2D &A, CELL_LOC ou Coordinates *metric = phi.getCoordinates(outloc); // Calculate phi derivatives - Field2D dpdx = DDX(phi, outloc); - Field2D dpdy = DDY(phi, outloc); - + Coordinates::FieldMetric dpdx = DDX(phi, outloc); + Coordinates::FieldMetric dpdy = DDY(phi, outloc); + // Calculate advection velocity - Field2D vx = -metric->g_23*dpdy; - Field2D vy = metric->g_23*dpdx; + Coordinates::FieldMetric vx = -metric->g_23 * dpdy; + Coordinates::FieldMetric vy = metric->g_23 * dpdx; // Upwind A using these velocities - Field2D result = VDDX(vx, A, outloc) + VDDY(vy, A, outloc); + Coordinates::FieldMetric result = VDDX(vx, A, outloc) + VDDY(vy, A, outloc); result /= metric->J*sqrt(metric->g_22); ASSERT1(result.getLocation() == outloc); @@ -432,7 +471,7 @@ const Field2D b0xGrad_dot_Grad(const Field2D &phi, const Field2D &A, CELL_LOC ou return result; } -const Field3D b0xGrad_dot_Grad(const Field2D &phi, const Field3D &A, CELL_LOC outloc) { +Field3D b0xGrad_dot_Grad(const Field2D& phi, const Field3D& A, CELL_LOC outloc) { TRACE("b0xGrad_dot_Grad( Field2D , Field3D )"); if (outloc == CELL_DEFAULT) outloc = A.getLocation(); @@ -444,13 +483,13 @@ const Field3D b0xGrad_dot_Grad(const Field2D &phi, const Field3D &A, CELL_LOC ou Coordinates *metric = phi.getCoordinates(outloc); // Calculate phi derivatives - Field2D dpdx = DDX(phi, outloc); - Field2D dpdy = DDY(phi, outloc); + Coordinates::FieldMetric dpdx = DDX(phi, outloc); + Coordinates::FieldMetric dpdy = DDY(phi, outloc); // Calculate advection velocity - Field2D vx = -metric->g_23 * dpdy; - Field2D vy = metric->g_23 * dpdx; - Field2D vz = metric->g_12 * dpdy - metric->g_22 * dpdx; + Coordinates::FieldMetric vx = -metric->g_23 * dpdy; + Coordinates::FieldMetric vy = metric->g_23 * dpdx; + Coordinates::FieldMetric vz = metric->g_12 * dpdy - metric->g_22 * dpdx; if(mesh->IncIntShear) { // BOUT-06 style differencing @@ -468,11 +507,11 @@ const Field3D b0xGrad_dot_Grad(const Field2D &phi, const Field3D &A, CELL_LOC ou #endif ASSERT2(result.getLocation() == outloc); - + return result; } -const Field3D b0xGrad_dot_Grad(const Field3D &p, const Field2D &A, CELL_LOC outloc) { +Field3D b0xGrad_dot_Grad(const Field3D& p, const Field2D& A, CELL_LOC outloc) { TRACE("b0xGrad_dot_Grad( Field3D , Field2D )"); if (outloc == CELL_DEFAULT) outloc = A.getLocation(); @@ -499,13 +538,13 @@ const Field3D b0xGrad_dot_Grad(const Field3D &p, const Field2D &A, CELL_LOC outl #if BOUT_USE_TRACK result.name = "b0xGrad_dot_Grad("+p.name+","+A.name+")"; #endif - + ASSERT2(result.getLocation() == outloc); return result; } -const Field3D b0xGrad_dot_Grad(const Field3D &phi, const Field3D &A, CELL_LOC outloc) { +Field3D b0xGrad_dot_Grad(const Field3D& phi, const Field3D& A, CELL_LOC outloc) { TRACE("b0xGrad_dot_Grad( Field3D , Field3D )"); if (outloc == CELL_DEFAULT) outloc = A.getLocation(); @@ -549,8 +588,9 @@ const Field3D b0xGrad_dot_Grad(const Field3D &phi, const Field3D &A, CELL_LOC ou * Terms of form b0 x Grad(f) dot Grad(g) / B = [f, g] *******************************************************************************/ -const Field2D bracket(const Field2D &f, const Field2D &g, BRACKET_METHOD method, - CELL_LOC outloc, Solver *UNUSED(solver)) { +Coordinates::FieldMetric bracket(const Field2D& f, const Field2D& g, + BRACKET_METHOD method, CELL_LOC outloc, + Solver* UNUSED(solver)) { TRACE("bracket(Field2D, Field2D)"); ASSERT1_FIELDS_COMPATIBLE(f, g); @@ -559,7 +599,7 @@ const Field2D bracket(const Field2D &f, const Field2D &g, BRACKET_METHOD method, } ASSERT1(outloc == g.getLocation()); - Field2D result{emptyFrom(f)}; + Coordinates::FieldMetric result{emptyFrom(f)}; if( (method == BRACKET_SIMPLE) || (method == BRACKET_ARAKAWA)) { // Use a subset of terms for comparison to BOUT-06 @@ -572,8 +612,8 @@ const Field2D bracket(const Field2D &f, const Field2D &g, BRACKET_METHOD method, return result; } -const Field3D bracket(const Field3D &f, const Field2D &g, BRACKET_METHOD method, - CELL_LOC outloc, Solver *solver) { +Field3D bracket(const Field3D& f, const Field2D& g, BRACKET_METHOD method, + CELL_LOC outloc, Solver* solver) { TRACE("bracket(Field3D, Field2D)"); ASSERT1_FIELDS_COMPATIBLE(f, g); @@ -582,7 +622,7 @@ const Field3D bracket(const Field3D &f, const Field2D &g, BRACKET_METHOD method, } ASSERT1(outloc == g.getLocation()); - Mesh *mesh = f.getMesh(); + MAYBE_UNUSED(Mesh * mesh) = f.getMesh(); Field3D result{emptyFrom(f).setLocation(outloc)}; @@ -592,51 +632,56 @@ const Field3D bracket(const Field3D &f, const Field2D &g, BRACKET_METHOD method, case BRACKET_CTU: { // First order Corner Transport Upwind method // P.Collela JCP 87, 171-200 (1990) - + if(!solver) throw BoutException("CTU method requires access to the solver"); - - int ncz = mesh->LocalNz; + +#if not(BOUT_USE_METRIC_3D) + const int ncz = mesh->LocalNz; + for(int x=mesh->xstart;x<=mesh->xend;x++) for(int y=mesh->ystart;y<=mesh->yend;y++) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = 0; z < ncz; z++) { int zm = (z - 1 + ncz) % ncz; - int zp = (z + 1) % ncz; - - BoutReal gp, gm; - + int zp = (z + 1) % ncz; + + BoutReal gp, gm; + // Vx = DDZ(f) - BoutReal vx = (f(x,y,zp) - f(x,y,zm))/(2.*metric->dz); - + BoutReal vx = (f(x, y, zp) - f(x, y, zm)) / (2. * metric->dz(x, y, z)); + // Set stability condition - solver->setMaxTimestep(metric->dx(x,y) / (fabs(vx) + 1e-16)); - + solver->setMaxTimestep(metric->dx(x, y, z) / (fabs(vx) + 1e-16)); + // X differencing if(vx > 0.0) { gp = g(x,y); - + gm = g(x-1,y); - + }else { gp = g(x+1,y); - + gm = g(x,y); } - - result(x,y,z) = vx * (gp - gm) / metric->dx(x,y); + + result(x, y, z) = vx * (gp - gm) / metric->dx(x, y, z); } } +#else + throw BoutException("BRACKET_CTU not valid with 3D metrics yet."); +#endif break; } case BRACKET_ARAKAWA: { // Arakawa scheme for perpendicular flow. Here as a test - const BoutReal fac = 1.0 / (12 * metric->dz); +#if not(BOUT_USE_METRIC_3D) const int ncz = mesh->LocalNz; BOUT_FOR(j2D, result.getRegion2D("RGN_NOBNDRY")) { // Get constants for this iteration - const BoutReal spacingFactor = fac / metric->dx[j2D]; + const BoutReal spacingFactor = 1.0 / (12 * metric->dz[j2D] * metric->dx[j2D]); const int jy = j2D.y(), jx = j2D.x(); const int xm = jx - 1, xp = jx + 1; @@ -695,45 +740,50 @@ const Field3D bracket(const Field3D &f, const Field2D &g, BRACKET_METHOD method, result(jx, jy, ncz - 1) = (Jpp + Jpx) * spacingFactor; } } +#else + throw BoutException("BRACKET_ARAKAWA not valid with 3D metrics yet."); +#endif break; } case BRACKET_ARAKAWA_OLD: { +#if not(BOUT_USE_METRIC_3D) const int ncz = mesh->LocalNz; - const BoutReal partialFactor = 1.0/(12 * metric->dz); BOUT_OMP(parallel for) for(int jx=mesh->xstart;jx<=mesh->xend;jx++){ for(int jy=mesh->ystart;jy<=mesh->yend;jy++){ - const BoutReal spacingFactor = partialFactor / metric->dx(jx,jy); + const BoutReal partialFactor = 1.0 / (12 * metric->dz(jx, jy)); + const BoutReal spacingFactor = partialFactor / metric->dx(jx, jy); for (int jz = 0; jz < mesh->LocalNz; jz++) { const int jzp = jz+1 < ncz ? jz + 1 : 0; - //Above is alternative to const int jzp = (jz + 1) % ncz; - const int jzm = jz-1 >= 0 ? jz - 1 : ncz-1; - //Above is alternative to const int jzmTmp = (jz - 1 + ncz) % ncz; + // Above is alternative to const int jzp = (jz + 1) % ncz; + const int jzm = jz - 1 >= 0 ? jz - 1 : ncz - 1; + // Above is alternative to const int jzmTmp = (jz - 1 + ncz) % ncz; // J++ = DDZ(f)*DDX(g) - DDX(f)*DDZ(g) - BoutReal Jpp = ( (f(jx,jy,jzp) - f(jx,jy,jzm))* - (g(jx+1,jy) - g(jx-1,jy)) - - (f(jx+1,jy,jz) - f(jx-1,jy,jz))* - (g(jx,jy) - g(jx,jy)) ); - + BoutReal Jpp = + ((f(jx, jy, jzp) - f(jx, jy, jzm)) * (g(jx + 1, jy) - g(jx - 1, jy)) + - (f(jx + 1, jy, jz) - f(jx - 1, jy, jz)) * (g(jx, jy) - g(jx, jy))); + // J+x - BoutReal Jpx = ( g(jx+1,jy)*(f(jx+1,jy,jzp)-f(jx+1,jy,jzm)) - - g(jx-1,jy)*(f(jx-1,jy,jzp)-f(jx-1,jy,jzm)) - - g(jx,jy)*(f(jx+1,jy,jzp)-f(jx-1,jy,jzp)) + - g(jx,jy)*(f(jx+1,jy,jzm)-f(jx-1,jy,jzm))); + BoutReal Jpx = (g(jx + 1, jy) * (f(jx + 1, jy, jzp) - f(jx + 1, jy, jzm)) + - g(jx - 1, jy) * (f(jx - 1, jy, jzp) - f(jx - 1, jy, jzm)) + - g(jx, jy) * (f(jx + 1, jy, jzp) - f(jx - 1, jy, jzp)) + + g(jx, jy) * (f(jx + 1, jy, jzm) - f(jx - 1, jy, jzm))); // Jx+ - BoutReal Jxp = ( g(jx+1,jy)*(f(jx,jy,jzp)-f(jx+1,jy,jz)) - - g(jx-1,jy)*(f(jx-1,jy,jz)-f(jx,jy,jzm)) - - g(jx-1,jy)*(f(jx,jy,jzp)-f(jx-1,jy,jz)) + - g(jx+1,jy)*(f(jx+1,jy,jz)-f(jx,jy,jzm))); - + BoutReal Jxp = (g(jx + 1, jy) * (f(jx, jy, jzp) - f(jx + 1, jy, jz)) + - g(jx - 1, jy) * (f(jx - 1, jy, jz) - f(jx, jy, jzm)) + - g(jx - 1, jy) * (f(jx, jy, jzp) - f(jx - 1, jy, jz)) + + g(jx + 1, jy) * (f(jx + 1, jy, jz) - f(jx, jy, jzm))); + result(jx,jy,jz) = (Jpp + Jpx + Jxp) * spacingFactor; - } - } + } } - + } +#else + throw BoutException("BRACKET_ARAKAWA_OLD not valid with 3D metrics yet."); +#endif break; } case BRACKET_SIMPLE: { @@ -749,8 +799,8 @@ const Field3D bracket(const Field3D &f, const Field2D &g, BRACKET_METHOD method, return result; } -const Field3D bracket(const Field2D &f, const Field3D &g, BRACKET_METHOD method, - CELL_LOC outloc, Solver *solver) { +Field3D bracket(const Field2D& f, const Field3D& g, BRACKET_METHOD method, + CELL_LOC outloc, Solver* solver) { TRACE("bracket(Field2D, Field3D)"); ASSERT1_FIELDS_COMPATIBLE(f, g); @@ -767,7 +817,7 @@ const Field3D bracket(const Field2D &f, const Field3D &g, BRACKET_METHOD method, case BRACKET_CTU: throw BoutException("Bracket method CTU is not yet implemented for [2d,3d] fields."); break; - case BRACKET_ARAKAWA: + case BRACKET_ARAKAWA: // It is symmetric, therefore we can return -[3d,2d] return -bracket(g,f,method,outloc,solver); break; @@ -782,12 +832,12 @@ const Field3D bracket(const Field2D &f, const Field3D &g, BRACKET_METHOD method, result = b0xGrad_dot_Grad(f, g, outloc) / metric->Bxy; } } - + return result; } -const Field3D bracket(const Field3D &f, const Field3D &g, BRACKET_METHOD method, - CELL_LOC outloc, Solver *solver) { +Field3D bracket(const Field3D& f, const Field3D& g, BRACKET_METHOD method, + CELL_LOC outloc, MAYBE_UNUSED(Solver* solver)) { TRACE("Field3D, Field3D"); ASSERT1_FIELDS_COMPATIBLE(f, g); @@ -807,44 +857,44 @@ const Field3D bracket(const Field3D &f, const Field3D &g, BRACKET_METHOD method, result.setLocation(outloc); return result; } - + switch(method) { case BRACKET_CTU: { // First order Corner Transport Upwind method // P.Collela JCP 87, 171-200 (1990) - +#if not(BOUT_USE_METRIC_3D) if(!solver) throw BoutException("CTU method requires access to the solver"); // Get current timestep BoutReal dt = solver->getCurrentTimestep(); - + FieldPerp vx(mesh), vz(mesh); vx.allocate(); vx.setLocation(outloc); vz.allocate(); vz.setLocation(outloc); - + int ncz = mesh->LocalNz; for(int y=mesh->ystart;y<=mesh->yend;y++) { for(int x=1;x<=mesh->LocalNx-2;x++) { for (int z = 0; z < mesh->LocalNz; z++) { int zm = (z - 1 + ncz) % ncz; int zp = (z + 1) % ncz; - + // Vx = DDZ(f) - vx(x,z) = (f(x,y,zp) - f(x,y,zm))/(2.*metric->dz); + vx(x, z) = (f(x, y, zp) - f(x, y, zm)) / (2. * metric->dz(x, y, z)); // Vz = -DDX(f) vz(x,z) = (f(x-1,y,z) - f(x+1,y,z))/(0.5*metric->dx(x-1,y) + metric->dx(x,y) + 0.5*metric->dx(x+1,y)); - + // Set stability condition solver->setMaxTimestep(fabs(metric->dx(x,y)) / (fabs(vx(x,z)) + 1e-16)); - solver->setMaxTimestep(metric->dz / (fabs(vz(x,z)) + 1e-16)); + solver->setMaxTimestep(metric->dz(x, y) / (fabs(vz(x, z)) + 1e-16)); } } - + // Simplest form: use cell-centered velocities (no divergence included so not flux conservative) - + for(int x=mesh->xstart;x<=mesh->xend;x++) for (int z = 0; z < ncz; z++) { int zm = (z - 1 + ncz) % ncz; @@ -854,26 +904,26 @@ const Field3D bracket(const Field3D &f, const Field3D &g, BRACKET_METHOD method, // X differencing if (vx(x, z) > 0.0) { - gp = g(x, y, z) + - (0.5 * dt / metric->dz) * ((vz(x, z) > 0) - ? vz(x, z) * (g(x, y, zm) - g(x, y, z)) - : vz(x, z) * (g(x, y, z) - g(x, y, zp))); + gp = g(x, y, z) + + (0.5 * dt / metric->dz(x, y)) + * ((vz(x, z) > 0) ? vz(x, z) * (g(x, y, zm) - g(x, y, z)) + : vz(x, z) * (g(x, y, z) - g(x, y, zp))); - gm = g(x - 1, y, z) + - (0.5 * dt / metric->dz) * - ((vz(x, z) > 0) ? vz(x, z) * (g(x - 1, y, zm) - g(x - 1, y, z)) - : vz(x, z) * (g(x - 1, y, z) - g(x - 1, y, zp))); + gm = g(x - 1, y, z) + + (0.5 * dt / metric->dz(x, y)) + * ((vz(x, z) > 0) ? vz(x, z) * (g(x - 1, y, zm) - g(x - 1, y, z)) + : vz(x, z) * (g(x - 1, y, z) - g(x - 1, y, zp))); } else { - gp = g(x + 1, y, z) + - (0.5 * dt / metric->dz) * - ((vz(x, z) > 0) ? vz(x, z) * (g(x + 1, y, zm) - g(x + 1, y, z)) - : vz[x][z] * (g(x + 1, y, z) - g(x + 1, y, zp))); - - gm = g(x, y, z) + - (0.5 * dt / metric->dz) * ((vz(x, z) > 0) - ? vz(x, z) * (g(x, y, zm) - g(x, y, z)) - : vz(x, z) * (g(x, y, z) - g(x, y, zp))); + gp = g(x + 1, y, z) + + (0.5 * dt / metric->dz(x, y)) + * ((vz(x, z) > 0) ? vz(x, z) * (g(x + 1, y, zm) - g(x + 1, y, z)) + : vz[x][z] * (g(x + 1, y, z) - g(x + 1, y, zp))); + + gm = g(x, y, z) + + (0.5 * dt / metric->dz(x, y)) + * ((vz(x, z) > 0) ? vz(x, z) * (g(x, y, zm) - g(x, y, z)) + : vz(x, z) * (g(x, y, z) - g(x, y, zp))); } result(x, y, z) = vx(x, z) * (gp - gm) / metric->dx(x, y); @@ -901,16 +951,17 @@ const Field3D bracket(const Field3D &f, const Field3D &g, BRACKET_METHOD method, : vx(x, z) * (g(x, y, z) - g(x + 1, y, z))); } - result(x, y, z) += vz(x, z) * (gp - gm) / metric->dz; + result(x, y, z) += vz(x, z) * (gp - gm) / metric->dz(x, y); } } +#else + throw BoutException("BRACKET_CTU not valid with 3D metrics yet."); +#endif break; } case BRACKET_ARAKAWA: { // Arakawa scheme for perpendicular flow - const int ncz = mesh->LocalNz; - const BoutReal partialFactor = 1.0/(12 * metric->dz); // We need to discard const qualifier in order to manipulate // storage array directly @@ -918,7 +969,9 @@ const Field3D bracket(const Field3D &f, const Field3D &g, BRACKET_METHOD method, Field3D g_temp = g; BOUT_FOR(j2D, result.getRegion2D("RGN_NOBNDRY")) { - const BoutReal spacingFactor = partialFactor / metric->dx[j2D]; +#if not(BOUT_USE_METRIC_3D) + const BoutReal spacingFactor = 1.0 / (12 * metric->dz[j2D] * metric->dx[j2D]); +#endif const int jy = j2D.y(), jx = j2D.x(); const int xm = jx - 1, xp = jx + 1; @@ -933,6 +986,10 @@ const Field3D bracket(const Field3D &f, const Field3D &g, BRACKET_METHOD method, const int jz = 0; const int jzp = 1; const int jzm = ncz - 1; +#if BOUT_USE_METRIC_3D + const BoutReal spacingFactor = + 1.0 / (12 * metric->dz(jx, jy, jz) * metric->dx(jx, jy, jz)); +#endif // J++ = DDZ(f)*DDX(g) - DDX(f)*DDZ(g) const BoutReal Jpp = ((Fx[jzp] - Fx[jzm]) * (Gxp[jz] - Gxm[jz]) - @@ -952,6 +1009,10 @@ const Field3D bracket(const Field3D &f, const Field3D &g, BRACKET_METHOD method, } for (int jz = 1; jz < mesh->LocalNz - 1; jz++) { +#if BOUT_USE_METRIC_3D + const BoutReal spacingFactor = + 1.0 / (12 * metric->dz(jx, jy, jz) * metric->dx(jx, jy, jz)); +#endif const int jzp = jz + 1; const int jzm = jz - 1; @@ -976,6 +1037,10 @@ const Field3D bracket(const Field3D &f, const Field3D &g, BRACKET_METHOD method, const int jz = ncz - 1; const int jzp = 0; const int jzm = ncz - 2; +#if BOUT_USE_METRIC_3D + const BoutReal spacingFactor = + 1.0 / (12 * metric->dz(jx, jy, jz) * metric->dx(jx, jy, jz)); +#endif // J++ = DDZ(f)*DDX(g) - DDX(f)*DDZ(g) const BoutReal Jpp = ((Fx[jzp] - Fx[jzm]) * (Gxp[jz] - Gxm[jz]) - @@ -994,14 +1059,12 @@ const Field3D bracket(const Field3D &f, const Field3D &g, BRACKET_METHOD method, result(jx, jy, jz) = (Jpp + Jpx + Jxp) * spacingFactor; } } - break; } case BRACKET_ARAKAWA_OLD: { // Arakawa scheme for perpendicular flow const int ncz = mesh->LocalNz; - const BoutReal partialFactor = 1.0 / (12 * metric->dz); // We need to discard const qualifier in order to manipulate // storage array directly @@ -1011,7 +1074,10 @@ const Field3D bracket(const Field3D &f, const Field3D &g, BRACKET_METHOD method, BOUT_OMP(parallel for) for(int jx=mesh->xstart;jx<=mesh->xend;jx++){ for(int jy=mesh->ystart;jy<=mesh->yend;jy++){ - const BoutReal spacingFactor = partialFactor / metric->dx(jx, jy); +#if not(BOUT_USE_METRIC_3D) + const BoutReal spacingFactor = + 1.0 / (12 * metric->dz(jx, jy) * metric->dx(jx, jy)); +#endif const BoutReal *Fxm = f_temp(jx-1, jy); const BoutReal *Fx = f_temp(jx, jy); const BoutReal *Fxp = f_temp(jx+1, jy); @@ -1019,31 +1085,37 @@ const Field3D bracket(const Field3D &f, const Field3D &g, BRACKET_METHOD method, const BoutReal *Gx = g_temp(jx, jy); const BoutReal *Gxp = g_temp(jx+1, jy); for (int jz = 0; jz < mesh->LocalNz; jz++) { +#if BOUT_USE_METRIC_3D + const BoutReal spacingFactor = + 1.0 / (12 * metric->dz(jx, jy, jz) * metric->dx(jx, jy, jz)); +#endif const int jzp = jz+1 < ncz ? jz + 1 : 0; - //Above is alternative to const int jzp = (jz + 1) % ncz; - const int jzm = jz-1 >= 0 ? jz - 1 : ncz-1; - //Above is alternative to const int jzm = (jz - 1 + ncz) % ncz; + // Above is alternative to const int jzp = (jz + 1) % ncz; + const int jzm = jz - 1 >= 0 ? jz - 1 : ncz - 1; + // Above is alternative to const int jzm = (jz - 1 + ncz) % ncz; // J++ = DDZ(f)*DDX(g) - DDX(f)*DDZ(g) - BoutReal Jpp = ((Fx[jzp] - Fx[jzm])*(Gxp[jz] - Gxm[jz]) - - (Fxp[jz] - Fxm[jz])*(Gx[jzp] - Gx[jzm])); + // NOLINTNEXTLINE + BoutReal Jpp = ((Fx[jzp] - Fx[jzm]) * (Gxp[jz] - Gxm[jz]) + - (Fxp[jz] - Fxm[jz]) * (Gx[jzp] - Gx[jzm])); // J+x - BoutReal Jpx = ( Gxp[jz]*(Fxp[jzp]-Fxp[jzm]) - - Gxm[jz]*(Fxm[jzp]-Fxm[jzm]) - - Gx[jzp]*(Fxp[jzp]-Fxm[jzp]) + - Gx[jzm]*(Fxp[jzm]-Fxm[jzm])) ; + // NOLINTNEXTLINE + BoutReal Jpx = + (Gxp[jz] * (Fxp[jzp] - Fxp[jzm]) - Gxm[jz] * (Fxm[jzp] - Fxm[jzm]) + - Gx[jzp] * (Fxp[jzp] - Fxm[jzp]) + Gx[jzm] * (Fxp[jzm] - Fxm[jzm])); // Jx+ - BoutReal Jxp = ( Gxp[jzp]*(Fx[jzp]-Fxp[jz]) - - Gxm[jzm]*(Fxm[jz]-Fx[jzm]) - - Gxm[jzp]*(Fx[jzp]-Fxm[jz]) + - Gxp[jzm]*(Fxp[jz]-Fx[jzm])); - + // NOLINTNEXTLINE + BoutReal Jxp = + (Gxp[jzp] * (Fx[jzp] - Fxp[jz]) - Gxm[jzm] * (Fxm[jz] - Fx[jzm]) + - Gxm[jzp] * (Fx[jzp] - Fxm[jz]) + Gxp[jzm] * (Fxp[jz] - Fx[jzm])); + result(jx, jy, jz) = (Jpp + Jpx + Jxp) * spacingFactor; } } } + break; } case BRACKET_SIMPLE: { @@ -1056,6 +1128,6 @@ const Field3D bracket(const Field3D &f, const Field3D &g, BRACKET_METHOD method, result = b0xGrad_dot_Grad(f, g, outloc) / metric->Bxy; } } - + return result; } diff --git a/src/mesh/fv_ops.cxx b/src/mesh/fv_ops.cxx index 0e3ddc54c8..1640c1bf0b 100644 --- a/src/mesh/fv_ops.cxx +++ b/src/mesh/fv_ops.cxx @@ -32,19 +32,22 @@ namespace FV { xe -= 1; */ - for(int i=xs;i<=xe;i++) - for(int j=mesh->ystart;j<=mesh->yend;j++) { - for(int k=0;kLocalNz;k++) { - // Calculate flux from i to i+1 - - BoutReal fout = 0.5*(a(i,j,k) + a(i+1,j,k)) * (coord->J(i,j)*coord->g11(i,j) + coord->J(i+1,j)*coord->g11(i+1,j)) * - (f(i+1,j,k) - f(i,j,k))/(coord->dx(i,j) + coord->dx(i+1,j)); - - result(i,j,k) += fout / (coord->dx(i,j)*coord->J(i,j)); - result(i+1,j,k) -= fout / (coord->dx(i+1,j)*coord->J(i+1,j)); - } - } + for (int i = xs; i <= xe; i++) { + for (int j = mesh->ystart; j <= mesh->yend; j++) { + for (int k = 0; k < mesh->LocalNz; k++) { + // Calculate flux from i to i+1 + BoutReal fout = 0.5 * (a(i, j, k) + a(i + 1, j, k)) + * (coord->J(i, j, k) * coord->g11(i, j, k) + + coord->J(i + 1, j, k) * coord->g11(i + 1, j, k)) + * (f(i + 1, j, k) - f(i, j, k)) + / (coord->dx(i, j, k) + coord->dx(i + 1, j, k)); + + result(i, j, k) += fout / (coord->dx(i, j, k) * coord->J(i, j, k)); + result(i + 1, j, k) -= fout / (coord->dx(i + 1, j, k) * coord->J(i + 1, j, k)); + } + } + } // Y and Z fluxes require Y derivatives @@ -52,11 +55,25 @@ namespace FV { Field3D fup(mesh), fdown(mesh); Field3D aup(mesh), adown(mesh); + Field3D g23up(mesh), g23down(mesh); + Field3D g_23up(mesh), g_23down(mesh); + Field3D Jup(mesh), Jdown(mesh); + Field3D dyup(mesh), dydown(mesh); + Field3D dzup(mesh), dzdown(mesh); + Field3D Bxyup(mesh), Bxydown(mesh); + // Values on this y slice (centre). // This is needed because toFieldAligned may modify the field Field3D fc = f; Field3D ac = a; + Field3D g23c = coord->g23; + Field3D g_23c = coord->g_23; + Field3D Jc = coord->J; + Field3D dyc = coord->dy; + Field3D dzc = coord->dz; + Field3D Bxyc = coord->Bxy; + // Result of the Y and Z fluxes Field3D yzresult(mesh); yzresult.allocate(); @@ -75,83 +92,128 @@ namespace FV { fup = fdown = fc = toFieldAligned(f); aup = adown = ac = toFieldAligned(a); + yzresult.setDirectionY(YDirectionType::Aligned); } + if (bout::build::use_metric_3d) { + // 3D Metric, need yup/ydown fields. + // Requires previous communication of metrics + // -- should insert communication here? + if (!coord->g23.hasParallelSlices() || !coord->g_23.hasParallelSlices() + || !coord->dy.hasParallelSlices() || !coord->dz.hasParallelSlices() + || !coord->Bxy.hasParallelSlices() || !coord->J.hasParallelSlices()) { + throw BoutException("metrics have no yup/down: Maybe communicate in init?"); + } + + g23up = coord->g23.yup(); + g23down = coord->g23.ydown(); + + g_23up = coord->g_23.yup(); + g_23down = coord->g_23.ydown(); + + Jup = coord->J.yup(); + Jdown = coord->J.ydown(); + + dyup = coord->dy.yup(); + dydown = coord->dy.ydown(); + + dzup = coord->dz.yup(); + dzdown = coord->dz.ydown(); + + Bxyup = coord->Bxy.yup(); + Bxydown = coord->Bxy.ydown(); + + } else { + // No 3D metrics + // Need to shift to/from field aligned coordinates + g23up = g23down = g23c = toFieldAligned(coord->g23); + g_23up = g_23down = g_23c = toFieldAligned(coord->g_23); + Jup = Jdown = Jc = toFieldAligned(coord->J); + dyup = dydown = dyc = toFieldAligned(coord->dy); + dzup = dzdown = dzc = toFieldAligned(coord->dz); + Bxyup = Bxydown = Bxyc = toFieldAligned(coord->Bxy); + } + // Y flux for (int i = mesh->xstart; i <= mesh->xend; i++) { for (int j = mesh->ystart; j <= mesh->yend; j++) { - - BoutReal coef = - 0.5 * (coord->g_23(i, j) / SQ(coord->J(i, j) * coord->Bxy(i, j)) + - coord->g_23(i, j + 1) / SQ(coord->J(i, j + 1) * coord->Bxy(i, j + 1))); - for (int k = 0; k < mesh->LocalNz; k++) { // Calculate flux between j and j+1 int kp = (k + 1) % mesh->LocalNz; int km = (k - 1 + mesh->LocalNz) % mesh->LocalNz; + BoutReal coef = + 0.5 + * (g_23c(i, j, k) / SQ(Jc(i, j, k) * Bxyc(i, j, k)) + + g_23up(i, j + 1, k) / SQ(Jup(i, j + 1, k) * Bxyup(i, j + 1, k))); + // Calculate Z derivative at y boundary - BoutReal dfdz = 0.25 * (fc(i, j, kp) - fc(i, j, km) + fup(i, j + 1, kp) - - fup(i, j + 1, km)) / - coord->dz; + BoutReal dfdz = + 0.5 * (fc(i, j, kp) - fc(i, j, km) + fup(i, j + 1, kp) - fup(i, j + 1, km)) + / (dzc(i, j, k) + dzup(i, j + 1, k)); // Y derivative - BoutReal dfdy = 2. * (fup(i, j + 1, k) - fc(i, j, k)) / - (coord->dy(i, j + 1) + coord->dy(i, j)); + BoutReal dfdy = + 2. * (fup(i, j + 1, k) - fc(i, j, k)) / (dyup(i, j + 1, k) + dyc(i, j, k)); - BoutReal fout = 0.25 * (ac(i, j, k) + aup(i, j + 1, k)) * - (coord->J(i, j) * coord->g23(i, j) + - coord->J(i, j + 1) * coord->g23(i, j + 1)) * - (dfdz - coef * dfdy); + BoutReal fout = + 0.25 * (ac(i, j, k) + aup(i, j + 1, k)) + * (Jc(i, j, k) * g23c(i, j, k) + Jup(i, j + 1, k) * g23up(i, j + 1, k)) + * (dfdz - coef * dfdy); - yzresult(i, j, k) = fout / (coord->dy(i, j) * coord->J(i, j)); + yzresult(i, j, k) = fout / (dyc(i, j, k) * Jc(i, j, k)); // Calculate flux between j and j-1 - dfdz = 0.25 * (fc(i, j, kp) - fc(i, j, km) + fdown(i, j - 1, kp) - - fdown(i, j - 1, km)) / - coord->dz; + coef = + 0.5 + * (g_23c(i, j, k) / SQ(Jc(i, j, k) * Bxyc(i, j, k)) + + g_23down(i, j - 1, k) / SQ(Jdown(i, j - 1, k) * Bxydown(i, j - 1, k))); + + dfdz = + 0.5 + * (fc(i, j, kp) - fc(i, j, km) + fdown(i, j - 1, kp) - fdown(i, j - 1, km)) + / (dzc(i, j, k) + dzdown(i, j - 1, k)); - dfdy = 2. * (fc(i, j, k) - fdown(i, j - 1, k)) / - (coord->dy(i, j) + coord->dy(i, j - 1)); + dfdy = 2. * (fc(i, j, k) - fdown(i, j - 1, k)) + / (dyc(i, j, k) + dydown(i, j - 1, k)); - fout = 0.25 * (ac(i, j, k) + adown(i, j - 1, k)) * - (coord->J(i, j) * coord->g23(i, j) + - coord->J(i, j - 1) * coord->g23(i, j - 1)) * - (dfdz - coef * dfdy); + fout = + 0.25 * (ac(i, j, k) + adown(i, j - 1, k)) + * (Jc(i, j, k) * g23c(i, j, k) + Jdown(i, j - 1, k) * g23down(i, j - 1, k)) + * (dfdz - coef * dfdy); - yzresult(i, j, k) -= fout / (coord->dy(i, j) * coord->J(i, j)); + yzresult(i, j, k) -= fout / (dyc(i, j, k) * Jc(i, j, k)); } } } // Z flux - // Easier since all metrics constant in Z for (int i = mesh->xstart; i <= mesh->xend; i++) { for (int j = mesh->ystart; j <= mesh->yend; j++) { - // Coefficient in front of df/dy term - BoutReal coef = coord->g_23(i, j) / (coord->dy(i, j + 1) + 2. * coord->dy(i, j) + - coord->dy(i, j - 1)) / - SQ(coord->J(i, j) * coord->Bxy(i, j)); - for (int k = 0; k < mesh->LocalNz; k++) { // Calculate flux between k and k+1 int kp = (k + 1) % mesh->LocalNz; - BoutReal fout = 0.5 * (ac(i, j, k) + ac(i, j, kp)) * coord->g33(i, j) * - ( - // df/dz - (fc(i, j, kp) - fc(i, j, k)) / coord->dz - - // - g_yz * df/dy / SQ(J*B) - - - coef * (fup(i, j + 1, k) + fup(i, j + 1, kp) - - fdown(i, j - 1, k) - fdown(i, j - 1, kp))); - - yzresult(i, j, k) += fout / coord->dz; - yzresult(i, j, kp) -= fout / coord->dz; + // Coefficient in front of df/dy term + BoutReal coef = g_23c(i, j, k) + / (dyup(i, j + 1, k) + 2. * dyc(i, j, k) + dydown(i, j - 1, k)) + / SQ(Jc(i, j, k) * Bxyc(i, j, k)); + + BoutReal fout = + 0.25 * (ac(i, j, k) + ac(i, j, kp)) + * (Jc(i, j, k) * coord->g33(i, j, k) + Jc(i, j, kp) * coord->g33(i, j, kp)) + * ( // df/dz + (fc(i, j, kp) - fc(i, j, k)) / dzc(i, j, k) + // - g_yz * df/dy / SQ(J*B) + - coef + * (fup(i, j + 1, k) + fup(i, j + 1, kp) - fdown(i, j - 1, k) + - fdown(i, j - 1, kp))); + + yzresult(i, j, k) += fout / (Jc(i, j, k) * dzc(i, j, k)); + yzresult(i, j, kp) -= fout / (Jc(i, j, kp) * dzc(i, j, kp)); } } } @@ -248,9 +310,8 @@ namespace FV { for(int i=mesh->xstart;i<=mesh->xend;i++) for(int j=mesh->ystart;j<=mesh->yend;j++) { - BoutReal dy3 = SQ(coord->dy(i,j))*coord->dy(i,j); for(int k=0;kLocalNz;k++) { - + BoutReal dy3 = SQ(coord->dy(i, j, k)) * coord->dy(i, j, k); // 3rd derivative at right boundary BoutReal d3fdx3 = ( @@ -259,12 +320,13 @@ namespace FV { + 3.*f(i,j, k) - f(i,j-1,k) ) / dy3; - - BoutReal flux = 0.5*(d(i,j,k) + d(i,j+1,k))*(coord->J(i,j) + coord->J(i,j+1)) * d3fdx3; - - result(i,j, k) += flux / (coord->J(i,j) * coord->dy(i,j)); - result(i,j+1,k) -= flux / (coord->J(i,j+1) * coord->dy(i,j+1)); - + + BoutReal flux = 0.5 * (d(i, j, k) + d(i, j + 1, k)) + * (coord->J(i, j, k) + coord->J(i, j + 1, k)) * d3fdx3; + + result(i, j, k) += flux / (coord->J(i, j, k) * coord->dy(i, j, k)); + result(i, j + 1, k) -= flux / (coord->J(i, j + 1, k) * coord->dy(i, j + 1, k)); + if(j == mesh->ystart && (!mesh->firstY())) { // Left cell boundary, no flux through boundaries d3fdx3 = ( @@ -273,11 +335,13 @@ namespace FV { + 3.*f(i,j-1,k) - f(i,j-2,k) ) / dy3; - - flux = 0.5*(d(i,j,k) + d(i,j-1,k))*(coord->J(i,j) + coord->J(i,j-1)) * d3fdx3; - - result(i,j, k) -= flux / (coord->J(i,j) * coord->dy(i,j)); - result(i,j-1,k) += flux / (coord->J(i,j-1) * coord->dy(i,j-1)); + + flux = 0.5 * (d(i, j, k) + d(i, j - 1, k)) + * (coord->J(i, j, k) + coord->J(i, j - 1, k)) * d3fdx3; + + result(i, j, k) -= flux / (coord->J(i, j, k) * coord->dy(i, j, k)); + result(i, j - 1, k) += + flux / (coord->J(i, j - 1, k) * coord->dy(i, j - 1, k)); } } } @@ -309,42 +373,51 @@ namespace FV { if (bndry_flux || (j != mesh->yend) || !has_upper_boundary) { // Calculate the fluxes - - // Right boundary common factors - BoutReal common_factor = 0.25 * - (coord->dy(i, j) + coord->dy(i, j + 1)) * - (coord->J(i, j) + coord->J(i, j + 1)); - - BoutReal factor_rc = common_factor / (coord->J(i,j) * coord->dy(i,j)); - BoutReal factor_rp = common_factor / (coord->J(i,j+1) * coord->dy(i,j+1)); - if ( j != mesh->yend || !has_upper_boundary ) { - for(int k=0;kLocalNz;k++) { + + if (j != mesh->yend || !has_upper_boundary) { + + for (int k = 0; k < mesh->LocalNz; k++) { + // Right boundary common factors + const BoutReal common_factor = + 0.25 * (coord->dy(i, j, k) + coord->dy(i, j + 1, k)) + * (coord->J(i, j, j) + coord->J(i, j + 1, k)); + + const BoutReal factor_rc = + common_factor / (coord->J(i, j, k) * coord->dy(i, j, k)); + const BoutReal factor_rp = + common_factor / (coord->J(i, j + 1, k) * coord->dy(i, j + 1, k)); + // Not on domain boundary // 3rd derivative at right cell boundary - - BoutReal d3fdx3 = ( - f(i,j+2,k) - - 3.*f(i,j+1,k) - + 3.*f(i,j, k) - - f(i,j-1,k) - ); - - result(i,j, k) += d3fdx3 * factor_rc; - result(i,j+1,k) -= d3fdx3 * factor_rp; + + const BoutReal d3fdx3 = (f(i, j + 2, k) - 3. * f(i, j + 1, k) + + 3. * f(i, j, k) - f(i, j - 1, k)); + + result(i,j, k) += d3fdx3 * factor_rc; + result(i, j + 1, k) -= d3fdx3 * factor_rp; } } else { // At a domain boundary // Use a one-sided difference formula for(int k=0;kLocalNz;k++) { + // Right boundary common factors + const BoutReal common_factor = + 0.25 * (coord->dy(i, j, k) + coord->dy(i, j + 1, k)) + * (coord->J(i, j, j) + coord->J(i, j + 1, k)); + + const BoutReal factor_rc = + common_factor / (coord->J(i, j, k) * coord->dy(i, j, k)); + const BoutReal factor_rp = + common_factor / (coord->J(i, j + 1, k) * coord->dy(i, j + 1, k)); + + const BoutReal d3fdx3 = + -((16. / 5) * 0.5 * (f(i, j + 1, k) + f(i, j, k)) // Boundary value f_b + - 6. * f(i, j, k) // f_0 + + 4. * f(i, j - 1, k) // f_1 + - (6. / 5) * f(i, j - 2, k) // f_2 + ); - BoutReal d3fdx3 = -((16. / 5) * 0.5 * - (f(i, j + 1, k) + f(i, j, k)) // Boundary value f_b - - 6. * f(i, j, k) // f_0 - + 4. * f(i, j - 1, k) // f_1 - - (6. / 5) * f(i, j - 2, k) // f_2 - ); - result(i,j, k) += d3fdx3 * factor_rc; result(i,j+1,k) -= d3fdx3 * factor_rp; } @@ -356,34 +429,42 @@ namespace FV { if (bndry_flux || (j != mesh->ystart) || !has_lower_boundary) { // Calculate the fluxes - BoutReal common_factor = 0.25 * - (coord->dy(i, j) + coord->dy(i, j + 1)) * - (coord->J(i, j) + coord->J(i, j - 1)); - - BoutReal factor_lc = common_factor / (coord->J(i, j) * coord->dy(i, j)); - BoutReal factor_lm = common_factor / (coord->J(i, j - 1) * coord->dy(i, j - 1)); if ( j != mesh->ystart || !has_lower_boundary ) { for(int k=0;kLocalNz;k++) { - + const BoutReal common_factor = + 0.25 * (coord->dy(i, j, k) + coord->dy(i, j + 1, k)) + * (coord->J(i, j, k) + coord->J(i, j - 1, k)); + + const BoutReal factor_lc = + common_factor / (coord->J(i, j, k) * coord->dy(i, j, k)); + const BoutReal factor_lm = + common_factor / (coord->J(i, j - 1, k) * coord->dy(i, j - 1, k)); + // Not on a domain boundary - BoutReal d3fdx3 = (f(i, j + 1, k) - - 3. * f(i, j, k) - + 3. * f(i, j - 1, k) - - f(i, j - 2, k)); - + const BoutReal d3fdx3 = (f(i, j + 1, k) - 3. * f(i, j, k) + + 3. * f(i, j - 1, k) - f(i, j - 2, k)); + result(i, j , k) -= d3fdx3 * factor_lc; result(i, j - 1, k) += d3fdx3 * factor_lm; } } else { // On a domain (Y) boundary for(int k=0;kLocalNz;k++) { - BoutReal d3fdx3 = -(-(16. / 5) * 0.5 * - (f(i, j - 1, k) + f(i, j, k)) // Boundary value f_b - + 6. * f(i, j, k) // f_0 - - 4. * f(i, j + 1, k) // f_1 - + (6. / 5) * f(i, j + 2, k) // f_2 - ); + const BoutReal common_factor = + 0.25 * (coord->dy(i, j, k) + coord->dy(i, j + 1, k)) + * (coord->J(i, j, k) + coord->J(i, j - 1, k)); + + const BoutReal factor_lc = + common_factor / (coord->J(i, j, k) * coord->dy(i, j, k)); + const BoutReal factor_lm = + common_factor / (coord->J(i, j - 1, k) * coord->dy(i, j - 1, k)); + const BoutReal d3fdx3 = + -(-(16. / 5) * 0.5 * (f(i, j - 1, k) + f(i, j, k)) // Boundary value f_b + + 6. * f(i, j, k) // f_0 + - 4. * f(i, j + 1, k) // f_1 + + (6. / 5) * f(i, j + 2, k) // f_2 + ); result(i, j , k) -= d3fdx3 * factor_lc; result(i, j - 1, k) += d3fdx3 * factor_lm; @@ -442,4 +523,87 @@ namespace FV { } } + Field3D Div_Perp_Lap(const Field3D& a, const Field3D& f, CELL_LOC outloc) { + + Field3D result = 0.0; + + ////////////////////////////////////////// + // X-Z diffusion + // + // Z + // | + // + // o --- gU --- o + // | nU | + // | | + // gL nL nR gR -> X + // | | + // | nD | + // o --- gD --- o + // + Coordinates* coords = a.getCoordinates(outloc); + Mesh* mesh = f.getMesh(); + + for (int i = mesh->xstart; i <= mesh->xend; i++) + for (int j = mesh->ystart; j <= mesh->yend; j++) + for (int k = 0; k < mesh->LocalNz; k++) { + + // wrap k-index around as Z is (currently) periodic. + int kp = (k + 1) % (mesh->LocalNz); + int km = (k - 1 + mesh->LocalNz) % (mesh->LocalNz); + + // Calculate gradients on cell faces -- assumes constant grid spacing + + BoutReal gR = + (coords->g11(i, j, k) + coords->g11(i + 1, j, k)) + * (f(i + 1, j, k) - f(i, j, k)) + / (coords->dx(i + 1, j, k) + coords->dx(i, j, k)) + + 0.5 * (coords->g13(i, j, k) + coords->g13(i + 1, j, k)) + * (f(i + 1, j, kp) - f(i + 1, j, km) + f(i, j, kp) - f(i, j, km)) + / (4. * coords->dz(i, j, k)); + + BoutReal gL = + (coords->g11(i - 1, j, k) + coords->g11(i, j, k)) + * (f(i, j, k) - f(i - 1, j, k)) + / (coords->dx(i - 1, j, k) + coords->dx(i, j, k)) + + 0.5 * (coords->g13(i - 1, j, k) + coords->g13(i, j, k)) + * (f(i - 1, j, kp) - f(i - 1, j, km) + f(i, j, kp) - f(i, j, km)) + / (4 * coords->dz(i, j, k)); + + BoutReal gD = + coords->g13(i, j, k) + * (f(i + 1, j, km) - f(i - 1, j, km) + f(i + 1, j, k) - f(i - 1, j, k)) + / (4. * coords->dx(i, j, k)) + + coords->g33(i, j, k) * (f(i, j, k) - f(i, j, km)) / coords->dz(i, j, k); + + BoutReal gU = + coords->g13(i, j, k) + * (f(i + 1, j, kp) - f(i - 1, j, kp) + f(i + 1, j, k) - f(i - 1, j, k)) + / (4. * coords->dx(i, j, k)) + + coords->g33(i, j, k) * (f(i, j, kp) - f(i, j, k)) / coords->dz(i, j, k); + + // Flow right + BoutReal flux = gR * 0.25 * (coords->J(i + 1, j, k) + coords->J(i, j, k)) + * (a(i + 1, j, k) + a(i, j, k)); + result(i, j, k) += flux / (coords->dx(i, j, k) * coords->J(i, j, k)); + + // Flow left + flux = gL * 0.25 * (coords->J(i - 1, j, k) + coords->J(i, j, k)) + * (a(i - 1, j, k) + a(i, j, k)); + result(i, j, k) -= flux / (coords->dx(i, j, k) * coords->J(i, j, k)); + + // Flow up + flux = gU * 0.25 * (coords->J(i, j, k) + coords->J(i, j, kp)) + * (a(i, j, k) + a(i, j, kp)); + result(i, j, k) += flux / (coords->dz(i, j, k) * coords->J(i, j, k)); + + // Flow down + flux = gD * 0.25 * (coords->J(i, j, km) + coords->J(i, j, k)) + * (a(i, j, km) + a(i, j, k)); + result(i, j, k) += flux / (coords->dz(i, j, k) * coords->J(i, j, k)); + } + + return result; + } + } // Namespace FV diff --git a/src/mesh/mesh.cxx b/src/mesh/mesh.cxx index 5b187c6779..931efa847c 100644 --- a/src/mesh/mesh.cxx +++ b/src/mesh/mesh.cxx @@ -149,7 +149,8 @@ int Mesh::get(bool &bval, const std::string &name, bool def) { return !success; } -int Mesh::get(Field2D &var, const std::string &name, BoutReal def, CELL_LOC location) { +int Mesh::get(Field2D& var, const std::string& name, BoutReal def, bool communicate, + CELL_LOC location) { TRACE("Loading 2D field: Mesh::get(Field2D, {:s})", name); if (source == nullptr or !source->get(this, var, name, def, location)) { @@ -160,7 +161,9 @@ int Mesh::get(Field2D &var, const std::string &name, BoutReal def, CELL_LOC loca } // Communicate to get guard cell data - Mesh::communicate(var); + if (communicate) { + Mesh::communicate(var); + } // Check that the data is valid checkData(var); @@ -217,43 +220,43 @@ int Mesh::get(FieldPerp &var, const std::string &name, BoutReal def, * Data get routines **************************************************************************/ -int Mesh::get(Vector2D &var, const std::string &name, BoutReal def) { +int Mesh::get(Vector2D& var, const std::string& name, BoutReal def, bool communicate) { TRACE("Loading 2D vector: Mesh::get(Vector2D, {:s})", name); if(var.covariant) { output << _("\tReading covariant vector ") << name << endl; - get(var.x, name+"_x", def); - get(var.y, name+"_y", def); - get(var.z, name+"_z", def); + get(var.x, name + "_x", def, communicate); + get(var.y, name + "_y", def, communicate); + get(var.z, name + "_z", def, communicate); }else { output << _("\tReading contravariant vector ") << name << endl; - get(var.x, name+"x", def); - get(var.y, name+"y", def); - get(var.z, name+"z", def); + get(var.x, name + "x", def, communicate); + get(var.y, name + "y", def, communicate); + get(var.z, name + "z", def, communicate); } return 0; } -int Mesh::get(Vector3D &var, const std::string &name, BoutReal def) { +int Mesh::get(Vector3D& var, const std::string& name, BoutReal def, bool communicate) { TRACE("Loading 3D vector: Mesh::get(Vector3D, {:s})", name); if(var.covariant) { output << _("\tReading covariant vector ") << name << endl; - get(var.x, name+"_x", def); - get(var.y, name+"_y", def); - get(var.z, name+"_z", def); + get(var.x, name + "_x", def, communicate); + get(var.y, name + "_y", def, communicate); + get(var.z, name + "_z", def, communicate); }else { output << ("\tReading contravariant vector ") << name << endl; - get(var.x, name+"x", def); - get(var.y, name+"y", def); - get(var.z, name+"z", def); + get(var.x, name + "x", def, communicate); + get(var.y, name + "y", def, communicate); + get(var.z, name + "z", def, communicate); } return 0; @@ -622,6 +625,7 @@ void Mesh::recalculateStaggeredCoordinates() { } *coords_map[location] = std::move(*createDefaultCoordinates(location, true)); + coords_map[location]->geometry(false, true); } } diff --git a/src/mesh/parallel/fci.cxx b/src/mesh/parallel/fci.cxx index 8a1e74b00f..4b90496227 100644 --- a/src/mesh/parallel/fci.cxx +++ b/src/mesh/parallel/fci.cxx @@ -47,8 +47,9 @@ #include -FCIMap::FCIMap(Mesh& mesh, const Field2D& dy, Options& options, int offset_, - BoundaryRegionPar* boundary, bool zperiodic) +FCIMap::FCIMap(Mesh& mesh, const Coordinates::FieldMetric& dy, Options& options, + int offset_, BoundaryRegionPar* inner_boundary, + BoundaryRegionPar* outer_boundary, bool zperiodic) : map_mesh(mesh), offset(offset_), boundary_mask(map_mesh), corner_boundary_mask(map_mesh) { @@ -115,7 +116,7 @@ FCIMap::FCIMap(Mesh& mesh, const Field2D& dy, Options& options, int offset_, // Cell corners Field3D xt_prime_corner{emptyFrom(xt_prime)}; - Field3D zt_prime_corner{emptyFrom(xt_prime)}; + Field3D zt_prime_corner{emptyFrom(zt_prime)}; BOUT_FOR(i, xt_prime_corner.getRegion("RGN_NOBNDRY")) { // Point interpolated from (x+1/2, z+1/2) @@ -230,6 +231,10 @@ FCIMap::FCIMap(Mesh& mesh, const Field2D& dy, Options& options, int offset_, // Invert 2x2 matrix to get change in index const BoutReal dx = (dZ_dz * dR - dR_dz * dZ) / det; const BoutReal dz = (dR_dx * dZ - dZ_dx * dR) / det; + + // Negative xt_prime means we've hit the inner boundary, otherwise + // the outer boundary + auto* boundary = (xt_prime[i] < 0.0) ? inner_boundary : outer_boundary; boundary->add_point(x, y, z, x + dx, y + 0.5 * offset, z + dz, // Intersection point in local index space 0.5 * dy[i], // Distance to intersection diff --git a/src/mesh/parallel/fci.hxx b/src/mesh/parallel/fci.hxx index 3560d7fec9..3ecd964bfa 100644 --- a/src/mesh/parallel/fci.hxx +++ b/src/mesh/parallel/fci.hxx @@ -44,8 +44,9 @@ class FCIMap { public: FCIMap() = delete; - FCIMap(Mesh& mesh, const Field2D& dy, Options& options, int offset, - BoundaryRegionPar* boundary, bool zperiodic); + FCIMap(Mesh& mesh, const Coordinates::FieldMetric& dy, Options& options, int offset, + BoundaryRegionPar* inner_boundary, BoundaryRegionPar* outer_boundary, + bool zperiodic); // The mesh this map was created on Mesh& map_mesh; @@ -71,42 +72,57 @@ public: class FCITransform : public ParallelTransform { public: FCITransform() = delete; - FCITransform(Mesh& mesh, const Field2D& dy, bool zperiodic = true, Options* opt = nullptr) + FCITransform(Mesh& mesh, const Coordinates::FieldMetric& dy, bool zperiodic = true, + Options* opt = nullptr) : ParallelTransform(mesh, opt) { // check the coordinate system used for the grid data source FCITransform::checkInputGrid(); - auto forward_boundary = new BoundaryRegionPar("FCI_forward", BNDRY_PAR_FWD, +1, &mesh); - auto backward_boundary = new BoundaryRegionPar("FCI_backward", BNDRY_PAR_BKWD, -1, &mesh); + auto forward_boundary_xin = + new BoundaryRegionPar("FCI_forward", BNDRY_PAR_FWD_XIN, +1, &mesh); + auto backward_boundary_xin = + new BoundaryRegionPar("FCI_backward", BNDRY_PAR_BKWD_XIN, -1, &mesh); + auto forward_boundary_xout = + new BoundaryRegionPar("FCI_forward", BNDRY_PAR_FWD_XOUT, +1, &mesh); + auto backward_boundary_xout = + new BoundaryRegionPar("FCI_backward", BNDRY_PAR_BKWD_XOUT, -1, &mesh); // Add the boundary region to the mesh's vector of parallel boundaries - mesh.addBoundaryPar(forward_boundary); - mesh.addBoundaryPar(backward_boundary); + mesh.addBoundaryPar(forward_boundary_xin); + mesh.addBoundaryPar(backward_boundary_xin); + mesh.addBoundaryPar(forward_boundary_xout); + mesh.addBoundaryPar(backward_boundary_xout); field_line_maps.reserve(mesh.ystart * 2); for (int offset = 1; offset < mesh.ystart + 1; ++offset) { - field_line_maps.emplace_back(mesh, dy, options, offset, forward_boundary, zperiodic); - field_line_maps.emplace_back(mesh, dy, options, -offset, backward_boundary, zperiodic); + field_line_maps.emplace_back(mesh, dy, options, offset, forward_boundary_xin, + forward_boundary_xout, zperiodic); + field_line_maps.emplace_back(mesh, dy, options, -offset, backward_boundary_xin, + backward_boundary_xout, zperiodic); } } void calcParallelSlices(Field3D &f) override; void integrateParallelSlices(Field3D &f) override; - - const Field3D toFieldAligned(const Field3D &UNUSED(f), const std::string& UNUSED(region) = "RGN_ALL") override { + + Field3D toFieldAligned(const Field3D& UNUSED(f), + const std::string& UNUSED(region) = "RGN_ALL") override { throw BoutException("FCI method cannot transform into field aligned grid"); } - const FieldPerp toFieldAligned(const FieldPerp &UNUSED(f), const std::string& UNUSED(region) = "RGN_ALL") override { + FieldPerp toFieldAligned(const FieldPerp& UNUSED(f), + const std::string& UNUSED(region) = "RGN_ALL") override { throw BoutException("FCI method cannot transform into field aligned grid"); } - const Field3D fromFieldAligned(const Field3D &UNUSED(f), const std::string& UNUSED(region) = "RGN_ALL") override { - throw BoutException("FCI method cannot transform into field aligned grid"); + Field3D fromFieldAligned(const Field3D& UNUSED(f), + const std::string& UNUSED(region) = "RGN_ALL") override { + throw BoutException("FCI method cannot transform from field aligned grid"); } - const FieldPerp fromFieldAligned(const FieldPerp &UNUSED(f), const std::string& UNUSED(region) = "RGN_ALL") override { - throw BoutException("FCI method cannot transform into field aligned grid"); + FieldPerp fromFieldAligned(const FieldPerp& UNUSED(f), + const std::string& UNUSED(region) = "RGN_ALL") override { + throw BoutException("FCI method cannot transform from field aligned grid"); } bool canToFromFieldAligned() override { return false; } diff --git a/src/mesh/parallel/shiftedmetric.cxx b/src/mesh/parallel/shiftedmetric.cxx index a204364d28..14b0af9d4b 100644 --- a/src/mesh/parallel/shiftedmetric.cxx +++ b/src/mesh/parallel/shiftedmetric.cxx @@ -121,12 +121,11 @@ void ShiftedMetric::cachePhases() { * Shift the field so that X-Z is not orthogonal, * and Y is then field aligned. */ -const Field3D ShiftedMetric::toFieldAligned(const Field3D& f, const std::string& region) { +Field3D ShiftedMetric::toFieldAligned(const Field3D& f, const std::string& region) { ASSERT2(f.getDirectionY() == YDirectionType::Standard); return shiftZ(f, toAlignedPhs, YDirectionType::Aligned, region); } -const FieldPerp ShiftedMetric::toFieldAligned(const FieldPerp& f, - const std::string& region) { +FieldPerp ShiftedMetric::toFieldAligned(const FieldPerp& f, const std::string& region) { ASSERT2(f.getDirectionY() == YDirectionType::Standard); // In principle, other regions are possible, but not yet implemented ASSERT2(region == "RGN_NOX"); @@ -137,22 +136,20 @@ const FieldPerp ShiftedMetric::toFieldAligned(const FieldPerp& f, * Shift back, so that X-Z is orthogonal, * but Y is not field aligned. */ -const Field3D ShiftedMetric::fromFieldAligned(const Field3D& f, - const std::string& region) { +Field3D ShiftedMetric::fromFieldAligned(const Field3D& f, const std::string& region) { ASSERT2(f.getDirectionY() == YDirectionType::Aligned); return shiftZ(f, fromAlignedPhs, YDirectionType::Standard, region); } -const FieldPerp ShiftedMetric::fromFieldAligned(const FieldPerp& f, - const std::string& region) { +FieldPerp ShiftedMetric::fromFieldAligned(const FieldPerp& f, const std::string& region) { ASSERT2(f.getDirectionY() == YDirectionType::Aligned); // In principle, other regions are possible, but not yet implemented ASSERT2(region == "RGN_NOX"); return shiftZ(f, fromAlignedPhs, YDirectionType::Standard, region); } -const Field3D ShiftedMetric::shiftZ(const Field3D& f, const Tensor& phs, - const YDirectionType y_direction_out, - const std::string& region) const { +Field3D ShiftedMetric::shiftZ(const Field3D& f, const Tensor& phs, + const YDirectionType y_direction_out, + const std::string& region) const { ASSERT1(f.getMesh() == &mesh); ASSERT1(f.getLocation() == location); @@ -171,9 +168,9 @@ const Field3D ShiftedMetric::shiftZ(const Field3D& f, const Tensor& ph return result; } -const FieldPerp ShiftedMetric::shiftZ(const FieldPerp& f, const Tensor& phs, - const YDirectionType y_direction_out, - const std::string& UNUSED(region)) const { +FieldPerp ShiftedMetric::shiftZ(const FieldPerp& f, const Tensor& phs, + const YDirectionType y_direction_out, + const std::string& UNUSED(region)) const { ASSERT1(f.getMesh() == &mesh); ASSERT1(f.getLocation() == location); @@ -284,8 +281,8 @@ ShiftedMetric::shiftZ(const Field3D& f, } // Old approach retained so we can still specify a general zShift -const Field3D ShiftedMetric::shiftZ(const Field3D& f, const Field2D& zangle, - const std::string& region) const { +Field3D ShiftedMetric::shiftZ(const Field3D& f, const Field2D& zangle, + const std::string& region) const { ASSERT1(&mesh == f.getMesh()); ASSERT1(f.getLocation() == zangle.getLocation()); if (mesh.LocalNz == 1) diff --git a/src/mesh/parallel/shiftedmetricinterp.cxx b/src/mesh/parallel/shiftedmetricinterp.cxx index f40bfe0b53..62140bc388 100644 --- a/src/mesh/parallel/shiftedmetricinterp.cxx +++ b/src/mesh/parallel/shiftedmetricinterp.cxx @@ -116,11 +116,11 @@ ShiftedMetricInterp::ShiftedMetricInterp(Mesh& mesh, CELL_LOC location_in, // Create regions for parallel boundary conditions Field2D dy; mesh.get(dy, "dy", 1.); - auto forward_boundary = - new BoundaryRegionPar("parallel_forward", BNDRY_PAR_FWD, +1, &mesh); + auto forward_boundary_xin = + new BoundaryRegionPar("parallel_forward_xin", BNDRY_PAR_FWD_XIN, +1, &mesh); for (auto it = mesh.iterateBndryUpperY(); not it.isDone(); it.next()) { for (int z = mesh.zstart; z <= mesh.zend; z++) { - forward_boundary->add_point( + forward_boundary_xin->add_point( it.ind, mesh.yend, z, mesh.GlobalX(it.ind), // x 2. * PI * mesh.GlobalY(mesh.yend + 0.5), // y @@ -133,11 +133,46 @@ ShiftedMetricInterp::ShiftedMetricInterp(Mesh& mesh, CELL_LOC location_in, ); } } - auto backward_boundary = - new BoundaryRegionPar("parallel_backward", BNDRY_PAR_BKWD, -1, &mesh); + auto backward_boundary_xin = + new BoundaryRegionPar("parallel_backward_xin", BNDRY_PAR_BKWD_XIN, -1, &mesh); for (auto it = mesh.iterateBndryLowerY(); not it.isDone(); it.next()) { for (int z = mesh.zstart; z <= mesh.zend; z++) { - backward_boundary->add_point( + backward_boundary_xin->add_point( + it.ind, mesh.ystart, z, + mesh.GlobalX(it.ind), // x + 2. * PI * mesh.GlobalY(mesh.ystart - 0.5), // y + 2. * PI * BoutReal(z) / BoutReal(mesh.GlobalNz) // z + + 0.5 * (zShift(it.ind, mesh.ystart) - zShift(it.ind, mesh.ystart - 1)), + 0.25 + * (dy(it.ind, mesh.ystart - 1) // dy/2 + + dy(it.ind, mesh.ystart)), + 0. // angle? + ); + } + } + // Create regions for parallel boundary conditions + auto forward_boundary_xout = + new BoundaryRegionPar("parallel_forward_xout", BNDRY_PAR_FWD_XOUT, +1, &mesh); + for (auto it = mesh.iterateBndryUpperY(); not it.isDone(); it.next()) { + for (int z = mesh.zstart; z <= mesh.zend; z++) { + forward_boundary_xout->add_point( + it.ind, mesh.yend, z, + mesh.GlobalX(it.ind), // x + 2. * PI * mesh.GlobalY(mesh.yend + 0.5), // y + 2. * PI * BoutReal(z) / BoutReal(mesh.GlobalNz) // z + + 0.5 * (zShift(it.ind, mesh.yend + 1) - zShift(it.ind, mesh.yend)), + 0.25 + * (dy(it.ind, mesh.yend) // dy/2 + + dy(it.ind, mesh.yend + 1)), + 0. // angle? + ); + } + } + auto backward_boundary_xout = + new BoundaryRegionPar("parallel_backward_xout", BNDRY_PAR_BKWD_XOUT, -1, &mesh); + for (auto it = mesh.iterateBndryLowerY(); not it.isDone(); it.next()) { + for (int z = mesh.zstart; z <= mesh.zend; z++) { + backward_boundary_xout->add_point( it.ind, mesh.ystart, z, mesh.GlobalX(it.ind), // x 2. * PI * mesh.GlobalY(mesh.ystart - 0.5), // y @@ -152,8 +187,10 @@ ShiftedMetricInterp::ShiftedMetricInterp(Mesh& mesh, CELL_LOC location_in, } // Add the boundary region to the mesh's vector of parallel boundaries - mesh.addBoundaryPar(forward_boundary); - mesh.addBoundaryPar(backward_boundary); + mesh.addBoundaryPar(forward_boundary_xin); + mesh.addBoundaryPar(backward_boundary_xin); + mesh.addBoundaryPar(forward_boundary_xout); + mesh.addBoundaryPar(backward_boundary_xout); } void ShiftedMetricInterp::checkInputGrid() { @@ -187,8 +224,7 @@ void ShiftedMetricInterp::calcParallelSlices(Field3D& f) { * Shift the field so that X-Z is not orthogonal, * and Y is then field aligned. */ -const Field3D ShiftedMetricInterp::toFieldAligned(const Field3D& f, - const std::string& region) { +Field3D ShiftedMetricInterp::toFieldAligned(const Field3D& f, const std::string& region) { ASSERT2(f.getDirectionY() == YDirectionType::Standard); return interp_to_aligned->interpolate(f, region).setDirectionY(YDirectionType::Aligned); } @@ -197,8 +233,8 @@ const Field3D ShiftedMetricInterp::toFieldAligned(const Field3D& f, * Shift back, so that X-Z is orthogonal, * but Y is not field aligned. */ -const Field3D ShiftedMetricInterp::fromFieldAligned(const Field3D& f, - const std::string& region) { +Field3D ShiftedMetricInterp::fromFieldAligned(const Field3D& f, + const std::string& region) { ASSERT2(f.getDirectionY() == YDirectionType::Aligned); return interp_from_aligned->interpolate(f, region).setDirectionY( YDirectionType::Standard); diff --git a/src/mesh/parallel/shiftedmetricinterp.hxx b/src/mesh/parallel/shiftedmetricinterp.hxx index 92ae7b92f7..ec87f7fd9d 100644 --- a/src/mesh/parallel/shiftedmetricinterp.hxx +++ b/src/mesh/parallel/shiftedmetricinterp.hxx @@ -57,10 +57,10 @@ public: * Note that the returned field will no longer be orthogonal in X-Z, and the * metric tensor will need to be changed if X derivatives are used. */ - const Field3D toFieldAligned(const Field3D& f, - const std::string& region = "RGN_ALL") override; - const FieldPerp toFieldAligned(const FieldPerp& UNUSED(f), - const std::string& UNUSED(region) = "RGN_ALL") override { + Field3D toFieldAligned(const Field3D& f, + const std::string& region = "RGN_ALL") override; + FieldPerp toFieldAligned(const FieldPerp& UNUSED(f), + const std::string& UNUSED(region) = "RGN_ALL") override { throw BoutException("Not implemented yet"); } @@ -68,11 +68,10 @@ public: * Converts a field back to X-Z orthogonal coordinates * from field aligned coordinates. */ - const Field3D fromFieldAligned(const Field3D& f, - const std::string& region = "RGN_ALL") override; - const FieldPerp - fromFieldAligned(const FieldPerp& UNUSED(f), - const std::string& UNUSED(region) = "RGN_ALL") override { + Field3D fromFieldAligned(const Field3D& f, + const std::string& region = "RGN_ALL") override; + FieldPerp fromFieldAligned(const FieldPerp& UNUSED(f), + const std::string& UNUSED(region) = "RGN_ALL") override { throw BoutException("Not implemented yet"); } diff --git a/src/mesh/parallel_boundary_op.cxx b/src/mesh/parallel_boundary_op.cxx index f385d10207..4e46687369 100644 --- a/src/mesh/parallel_boundary_op.cxx +++ b/src/mesh/parallel_boundary_op.cxx @@ -74,7 +74,6 @@ BoundaryOpPar* BoundaryOpPar_dirichlet::clone(BoundaryRegionPar *region, Field3D } void BoundaryOpPar_dirichlet::apply(Field3D &f, BoutReal t) { - Field3D& f_next = f.ynext(bndry->dir); Coordinates& coord = *(f.getCoordinates()); @@ -90,7 +89,7 @@ void BoundaryOpPar_dirichlet::apply(Field3D &f, BoutReal t) { // Scale the field and normalise to the desired value BoutReal y_prime = bndry->length; - BoutReal f2 = (f(x,y,z) - value) * (coord.dy(x, y) - y_prime) / y_prime; + BoutReal f2 = (f(x, y, z) - value) * (coord.dy(x, y, z) - y_prime) / y_prime; f_next(x, y+bndry->dir, z) = value - f2; } @@ -135,9 +134,9 @@ void BoundaryOpPar_dirichlet_O3::apply(Field3D &f, BoutReal t) { BoutReal fb = getValue(*bndry, t); BoutReal f1 = f_prev(x, y-bndry->dir, z); BoutReal f2 = f(x,y,z); - BoutReal l1 = coord.dy(x, y); + BoutReal l1 = coord.dy(x, y, z); BoutReal l2 = bndry->length; - BoutReal l3 = coord.dy(x, y) - l2; + BoutReal l3 = coord.dy(x, y, z) - l2; BoutReal denom = (l1*l1*l2 + l1*l2*l2); BoutReal term1 = (l2*l2*l3 + l2*l3*l3); @@ -187,7 +186,7 @@ void BoundaryOpPar_dirichlet_interp::apply(Field3D &f, BoutReal t) { BoutReal fs = getValue(*bndry, t); // Scale the field and normalise to the desired value - BoutReal dy = coord.dy(x, y); + BoutReal dy = coord.dy(x, y, z); BoutReal s = bndry->length*dy; f_next(x, y+bndry->dir, z) = f_prev(x, y-bndry->dir, z)*(1.-(2.*s/(dy+s))) @@ -235,7 +234,7 @@ void BoundaryOpPar_neumann::apply(Field3D &f, BoutReal t) { // Generate the boundary value BoutReal value = getValue(x, y, z, t); - BoutReal dy = coord.dy(x, y); + BoutReal dy = coord.dy(x, y, z); f_next(x, y+bndry->dir, z) = f(x, y, z) + bndry->dir*value*dy; } diff --git a/src/physics/smoothing.cxx b/src/physics/smoothing.cxx index 5cfc935283..465cedcce6 100644 --- a/src/physics/smoothing.cxx +++ b/src/physics/smoothing.cxx @@ -32,14 +32,17 @@ #include +#include "bout/build_config.hxx" + #include #include #include #include #include -#include #include +#include +#include // Smooth using simple 1-2-1 filter const Field3D smooth_x(const Field3D &f) { @@ -320,11 +323,15 @@ BoutReal Average_XY(const Field2D &var) { } BoutReal Vol_Integral(const Field2D &var) { +#if BOUT_USE_METRIC_3D + AUTO_TRACE(); + throw BoutException("Vol_Intregral currently incompatible with 3D metrics"); +#else Mesh *mesh = var.getMesh(); BoutReal Int_Glb; Coordinates *metric = var.getCoordinates(); - Field2D result = metric->J * var * metric->dx * metric->dy; + auto result = metric->J * var * metric->dx * metric->dy; Int_Glb = Average_XY(result); Int_Glb *= static_cast( @@ -332,6 +339,7 @@ BoutReal Vol_Integral(const Field2D &var) { * (mesh->GlobalNy-mesh->numberOfYBoundaries()*2*mesh->ystart)) * PI * 2.; return Int_Glb; +#endif } const Field3D smoothXY(const Field3D &f) { diff --git a/src/solver/solver.cxx b/src/solver/solver.cxx index ef30274e88..0fee2ca699 100644 --- a/src/solver/solver.cxx +++ b/src/solver/solver.cxx @@ -794,14 +794,14 @@ int Solver::call_monitors(BoutReal simtime, int iter, int NOUT) { int ret = monitor->call(this, simtime, iter / monitor->period - 1, NOUT / monitor->period); if (ret) - throw BoutException(_("Monitor signalled to quit")); + throw BoutException(_("Monitor signalled to quit (return code {})"), ret); } } - } catch (const BoutException&) { + } catch (const BoutException& e) { for (const auto& it : monitors) { it->cleanup(); } - output_error.write(_("Monitor signalled to quit\n")); + output_error.write(_("Monitor signalled to quit (exception {})\n"), e.what()); throw; } diff --git a/src/sys/derivs.cxx b/src/sys/derivs.cxx index c0524ec41c..351c5d4c30 100644 --- a/src/sys/derivs.cxx +++ b/src/sys/derivs.cxx @@ -58,25 +58,13 @@ ////////////// X DERIVATIVE ///////////////// -Field3D DDX(const Field3D &f, CELL_LOC outloc, const std::string &method, - const std::string& region) { - Field3D result = bout::derivatives::index::DDX(f, outloc, method, region); - Coordinates *coords = f.getCoordinates(outloc); - result /= coords->dx; - - if(f.getMesh()->IncIntShear) { - // Using BOUT-06 style shifting - result += coords->IntShiftTorsion * DDZ(f, outloc, method, region); - } - - ASSERT2(((outloc == CELL_DEFAULT) && (result.getLocation() == f.getLocation())) || - (result.getLocation() == outloc)); - - return result; +Field3D DDX(const Field3D& f, CELL_LOC outloc, const std::string& method, + const std::string& region) { + return f.getCoordinates(outloc)->DDX(f, outloc, method, region); } -Field2D DDX(const Field2D &f, CELL_LOC outloc, const std::string &method, - const std::string& region) { +Coordinates::FieldMetric DDX(const Field2D& f, CELL_LOC outloc, const std::string& method, + const std::string& region) { return f.getCoordinates(outloc)->DDX(f, outloc, method, region); } @@ -88,8 +76,8 @@ Field3D DDY(const Field3D &f, CELL_LOC outloc, const std::string &method, / f.getCoordinates(outloc)->dy; } -Field2D DDY(const Field2D &f, CELL_LOC outloc, const std::string &method, - const std::string& region) { +Coordinates::FieldMetric DDY(const Field2D& f, CELL_LOC outloc, const std::string& method, + const std::string& region) { return f.getCoordinates(outloc)->DDY(f, outloc, method, region); } @@ -101,8 +89,9 @@ Field3D DDZ(const Field3D &f, CELL_LOC outloc, const std::string &method, / f.getCoordinates(outloc)->dz; } -Field2D DDZ(const Field2D &f, CELL_LOC UNUSED(outloc), const std::string - &UNUSED(method), const std::string& UNUSED(region)) { +Coordinates::FieldMetric DDZ(const Field2D& f, CELL_LOC UNUSED(outloc), + const std::string& UNUSED(method), + const std::string& UNUSED(region)) { auto tmp = Field2D(0., f.getMesh()); tmp.setLocation(f.getLocation()); return tmp; @@ -175,11 +164,11 @@ Field3D D2DX2(const Field3D &f, CELL_LOC outloc, const std::string &method, return result; } -Field2D D2DX2(const Field2D &f, CELL_LOC outloc, const std::string &method, - const std::string& region) { +Coordinates::FieldMetric D2DX2(const Field2D& f, CELL_LOC outloc, + const std::string& method, const std::string& region) { Coordinates *coords = f.getCoordinates(outloc); - Field2D result = + auto result = bout::derivatives::index::D2DX2(f, outloc, method, region) / SQ(coords->dx); if(coords->non_uniform) { @@ -212,11 +201,11 @@ Field3D D2DY2(const Field3D &f, CELL_LOC outloc, const std::string &method, return result; } -Field2D D2DY2(const Field2D &f, CELL_LOC outloc, const std::string &method, - const std::string& region) { +Coordinates::FieldMetric D2DY2(const Field2D& f, CELL_LOC outloc, + const std::string& method, const std::string& region) { Coordinates *coords = f.getCoordinates(outloc); - Field2D result = + auto result = bout::derivatives::index::D2DY2(f, outloc, method, region) / SQ(coords->dy); if(coords->non_uniform) { // Correction for non-uniform f.getMesh() @@ -235,12 +224,10 @@ Field3D D2DZ2(const Field3D &f, CELL_LOC outloc, const std::string &method, / SQ(f.getCoordinates(outloc)->dz); } -Field2D D2DZ2(const Field2D &f, CELL_LOC outloc, const std::string &UNUSED(method), - const std::string& UNUSED(region)) { - if (outloc == CELL_DEFAULT) { - outloc = f.getLocation(); - } - return zeroFrom(f).setLocation(outloc); +Coordinates::FieldMetric D2DZ2(const Field2D& f, CELL_LOC outloc, + const std::string& method, const std::string& region) { + return bout::derivatives::index::D2DZ2(f, outloc, method, region) + / SQ(f.getCoordinates(outloc)->dz); } /******************************************************************************* @@ -253,8 +240,8 @@ Field3D D4DX4(const Field3D &f, CELL_LOC outloc, const std::string &method, / SQ(SQ(f.getCoordinates(outloc)->dx)); } -Field2D D4DX4(const Field2D &f, CELL_LOC outloc, const std::string &method, - const std::string& region) { +Coordinates::FieldMetric D4DX4(const Field2D& f, CELL_LOC outloc, + const std::string& method, const std::string& region) { return bout::derivatives::index::D4DX4(f, outloc, method, region) / SQ(SQ(f.getCoordinates(outloc)->dx)); } @@ -265,8 +252,8 @@ Field3D D4DY4(const Field3D &f, CELL_LOC outloc, const std::string &method, / SQ(SQ(f.getCoordinates(outloc)->dy)); } -Field2D D4DY4(const Field2D &f, CELL_LOC outloc, const std::string &method, - const std::string& region) { +Coordinates::FieldMetric D4DY4(const Field2D& f, CELL_LOC outloc, + const std::string& method, const std::string& region) { return bout::derivatives::index::D4DY4(f, outloc, method, region) / SQ(SQ(f.getCoordinates(outloc)->dy)); } @@ -277,8 +264,8 @@ Field3D D4DZ4(const Field3D &f, CELL_LOC outloc, const std::string &method, / SQ(SQ(f.getCoordinates(outloc)->dz)); } -Field2D D4DZ4(const Field2D &f, CELL_LOC outloc, const std::string &method, - const std::string& region) { +Coordinates::FieldMetric D4DZ4(const Field2D& f, CELL_LOC outloc, + const std::string& method, const std::string& region) { return bout::derivatives::index::D4DZ4(f, outloc, method, region) / SQ(SQ(f.getCoordinates(outloc)->dz)); } @@ -294,16 +281,17 @@ Field2D D4DZ4(const Field2D &f, CELL_LOC outloc, const std::string &method, * * ** Communicates and applies boundary in X. */ -Field2D D2DXDY(const Field2D& f, CELL_LOC outloc, const std::string& method, - const std::string& region, const std::string& dfdy_boundary_condition, - const std::string& dfdy_region) { +Coordinates::FieldMetric D2DXDY(const Field2D& f, CELL_LOC outloc, + const std::string& method, const std::string& region, + const std::string& dfdy_boundary_condition, + const std::string& dfdy_region) { std::string dy_region = dfdy_region.empty() ? region : dfdy_region; // If staggering in x, take y-derivative at f's location. const auto y_location = (outloc == CELL_XLOW or f.getLocation() == CELL_XLOW) ? CELL_DEFAULT : outloc; - Field2D dfdy = DDY(f, y_location, method, dy_region); + auto dfdy = DDY(f, y_location, method, dy_region); // Set x-guard cells and x-boundary cells before calculating DDX f.getMesh()->communicate(dfdy); @@ -337,12 +325,18 @@ Field3D D2DXDY(const Field3D& f, CELL_LOC outloc, const std::string& method, return DDX(dfdy, outloc, method, region); } -Field2D D2DXDZ(const Field2D &f, CELL_LOC outloc, const std::string &UNUSED(method), - const std::string& UNUSED(region)) { +Coordinates::FieldMetric D2DXDZ(const Field2D& f, CELL_LOC outloc, + MAYBE_UNUSED(const std::string& method), + MAYBE_UNUSED(const std::string& region)) { +#if BOUT_USE_METRIC_3D + Field3D tmp{f}; + return D2DXDZ(tmp, outloc, method, region); +#else if (outloc == CELL_DEFAULT) { outloc = f.getLocation(); } return zeroFrom(f).setLocation(outloc); +#endif } /// X-Z mixed derivative @@ -356,12 +350,18 @@ Field3D D2DXDZ(const Field3D &f, CELL_LOC outloc, const std::string &method, return DDZ(DDX(f, x_location, method, region), outloc, method, region); } -Field2D D2DYDZ(const Field2D &f, CELL_LOC outloc, const std::string &UNUSED(method), - const std::string& UNUSED(region)) { +Coordinates::FieldMetric D2DYDZ(const Field2D& f, CELL_LOC outloc, + MAYBE_UNUSED(const std::string& method), + MAYBE_UNUSED(const std::string& region)) { +#if BOUT_USE_METRIC_3D + Field3D tmp{f}; + return D2DYDZ(tmp, outloc, method, region); +#else if (outloc == CELL_DEFAULT) { outloc = f.getLocation(); } return zeroFrom(f).setLocation(outloc); +#endif } Field3D D2DYDZ(const Field3D& f, CELL_LOC outloc, MAYBE_UNUSED(const std::string& @@ -381,9 +381,9 @@ Field3D D2DYDZ(const Field3D& f, CELL_LOC outloc, MAYBE_UNUSED(const std::string ////////////// X DERIVATIVE ///////////////// -/// Special case where both arguments are 2D. Output location ignored for now -Field2D VDDX(const Field2D &v, const Field2D &f, CELL_LOC outloc, const std::string &method, - const std::string& region) { +/// Special case where both arguments are 2D. +Coordinates::FieldMetric VDDX(const Field2D& v, const Field2D& f, CELL_LOC outloc, + const std::string& method, const std::string& region) { return bout::derivatives::index::VDDX(v, f, outloc, method, region) / f.getCoordinates(outloc)->dx; } @@ -398,8 +398,8 @@ Field3D VDDX(const Field3D &v, const Field3D &f, CELL_LOC outloc, const std::str ////////////// Y DERIVATIVE ///////////////// // special case where both are 2D -Field2D VDDY(const Field2D &v, const Field2D &f, CELL_LOC outloc, const std::string &method, - const std::string& region) { +Coordinates::FieldMetric VDDY(const Field2D& v, const Field2D& f, CELL_LOC outloc, + const std::string& method, const std::string& region) { return bout::derivatives::index::VDDY(v, f, outloc, method, region) / f.getCoordinates(outloc)->dy; } @@ -414,21 +414,26 @@ Field3D VDDY(const Field3D &v, const Field3D &f, CELL_LOC outloc, const std::str ////////////// Z DERIVATIVE ///////////////// // special case where both are 2D -Field2D VDDZ(const Field2D &UNUSED(v), const Field2D &f, CELL_LOC outloc, const - std::string &UNUSED(method), const std::string& UNUSED(region)) { - if (outloc == CELL_DEFAULT) { - outloc = f.getLocation(); - } - return zeroFrom(f).setLocation(outloc); +Coordinates::FieldMetric VDDZ(const Field2D& v, const Field2D& f, CELL_LOC outloc, + const std::string& method, const std::string& region) { + return bout::derivatives::index::VDDZ(v, f, outloc, method, region) + / f.getCoordinates(outloc)->dz; } // Note that this is zero because no compression is included -Field2D VDDZ(const Field3D &UNUSED(v), const Field2D &f, CELL_LOC outloc, const - std::string &UNUSED(method), const std::string& UNUSED(region)) { +Coordinates::FieldMetric VDDZ(MAYBE_UNUSED(const Field3D& v), const Field2D& f, + CELL_LOC outloc, MAYBE_UNUSED(const std::string& method), + MAYBE_UNUSED(const std::string& region)) { +#if BOUT_USE_METRIC_3D + Field3D tmp{f}; + return bout::derivatives::index::VDDZ(v, tmp, outloc, method, region) + / f.getCoordinates(outloc)->dz; +#else if (outloc == CELL_DEFAULT) { outloc = f.getLocation(); } return zeroFrom(f).setLocation(outloc); +#endif } // general case @@ -441,8 +446,8 @@ Field3D VDDZ(const Field3D &v, const Field3D &f, CELL_LOC outloc, const std::str /******************************************************************************* * Flux conserving schemes *******************************************************************************/ -Field2D FDDX(const Field2D &v, const Field2D &f, CELL_LOC outloc, const std::string &method, - const std::string& region) { +Coordinates::FieldMetric FDDX(const Field2D& v, const Field2D& f, CELL_LOC outloc, + const std::string& method, const std::string& region) { return bout::derivatives::index::FDDX(v, f, outloc, method, region) / f.getCoordinates(outloc)->dx; } @@ -455,8 +460,8 @@ Field3D FDDX(const Field3D &v, const Field3D &f, CELL_LOC outloc, const std::str ///////////////////////////////////////////////////////////////////////// -Field2D FDDY(const Field2D &v, const Field2D &f, CELL_LOC outloc, const std::string &method, - const std::string& region) { +Coordinates::FieldMetric FDDY(const Field2D& v, const Field2D& f, CELL_LOC outloc, + const std::string& method, const std::string& region) { return bout::derivatives::index::FDDY(v, f, outloc, method, region) / f.getCoordinates(outloc)->dy; } @@ -469,12 +474,10 @@ Field3D FDDY(const Field3D &v, const Field3D &f, CELL_LOC outloc, const std::str ///////////////////////////////////////////////////////////////////////// -Field2D FDDZ(const Field2D &UNUSED(v), const Field2D &f, CELL_LOC outloc, const - std::string &UNUSED(method), const std::string& UNUSED(region)) { - if (outloc == CELL_DEFAULT) { - outloc = f.getLocation(); - } - return zeroFrom(f).setLocation(outloc); +Coordinates::FieldMetric FDDZ(const Field2D& v, const Field2D& f, CELL_LOC outloc, + const std::string& method, const std::string& region) { + return bout::derivatives::index::FDDZ(v, f, outloc, method, region) + / f.getCoordinates(outloc)->dz; } Field3D FDDZ(const Field3D &v, const Field3D &f, CELL_LOC outloc, const std::string &method, diff --git a/tests/MMS/diffusion2/diffusion.cxx b/tests/MMS/diffusion2/diffusion.cxx index 017e45dfa6..e568c952c5 100644 --- a/tests/MMS/diffusion2/diffusion.cxx +++ b/tests/MMS/diffusion2/diffusion.cxx @@ -26,7 +26,7 @@ class Diffusion : public PhysicsModel { coords->dy = Ly / (mesh->GlobalNy - 2 * mesh->ystart); output.write("SIZES: {:d}, {:d}, {:e}\n", mesh->GlobalNy, - (mesh->GlobalNy - 2 * mesh->ystart), coords->dy(0, 0)); + (mesh->GlobalNy - 2 * mesh->ystart), coords->dy(0, 0, 0)); SAVE_ONCE2(Lx, Ly); diff --git a/tests/MMS/hw/runtest b/tests/MMS/hw/runtest index 75b819c8c9..03ea7d585e 100755 --- a/tests/MMS/hw/runtest +++ b/tests/MMS/hw/runtest @@ -3,6 +3,7 @@ # Python script to run and analyse MMS test # requires: all_tests +# requires: not metric_3d # cores: 4 from __future__ import division diff --git a/tests/MMS/spatial/diffusion/diffusion.cxx b/tests/MMS/spatial/diffusion/diffusion.cxx index fd29188883..4c821c9df6 100644 --- a/tests/MMS/spatial/diffusion/diffusion.cxx +++ b/tests/MMS/spatial/diffusion/diffusion.cxx @@ -27,7 +27,7 @@ class Diffusion : public PhysicsModel { coords->dy = Ly / (mesh->GlobalNy - 2 * mesh->ystart); output.write("SIZES: {:d}, {:d}, {:e}\n", mesh->GlobalNy, - (mesh->GlobalNy - 2 * mesh->ystart), coords->dy(0, 0)); + (mesh->GlobalNy - 2 * mesh->ystart), coords->dy(0, 0, 0)); SAVE_ONCE2(Lx, Ly); diff --git a/tests/MMS/spatial/fci/runtest b/tests/MMS/spatial/fci/runtest index 1e66f7c272..1613155ed2 100755 --- a/tests/MMS/spatial/fci/runtest +++ b/tests/MMS/spatial/fci/runtest @@ -9,6 +9,7 @@ from __future__ import print_function from boututils.run_wrapper import build_and_log, launch_safe from boutdata.collect import collect +import boutconfig as conf from numpy import array, log, polyfit, linspace, arange @@ -72,7 +73,9 @@ for nslice in nslices: grid = zb.grid.Grid(poloidal_grid, ycoords, ylength, yperiodic=yperiodic) # Make and write maps maps = zb.make_maps(grid, field, nslice=nslice, quiet=True) - zb.write_maps(grid, field, maps, new_names=False, metric2d=True, quiet=True) + zb.write_maps( + grid, field, maps, new_names=False, metric2d=conf.isMetric2D(), quiet=True + ) args = " MZ={} MYG={} mesh:paralleltransform:y_periodic={} mesh:ddy:first={}".format( n, nslice, yperiodic, method_orders[nslice]["name"] diff --git a/tests/integrated/CMakeLists.txt b/tests/integrated/CMakeLists.txt index bac6fb0507..aa3f2b2c3e 100644 --- a/tests/integrated/CMakeLists.txt +++ b/tests/integrated/CMakeLists.txt @@ -23,6 +23,7 @@ add_subdirectory(test-laplace) add_subdirectory(test-laplace-petsc3d) add_subdirectory(test-laplacexy-fv) add_subdirectory(test-laplacexy-short) +add_subdirectory(test-laplacexz) add_subdirectory(test-multigrid_laplace) add_subdirectory(test-naulin-laplace) add_subdirectory(test-options-netcdf) diff --git a/tests/integrated/makefile b/tests/integrated/makefile index d383236259..29215a7185 100644 --- a/tests/integrated/makefile +++ b/tests/integrated/makefile @@ -24,11 +24,10 @@ all-all: $(BUILD_ALL) buildncheck: all check -$(BUILD): +$(BUILDALL): @$(MAKE) --no-print-directory -C $@ check: $(CHECKING_) - @echo $(CHECKING_) $(CHECKING_ALL_): @$(MAKE) --no-print-directory -C $(@:%_checking=%) runtest diff --git a/tests/integrated/test-backtrace/makefile b/tests/integrated/test-backtrace/makefile index bee3a2c5d5..27a1ecaf3a 100644 --- a/tests/integrated/test-backtrace/makefile +++ b/tests/integrated/test-backtrace/makefile @@ -1,6 +1,6 @@ BOUT_TOP ?= ../../.. -SOURCEC = boutexcept.cxx +SOURCEC = boutexcept.cxx include $(BOUT_TOP)/make.config diff --git a/tests/integrated/test-compile-examples/runtest b/tests/integrated/test-compile-examples/runtest index 9a77d6d04b..31650adeab 100755 --- a/tests/integrated/test-compile-examples/runtest +++ b/tests/integrated/test-compile-examples/runtest @@ -23,9 +23,10 @@ do if test $ex -gt 0 then - echo $(basename $dir) failed + dir=${dir#../../../examples/} + echo $dir failed error=1 - failed="$failed\n$(basename $dir) failed" + failed="$failed\n$dir failed" fi fi done diff --git a/tests/integrated/test-drift-instability-staggered/CMakeLists.txt b/tests/integrated/test-drift-instability-staggered/CMakeLists.txt index 185256d71b..66ba7c56fc 100644 --- a/tests/integrated/test-drift-instability-staggered/CMakeLists.txt +++ b/tests/integrated/test-drift-instability-staggered/CMakeLists.txt @@ -1,5 +1,6 @@ bout_add_integrated_test(test-drift-instability-staggered SOURCES 2fluid.cxx + CONFLICTS BOUT_USE_METRIC_3D USE_RUNTEST USE_DATA_BOUT_INP EXTRA_FILES uedge.grd_std.cdl diff --git a/tests/integrated/test-drift-instability/CMakeLists.txt b/tests/integrated/test-drift-instability/CMakeLists.txt index 90f5cce8b0..1f8b97af76 100644 --- a/tests/integrated/test-drift-instability/CMakeLists.txt +++ b/tests/integrated/test-drift-instability/CMakeLists.txt @@ -1,5 +1,6 @@ bout_add_integrated_test(test-drift-instability SOURCES 2fluid.cxx + CONFLICTS BOUT_USE_METRIC_3D USE_RUNTEST USE_DATA_BOUT_INP EXTRA_FILES uedge.grd_std.cdl diff --git a/tests/integrated/test-drift-instability/makefile b/tests/integrated/test-drift-instability/makefile index 23554affba..419aa3c503 100644 --- a/tests/integrated/test-drift-instability/makefile +++ b/tests/integrated/test-drift-instability/makefile @@ -1,4 +1,4 @@ -BOUT_TOP = ../../.. +BOUT_TOP ?= ../../.. SOURCEC = 2fluid.cxx diff --git a/tests/integrated/test-drift-instability/runtest b/tests/integrated/test-drift-instability/runtest index 004edec6bf..02eb2621e8 100755 --- a/tests/integrated/test-drift-instability/runtest +++ b/tests/integrated/test-drift-instability/runtest @@ -4,6 +4,8 @@ # Run the test, compare results against the benchmark # +# Requires: not metric_3d + from boututils.run_wrapper import build_and_log, shell, launch_safe from boututils.file_import import file_import from boututils.calculus import deriv diff --git a/tests/integrated/test-fieldgroupComm/CMakeLists.txt b/tests/integrated/test-fieldgroupComm/CMakeLists.txt index d95436b01a..9417829f21 100644 --- a/tests/integrated/test-fieldgroupComm/CMakeLists.txt +++ b/tests/integrated/test-fieldgroupComm/CMakeLists.txt @@ -1,5 +1,6 @@ bout_add_integrated_test(test-fieldgroupComm SOURCES test_fieldgroupcomm.cxx + CONFLICTS BOUT_USE_METRIC_3D USE_RUNTEST USE_DATA_BOUT_INP EXTRA_FILES cyclone_68x32.nc diff --git a/tests/integrated/test-fieldgroupComm/runtest b/tests/integrated/test-fieldgroupComm/runtest index 6013be9477..ca6d5f89f3 100755 --- a/tests/integrated/test-fieldgroupComm/runtest +++ b/tests/integrated/test-fieldgroupComm/runtest @@ -6,6 +6,7 @@ # Requires: all_tests # Requires: netcdf +# Requires: not metric_3d # Cores: 4 # Variables to compare diff --git a/tests/integrated/test-griddata/runtest b/tests/integrated/test-griddata/runtest index 8e03a6ed97..1c57bf5be7 100755 --- a/tests/integrated/test-griddata/runtest +++ b/tests/integrated/test-griddata/runtest @@ -32,11 +32,15 @@ for nproc in [1]: nx, ny = Rxy.shape + # Handle 3D metric case + if len(dx.shape) == 3: + dx = dx[:, :, 0] + rwidth = 0.4 dr = float(rwidth) / nx # Test value of dx - if np.max(np.abs(dx - dr * Bpxy * Rxy)) > 1e-7: + if not np.allclose(dx, dr * Bpxy * Rxy, atol=1e-7): print("Failed: dx does not match") exit(1) diff --git a/tests/integrated/test-griddata/screw/BOUT.inp b/tests/integrated/test-griddata/screw/BOUT.inp index 3b13eab395..4aa919d2a7 100644 --- a/tests/integrated/test-griddata/screw/BOUT.inp +++ b/tests/integrated/test-griddata/screw/BOUT.inp @@ -28,3 +28,7 @@ zShift = Btxy * y * L / (2*pi*Rxy * Bpxy) # Define a twist-shift angle, for periodic in y runs ShiftAngle = Btxy * L / (Rxy * Bpxy) # Should be just a function of x + +# With 3D metrics, we can only calcParallelSlice after the coordinates is created. +# Because we use a custom main, we musn't do this. +calcParallelSlices_on_communicate = false diff --git a/tests/integrated/test-gyro/CMakeLists.txt b/tests/integrated/test-gyro/CMakeLists.txt index bfa45a773c..b93fea493b 100644 --- a/tests/integrated/test-gyro/CMakeLists.txt +++ b/tests/integrated/test-gyro/CMakeLists.txt @@ -1,5 +1,6 @@ bout_add_integrated_test(test-gyro SOURCES test_gyro.cxx + CONFLICTS BOUT_USE_METRIC_3D USE_RUNTEST USE_DATA_BOUT_INP EXTRA_FILES cyclone_68x32.nc data/benchmark.0.nc diff --git a/tests/integrated/test-gyro/runtest b/tests/integrated/test-gyro/runtest index 49cd1c8ff8..fd349f7255 100755 --- a/tests/integrated/test-gyro/runtest +++ b/tests/integrated/test-gyro/runtest @@ -4,6 +4,7 @@ # Run the test, compare results against the benchmark # +# requires: not metric_3d # Requires: netcdf # Cores: 4 diff --git a/tests/integrated/test-interchange-instability/CMakeLists.txt b/tests/integrated/test-interchange-instability/CMakeLists.txt index f2d4847a40..2190e8b8be 100644 --- a/tests/integrated/test-interchange-instability/CMakeLists.txt +++ b/tests/integrated/test-interchange-instability/CMakeLists.txt @@ -1,5 +1,6 @@ bout_add_integrated_test(test-interchange-instability SOURCES 2fluid.cxx + CONFLICTS BOUT_USE_METRIC_3D USE_RUNTEST EXTRA_FILES slab.6b5.r1.cdl slab.6b5.r10.cdl data_1/BOUT.inp data_10/BOUT.inp REQUIRES BOUT_HAS_NETCDF diff --git a/tests/integrated/test-interchange-instability/runtest b/tests/integrated/test-interchange-instability/runtest index d72ca3af35..0f75157aaf 100755 --- a/tests/integrated/test-interchange-instability/runtest +++ b/tests/integrated/test-interchange-instability/runtest @@ -4,6 +4,7 @@ # Run the test, compare results against the benchmark # +# requires: not metric_3d # requires: netcdf # cores: 2 diff --git a/tests/integrated/test-invertable-operator/CMakeLists.txt b/tests/integrated/test-invertable-operator/CMakeLists.txt index 9931b2a9b5..c35a1407f3 100644 --- a/tests/integrated/test-invertable-operator/CMakeLists.txt +++ b/tests/integrated/test-invertable-operator/CMakeLists.txt @@ -1,5 +1,6 @@ bout_add_integrated_test(test-invertable-operator SOURCES invertable_operator.cxx + CONFLICTS BOUT_USE_METRIC_3D USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_HAS_PETSC diff --git a/tests/integrated/test-invertable-operator/runtest b/tests/integrated/test-invertable-operator/runtest index 9f71d177ae..47fc5f1b53 100755 --- a/tests/integrated/test-invertable-operator/runtest +++ b/tests/integrated/test-invertable-operator/runtest @@ -1,6 +1,7 @@ #!/usr/bin/env python3 # requires: petsc +# requires: not metric_3d # cores: 2 # diff --git a/tests/integrated/test-invpar/CMakeLists.txt b/tests/integrated/test-invpar/CMakeLists.txt index b1ab558af4..f7cbc10b17 100644 --- a/tests/integrated/test-invpar/CMakeLists.txt +++ b/tests/integrated/test-invpar/CMakeLists.txt @@ -1,5 +1,6 @@ bout_add_integrated_test(test-invpar SOURCES test_invpar.cxx + CONFLICTS BOUT_USE_METRIC_3D USE_RUNTEST USE_DATA_BOUT_INP ) diff --git a/tests/integrated/test-invpar/runtest b/tests/integrated/test-invpar/runtest index 88abc414d5..de7f028528 100755 --- a/tests/integrated/test-invpar/runtest +++ b/tests/integrated/test-invpar/runtest @@ -4,6 +4,7 @@ # Run the test, check it completed successfully # +# requires: not metric_3d # Requires: netcdf # Cores: 4 diff --git a/tests/integrated/test-io/runtest b/tests/integrated/test-io/runtest index 3a9ea2d78e..95d126a80c 100755 --- a/tests/integrated/test-io/runtest +++ b/tests/integrated/test-io/runtest @@ -7,13 +7,14 @@ # requires: not legacy_netcdf # cores: 4 +from boutconfig import isMetric3D from boututils.run_wrapper import build_and_log, shell, launch_safe from boutdata.collect import collect from boututils.datafile import DataFile + import numpy as np from sys import exit - build_and_log("I/O test") # Read benchmark values @@ -51,7 +52,6 @@ field_vars = [ ] # Field quantities, not scalars tol = 1e-10 - print("Reading benchmark data") bmk = {} for v in vars: @@ -78,9 +78,17 @@ def check_output(): # Compare benchmark and output if np.shape(bmk[v]) != np.shape(result): - print("Fail, wrong shape") - success = False - continue + if ( + isMetric3D() + and v.startswith("v2d") + and bmk[v].shape == result.shape[:-1] + ): + result = result[..., 0] + else: + print("Fail, wrong shape") + print(v, bmk[v].shape, result.shape) + success = False + continue if result.dtype.kind not in np.typecodes["AllFloat"]: # Non-float types should be identical @@ -95,6 +103,11 @@ def check_output(): success = False continue + if isMetric3D() and v.startswith("v2d"): + # Vector2Ds are based on Field3Ds for 3D metrics + bmk[v].attributes["bout_type"] = "Field3D_t" + bmk[v].attributes["direction_z"] = "Standard" + for attrname in bmk[v].attributes: bmkattr = bmk[v].attributes[attrname] resattr = result.attributes[attrname] diff --git a/tests/integrated/test-laplace/CMakeLists.txt b/tests/integrated/test-laplace/CMakeLists.txt index ec1c065df0..33da197985 100644 --- a/tests/integrated/test-laplace/CMakeLists.txt +++ b/tests/integrated/test-laplace/CMakeLists.txt @@ -1,5 +1,6 @@ bout_add_integrated_test(test-laplace SOURCES test_laplace.cxx + CONFLICTS BOUT_USE_METRIC_3D EXTRA_FILES test_laplace.grd.nc data/benchmark.0.nc USE_RUNTEST USE_DATA_BOUT_INP diff --git a/tests/integrated/test-laplace/runtest b/tests/integrated/test-laplace/runtest index ff6990d303..04236a598e 100755 --- a/tests/integrated/test-laplace/runtest +++ b/tests/integrated/test-laplace/runtest @@ -4,6 +4,7 @@ # Run the test, compare results against the benchmark # +# requires: not metric_3d # Requires: netcdf # Cores: 4 diff --git a/tests/integrated/test-laplacexy-fv/CMakeLists.txt b/tests/integrated/test-laplacexy-fv/CMakeLists.txt index d2c30f366b..d52a1dd9c6 100644 --- a/tests/integrated/test-laplacexy-fv/CMakeLists.txt +++ b/tests/integrated/test-laplacexy-fv/CMakeLists.txt @@ -1,6 +1,7 @@ bout_add_integrated_test(test-laplacexy-fv SOURCES test-laplacexy.cxx REQUIRES BOUT_HAS_PETSC + CONFLICTS BOUT_USE_METRIC_3D # Test requires Laplace_perpXY operator, which is not implemented for 3d metrics USE_RUNTEST USE_DATA_BOUT_INP ) diff --git a/tests/integrated/test-laplacexy-fv/runtest b/tests/integrated/test-laplacexy-fv/runtest index f560f22abf..f87a12e2ab 100755 --- a/tests/integrated/test-laplacexy-fv/runtest +++ b/tests/integrated/test-laplacexy-fv/runtest @@ -4,6 +4,7 @@ # Run the test, compare results against the benchmark # +# requires: not metric_3d # requires: petsc # cores: 8 @@ -59,7 +60,7 @@ for nproc in [8]: "hypre not available as pre-conditioner in PETSc. Re-running with " + "pctype=shell..." ) - s, out = launch(cmd, nproc=nproc, pipe=True, verbose=True) + _, out = launch_safe(cmd, nproc=nproc, pipe=True, verbose=True) f = open("run.log." + str(nproc), "w") f.write(out) diff --git a/tests/integrated/test-laplacexy-short/CMakeLists.txt b/tests/integrated/test-laplacexy-short/CMakeLists.txt index 0f97dba818..d9a01cf3a4 100644 --- a/tests/integrated/test-laplacexy-short/CMakeLists.txt +++ b/tests/integrated/test-laplacexy-short/CMakeLists.txt @@ -1,6 +1,7 @@ bout_add_integrated_test(test-laplacexy-short SOURCES test-laplacexy.cxx REQUIRES BOUT_HAS_PETSC + CONFLICTS BOUT_USE_METRIC_3D # Test uses cyclic Laplace solver as a preconditioner, which is not available with 3d metrics USE_RUNTEST USE_DATA_BOUT_INP ) diff --git a/tests/integrated/test-laplacexy-short/runtest b/tests/integrated/test-laplacexy-short/runtest index 8d20d96cdf..3325a32f4d 100755 --- a/tests/integrated/test-laplacexy-short/runtest +++ b/tests/integrated/test-laplacexy-short/runtest @@ -4,6 +4,7 @@ # Run the test, compare results against the benchmark # +# requires: not metric_3d # requires: petsc # cores: 8 @@ -13,12 +14,6 @@ from sys import exit tol = 5.0e-8 -# Note accuracy of test is limited when g12!=0 by inconsistency between the way boundary -# conditions are applied in LaplaceXY and the way they are applied in the D2DXDY() -# operator called by Laplace_perp(). In D2DXDY(f) 'free_o3' boundary conditions are -# applied to dfdy before calculating DDX(dfdy). -tol_nonorth = 2.0e-5 - argslist = [ "laplacexy:core_bndry_dirichlet=true laplacexy:pf_bndry_dirichlet=true laplacexy:y_bndry=dirichlet " "f:bndry_xin=dirichlet f:bndry_xout=dirichlet f:bndry_yup=dirichlet f:bndry_ydown=dirichlet", @@ -73,7 +68,7 @@ for nproc in [8]: "hypre not available as pre-conditioner in PETSc. Re-running with " + "pctype=shell..." ) - s, out = launch(cmd, nproc=nproc, pipe=True, verbose=True) + _, out = launch_safe(cmd, nproc=nproc, pipe=True, verbose=True) f = open("run.log." + str(nproc), "w") f.write(out) diff --git a/tests/integrated/test-laplacexy-short/test-laplacexy.cxx b/tests/integrated/test-laplacexy-short/test-laplacexy.cxx index 20fd42db7e..68f67afba2 100644 --- a/tests/integrated/test-laplacexy-short/test-laplacexy.cxx +++ b/tests/integrated/test-laplacexy-short/test-laplacexy.cxx @@ -63,9 +63,10 @@ int main(int argc, char** argv) { Field2D rhs, rhs_check; if (include_y_derivs) { - rhs = a*Laplace_perp(f) + Grad_perp(a)*Grad_perp(f) + b*f; + rhs = a * DC(Laplace_perp(f)) + DC(Grad_perp(a) * Grad_perp(f)) + b * f; } else { - rhs = a*Delp2(f, CELL_DEFAULT, false) + coords->g11*DDX(a)*DDX(f) + b*f; + rhs = + a * DC(Delp2(f, CELL_DEFAULT, false)) + DC(coords->g11 * DDX(a) * DDX(f)) + b * f; } laplacexy.setCoefs(a, b); @@ -79,9 +80,10 @@ int main(int argc, char** argv) { mesh->communicate(sol); if (include_y_derivs) { - rhs_check = a*Laplace_perp(sol) + Grad_perp(a)*Grad_perp(sol) + b*sol; + rhs_check = a * DC(Laplace_perp(sol)) + DC(Grad_perp(a) * Grad_perp(sol)) + b * sol; } else { - rhs_check = a*Delp2(sol, CELL_DEFAULT, false) + coords->g11*DDX(a)*DDX(sol) + b*sol; + rhs_check = a * DC(Delp2(sol, CELL_DEFAULT, false)) + + DC(coords->g11 * DDX(a) * DDX(sol)) + b * sol; } using bout::globals::dump; diff --git a/tests/integrated/test-laplacexy/runtest b/tests/integrated/test-laplacexy/runtest index 30a3f36e0e..d2f99c5100 100755 --- a/tests/integrated/test-laplacexy/runtest +++ b/tests/integrated/test-laplacexy/runtest @@ -4,6 +4,7 @@ # Run the test, compare results against the benchmark # +# requires: not metric_3d # requires: petsc # requires: all_tests # cores: 8 diff --git a/tests/integrated/test-laplacexy/test-laplacexy.cxx b/tests/integrated/test-laplacexy/test-laplacexy.cxx index 4e85b48fef..e3219e68f6 100644 --- a/tests/integrated/test-laplacexy/test-laplacexy.cxx +++ b/tests/integrated/test-laplacexy/test-laplacexy.cxx @@ -30,6 +30,9 @@ #include #include +using bout::globals::dump; +using bout::globals::mesh; + int main(int argc, char** argv) { BoutInitialise(argc, argv); diff --git a/tests/integrated/test-laplacexz/CMakeLists.txt b/tests/integrated/test-laplacexz/CMakeLists.txt new file mode 100644 index 0000000000..b19aa986ad --- /dev/null +++ b/tests/integrated/test-laplacexz/CMakeLists.txt @@ -0,0 +1,7 @@ +bout_add_integrated_test(test-laplacexz + SOURCES test-laplacexz.cxx + REQUIRES BOUT_HAS_PETSC + CONFLICTS BOUT_USE_METRIC_3D + USE_RUNTEST + USE_DATA_BOUT_INP + ) diff --git a/tests/integrated/test-laplacexz/data/BOUT.inp b/tests/integrated/test-laplacexz/data/BOUT.inp index f30a7bb1c5..f20c3d6c11 100644 --- a/tests/integrated/test-laplacexz/data/BOUT.inp +++ b/tests/integrated/test-laplacexz/data/BOUT.inp @@ -1,21 +1,28 @@ +f = gauss(x-0.5,0.05) * gauss(z-pi, 0.25) rhs = sin(x - z) rhs2 = rhs +MXG=1 +MYG=0 + [mesh] -nx = 36 -ny = 2 +nx = 34 +ny = 1 nz = 32 dx = 1 dz = 1 -[laplace] -type = petscamg # Use PETSc implementation +[laplacexz] +type = petsc # Use PETSc implementation + +rtol=1e-11 +atol=1e-11 ksptype = gmres # Linear iterative method -pctype = lu # Preconditioner. Direct "lu" or "ilu"; iterative "jacobi", "sor" +pctype = hypre # Preconditioner. Direct "lu" or "ilu"; iterative "jacobi", "sor" # Set package to perform factorisation for direct solves # "petsc" built-in solver only serial diff --git a/tests/integrated/test-laplacexz/runtest b/tests/integrated/test-laplacexz/runtest new file mode 100755 index 0000000000..e9e91fabe8 --- /dev/null +++ b/tests/integrated/test-laplacexz/runtest @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 + +# requires: not metric_3d +# requires: petsc +# travis doesn't have superlu_dist +# requires: not travis + +# +# Run the test, compare results against the benchmark +# + +from boututils.run_wrapper import shell, launch_safe, getmpirun, build_and_log +from boutdata.collect import collect +import numpy as np +from sys import exit + +tol = 1e-10 # Absolute tolerance + +MPIRUN = getmpirun() + +build_and_log("LaplaceXZ test") + +print("Running LaplaceXZ test") +success = True + +for nproc in [1, 2, 4]: + nxpe = nproc + + cmd = "./test-laplacexz nxpe=" + str(nxpe) + + shell("rm data/BOUT.dmp.*.nc") + + print(" %d processors (nxpe = %d)...." % (nproc, nxpe)) + s, out = launch_safe(cmd, runcmd=MPIRUN, nproc=nproc, mthread=1, pipe=True) + with open("run.log." + str(nproc), "w") as f: + f.write(out) + + # Collect output data + f = collect("f", path="data", info=False) + f2 = collect("f2", path="data", info=False) + print(" Checking tolerance... ") + # Compare benchmark and output + if np.shape(f) != np.shape(f2): + print("Fail, wrong shape") + success = False + diff = np.max(np.abs(f2 - f)) + if diff > tol: + print("Fail, maximum difference = " + str(diff)) + success = False + else: + print("Pass") + +if success: + print(" => LaplaceXZ inversion test passed") + exit(0) + +print(" => LaplaceXZ test failed") +exit(1) diff --git a/tests/integrated/test-laplacexz/test-laplacexz.cxx b/tests/integrated/test-laplacexz/test-laplacexz.cxx index 85da15cb95..8ecf77cced 100644 --- a/tests/integrated/test-laplacexz/test-laplacexz.cxx +++ b/tests/integrated/test-laplacexz/test-laplacexz.cxx @@ -11,20 +11,40 @@ #include #include +#include #include int main(int argc, char** argv) { BoutInitialise(argc, argv); - auto inv = LaplaceXZ::create(mesh); + auto inv = LaplaceXZ::create(bout::globals::mesh); + auto coord = bout::globals::mesh->getCoordinates(); + coord->g13 = 1.8; // test off-diagonal components with nonzero value + + // create some input field + Field3D f = FieldFactory::get()->create3D("f", Options::getRoot(), bout::globals::mesh); + + // Calculate the Laplacian with non-zero g13 + Field3D g = coord->g11 * D2DX2(f) + coord->g13 * D2DXDZ(f) + coord->g33 * D2DZ2(f); + + inv->setCoefs(Field2D(1.0), Field2D(0.0)); + + Field3D f2 = inv->solve(g, 0.0); // Invert the Laplacian. + + SAVE_ONCE3(f, f2, g); + + coord->g13 = 0.0; // reset to 0.0 for original laplacexz test + + // Now the normal test. output.write("Setting coefficients\n"); inv->setCoefs(Field3D(1.0),Field3D(0.0)); output.write("First solve\n"); - Field3D rhs = FieldFactory::get()->create3D("rhs", Options::getRoot(), mesh); + Field3D rhs = + FieldFactory::get()->create3D("rhs", Options::getRoot(), bout::globals::mesh); Field3D x = inv->solve(rhs, 0.0); SAVE_ONCE2(rhs, x); @@ -33,11 +53,12 @@ int main(int argc, char** argv) { inv->setCoefs(Field3D(2.0),Field3D(0.1)); - Field3D rhs2 = FieldFactory::get()->create3D("rhs", Options::getRoot(), mesh); + Field3D rhs2 = + FieldFactory::get()->create3D("rhs", Options::getRoot(), bout::globals::mesh); Field3D x2 = inv->solve(rhs2, 0.0); SAVE_ONCE2(rhs2, x2); - dump.write(); + bout::globals::dump.write(); BoutFinalise(); return 0; diff --git a/tests/integrated/test-multigrid_laplace/CMakeLists.txt b/tests/integrated/test-multigrid_laplace/CMakeLists.txt index 482d91f38f..6bf744dc7c 100644 --- a/tests/integrated/test-multigrid_laplace/CMakeLists.txt +++ b/tests/integrated/test-multigrid_laplace/CMakeLists.txt @@ -1,5 +1,6 @@ bout_add_integrated_test(test-multigrid-laplace SOURCES test_multigrid_laplace.cxx + CONFLICTS BOUT_USE_METRIC_3D USE_RUNTEST USE_DATA_BOUT_INP ) diff --git a/tests/integrated/test-multigrid_laplace/runtest b/tests/integrated/test-multigrid_laplace/runtest index e7e3568c4f..9bc6eb89b2 100755 --- a/tests/integrated/test-multigrid_laplace/runtest +++ b/tests/integrated/test-multigrid_laplace/runtest @@ -1,5 +1,7 @@ #!/usr/bin/env python3 +# requires: not metric_3d + # # Run the test, check the error # diff --git a/tests/integrated/test-multigrid_laplace/test_multigrid_laplace.cxx b/tests/integrated/test-multigrid_laplace/test_multigrid_laplace.cxx index 750d600366..92f34b9d10 100644 --- a/tests/integrated/test-multigrid_laplace/test_multigrid_laplace.cxx +++ b/tests/integrated/test-multigrid_laplace/test_multigrid_laplace.cxx @@ -228,14 +228,16 @@ int main(int argc, char** argv) { x0 = 0.; if (mesh->firstX()) for (int k=0;kLocalNz;k++) - x0(mesh->xstart-1,mesh->ystart,k) = (f4(mesh->xstart,mesh->ystart,k)-f4(mesh->xstart-1,mesh->ystart,k)) - /mesh->getCoordinates()->dx(mesh->xstart,mesh->ystart) - /sqrt(mesh->getCoordinates()->g_11(mesh->xstart,mesh->ystart)); + x0(mesh->xstart - 1, mesh->ystart, k) = + (f4(mesh->xstart, mesh->ystart, k) - f4(mesh->xstart - 1, mesh->ystart, k)) + / mesh->getCoordinates()->dx(mesh->xstart, mesh->ystart, k) + / sqrt(mesh->getCoordinates()->g_11(mesh->xstart, mesh->ystart, k)); if (mesh->lastX()) for (int k=0;kLocalNz;k++) - x0(mesh->xend+1,mesh->ystart,k) = (f4(mesh->xend+1,mesh->ystart,k)-f4(mesh->xend,mesh->ystart,k)) - /mesh->getCoordinates()->dx(mesh->xend,mesh->ystart) - /sqrt(mesh->getCoordinates()->g_11(mesh->xend,mesh->ystart)); + x0(mesh->xend + 1, mesh->ystart, k) = + (f4(mesh->xend + 1, mesh->ystart, k) - f4(mesh->xend, mesh->ystart, k)) + / mesh->getCoordinates()->dx(mesh->xend, mesh->ystart, k) + / sqrt(mesh->getCoordinates()->g_11(mesh->xend, mesh->ystart, k)); try { sol4 = invert->solve(sliceXZ(b4, mesh->ystart), sliceXZ(x0, mesh->ystart)); diff --git a/tests/integrated/test-naulin-laplace/CMakeLists.txt b/tests/integrated/test-naulin-laplace/CMakeLists.txt index 62e723e727..086212d7d8 100644 --- a/tests/integrated/test-naulin-laplace/CMakeLists.txt +++ b/tests/integrated/test-naulin-laplace/CMakeLists.txt @@ -1,5 +1,6 @@ bout_add_integrated_test(test-naulin-laplace SOURCES test_naulin_laplace.cxx + CONFLICTS BOUT_USE_METRIC_3D USE_RUNTEST USE_DATA_BOUT_INP ) diff --git a/tests/integrated/test-naulin-laplace/runtest b/tests/integrated/test-naulin-laplace/runtest index fa90b79938..82dd22776e 100755 --- a/tests/integrated/test-naulin-laplace/runtest +++ b/tests/integrated/test-naulin-laplace/runtest @@ -1,5 +1,7 @@ #!/usr/bin/env python3 +# requires: not metric_3d + # # Run the test, check the error # diff --git a/tests/integrated/test-naulin-laplace/test_naulin_laplace.cxx b/tests/integrated/test-naulin-laplace/test_naulin_laplace.cxx index 339871e8de..89892d36cc 100644 --- a/tests/integrated/test-naulin-laplace/test_naulin_laplace.cxx +++ b/tests/integrated/test-naulin-laplace/test_naulin_laplace.cxx @@ -235,14 +235,16 @@ int main(int argc, char** argv) { x0 = 0.; if (mesh->firstX()) for (int k=0;kLocalNz;k++) - x0(mesh->xstart-1,mesh->ystart,k) = (f4(mesh->xstart,mesh->ystart,k)-f4(mesh->xstart-1,mesh->ystart,k)) - /mesh->getCoordinates()->dx(mesh->xstart,mesh->ystart) - /sqrt(mesh->getCoordinates()->g_11(mesh->xstart,mesh->ystart)); + x0(mesh->xstart - 1, mesh->ystart, k) = + (f4(mesh->xstart, mesh->ystart, k) - f4(mesh->xstart - 1, mesh->ystart, k)) + / mesh->getCoordinates()->dx(mesh->xstart, mesh->ystart, k) + / sqrt(mesh->getCoordinates()->g_11(mesh->xstart, mesh->ystart, k)); if (mesh->lastX()) for (int k=0;kLocalNz;k++) - x0(mesh->xend+1,mesh->ystart,k) = (f4(mesh->xend+1,mesh->ystart,k)-f4(mesh->xend,mesh->ystart,k)) - /mesh->getCoordinates()->dx(mesh->xend,mesh->ystart) - /sqrt(mesh->getCoordinates()->g_11(mesh->xend,mesh->ystart)); + x0(mesh->xend + 1, mesh->ystart, k) = + (f4(mesh->xend + 1, mesh->ystart, k) - f4(mesh->xend, mesh->ystart, k)) + / mesh->getCoordinates()->dx(mesh->xend, mesh->ystart, k) + / sqrt(mesh->getCoordinates()->g_11(mesh->xend, mesh->ystart, k)); try { sol4 = invert.solve(b4, x0); diff --git a/tests/integrated/test-petsc_laplace/CMakeLists.txt b/tests/integrated/test-petsc_laplace/CMakeLists.txt index 6a4a670098..6f3a617f0b 100644 --- a/tests/integrated/test-petsc_laplace/CMakeLists.txt +++ b/tests/integrated/test-petsc_laplace/CMakeLists.txt @@ -1,6 +1,7 @@ bout_add_integrated_test(test-petsc-laplace SOURCES test_petsc_laplace.cxx REQUIRES BOUT_HAS_PETSC + CONFLICTS BOUT_USE_METRIC_3D # default preconditioner uses 'cyclic' Laplace solver which is not available with 3d metrics USE_RUNTEST USE_DATA_BOUT_INP ) diff --git a/tests/integrated/test-petsc_laplace_MAST-grid/CMakeLists.txt b/tests/integrated/test-petsc_laplace_MAST-grid/CMakeLists.txt index 625365d846..9ccd3b1947 100644 --- a/tests/integrated/test-petsc_laplace_MAST-grid/CMakeLists.txt +++ b/tests/integrated/test-petsc_laplace_MAST-grid/CMakeLists.txt @@ -1,6 +1,8 @@ +# Extremly slow on travis - thus skip bout_add_integrated_test(test-petsc-laplace-MAST-grid SOURCES test_petsc_laplace_MAST_grid.cxx REQUIRES BOUT_HAS_PETSC + REQUIRES NOT DEFINED ENV{TRAVIS} USE_RUNTEST USE_DATA_BOUT_INP EXTRA_FILES diff --git a/tests/integrated/test-petsc_laplace_MAST-grid/test_petsc_laplace_MAST_grid.cxx b/tests/integrated/test-petsc_laplace_MAST-grid/test_petsc_laplace_MAST_grid.cxx index 09b7f6a596..cdc31d774c 100644 --- a/tests/integrated/test-petsc_laplace_MAST-grid/test_petsc_laplace_MAST_grid.cxx +++ b/tests/integrated/test-petsc_laplace_MAST-grid/test_petsc_laplace_MAST_grid.cxx @@ -665,9 +665,9 @@ int main(int argc, char** argv) { dump.write(); dump.close(); - - output << "\nFinished running test. Triggering error to quit\n\n"; - + + output << "\nFinished running test.\n"; + MPI_Barrier(BoutComm::get()); // Wait for all processors to write data } BoutFinalise(); diff --git a/tests/integrated/test-snb/CMakeLists.txt b/tests/integrated/test-snb/CMakeLists.txt index e426b7f3fb..5814956f8f 100644 --- a/tests/integrated/test-snb/CMakeLists.txt +++ b/tests/integrated/test-snb/CMakeLists.txt @@ -1,4 +1,5 @@ bout_add_integrated_test(test-snb SOURCES test_snb.cxx + CONFLICTS BOUT_USE_METRIC_3D USE_DATA_BOUT_INP ) diff --git a/tests/integrated/test-snb/runtest b/tests/integrated/test-snb/runtest index f283dfc8ab..6aaee40a22 100755 --- a/tests/integrated/test-snb/runtest +++ b/tests/integrated/test-snb/runtest @@ -1,4 +1,6 @@ #!/bin/sh +#requires: not metric_3d + make ./test_snb diff --git a/tests/integrated/test-twistshift-staggered/data/BOUT.inp b/tests/integrated/test-twistshift-staggered/data/BOUT.inp index c2d285ff87..3aa7925224 100644 --- a/tests/integrated/test-twistshift-staggered/data/BOUT.inp +++ b/tests/integrated/test-twistshift-staggered/data/BOUT.inp @@ -9,7 +9,10 @@ nx = 5 ny = 7 nz = 5 +dx = 1.0 +dx_ylow = 1.0 # Needed so BOUT++ recognises that _ylow quantities are present zShift = (y-0.5) * (x+1) +zShift_ylow = (y-0.5) * (x+1) ShiftAngle = 2*pi*(x+1) [mesh:paralleltransform] diff --git a/tests/requirements/metric_3d b/tests/requirements/metric_3d new file mode 100755 index 0000000000..209ca4e05c --- /dev/null +++ b/tests/requirements/metric_3d @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 + +# Tests if build has 3D metric + +import os +from sys import exit +from boututils.run_wrapper import shell + +selflocation = os.path.realpath(__file__) +selflocation = os.path.dirname(selflocation) +status, out = shell(selflocation + "/../../bin/bout-config --metric-type", pipe=True) + +if "3D" in out: + exit(0) # True + +exit(1) # False diff --git a/tests/unit/fake_parallel_mesh.hxx b/tests/unit/fake_parallel_mesh.hxx index aa7f930ae8..d32450ea5f 100644 --- a/tests/unit/fake_parallel_mesh.hxx +++ b/tests/unit/fake_parallel_mesh.hxx @@ -297,7 +297,8 @@ std::vector createFakeProcessors(int nx, int ny, int nz, int n bout::globals::mesh, Field2D{1.0}, Field2D{1.0}, BoutReal{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, - Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, false); + Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}); + // No call to Coordinates::geometry() needed here static_cast(&meshes[j + i * nype])->setCoordinates(test_coords); test_coords->setParallelTransform( bout::utils::make_unique(*bout::globals::mesh)); diff --git a/tests/unit/field/test_field.cxx b/tests/unit/field/test_field.cxx index 813afe799d..b5d586e512 100644 --- a/tests/unit/field/test_field.cxx +++ b/tests/unit/field/test_field.cxx @@ -203,3 +203,51 @@ TEST_F(FieldTest, filledFromConstInd3D) { ASSERT_DOUBLE_EQ(result[i], i.x() * i.y()); } } + +TEST_F(FieldTest, IsUniformTrue3D) { + Field3D uniform{1.0}; + EXPECT_TRUE(isUniform(uniform)); +} + +TEST_F(FieldTest, IsUniformFalse3D) { + Field3D nonuniform{2.0}; + nonuniform(1, 2, 3) = 3.0; + EXPECT_FALSE(isUniform(nonuniform)); +} + +TEST_F(FieldTest, IsUniformTrue2D) { + Field2D uniform{4.0}; + EXPECT_TRUE(isUniform(uniform)); +} + +TEST_F(FieldTest, IsUniformFalse2D) { + Field2D nonuniform{5.0}; + nonuniform(2, 1) = 6.0; + EXPECT_FALSE(isUniform(nonuniform)); +} + +TEST_F(FieldTest, GetUniformTrue3D) { + Field3D uniform{10.0}; + EXPECT_EQ(getUniform(uniform), 10.0); +} + +TEST_F(FieldTest, GetUniformFalse3D) { + Field3D nonuniform{20.0}; + nonuniform(1, 2, 3) = 30.0; +#if CHECK > 1 + EXPECT_THROW(getUniform(nonuniform), BoutException); +#endif +} + +TEST_F(FieldTest, GetUniformTrue2D) { + Field2D uniform{40.0}; + EXPECT_EQ(getUniform(uniform), 40.0); +} + +TEST_F(FieldTest, GetUniformFalse2D) { + Field2D nonuniform{50.0}; + nonuniform(2, 1) = 60.0; +#if CHECK > 1 + EXPECT_THROW(getUniform(nonuniform), BoutException); +#endif +} diff --git a/tests/unit/field/test_field2d.cxx b/tests/unit/field/test_field2d.cxx index 5694633b62..a805b9e320 100644 --- a/tests/unit/field/test_field2d.cxx +++ b/tests/unit/field/test_field2d.cxx @@ -1473,4 +1473,11 @@ TEST_F(Field2DTest, ZeroFrom) { EXPECT_TRUE(field2.isAllocated()); EXPECT_TRUE(IsFieldEqual(field2, 0.)); } + +TEST_F(Field2DTest, DC) { + Field2D field = + makeField([](const Ind2D& i) -> BoutReal { return i.x() * i.y(); }); + EXPECT_EQ(DC(field), field); +} + #pragma GCC diagnostic pop diff --git a/tests/unit/field/test_field_factory.cxx b/tests/unit/field/test_field_factory.cxx index a2118417f9..0f5d18ba8a 100644 --- a/tests/unit/field/test_field_factory.cxx +++ b/tests/unit/field/test_field_factory.cxx @@ -575,9 +575,10 @@ TYPED_TEST(FieldFactoryCreationTest, CreateOnMesh) { localmesh.createDefaultRegions(); localmesh.setCoordinates(std::make_shared( &localmesh, Field2D{1.0}, Field2D{1.0}, BoutReal{1.0}, Field2D{1.0}, Field2D{0.0}, - Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{0.0}, - Field2D{0.0}, Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, Field2D{0.0}, - Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, false)); + Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, + Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, + Field2D{0.0}, Field2D{0.0})); + // No call to Coordinates::geometry() needed here localmesh.getCoordinates()->setParallelTransform( bout::utils::make_unique(localmesh)); @@ -630,7 +631,7 @@ TEST_F(FieldFactoryTest, RequireMesh) { TEST_F(FieldFactoryTest, CreateOnMeshWithoutCoordinates) { static_cast(mesh)->setCoordinates(nullptr); - EXPECT_THROW(factory.create3D("x"), BoutException); + EXPECT_NO_THROW(factory.create3D("x")); } TEST_F(FieldFactoryTest, CleanCache) { @@ -860,27 +861,27 @@ class MockParallelTransform : public ParallelTransform { void checkInputGrid() override {} - const Field3D fromFieldAligned(const Field3D& f, const std::string&) override { + Field3D fromFieldAligned(const Field3D& f, const std::string&) override { if (f.getDirectionY() != YDirectionType::Aligned) { throw BoutException("Unaligned field passed to fromFieldAligned"); } return -f; } - const FieldPerp fromFieldAligned(const FieldPerp& f, const std::string&) override { + FieldPerp fromFieldAligned(const FieldPerp& f, const std::string&) override { if (f.getDirectionY() != YDirectionType::Aligned) { throw BoutException("Unaligned field passed to fromFieldAligned"); } return -f; } - const Field3D toFieldAligned(const Field3D& f, const std::string&) override { + Field3D toFieldAligned(const Field3D& f, const std::string&) override { if (f.getDirectionY() != YDirectionType::Standard) { throw BoutException("Aligned field passed to toFieldAligned"); } return -f; } - const FieldPerp toFieldAligned(const FieldPerp& f, const std::string&) override { + FieldPerp toFieldAligned(const FieldPerp& f, const std::string&) override { if (f.getDirectionY() != YDirectionType::Standard) { throw BoutException("Aligned field passed to toFieldAligned"); } diff --git a/tests/unit/field/test_vector2d.cxx b/tests/unit/field/test_vector2d.cxx index 3f476a73bd..122f56a3ab 100644 --- a/tests/unit/field/test_vector2d.cxx +++ b/tests/unit/field/test_vector2d.cxx @@ -1,6 +1,7 @@ #include "gtest/gtest.h" #include "boutexception.hxx" +#if not(BOUT_USE_METRIC_3D) #include "output.hxx" #include "test_extras.hxx" #include "unused.hxx" @@ -49,7 +50,8 @@ class Vector2DTest : public ::testing::Test { mesh, Field2D{1.0}, Field2D{1.0}, BoutReal{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{1.0}, Field2D{2.0}, Field2D{3.0}, Field2D{4.0}, Field2D{5.0}, Field2D{6.0}, Field2D{1.0}, Field2D{2.0}, Field2D{3.0}, Field2D{4.0}, - Field2D{5.0}, Field2D{6.0}, Field2D{0.0}, Field2D{0.0}, false)); + Field2D{5.0}, Field2D{6.0}, Field2D{0.0}, Field2D{0.0})); + // No call to Coordinates::geometry() needed here delete mesh_staggered; mesh_staggered = new FakeMesh(nx, ny, nz); @@ -740,3 +742,4 @@ TEST_F(Vector2DTest, AbsContra) { EXPECT_TRUE(IsFieldEqual(result, 24.819347291981714)); } +#endif diff --git a/tests/unit/field/test_vector3d.cxx b/tests/unit/field/test_vector3d.cxx index 6eacf1ae43..693e596714 100644 --- a/tests/unit/field/test_vector3d.cxx +++ b/tests/unit/field/test_vector3d.cxx @@ -48,7 +48,8 @@ class Vector3DTest : public ::testing::Test { mesh, Field2D{1.0}, Field2D{1.0}, BoutReal{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{1.0}, Field2D{2.0}, Field2D{3.0}, Field2D{4.0}, Field2D{5.0}, Field2D{6.0}, Field2D{1.0}, Field2D{2.0}, Field2D{3.0}, Field2D{4.0}, - Field2D{5.0}, Field2D{6.0}, Field2D{0.0}, Field2D{0.0}, false)); + Field2D{5.0}, Field2D{6.0}, Field2D{0.0}, Field2D{0.0})); + // No call to Coordinates::geometry() needed here delete mesh_staggered; mesh_staggered = new FakeMesh(nx, ny, nz); diff --git a/tests/unit/include/bout/test_petsc_indexer.cxx b/tests/unit/include/bout/test_petsc_indexer.cxx index 6b45c30a80..15d510ae48 100644 --- a/tests/unit/include/bout/test_petsc_indexer.cxx +++ b/tests/unit/include/bout/test_petsc_indexer.cxx @@ -51,7 +51,8 @@ class IndexerTest : public FakeMeshFixture { bout::globals::mesh, Field2D{1.0}, Field2D{1.0}, BoutReal{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, - Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, false); + Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}); + // No call to Coordinates::geometry() needed here mesh2.setCoordinates(test_coords); // May need a ParallelTransform to create fields, because create3D calls // fromFieldAligned diff --git a/tests/unit/mesh/data/test_gridfromoptions.cxx b/tests/unit/mesh/data/test_gridfromoptions.cxx index fe7f6ae7e5..53cd35eeae 100644 --- a/tests/unit/mesh/data/test_gridfromoptions.cxx +++ b/tests/unit/mesh/data/test_gridfromoptions.cxx @@ -62,6 +62,12 @@ class GridFromOptionsTest : public ::testing::Test { return index.x() + (TWOPI * index.y()) + (TWOPI * index.z() / nz) + 3; }, &mesh_from_options); + expected_metric = +#if BOUT_USE_METRIC_3D + expected_3d; +#else + expected_2d; +#endif } ~GridFromOptionsTest() override { @@ -82,6 +88,7 @@ class GridFromOptionsTest : public ::testing::Test { std::string expected_string{"x + y + z + 3"}; Field2D expected_2d; Field3D expected_3d; + Coordinates::FieldMetric expected_metric; FakeMesh mesh_from_options{nx, ny, nz}; }; @@ -336,26 +343,30 @@ TEST_F(GridFromOptionsTest, CoordinatesCentre) { mesh_from_options.communicate(expected_2d); - EXPECT_TRUE(IsFieldEqual(coords->g11, expected_2d + 5.)); - EXPECT_TRUE(IsFieldEqual(coords->g22, expected_2d + 4.)); - EXPECT_TRUE(IsFieldEqual(coords->g33, expected_2d + 3.)); - EXPECT_TRUE(IsFieldEqual(coords->g12, expected_2d + 2.)); - EXPECT_TRUE(IsFieldEqual(coords->g13, expected_2d + 1.)); - EXPECT_TRUE(IsFieldEqual(coords->g23, expected_2d)); + EXPECT_TRUE(IsFieldEqual(coords->g11, expected_metric + 5.)); + EXPECT_TRUE(IsFieldEqual(coords->g22, expected_metric + 4.)); + EXPECT_TRUE(IsFieldEqual(coords->g33, expected_metric + 3.)); + EXPECT_TRUE(IsFieldEqual(coords->g12, expected_metric + 2.)); + EXPECT_TRUE(IsFieldEqual(coords->g13, expected_metric + 1.)); + EXPECT_TRUE(IsFieldEqual(coords->g23, expected_metric)); } +#if not(BOUT_USE_METRIC_3D) TEST_F(GridFromOptionsTest, CoordinatesZlow) { auto coords = mesh_from_options.getCoordinates(CELL_ZLOW); mesh_from_options.communicate(expected_2d); - EXPECT_TRUE(IsFieldEqual(coords->g11, expected_2d + 5.)); - EXPECT_TRUE(IsFieldEqual(coords->g22, expected_2d + 4.)); - EXPECT_TRUE(IsFieldEqual(coords->g33, expected_2d + 3.)); - EXPECT_TRUE(IsFieldEqual(coords->g12, expected_2d + 2.)); - EXPECT_TRUE(IsFieldEqual(coords->g13, expected_2d + 1.)); - EXPECT_TRUE(IsFieldEqual(coords->g23, expected_2d)); + EXPECT_TRUE(IsFieldEqual(coords->g11, expected_metric + 5.)); + EXPECT_TRUE(IsFieldEqual(coords->g22, expected_metric + 4.)); + EXPECT_TRUE(IsFieldEqual(coords->g33, expected_metric + 3.)); + EXPECT_TRUE(IsFieldEqual(coords->g12, expected_metric + 2.)); + EXPECT_TRUE(IsFieldEqual(coords->g13, expected_metric + 1.)); + EXPECT_TRUE(IsFieldEqual(coords->g23, expected_metric)); } +#else +// Maybe replace by MMS test, because we need a periodic function in z. +#endif TEST_F(GridFromOptionsTest, CoordinatesXlowInterp) { // *_xlow fields not present in options, Coordinates will be interpolated @@ -366,8 +377,8 @@ TEST_F(GridFromOptionsTest, CoordinatesXlowInterp) { auto coords = mesh_from_options.getCoordinates(CELL_XLOW); - Field2D expected_xlow = makeField( - [](Field2D::ind_type& index) { + Coordinates::FieldMetric expected_xlow = makeField( + [](Coordinates::FieldMetric::ind_type& index) { return index.x() - 0.5 + (TWOPI * index.y()) + (TWOPI * index.z() / nz) + 3; }, &mesh_from_options); @@ -425,6 +436,7 @@ TEST_F(GridFromOptionsTest, CoordinatesXlowRead) { } TEST_F(GridFromOptionsTest, CoordinatesYlowInterp) { +#if not(BOUT_USE_METRIC_3D) // *_ylow fields not present in options, Coordinates will be interpolated // from CELL_CENTRE @@ -453,9 +465,11 @@ TEST_F(GridFromOptionsTest, CoordinatesYlowInterp) { EXPECT_TRUE(coords->g13.getLocation() == CELL_YLOW); EXPECT_TRUE(IsFieldEqual(coords->g23, expected_ylow, "RGN_NOBNDRY", this_tolerance)); EXPECT_TRUE(coords->g23.getLocation() == CELL_YLOW); +#endif } TEST_F(GridFromOptionsTest, CoordinatesYlowRead) { +#if not(BOUT_USE_METRIC_3D) // *_ylow fields added to options, will be read to initialise Coordinates // Note '(2*pi*11 - y)' here because FakeMesh::GlobalY(int jy) returns jy, not a @@ -496,9 +510,11 @@ TEST_F(GridFromOptionsTest, CoordinatesYlowRead) { EXPECT_TRUE(coords->g13.getLocation() == CELL_YLOW); EXPECT_TRUE(IsFieldEqual(coords->g23, expected_ylow, "RGN_ALL", this_tolerance)); EXPECT_TRUE(coords->g23.getLocation() == CELL_YLOW); +#endif } TEST_F(GridFromOptionsTest, CoordinatesZlowRead) { +#if not(BOUT_USE_METRIC_3D) // Grids are axisymmetric, so CELL_ZLOW Coordinates will be read from // CELL_CENTRE variables @@ -510,4 +526,5 @@ TEST_F(GridFromOptionsTest, CoordinatesZlowRead) { EXPECT_TRUE(IsFieldEqual(coords->g12, expected_2d + 2.)); EXPECT_TRUE(IsFieldEqual(coords->g13, expected_2d + 1.)); EXPECT_TRUE(IsFieldEqual(coords->g23, expected_2d)); +#endif } diff --git a/tests/unit/mesh/parallel/test_shiftedmetric.cxx b/tests/unit/mesh/parallel/test_shiftedmetric.cxx index 513e38fc24..b177d75267 100644 --- a/tests/unit/mesh/parallel/test_shiftedmetric.cxx +++ b/tests/unit/mesh/parallel/test_shiftedmetric.cxx @@ -41,12 +41,12 @@ class ShiftedMetricTest : public ::testing::Test { mesh, Field2D{1.0}, Field2D{1.0}, BoutReal{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, Field2D{0.0}, - Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, false)); + Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0})); + // No call to Coordinates::geometry() needed here auto coords = mesh->getCoordinates(); - coords->setParallelTransform( - bout::utils::make_unique(*mesh, CELL_CENTRE, zShift, - coords->zlength())); + coords->setParallelTransform(bout::utils::make_unique( + *mesh, CELL_CENTRE, zShift, coords->zlength()(0, 0))); Field3D input_temp{mesh}; diff --git a/tests/unit/mesh/test_coordinates.cxx b/tests/unit/mesh/test_coordinates.cxx index 87ce0962c1..9d766f65b6 100644 --- a/tests/unit/mesh/test_coordinates.cxx +++ b/tests/unit/mesh/test_coordinates.cxx @@ -1,5 +1,6 @@ #include "gtest/gtest.h" +#include "bout/build_config.hxx" #include "bout/coordinates.hxx" #include "bout/constants.hxx" #include "bout/mesh.hxx" @@ -16,26 +17,96 @@ extern Mesh* mesh; using bout::globals::mesh; -using CoordinatesTest = FakeMeshFixture; +class CoordinatesTest : public FakeMeshFixture { +public: + using FieldMetric = Coordinates::FieldMetric; + CoordinatesTest() : FakeMeshFixture() {} +}; constexpr BoutReal default_dz{TWOPI / CoordinatesTest::nz}; TEST_F(CoordinatesTest, ZLength) { - Coordinates coords{ - mesh, Field2D{1.0}, Field2D{1.0}, BoutReal{1.0}, Field2D{1.0}, Field2D{1.0}, - Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, - Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, - Field2D{0.0}, Field2D{0.0}, false}; + Coordinates coords{mesh, + FieldMetric{1.0}, // dx + FieldMetric{1.0}, // dy + FieldMetric{1.0}, // dz + FieldMetric{1.0}, // J + FieldMetric{1.0}, // Bxy + FieldMetric{1.0}, // g11 + FieldMetric{1.0}, // g22 + FieldMetric{1.0}, // g33 + FieldMetric{0.0}, // g12 + FieldMetric{0.0}, // g13 + FieldMetric{0.0}, // g23 + FieldMetric{1.0}, // g_11 + FieldMetric{1.0}, // g_22 + FieldMetric{1.0}, // g_23 + FieldMetric{0.0}, // g_12 + FieldMetric{0.0}, // g_13 + FieldMetric{0.0}, // g_23 + FieldMetric{0.0}, // ShiftTorsion + FieldMetric{0.0}}; // IntShiftTorsion + // No call to Coordinates::geometry() needed here + + EXPECT_TRUE(IsFieldEqual(coords.zlength(), 7.0)); +} - EXPECT_DOUBLE_EQ(coords.zlength(), 7.0); +#if BOUT_USE_METRIC_3D +TEST_F(CoordinatesTest, ZLength3D) { + auto dz = makeField([](const Ind3D& i) -> BoutReal { + return static_cast(i.x() + i.y()) / nz; + }); + auto expected = + makeField([](const Ind2D& i) -> BoutReal { return i.x() + i.y(); }); + + Coordinates coords{mesh, + FieldMetric{1.0}, // dx + FieldMetric{1.0}, // dy + dz, // dz + FieldMetric{1.0}, // J + FieldMetric{1.0}, // Bxy + FieldMetric{1.0}, // g11 + FieldMetric{1.0}, // g22 + FieldMetric{1.0}, // g33 + FieldMetric{0.0}, // g12 + FieldMetric{0.0}, // g13 + FieldMetric{0.0}, // g23 + FieldMetric{1.0}, // g_11 + FieldMetric{1.0}, // g_22 + FieldMetric{1.0}, // g_23 + FieldMetric{0.0}, // g_12 + FieldMetric{0.0}, // g_13 + FieldMetric{0.0}, // g_23 + FieldMetric{0.0}, // ShiftTorsion + FieldMetric{0.0}}; // IntShiftTorsion + // No call to Coordinates::geometry() needed here + + EXPECT_TRUE(IsFieldEqual(coords.zlength(), expected)); } +#endif TEST_F(CoordinatesTest, Jacobian) { - Coordinates coords{ - mesh, Field2D{1.0}, Field2D{1.0}, BoutReal{1.0}, Field2D{1.0}, Field2D{1.0}, - Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, - Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, - Field2D{0.0}, Field2D{0.0}, false}; + Coordinates coords{mesh, + FieldMetric{1.0}, // dx + FieldMetric{1.0}, // dy + FieldMetric{1.0}, // dz + FieldMetric{1.0}, // J + FieldMetric{1.0}, // Bxy + FieldMetric{1.0}, // g11 + FieldMetric{1.0}, // g22 + FieldMetric{1.0}, // g33 + FieldMetric{0.0}, // g12 + FieldMetric{0.0}, // g13 + FieldMetric{0.0}, // g23 + FieldMetric{1.0}, // g_11 + FieldMetric{1.0}, // g_22 + FieldMetric{1.0}, // g_23 + FieldMetric{0.0}, // g_12 + FieldMetric{0.0}, // g_13 + FieldMetric{0.0}, // g_23 + FieldMetric{0.0}, // ShiftTorsion + FieldMetric{0.0}}; // IntShiftTorsion + // No call to Coordinates::geometry() needed here EXPECT_NO_THROW(coords.jacobian()); @@ -43,12 +114,30 @@ TEST_F(CoordinatesTest, Jacobian) { EXPECT_TRUE(IsFieldEqual(coords.Bxy, 1.0)); } +/// To do generalise these tests +// #if not(BOUT_USE_METRIC_3D) TEST_F(CoordinatesTest, CalcContravariant) { - Coordinates coords{ - mesh, Field2D{1.0}, Field2D{1.0}, BoutReal{1.0}, Field2D{0.0}, Field2D{0.0}, - Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, - Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, - Field2D{0.0}, Field2D{0.0}, false}; + Coordinates coords{mesh, + FieldMetric{1.0}, // dx + FieldMetric{1.0}, // dy + FieldMetric{1.0}, // dz + FieldMetric{0.0}, // J + FieldMetric{0.0}, // Bxy + FieldMetric{1.0}, // g11 + FieldMetric{1.0}, // g22 + FieldMetric{1.0}, // g33 + FieldMetric{0.0}, // g12 + FieldMetric{0.0}, // g13 + FieldMetric{0.0}, // g23 + FieldMetric{0.0}, // g_11 + FieldMetric{0.0}, // g_22 + FieldMetric{0.0}, // g_23 + FieldMetric{0.0}, // g_12 + FieldMetric{0.0}, // g_13 + FieldMetric{0.0}, // g_23 + FieldMetric{0.0}, // ShiftTorsion + FieldMetric{0.0}}; // IntShiftTorsion + // No call to Coordinates::geometry() needed here output_info.disable(); coords.calcCovariant(); @@ -63,11 +152,27 @@ TEST_F(CoordinatesTest, CalcContravariant) { } TEST_F(CoordinatesTest, CalcCovariant) { - Coordinates coords{ - mesh, Field2D{1.0}, Field2D{1.0}, BoutReal{1.0}, Field2D{0.0}, Field2D{0.0}, - Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, - Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, - Field2D{0.0}, Field2D{0.0}, false}; + Coordinates coords{mesh, + FieldMetric{1.0}, // dx + FieldMetric{1.0}, // dy + FieldMetric{1.0}, // dz + FieldMetric{0.0}, // J + FieldMetric{0.0}, // Bxy + FieldMetric{0.0}, // g11 + FieldMetric{0.0}, // g22 + FieldMetric{0.0}, // g33 + FieldMetric{0.0}, // g12 + FieldMetric{0.0}, // g13 + FieldMetric{0.0}, // g23 + FieldMetric{1.0}, // g_11 + FieldMetric{1.0}, // g_22 + FieldMetric{1.0}, // g_23 + FieldMetric{0.0}, // g_12 + FieldMetric{0.0}, // g_13 + FieldMetric{0.0}, // g_23 + FieldMetric{0.0}, // ShiftTorsion + FieldMetric{0.0}}; // IntShiftTorsion + // No call to Coordinates::geometry() needed here output_info.disable(); coords.calcContravariant(); @@ -80,6 +185,7 @@ TEST_F(CoordinatesTest, CalcCovariant) { EXPECT_TRUE(IsFieldEqual(coords.g13, 0.0)); EXPECT_TRUE(IsFieldEqual(coords.g23, 0.0)); } +// #endif TEST_F(CoordinatesTest, DefaultConstructor) { output_info.disable(); @@ -90,7 +196,7 @@ TEST_F(CoordinatesTest, DefaultConstructor) { EXPECT_TRUE(IsFieldEqual(coords.dx, 1.0)); EXPECT_TRUE(IsFieldEqual(coords.dy, 1.0)); - EXPECT_DOUBLE_EQ(coords.dz, default_dz); + EXPECT_TRUE(IsFieldEqual(coords.dz, default_dz)); EXPECT_TRUE(IsFieldEqual(coords.g11, 1.0)); EXPECT_TRUE(IsFieldEqual(coords.g22, 1.0)); @@ -116,7 +222,7 @@ TEST_F(CoordinatesTest, ConstructWithMeshSpacing) { EXPECT_TRUE(IsFieldEqual(coords.dx, 2.0)); EXPECT_TRUE(IsFieldEqual(coords.dy, 3.2)); - EXPECT_DOUBLE_EQ(coords.dz, 42.); + EXPECT_TRUE(IsFieldEqual(coords.dz, 42.)); EXPECT_TRUE(IsFieldEqual(coords.g11, 1.0)); EXPECT_TRUE(IsFieldEqual(coords.g22, 1.0)); @@ -135,7 +241,8 @@ TEST_F(CoordinatesTest, SmallMeshSpacing) { output_info.disable(); output_warn.disable(); - EXPECT_THROW(Coordinates coords(mesh), BoutException); + Coordinates coords(mesh); + EXPECT_THROW(coords.geometry(), BoutException); output_warn.enable(); output_info.enable(); } @@ -154,7 +261,7 @@ TEST_F(CoordinatesTest, ConstructWithDiagonalContravariantMetric) { // Didn't specify grid spacing, so default to 1 EXPECT_TRUE(IsFieldEqual(coords.dx, 1.0)); EXPECT_TRUE(IsFieldEqual(coords.dy, 1.0)); - EXPECT_DOUBLE_EQ(coords.dz, default_dz); + EXPECT_TRUE(IsFieldEqual(coords.dz, default_dz)); // Diagonal contravariant metric EXPECT_TRUE(IsFieldEqual(coords.g11, 2.0)); diff --git a/tests/unit/mesh/test_interpolation.cxx b/tests/unit/mesh/test_interpolation.cxx index 27df2c62ed..94bd181eab 100644 --- a/tests/unit/mesh/test_interpolation.cxx +++ b/tests/unit/mesh/test_interpolation.cxx @@ -72,8 +72,9 @@ class Field3DInterpToTest : public ::testing::Test { Field2D{0.0, mesh}, Field2D{1.0, mesh}, Field2D{1.0, mesh}, Field2D{1.0, mesh}, Field2D{0.0, mesh}, Field2D{0.0, mesh}, Field2D{0.0, mesh}, Field2D{1.0, mesh}, Field2D{1.0, mesh}, Field2D{1.0, mesh}, Field2D{0.0, mesh}, Field2D{0.0, mesh}, - Field2D{0.0, mesh}, Field2D{0.0, mesh}, Field2D{0.0, mesh}, false), + Field2D{0.0, mesh}, Field2D{0.0, mesh}, Field2D{0.0, mesh}), location); + // No call to Coordinates::geometry() needed here mesh->getCoordinates(location)->setParallelTransform( bout::utils::make_unique(*mesh)); } @@ -315,8 +316,9 @@ class Field2DInterpToTest : public ::testing::Test { Field2D{0.0, mesh}, Field2D{1.0, mesh}, Field2D{1.0, mesh}, Field2D{1.0, mesh}, Field2D{0.0, mesh}, Field2D{0.0, mesh}, Field2D{0.0, mesh}, Field2D{1.0, mesh}, Field2D{1.0, mesh}, Field2D{1.0, mesh}, Field2D{0.0, mesh}, Field2D{0.0, mesh}, - Field2D{0.0, mesh}, Field2D{0.0, mesh}, Field2D{0.0, mesh}, false), + Field2D{0.0, mesh}, Field2D{0.0, mesh}, Field2D{0.0, mesh}), location); + // No call to Coordinates::geometry() needed here mesh->getCoordinates(location)->setParallelTransform( bout::utils::make_unique(*mesh)); } diff --git a/tests/unit/solver/test_solver.cxx b/tests/unit/solver/test_solver.cxx index 0d81489166..9d913b0ebd 100644 --- a/tests/unit/solver/test_solver.cxx +++ b/tests/unit/solver/test_solver.cxx @@ -314,22 +314,27 @@ TEST_F(SolverTest, AddVector2D) { Vector2D vector1{}, vector2{}; EXPECT_NO_THROW(solver.add(vector1, "vector")); - EXPECT_EQ(solver.n2Dvars(), 3); - EXPECT_EQ(solver.n3Dvars(), 0); +#if not(BOUT_USE_METRIC_3D) + constexpr int n2d = 3, n3d = 0; +#else + constexpr int n2d = 0, n3d = 3; +#endif + EXPECT_EQ(solver.n2Dvars(), n2d); + EXPECT_EQ(solver.n3Dvars(), n3d); EXPECT_TRUE(IsFieldEqual(vector1.x, 5.0)); EXPECT_TRUE(IsFieldEqual(vector1.y, 6.0)); EXPECT_TRUE(IsFieldEqual(vector1.z, 7.0)); #if CHECK > 0 EXPECT_THROW(solver.add(vector2, "vector"), BoutException); - EXPECT_EQ(solver.n2Dvars(), 3); - EXPECT_EQ(solver.n3Dvars(), 0); + EXPECT_EQ(solver.n2Dvars(), n2d); + EXPECT_EQ(solver.n3Dvars(), n3d); #endif vector2.covariant = false; EXPECT_NO_THROW(solver.add(vector2, "another_vector")); - EXPECT_EQ(solver.n2Dvars(), 6); - EXPECT_EQ(solver.n3Dvars(), 0); + EXPECT_EQ(solver.n2Dvars(), n2d * 2); + EXPECT_EQ(solver.n3Dvars(), n3d * 2); EXPECT_TRUE(IsFieldEqual(vector2.x, 8.0)); EXPECT_TRUE(IsFieldEqual(vector2.y, 9.0)); EXPECT_TRUE(IsFieldEqual(vector2.z, 10.0)); @@ -440,29 +445,34 @@ TEST_F(SolverTest, ConstraintVector2D) { Vector2D vector1{}, vector2{}; EXPECT_NO_THROW(solver.constraint(vector1, vector1, "vector")); - EXPECT_EQ(solver.n2Dvars(), 3); - EXPECT_EQ(solver.n3Dvars(), 0); +#if not(BOUT_USE_METRIC_3D) + constexpr int n2d = 3, n3d = 0; +#else + constexpr int n2d = 0, n3d = 3; +#endif + EXPECT_EQ(solver.n2Dvars(), n2d); + EXPECT_EQ(solver.n3Dvars(), n3d); #if CHECK > 0 EXPECT_THROW(solver.constraint(vector2, vector2, "vector"), BoutException); - EXPECT_EQ(solver.n2Dvars(), 3); - EXPECT_EQ(solver.n3Dvars(), 0); + EXPECT_EQ(solver.n2Dvars(), n2d); + EXPECT_EQ(solver.n3Dvars(), n3d); EXPECT_THROW(solver.constraint(vector2, vector2, ""), BoutException); - EXPECT_EQ(solver.n2Dvars(), 3); - EXPECT_EQ(solver.n3Dvars(), 0); + EXPECT_EQ(solver.n2Dvars(), n2d); + EXPECT_EQ(solver.n3Dvars(), n3d); solver.changeHasConstraints(false); EXPECT_THROW(solver.constraint(vector2, vector2, "some_other_name"), BoutException); - EXPECT_EQ(solver.n2Dvars(), 3); - EXPECT_EQ(solver.n3Dvars(), 0); + EXPECT_EQ(solver.n2Dvars(), n2d); + EXPECT_EQ(solver.n3Dvars(), n3d); solver.changeHasConstraints(true); #endif vector2.covariant = false; EXPECT_NO_THROW(solver.constraint(vector2, vector2, "another_vector")); - EXPECT_EQ(solver.n2Dvars(), 6); - EXPECT_EQ(solver.n3Dvars(), 0); + EXPECT_EQ(solver.n2Dvars(), n2d * 2); + EXPECT_EQ(solver.n3Dvars(), n3d * 2); const auto expected_names = std::vector{"vector", "another_vector"}; EXPECT_EQ(solver.listVector2DNames(), expected_names); diff --git a/tests/unit/test_extras.hxx b/tests/unit/test_extras.hxx index 843fac0138..111a05f0ba 100644 --- a/tests/unit/test_extras.hxx +++ b/tests/unit/test_extras.hxx @@ -361,7 +361,6 @@ private: class FakeGridDataSource : public GridDataSource { public: FakeGridDataSource() {} - /// Constructor setting values which can be fetched from this source FakeGridDataSource(Options& values) : values(values) {} @@ -467,8 +466,8 @@ public: bout::globals::mesh, Field2D{1.0}, Field2D{1.0}, BoutReal{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, - Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, - false); + Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}); + // No call to Coordinates::geometry() needed here static_cast(bout::globals::mesh)->setCoordinates(test_coords); static_cast(bout::globals::mesh)->setGridDataSource( new FakeGridDataSource()); @@ -496,7 +495,8 @@ public: Field2D{1.0, mesh_staggered}, Field2D{1.0, mesh_staggered}, Field2D{1.0, mesh_staggered}, Field2D{0.0, mesh_staggered}, Field2D{0.0, mesh_staggered}, Field2D{0.0, mesh_staggered}, - Field2D{0.0, mesh_staggered}, Field2D{0.0, mesh_staggered}, false); + Field2D{0.0, mesh_staggered}, Field2D{0.0, mesh_staggered}); + // No call to Coordinates::geometry() needed here test_coords_staggered->setParallelTransform( bout::utils::make_unique(*mesh_staggered)); } diff --git a/tools/pylib/_boutcore_build/boutcore.pyx.in b/tools/pylib/_boutcore_build/boutcore.pyx.in index 60b81d0e72..b89ce28ec0 100755 --- a/tools/pylib/_boutcore_build/boutcore.pyx.in +++ b/tools/pylib/_boutcore_build/boutcore.pyx.in @@ -689,8 +689,6 @@ cdef extern from "helper.h": void c_laplacian_solve(c.Laplacian *, c.Field3D *, c.Field3D*) c.Field3D c_Grad_perp_dot_Grad_perp(c.Field3D,c.Field3D); void c_mesh_normalise(c.Mesh* , double) - void c_set_dz(c.Coordinates*, double) - double c_get_dz(c.Coordinates*) c.Datafile * c_get_global_datafile() cdef class Mesh: @@ -845,17 +843,17 @@ cdef class Coordinates: """ cdef c.Coordinates * cobj cdef c.bool isSelfOwned - cdef public Field2D dx, dy - cdef public Field2D J - cdef public Field2D Bxy - cdef public Field2D g11, g22, g33, g12, g13, g23 - cdef public Field2D g_11, g_22, g_33, g_12, g_13, g_23 - cdef public Field2D G1_11, G1_22, G1_33, G1_12, G1_13, G1_23 - cdef public Field2D G2_11, G2_22, G2_33, G2_12, G2_13, G2_23 - cdef public Field2D G3_11, G3_22, G3_33, G3_12, G3_13, G3_23 - cdef public Field2D G1, G2, G3 - cdef public Field2D ShiftTorsion - cdef public Field2D IntShiftTorsion + cdef public $metric_field dx, dy, dz + cdef public $metric_field J + cdef public $metric_field Bxy + cdef public $metric_field g11, g22, g33, g12, g13, g23 + cdef public $metric_field g_11, g_22, g_33, g_12, g_13, g_23 + cdef public $metric_field G1_11, G1_22, G1_33, G1_12, G1_13, G1_23 + cdef public $metric_field G2_11, G2_22, G2_33, G2_12, G2_13, G2_23 + cdef public $metric_field G3_11, G3_22, G3_33, G3_12, G3_13, G3_23 + cdef public $metric_field G1, G2, G3 + cdef public $metric_field ShiftTorsion + cdef public $metric_field IntShiftTorsion def __init__(self): self.cobj = NULL @@ -863,24 +861,11 @@ cdef class Coordinates: def _setmembers(self): EOF -for f in "dx" "dy" "J" "Bxy" "g11" "g22" "g33" "g12" "g13" "g23" "g_11" "g_22" "g_33" "g_12" "g_13" "g_23" "G1_11" "G1_22" "G1_33" "G1_12" "G1_13" "G1_23" "G2_11" "G2_22" "G2_33" "G2_12" "G2_13" "G2_23" "G3_11" "G3_22" "G3_33" "G3_12" "G3_13" "G3_23" "G1" "G2" "G3" "ShiftTorsion" "IntShiftTorsion" +for f in "dx" "dy" "dz" "J" "Bxy" "g11" "g22" "g33" "g12" "g13" "g23" "g_11" "g_22" "g_33" "g_12" "g_13" "g_23" "G1_11" "G1_22" "G1_33" "G1_12" "G1_13" "G1_23" "G2_11" "G2_22" "G2_33" "G2_12" "G2_13" "G2_23" "G3_11" "G3_22" "G3_33" "G3_12" "G3_13" "G3_23" "G1" "G2" "G3" "ShiftTorsion" "IntShiftTorsion" do - echo " self.${f} = f2dFromPtr(&self.cobj.${f})" + echo " self.${f} = ${metric_fdd}FromPtr(&self.cobj.${f})" done cat <<"EOF" - cdef getDz(self): - return c_get_dz(self.cobj) - - cdef setDz(self, val): - c_set_dz(self.cobj, val) - - @property - def dz(self): - return self.getDz() - - @dz.setter - def dz(self, val): - self.setDz(val) def __dealloc__(self): self.__boutcore_dealloc() diff --git a/tools/pylib/_boutcore_build/boutcpp.pxd.in b/tools/pylib/_boutcore_build/boutcpp.pxd.in index d542ffefdc..78706414f0 100755 --- a/tools/pylib/_boutcore_build/boutcpp.pxd.in +++ b/tools/pylib/_boutcore_build/boutcpp.pxd.in @@ -83,18 +83,17 @@ cdef extern from "bout/mesh.hxx": cdef extern from "bout/coordinates.hxx": cppclass Coordinates: Coordinates() - Field2D dx, dy - double dz - Field2D J - Field2D Bxy - Field2D g11, g22, g33, g12, g13, g23 - Field2D g_11, g_22, g_33, g_12, g_13, g_23 - Field2D G1_11, G1_22, G1_33, G1_12, G1_13, G1_23 - Field2D G2_11, G2_22, G2_33, G2_12, G2_13, G2_23 - Field2D G3_11, G3_22, G3_33, G3_12, G3_13, G3_23 - Field2D G1, G2, G3 - Field2D ShiftTorsion - Field2D IntShiftTorsion + $metric_field dx, dy, dz + $metric_field J + $metric_field Bxy + $metric_field g11, g22, g33, g12, g13, g23 + $metric_field g_11, g_22, g_33, g_12, g_13, g_23 + $metric_field G1_11, G1_22, G1_33, G1_12, G1_13, G1_23 + $metric_field G2_11, G2_22, G2_33, G2_12, G2_13, G2_23 + $metric_field G3_11, G3_22, G3_33, G3_12, G3_13, G3_23 + $metric_field G1, G2, G3 + $metric_field ShiftTorsion + $metric_field IntShiftTorsion int geometry() int calcCovariant() int calcContravariant() diff --git a/tools/pylib/_boutcore_build/common.sh b/tools/pylib/_boutcore_build/common.sh index 7f5a12acc0..2e53564780 100644 --- a/tools/pylib/_boutcore_build/common.sh +++ b/tools/pylib/_boutcore_build/common.sh @@ -19,12 +19,34 @@ makelist () { start="$spacing" ; i=$(( i + 1 )) done ; } +# What is the metric type? +coords_is() { + if grep '^#define BOUT_USE_METRIC_3D 1' ../../../include/bout/build_defines.hxx -q + then + echo "f3d" + else + echo "f2d" + fi ; } + +name_of_fdd() { + case $1 in + f3d) + echo "Field3D" + ;; + f2d) + echo "Field2D" + ;; + *) + echo "unexpected fdd" + exit 4; + esac ; } + # Set variables for Vectors and Fields setvars() { case $1 in Vector2D) - field=Field2D - fdd=f2d + fdd=$(coords_is) + field=$(name_of_fdd $fdd) vdd=v2d fheader=vector2d ;; @@ -55,3 +77,6 @@ setvars() { vecs="Vector3D Vector2D" fields="Field3D Field2D" + +metric_fdd=$(coords_is) +metric_field=$(name_of_fdd $metric_fdd) diff --git a/tools/pylib/_boutcore_build/helper.h.in b/tools/pylib/_boutcore_build/helper.h.in index 433d4ba01b..7131d21a83 100644 --- a/tools/pylib/_boutcore_build/helper.h.in +++ b/tools/pylib/_boutcore_build/helper.h.in @@ -126,11 +126,4 @@ Datafile * c_get_global_datafile(){ return &bout::globals::dump; } -double c_get_dz(Coordinates * coords) { - return coords->dz; -} -void c_set_dz(Coordinates * coords, double val) { - coords->dz = val; -} - EOF diff --git a/tools/pylib/boutconfig/__init__.py.cin b/tools/pylib/boutconfig/__init__.py.cin index 069823de7c..2205f2d7bd 100644 --- a/tools/pylib/boutconfig/__init__.py.cin +++ b/tools/pylib/boutconfig/__init__.py.cin @@ -31,6 +31,7 @@ config = { "has_nls": "@BOUT_HAS_GETTEXT@", "has_fftw": "@BOUT_HAS_FFTW@", "petsc_has_sundials": "@PETSC_HAS_SUNDIALS@", + "metric_type": "@BOUT_METRIC_TYPE@", } @@ -42,3 +43,13 @@ def has(): if k.startswith("has_"): _has[k[4:]] = _yesno[config[k]] return _has + + +def isMetric2D(): + """Is the metric 2D?""" + return config["metric_type"] == "2D" + + +def isMetric3D(): + """Is the metric 3D?""" + return config["metric_type"] == "3D" diff --git a/tools/pylib/boutconfig/__init__.py.in b/tools/pylib/boutconfig/__init__.py.in index bdc382420b..83dc78b567 100644 --- a/tools/pylib/boutconfig/__init__.py.in +++ b/tools/pylib/boutconfig/__init__.py.in @@ -30,6 +30,7 @@ config = { "has_nls": "@BOUT_HAS_GETTEXT@", "has_fftw": "@BOUT_HAS_FFTW@", "petsc_has_sundials": "@PETSC_HAS_SUNDIALS@", + "metric_type": "@BOUT_METRIC_TYPE@", } @@ -41,3 +42,13 @@ def has(): if k.startswith("has_"): _has[k[4:]] = _yesno[config[k]] return _has + + +def isMetric2D(): + """Is the metric 2D?""" + return config["metric_type"] == "2D" + + +def isMetric3D(): + """Is the metric 3D?""" + return config["metric_type"] == "3D" diff --git a/tools/pylib/zoidberg/field.py b/tools/pylib/zoidberg/field.py index bcedfd0994..7935413d4c 100644 --- a/tools/pylib/zoidberg/field.py +++ b/tools/pylib/zoidberg/field.py @@ -1,6 +1,8 @@ from builtins import object import numpy as np +import sys + try: from . import boundary @@ -8,6 +10,16 @@ import boundary +if sys.version_info >= (3, 0): + pickle_read_mode = "rb" + pickle_write_mode = "wb" +else: + pickle_read_mode = "r" + pickle_write_mode = "w" + +from .progress import update_progress + + class MagneticField(object): """Represents a magnetic field in either Cartesian or cylindrical geometry @@ -36,6 +48,7 @@ class MagneticField(object): Slab : A straight field in normal Cartesian coordinates CurvedSlab : A field in curvilinear coordinates StraightStellarator : A rotating ellipse stellarator without curvature + RotatingEllipse : A rotating ellipse stellarator with curvature VMEC : A numerical field from a VMEC equilibrium file GEQDSK : A numerical field from an EFIT g-file @@ -194,7 +207,9 @@ def field_direction(self, pos, ycoord, flatten=False): if np.amin(np.abs(By)) < 1e-8: # Very small By print(x, z, ycoord, By) - raise ValueError("Small By") + raise ValueError( + "Small By ({}) at (x={}, y={}, z={})".format(By, x, ycoord, z) + ) R_By = Rmaj / By # Rate of change of x location [m] with y angle [radians] @@ -318,7 +333,22 @@ def Rfunc(self, x, z, phi): try: - from sympy import Symbol, atan2, cos, sin, log, pi, sqrt, lambdify + from sympy import ( + Symbol, + atan2, + cos, + sin, + log, + pi, + sqrt, + lambdify, + Piecewise, + Sum, + gamma, + And, + factorial, + diff, + ) class StraightStellarator(MagneticField): """A "rotating ellipse" stellarator without curvature @@ -426,6 +456,506 @@ def __init__( self.Bxfunc = lambdify((self.x, self.z, self.phi), Bx, "numpy") self.Bzfunc = lambdify((self.x, self.z, self.phi), Bz, "numpy") + class RotatingEllipse(MagneticField): + """A "rotating ellipse" stellarator + Parameters + ---------- + xcentre : float, optional + Middle of the domain in x [m] + zcentre : float, optional + Middle of the domain in z [m] + radius : float, optional + Radius of coils [meters] + yperiod : float, optional + The period over which the coils return to their original position + I_coil : float, optional + Current in each coil + Btor : float, optional + Toroidal magnetic field strength + """ + + def coil(self, xcentre, zcentre, radius, angle, iota, I): + """Defines a single coil + Parameters + ---------- + radius : float + Radius to coil + angle : float + Initial angle of coil + iota : float + Rotational transform of coil + I : float + Current through coil + Returns + ------- + (x, z) - x, z coordinates of coils along phi + """ + + return ( + xcentre + radius * cos(angle + iota * self.phi), + zcentre + radius * sin(angle + iota * self.phi), + I, + ) + + def __init__( + self, + xcentre=0.0, + zcentre=0.0, + radius=0.8, + yperiod=np.pi, + I_coil=0.05, + Btor=1.0, + smooth=False, + smooth_args={}, + ): + xcentre = float(xcentre) + zcentre = float(zcentre) + radius = float(radius) + yperiod = float(yperiod) + Btor = float(Btor) + + iota = 2.0 * np.pi / yperiod + + self.x = Symbol("x") + self.z = Symbol("z") + self.y = Symbol("y") + self.r = Symbol("r") + self.r = (self.x ** 2 + self.z ** 2) ** (0.5) + self.phi = Symbol("phi") + + self.xcentre = xcentre + self.zcentre = zcentre + self.radius = radius + + # Four coils equally spaced, alternating direction for current + self.coil_list = [ + self.coil( + xcentre, + zcentre, + radius, + n * pi, + iota, + ((-1) ** np.mod(i, 2)) * I_coil, + ) + for i, n in enumerate(np.arange(4) / 2.0) + ] + + A = 0.0 + Bx = 0.0 + Bz = 0.0 + + for c in self.coil_list: + xc, zc, Ic = c + rc = (xc ** 2 + zc ** 2) ** (0.5) + r2 = (self.x - xc) ** 2 + (self.z - zc) ** 2 + theta = atan2(self.z - zc, self.x - xc) # Angle relative to coil + + A -= Ic * 0.1 * log(r2) + + B = Ic * 0.2 / sqrt(r2) + + Bx += B * sin(theta) + Bz -= B * cos(theta) + + By = Btor / self.x + self.Afunc = lambdify((self.x, self.z, self.phi), A, "numpy") + + self.Bxfunc = lambdify((self.x, self.z, self.phi), Bx, "numpy") + self.Bzfunc = lambdify((self.x, self.z, self.phi), Bz, "numpy") + self.Byfunc = lambdify((self.x, self.z, self.phi), By, "numpy") + + def Rfunc(self, x, z, phi): + return np.full(x.shape, x) + + class DommaschkPotentials(MagneticField): + """A magnetic field generator using the Dommaschk potentials. + Parameters + ---------- + A: Coefficient matrix for the torodial and polidial harmonics. Form: (m,l,(a,b,c,d)) + R_0: major radius [m] + B_0: magnetic field on axis [T] + + Important Methods + ----------------- + Bxfunc/Byfunc/Bzfunc(x,z,y): Returns magnetic field in radial/torodial/z-direction + Sfunc(x,z,y): Returns approximate magnetic surface invariant for Dommaschk potentials. Use this to visualize flux surfaces + + + + """ + + def __init__(self, A, R_0=1.0, B_0=1.0): + + self.R_0 = R_0 + self.B_0 = B_0 + + self.R = Symbol("R") + self.phi = Symbol("phi") + self.Z = Symbol("Z") + + self.m = Symbol("m") + self.l = Symbol("l") + self.n = Symbol("n") + self.k = Symbol("k") + + self.A = A + + self.P = ( + self.U(self.A) + .doit() + .subs([(self.R, self.R / self.R_0), (self.Z, self.Z / R_0)]) + ) + self.P_hat = ( + self.U_hat(self.A) + .doit() + .subs([(self.R, self.R / self.R_0), (self.Z, self.Z / R_0)]) + ) + + S = 0.5 * ( + log(self.R / self.R_0) ** 2 + (self.Z / R_0) ** 2 + ) - self.R / self.R_0 * ( + log(self.R / self.R_0) * self.R_0 * diff(self.P_hat, self.R) + + self.Z * self.R / self.R_0 * diff(self.P_hat, self.Z) + ) # .subs([(self.R, self.R/self.R_0), (self.Z, self.Z)]) + + Bx = R_0 * diff(self.P, self.R) + By = R_0 / self.R * diff(self.P, self.phi) + Bz = diff(self.P, self.Z) + + self.Sf = lambdify((self.R, self.phi, self.Z), S, "numpy") + + self.Bxf = lambdify((self.R, self.phi, self.Z), Bx, "numpy") + self.Byf = lambdify((self.R, self.phi, self.Z), By, "numpy") + self.Bzf = lambdify((self.R, self.phi, self.Z), Bz, "numpy") + + def Bxfunc(self, x, z, phi): + + return self.Bxf(x, phi, z) / self.Byf(self.R_0, 0, 0) * self.B_0 + + def Byfunc(self, x, z, phi): + + return self.Byf(x, phi, z) / self.Byf(self.R_0, 0, 0) * self.B_0 + + def Bzfunc(self, x, z, phi): + + return self.Bzf(x, phi, z) / self.Byf(self.R_0, 0, 0) * self.B_0 + + def Sfunc(self, x, z, y): + """ + Parameters + ---------- + x: radial coordinates normalized to R_0 + z: binormal coordinate + y: torodial angle normalized to 2*pi + + Returns + ------- + Approximate magnetic surface invariant S at location (x,z,y). This is from the original Dommaschk paper. Use to visualize flux surfaces + """ + return self.Sf(x, y, z) + + def Rfunc(self, x, z, phi): + """ + Parameters + ---------- + x: radial coordinates normalized to R_0 + z: binormal coordinate + y: torodial angle normalized to 2*pi + + Returns + ------- + Radial coordinate x + """ + + return x + + def CD(self, m, k): + """ + Parameters + ---------- + m: torodial harmonic + k: summation index in D + + Returns: + -------- + Sympy function CD_mk (R) (Dirichlet boudary conditions) + """ + + n = Symbol("n") + b = Symbol("b") + i = Symbol("i") + + alpha = Piecewise( + ( + ( + ((-1) ** n) + / (gamma(b + n + 1) * gamma(n + 1) * 2 ** (2 * n + b)) + ), + n >= 0, + ), + (0, True), + ) + alpha_st = Piecewise((alpha * (2 * n + b), n >= 0), (0, True)) + + beta = Piecewise( + ( + (gamma(b - n)) / (gamma(n + 1) * 2 ** (2 * n - b + 1)), + And(n >= 0, n < b), + ), + (0, True), + ) + beta_st = Piecewise((beta * (2 * n - b), And(n >= 0, n < b)), (0, True)) + + delta = Piecewise( + (alpha / 2 * Sum(1 / i + 1 / (b + i), (i, 1, n + 1)), n > 0), (0, True) + ) + delta_st = Piecewise((delta * (2 * n + b), n > 0), (0, True)) + + j = Symbol("j") + + CD = Sum( + -( + alpha.subs([(n, j), (b, m)]) + * ( + alpha_st.subs([(n, k - m - j), (b, m)]) * log(self.R) + + delta_st.subs([(n, k - m - j), (b, m)]) + - alpha.subs([(n, k - m - j), (b, m)]) + ) + - delta.subs([(n, j), (b, m)]) + * alpha_st.subs([(n, k - m - j), (b, m)]) + + alpha.subs([(n, j), (b, m)]) * beta_st.subs([(n, k - j), (b, m)]) + ) + * self.R ** (2 * j + m) + + beta.subs([(n, j), (b, m)]) + * alpha_st.subs([(n, k - j), (b, m)]) + * self.R ** (2 * j - m), + (j, 0, k), + ) + + return CD + + def CN(self, m, k): + """ + Parameters + ---------- + m: torodial harmonic + k: summation index in N + + Returns: + -------- + Sympy function CN_mk (R) (Neumann boundary conditions) + """ + + n = Symbol("n") + b = Symbol("b") + i = Symbol("i") + + alpha = Piecewise( + ( + ( + ((-1) ** n) + / (gamma(b + n + 1) * gamma(n + 1) * 2 ** (2 * n + b)) + ), + n >= 0, + ), + (0, True), + ) + alpha_st = Piecewise((alpha * (2 * n + b), n >= 0), (0, True)) + + beta = Piecewise( + ( + (gamma(b - n)) / (gamma(n + 1) * 2 ** (2 * n - b + 1)), + And(n >= 0, n < b), + ), + (0, True), + ) + beta_st = Piecewise((beta * (2 * n - b), And(n >= 0, n < b)), (0, True)) + + delta = Piecewise( + (alpha / 2 * Sum(1 / i + 1 / (b + i), (i, 1, n + 1)), n > 0), (0, True) + ) + delta_st = Piecewise((delta * (2 * n + b), n > 0), (0, True)) + + j = Symbol("j") + + CN = Sum( + ( + alpha.subs([(n, j), (b, m)]) + * ( + alpha.subs([(n, k - m - j), (b, m)]) * log(self.R) + + delta.subs([(n, k - m - j), (b, m)]) + ) + - delta.subs([(n, j), (b, m)]) + * alpha.subs([(n, k - m - j), (b, m)]) + + alpha.subs([(n, j), (b, m)]) * beta.subs([(n, k - j), (b, m)]) + ) + * self.R ** (2 * j + m) + - beta.subs([(n, j), (b, m)]) + * alpha.subs([(n, k - j), (b, m)]) + * self.R ** (2 * j - m), + (j, 0, k), + ) + + return CN + + def D(self, m, n): + """ + Parameters + ---------- + m: torodial mode number + n: summation index in V + + Returns: + -------- + Sympy function D_mn (R, Z) (Dirichlet boundary conditions) + """ + + i = Symbol("i") + D = log(1) + k_arr = np.arange(0, int(n / 2) + 1, 1) + CD_f = self.CD(m, i) + + for k in k_arr: + D += (self.Z ** (n - 2 * k)) / factorial(n - 2 * k) * CD_f.subs(i, k) + + return D + + def N(self, m, n): + """ + Parameters + ---------- + m: torodial mode number + n: summation index in V + + Returns: + -------- + Sympy function N_mn (R, Z) (Neumann boundary conditions) + """ + + i = Symbol("i") + N = log(1) + k_arr = np.arange(0, int(n / 2) + 1, 1) + CN_f = self.CN(m, i) + for k in k_arr: + N += (self.Z ** (n - 2 * k)) / factorial(n - 2 * k) * CN_f.subs(i, k) + + return N + + def V(self, m, l, a, b, c, d): + """ + Parameters + ---------- + m: torodial mode number + l: polodial mode number + a,b,c,d: Coefficients for m,l-th Dommaschk potential (elements of matrix A) + + Returns: + -------- + Sympy function V_ml + """ + + V = (a * cos(m * self.phi) + b * sin(m * self.phi)) * self.D(m, l) + ( + c * cos(m * self.phi) + d * sin(m * self.phi) + ) * self.N(m, l - 1) + + return V + + def U(self, A): + """ + Parameters + ---------- + A: Coefficient matrix for the torodial and polidial harmonics. Form: (m,l,(a,b,c,d)) + + Returns + ----------------- + U: Superposition of all modes given in A + + """ + U = self.phi + for i in range(A.shape[0]): + for j in range(A.shape[1]): + if A[i, j, 0] or A[i, j, 1] or A[i, j, 2] or A[i, j, 3] != 0: + + U += self.V( + i, j, A[i, j, 0], A[i, j, 1], A[i, j, 2], A[i, j, 3] + ) + + return U + + def V_hat(self, m, l, a, b, c, d): + """ + Parameters + ---------- + m: torodial mode number + l: polodial mode number + a,b,c,d: Coefficients for m,l-th Dommaschk potential (elements of matrix A) + + Returns: + -------- + Sympy function V_hat_ml; Similar to V; needed for calculation of magnetic surface invariant S + """ + + V = ( + a * cos(m * (self.phi - np.pi / (2 * m))) + + b * sin(m * (self.phi - np.pi / (2 * m))) + ) * self.D(m, l) + ( + c * cos(m * (self.phi - np.pi / (2 * m))) + + d * sin(m * (self.phi - np.pi / (2 * m))) + ) * self.N( + m, l - 1 + ) + + return V + + def U_hat(self, A): + """ + Parameters + ---------- + A: Coefficient matrix for the torodial and polidial harmonics. Form: (m,l,(a,b,c,d)) + + Returns + ----------------- + U: Superposition of all modes given in A + + """ + + U = log(1) + for i in range(A.shape[0]): + for j in range(A.shape[1]): + if A[i, j, 0] or A[i, j, 1] or A[i, j, 2] or A[i, j, 3] != 0: + U += self.V_hat( + i, j, A[i, j, 0], A[i, j, 1], A[i, j, 2], A[i, j, 3] + ) * Piecewise((self.phi, i == 0), (1 / i, i > 0)) + + return U + + class Screwpinch(MagneticField): + def __init__( + self, xcentre=1.5, zcentre=0.0, shear=0, yperiod=2 * np.pi, Btor=1.0 + ): + self.x = Symbol("x") + self.z = Symbol("z") + self.y = Symbol("y") + self.r = Symbol("r") + self.r = ((self.x - xcentre) ** 2 + (self.z - zcentre) ** 2) ** (0.5) + + self.phi = Symbol("phi") + + alpha = shear + self.theta = atan2(self.z - zcentre, self.x - xcentre) + A = alpha * self.r ** 2 + Bx = -alpha * self.r * self.r * sin(self.theta) + Bz = alpha * self.r * self.r * cos(self.theta) + By = Btor / self.x + + self.Afunc = lambdify((self.x, self.z, self.phi), A, "numpy") + self.Bxfunc = lambdify((self.x, self.z, self.phi), Bx, "numpy") + self.Bzfunc = lambdify((self.x, self.z, self.phi), Bz, "numpy") + self.Byfunc = lambdify((self.x, self.z, self.phi), By, "numpy") + + def Rfunc(self, x, z, phi): + return np.full(x.shape, x) + except ImportError: @@ -443,6 +973,28 @@ def __init__(self, *args, **kwargs): "No Sympy module: Can't generate StraightStellarator fields" ) + class RotatingEllipse(MagneticField): + """ + Invalid RotatingEllipse, since no Sympy module. + Rather than printing an error on startup, which may + be missed or ignored, this raises + an exception if StraightStellarator is ever used. + """ + + def __init__(self, *args, **kwargs): + raise ImportError("No Sympy module: Can't generate RotatingEllipse fields") + + class Screwpinch(MagneticField): + """ + Invalid screwpinch, since no Sympy module. + Rather than printing an error on startup, which may + be missed or ignored, this raises + an exception if StraightStellarator is ever used. + """ + + def __init__(self, *args, **kwargs): + raise ImportError("No Sympy module: Can't generate screwpinch fields") + class VMEC(MagneticField): """A numerical magnetic field from a VMEC equilibrium file @@ -870,3 +1422,448 @@ def pressure(self, x, z, phi): return np.reshape(self.p_spl(np.ravel(psinorm)), psinorm.shape) return self.p_spl(psinorm) + + +class W7X_vacuum(MagneticField): + def __init__( + self, + nx=128, + ny=32, + nz=128, + x_range=(4.05, 6.55), + z_range=(-1.35, 1, 35), + phimax=2.0 * np.pi, + configuration=0, + plot_poincare=False, + include_plasma_field=False, + wout_file="wout_w7x.0972_0926_0880_0852_+0000_+0000.01.00jh.nc", + ): + """ + Get the field for W7X from the webservices. + + Parameters + ---------- + configuration : int + The id's are listed here: + http://webservices.ipp-hgw.mpg.de/docs/fieldlinetracer.html#MagneticConfig + While the description are at: + http://svvmec1.ipp-hgw.mpg.de:8080/vmecrest/v1/Coil_currents_1_AA_T_0011.pdf + """ + from scipy.interpolate import RegularGridInterpolator + import numpy as np + + ## create 1D arrays of cylindrical coordinates + r = np.linspace(x_range[0], x_range[-1], nx) + phi = np.linspace(0, phimax, ny) + z = np.linspace(z_range[0], z_range[-1], nz) + + ## make those 1D arrays 3D + rarray, yarray, zarray = np.meshgrid(r, phi, z, indexing="ij") + + ## call vacuum field values + b_vac = W7X_vacuum.field_values( + rarray, yarray, zarray, configuration, plot_poincare + ) + Bx_vac = b_vac[0] + By_vac = b_vac[1] + Bz_vac = b_vac[2] + + if include_plasma_field: + b_plasma = W7X_vacuum.plasma_field( + rarray, yarray, zarray, wout_file=wout_file + ) + Bx_plasma = b_plasma[0] + By_plasma = b_plasma[1] + Bz_plasma = b_plasma[2] + else: + Bx_plasma = 0 + By_plasma = 0 + Bz_plasma = 0 + + Bx = Bx_vac + Bx_plasma + By = By_vac + By_plasma + Bz = Bz_vac + Bz_plasma + + # Now we have a field and regular grid in (R,Z,phi) so + # we can get an interpolation function in 3D + points = (r, phi, z) + + try: + self.br_interp = RegularGridInterpolator( + points, Bx, bounds_error=False, fill_value=0.0 + ) + except: + print([i.shape for i in points], Bx.shape) + import matplotlib.pyplot as plt + + for i in points: + plt.plot(i) + plt.show() + raise + + self.bz_interp = RegularGridInterpolator( + points, Bz, bounds_error=False, fill_value=0.0 + ) + self.bphi_interp = RegularGridInterpolator( + points, By, bounds_error=False, fill_value=1.0 + ) + + # if you want non-interpolated, 3D arrays, make this your return function: + # return Bx,By,Bz + + # return points, br_interp, bphi_interp, bz_interp + + def field_values(r, phi, z, configuration=0, plot_poincare=False): + """This uses the webservices field line tracer to get the vacuum + magnetic field given 3d arrrays for R, phi, and Z. Only works + on IPP network + + http://webservices.ipp-hgw.mpg.de/docs/fieldlinetracer.html + + Contact brendan.shanahan@ipp.mpg.de for questions + + """ + from osa import Client + import os.path + import pickle + import matplotlib.pyplot as plt + + tracer = Client("http://esb.ipp-hgw.mpg.de:8280/services/FieldLineProxy?wsdl") + + nx = r.shape[0] + ny = phi.shape[1] + nz = z.shape[2] + + # create (standardized) file name for saving/loading magnetic field. + fname = "B.w7x.{}.{}.{}.{:.2f}-{:.2f}.{:.2f}-{:.2f}.{:.2f}-{:.2f}.dat".format( + nx, + ny, + nz, + r[0, 0, 0], + r[-1, 0, 0], + phi[0, 0, 0], + phi[0, -1, 0], + z[0, 0, 0], + z[0, 0, -1], + ) + + if os.path.isfile(fname): + print("Saved field found, loading from: ", fname) + with open(fname, pickle_read_mode) as f: + Br, Bphi, Bz = pickle.load(f) + + else: + print( + "No saved field found -- (re)calculating (must be on IPP network for this to work...)" + ) + print( + "Calculating field for Wendelstein 7-X; nx = ", + nx, + " ny = ", + ny, + " nz = ", + nz, + ) + + ## Create configuration objects + config = tracer.types.MagneticConfig() + config.configIds = configuration + + tot = nx * ny * nz + + Bx = np.zeros(tot) + By = np.zeros(tot) + Bz = np.zeros(tot) + pos = tracer.types.Points3D() + + x1 = np.ndarray.flatten( + np.ones((nx, ny, nz)) * r * np.cos(phi) + ) # x in Cartesian (real-space) + x2 = np.ndarray.flatten( + np.ones((nx, ny, nz)) * r * np.sin(phi) + ) # y in Cartesian (real-space) + x3 = np.ndarray.flatten(z) # z in Cartesian (real-space) + chunk = 100000 + if tot > chunk * 2: + update_progress(0) + for i in range(0, tot, chunk): + end = i + chunk + end = min(end, tot) + slc = slice(i, end) + pos.x1 = x1[slc] + pos.x2 = x2[slc] + pos.x3 = x3[slc] + + ## Call tracer service + res = tracer.service.magneticField(pos, config) + + Bx[slc] = res.field.x1 + By[slc] = res.field.x2 + Bz[slc] = res.field.x3 + + if tot > chunk * 2: + update_progress((i + 1) / tot) + ## Reshape to 3d array + Bx = Bx.reshape((nx, ny, nz)) + By = By.reshape((nx, ny, nz)) + Bz = Bz.reshape((nx, ny, nz)) + + ## Convert to cylindrical coordinates + Br = Bx * np.cos(phi) + By * np.sin(phi) + Bphi = -Bx * np.sin(phi) + By * np.cos(phi) + + ## Save so we don't have to do this every time. + with open(fname, pickle_write_mode) as f: + pickle.dump([Br, Bphi, Bz], f) + + if plot_poincare: + ## Poincare plot as done on the web services + ## Independent of the previously-made field. + + print("Making poincare plot (only works on IPP network)...") + ## Create configuration objects + config = tracer.types.MagneticConfig() + config.configIds = configuration + pos = tracer.types.Points3D() + + pos.x1 = np.linspace(5.6, 6.2, 80) + pos.x2 = np.zeros(80) + pos.x3 = np.zeros(80) + + poincare = tracer.types.PoincareInPhiPlane() + poincare.numPoints = 200 + poincare.phi0 = [ + 0.0 + ] ## This is where the poincare plane is (bean=0, triangle = pi/5.) + + task = tracer.types.Task() + task.step = 0.2 + task.poincare = poincare + + res = tracer.service.trace(pos, config, task, None, None) + + for i in range(0, len(res.surfs)): + plt.scatter( + res.surfs[i].points.x1, res.surfs[i].points.x3, color="black", s=0.1 + ) + plt.show() + + return Br, Bphi, Bz + + def plasma_field(r, phi, z, wout_file="wout.nc"): + """This uses EXTENDER via the IPP webservices to get the magnetic + field from the plasma given 3d arrrays for R, phi, and Z. Only + works on IPP network + + http://webservices.ipp-hgw.mpg.de/docs/extender.html + + Contact brendan.shanahan@ipp.mpg.de for questions + + """ + from osa import Client + import os.path + import pickle + + cl = Client("http://esb.ipp-hgw.mpg.de:8280/services/Extender?wsdl") + vmecURL = "http://svvmec1.ipp-hgw.mpg.de:8080/vmecrest/v1/w7x_ref_1/wout.nc" + + nx = r.shape[0] + ny = phi.shape[1] + nz = z.shape[2] + + # create (standardized) file name for saving/loading magnetic field. + fname = "B.w7x_plasma_field.{}.{}.{}.{:.2f}-{:.2f}.{:.2f}-{:.2f}.{:.2f}-{:.2f}.dat".format( + nx, + ny, + nz, + r[0, 0, 0], + r[-1, 0, 0], + phi[0, 0, 0], + phi[0, -1, 0], + z[0, 0, 0], + z[0, 0, -1], + ) + + if os.path.isfile(fname): + print("Saved field found, loading from: ", fname) + with open(fname, pickle_read_mode) as f: + Br, Bphi, Bz = pickle.load(f) + else: + print( + "No saved plasma field found -- (re)calculating (must be on IPP network for this to work...)" + ) + print( + "Calculating plasma field for Wendelstein 7-X; nx = ", + nx, + " ny = ", + ny, + " nz = ", + nz, + ) + print( + "This part takes AGES... estimate: ", + nx * ny * nz / 52380.0, + " minutes.", + ) + + points = cl.types.Points3D() + + ## Extender uses cylindrical coordinates, no need to convert, just flatten. + points.x1 = np.ndarray.flatten(r) # x in Cylindrical + points.x2 = np.ndarray.flatten(phi) # y in Cylindrical + points.x3 = np.ndarray.flatten(z) # z in Cylindrical + + ## call EXTENDER on web services + # if not (os.path.isfile(wout_file)): + plasmafield = cl.service.getPlasmaField(None, vmecURL, points, None) + # else: + # plasmafield = cl.service.getPlasmaField(wout, None, points, None) + + ## Reshape to 3d array + Br = np.ndarray.reshape(np.asarray(plasmafield.x1), (nx, ny, nz)) + Bphi = np.ndarray.reshape(np.asarray(plasmafield.x2), (nx, ny, nz)) + Bz = np.ndarray.reshape(np.asarray(plasmafield.x3), (nx, ny, nz)) + + ## Save so we don't have to do this every time. + with open(fname, pickle_write_mode) as f: + pickle.dump([Br, Bphi, Bz], f) + + return Br, Bphi, Bz + + def magnetic_axis(self, phi_axis=0, configuration=0): + from osa import Client + + tracer = Client("http://esb.ipp-hgw.mpg.de:8280/services/FieldLineProxy?wsdl") + + config = tracer.types.MagneticConfig() + config.configIds = configuration + settings = tracer.types.AxisSettings() + res = tracer.service.findAxis(0.05, config, settings) + + magnetic_axis_x = np.asarray(res.axis.vertices.x1) # (m) + magnetic_axis_y = np.asarray( + res.axis.vertices.x2 + ) # (m) -- REAL SPACE from an arbitrary start point + magnetic_axis_z = np.asarray(res.axis.vertices.x3) # (m) + magnetic_axis_rmaj = np.sqrt( + magnetic_axis_x ** 2 + magnetic_axis_y ** 2 + magnetic_axis_z ** 2 + ) + + magnetic_axis_r = np.sqrt( + np.asarray(magnetic_axis_x) ** 2 + np.asarray(magnetic_axis_y ** 2) + ) + magnetic_axis_phi = np.arctan(magnetic_axis_y / magnetic_axis_x) + + index = np.where( + (magnetic_axis_phi >= 0.97 * phi_axis) + & (magnetic_axis_phi <= 1.03 * phi_axis) + ) + index = index[0] + + return np.asarray([magnetic_axis_r[index], magnetic_axis_z[index]])[:, 0] + + def Bxfunc(self, x, z, phi): + phi = np.mod(phi, 2.0 * np.pi) + return self.br_interp((x, phi, z)) + + def Bzfunc(self, x, z, phi): + phi = np.mod(phi, 2.0 * np.pi) + return self.bz_interp((x, phi, z)) + + def Byfunc(self, x, z, phi): + phi = np.mod(phi, 2.0 * np.pi) + # Interpolate to get flux surface normalised psi + return self.bphi_interp((x, phi, z)) + + def Rfunc(self, x, z, phi): + phi = np.mod(phi, 2.0 * np.pi) + return x + + +class W7X_VMEC(MagneticField): + def __init__( + self, + nx=512, + ny=32, + nz=512, + x_range=(4.05, 6.55), + z_range=(-1.35, 1, 35), + phi_range=(0, 2 * np.pi), + vmec_id="w7x_ref_171", + ): + from scipy.interpolate import RegularGridInterpolator + + self.nx = nx + self.ny = ny + self.nz = nz + ## create 1D arrays of cylindrical coordinates + r = np.linspace(x_range[0], x_range[-1], nx) + phi = np.linspace(phi_range[0], phi_range[-1], ny) + z = np.linspace(z_range[0], z_range[-1], nz) + + ## make those 1D arrays 3D + rarray, yarray, zarray = np.meshgrid(r, phi, z, indexing="ij") + + ## call vacuum field values + b_vmec = self.field_values(rarray, yarray, zarray, vmec_id) + Bx_vmec = b_vmec[0] + By_vmec = b_vmec[1] + Bz_vmec = b_vmec[2] + + # Now we have a field and regular grid in (R,Z,phi) so + # we can get an interpolation function in 3D + points = (r, phi, z) + + self.br_interp = RegularGridInterpolator( + points, Bx_vmec, bounds_error=False, fill_value=0.0 + ) + self.bz_interp = RegularGridInterpolator( + points, Bz_vmec, bounds_error=False, fill_value=0.0 + ) + self.bphi_interp = RegularGridInterpolator( + points, By_vmec, bounds_error=False, fill_value=1.0 + ) + + def field_values(self, r, phi, z, vmec_id="w7x_ref_171"): + from osa import Client + + vmec = Client("http://esb:8280/services/vmec_v5?wsdl") + + pos = vmec.types.Points3D() + + pos.x1 = np.ndarray.flatten( + np.ones((self.nx, self.ny, self.nz)) * r * np.cos(phi) + ) # x in Cartesian (real-space) + pos.x2 = np.ndarray.flatten( + np.ones((self.nx, self.ny, self.nz)) * r * np.sin(phi) + ) # y in Cartesian (real-space) + pos.x3 = np.ndarray.flatten(z) # z in Cartesian (real-space) + b = vmec.service.magneticField(str(vmec_id), pos) + + ## Reshape to 3d array + Bx = np.ndarray.reshape(np.asarray(b.field.x1), (self.nx, self.ny, self.nz)) + By = np.ndarray.reshape(np.asarray(b.field.x2), (self.nx, self.ny, self.nz)) + Bz = np.ndarray.reshape(np.asarray(b.field.x3), (self.nx, self.ny, self.nz)) + + ## Convert to cylindrical coordinates + Br = Bx * np.cos(phi) + By * np.sin(phi) + Bphi = -Bx * np.sin(phi) + By * np.cos(phi) + + return Br, Bphi, Bz + + def Bxfunc(self, x, z, phi): + phi = np.mod(phi, 2.0 * np.pi) + return self.br_interp((x, phi, z)) + + def Bzfunc(self, x, z, phi): + phi = np.mod(phi, 2.0 * np.pi) + return self.bz_interp((x, phi, z)) + + def Byfunc(self, x, z, phi): + phi = np.mod(phi, 2.0 * np.pi) + # Interpolate to get flux surface normalised psi + return self.bphi_interp((x, phi, z)) + + def Rfunc(self, x, z, phi): + phi = np.mod(phi, 2.0 * np.pi) + return x diff --git a/tools/pylib/zoidberg/poloidal_grid.py b/tools/pylib/zoidberg/poloidal_grid.py index 3be009b529..5616cc86cd 100644 --- a/tools/pylib/zoidberg/poloidal_grid.py +++ b/tools/pylib/zoidberg/poloidal_grid.py @@ -397,6 +397,9 @@ def findIndex(self, R, Z, tol=1e-10, show=False): plt.plot(self.R, self.Z, ".") plt.plot(R, Z, "x") + cnt = 0 + underrelax = 1 + print() while True: # Use Newton iteration to find the index # dR, dZ are the distance away from the desired point @@ -408,8 +411,18 @@ def findIndex(self, R, Z, tol=1e-10, show=False): # Check if close enough # Note: only check the points which are not in the boundary - if np.amax(mask * (dR ** 2 + dZ ** 2)) < tol: + val = np.amax(mask * (dR ** 2 + dZ ** 2)) + if val < tol: break + cnt += 1 + if cnt == 10: + underrelax = 1.5 + if cnt == 100: + underrelax = 2 + if cnt == 1000: + underrelax = 2.5 + if cnt == 10000: + underrelax = 3 # Calculate derivatives dRdx, dZdx = self.getCoordinate(xind, zind, dx=1) @@ -425,8 +438,8 @@ def findIndex(self, R, Z, tol=1e-10, show=False): # (y) (-dZ/dx dR/dx ) (dZ) / (dR/dx*dZ/dy - dR/dy*dZ/dx) determinant = dRdx * dZdz - dRdz * dZdx - xind -= mask * ((dZdz * dR - dRdz * dZ) / determinant) - zind -= mask * ((dRdx * dZ - dZdx * dR) / determinant) + xind -= mask * ((dZdz * dR - dRdz * dZ) / determinant / underrelax) + zind -= mask * ((dRdx * dZ - dZdx * dR) / determinant / underrelax) # Re-check for boundary in_boundary = xind < 0.5 diff --git a/tools/pylib/zoidberg/zoidberg.py b/tools/pylib/zoidberg/zoidberg.py index 4829e8ec79..21dcae8696 100644 --- a/tools/pylib/zoidberg/zoidberg.py +++ b/tools/pylib/zoidberg/zoidberg.py @@ -66,6 +66,9 @@ def make_maps(grid, magnetic_field, nslice=1, quiet=False, **kwargs): nx = pol.nx nz = pol.nz + # Get number of guard cells (default 2) + mxg = kwargs.get("MXG", 2) + shape = (nx, ny, nz) # Coordinates of each grid point @@ -73,6 +76,7 @@ def make_maps(grid, magnetic_field, nslice=1, quiet=False, **kwargs): Z = np.zeros(shape) for j in range(ny): + pol, _ = grid.getPoloidalGrid(j) R[:, j, :] = pol.R Z[:, j, :] = pol.Z @@ -111,15 +115,14 @@ def make_maps(grid, magnetic_field, nslice=1, quiet=False, **kwargs): parallel_slices.append(ParallelSlice(offset, *fields)) # Total size of the progress bar - total_work = float((len(parallel_slices) - 1) * (ny - 1)) + total_work = len(parallel_slices) * ny # TODO: if axisymmetric, don't loop, do one slice and copy # TODO: restart tracing for adjacent offsets + if (not quiet) and (ny > 1): + update_progress(0, **kwargs) for slice_index, parallel_slice in enumerate(parallel_slices): for j in range(ny): - if (not quiet) and (ny > 1): - update_progress(float(slice_index * j) / total_work, **kwargs) - # Get this poloidal grid pol, ycoord = grid.getPoloidalGrid(j) @@ -154,6 +157,9 @@ def make_maps(grid, magnetic_field, nslice=1, quiet=False, **kwargs): parallel_slice.xt_prime[:, j, :] = xind parallel_slice.zt_prime[:, j, :] = zind + if (not quiet) and (ny > 1): + update_progress((slice_index * ny + j + 1) / total_work, **kwargs) + return maps @@ -258,6 +264,9 @@ def write_maps( # Add Rxy, Bxy metric["Rxy"] = maps["R"][:, :, 0] metric["Bxy"] = Bmag[:, :, 0] + else: + metric["Rxy"] = maps["R"] + metric["Bxy"] = Bmag with bdata.DataFile(gridfile, write=True, create=True, format=format) as f: ixseps = nx + 1