From 1d4cb83d031602b6a05a31072b6a2129872cce6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 22 Aug 2024 10:53:37 +0200 Subject: [PATCH 01/22] Get the ice-ocean stress into the ModelArrayRef system Introduces a function called getIceOceanStress into CGDynamicsKernel, which is called by getDG0Data when that function gets the right arguments. All derived DynamicsKernels (VPCGDynamicsKernel, BrittleCGDynamicsKernel, and FreeDriftDynamicsKernel) must implement getIceOceanStress. TODO: There's quite a bit of code repetition between the implementations in the different kernels, and VPCGDynamicsKernel and FreeDriftDynamicsKernel implement it in the exact same way. --- core/src/include/ModelComponent.hpp | 2 ++ core/src/include/gridNames.hpp | 3 +++ .../modules/DynamicsModule/BBMDynamics.cpp | 3 +++ .../modules/DynamicsModule/MEVPDynamics.cpp | 3 +++ core/src/modules/include/IDynamics.hpp | 6 ++++- dynamics/src/CGDynamicsKernel.cpp | 10 +++++++ .../src/include/BrittleCGDynamicsKernel.hpp | 26 +++++++++++++++++++ dynamics/src/include/CGDynamicsKernel.hpp | 2 ++ .../src/include/FreeDriftDynamicsKernel.hpp | 26 +++++++++++++++++++ dynamics/src/include/VPCGDynamicsKernel.hpp | 26 +++++++++++++++++++ 10 files changed, 106 insertions(+), 1 deletion(-) diff --git a/core/src/include/ModelComponent.hpp b/core/src/include/ModelComponent.hpp index 73e944c94..6616a23e9 100644 --- a/core/src/include/ModelComponent.hpp +++ b/core/src/include/ModelComponent.hpp @@ -64,6 +64,8 @@ namespace Protected { inline constexpr TextTag WIND_V = "WIND_V"; // y(north)-ward component of wind, m s⁻¹ inline constexpr TextTag ICE_U = "ICE_U"; // x(east)-ward ice velocity, m s⁻¹ inline constexpr TextTag ICE_V = "ICE_V"; // y(north)-ward ice velocity, m s⁻¹ + inline constexpr TextTag IO_STRESS_U = "IO_STRESS_U"; // x(east)-ward ice-ocean stress, Pa + inline constexpr TextTag IO_STRESS_V = "IO_STRESS_V"; // y(north)-ward ice-ocean stress, Pa // Slab ocean fields inline constexpr TextTag SLAB_SST = "SLAB_SST"; // Slab ocean sea surface temperature, ˚C inline constexpr TextTag SLAB_SSS = "SLAB_SSS"; // Slab ocean sea surface salinity, ˚C diff --git a/core/src/include/gridNames.hpp b/core/src/include/gridNames.hpp index 82ddcde61..6fc8a6f7a 100644 --- a/core/src/include/gridNames.hpp +++ b/core/src/include/gridNames.hpp @@ -29,6 +29,9 @@ static const std::string vWindName = "vwind"; static const std::string uOceanName = "uocean"; static const std::string vOceanName = "vocean"; +static const std::string uIOStressName = "uiostress"; +static const std::string vIOStressName = "viostress"; + static const std::string coordsName = "coords"; static const std::string latitudeName = "latitude"; static const std::string longitudeName = "longitude"; diff --git a/core/src/modules/DynamicsModule/BBMDynamics.cpp b/core/src/modules/DynamicsModule/BBMDynamics.cpp index 935d8aecc..f6ada9cd0 100644 --- a/core/src/modules/DynamicsModule/BBMDynamics.cpp +++ b/core/src/modules/DynamicsModule/BBMDynamics.cpp @@ -141,6 +141,9 @@ void BBMDynamics::update(const TimestepTime& tst) uice = kernel.getDG0Data(uName); vice = kernel.getDG0Data(vName); + + taux = kernel.getDG0Data(uIOStressName); + tauy = kernel.getDG0Data(vIOStressName); } // All data for prognostic output diff --git a/core/src/modules/DynamicsModule/MEVPDynamics.cpp b/core/src/modules/DynamicsModule/MEVPDynamics.cpp index 823fc8f16..f8cdde95f 100644 --- a/core/src/modules/DynamicsModule/MEVPDynamics.cpp +++ b/core/src/modules/DynamicsModule/MEVPDynamics.cpp @@ -108,6 +108,9 @@ void MEVPDynamics::update(const TimestepTime& tst) uice = kernel.getDG0Data(uName); vice = kernel.getDG0Data(vName); + + taux = kernel.getDG0Data(uIOStressName); + tauy = kernel.getDG0Data(vIOStressName); } ModelState MEVPDynamics::getStateRecursive(const OutputSpec& os) const diff --git a/core/src/modules/include/IDynamics.hpp b/core/src/modules/include/IDynamics.hpp index 29d9423d2..9ec1ed21e 100644 --- a/core/src/modules/include/IDynamics.hpp +++ b/core/src/modules/include/IDynamics.hpp @@ -23,6 +23,8 @@ class IDynamics : public ModelComponent { : uice(ModelArray::Type::H) , vice(ModelArray::Type::H) , damage(ModelArray::Type::H) + , taux(ModelArray::Type::H) + , tauy(ModelArray::Type::H) , hice(getStore()) , cice(getStore()) , hsnow(getStore()) @@ -76,12 +78,14 @@ class IDynamics : public ModelComponent { HField vice; // Updated damage array HField damage; + // Ice-ocean stress (for the coupler, mostly) + HField taux; + HField tauy; // References to the DG0 finite volume data arrays ModelArrayRef hice; ModelArrayRef cice; ModelArrayRef hsnow; ModelArrayRef damage0; - // ModelArrayRef damage; // References to the forcing velocity arrays ModelArrayRef uwind; diff --git a/dynamics/src/CGDynamicsKernel.cpp b/dynamics/src/CGDynamicsKernel.cpp index fd779060d..2124519d5 100644 --- a/dynamics/src/CGDynamicsKernel.cpp +++ b/dynamics/src/CGDynamicsKernel.cpp @@ -98,6 +98,16 @@ ModelArray CGDynamicsKernel::getDG0Data(const std::string& name) co DGVector vtmp(*smesh); Nextsim::Interpolations::CG2DG(*smesh, vtmp, v); return DGModelArray::dg2ma(vtmp, data); + } else if (name == uIOStressName) { + ModelArray data(ModelArray::Type::U); + DGVector utmp(*smesh); + Nextsim::Interpolations::CG2DG(*smesh, utmp, getIceOceanStress(name)); + return DGModelArray::dg2ma(utmp, data); + } else if (name == vIOStressName) { + ModelArray data(ModelArray::Type::V); + DGVector vtmp(*smesh); + Nextsim::Interpolations::CG2DG(*smesh, vtmp, getIceOceanStress(name)); + return DGModelArray::dg2ma(vtmp, data); } else { return DynamicsKernel::getDG0Data(name); } diff --git a/dynamics/src/include/BrittleCGDynamicsKernel.hpp b/dynamics/src/include/BrittleCGDynamicsKernel.hpp index 99254598a..089490094 100644 --- a/dynamics/src/include/BrittleCGDynamicsKernel.hpp +++ b/dynamics/src/include/BrittleCGDynamicsKernel.hpp @@ -163,6 +163,32 @@ template class BrittleCGDynamicsKernel : public CGDynamicsKern } } + CGVector getIceOceanStress(const std::string& name) const override + { + CGVector taux, tauy; + taux.resizeLike(avgU); + tauy.resizeLike(avgV); + +#pragma omp parallel for + for (int i = 0; i < taux.rows(); ++i) { + double uOcnRel = avgU(i) - uOcean(i); + double vOcnRel = avgV(i) - vOcean(i); + double absocn = sqrt(SQR(uOcnRel) + SQR(vOcnRel)); + + taux(i) = params.F_ocean * absocn * uOcnRel; + tauy(i) = params.F_ocean * absocn * vOcnRel; + } + + if (name == uIOStressName) { + return taux; + } else if (name == vIOStressName) { + return tauy; + } else { + throw std::logic_error(std::string(__func__) + " called with an unknown argument " + + name + ". Only " + uIOStressName + " and " + vIOStressName + " are supported\n"); + } + } + protected: CGVector avgU; CGVector avgV; diff --git a/dynamics/src/include/CGDynamicsKernel.hpp b/dynamics/src/include/CGDynamicsKernel.hpp index d2d376f4f..37abdf3b4 100644 --- a/dynamics/src/include/CGDynamicsKernel.hpp +++ b/dynamics/src/include/CGDynamicsKernel.hpp @@ -47,6 +47,8 @@ class CGDynamicsKernel : public DynamicsKernel { void applyBoundaries() override; void prepareAdvection() override; + virtual CGVector getIceOceanStress(const std::string& name) const = 0; + protected: void addStressTensorCell(const size_t eid, const size_t cx, const size_t cy); void dirichletZero(CGVector&) const; diff --git a/dynamics/src/include/FreeDriftDynamicsKernel.hpp b/dynamics/src/include/FreeDriftDynamicsKernel.hpp index f007ecfb7..7851ebb0b 100644 --- a/dynamics/src/include/FreeDriftDynamicsKernel.hpp +++ b/dynamics/src/include/FreeDriftDynamicsKernel.hpp @@ -69,6 +69,32 @@ template class FreeDriftDynamicsKernel : public CGDynamicsKern + NansenNumber * (-uAtmos(i) * sinOceanAngle + vAtmos(i) * cosOceanAngle); } } + + CGVector getIceOceanStress(const std::string& name) const override + { + CGVector taux, tauy; + taux.resizeLike(u); + tauy.resizeLike(v); + +#pragma omp parallel for + for (int i = 0; i < taux.rows(); ++i) { + double uOcnRel = u(i) - uOcean(i); + double vOcnRel = v(i) - vOcean(i); + double absocn = sqrt(SQR(uOcnRel) + SQR(vOcnRel)); + + taux(i) = params.F_ocean * absocn * uOcnRel; + tauy(i) = params.F_ocean * absocn * vOcnRel; + } + + if (name == uIOStressName) { + return taux; + } else if (name == vIOStressName) { + return tauy; + } else { + throw std::logic_error(std::string(__func__) + " called with an unknown argument " + name + + ". Only " + uIOStressName + " and " + vIOStressName + " are supported\n"); + } + } }; } /* namespace Nextsim */ diff --git a/dynamics/src/include/VPCGDynamicsKernel.hpp b/dynamics/src/include/VPCGDynamicsKernel.hpp index badc3c33c..e67465d44 100644 --- a/dynamics/src/include/VPCGDynamicsKernel.hpp +++ b/dynamics/src/include/VPCGDynamicsKernel.hpp @@ -89,6 +89,32 @@ template class VPCGDynamicsKernel : public CGDynamicsKernel::update(tst); } + CGVector getIceOceanStress(const std::string& name) const override + { + CGVector taux, tauy; + taux.resizeLike(u); + tauy.resizeLike(v); + +#pragma omp parallel for + for (int i = 0; i < taux.rows(); ++i) { + double uOcnRel = u(i) - uOcean(i); + double vOcnRel = v(i) - vOcean(i); + double absocn = sqrt(SQR(uOcnRel) + SQR(vOcnRel)); + + taux(i) = params.F_ocean * absocn * uOcnRel; + tauy(i) = params.F_ocean * absocn * vOcnRel; + } + + if (name == uIOStressName) { + return taux; + } else if (name == vIOStressName) { + return tauy; + } else { + throw std::logic_error(std::string(__func__) + " called with an unknown argument " + + name + ". Only " + uIOStressName + " and " + vIOStressName + " are supported\n"); + } + } + protected: StressUpdateStep& stressStep; const VPParameters& params; From ae43862bc6260b1d82f2f099e62c50098c5d6200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 22 Aug 2024 13:25:44 +0200 Subject: [PATCH 02/22] Ice-ocean stress updates In BrittleCGDynamicsKernel and FreeDriftDynamicsKernel the ice-ocean stress calculation now follows the Hunke and Dukowicz formulation, taking the oceanic turning angle into account. --- dynamics/src/include/BrittleCGDynamicsKernel.hpp | 10 +++++----- dynamics/src/include/FreeDriftDynamicsKernel.hpp | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/dynamics/src/include/BrittleCGDynamicsKernel.hpp b/dynamics/src/include/BrittleCGDynamicsKernel.hpp index 089490094..a09e2965e 100644 --- a/dynamics/src/include/BrittleCGDynamicsKernel.hpp +++ b/dynamics/src/include/BrittleCGDynamicsKernel.hpp @@ -171,12 +171,12 @@ template class BrittleCGDynamicsKernel : public CGDynamicsKern #pragma omp parallel for for (int i = 0; i < taux.rows(); ++i) { - double uOcnRel = avgU(i) - uOcean(i); - double vOcnRel = avgV(i) - vOcean(i); - double absocn = sqrt(SQR(uOcnRel) + SQR(vOcnRel)); + const double uOceanRel = uOcean(i) - avgU(i); + const double vOceanRel = vOcean(i) - avgV(i); + const double cPrime = params.F_ocean * std::hypot(uOceanRel, vOceanRel); - taux(i) = params.F_ocean * absocn * uOcnRel; - tauy(i) = params.F_ocean * absocn * vOcnRel; + taux(i) = cPrime * (uOceanRel * cosOceanAngle - vOceanRel * sinOceanAngle); + tauy(i) = cPrime * (vOceanRel * cosOceanAngle + uOceanRel * sinOceanAngle); } if (name == uIOStressName) { diff --git a/dynamics/src/include/FreeDriftDynamicsKernel.hpp b/dynamics/src/include/FreeDriftDynamicsKernel.hpp index 7851ebb0b..2ebd7d160 100644 --- a/dynamics/src/include/FreeDriftDynamicsKernel.hpp +++ b/dynamics/src/include/FreeDriftDynamicsKernel.hpp @@ -78,12 +78,12 @@ template class FreeDriftDynamicsKernel : public CGDynamicsKern #pragma omp parallel for for (int i = 0; i < taux.rows(); ++i) { - double uOcnRel = u(i) - uOcean(i); - double vOcnRel = v(i) - vOcean(i); - double absocn = sqrt(SQR(uOcnRel) + SQR(vOcnRel)); + const double uOceanRel = uOcean(i) - u(i); + const double vOceanRel = vOcean(i) - v(i); + const double cPrime = params.F_ocean * std::hypot(uOceanRel, vOceanRel); - taux(i) = params.F_ocean * absocn * uOcnRel; - tauy(i) = params.F_ocean * absocn * vOcnRel; + taux(i) = cPrime * (uOceanRel * cosOceanAngle - vOceanRel * sinOceanAngle); + tauy(i) = cPrime * (vOceanRel * cosOceanAngle + uOceanRel * sinOceanAngle); } if (name == uIOStressName) { From 62c9395fa7c70055a93136e7f20a32bb9647f746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 22 Aug 2024 14:48:05 +0200 Subject: [PATCH 03/22] Add ice-ocean stresses to config output outputs Just register taux and tauy in the ModelArrayRef system and add a sensible name in ProtectedArrayNames.ipp. --- core/src/modules/include/IDynamics.hpp | 2 ++ core/src/modules/include/ProtectedArrayNames.ipp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/core/src/modules/include/IDynamics.hpp b/core/src/modules/include/IDynamics.hpp index 9ec1ed21e..33c7e17aa 100644 --- a/core/src/modules/include/IDynamics.hpp +++ b/core/src/modules/include/IDynamics.hpp @@ -36,6 +36,8 @@ class IDynamics : public ModelComponent { , m_usesDamage(usesDamageIn) { getStore().registerArray(Shared::DAMAGE, &damage, RW); + getStore().registerArray(Protected::IO_STRESS_U, &taux, RO); + getStore().registerArray(Protected::IO_STRESS_V, &tauy, RO); } virtual ~IDynamics() = default; diff --git a/core/src/modules/include/ProtectedArrayNames.ipp b/core/src/modules/include/ProtectedArrayNames.ipp index f273e9d9f..8dacc6750 100644 --- a/core/src/modules/include/ProtectedArrayNames.ipp +++ b/core/src/modules/include/ProtectedArrayNames.ipp @@ -40,3 +40,5 @@ { "sss_slab", "SLAB_SSS" }, // Slab ocean surface salinity PSU { "qdw", "SLAB_QDW" }, // Slab ocean temperature nudging heat flux, W m⁻² { "fdw", "SLAB_FDW" }, // Slab ocean salinity nudging water flux, kg s⁻¹ m⁻² + { "taux", "IO_STRESS_U" }, // Ice-ocean stress x(east) direction, Pa + { "tauy", "IO_STRESS_V" }, // Ice-ocean stress x(east) direction, Pa From 9ee8c56a5f0b3e8f076dd13210a9be8172fd3754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Fri, 6 Dec 2024 06:18:16 +0100 Subject: [PATCH 04/22] Fixes to getIceOceanStress functions They didn't work properly because of changes in the structure of params. --- dynamics/src/include/BrittleCGDynamicsKernel.hpp | 6 ++++-- dynamics/src/include/FreeDriftDynamicsKernel.hpp | 10 ++++++---- dynamics/src/include/VPCGDynamicsKernel.hpp | 8 +++++--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/dynamics/src/include/BrittleCGDynamicsKernel.hpp b/dynamics/src/include/BrittleCGDynamicsKernel.hpp index a09e2965e..d366dc1c7 100644 --- a/dynamics/src/include/BrittleCGDynamicsKernel.hpp +++ b/dynamics/src/include/BrittleCGDynamicsKernel.hpp @@ -1,7 +1,7 @@ /*! * @file BrittleCGDynamicsKernel.hpp * - * @date 19 Nov 2024 + * @date 06 Dec 2024 * @author Tim Spain * @author Einar Ólason */ @@ -169,11 +169,13 @@ template class BrittleCGDynamicsKernel : public CGDynamicsKern taux.resizeLike(avgU); tauy.resizeLike(avgV); + const double FOcean = params.COcean * params.rhoOcean; + #pragma omp parallel for for (int i = 0; i < taux.rows(); ++i) { const double uOceanRel = uOcean(i) - avgU(i); const double vOceanRel = vOcean(i) - avgV(i); - const double cPrime = params.F_ocean * std::hypot(uOceanRel, vOceanRel); + const double cPrime = FOcean * std::hypot(uOceanRel, vOceanRel); taux(i) = cPrime * (uOceanRel * cosOceanAngle - vOceanRel * sinOceanAngle); tauy(i) = cPrime * (vOceanRel * cosOceanAngle + uOceanRel * sinOceanAngle); diff --git a/dynamics/src/include/FreeDriftDynamicsKernel.hpp b/dynamics/src/include/FreeDriftDynamicsKernel.hpp index 2ebd7d160..b7972fa09 100644 --- a/dynamics/src/include/FreeDriftDynamicsKernel.hpp +++ b/dynamics/src/include/FreeDriftDynamicsKernel.hpp @@ -4,7 +4,7 @@ * Implementation of "classic free drift", where we ignore all \rho h terms in the momentum * equation. This is equivalent to assuming that the ice is very thin. * - * @date 19 Nov 2024 + * @date 06 Dec 2024 * @author Tim Spain * @author Einar Ólason */ @@ -76,11 +76,13 @@ template class FreeDriftDynamicsKernel : public CGDynamicsKern taux.resizeLike(u); tauy.resizeLike(v); + const double FOcean = params.COcean * params.rhoOcean; + #pragma omp parallel for for (int i = 0; i < taux.rows(); ++i) { const double uOceanRel = uOcean(i) - u(i); const double vOceanRel = vOcean(i) - v(i); - const double cPrime = params.F_ocean * std::hypot(uOceanRel, vOceanRel); + const double cPrime = FOcean * std::hypot(uOceanRel, vOceanRel); taux(i) = cPrime * (uOceanRel * cosOceanAngle - vOceanRel * sinOceanAngle); tauy(i) = cPrime * (vOceanRel * cosOceanAngle + uOceanRel * sinOceanAngle); @@ -91,8 +93,8 @@ template class FreeDriftDynamicsKernel : public CGDynamicsKern } else if (name == vIOStressName) { return tauy; } else { - throw std::logic_error(std::string(__func__) + " called with an unknown argument " + name - + ". Only " + uIOStressName + " and " + vIOStressName + " are supported\n"); + throw std::logic_error(std::string(__func__) + " called with an unknown argument " + + name + ". Only " + uIOStressName + " and " + vIOStressName + " are supported\n"); } } }; diff --git a/dynamics/src/include/VPCGDynamicsKernel.hpp b/dynamics/src/include/VPCGDynamicsKernel.hpp index e67465d44..2db4ac630 100644 --- a/dynamics/src/include/VPCGDynamicsKernel.hpp +++ b/dynamics/src/include/VPCGDynamicsKernel.hpp @@ -1,7 +1,7 @@ /*! * @file VPCGDynamicsKernel.hpp * - * @date 19 Nov 2024 + * @date 06 Dec 2024 * @author Tim Spain */ @@ -95,14 +95,16 @@ template class VPCGDynamicsKernel : public CGDynamicsKernel Date: Fri, 6 Dec 2024 06:51:42 +0100 Subject: [PATCH 05/22] Collect looping logic of getIceOceanStress in base class CGDynamicsKernel now handles the basic logic of getIceOceanStress and calls getIceOceanStressElement for the physical implementation of each child class. Those are still very similar to each other, but not the same. Maybe one day they will be, and then we need to reconsider this. --- .../src/include/BrittleCGDynamicsKernel.hpp | 32 ++++++------------- dynamics/src/include/CGDynamicsKernel.hpp | 19 +++++++++-- .../src/include/FreeDriftDynamicsKernel.hpp | 32 ++++++------------- dynamics/src/include/VPCGDynamicsKernel.hpp | 32 ++++++------------- 4 files changed, 47 insertions(+), 68 deletions(-) diff --git a/dynamics/src/include/BrittleCGDynamicsKernel.hpp b/dynamics/src/include/BrittleCGDynamicsKernel.hpp index d366dc1c7..bedd45270 100644 --- a/dynamics/src/include/BrittleCGDynamicsKernel.hpp +++ b/dynamics/src/include/BrittleCGDynamicsKernel.hpp @@ -163,32 +163,20 @@ template class BrittleCGDynamicsKernel : public CGDynamicsKern } } - CGVector getIceOceanStress(const std::string& name) const override + double getIceOceanStressElement(const std::string& name, const int i) const override { - CGVector taux, tauy; - taux.resizeLike(avgU); - tauy.resizeLike(avgV); - const double FOcean = params.COcean * params.rhoOcean; -#pragma omp parallel for - for (int i = 0; i < taux.rows(); ++i) { - const double uOceanRel = uOcean(i) - avgU(i); - const double vOceanRel = vOcean(i) - avgV(i); - const double cPrime = FOcean * std::hypot(uOceanRel, vOceanRel); - - taux(i) = cPrime * (uOceanRel * cosOceanAngle - vOceanRel * sinOceanAngle); - tauy(i) = cPrime * (vOceanRel * cosOceanAngle + uOceanRel * sinOceanAngle); - } + const double uOceanRel = uOcean(i) - avgU(i); + const double vOceanRel = vOcean(i) - avgV(i); + const double cPrime = FOcean * std::hypot(uOceanRel, vOceanRel); - if (name == uIOStressName) { - return taux; - } else if (name == vIOStressName) { - return tauy; - } else { - throw std::logic_error(std::string(__func__) + " called with an unknown argument " - + name + ". Only " + uIOStressName + " and " + vIOStressName + " are supported\n"); - } + if (name == uIOStressName) + return cPrime * (uOceanRel * cosOceanAngle - vOceanRel * sinOceanAngle); + else if (name == vIOStressName) + return cPrime * (vOceanRel * cosOceanAngle + uOceanRel * sinOceanAngle); + else + return std::nan(""); } protected: diff --git a/dynamics/src/include/CGDynamicsKernel.hpp b/dynamics/src/include/CGDynamicsKernel.hpp index 37abdf3b4..a3bf5670b 100644 --- a/dynamics/src/include/CGDynamicsKernel.hpp +++ b/dynamics/src/include/CGDynamicsKernel.hpp @@ -1,7 +1,7 @@ /*! * @file CGDynamicsKernel.hpp * - * @date Jan 31, 2024 + * @date 06 Dec 2024 * @author Tim Spain */ @@ -47,7 +47,22 @@ class CGDynamicsKernel : public DynamicsKernel { void applyBoundaries() override; void prepareAdvection() override; - virtual CGVector getIceOceanStress(const std::string& name) const = 0; + virtual inline double getIceOceanStressElement(const std::string& name, const int i) const = 0; + CGVector getIceOceanStress(const std::string& name) const + { + if (name != uIOStressName && name != vIOStressName) + throw std::logic_error(std::string(__func__) + " called with an unknown argument " + + name + ". Only " + uIOStressName + " and " + vIOStressName + " are supported\n"); + + CGVector tau; + tau.resizeLike(u); + +#pragma omp parallel for + for (int i = 0; i < tau.rows(); ++i) + tau(i) = getIceOceanStressElement(name, i); + + return tau; + } protected: void addStressTensorCell(const size_t eid, const size_t cx, const size_t cy); diff --git a/dynamics/src/include/FreeDriftDynamicsKernel.hpp b/dynamics/src/include/FreeDriftDynamicsKernel.hpp index b7972fa09..973f21395 100644 --- a/dynamics/src/include/FreeDriftDynamicsKernel.hpp +++ b/dynamics/src/include/FreeDriftDynamicsKernel.hpp @@ -70,32 +70,20 @@ template class FreeDriftDynamicsKernel : public CGDynamicsKern } } - CGVector getIceOceanStress(const std::string& name) const override + double getIceOceanStressElement(const std::string& name, const int i) const override { - CGVector taux, tauy; - taux.resizeLike(u); - tauy.resizeLike(v); - const double FOcean = params.COcean * params.rhoOcean; -#pragma omp parallel for - for (int i = 0; i < taux.rows(); ++i) { - const double uOceanRel = uOcean(i) - u(i); - const double vOceanRel = vOcean(i) - v(i); - const double cPrime = FOcean * std::hypot(uOceanRel, vOceanRel); + const double uOceanRel = uOcean(i) - u(i); + const double vOceanRel = vOcean(i) - v(i); + const double cPrime = FOcean * std::hypot(uOceanRel, vOceanRel); - taux(i) = cPrime * (uOceanRel * cosOceanAngle - vOceanRel * sinOceanAngle); - tauy(i) = cPrime * (vOceanRel * cosOceanAngle + uOceanRel * sinOceanAngle); - } - - if (name == uIOStressName) { - return taux; - } else if (name == vIOStressName) { - return tauy; - } else { - throw std::logic_error(std::string(__func__) + " called with an unknown argument " - + name + ". Only " + uIOStressName + " and " + vIOStressName + " are supported\n"); - } + if (name == uIOStressName) + return cPrime * (uOceanRel * cosOceanAngle - vOceanRel * sinOceanAngle); + else if (name == vIOStressName) + return cPrime * (vOceanRel * cosOceanAngle + uOceanRel * sinOceanAngle); + else + return std::nan(""); } }; } /* namespace Nextsim */ diff --git a/dynamics/src/include/VPCGDynamicsKernel.hpp b/dynamics/src/include/VPCGDynamicsKernel.hpp index 2db4ac630..b7c51d8bc 100644 --- a/dynamics/src/include/VPCGDynamicsKernel.hpp +++ b/dynamics/src/include/VPCGDynamicsKernel.hpp @@ -89,32 +89,20 @@ template class VPCGDynamicsKernel : public CGDynamicsKernel::update(tst); } - CGVector getIceOceanStress(const std::string& name) const override + double getIceOceanStressElement(const std::string& name, const int i) const override { - CGVector taux, tauy; - taux.resizeLike(u); - tauy.resizeLike(v); - const double FOcean = params.COcean * params.rhoOcean; -#pragma omp parallel for - for (int i = 0; i < taux.rows(); ++i) { - double uOcnRel = u(i) - uOcean(i); - double vOcnRel = v(i) - vOcean(i); - double absocn = sqrt(SQR(uOcnRel) + SQR(vOcnRel)); + double uOcnRel = u(i) - uOcean(i); + double vOcnRel = v(i) - vOcean(i); + double absocn = sqrt(SQR(uOcnRel) + SQR(vOcnRel)); - taux(i) = FOcean * absocn * uOcnRel; - tauy(i) = FOcean * absocn * vOcnRel; - } - - if (name == uIOStressName) { - return taux; - } else if (name == vIOStressName) { - return tauy; - } else { - throw std::logic_error(std::string(__func__) + " called with an unknown argument " - + name + ". Only " + uIOStressName + " and " + vIOStressName + " are supported\n"); - } + if (name == uIOStressName) + return FOcean * absocn * uOcnRel; + else if (name == vIOStressName) + return FOcean * absocn * vOcnRel; + else + return std::nan(""); } protected: From 0d813831038d12a34570525593236c2e284c3ec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Fri, 6 Dec 2024 10:31:21 +0100 Subject: [PATCH 06/22] Address Tim's comments on github See PR #754. Minor renaming and a different way of producing an NaN. --- core/src/include/ModelComponent.hpp | 6 +++--- core/src/modules/include/IDynamics.hpp | 6 +++--- core/src/modules/include/ProtectedArrayNames.ipp | 4 ++-- dynamics/src/include/BrittleCGDynamicsKernel.hpp | 2 +- dynamics/src/include/FreeDriftDynamicsKernel.hpp | 2 +- dynamics/src/include/VPCGDynamicsKernel.hpp | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/src/include/ModelComponent.hpp b/core/src/include/ModelComponent.hpp index 6616a23e9..26f8ae0a0 100644 --- a/core/src/include/ModelComponent.hpp +++ b/core/src/include/ModelComponent.hpp @@ -1,7 +1,7 @@ /*! * @file ModelComponent.hpp * - * @date 1 Jul 2024 + * @date 06 Dec 2024 * @author Tim Spain * @author Einar Ólason */ @@ -64,8 +64,8 @@ namespace Protected { inline constexpr TextTag WIND_V = "WIND_V"; // y(north)-ward component of wind, m s⁻¹ inline constexpr TextTag ICE_U = "ICE_U"; // x(east)-ward ice velocity, m s⁻¹ inline constexpr TextTag ICE_V = "ICE_V"; // y(north)-ward ice velocity, m s⁻¹ - inline constexpr TextTag IO_STRESS_U = "IO_STRESS_U"; // x(east)-ward ice-ocean stress, Pa - inline constexpr TextTag IO_STRESS_V = "IO_STRESS_V"; // y(north)-ward ice-ocean stress, Pa + inline constexpr TextTag IO_STRESS_X = "IO_STRESS_X"; // x(east)-ward ice-ocean stress, Pa + inline constexpr TextTag IO_STRESS_Y = "IO_STRESS_Y"; // y(north)-ward ice-ocean stress, Pa // Slab ocean fields inline constexpr TextTag SLAB_SST = "SLAB_SST"; // Slab ocean sea surface temperature, ˚C inline constexpr TextTag SLAB_SSS = "SLAB_SSS"; // Slab ocean sea surface salinity, ˚C diff --git a/core/src/modules/include/IDynamics.hpp b/core/src/modules/include/IDynamics.hpp index 33c7e17aa..82c8614f4 100644 --- a/core/src/modules/include/IDynamics.hpp +++ b/core/src/modules/include/IDynamics.hpp @@ -1,7 +1,7 @@ /*! * @file IDynamics.hpp * - * @date 7 Sep 2023 + * @date 06 Dec 2024 * @author Tim Spain */ @@ -36,8 +36,8 @@ class IDynamics : public ModelComponent { , m_usesDamage(usesDamageIn) { getStore().registerArray(Shared::DAMAGE, &damage, RW); - getStore().registerArray(Protected::IO_STRESS_U, &taux, RO); - getStore().registerArray(Protected::IO_STRESS_V, &tauy, RO); + getStore().registerArray(Protected::IO_STRESS_X, &taux, RO); + getStore().registerArray(Protected::IO_STRESS_Y, &tauy, RO); } virtual ~IDynamics() = default; diff --git a/core/src/modules/include/ProtectedArrayNames.ipp b/core/src/modules/include/ProtectedArrayNames.ipp index 8dacc6750..7781eb7ad 100644 --- a/core/src/modules/include/ProtectedArrayNames.ipp +++ b/core/src/modules/include/ProtectedArrayNames.ipp @@ -40,5 +40,5 @@ { "sss_slab", "SLAB_SSS" }, // Slab ocean surface salinity PSU { "qdw", "SLAB_QDW" }, // Slab ocean temperature nudging heat flux, W m⁻² { "fdw", "SLAB_FDW" }, // Slab ocean salinity nudging water flux, kg s⁻¹ m⁻² - { "taux", "IO_STRESS_U" }, // Ice-ocean stress x(east) direction, Pa - { "tauy", "IO_STRESS_V" }, // Ice-ocean stress x(east) direction, Pa + { "taux", "IO_STRESS_X" }, // Ice-ocean stress x(east) direction, Pa + { "tauy", "IO_STRESS_Y" }, // Ice-ocean stress y(north) direction, Pa diff --git a/dynamics/src/include/BrittleCGDynamicsKernel.hpp b/dynamics/src/include/BrittleCGDynamicsKernel.hpp index bedd45270..ffe9cd353 100644 --- a/dynamics/src/include/BrittleCGDynamicsKernel.hpp +++ b/dynamics/src/include/BrittleCGDynamicsKernel.hpp @@ -176,7 +176,7 @@ template class BrittleCGDynamicsKernel : public CGDynamicsKern else if (name == vIOStressName) return cPrime * (vOceanRel * cosOceanAngle + uOceanRel * sinOceanAngle); else - return std::nan(""); + return std::numeric_limits::quiet_NaN(); } protected: diff --git a/dynamics/src/include/FreeDriftDynamicsKernel.hpp b/dynamics/src/include/FreeDriftDynamicsKernel.hpp index 973f21395..ccacacc08 100644 --- a/dynamics/src/include/FreeDriftDynamicsKernel.hpp +++ b/dynamics/src/include/FreeDriftDynamicsKernel.hpp @@ -83,7 +83,7 @@ template class FreeDriftDynamicsKernel : public CGDynamicsKern else if (name == vIOStressName) return cPrime * (vOceanRel * cosOceanAngle + uOceanRel * sinOceanAngle); else - return std::nan(""); + return std::numeric_limits::quiet_NaN(); } }; } /* namespace Nextsim */ diff --git a/dynamics/src/include/VPCGDynamicsKernel.hpp b/dynamics/src/include/VPCGDynamicsKernel.hpp index b7c51d8bc..8bb7149a4 100644 --- a/dynamics/src/include/VPCGDynamicsKernel.hpp +++ b/dynamics/src/include/VPCGDynamicsKernel.hpp @@ -102,7 +102,7 @@ template class VPCGDynamicsKernel : public CGDynamicsKernel::quiet_NaN(); } protected: From dec46a3c40837d511fc196628e70781e8f19e30c Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Tue, 3 Dec 2024 10:47:29 +0000 Subject: [PATCH 07/22] Set default timestep; allow calendar specification --- core/src/Xios.cpp | 20 ++++++++++++-------- core/src/include/Xios.hpp | 9 ++++++--- core/test/XiosAxis_test.cpp | 5 +---- core/test/XiosCalendar_test.cpp | 3 ++- core/test/XiosDomain_test.cpp | 5 +---- core/test/XiosField_test.cpp | 8 ++------ core/test/XiosFile_test.cpp | 9 +++------ core/test/XiosGrid_test.cpp | 5 +---- core/test/XiosReadWrite_test.cpp | 7 +++---- 9 files changed, 31 insertions(+), 40 deletions(-) diff --git a/core/src/Xios.cpp b/core/src/Xios.cpp index 5bccdd3b7..86166795a 100644 --- a/core/src/Xios.cpp +++ b/core/src/Xios.cpp @@ -2,7 +2,7 @@ * @file Xios.cpp * @author Tom Meltzer * @author Joe Wallwork - * @date 19 Nov 2024 + * @date 03 Dec 2024 * @brief XIOS interface implementation * @details * @@ -45,13 +45,17 @@ void enableXios() } /*! - * Constructor + * Constructor: Configure an XIOS server * - * Configure an XIOS server + * @param timestep Timestep to use for the model + * @param contextId ID for the XIOS context + * @param calendarType Type of calendar to use */ -Xios::Xios(const std::string contextId) +Xios::Xios(const std::string timestep, const std::string contextId, const std::string calendarType) { + _calendarType = calendarType; _contextId = contextId; + _timestep = Duration(timestep); configure(); } @@ -99,7 +103,7 @@ void Xios::configure() } //! Configure calendar settings -void Xios::configureServer(const std::string calendarType) +void Xios::configureServer() { // Initialize XIOS Server process and store MPI communicator clientId = "client"; @@ -116,10 +120,10 @@ void Xios::configureServer(const std::string calendarType) // Initialize calendar wrapper for 'nextSIM-DG' context cxios_get_current_calendar_wrapper(&clientCalendar); - cxios_set_calendar_wrapper_type(clientCalendar, calendarType.c_str(), calendarType.length()); - cxios_duration timestep { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 }; - cxios_set_calendar_wrapper_timestep(clientCalendar, timestep); + cxios_set_calendar_wrapper_type(clientCalendar, _calendarType.c_str(), _calendarType.length()); + cxios_set_calendar_wrapper_timestep(clientCalendar, convertDurationToXios(_timestep)); cxios_create_calendar(clientCalendar); + cxios_update_calendar_timestep(clientCalendar); } /*! diff --git a/core/src/include/Xios.hpp b/core/src/include/Xios.hpp index 6489bbed7..14b8515b1 100644 --- a/core/src/include/Xios.hpp +++ b/core/src/include/Xios.hpp @@ -2,7 +2,7 @@ * @file Xios.hpp * @author Tom Meltzer * @author Joe Wallwork - * @date 18 Nov 2024 + * @date 03 Dec 2024 * @brief XIOS interface header * @details * @@ -31,7 +31,8 @@ void enableXios(); class Xios : public Configured { public: - Xios(const std::string contextId = "nextSIM-DG"); + Xios(const std::string timestep = "P0-0T01:00:00", const std::string contextId = "nextSIM-DG", + const std::string calendarType = "Gregorian"); ~Xios(); /* Initialization and finalization */ @@ -42,7 +43,7 @@ class Xios : public Configured { /* Configuration */ void configure() override; - void configureServer(const std::string calendarType = "Gregorian"); + void configureServer(); /* MPI decomposition */ int getClientMPISize(); @@ -146,7 +147,9 @@ class Xios : public Configured { bool isEnabled; std::string clientId; + std::string _calendarType; std::string _contextId; + Duration _timestep; MPI_Comm clientComm; MPI_Fint clientComm_F; MPI_Fint nullComm_F; diff --git a/core/test/XiosAxis_test.cpp b/core/test/XiosAxis_test.cpp index c5269652d..74e47ac9a 100644 --- a/core/test/XiosAxis_test.cpp +++ b/core/test/XiosAxis_test.cpp @@ -1,7 +1,7 @@ /*! * @file XiosAxis_test.cpp * @author Joe Wallwork - * @date 19 Nov 2024 + * @date 03 Dec 2024 * @brief Tests for XIOS axes * @details * This test is designed to test axis functionality of the C++ interface @@ -34,9 +34,6 @@ MPI_TEST_CASE("TestXiosAxis", 2) REQUIRE(xios_handler.isInitialized()); REQUIRE(xios_handler.getClientMPISize() == 2); - // Set timestep as a minimum - xios_handler.setCalendarTimestep(Duration("P0-0T01:00:00")); - // --- Tests for axis API const std::string axisId = { "axis_A" }; REQUIRE_THROWS_WITH(xios_handler.getAxisSize(axisId), "Xios: Undefined axis 'axis_A'"); diff --git a/core/test/XiosCalendar_test.cpp b/core/test/XiosCalendar_test.cpp index c98c3e2d9..2bd1b2965 100644 --- a/core/test/XiosCalendar_test.cpp +++ b/core/test/XiosCalendar_test.cpp @@ -1,7 +1,7 @@ /*! * @file XiosCalendar_test.cpp * @author Joe Wallwork - * @date 19 Nov 2024 + * @date 03 Dec 2024 * @brief Tests for XIOS calandars * @details * This test is designed to test calendar functionality of the C++ interface @@ -48,6 +48,7 @@ MPI_TEST_CASE("TestXiosInitialization", 2) REQUIRE(start == xios_handler.getCalendarStart()); REQUIRE(start.format() == "2023-03-17T17:11:00Z"); // Timestep + REQUIRE(xios_handler.getCalendarTimestep().seconds() == doctest::Approx(3600.0)); // Default Duration timestep("P0-0T01:30:00"); REQUIRE(timestep.seconds() == doctest::Approx(5400.0)); xios_handler.setCalendarTimestep(timestep); diff --git a/core/test/XiosDomain_test.cpp b/core/test/XiosDomain_test.cpp index 42e29d653..04f9cb17e 100644 --- a/core/test/XiosDomain_test.cpp +++ b/core/test/XiosDomain_test.cpp @@ -1,7 +1,7 @@ /*! * @file XiosDomain_test.cpp * @author Joe Wallwork - * @date 19 Nov 2024 + * @date 03 Dec 2024 * @brief Tests for XIOS domains * @details * This test is designed to test domain functionality of the C++ interface @@ -36,9 +36,6 @@ MPI_TEST_CASE("TestXiosDomain", 2) REQUIRE(size == 2); const size_t rank = xios_handler.getClientMPIRank(); - // Set timestep as a minimum - xios_handler.setCalendarTimestep(Duration("P0-0T01:00:00")); - // --- Tests for domain API const std::string domainId = "domain_A"; REQUIRE_THROWS_WITH(xios_handler.getDomainType(domainId), "Xios: Undefined domain 'domain_A'"); diff --git a/core/test/XiosField_test.cpp b/core/test/XiosField_test.cpp index 743da1d2f..6358470f9 100644 --- a/core/test/XiosField_test.cpp +++ b/core/test/XiosField_test.cpp @@ -1,7 +1,7 @@ /*! * @file XiosField_test.cpp * @author Joe Wallwork - * @date 19 Nov 2024 + * @date 03 Dec 2024 * @brief Tests for XIOS axes * @details * This test is designed to test axis functionality of the C++ interface @@ -36,10 +36,6 @@ MPI_TEST_CASE("TestXiosField", 2) REQUIRE(size == 2); const size_t rank = xios_handler.getClientMPIRank(); - // Set timestep as a minimum - Duration timestep("P0-0T01:00:00"); - xios_handler.setCalendarTimestep(timestep); - // Create an axis with two points xios_handler.createAxis("axis_A"); xios_handler.setAxisValues("axis_A", { 0.0, 1.0 }); @@ -76,7 +72,7 @@ MPI_TEST_CASE("TestXiosField", 2) xios_handler.setFieldReadAccess(fieldId, readAccess); REQUIRE(xios_handler.getFieldReadAccess(fieldId)); // Frequency offset - Duration freqOffset = timestep; + Duration freqOffset = xios_handler.getCalendarTimestep(); xios_handler.setFieldFreqOffset(fieldId, freqOffset); // TODO: Overload == for Duration REQUIRE(xios_handler.getFieldFreqOffset(fieldId).seconds() == freqOffset.seconds()); diff --git a/core/test/XiosFile_test.cpp b/core/test/XiosFile_test.cpp index 4a15ad5f6..34a59552d 100644 --- a/core/test/XiosFile_test.cpp +++ b/core/test/XiosFile_test.cpp @@ -1,7 +1,7 @@ /*! * @file XiosFile_test.cpp * @author Joe Wallwork - * @date 19 Nov 2024 + * @date 03 Dec 2024 * @brief Tests for XIOS axes * @details * This test is designed to test axis functionality of the C++ interface @@ -32,16 +32,12 @@ MPI_TEST_CASE("TestXiosFile", 2) enableXios(); // Initialize an Xios instance called xios_handler - Xios xios_handler; + Xios xios_handler("P0-0T01:30:00"); REQUIRE(xios_handler.isInitialized()); const size_t size = xios_handler.getClientMPISize(); REQUIRE(size == 2); const size_t rank = xios_handler.getClientMPIRank(); - // Set timestep as a minimum - Duration timestep("P0-0T01:30:00"); - xios_handler.setCalendarTimestep(timestep); - // Create a simple axis with two points xios_handler.createAxis("axis_A"); xios_handler.setAxisValues("axis_A", { 0.0, 1.0 }); @@ -73,6 +69,7 @@ MPI_TEST_CASE("TestXiosFile", 2) // Output frequency REQUIRE_THROWS_WITH(xios_handler.getFileOutputFreq(fileId), "Xios: Undefined output frequency for file 'output'"); + Duration timestep = xios_handler.getCalendarTimestep(); xios_handler.setFileOutputFreq(fileId, timestep); REQUIRE(xios_handler.getFileOutputFreq(fileId).seconds() == 1.5 * 60 * 60); // Split frequency diff --git a/core/test/XiosGrid_test.cpp b/core/test/XiosGrid_test.cpp index 114c08b87..1c4e24d85 100644 --- a/core/test/XiosGrid_test.cpp +++ b/core/test/XiosGrid_test.cpp @@ -1,7 +1,7 @@ /*! * @file XiosGrid_test.cpp * @author Joe Wallwork - * @date 19 Nov 2024 + * @date 03 Dec 2024 * @brief Tests for XIOS axes * @details * This test is designed to test axis functionality of the C++ interface @@ -35,9 +35,6 @@ MPI_TEST_CASE("TestXiosGrid", 2) REQUIRE(size == 2); const size_t rank = xios_handler.getClientMPIRank(); - // Set timestep as a minimum - xios_handler.setCalendarTimestep(Duration("P0-0T01:30:00")); - // Create a 4x2 horizontal domain with a partition halving the x-extent xios_handler.createDomain("domain_XY"); xios_handler.setDomainType("domain_XY", "rectilinear"); diff --git a/core/test/XiosReadWrite_test.cpp b/core/test/XiosReadWrite_test.cpp index aaab7da45..b413c94cb 100644 --- a/core/test/XiosReadWrite_test.cpp +++ b/core/test/XiosReadWrite_test.cpp @@ -1,7 +1,7 @@ /*! * @file XiosReadWrite_test.cpp * @author Joe Wallwork - * @date 19 Nov 2024 + * @date 03 Dec 2024 * @brief Tests for XIOS write method * @details * This test is designed to test the read and write methods of the C++ @@ -61,7 +61,7 @@ Xios setupXiosHandler(int dim, bool read) } else { label = "write"; } - Xios xios_handler(formatId(label, dim)); + Xios xios_handler("P0-0T01:30:00", formatId(label, dim)); REQUIRE(xios_handler.isInitialized()); const size_t size = xios_handler.getClientMPISize(); REQUIRE(size == 2); @@ -70,8 +70,6 @@ Xios setupXiosHandler(int dim, bool read) // Calendar setup xios_handler.setCalendarOrigin(TimePoint("2020-01-23T00:08:15Z")); xios_handler.setCalendarStart(TimePoint("2023-03-17T17:11:00Z")); - Duration timestep("P0-0T01:30:00"); - xios_handler.setCalendarTimestep(timestep); // Set ModelArray dimensions const size_t nx_glo = 4; @@ -111,6 +109,7 @@ Xios setupXiosHandler(int dim, bool read) xios_handler.setFieldOperation(fieldId, "instant"); xios_handler.setFieldGridRef(fieldId, formatId("grid", dim)); xios_handler.setFieldReadAccess(fieldId, read); + Duration timestep = xios_handler.getCalendarTimestep(); xios_handler.setFieldFreqOffset(fieldId, timestep); // Create an file for reading/writing of field data From c8b35191739ff945f44e77335e278a8937daa7f9 Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Tue, 3 Dec 2024 11:13:12 +0000 Subject: [PATCH 08/22] Set default calendar origin and start --- core/src/Xios.cpp | 4 ++++ core/test/XiosReadWrite_test.cpp | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/Xios.cpp b/core/src/Xios.cpp index 86166795a..b3d28f4a3 100644 --- a/core/src/Xios.cpp +++ b/core/src/Xios.cpp @@ -124,6 +124,10 @@ void Xios::configureServer() cxios_set_calendar_wrapper_timestep(clientCalendar, convertDurationToXios(_timestep)); cxios_create_calendar(clientCalendar); cxios_update_calendar_timestep(clientCalendar); + + // Set default calendar origin and start // TODO: Pick something sensible + setCalendarOrigin(TimePoint("2020-01-23T00:08:15Z")); + setCalendarStart(TimePoint("2023-03-17T17:11:00Z")); } /*! diff --git a/core/test/XiosReadWrite_test.cpp b/core/test/XiosReadWrite_test.cpp index b413c94cb..ab497e10b 100644 --- a/core/test/XiosReadWrite_test.cpp +++ b/core/test/XiosReadWrite_test.cpp @@ -67,10 +67,6 @@ Xios setupXiosHandler(int dim, bool read) REQUIRE(size == 2); const size_t rank = xios_handler.getClientMPIRank(); - // Calendar setup - xios_handler.setCalendarOrigin(TimePoint("2020-01-23T00:08:15Z")); - xios_handler.setCalendarStart(TimePoint("2023-03-17T17:11:00Z")); - // Set ModelArray dimensions const size_t nx_glo = 4; const size_t ny_glo = 2; From e0072458160d39e4d8c90b3103ab90106dfca2be Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Wed, 4 Dec 2024 12:22:12 +0000 Subject: [PATCH 09/22] Don't use Approx for integer checks --- core/test/XiosCalendar_test.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/test/XiosCalendar_test.cpp b/core/test/XiosCalendar_test.cpp index 2bd1b2965..7e6b663b8 100644 --- a/core/test/XiosCalendar_test.cpp +++ b/core/test/XiosCalendar_test.cpp @@ -1,7 +1,7 @@ /*! * @file XiosCalendar_test.cpp * @author Joe Wallwork - * @date 03 Dec 2024 + * @date 04 Dec 2024 * @brief Tests for XIOS calandars * @details * This test is designed to test calendar functionality of the C++ interface @@ -48,11 +48,11 @@ MPI_TEST_CASE("TestXiosInitialization", 2) REQUIRE(start == xios_handler.getCalendarStart()); REQUIRE(start.format() == "2023-03-17T17:11:00Z"); // Timestep - REQUIRE(xios_handler.getCalendarTimestep().seconds() == doctest::Approx(3600.0)); // Default + REQUIRE(xios_handler.getCalendarTimestep().seconds() == 3600.0); // Default Duration timestep("P0-0T01:30:00"); - REQUIRE(timestep.seconds() == doctest::Approx(5400.0)); + REQUIRE(timestep.seconds() == 5400.0); xios_handler.setCalendarTimestep(timestep); - REQUIRE(xios_handler.getCalendarTimestep().seconds() == doctest::Approx(5400.0)); + REQUIRE(xios_handler.getCalendarTimestep().seconds() == 5400.0); xios_handler.close_context_definition(); From 8ee9291d86742ca56fba47caf8add21ea16a33b9 Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Wed, 4 Dec 2024 12:25:56 +0000 Subject: [PATCH 10/22] Avoid class members starting with underscore --- core/src/Xios.cpp | 24 ++++++++++++------------ core/src/include/Xios.hpp | 12 ++++++------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/core/src/Xios.cpp b/core/src/Xios.cpp index b3d28f4a3..f97668c05 100644 --- a/core/src/Xios.cpp +++ b/core/src/Xios.cpp @@ -2,7 +2,7 @@ * @file Xios.cpp * @author Tom Meltzer * @author Joe Wallwork - * @date 03 Dec 2024 + * @date 04 Dec 2024 * @brief XIOS interface implementation * @details * @@ -47,15 +47,15 @@ void enableXios() /*! * Constructor: Configure an XIOS server * - * @param timestep Timestep to use for the model - * @param contextId ID for the XIOS context - * @param calendarType Type of calendar to use + * @param dt Timestep to use for the model + * @param contextid ID for the XIOS context + * @param calendartype Type of calendar to use */ -Xios::Xios(const std::string timestep, const std::string contextId, const std::string calendarType) +Xios::Xios(const std::string dt, const std::string contextid, const std::string calendartype) { - _calendarType = calendarType; - _contextId = contextId; - _timestep = Duration(timestep); + calendarType = calendartype; + contextId = contextid; + timestep = Duration(dt); configure(); } @@ -116,12 +116,12 @@ void Xios::configureServer() MPI_Comm_size(clientComm, &mpi_size); // Initialize 'nextSIM-DG' context - cxios_context_initialize(_contextId.c_str(), _contextId.length(), &clientComm_F); + cxios_context_initialize(contextId.c_str(), contextId.length(), &clientComm_F); // Initialize calendar wrapper for 'nextSIM-DG' context cxios_get_current_calendar_wrapper(&clientCalendar); - cxios_set_calendar_wrapper_type(clientCalendar, _calendarType.c_str(), _calendarType.length()); - cxios_set_calendar_wrapper_timestep(clientCalendar, convertDurationToXios(_timestep)); + cxios_set_calendar_wrapper_type(clientCalendar, calendarType.c_str(), calendarType.length()); + cxios_set_calendar_wrapper_timestep(clientCalendar, convertDurationToXios(timestep)); cxios_create_calendar(clientCalendar); cxios_update_calendar_timestep(clientCalendar); @@ -148,7 +148,7 @@ int Xios::getClientMPIRank() { return mpi_rank; } bool Xios::isInitialized() { bool init = false; - cxios_context_is_initialized(_contextId.c_str(), _contextId.length(), &init); + cxios_context_is_initialized(contextId.c_str(), contextId.length(), &init); return init; } diff --git a/core/src/include/Xios.hpp b/core/src/include/Xios.hpp index 14b8515b1..7b91e007c 100644 --- a/core/src/include/Xios.hpp +++ b/core/src/include/Xios.hpp @@ -2,7 +2,7 @@ * @file Xios.hpp * @author Tom Meltzer * @author Joe Wallwork - * @date 03 Dec 2024 + * @date 04 Dec 2024 * @brief XIOS interface header * @details * @@ -31,8 +31,8 @@ void enableXios(); class Xios : public Configured { public: - Xios(const std::string timestep = "P0-0T01:00:00", const std::string contextId = "nextSIM-DG", - const std::string calendarType = "Gregorian"); + Xios(const std::string dt = "P0-0T01:00:00", const std::string contextid = "nextSIM-DG", + const std::string calendartype = "Gregorian"); ~Xios(); /* Initialization and finalization */ @@ -147,9 +147,9 @@ class Xios : public Configured { bool isEnabled; std::string clientId; - std::string _calendarType; - std::string _contextId; - Duration _timestep; + std::string calendarType; + std::string contextId; + Duration timestep; MPI_Comm clientComm; MPI_Fint clientComm_F; MPI_Fint nullComm_F; From 63f9d1a5c1952bda6815f32d1d5d100c382f90f4 Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Wed, 4 Dec 2024 12:59:30 +0000 Subject: [PATCH 11/22] Use Unix epoch for calendar origin --- core/src/Xios.cpp | 6 +++--- core/test/XiosCalendar_test.cpp | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/core/src/Xios.cpp b/core/src/Xios.cpp index f97668c05..274640cfa 100644 --- a/core/src/Xios.cpp +++ b/core/src/Xios.cpp @@ -125,9 +125,9 @@ void Xios::configureServer() cxios_create_calendar(clientCalendar); cxios_update_calendar_timestep(clientCalendar); - // Set default calendar origin and start // TODO: Pick something sensible - setCalendarOrigin(TimePoint("2020-01-23T00:08:15Z")); - setCalendarStart(TimePoint("2023-03-17T17:11:00Z")); + // Set default calendar origin and start + setCalendarOrigin(TimePoint("1970-01-01T00:00:00Z")); // Unix epoch + setCalendarStart(TimePoint("2023-03-17T17:11:00Z")); // TODO: Read from config } /*! diff --git a/core/test/XiosCalendar_test.cpp b/core/test/XiosCalendar_test.cpp index 7e6b663b8..a77699789 100644 --- a/core/test/XiosCalendar_test.cpp +++ b/core/test/XiosCalendar_test.cpp @@ -38,10 +38,11 @@ MPI_TEST_CASE("TestXiosInitialization", 2) // Calendar type REQUIRE(xios_handler.getCalendarType() == "Gregorian"); // Calendar origin + REQUIRE(xios_handler.getCalendarOrigin().format() == "1970-01-01T00:00:00Z"); // Default TimePoint origin("2020-01-23T00:08:15Z"); xios_handler.setCalendarOrigin(origin); REQUIRE(origin == xios_handler.getCalendarOrigin()); - REQUIRE(origin.format() == "2020-01-23T00:08:15Z"); + REQUIRE(origin.format() == "1970-01-01T00:00:00Z"); // Calendar start TimePoint start("2023-03-17T17:11:00Z"); xios_handler.setCalendarStart(start); From d5b28d0878695ae8c9a9acd5704e370e311a0eb0 Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Wed, 4 Dec 2024 13:08:55 +0000 Subject: [PATCH 12/22] Allow calendar start to be set, too --- core/src/Xios.cpp | 11 +++++++---- core/src/include/Xios.hpp | 2 ++ core/test/XiosCalendar_test.cpp | 3 ++- core/test/XiosReadWrite_test.cpp | 4 ++-- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/core/src/Xios.cpp b/core/src/Xios.cpp index 274640cfa..93b65a176 100644 --- a/core/src/Xios.cpp +++ b/core/src/Xios.cpp @@ -49,13 +49,16 @@ void enableXios() * * @param dt Timestep to use for the model * @param contextid ID for the XIOS context + * @param starttime Datetime string for the start of the simulation * @param calendartype Type of calendar to use */ -Xios::Xios(const std::string dt, const std::string contextid, const std::string calendartype) +Xios::Xios(const std::string dt, const std::string contextid, const std::string starttime, + const std::string calendartype) { - calendarType = calendartype; - contextId = contextid; timestep = Duration(dt); + startTime = TimePoint(starttime); + contextId = contextid; + calendarType = calendartype; configure(); } @@ -127,7 +130,7 @@ void Xios::configureServer() // Set default calendar origin and start setCalendarOrigin(TimePoint("1970-01-01T00:00:00Z")); // Unix epoch - setCalendarStart(TimePoint("2023-03-17T17:11:00Z")); // TODO: Read from config + setCalendarStart(TimePoint(startTime)); } /*! diff --git a/core/src/include/Xios.hpp b/core/src/include/Xios.hpp index 7b91e007c..cd846ded7 100644 --- a/core/src/include/Xios.hpp +++ b/core/src/include/Xios.hpp @@ -32,6 +32,7 @@ void enableXios(); class Xios : public Configured { public: Xios(const std::string dt = "P0-0T01:00:00", const std::string contextid = "nextSIM-DG", + const std::string starttime = "1970-01-01T00:00:00Z", const std::string calendartype = "Gregorian"); ~Xios(); @@ -150,6 +151,7 @@ class Xios : public Configured { std::string calendarType; std::string contextId; Duration timestep; + TimePoint startTime; MPI_Comm clientComm; MPI_Fint clientComm_F; MPI_Fint nullComm_F; diff --git a/core/test/XiosCalendar_test.cpp b/core/test/XiosCalendar_test.cpp index a77699789..6dcd0a7cc 100644 --- a/core/test/XiosCalendar_test.cpp +++ b/core/test/XiosCalendar_test.cpp @@ -42,8 +42,9 @@ MPI_TEST_CASE("TestXiosInitialization", 2) TimePoint origin("2020-01-23T00:08:15Z"); xios_handler.setCalendarOrigin(origin); REQUIRE(origin == xios_handler.getCalendarOrigin()); - REQUIRE(origin.format() == "1970-01-01T00:00:00Z"); + REQUIRE(origin.format() == "2020-01-23T00:08:15Z"); // Calendar start + REQUIRE(xios_handler.getCalendarStart().format() == "1970-01-01T00:00:00Z"); // Default TimePoint start("2023-03-17T17:11:00Z"); xios_handler.setCalendarStart(start); REQUIRE(start == xios_handler.getCalendarStart()); diff --git a/core/test/XiosReadWrite_test.cpp b/core/test/XiosReadWrite_test.cpp index ab497e10b..d746216a8 100644 --- a/core/test/XiosReadWrite_test.cpp +++ b/core/test/XiosReadWrite_test.cpp @@ -1,7 +1,7 @@ /*! * @file XiosReadWrite_test.cpp * @author Joe Wallwork - * @date 03 Dec 2024 + * @date 04 Dec 2024 * @brief Tests for XIOS write method * @details * This test is designed to test the read and write methods of the C++ @@ -61,7 +61,7 @@ Xios setupXiosHandler(int dim, bool read) } else { label = "write"; } - Xios xios_handler("P0-0T01:30:00", formatId(label, dim)); + Xios xios_handler("P0-0T01:30:00", formatId(label, dim), "2023-03-17T17:11:00Z"); REQUIRE(xios_handler.isInitialized()); const size_t size = xios_handler.getClientMPISize(); REQUIRE(size == 2); From cd35f66cc034f7c0a188496ad91a39641f80031a Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Mon, 9 Dec 2024 13:20:13 +0000 Subject: [PATCH 13/22] Throw error if calendar attributes unset --- core/src/Xios.cpp | 11 ++++++++++- core/src/include/xios_c_interface.hpp | 5 ++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/core/src/Xios.cpp b/core/src/Xios.cpp index 93b65a176..68d8b257e 100644 --- a/core/src/Xios.cpp +++ b/core/src/Xios.cpp @@ -2,7 +2,7 @@ * @file Xios.cpp * @author Tom Meltzer * @author Joe Wallwork - * @date 04 Dec 2024 + * @date 09 Dec 2024 * @brief XIOS interface implementation * @details * @@ -290,6 +290,9 @@ std::string Xios::getCalendarType() */ TimePoint Xios::getCalendarOrigin() { + if (!cxios_is_defined_calendar_wrapper_time_origin(clientCalendar)) { + throw std::runtime_error("Xios: Calendar origin has not been set"); + } cxios_date calendar_origin; cxios_get_calendar_wrapper_date_time_origin(clientCalendar, &calendar_origin); return TimePoint(convertXiosDatetimeToString(calendar_origin, true)); @@ -302,6 +305,9 @@ TimePoint Xios::getCalendarOrigin() */ TimePoint Xios::getCalendarStart() { + if (!cxios_is_defined_calendar_wrapper_start_date(clientCalendar)) { + throw std::runtime_error("Xios: Calendar start date has not been set"); + } cxios_date calendar_start; cxios_get_calendar_wrapper_date_start_date(clientCalendar, &calendar_start); return TimePoint(convertXiosDatetimeToString(calendar_start, true)); @@ -314,6 +320,9 @@ TimePoint Xios::getCalendarStart() */ Duration Xios::getCalendarTimestep() { + if (!cxios_is_defined_calendar_wrapper_timestep(clientCalendar)) { + throw std::runtime_error("Xios: Calendar timestep has not been set"); + } cxios_duration calendar_timestep; cxios_get_calendar_wrapper_timestep(clientCalendar, &calendar_timestep); return convertDurationFromXios(calendar_timestep); diff --git a/core/src/include/xios_c_interface.hpp b/core/src/include/xios_c_interface.hpp index c3663f213..11713b637 100644 --- a/core/src/include/xios_c_interface.hpp +++ b/core/src/include/xios_c_interface.hpp @@ -2,7 +2,7 @@ * @file xios_c_interface.hpp * @author Tom Meltzer * @author Joe Wallwork - * @date 12 August 2024 + * @date 09 Dec 2024 * @brief C interface for XIOS library * @details * This interface is based on an earlier version provided by Laurent as part of @@ -59,6 +59,8 @@ void cxios_get_calendar_wrapper_date_time_origin( void cxios_get_calendar_wrapper_type( xios::CCalendarWrapper* calendarWrapper_hdl, const char* type, int type_size); void cxios_get_current_date(cxios_date* date); +bool cxios_is_defined_calendar_wrapper_time_origin(xios::CCalendarWrapper* calendar_wrapper_hdl); +bool cxios_is_defined_calendar_wrapper_start_date(xios::CCalendarWrapper* calendar_wrapper_hdl); void cxios_update_calendar(int step); // timestep methods @@ -66,6 +68,7 @@ void cxios_set_calendar_wrapper_timestep( xios::CCalendarWrapper* calendar_wrapper_hdl, cxios_duration timestep_c); void cxios_get_calendar_wrapper_timestep( xios::CCalendarWrapper* calendar_wrapper_hdl, cxios_duration* timestep_c); +bool cxios_is_defined_calendar_wrapper_timestep(xios::CCalendarWrapper* calendar_wrapper_hdl); void cxios_update_calendar_timestep(xios::CCalendarWrapper* calendarWrapper_hdl); // axis group methods From ab56c4820bf3fb6f4ff0f64408bb3a405c9fe126 Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Mon, 9 Dec 2024 13:21:42 +0000 Subject: [PATCH 14/22] Fix minor error in pre-commit --- .pre-commit | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit b/.pre-commit index 55bdc4b1f..3f2f5dd6d 100644 --- a/.pre-commit +++ b/.pre-commit @@ -3,7 +3,7 @@ # An example pre-commit file for nextSIM-DG development BEFORE=$(mktemp) -git diff > ${BEFORE} +git diff >${BEFORE} for FILE in $(git diff --cached --name-only | grep -iE '\.(cpp|cc|h|hpp)$'); do # Apply clang-format for linting @@ -17,8 +17,8 @@ done # Abort commit if formatting was applied AFTER=$(mktemp) -git diff > ${AFTER} -if [ "$(diff nextsim_precommit_before.patch nextsim_precommit_after.patch)" ]; then +git diff >${AFTER} +if [ "$(diff ${BEFORE} ${AFTER})" ]; then echo "Aborting git commit because the pre-commit hook made changes." rm ${BEFORE} ${AFTER} exit 1 From 672dcf9f160dd52d3013913e77ee82eadd7b882e Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Tue, 10 Dec 2024 09:28:12 +0000 Subject: [PATCH 15/22] Add docs on pre-commit --- .pre-commit | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.pre-commit b/.pre-commit index 3f2f5dd6d..9d3d9f4d2 100644 --- a/.pre-commit +++ b/.pre-commit @@ -1,6 +1,15 @@ #!/bin/bash # An example pre-commit file for nextSIM-DG development +# +# To use this pre-commit hook, you will need to install: +# * clang-format (see https://clang.llvm.org/docs/ClangFormat.html) +# * cmake-format (see https://github.com/cheshirekow/cmake_format) +# +# Then copy this file as `.git/hooks/pre-commit` to set the pre-commit hook up. +# +# If changes are made by the pre-commit hook then the commit will abort and you +# will need to re-run the commit command. BEFORE=$(mktemp) git diff >${BEFORE} From c84b2cb5c9bcedffdc7e120e629893abad32c867 Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Tue, 10 Dec 2024 10:00:56 +0000 Subject: [PATCH 16/22] Rename linting workflow; add CMake linting, too --- .../workflows/{clang_format.yml => linting.yml} | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) rename .github/workflows/{clang_format.yml => linting.yml} (64%) diff --git a/.github/workflows/clang_format.yml b/.github/workflows/linting.yml similarity index 64% rename from .github/workflows/clang_format.yml rename to .github/workflows/linting.yml index e5101a48f..c839aab4e 100644 --- a/.github/workflows/clang_format.yml +++ b/.github/workflows/linting.yml @@ -8,14 +8,11 @@ on: jobs: clang-formatter: - - runs-on: ubuntu-24.04 - + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: clang-format run: | - sudo apt update sudo apt install clang-format for component in core dynamics physics @@ -24,3 +21,12 @@ jobs: clang-format --dry-run -Werror *cpp include/*hpp cd - done + + cmake-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: clang-format + run: | + pip install cmakelang + cmake-lint $(find . -name CMakeLists.txt) --disabled {C0103,C0113} From 4a210316a36fa0c8d95123a9f13afa76414d83f7 Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Tue, 10 Dec 2024 10:02:07 +0000 Subject: [PATCH 17/22] Add notes on ignores --- .github/workflows/linting.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index c839aab4e..200286723 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -30,3 +30,5 @@ jobs: run: | pip install cmakelang cmake-lint $(find . -name CMakeLists.txt) --disabled {C0103,C0113} + # NOTE: We ignore C0103, which would enforce that variables are all-caps + # NOTE: We ignore C0113, which would enforce that comments are used From ebf1c1ce2a676f55b1f98466a7b6f87376c53bd7 Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Tue, 10 Dec 2024 10:08:16 +0000 Subject: [PATCH 18/22] Revert clang-format to run on Ubuntu 24.04 --- .github/workflows/linting.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 200286723..783440c65 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -8,7 +8,7 @@ on: jobs: clang-formatter: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v2 - name: clang-format From 1a1e0774519990a6f585695a51c79d150073f856 Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Tue, 10 Dec 2024 10:12:41 +0000 Subject: [PATCH 19/22] Add CMake formatting to pre-commit --- .pre-commit | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.pre-commit b/.pre-commit index 9d3d9f4d2..af53f8dba 100644 --- a/.pre-commit +++ b/.pre-commit @@ -24,6 +24,11 @@ for FILE in $(git diff --cached --name-only | grep -iE '\.(cpp|cc|h|hpp)$'); do sed -i~ "s/${LINE}/${NEWLINE}/" ${FILE} done +# Apply cmake-lint for linting +for FILE in $(git diff --cached --name-only | grep -i 'CMakeLists.txt'); do + cmake-lint ${FILE} --disabled {C0103,C0113} --line-width 100 +done + # Abort commit if formatting was applied AFTER=$(mktemp) git diff >${AFTER} From 2ff7a0579f96e6975555ae16585c941d93d8ed73 Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Tue, 10 Dec 2024 10:19:18 +0000 Subject: [PATCH 20/22] Set line width 100 and tab size 4 --- .github/workflows/linting.yml | 3 ++- .pre-commit | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 783440c65..04234d07d 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -29,6 +29,7 @@ jobs: - name: clang-format run: | pip install cmakelang - cmake-lint $(find . -name CMakeLists.txt) --disabled {C0103,C0113} + cmake-lint $(find . -name CMakeLists.txt) \ + --disabled {C0103,C0113} --line-width 100 --tab-size 4 # NOTE: We ignore C0103, which would enforce that variables are all-caps # NOTE: We ignore C0113, which would enforce that comments are used diff --git a/.pre-commit b/.pre-commit index af53f8dba..604a544c5 100644 --- a/.pre-commit +++ b/.pre-commit @@ -26,7 +26,7 @@ done # Apply cmake-lint for linting for FILE in $(git diff --cached --name-only | grep -i 'CMakeLists.txt'); do - cmake-lint ${FILE} --disabled {C0103,C0113} --line-width 100 + cmake-lint ${FILE} --disabled {C0103,C0113} --line-width 100 --tab-size 4 done # Abort commit if formatting was applied From db9b78ee62113507cbb313810700110b5ef993ed Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Tue, 10 Dec 2024 10:27:54 +0000 Subject: [PATCH 21/22] Add config based on defaults plus four edits; use it --- .cmake-lint-config.py | 241 ++++++++++++++++++++++++++++++++++ .github/workflows/linting.yml | 5 +- .pre-commit | 2 +- 3 files changed, 245 insertions(+), 3 deletions(-) create mode 100644 .cmake-lint-config.py diff --git a/.cmake-lint-config.py b/.cmake-lint-config.py new file mode 100644 index 000000000..d69885fbf --- /dev/null +++ b/.cmake-lint-config.py @@ -0,0 +1,241 @@ +# ---------------------------------- +# Options affecting listfile parsing +# ---------------------------------- +with section("parse"): + + # Specify structure for custom cmake functions + additional_commands = { 'foo': { 'flags': ['BAR', 'BAZ'], + 'kwargs': {'DEPENDS': '*', 'HEADERS': '*', 'SOURCES': '*'}}} + + # Override configurations per-command where available + override_spec = {} + + # Specify variable tags. + vartags = [] + + # Specify property tags. + proptags = [] + +# ----------------------------- +# Options affecting formatting. +# ----------------------------- +with section("format"): + + # Disable formatting entirely, making cmake-format a no-op + disable = False + + # How wide to allow formatted cmake files + line_width = 100 + + # How many spaces to tab for indent + tab_size = 4 + + # If true, lines are indented using tab characters (utf-8 0x09) instead of + # space characters (utf-8 0x20). In cases where the layout would + # require a fractional tab character, the behavior of the fractional + # indentation is governed by + use_tabchars = False + + # If is True, then the value of this variable indicates how + # fractional indentions are handled during whitespace replacement. If set to + # 'use-space', fractional indentation is left as spaces (utf-8 0x20). If set + # to `round-up` fractional indentation is replaced with a single tab character + # (utf-8 0x09) effectively shifting the column to the next tabstop + fractional_tab_policy = 'use-space' + + # If an argument group contains more than this many sub-groups (parg or kwarg + # groups) then force it to a vertical layout. + max_subgroups_hwrap = 2 + + # If a positional argument group contains more than this many arguments, then + # force it to a vertical layout. + max_pargs_hwrap = 6 + + # If a cmdline positional group consumes more than this many lines without + # nesting, then invalidate the layout (and nest) + max_rows_cmdline = 2 + + # If true, separate flow control names from their parentheses with a space + separate_ctrl_name_with_space = False + + # If true, separate function names from parentheses with a space + separate_fn_name_with_space = False + + # If a statement is wrapped to more than one line, than dangle the closing + # parenthesis on its own line. + dangle_parens = False + + # If the trailing parenthesis must be 'dangled' on its on line, then align it + # to this reference: `prefix`: the start of the statement, `prefix-indent`: + # the start of the statement, plus one indentation level, `child`: align to + # the column of the arguments + dangle_align = 'prefix' + + # If the statement spelling length (including space and parenthesis) is + # smaller than this amount, then force reject nested layouts. + min_prefix_chars = 4 + + # If the statement spelling length (including space and parenthesis) is larger + # than the tab width by more than this amount, then force reject un-nested + # layouts. + max_prefix_chars = 10 + + # If a candidate layout is wrapped horizontally but it exceeds this many + # lines, then reject the layout. + max_lines_hwrap = 2 + + # What style line endings to use in the output. + line_ending = 'unix' + + # Format command names consistently as 'lower' or 'upper' case + command_case = 'canonical' + + # Format keywords consistently as 'lower' or 'upper' case + keyword_case = 'unchanged' + + # A list of command names which should always be wrapped + always_wrap = [] + + # If true, the argument lists which are known to be sortable will be sorted + # lexicographicall + enable_sort = True + + # If true, the parsers may infer whether or not an argument list is sortable + # (without annotation). + autosort = False + + # By default, if cmake-format cannot successfully fit everything into the + # desired linewidth it will apply the last, most agressive attempt that it + # made. If this flag is True, however, cmake-format will print error, exit + # with non-zero status code, and write-out nothing + require_valid_layout = False + + # A dictionary mapping layout nodes to a list of wrap decisions. See the + # documentation for more information. + layout_passes = {} + +# ------------------------------------------------ +# Options affecting comment reflow and formatting. +# ------------------------------------------------ +with section("markup"): + + # What character to use for bulleted lists + bullet_char = '*' + + # What character to use as punctuation after numerals in an enumerated list + enum_char = '.' + + # If comment markup is enabled, don't reflow the first comment block in each + # listfile. Use this to preserve formatting of your copyright/license + # statements. + first_comment_is_literal = False + + # If comment markup is enabled, don't reflow any comment block which matches + # this (regex) pattern. Default is `None` (disabled). + literal_comment_pattern = None + + # Regular expression to match preformat fences in comments default= + # ``r'^\s*([`~]{3}[`~]*)(.*)$'`` + fence_pattern = '^\\s*([`~]{3}[`~]*)(.*)$' + + # Regular expression to match rulers in comments default= + # ``r'^\s*[^\w\s]{3}.*[^\w\s]{3}$'`` + ruler_pattern = '^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$' + + # If a comment line matches starts with this pattern then it is explicitly a + # trailing comment for the preceeding argument. Default is '#<' + explicit_trailing_pattern = '#<' + + # If a comment line starts with at least this many consecutive hash + # characters, then don't lstrip() them off. This allows for lazy hash rulers + # where the first hash char is not separated by space + hashruler_min_length = 10 + + # If true, then insert a space between the first hash char and remaining hash + # chars in a hash ruler, and normalize its length to fill the column + canonicalize_hashrulers = True + + # enable comment markup parsing and reflow + enable_markup = True + +# ---------------------------- +# Options affecting the linter +# ---------------------------- +with section("lint"): + + # a list of lint codes to disable + disabled_codes = ['C0103', 'C0113'] + + # regular expression pattern describing valid function names + function_pattern = '[0-9a-z_]+' + + # regular expression pattern describing valid macro names + macro_pattern = '[0-9A-Z_]+' + + # regular expression pattern describing valid names for variables with global + # (cache) scope + global_var_pattern = '[A-Z][0-9A-Z_]+' + + # regular expression pattern describing valid names for variables with global + # scope (but internal semantic) + internal_var_pattern = '_[A-Z][0-9A-Z_]+' + + # regular expression pattern describing valid names for variables with local + # scope + local_var_pattern = '[a-z][a-z0-9_]+' + + # regular expression pattern describing valid names for privatedirectory + # variables + private_var_pattern = '_[0-9a-z_]+' + + # regular expression pattern describing valid names for public directory + # variables + public_var_pattern = '[A-Z][0-9A-Z_]+' + + # regular expression pattern describing valid names for function/macro + # arguments and loop variables. + argument_var_pattern = '[a-z][a-z0-9_]+' + + # regular expression pattern describing valid names for keywords used in + # functions or macros + keyword_pattern = '[A-Z][0-9A-Z_]+' + + # In the heuristic for C0201, how many conditionals to match within a loop in + # before considering the loop a parser. + max_conditionals_custom_parser = 2 + + # Require at least this many newlines between statements + min_statement_spacing = 1 + + # Require no more than this many newlines between statements + max_statement_spacing = 2 + max_returns = 6 + max_branches = 12 + max_arguments = 5 + max_localvars = 15 + max_statements = 50 + +# ------------------------------- +# Options affecting file encoding +# ------------------------------- +with section("encode"): + + # If true, emit the unicode byte-order mark (BOM) at the start of the file + emit_byteorder_mark = False + + # Specify the encoding of the input file. Defaults to utf-8 + input_encoding = 'utf-8' + + # Specify the encoding of the output file. Defaults to utf-8. Note that cmake + # only claims to support utf-8 so be careful when using anything else + output_encoding = 'utf-8' + +# ------------------------------------- +# Miscellaneous configurations options. +# ------------------------------------- +with section("misc"): + + # A dictionary containing any per-command configuration overrides. Currently + # only `command_case` is supported. + per_command = {} + diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 04234d07d..1d6dec884 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -29,7 +29,8 @@ jobs: - name: clang-format run: | pip install cmakelang - cmake-lint $(find . -name CMakeLists.txt) \ - --disabled {C0103,C0113} --line-width 100 --tab-size 4 + cmake-lint $(find . -name CMakeLists.txt) -c .cmake-lint-config.py # NOTE: We ignore C0103, which would enforce that variables are all-caps # NOTE: We ignore C0113, which would enforce that comments are used + # NOTE: We set the tab size to 4 + # NOTE: We set the maximum line length to 100 diff --git a/.pre-commit b/.pre-commit index 604a544c5..2a74e3b1b 100644 --- a/.pre-commit +++ b/.pre-commit @@ -26,7 +26,7 @@ done # Apply cmake-lint for linting for FILE in $(git diff --cached --name-only | grep -i 'CMakeLists.txt'); do - cmake-lint ${FILE} --disabled {C0103,C0113} --line-width 100 --tab-size 4 + cmake-lint ${FILE} -c .cmake-lint-config.py done # Abort commit if formatting was applied From 65c68c3b5dfdab74beaf6655da0198784814e2c1 Mon Sep 17 00:00:00 2001 From: Joe Wallwork Date: Tue, 10 Dec 2024 10:32:13 +0000 Subject: [PATCH 22/22] Minor renaming --- .github/workflows/linting.yml | 2 +- .pre-commit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 1d6dec884..5ee562626 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -7,7 +7,7 @@ on: branches: [ main, develop ] jobs: - clang-formatter: + clang-format: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v2 diff --git a/.pre-commit b/.pre-commit index 2a74e3b1b..2b2effeea 100644 --- a/.pre-commit +++ b/.pre-commit @@ -4,7 +4,7 @@ # # To use this pre-commit hook, you will need to install: # * clang-format (see https://clang.llvm.org/docs/ClangFormat.html) -# * cmake-format (see https://github.com/cheshirekow/cmake_format) +# * cmake-lint (see https://github.com/cheshirekow/cmake_format) # # Then copy this file as `.git/hooks/pre-commit` to set the pre-commit hook up. #