From 5b178d33a4cecae5e40568bf6675ce2deb35ab38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Fri, 3 May 2024 14:40:29 +0200 Subject: [PATCH 01/78] New IOceanBoundary derived module Define a new IOceanBoundary derived module for the OASIS ocean coupling. I simply copied and renamed the ConstantOceanBoundary module. Next steps are to include the OASIS calls themselves. --- .../OceanBoundaryModule/OASISCoupledOcean.cpp | 42 +++++++++++++++++++ .../include/OASISCoupledOcean.hpp | 29 +++++++++++++ .../modules/OceanBoundaryModule/module.cfg | 5 +++ 3 files changed, 76 insertions(+) create mode 100644 physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp create mode 100644 physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp new file mode 100644 index 000000000..c102bc6aa --- /dev/null +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -0,0 +1,42 @@ +/*! + * @file OASISCoupledOcean.cpp + * + * @date Sep 26, 2022 + * @author Tim Spain + * @author Einar Ólason + */ + +#include "include/OASISCoupledOcean.hpp" +#include "include/IIceOceanHeatFlux.hpp" +#include "include/Module.hpp" +#include "include/constants.hpp" + +namespace Nextsim { +OASISCoupledOcean::OASISCoupledOcean() + : IOceanBoundary() +{ +} + +void OASISCoupledOcean::setData(const ModelState::DataMap& ms) +{ + IOceanBoundary::setData(ms); + // Directly set the array values + // TODO: Replace this code with OASIS calls + sss = 32.; + u = 0; + v = 0; + mld = 10.; + double tf32 = -1.751; // Hand calculated from S = 32 using UNESCO + tf = tf32; + sst = tf32; // Tf == SST ensures that there is no ice-ocean heat flux + cpml = Water::cp * Water::rho * mld; + qio = 0.; +} + +void OASISCoupledOcean::updateBefore(const TimestepTime& tst) +{ + Module::getImplementation().update(tst); +} + +void OASISCoupledOcean::updateAfter(const TimestepTime& tst) { } +} /* namespace Nextsim */ diff --git a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp new file mode 100644 index 000000000..9b62c1760 --- /dev/null +++ b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp @@ -0,0 +1,29 @@ +/*! + * @file OASISCoupledOcean.hpp + * + * @date Sep 26, 2022 + * @author Tim Spain + * @author Einar Ólason + */ + +#ifndef OASISCOUPLEDOCEAN_HPP +#define OASISCOUPLEDOCEAN_HPP + +#include "include/IOceanBoundary.hpp" + +namespace Nextsim { + +//* Ocean boundary data values that are hardcoded. +class OASISCoupledOcean : public IOceanBoundary { +public: + OASISCoupledOcean(); + + std::string getName() const override { return "OASISCoupledOcean"; } + void setData(const ModelState::DataMap&) override; + void updateBefore(const TimestepTime& tst) override; + void updateAfter(const TimestepTime& tst) override; +}; + +} /* namespace Nextsim */ + +#endif /* OASISCOUPLEDOCEAN_HPP */ diff --git a/physics/src/modules/OceanBoundaryModule/module.cfg b/physics/src/modules/OceanBoundaryModule/module.cfg index ddadaf128..312dc572a 100644 --- a/physics/src/modules/OceanBoundaryModule/module.cfg +++ b/physics/src/modules/OceanBoundaryModule/module.cfg @@ -31,3 +31,8 @@ has_help = true file_prefix = BenchmarkOcean description = Calculated ocean for the DG dynamics benchmark. has_help = false + +[Nextsim::OASISCoupledOcean] +file_prefix = OASISCoupledOcean +description = Receive and send ocean fields via the OASIS coupler +has_help = false From 0acbdf8c14df68525dd00fe1113cc81b8c55b3a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Fri, 3 May 2024 14:41:38 +0200 Subject: [PATCH 02/78] Put MLD into m_couplingArrays of IOceanBoundary.hpp It belongs there, but it doesn't look like m_couplingArrays is actively used. --- physics/src/modules/include/IOceanBoundary.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/physics/src/modules/include/IOceanBoundary.hpp b/physics/src/modules/include/IOceanBoundary.hpp index b85c4cb06..8863806b7 100644 --- a/physics/src/modules/include/IOceanBoundary.hpp +++ b/physics/src/modules/include/IOceanBoundary.hpp @@ -28,11 +28,11 @@ class IOceanBoundary : public ModelComponent { m_couplingArrays.registerArray(CouplingFields::SSS, &sss, RW); m_couplingArrays.registerArray(CouplingFields::OCEAN_U, &u, RW); m_couplingArrays.registerArray(CouplingFields::OCEAN_V, &v, RW); + m_couplingArrays.registerArray(CouplingFields::MLD, &mld, RW); getStore().registerArray(Shared::Q_IO, &qio, RW); getStore().registerArray(Protected::SST, &sst, RO); getStore().registerArray(Protected::SSS, &sss, RO); - getStore().registerArray(Protected::MLD, &mld, RO); getStore().registerArray(Protected::ML_BULK_CP, &cpml, RO); getStore().registerArray(Protected::TF, &tf, RO); getStore().registerArray(Protected::OCEAN_U, &u, RO); From 190299c6bc4de2a2e74023652d0a8cd66d942eda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Tue, 7 May 2024 13:20:11 +0200 Subject: [PATCH 03/78] More details on where the OASIS calls go in OASISCoupledOcean setData should contain the initialisation calls, updateBefore the receive calls and updateAfter the send calls. --- .../OceanBoundaryModule/OASISCoupledOcean.cpp | 19 +++++++++++++++---- .../include/OASISCoupledOcean.hpp | 1 + 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index c102bc6aa..1b811807b 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -20,8 +20,13 @@ OASISCoupledOcean::OASISCoupledOcean() void OASISCoupledOcean::setData(const ModelState::DataMap& ms) { IOceanBoundary::setData(ms); + // TODO: Insert OASIS initialisation calls here +} + +void OASISCoupledOcean::updateBefore(const TimestepTime& tst) +{ // Directly set the array values - // TODO: Replace this code with OASIS calls + // TODO: Replace this code with OASIS receive-calls sss = 32.; u = 0; v = 0; @@ -31,12 +36,18 @@ void OASISCoupledOcean::setData(const ModelState::DataMap& ms) sst = tf32; // Tf == SST ensures that there is no ice-ocean heat flux cpml = Water::cp * Water::rho * mld; qio = 0.; + + Module::getImplementation().update(tst); } -void OASISCoupledOcean::updateBefore(const TimestepTime& tst) +void OASISCoupledOcean::updateAfter(const TimestepTime& tst) { - Module::getImplementation().update(tst); + // TODO: Add OASIS send-calls here +} + +OASISCoupledOcean::~OASISCoupledOcean() +{ + // TODO: Insert OASIS finalise call(s) here } -void OASISCoupledOcean::updateAfter(const TimestepTime& tst) { } } /* namespace Nextsim */ diff --git a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp index 9b62c1760..b041047e6 100644 --- a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp +++ b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp @@ -17,6 +17,7 @@ namespace Nextsim { class OASISCoupledOcean : public IOceanBoundary { public: OASISCoupledOcean(); + ~OASISCoupledOcean(); std::string getName() const override { return "OASISCoupledOcean"; } void setData(const ModelState::DataMap&) override; From 023b916100e32811e9afa96b0bbe2e226775be19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 15 Aug 2024 07:41:35 +0200 Subject: [PATCH 04/78] Get the MPI communicator to atm and ocean boundary Adds a setMetadata function to PrognosticData, which passes ModelMedata to IAtmosphereBoundary and IOceanBoundary derived classes. This allows them to call OASIS functions to initialise the coupling. This can be done much better, but this commit acts a check-point, more than anything else. --- core/src/Model.cpp | 1 + core/src/PrognosticData.cpp | 6 ++++ core/src/include/PrognosticData.hpp | 2 ++ .../OceanBoundaryModule/OASISCoupledOcean.cpp | 29 +++++++++++++++++-- .../include/OASISCoupledOcean.hpp | 2 +- .../modules/include/IAtmosphereBoundary.hpp | 2 ++ .../src/modules/include/IOceanBoundary.hpp | 2 ++ 7 files changed, 41 insertions(+), 3 deletions(-) diff --git a/core/src/Model.cpp b/core/src/Model.cpp index 63bf2f9e6..2cea08c60 100644 --- a/core/src/Model.cpp +++ b/core/src/Model.cpp @@ -140,6 +140,7 @@ void Model::configure() modelStep.setMetadata(m_etadata); modelStep.setRestartDetails(restartPeriod, finalFileName); pData.setData(initialState.data); + pData.setMetadata(m_etadata); } ConfigMap Model::getConfig() const diff --git a/core/src/PrognosticData.cpp b/core/src/PrognosticData.cpp index 9ac0b146e..2e7806b9b 100644 --- a/core/src/PrognosticData.cpp +++ b/core/src/PrognosticData.cpp @@ -74,6 +74,12 @@ void PrognosticData::setData(const ModelState::DataMap& ms) iceGrowth.setData(ms); } +void PrognosticData::setMetadata(const Nextsim::ModelMetadata& metadata) +{ + pAtmBdy->setMetadata(metadata); + pOcnBdy->setMetadata(metadata); +} + void PrognosticData::update(const TimestepTime& tst) { ModelArrayRef ticeUpd(getStore()); diff --git a/core/src/include/PrognosticData.hpp b/core/src/include/PrognosticData.hpp index 85426a0e8..2f1a5f06c 100644 --- a/core/src/include/PrognosticData.hpp +++ b/core/src/include/PrognosticData.hpp @@ -38,6 +38,8 @@ class PrognosticData : public ModelComponent, public Configured ModelState getState(const OutputLevel& lvl) const override { return getState(); } ModelState getStateRecursive(const OutputSpec& os) const override; + void setMetadata(const ModelMetadata& metadata); + static HelpMap& getHelpText(HelpMap& map, bool getAll); static HelpMap& getHelpRecursive(HelpMap& map, bool getAll); diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 1b811807b..241a1a871 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -17,10 +17,35 @@ OASISCoupledOcean::OASISCoupledOcean() { } -void OASISCoupledOcean::setData(const ModelState::DataMap& ms) +void OASISCoupledOcean::setMetadata(const ModelMetadata& metadata) { - IOceanBoundary::setData(ms); // TODO: Insert OASIS initialisation calls here + /* + * These three may need to be global to the class + * int compID; + * MPI_Comm localComm; + * MPI_Comm coupledComm; + * + * std::string compName = "nextsim"; + * std::string functionName = getName()+"::setMetadata"; + * std::string message = "couldn't initialise component"; + * std::string file = "OASISCoupledOcean.cpp"; + * const bool coupled = true; + * if ( ! oasis_c_init_comp_with_comm(&compID, &compName.c_str(), coupled, metadata.mpiComm) ) + * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 36); + * + * if ( ! oasis_c_get_localcomm(&localComm) ) + * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 39); + * + * if ( ! oasis_c_create_couplcomm(metadata.mpiMyRank, &localComm, &coupledComm) ) + * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 42); + * + * ... and then def_partition, def_var and end_def calls + * + * But actually, everything except def_var (and the following end_def) can be seperated out in + * its own OASISCoupled class, which OASISCoupledOcean and OASISCoupledAtmosphere inherit from + * (and OASISCoupledWaves will also inherit from). + */ } void OASISCoupledOcean::updateBefore(const TimestepTime& tst) diff --git a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp index b041047e6..3901a25f0 100644 --- a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp +++ b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp @@ -20,9 +20,9 @@ class OASISCoupledOcean : public IOceanBoundary { ~OASISCoupledOcean(); std::string getName() const override { return "OASISCoupledOcean"; } - void setData(const ModelState::DataMap&) override; void updateBefore(const TimestepTime& tst) override; void updateAfter(const TimestepTime& tst) override; + void setMetadata(const ModelMetadata& metadata) override; }; } /* namespace Nextsim */ diff --git a/physics/src/modules/include/IAtmosphereBoundary.hpp b/physics/src/modules/include/IAtmosphereBoundary.hpp index 5b64a734a..49f8c4cfe 100644 --- a/physics/src/modules/include/IAtmosphereBoundary.hpp +++ b/physics/src/modules/include/IAtmosphereBoundary.hpp @@ -7,6 +7,7 @@ #include "include/ModelArrayRef.hpp" #include "include/ModelComponent.hpp" +#include "include/ModelMetadata.hpp" #include "include/Time.hpp" #ifndef IATMOSPHEREBOUNDARY_HPP @@ -62,6 +63,7 @@ class IAtmosphereBoundary : public ModelComponent { ModelState getState(const OutputLevel&) const override { return getState(); } std::string getName() const override { return "IAtmosphereBoundary"; } + virtual void setMetadata(const ModelMetadata& metadata) {} void setData(const ModelState::DataMap& ms) override { qia.resize(); diff --git a/physics/src/modules/include/IOceanBoundary.hpp b/physics/src/modules/include/IOceanBoundary.hpp index 8863806b7..9cfc4dcf6 100644 --- a/physics/src/modules/include/IOceanBoundary.hpp +++ b/physics/src/modules/include/IOceanBoundary.hpp @@ -9,6 +9,7 @@ #define IOCEANBOUNDARY_HPP #include "include/ModelComponent.hpp" +#include "include/ModelMetadata.hpp" namespace Nextsim { @@ -44,6 +45,7 @@ class IOceanBoundary : public ModelComponent { ModelState getState(const OutputLevel&) const override { return getState(); } std::string getName() const override { return "IOceanBoundary"; } + virtual void setMetadata(const ModelMetadata& metadata) {} void setData(const ModelState::DataMap& ms) override { qio.resize(); From 4f187325d4e892beed4168efcb761b5b8beb4374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 15 Aug 2024 08:27:49 +0200 Subject: [PATCH 05/78] Move as much of OASIS initialisation as I can to its own class All the communicator and partitioning calls are common to ocean, atmosphere, and wave coupling. So, I created OASISCoupled.hpp to keep those calls. OASISCoupledOcean (and others later) then inherits from this class and calls the parent class' setMetadata function in its own setMetadata call. The rest of the child's setMetdata function then takes care of the def_var and end_def calls --- physics/src/include/OASISCoupled.hpp | 58 +++++++++++++++++++ .../OceanBoundaryModule/OASISCoupledOcean.cpp | 31 ++-------- .../include/OASISCoupledOcean.hpp | 3 +- 3 files changed, 64 insertions(+), 28 deletions(-) create mode 100644 physics/src/include/OASISCoupled.hpp diff --git a/physics/src/include/OASISCoupled.hpp b/physics/src/include/OASISCoupled.hpp new file mode 100644 index 000000000..a676581bc --- /dev/null +++ b/physics/src/include/OASISCoupled.hpp @@ -0,0 +1,58 @@ +/*! + * @file OASISCoupled.hpp + * + * @date Aug 15, 2024 + * @author Einar Ólason + */ + +#ifndef OASISCOUPLED_HPP +#define OASISCOUPLED_HPP + +#include "include/ModelMetadata.hpp" + +namespace Nextsim { + +class OASISCoupled { +public: + + virtual void setMetadata(const ModelMetadata& metadata) + { + // TODO: Insert OASIS initialisation calls here + // Set the communicators + /* This is commented for now, as I don't have OASIS ready on my system + * These three may need to be global to the class + * int compID; + * MPI_Comm localComm; + * MPI_Comm coupledComm; + * + * const std::string compName = "nextsim"; + * const std::string functionName = getName()+"::setMetadata"; + * const std::string message = "couldn't initialise component"; + * const std::string file = "OASISCoupledOcean.cpp"; + * const bool coupled = true; + * if ( ! oasis_c_init_comp_with_comm(&compID, &compName.c_str(), coupled, metadata.mpiComm) ) + * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 36); + * + * if ( ! oasis_c_get_localcomm(&localComm) ) + * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 39); + * + * if ( ! oasis_c_create_couplcomm(metadata.mpiMyRank, &localComm, &coupledComm) ) + * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 42); + */ + + // Set the partitioning + /* This is commented for now, as I don't have OASIS ready on my system + * I don't know which values paral and igSize should take(!) + * const std::vector paral; + * const int igSize = -1; + * if ( ! oasis_c_def_partition(&partitionID, ¶l.data(), paral.size(), igSize, &compName.c_str()) ) + * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 42); + * + * def_var and end_def calls are called by the child class + */ + } +}; + +} + +#endif // OASISCOUPLED_HPP diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 241a1a871..de345d51f 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -19,33 +19,10 @@ OASISCoupledOcean::OASISCoupledOcean() void OASISCoupledOcean::setMetadata(const ModelMetadata& metadata) { - // TODO: Insert OASIS initialisation calls here - /* - * These three may need to be global to the class - * int compID; - * MPI_Comm localComm; - * MPI_Comm coupledComm; - * - * std::string compName = "nextsim"; - * std::string functionName = getName()+"::setMetadata"; - * std::string message = "couldn't initialise component"; - * std::string file = "OASISCoupledOcean.cpp"; - * const bool coupled = true; - * if ( ! oasis_c_init_comp_with_comm(&compID, &compName.c_str(), coupled, metadata.mpiComm) ) - * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 36); - * - * if ( ! oasis_c_get_localcomm(&localComm) ) - * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 39); - * - * if ( ! oasis_c_create_couplcomm(metadata.mpiMyRank, &localComm, &coupledComm) ) - * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 42); - * - * ... and then def_partition, def_var and end_def calls - * - * But actually, everything except def_var (and the following end_def) can be seperated out in - * its own OASISCoupled class, which OASISCoupledOcean and OASISCoupledAtmosphere inherit from - * (and OASISCoupledWaves will also inherit from). - */ + // The parent class knows how to set the communicators and partitions + OASISCoupled::setMetadata(metadata); + + // TODO: Insert OASIS def_var and end_def calls } void OASISCoupledOcean::updateBefore(const TimestepTime& tst) diff --git a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp index 3901a25f0..ddcdff706 100644 --- a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp +++ b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp @@ -10,11 +10,12 @@ #define OASISCOUPLEDOCEAN_HPP #include "include/IOceanBoundary.hpp" +#include "include/OASISCoupled.hpp" namespace Nextsim { //* Ocean boundary data values that are hardcoded. -class OASISCoupledOcean : public IOceanBoundary { +class OASISCoupledOcean : public IOceanBoundary, OASISCoupled { public: OASISCoupledOcean(); ~OASISCoupledOcean(); From 03b555eb5e8ff78c72036a7391ca32da0bad7465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 15 Aug 2024 08:33:13 +0200 Subject: [PATCH 06/78] Code cleanup Only formatting changes. --- physics/src/include/OASISCoupled.hpp | 9 +++-- .../OceanBoundaryModule/OASISCoupledOcean.cpp | 2 +- .../modules/include/IAtmosphereBoundary.hpp | 33 +++++++++---------- .../src/modules/include/IOceanBoundary.hpp | 12 +++---- 4 files changed, 27 insertions(+), 29 deletions(-) diff --git a/physics/src/include/OASISCoupled.hpp b/physics/src/include/OASISCoupled.hpp index a676581bc..c15cad07a 100644 --- a/physics/src/include/OASISCoupled.hpp +++ b/physics/src/include/OASISCoupled.hpp @@ -14,7 +14,6 @@ namespace Nextsim { class OASISCoupled { public: - virtual void setMetadata(const ModelMetadata& metadata) { // TODO: Insert OASIS initialisation calls here @@ -31,13 +30,13 @@ class OASISCoupled { * const std::string file = "OASISCoupledOcean.cpp"; * const bool coupled = true; * if ( ! oasis_c_init_comp_with_comm(&compID, &compName.c_str(), coupled, metadata.mpiComm) ) - * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 36); + * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 33); * * if ( ! oasis_c_get_localcomm(&localComm) ) - * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 39); + * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 36); * * if ( ! oasis_c_create_couplcomm(metadata.mpiMyRank, &localComm, &coupledComm) ) - * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 42); + * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 39); */ // Set the partitioning @@ -46,7 +45,7 @@ class OASISCoupled { * const std::vector paral; * const int igSize = -1; * if ( ! oasis_c_def_partition(&partitionID, ¶l.data(), paral.size(), igSize, &compName.c_str()) ) - * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 42); + * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 48); * * def_var and end_def calls are called by the child class */ diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index de345d51f..5a92d02fc 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -49,7 +49,7 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) OASISCoupledOcean::~OASISCoupledOcean() { - // TODO: Insert OASIS finalise call(s) here + // TODO: Insert OASIS finalise call(s) here } } /* namespace Nextsim */ diff --git a/physics/src/modules/include/IAtmosphereBoundary.hpp b/physics/src/modules/include/IAtmosphereBoundary.hpp index 49f8c4cfe..e62a4a80c 100644 --- a/physics/src/modules/include/IAtmosphereBoundary.hpp +++ b/physics/src/modules/include/IAtmosphereBoundary.hpp @@ -16,12 +16,12 @@ namespace Nextsim { namespace CouplingFields { -constexpr TextTag SUBL = "SUBL"; // sublimation mass flux kg s⁻¹ m⁻² -constexpr TextTag SNOW = "SNOW"; // snowfall mass flux kg s⁻¹ m⁻² -constexpr TextTag RAIN = "RAIN"; // rainfall mass flux kg s⁻¹ m⁻² -constexpr TextTag EVAP = "EVAP"; // evaporation mass flux kg s⁻¹ m⁻² -constexpr TextTag WIND_U = "WIND_U"; // x-aligned wind component m s⁻¹ -constexpr TextTag WIND_V = "WIND_V"; // y-aligned wind component m s⁻¹ + constexpr TextTag SUBL = "SUBL"; // sublimation mass flux kg s⁻¹ m⁻² + constexpr TextTag SNOW = "SNOW"; // snowfall mass flux kg s⁻¹ m⁻² + constexpr TextTag RAIN = "RAIN"; // rainfall mass flux kg s⁻¹ m⁻² + constexpr TextTag EVAP = "EVAP"; // evaporation mass flux kg s⁻¹ m⁻² + constexpr TextTag WIND_U = "WIND_U"; // x-aligned wind component m s⁻¹ + constexpr TextTag WIND_V = "WIND_V"; // y-aligned wind component m s⁻¹ } //! An interface class for the atmospheric inputs into the ice physics. @@ -30,15 +30,15 @@ class IAtmosphereBoundary : public ModelComponent { IAtmosphereBoundary() : qia(ModelArray::Type::H) , dqia_dt(ModelArray::Type::H) - , qow(ModelArray::Type::H) - , subl(ModelArray::Type::H) - , snow(ModelArray::Type::H) - , rain(ModelArray::Type::H) - , evap(ModelArray::Type::H) - , emp(ModelArray::Type::H) - , uwind(ModelArray::Type::U) - , vwind(ModelArray::Type::V) - , penSW(ModelArray::Type::H) + , qow(ModelArray::Type::H) + , subl(ModelArray::Type::H) + , snow(ModelArray::Type::H) + , rain(ModelArray::Type::H) + , evap(ModelArray::Type::H) + , emp(ModelArray::Type::H) + , uwind(ModelArray::Type::U) + , vwind(ModelArray::Type::V) + , penSW(ModelArray::Type::H) { m_couplingArrays.registerArray(CouplingFields::SUBL, &subl, RW); m_couplingArrays.registerArray(CouplingFields::SNOW, &snow, RW); @@ -63,7 +63,7 @@ class IAtmosphereBoundary : public ModelComponent { ModelState getState(const OutputLevel&) const override { return getState(); } std::string getName() const override { return "IAtmosphereBoundary"; } - virtual void setMetadata(const ModelMetadata& metadata) {} + virtual void setMetadata(const ModelMetadata& metadata) { } void setData(const ModelState::DataMap& ms) override { qia.resize(); @@ -81,7 +81,6 @@ class IAtmosphereBoundary : public ModelComponent { virtual void update(const TimestepTime& tst) { } protected: - ModelArrayReferenceStore& couplingArrays() { return m_couplingArrays; } HField qia; diff --git a/physics/src/modules/include/IOceanBoundary.hpp b/physics/src/modules/include/IOceanBoundary.hpp index 9cfc4dcf6..4ee567ced 100644 --- a/physics/src/modules/include/IOceanBoundary.hpp +++ b/physics/src/modules/include/IOceanBoundary.hpp @@ -14,11 +14,11 @@ namespace Nextsim { namespace CouplingFields { -constexpr TextTag SST = "SST"; // sea surface temperature ˚C -constexpr TextTag SSS = "SSS"; // sea surface salinity PSU -constexpr TextTag MLD = "MLD"; // Mixed layer or slab ocean depth m -constexpr TextTag OCEAN_U = "U"; // x(east)-ward ocean current m s⁻¹ -constexpr TextTag OCEAN_V = "V"; // y(north)-ward ocean current m s⁻¹ + constexpr TextTag SST = "SST"; // sea surface temperature ˚C + constexpr TextTag SSS = "SSS"; // sea surface salinity PSU + constexpr TextTag MLD = "MLD"; // Mixed layer or slab ocean depth m + constexpr TextTag OCEAN_U = "U"; // x(east)-ward ocean current m s⁻¹ + constexpr TextTag OCEAN_V = "V"; // y(north)-ward ocean current m s⁻¹ } //! An interface class for the oceanic inputs into the ice physics. class IOceanBoundary : public ModelComponent { @@ -45,7 +45,7 @@ class IOceanBoundary : public ModelComponent { ModelState getState(const OutputLevel&) const override { return getState(); } std::string getName() const override { return "IOceanBoundary"; } - virtual void setMetadata(const ModelMetadata& metadata) {} + virtual void setMetadata(const ModelMetadata& metadata) { } void setData(const ModelState::DataMap& ms) override { qio.resize(); From 5549c7ec22c68c3718a0fc310dded50fa04cc083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 15 Aug 2024 09:25:39 +0200 Subject: [PATCH 07/78] Add a destructor in OASISCoupled.hpp and call it We need to call oasis_c_terminate before MPI_Finalize is called. So, I put this in the destructor of OASISCoupled.hpp and make sure that the destructor of OASISCoupledOcean.hpp calls this. This should work, as the model object goes out of scope in main.cpp before MPI_Finalize is called. --- physics/src/include/OASISCoupled.hpp | 16 +++++++++++++++- .../OceanBoundaryModule/OASISCoupledOcean.cpp | 5 ----- .../include/OASISCoupledOcean.hpp | 2 +- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/physics/src/include/OASISCoupled.hpp b/physics/src/include/OASISCoupled.hpp index c15cad07a..aa88eda89 100644 --- a/physics/src/include/OASISCoupled.hpp +++ b/physics/src/include/OASISCoupled.hpp @@ -14,6 +14,20 @@ namespace Nextsim { class OASISCoupled { public: + ~OASISCoupled() + { + // TODO: Insert the oasis terminate call here + /* + * const std::string functionName = "~OASISCoupled"; + * const std::string message = "couldn't terminate OASIS"; + * const std::string file = "OASISCoupled.hpp"; + * if ( ! oasis_c_terminate() ) + * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 26); + */ + } + + virtual std::string getName() const { return "OASISCoupled"; } + virtual void setMetadata(const ModelMetadata& metadata) { // TODO: Insert OASIS initialisation calls here @@ -27,7 +41,7 @@ class OASISCoupled { * const std::string compName = "nextsim"; * const std::string functionName = getName()+"::setMetadata"; * const std::string message = "couldn't initialise component"; - * const std::string file = "OASISCoupledOcean.cpp"; + * const std::string file = "OASISCoupled.hpp"; * const bool coupled = true; * if ( ! oasis_c_init_comp_with_comm(&compID, &compName.c_str(), coupled, metadata.mpiComm) ) * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 33); diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 5a92d02fc..23fc3dab2 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -47,9 +47,4 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) // TODO: Add OASIS send-calls here } -OASISCoupledOcean::~OASISCoupledOcean() -{ - // TODO: Insert OASIS finalise call(s) here -} - } /* namespace Nextsim */ diff --git a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp index ddcdff706..498cd5c1a 100644 --- a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp +++ b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp @@ -18,7 +18,7 @@ namespace Nextsim { class OASISCoupledOcean : public IOceanBoundary, OASISCoupled { public: OASISCoupledOcean(); - ~OASISCoupledOcean(); + ~OASISCoupledOcean() { OASISCoupled::~OASISCoupled(); } std::string getName() const override { return "OASISCoupledOcean"; } void updateBefore(const TimestepTime& tst) override; From c227f13db30fbd20df2e3566151c859d1cdebe1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 15 Aug 2024 10:04:32 +0200 Subject: [PATCH 08/78] More comments on how the oasis calls should look like But no actual code. --- physics/src/include/OASISCoupled.hpp | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/physics/src/include/OASISCoupled.hpp b/physics/src/include/OASISCoupled.hpp index aa88eda89..1452ec534 100644 --- a/physics/src/include/OASISCoupled.hpp +++ b/physics/src/include/OASISCoupled.hpp @@ -44,22 +44,36 @@ class OASISCoupled { * const std::string file = "OASISCoupled.hpp"; * const bool coupled = true; * if ( ! oasis_c_init_comp_with_comm(&compID, &compName.c_str(), coupled, metadata.mpiComm) ) - * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 33); + * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 47); * * if ( ! oasis_c_get_localcomm(&localComm) ) - * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 36); + * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 50); * - * if ( ! oasis_c_create_couplcomm(metadata.mpiMyRank, &localComm, &coupledComm) ) - * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 39); + * const int icpl = 1; + * if ( ! oasis_c_create_couplcomm(icpl, &localComm, &coupledComm) ) + * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 54); */ // Set the partitioning /* This is commented for now, as I don't have OASIS ready on my system - * I don't know which values paral and igSize should take(!) - * const std::vector paral; + * + * From the manual: "vector of integers describing the local grid partition in the global + * index space; has a different expression depending on the type of the partition; in + * OASIS3-MCT, 5 types of partition are supported: Serial (no partition), Apple, Box, + * Orange, and Points" - it looks like we should use "Box", so partInfo[0] = 2. + * + * metdatata contains: localCornerX, localCornerY, localExtentX, localExtentY, globalExtentX, + * globalExtentY; + * + * int partitionID; + * const int offset = metadata.localExtentX*metadata.localCornerY + metadata.localCornerX; + * const std::vector partInfo {2, offset, metadata.localExtentX, metadata.localExtentY, metadata.globalExtentX}; + * + * igSize and name are optional and seem not relevant for our setup. Using -1 and "" in the + * c-interface means they are not sent further when the Fortran code is called. * const int igSize = -1; * if ( ! oasis_c_def_partition(&partitionID, ¶l.data(), paral.size(), igSize, &compName.c_str()) ) - * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 48); + * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 76); * * def_var and end_def calls are called by the child class */ From 3713ea834b865a05626c375b5a2c8c1a786ebab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Fri, 16 Aug 2024 15:00:26 +0200 Subject: [PATCH 09/78] Add some OASIS calls to OASISCoupled.hpp This adds functionality to find OASIS to cmake and some oasis calls to OASISCoupled.hpp. It compiles, but I haven't tested anything yet! --- CMakeLists.txt | 14 ++- cmake/Findoasis.cmake | 21 ++++ physics/src/include/OASISCoupled.hpp | 103 ++++++++++-------- .../OceanBoundaryModule/OASISCoupledOcean.cpp | 15 +++ 4 files changed, 106 insertions(+), 47 deletions(-) create mode 100644 cmake/Findoasis.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index a0034efbd..65655a007 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,6 +85,14 @@ find_package (Python COMPONENTS Interpreter) #"physics" #) +# Do we want to include the OASIS interface? It'll only work if we have MPI +if(ENABLE_MPI) + option(ENABLE_OASIS "Enable the OASIS interface" OFF) + if(ENABLE_OASIS) + find_package(OASIS) + endif () +endif () + # The location of the module_builder.py scripts set(ScriptDirectory "${PROJECT_SOURCE_DIR}/scripts") set(ModuleBuilderScript "${ScriptDirectory}/module_builder.py") @@ -164,6 +172,7 @@ target_compile_definitions(nextsimlib PRIVATE $<$:USE_MPI> $<$:USE_XIOS> + $<$:USE_OASIS> DGCOMP=${DGComp} DGSTRESSCOMP=${DGStressComp} CGDEGREE=${CGDegree} @@ -175,14 +184,16 @@ target_include_directories(nextsimlib $<$:${xios_INCLUDES}> $<$:${xios_EXTERNS}/blitz/> $<$:${xios_EXTERNS}/rapidxml/include> + $<$:${OASIS_INCLUDES}> PUBLIC "${netCDF_INCLUDE_DIR}" ) -target_link_directories(nextsimlib PUBLIC "${netCDF_LIB_DIR}" $<$:${xios_LIBRARIES}>) +target_link_directories(nextsimlib PUBLIC "${netCDF_LIB_DIR}" $<$:${xios_LIBRARIES}> $<$:${OASIS_LIBRARIES}>) target_link_libraries(nextsimlib PUBLIC $<$:xios> $<$:HDF5::HDF5> + $<$:mct mpeu psmile.MPI1 scrip oasis.cbind> Boost::program_options Boost::log "${NSDG_NetCDF_Library}" Eigen3::Eigen $<$:MPI::MPI_C> $<$:MPI::MPI_CXX> "${FORTRAN_RUNTIME_LIB}" @@ -193,6 +204,7 @@ target_compile_definitions(nextsim PRIVATE $<$:USE_MPI> $<$:USE_XIOS> + $<$:USE_OASIS> ) target_include_directories(nextsim PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" diff --git a/cmake/Findoasis.cmake b/cmake/Findoasis.cmake new file mode 100644 index 000000000..d39c9d4bf --- /dev/null +++ b/cmake/Findoasis.cmake @@ -0,0 +1,21 @@ +# Find oasis +# +# Please pass the -DOASIS_DIR variable to cmake (location of the OASIS libraries). + +find_library (OASIS_LIBRARIES NAMES mct mpeu oasis psmile scrip HINTS ${OASIS_DIR}/lib ENV LD_LIBRARY_PATH) + +get_filename_component (OASIS_LIBRARIES "${OASIS_LIBRARIES}" PATH) + +set (OASIS_DIR "${OASIS_LIBRARIES}/../") +cmake_path(NORMAL_PATH OASIS_DIR) + +find_path (OASIS_INCLUDES NAMES oasis_c.h HINTS ${OASIS_DIR}/inc) + +# handle the QUIETLY and REQUIRED arguments and set OASIS_FOUND to TRUE if all listed variables are TRUE +include (FindPackageHandleStandardArgs) +find_package_handle_standard_args(OASIS DEFAULT_MSG + OASIS_LIBRARIES OASIS_INCLUDES) + +# message (STATUS "OASIS_LIBRARIES: ${OASIS_LIBRARIES}") +# message (STATUS "OASIS_INCLUDES: ${OASIS_INCLUDES}") +# message (STATUS "OASIS_DIR: ${OASIS_DIR}") diff --git a/physics/src/include/OASISCoupled.hpp b/physics/src/include/OASISCoupled.hpp index 1452ec534..d860afdec 100644 --- a/physics/src/include/OASISCoupled.hpp +++ b/physics/src/include/OASISCoupled.hpp @@ -9,75 +9,86 @@ #define OASISCOUPLED_HPP #include "include/ModelMetadata.hpp" +#ifdef USE_OASIS +#include +#endif namespace Nextsim { class OASISCoupled { public: + int compID; + +#ifdef USE_OASIS ~OASISCoupled() { - // TODO: Insert the oasis terminate call here - /* - * const std::string functionName = "~OASISCoupled"; - * const std::string message = "couldn't terminate OASIS"; - * const std::string file = "OASISCoupled.hpp"; - * if ( ! oasis_c_terminate() ) - * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 26); - */ + const std::string functionName = __func__; + const std::string message = "Couldn't terminate OASIS"; + const std::string file = __FILE__; + if (!oasis_c_terminate()) + oasis_c_abort(compID, functionName.c_str(), message.c_str(), file.c_str(), 28); } +#else + ~OASISCoupled() { } +#endif virtual std::string getName() const { return "OASISCoupled"; } +#ifdef USE_OASIS virtual void setMetadata(const ModelMetadata& metadata) { // TODO: Insert OASIS initialisation calls here // Set the communicators - /* This is commented for now, as I don't have OASIS ready on my system - * These three may need to be global to the class - * int compID; - * MPI_Comm localComm; - * MPI_Comm coupledComm; - * - * const std::string compName = "nextsim"; - * const std::string functionName = getName()+"::setMetadata"; - * const std::string message = "couldn't initialise component"; - * const std::string file = "OASISCoupled.hpp"; - * const bool coupled = true; - * if ( ! oasis_c_init_comp_with_comm(&compID, &compName.c_str(), coupled, metadata.mpiComm) ) - * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 47); - * - * if ( ! oasis_c_get_localcomm(&localComm) ) - * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 50); - * - * const int icpl = 1; - * if ( ! oasis_c_create_couplcomm(icpl, &localComm, &coupledComm) ) - * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 54); - */ + // These two may need to be global to the class + MPI_Comm localComm; + MPI_Comm coupledComm; + + const std::string compName = "nextsim"; + const std::string functionName = __func__; + const std::string message = "Couldn't initialise component"; + const std::string file = __FILE__; + const bool coupled = true; + if (!oasis_c_init_comp_with_comm(&compID, compName.c_str(), coupled, metadata.mpiComm)) + oasis_c_abort(compID, functionName.c_str(), message.c_str(), file.c_str(), __LINE__); + + if (!oasis_c_get_localcomm(&localComm)) + oasis_c_abort(compID, functionName.c_str(), message.c_str(), file.c_str(), __LINE__); + + const int icpl = 1; + if (!oasis_c_create_couplcomm(icpl, localComm, &coupledComm)) + oasis_c_abort(compID, functionName.c_str(), message.c_str(), file.c_str(), __LINE__); // Set the partitioning - /* This is commented for now, as I don't have OASIS ready on my system - * - * From the manual: "vector of integers describing the local grid partition in the global + /* From the manual: "vector of integers describing the local grid partition in the global * index space; has a different expression depending on the type of the partition; in * OASIS3-MCT, 5 types of partition are supported: Serial (no partition), Apple, Box, * Orange, and Points" - it looks like we should use "Box", so partInfo[0] = 2. * - * metdatata contains: localCornerX, localCornerY, localExtentX, localExtentY, globalExtentX, - * globalExtentY; - * - * int partitionID; - * const int offset = metadata.localExtentX*metadata.localCornerY + metadata.localCornerX; - * const std::vector partInfo {2, offset, metadata.localExtentX, metadata.localExtentY, metadata.globalExtentX}; - * - * igSize and name are optional and seem not relevant for our setup. Using -1 and "" in the - * c-interface means they are not sent further when the Fortran code is called. - * const int igSize = -1; - * if ( ! oasis_c_def_partition(&partitionID, ¶l.data(), paral.size(), igSize, &compName.c_str()) ) - * oasis_c_abort(compID, &functionName.c_str(), &message.c_str(), &file.c_str(), 76); - * - * def_var and end_def calls are called by the child class + * metdatata contains: localCornerX, localCornerY, localExtentX, localExtentY, + * globalExtentX, globalExtentY; */ + int partitionID; + const int offset = metadata.localExtentX * metadata.localCornerY + metadata.localCornerX; + const std::vector partInfo + = { 2, offset, metadata.localExtentX, metadata.localExtentY, metadata.globalExtentX }; + + /* igSize and name are optional and seem not relevant for our setup. Using -1 and "" in the + * c-interface means they are not sent further when the Fortran code is called. */ + const int igSize = -1; + const std::string name = ""; + if (!oasis_c_def_partition( + &partitionID, partInfo.size(), &partInfo[0], igSize, name.c_str())) + oasis_c_abort(compID, functionName.c_str(), message.c_str(), file.c_str(), __LINE__); + + // def_var and end_def calls are called by the child class + } +#else + virtual void setMetadata(const ModelMetadata& metadata) + { + std::string message = __func__ + std::string(": OASIS support not compiled in.\n"); + throw std::runtime_error(message); } +#endif }; } diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 23fc3dab2..4d1a5cc5f 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -22,13 +22,23 @@ void OASISCoupledOcean::setMetadata(const ModelMetadata& metadata) // The parent class knows how to set the communicators and partitions OASISCoupled::setMetadata(metadata); +#ifdef USE_OASIS // TODO: Insert OASIS def_var and end_def calls +#else + std::string message = __func__ + std::string(": OASIS support not compiled in.\n"); + throw std::runtime_error(message); +#endif } void OASISCoupledOcean::updateBefore(const TimestepTime& tst) { // Directly set the array values +#ifdef USE_OASIS // TODO: Replace this code with OASIS receive-calls +#else + std::string message = __func__ + std::string(": OASIS support not compiled in.\n"); + throw std::runtime_error(message); +#endif sss = 32.; u = 0; v = 0; @@ -44,7 +54,12 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) void OASISCoupledOcean::updateAfter(const TimestepTime& tst) { +#ifdef USE_OASIS // TODO: Add OASIS send-calls here +#else + std::string message = __func__ + std::string(": OASIS support not compiled in.\n"); + throw std::runtime_error(message); +#endif } } /* namespace Nextsim */ From c9ebce51fab2c4929961a11f0864cbfa7234e7de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Tue, 20 Aug 2024 16:04:03 +0200 Subject: [PATCH 10/78] Implement OASIS calls Move from all comments to actual code that compiles for the OASIS calls. We still don't have any passing of information to the ocean - this needs implementing. Most of the fields from the ocean to ice are implemented already, but we're missing ssh and we need special handling for mld (which is optional). --- physics/src/include/OASISCoupled.hpp | 70 ++++++------ .../OceanBoundaryModule/OASISCoupledOcean.cpp | 100 +++++++++++++++--- .../include/OASISCoupledOcean.hpp | 28 ++++- 3 files changed, 142 insertions(+), 56 deletions(-) diff --git a/physics/src/include/OASISCoupled.hpp b/physics/src/include/OASISCoupled.hpp index d860afdec..c81a8a3f3 100644 --- a/physics/src/include/OASISCoupled.hpp +++ b/physics/src/include/OASISCoupled.hpp @@ -17,17 +17,8 @@ namespace Nextsim { class OASISCoupled { public: - int compID; - #ifdef USE_OASIS - ~OASISCoupled() - { - const std::string functionName = __func__; - const std::string message = "Couldn't terminate OASIS"; - const std::string file = __FILE__; - if (!oasis_c_terminate()) - oasis_c_abort(compID, functionName.c_str(), message.c_str(), file.c_str(), 28); - } + ~OASISCoupled() { OASIS_CHECK_ERR(!oasis_c_terminate()); } #else ~OASISCoupled() { } #endif @@ -35,50 +26,51 @@ class OASISCoupled { virtual std::string getName() const { return "OASISCoupled"; } #ifdef USE_OASIS + int partitionID; virtual void setMetadata(const ModelMetadata& metadata) { - // TODO: Insert OASIS initialisation calls here // Set the communicators - // These two may need to be global to the class + // Some of these three may need to be global to the class + int compID; MPI_Comm localComm; MPI_Comm coupledComm; const std::string compName = "nextsim"; - const std::string functionName = __func__; - const std::string message = "Couldn't initialise component"; - const std::string file = __FILE__; - const bool coupled = true; - if (!oasis_c_init_comp_with_comm(&compID, compName.c_str(), coupled, metadata.mpiComm)) - oasis_c_abort(compID, functionName.c_str(), message.c_str(), file.c_str(), __LINE__); + OASIS_CHECK_ERR(oasis_c_init_comp_with_comm( + &compID, compName.c_str(), OASIS_COUPLED, metadata.mpiComm)); - if (!oasis_c_get_localcomm(&localComm)) - oasis_c_abort(compID, functionName.c_str(), message.c_str(), file.c_str(), __LINE__); - - const int icpl = 1; - if (!oasis_c_create_couplcomm(icpl, localComm, &coupledComm)) - oasis_c_abort(compID, functionName.c_str(), message.c_str(), file.c_str(), __LINE__); + // Eric didn't include those, but it seems they should be necessary + OASIS_CHECK_ERR(oasis_c_get_localcomm(&localComm)); + OASIS_CHECK_ERR(oasis_c_create_couplcomm(OASIS_COUPLED, localComm, &coupledComm)); // Set the partitioning - /* From the manual: "vector of integers describing the local grid partition in the global - * index space; has a different expression depending on the type of the partition; in - * OASIS3-MCT, 5 types of partition are supported: Serial (no partition), Apple, Box, - * Orange, and Points" - it looks like we should use "Box", so partInfo[0] = 2. + /* From the manual: "[ig_paral is a] vector of integers describing the local grid partition + * in the global index space; has a different expression depending on the type of the + * partition; in OASIS3-MCT, 5 types of partition are supported: Serial (no partition), + * Apple, Box, Orange, and Points" - it looks like we should use "Box", so partInfo[0] = 2 + * (aka. ig_paral). + * + * #Box partition# + * Each partition is a rectangular region of the global domain, described by the global + * offset of its upper left corner, and its local extents in the X and Y dimensions. The + * global extent in the X dimension must also be given. In this case, we have ig_paral(1:5): + * - ig_paral(1) = 2 (indicates a Box partition) + * - ig_paral(2) = the upper left corner global offset + * - ig paral(3) = the local extent in x + * - ig_paral(4) = the local extent in y + * - ig_paral(5) = the global extent in x. * * metdatata contains: localCornerX, localCornerY, localExtentX, localExtentY, * globalExtentX, globalExtentY; */ - int partitionID; - const int offset = metadata.localExtentX * metadata.localCornerY + metadata.localCornerX; - const std::vector partInfo - = { 2, offset, metadata.localExtentX, metadata.localExtentY, metadata.globalExtentX }; + // TODO: The contents of metadata is not certain! + const int offset = metadata.globalExtentX * metadata.localCornerY + metadata.localCornerX; + const std::vector partInfo = { OASIS_Box, offset, metadata.localExtentX, + metadata.localExtentY, metadata.globalExtentX }; - /* igSize and name are optional and seem not relevant for our setup. Using -1 and "" in the - * c-interface means they are not sent further when the Fortran code is called. */ - const int igSize = -1; - const std::string name = ""; - if (!oasis_c_def_partition( - &partitionID, partInfo.size(), &partInfo[0], igSize, name.c_str())) - oasis_c_abort(compID, functionName.c_str(), message.c_str(), file.c_str(), __LINE__); + const int globalSize = metadata.globalExtentX * metadata.globalExtentY; + OASIS_CHECK_ERR(oasis_c_def_partition( + &partitionID, OASIS_Box_Params, &partInfo[0], globalSize, compName.c_str())); // def_var and end_def calls are called by the child class } diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 4d1a5cc5f..2e507603c 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -12,10 +12,6 @@ #include "include/constants.hpp" namespace Nextsim { -OASISCoupledOcean::OASISCoupledOcean() - : IOceanBoundary() -{ -} void OASISCoupledOcean::setMetadata(const ModelMetadata& metadata) { @@ -23,7 +19,25 @@ void OASISCoupledOcean::setMetadata(const ModelMetadata& metadata) OASISCoupled::setMetadata(metadata); #ifdef USE_OASIS - // TODO: Insert OASIS def_var and end_def calls + // OASIS defining variable + + /* Populate cplIdIn with return values from def_var, based on input from cplStringsIn. We assume + * that the coupledIdIn enum starts at zero, is continuously numbered, and is in the same order + * as cplStringsIn. Same story vor the input variables below. */ + cplIdIn.resize(cplStringsIn.size()); + for (int i = 0; i < cplStringsIn.size(); ++i) { + OASIS_CHECK_ERR(oasis_c_def_var( + &cplIdIn[i], cplStringsIn[i].c_str(), partitionID, bundleSize, OASIS_IN, OASIS_DOUBLE)); + } + + cplIdIn.resize(cplStringsOut.size()); + for (int i = 0; i < cplStringsOut.size(); ++i) { + OASIS_CHECK_ERR(oasis_c_def_var(&cplIdOut[i], cplStringsOut[i].c_str(), partitionID, + bundleSize, OASIS_IN, OASIS_DOUBLE)); + } + + // OASIS finalising definition + OASIS_CHECK_ERR(oasis_c_enddef()); #else std::string message = __func__ + std::string(": OASIS support not compiled in.\n"); throw std::runtime_error(message); @@ -35,27 +49,81 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) // Directly set the array values #ifdef USE_OASIS // TODO: Replace this code with OASIS receive-calls -#else - std::string message = __func__ + std::string(": OASIS support not compiled in.\n"); - throw std::runtime_error(message); -#endif - sss = 32.; - u = 0; - v = 0; + + int kinfo; + int date_cpl; // TODO: Figure this one out! + const int dimension0 = ModelArray::dimensions(ModelArray::Type::H)[0]; + const int dimension1 = ModelArray::dimensions(ModelArray::Type::H)[1]; + + OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::SST], date_cpl, dimension0, dimension1, + bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sst[0], &kinfo)); + + OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::SSS], date_cpl, dimension0, dimension1, + bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sss[0], &kinfo)); + + OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::UOCEAN], date_cpl, dimension0, dimension1, + bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &u[0], &kinfo)); + + OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::VOCEAN], date_cpl, dimension0, dimension1, + bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &v[0], &kinfo)); + + // TODO: Implement ssh reading and passing to dynamics! + // OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::SSH], date_cpl, dimension0, dimension1, + // bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &ssh[0], &kinfo)); + + // TODO: Handle mld being an optional field mld = 10.; - double tf32 = -1.751; // Hand calculated from S = 32 using UNESCO - tf = tf32; - sst = tf32; // Tf == SST ensures that there is no ice-ocean heat flux + cpml = Water::cp * Water::rho * mld; - qio = 0.; + + overElements( + std::bind(&OASISCoupledOcean::updateTf, this, std::placeholders::_1, std::placeholders::_2), + TimestepTime()); Module::getImplementation().update(tst); + +#else + std::string message = __func__ + std::string(": OASIS support not compiled in.\n"); + throw std::runtime_error(message); +#endif } void OASISCoupledOcean::updateAfter(const TimestepTime& tst) { #ifdef USE_OASIS // TODO: Add OASIS send-calls here + int kinfo; + int date_cpl; // TODO: Figure this one out! + const int dimension0 = ModelArray::dimensions(ModelArray::Type::H)[0]; + const int dimension1 = ModelArray::dimensions(ModelArray::Type::H)[1]; + + // We still need the the outputs + HField dummy; + dummy.resize(); + dummy.setData(0.); + OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::TAUX], date_cpl, dimension0, dimension1, 1, + OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + + OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::TAUY], date_cpl, dimension0, dimension1, 1, + OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + + OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::EMP], date_cpl, dimension0, dimension1, 1, + OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + + OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::QSW], date_cpl, dimension0, dimension1, 1, + OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + + OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::QNOSUN], date_cpl, dimension0, dimension1, + 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + + OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::SFLX], date_cpl, dimension0, dimension1, 1, + OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + + OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::TAUMOD], date_cpl, dimension0, dimension1, + 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + + OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::CICE], date_cpl, dimension0, dimension1, 1, + OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); #else std::string message = __func__ + std::string(": OASIS support not compiled in.\n"); throw std::runtime_error(message); diff --git a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp index 498cd5c1a..681106bc0 100644 --- a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp +++ b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp @@ -9,21 +9,47 @@ #ifndef OASISCOUPLEDOCEAN_HPP #define OASISCOUPLEDOCEAN_HPP +#include "include/IFreezingPoint.hpp" #include "include/IOceanBoundary.hpp" +#include "include/Module.hpp" #include "include/OASISCoupled.hpp" +#include "include/SlabOcean.hpp" namespace Nextsim { //* Ocean boundary data values that are hardcoded. class OASISCoupledOcean : public IOceanBoundary, OASISCoupled { public: - OASISCoupledOcean(); + OASISCoupledOcean() + : IOceanBoundary() + { + } ~OASISCoupledOcean() { OASISCoupled::~OASISCoupled(); } std::string getName() const override { return "OASISCoupledOcean"; } void updateBefore(const TimestepTime& tst) override; void updateAfter(const TimestepTime& tst) override; void setMetadata(const ModelMetadata& metadata) override; + +private: + int bundleSize = 1; + + SlabOcean slabOcean; + + void updateTf(size_t i, const TimestepTime& tst) + { + tf[i] = Module::getImplementation()(sss[i]); + } + + enum couplingIdIn { SST, SSS, UOCEAN, VOCEAN, SSH, MLD }; // MLD is optional + enum couplingIdOut { TAUX, TAUY, TAUMOD, EMP, QNOSUN, QSW, SFLX, CICE }; + + const std::vector cplStringsIn + = { "I_SST", "I_SSS", "I_Uocn", "I_Vocn", "I_SSH", "IFrcQsr" }; + const std::vector cplStringsOut + = { "I_taux", "I_tauy", "I_taumod", "I_fwflux", "I_rsnos", "I_rsso", "I_sfi", "I_conc" }; + + std::vector cplIdIn, cplIdOut; }; } /* namespace Nextsim */ From 3e51d86bf369d0f0f7732abb701b1f579348e7e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Tue, 20 Aug 2024 16:40:40 +0200 Subject: [PATCH 11/78] Revert a merge mistake Something seems to have gone wrong in merge commit 1a155c9c. Reverting the error makes testTOPAZOcn run again. --- physics/src/modules/include/IOceanBoundary.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/physics/src/modules/include/IOceanBoundary.hpp b/physics/src/modules/include/IOceanBoundary.hpp index 4ee567ced..fa1a942dc 100644 --- a/physics/src/modules/include/IOceanBoundary.hpp +++ b/physics/src/modules/include/IOceanBoundary.hpp @@ -29,11 +29,11 @@ class IOceanBoundary : public ModelComponent { m_couplingArrays.registerArray(CouplingFields::SSS, &sss, RW); m_couplingArrays.registerArray(CouplingFields::OCEAN_U, &u, RW); m_couplingArrays.registerArray(CouplingFields::OCEAN_V, &v, RW); - m_couplingArrays.registerArray(CouplingFields::MLD, &mld, RW); getStore().registerArray(Shared::Q_IO, &qio, RW); getStore().registerArray(Protected::SST, &sst, RO); getStore().registerArray(Protected::SSS, &sss, RO); + getStore().registerArray(Protected::MLD, &mld, RO); getStore().registerArray(Protected::ML_BULK_CP, &cpml, RO); getStore().registerArray(Protected::TF, &tf, RO); getStore().registerArray(Protected::OCEAN_U, &u, RO); From ab0c3048aae74fb4b28aed2e3b31b97ca46cd472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Wed, 21 Aug 2024 10:05:33 +0200 Subject: [PATCH 12/78] Add OASIS time OASIS needs to know how many seconds have passed since the experiment started. I've added a simple internal counter for this, which is local to OASISCoupled.hpp. --- physics/src/include/OASISCoupled.hpp | 11 +++++-- .../OceanBoundaryModule/OASISCoupledOcean.cpp | 32 +++++++++---------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/physics/src/include/OASISCoupled.hpp b/physics/src/include/OASISCoupled.hpp index c81a8a3f3..4f83a437a 100644 --- a/physics/src/include/OASISCoupled.hpp +++ b/physics/src/include/OASISCoupled.hpp @@ -26,9 +26,12 @@ class OASISCoupled { virtual std::string getName() const { return "OASISCoupled"; } #ifdef USE_OASIS - int partitionID; - virtual void setMetadata(const ModelMetadata& metadata) + int partitionID, OASISTime; + void setMetadata(const ModelMetadata& metadata) { + // Set the "OASIS time" (seconds since start) to zero + OASISTime = 0; + // Set the communicators // Some of these three may need to be global to the class int compID; @@ -74,6 +77,10 @@ class OASISCoupled { // def_var and end_def calls are called by the child class } + + // Increment the "OASIS" time by the number of seconds in the time step + // Must be called at the end of the child class' update or updateAfter call. + void updateOASISTime(const TimestepTime &tst) { OASISTime += tst.step.seconds(); } #else virtual void setMetadata(const ModelMetadata& metadata) { diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 2e507603c..40c8f2822 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -48,27 +48,25 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) { // Directly set the array values #ifdef USE_OASIS - // TODO: Replace this code with OASIS receive-calls int kinfo; - int date_cpl; // TODO: Figure this one out! const int dimension0 = ModelArray::dimensions(ModelArray::Type::H)[0]; const int dimension1 = ModelArray::dimensions(ModelArray::Type::H)[1]; - OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::SST], date_cpl, dimension0, dimension1, + OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::SST], OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sst[0], &kinfo)); - OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::SSS], date_cpl, dimension0, dimension1, + OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::SSS], OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sss[0], &kinfo)); - OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::UOCEAN], date_cpl, dimension0, dimension1, + OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::UOCEAN], OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &u[0], &kinfo)); - OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::VOCEAN], date_cpl, dimension0, dimension1, + OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::VOCEAN], OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &v[0], &kinfo)); // TODO: Implement ssh reading and passing to dynamics! - // OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::SSH], date_cpl, dimension0, dimension1, + // OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::SSH], OASISTime, dimension0, dimension1, // bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &ssh[0], &kinfo)); // TODO: Handle mld being an optional field @@ -93,7 +91,6 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) #ifdef USE_OASIS // TODO: Add OASIS send-calls here int kinfo; - int date_cpl; // TODO: Figure this one out! const int dimension0 = ModelArray::dimensions(ModelArray::Type::H)[0]; const int dimension1 = ModelArray::dimensions(ModelArray::Type::H)[1]; @@ -101,29 +98,32 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) HField dummy; dummy.resize(); dummy.setData(0.); - OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::TAUX], date_cpl, dimension0, dimension1, 1, + OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::TAUX], OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::TAUY], date_cpl, dimension0, dimension1, 1, + OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::TAUY], OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::EMP], date_cpl, dimension0, dimension1, 1, + OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::EMP], OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::QSW], date_cpl, dimension0, dimension1, 1, + OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::QSW], OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::QNOSUN], date_cpl, dimension0, dimension1, + OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::QNOSUN], OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::SFLX], date_cpl, dimension0, dimension1, 1, + OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::SFLX], OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::TAUMOD], date_cpl, dimension0, dimension1, + OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::TAUMOD], OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::CICE], date_cpl, dimension0, dimension1, 1, + OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::CICE], OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + + // Increment the "OASIS" time by the number of seconds in the time step + updateOASISTime(tst); #else std::string message = __func__ + std::string(": OASIS support not compiled in.\n"); throw std::runtime_error(message); From a2e6c050356056dffdeb9ead68afd461506867df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Wed, 21 Aug 2024 11:13:29 +0200 Subject: [PATCH 13/78] Clang formatting No other changes --- physics/src/include/OASISCoupled.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/physics/src/include/OASISCoupled.hpp b/physics/src/include/OASISCoupled.hpp index 4f83a437a..be8a34b1a 100644 --- a/physics/src/include/OASISCoupled.hpp +++ b/physics/src/include/OASISCoupled.hpp @@ -80,7 +80,7 @@ class OASISCoupled { // Increment the "OASIS" time by the number of seconds in the time step // Must be called at the end of the child class' update or updateAfter call. - void updateOASISTime(const TimestepTime &tst) { OASISTime += tst.step.seconds(); } + void updateOASISTime(const TimestepTime& tst) { OASISTime += tst.step.seconds(); } #else virtual void setMetadata(const ModelMetadata& metadata) { From 3e6ccdc020abdf50d032d507138b1dba546a532f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Wed, 21 Aug 2024 11:45:55 +0200 Subject: [PATCH 14/78] Use a map for the coupling id This uses std::map to link the namcople strings and the values returned by oasis_c_def_var. Much better than what I had before! --- .../OceanBoundaryModule/OASISCoupledOcean.cpp | 61 ++++++++++--------- .../include/OASISCoupledOcean.hpp | 23 +++++-- 2 files changed, 51 insertions(+), 33 deletions(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 40c8f2822..4de311b61 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -21,19 +21,20 @@ void OASISCoupledOcean::setMetadata(const ModelMetadata& metadata) #ifdef USE_OASIS // OASIS defining variable - /* Populate cplIdIn with return values from def_var, based on input from cplStringsIn. We assume - * that the coupledIdIn enum starts at zero, is continuously numbered, and is in the same order - * as cplStringsIn. Same story vor the input variables below. */ - cplIdIn.resize(cplStringsIn.size()); - for (int i = 0; i < cplStringsIn.size(); ++i) { + /* Populate the couplingId map with the id string and number pair. We need to do this seperately + * for the input (get) and output (put) variables. */ + for (std::string idString : cplStringsIn) { + int idNumber; OASIS_CHECK_ERR(oasis_c_def_var( - &cplIdIn[i], cplStringsIn[i].c_str(), partitionID, bundleSize, OASIS_IN, OASIS_DOUBLE)); + &idNumber, idString.c_str(), partitionID, bundleSize, OASIS_IN, OASIS_DOUBLE)); + couplingId[idString] = idNumber; } - cplIdIn.resize(cplStringsOut.size()); - for (int i = 0; i < cplStringsOut.size(); ++i) { - OASIS_CHECK_ERR(oasis_c_def_var(&cplIdOut[i], cplStringsOut[i].c_str(), partitionID, - bundleSize, OASIS_IN, OASIS_DOUBLE)); + for (std::string idString : cplStringsOut) { + int idNumber; + OASIS_CHECK_ERR(oasis_c_def_var( + &idNumber, idString.c_str(), partitionID, bundleSize, OASIS_OUT, OASIS_DOUBLE)); + couplingId[idString] = idNumber; } // OASIS finalising definition @@ -53,24 +54,28 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) const int dimension0 = ModelArray::dimensions(ModelArray::Type::H)[0]; const int dimension1 = ModelArray::dimensions(ModelArray::Type::H)[1]; - OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::SST], OASISTime, dimension0, dimension1, - bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sst[0], &kinfo)); + OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SST), OASISTime, dimension0, dimension1, bundleSize, + OASIS_DOUBLE, OASIS_COL_MAJOR, &sst[0], &kinfo)); - OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::SSS], OASISTime, dimension0, dimension1, - bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sss[0], &kinfo)); + OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSS), OASISTime, dimension0, dimension1, bundleSize, + OASIS_DOUBLE, OASIS_COL_MAJOR, &sss[0], &kinfo)); - OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::UOCEAN], OASISTime, dimension0, dimension1, + OASIS_CHECK_ERR(oasis_c_get(couplingId.at(UOCEAN), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &u[0], &kinfo)); - OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::VOCEAN], OASISTime, dimension0, dimension1, + OASIS_CHECK_ERR(oasis_c_get(couplingId.at(VOCEAN), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &v[0], &kinfo)); // TODO: Implement ssh reading and passing to dynamics! // OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::SSH], OASISTime, dimension0, dimension1, // bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &ssh[0], &kinfo)); - // TODO: Handle mld being an optional field - mld = 10.; + // TODO: Handle mld being an optional field with a command line option for the value + if (couplingId.find(SSH) != couplingId.end()) + OASIS_CHECK_ERR(oasis_c_get(couplingId.at(VOCEAN), OASISTime, dimension0, dimension1, + bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &mld[0], &kinfo)); + else + mld = 10.; cpml = Water::cp * Water::rho * mld; @@ -98,28 +103,28 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) HField dummy; dummy.resize(); dummy.setData(0.); - OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::TAUX], OASISTime, dimension0, dimension1, 1, + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TAUX), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::TAUY], OASISTime, dimension0, dimension1, 1, + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TAUY), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::EMP], OASISTime, dimension0, dimension1, 1, + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(EMP), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::QSW], OASISTime, dimension0, dimension1, 1, + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(QSW), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::QNOSUN], OASISTime, dimension0, dimension1, - 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(QNOSUN), OASISTime, dimension0, dimension1, 1, + OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::SFLX], OASISTime, dimension0, dimension1, 1, + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(SFLX), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::TAUMOD], OASISTime, dimension0, dimension1, - 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TAUMOD), OASISTime, dimension0, dimension1, 1, + OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(cplIdOut[couplingIdOut::CICE], OASISTime, dimension0, dimension1, 1, + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(CICE), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); // Increment the "OASIS" time by the number of seconds in the time step diff --git a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp index 681106bc0..647f6c687 100644 --- a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp +++ b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp @@ -41,15 +41,28 @@ class OASISCoupledOcean : public IOceanBoundary, OASISCoupled { tf[i] = Module::getImplementation()(sss[i]); } - enum couplingIdIn { SST, SSS, UOCEAN, VOCEAN, SSH, MLD }; // MLD is optional - enum couplingIdOut { TAUX, TAUY, TAUMOD, EMP, QNOSUN, QSW, SFLX, CICE }; + // A map to relate the strings in the namcouple file to the numbers def_var spits out + std::map couplingId; + std::string SST = "I_SST"; + std::string SSS = "I_SSS"; + std::string UOCEAN = "I_Uocn"; + std::string VOCEAN = "I_Vocn"; + std::string SSH = "I_SSH"; + std::string MLD = "I_MLD"; // This one is optional + std::string TAUX = "I_taux"; + std::string TAUY = "I_tauy"; + std::string TAUMOD = "I_taumod"; + std::string EMP = "I_fwflux"; + std::string QNOSUN = "I_rsnos"; + std::string QSW = "I_rsso"; + std::string SFLX = "I_sfi"; + std::string CICE = "I_conc"; const std::vector cplStringsIn - = { "I_SST", "I_SSS", "I_Uocn", "I_Vocn", "I_SSH", "IFrcQsr" }; + = { SST, SSS, UOCEAN, VOCEAN, SSH }; // MLD can be added to this one const std::vector cplStringsOut - = { "I_taux", "I_tauy", "I_taumod", "I_fwflux", "I_rsnos", "I_rsso", "I_sfi", "I_conc" }; + = { TAUX, TAUY, TAUMOD, EMP, QNOSUN, QSW, SFLX, CICE }; - std::vector cplIdIn, cplIdOut; }; } /* namespace Nextsim */ From e8d363381a9d34b52fbf7d141e3da60563bfe5b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Wed, 21 Aug 2024 16:09:50 +0200 Subject: [PATCH 15/78] Options for the couplingId code There are now a lot of options to set, so that it's flexible what you put in the namcouple file. --- .../OceanBoundaryModule/OASISCoupledOcean.cpp | 113 ++++++++++++++---- .../include/OASISCoupledOcean.hpp | 77 ++++++++---- 2 files changed, 146 insertions(+), 44 deletions(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 4de311b61..2e4a00a3d 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -54,28 +54,29 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) const int dimension0 = ModelArray::dimensions(ModelArray::Type::H)[0]; const int dimension1 = ModelArray::dimensions(ModelArray::Type::H)[1]; - OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SST), OASISTime, dimension0, dimension1, bundleSize, - OASIS_DOUBLE, OASIS_COL_MAJOR, &sst[0], &kinfo)); + OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSTKey), OASISTime, dimension0, dimension1, + bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sst[0], &kinfo)); - OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSS), OASISTime, dimension0, dimension1, bundleSize, - OASIS_DOUBLE, OASIS_COL_MAJOR, &sss[0], &kinfo)); + OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSSKey), OASISTime, dimension0, dimension1, + bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sss[0], &kinfo)); - OASIS_CHECK_ERR(oasis_c_get(couplingId.at(UOCEAN), OASISTime, dimension0, dimension1, + OASIS_CHECK_ERR(oasis_c_get(couplingId.at(UOceanKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &u[0], &kinfo)); - OASIS_CHECK_ERR(oasis_c_get(couplingId.at(VOCEAN), OASISTime, dimension0, dimension1, + OASIS_CHECK_ERR(oasis_c_get(couplingId.at(VOceanKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &v[0], &kinfo)); // TODO: Implement ssh reading and passing to dynamics! - // OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::SSH], OASISTime, dimension0, dimension1, + // OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::SSHKey], OASISTime, dimension0, + // dimension1, // bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &ssh[0], &kinfo)); - // TODO: Handle mld being an optional field with a command line option for the value - if (couplingId.find(SSH) != couplingId.end()) - OASIS_CHECK_ERR(oasis_c_get(couplingId.at(VOCEAN), OASISTime, dimension0, dimension1, + if (couplingId.find(SSHKey) != couplingId.end()) { + OASIS_CHECK_ERR(oasis_c_get(couplingId.at(VOceanKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &mld[0], &kinfo)); - else - mld = 10.; + } else { + mld = firstLayerDepth; + } cpml = Water::cp * Water::rho * mld; @@ -94,37 +95,36 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) void OASISCoupledOcean::updateAfter(const TimestepTime& tst) { #ifdef USE_OASIS - // TODO: Add OASIS send-calls here int kinfo; const int dimension0 = ModelArray::dimensions(ModelArray::Type::H)[0]; const int dimension1 = ModelArray::dimensions(ModelArray::Type::H)[1]; - // We still need the the outputs + // TODO We still need the the actual data HField dummy; dummy.resize(); dummy.setData(0.); - OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TAUX), OASISTime, dimension0, dimension1, 1, + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauXKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TAUY), OASISTime, dimension0, dimension1, 1, + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauYKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(couplingId.at(EMP), OASISTime, dimension0, dimension1, 1, + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(EMPKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(couplingId.at(QSW), OASISTime, dimension0, dimension1, 1, + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(QSWKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(couplingId.at(QNOSUN), OASISTime, dimension0, dimension1, 1, + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(QNoSunKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(couplingId.at(SFLX), OASISTime, dimension0, dimension1, 1, + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(SFluxKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TAUMOD), OASISTime, dimension0, dimension1, 1, + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauModKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - OASIS_CHECK_ERR(oasis_c_put(couplingId.at(CICE), OASISTime, dimension0, dimension1, 1, + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(CIceKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); // Increment the "OASIS" time by the number of seconds in the time step @@ -135,4 +135,73 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) #endif } +void OASISCoupledOcean::configure() +{ + firstLayerDepth = Configured::getConfiguration(layerDepthConfigKey, FIRST_LAYER_DEPTH); + if (Configured::getConfiguration(exchangeFirstLayerConfigKey, EXCHANGE_FIRST_LAYER)) { + cplStringsIn.push_back(MLDKey); + } + + SSTKey = Configured::getConfiguration(SSTConfigKey, SSTKeyDefault); + SSSKey = Configured::getConfiguration(SSSConfigKey, SSSKeyDefault); + UOceanKey = Configured::getConfiguration(UOceanConfigKey, UOceanKeyDefault); + VOceanKey = Configured::getConfiguration(VOceanConfigKey, VOceanKeyDefault); + SSHKey = Configured::getConfiguration(SSHConfigKey, SSHKeyDefault); + MLDKey = Configured::getConfiguration(MLDConfigKey, MLDKeyDefault); + TauXKey = Configured::getConfiguration(TauXConfigKey, TauXKeyDefault); + TauYKey = Configured::getConfiguration(TauYConfigKey, TauYKeyDefault); + TauModKey = Configured::getConfiguration(TauModConfigKey, TauModKeyDefault); + EMPKey = Configured::getConfiguration(EMPConfigKey, EMPKeyDefault); + QNoSunKey = Configured::getConfiguration(QNoSunConfigKey, QNoSunKeyDefault); + QSWKey = Configured::getConfiguration(QSWConfigKey, QSWKeyDefault); + SFluxKey = Configured::getConfiguration(SFluxConfigKey, SFluxKeyDefault); + CIceKey = Configured::getConfiguration(CIceConfigKey, CIceKeyDefault); +} + +OASISCoupledOcean::HelpMap& OASISCoupledOcean::getHelpText(HelpMap& map, bool getAll) +{ + map[moduleName] = { + { layerDepthConfigKey, ConfigType::NUMERIC, { "0", "∞" }, std::to_string(FIRST_LAYER_DEPTH), + "m", "Depth of the first ocean model layer (if this is fixed)." }, + { exchangeFirstLayerConfigKey, ConfigType::BOOLEAN, { "true", "false" }, + std::to_string(EXCHANGE_FIRST_LAYER), "", + "Use the thickness of the first ocean layer provided through the coupler" }, + { SSTConfigKey, ConfigType::STRING, {}, SSTKeyDefault, "", + "The field name for sea surface temperature used in namcouple" }, + { SSSConfigKey, ConfigType::STRING, {}, SSSKeyDefault, "", + "The field name for sea surface salinity used in namcouple" }, + { UOceanConfigKey, ConfigType::STRING, {}, UOceanKeyDefault, "", + "The field name for ocean u-velocity used in namcouple" }, + { VOceanConfigKey, ConfigType::STRING, {}, VOceanKeyDefault, "", + "The field name for ocean v-velocity used in namcouple" }, + { SSHConfigKey, ConfigType::STRING, {}, SSHKeyDefault, "", + "The field name for sea surface height used in namcouple" }, + { MLDConfigKey, ConfigType::STRING, {}, MLDKeyDefault, "", + "The field name for the thickness of the first ocean layer in namcouple (if that's defined)." }, + { TauXConfigKey, ConfigType::STRING, {}, TauXKeyDefault, "", + "The field name for the x-component of ice-ocean stress in namcouple." }, + { TauYConfigKey, ConfigType::STRING, {}, TauYKeyDefault, "", + "The field name for the y-component of ice-ocean stress in namcouple." }, + { TauModConfigKey, ConfigType::STRING, {}, TauModKeyDefault, "", + "The field name for the modulus of ice-ocean stress in namcouple." }, + { EMPConfigKey, ConfigType::STRING, {}, EMPKeyDefault, "", + "The field name for freshwater flux used in namcouple" }, + { QNoSunConfigKey, ConfigType::STRING, {}, QNoSunKeyDefault, "", + "The field name for non-solar flux used in namcouple" }, + { QSWConfigKey, ConfigType::STRING, {}, QSWKeyDefault, "", + "The field name for showrt-wave flux used in namcouple" }, + { QSWConfigKey, ConfigType::STRING, {}, QSWKeyDefault, "", + "The field name for showrt-wave flux used in namcouple" }, + { SFluxConfigKey, ConfigType::STRING, {}, SFluxKeyDefault, "", + "The field name for salt flux used in namcouple" }, + { CIceConfigKey, ConfigType::STRING, {}, CIceKeyDefault, "", + "The field name for sea-ice concentration used in namcouple" }, + }; + return map; +} +OASISCoupledOcean::HelpMap& OASISCoupledOcean::getHelpRecursive(HelpMap& map, bool getAll) +{ + return getHelpText(map, getAll); +} + } /* namespace Nextsim */ diff --git a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp index 647f6c687..75bd91715 100644 --- a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp +++ b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp @@ -17,8 +17,48 @@ namespace Nextsim { +static const std::string moduleName = "OASISCoupledOcean"; + +static const std::string layerDepthConfigKey = moduleName + ".layer_depth"; +static const std::string exchangeFirstLayerConfigKey = moduleName + ".exchange_first_layer"; + +static const double FIRST_LAYER_DEPTH = 1.; // There really is no sensible default here(!) +static const bool EXCHANGE_FIRST_LAYER = false; + +static const std::string SSTKeyDefault = "I_SST"; +static const std::string SSSKeyDefault = "I_SSS"; +static const std::string UOceanKeyDefault = "I_Uocn"; +static const std::string VOceanKeyDefault = "I_Vocn"; +static const std::string SSHKeyDefault = "I_SSH"; +static const std::string MLDKeyDefault = "I_MLD"; // This one is optional +static const std::string TauXKeyDefault = "I_taux"; +static const std::string TauYKeyDefault = "I_tauy"; +static const std::string TauModKeyDefault = "I_taumod"; +static const std::string EMPKeyDefault = "I_fwflux"; +static const std::string QNoSunKeyDefault = "I_rsnos"; +static const std::string QSWKeyDefault = "I_rsso"; +static const std::string SFluxKeyDefault = "I_sfi"; +static const std::string CIceKeyDefault = "I_conc"; + +static const std::string SSTConfigKey = ".sea_surface_temperature"; +static const std::string SSSConfigKey = ".sea_surface_salinity"; +static const std::string UOceanConfigKey = ".ocean_u_velocity"; +static const std::string VOceanConfigKey = ".ocean_v_velocity"; +static const std::string SSHConfigKey = ".sea_surface_height"; +static const std::string MLDConfigKey = ".first_ocean_layer_depth"; // This one is optional +static const std::string TauXConfigKey = ".ice_ocean_stress_x"; +static const std::string TauYConfigKey = ".ice_ocean_stress_y"; +static const std::string TauModConfigKey = ".ice_ocean_stress_modulo"; +static const std::string EMPConfigKey = ".fresh_water_flux"; +static const std::string QNoSunConfigKey = ".non_solar_heatflux"; +static const std::string QSWConfigKey = ".short_wave_flux"; +static const std::string SFluxConfigKey = ".salt_flux"; +static const std::string CIceConfigKey = ".sea_ice_concentration"; + //* Ocean boundary data values that are hardcoded. -class OASISCoupledOcean : public IOceanBoundary, OASISCoupled { +class OASISCoupledOcean : public IOceanBoundary, + public OASISCoupled, + public Configured { public: OASISCoupledOcean() : IOceanBoundary() @@ -26,13 +66,19 @@ class OASISCoupledOcean : public IOceanBoundary, OASISCoupled { } ~OASISCoupledOcean() { OASISCoupled::~OASISCoupled(); } - std::string getName() const override { return "OASISCoupledOcean"; } + std::string getName() const override { return moduleName; } void updateBefore(const TimestepTime& tst) override; void updateAfter(const TimestepTime& tst) override; void setMetadata(const ModelMetadata& metadata) override; + void configure() override; + + static HelpMap& getHelpText(HelpMap& map, bool getAll); + static HelpMap& getHelpRecursive(HelpMap& map, bool getAll); + private: - int bundleSize = 1; + int bundleSize = 1; // Always "unbundled", as per the OASIS manual + double firstLayerDepth = FIRST_LAYER_DEPTH; SlabOcean slabOcean; @@ -43,26 +89,13 @@ class OASISCoupledOcean : public IOceanBoundary, OASISCoupled { // A map to relate the strings in the namcouple file to the numbers def_var spits out std::map couplingId; - std::string SST = "I_SST"; - std::string SSS = "I_SSS"; - std::string UOCEAN = "I_Uocn"; - std::string VOCEAN = "I_Vocn"; - std::string SSH = "I_SSH"; - std::string MLD = "I_MLD"; // This one is optional - std::string TAUX = "I_taux"; - std::string TAUY = "I_tauy"; - std::string TAUMOD = "I_taumod"; - std::string EMP = "I_fwflux"; - std::string QNOSUN = "I_rsnos"; - std::string QSW = "I_rsso"; - std::string SFLX = "I_sfi"; - std::string CICE = "I_conc"; - - const std::vector cplStringsIn - = { SST, SSS, UOCEAN, VOCEAN, SSH }; // MLD can be added to this one - const std::vector cplStringsOut - = { TAUX, TAUY, TAUMOD, EMP, QNOSUN, QSW, SFLX, CICE }; + std::string SSTKey, SSSKey, UOceanKey, VOceanKey, SSHKey, MLDKey, TauXKey, TauYKey, TauModKey, + EMPKey, QNoSunKey, QSWKey, SFluxKey, CIceKey; + std::vector cplStringsIn + = { SSTKey, SSSKey, UOceanKey, VOceanKey, SSHKey }; // MLDKey can be added to this one + std::vector cplStringsOut + = { TauXKey, TauYKey, TauModKey, EMPKey, QNoSunKey, QSWKey, SFluxKey, CIceKey }; }; } /* namespace Nextsim */ From db21d611534909ac29eac29f7bd3466da6bd66c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 22 Aug 2024 15:08:16 +0200 Subject: [PATCH 16/78] Small bug fix for OASISCoupledOcean::configure I (optionally) pushed MLDKey to cplStringsIn before it could be redefined by the configuration system. --- .../modules/OceanBoundaryModule/OASISCoupledOcean.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 2e4a00a3d..a8dd75ee1 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -137,11 +137,6 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) void OASISCoupledOcean::configure() { - firstLayerDepth = Configured::getConfiguration(layerDepthConfigKey, FIRST_LAYER_DEPTH); - if (Configured::getConfiguration(exchangeFirstLayerConfigKey, EXCHANGE_FIRST_LAYER)) { - cplStringsIn.push_back(MLDKey); - } - SSTKey = Configured::getConfiguration(SSTConfigKey, SSTKeyDefault); SSSKey = Configured::getConfiguration(SSSConfigKey, SSSKeyDefault); UOceanKey = Configured::getConfiguration(UOceanConfigKey, UOceanKeyDefault); @@ -156,6 +151,11 @@ void OASISCoupledOcean::configure() QSWKey = Configured::getConfiguration(QSWConfigKey, QSWKeyDefault); SFluxKey = Configured::getConfiguration(SFluxConfigKey, SFluxKeyDefault); CIceKey = Configured::getConfiguration(CIceConfigKey, CIceKeyDefault); + + firstLayerDepth = Configured::getConfiguration(layerDepthConfigKey, FIRST_LAYER_DEPTH); + if (Configured::getConfiguration(exchangeFirstLayerConfigKey, EXCHANGE_FIRST_LAYER)) { + cplStringsIn.push_back(MLDKey); + } } OASISCoupledOcean::HelpMap& OASISCoupledOcean::getHelpText(HelpMap& map, bool getAll) From b9d2da54400e3d2868564868c420b149408878eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 29 Aug 2024 10:54:26 +0200 Subject: [PATCH 17/78] A few comments for the OASIS team Just so Andrea and Sophie see what I'm thinking --- physics/src/include/OASISCoupled.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/physics/src/include/OASISCoupled.hpp b/physics/src/include/OASISCoupled.hpp index be8a34b1a..8363974e6 100644 --- a/physics/src/include/OASISCoupled.hpp +++ b/physics/src/include/OASISCoupled.hpp @@ -1,7 +1,7 @@ /*! * @file OASISCoupled.hpp * - * @date Aug 15, 2024 + * @date 29 Aug 2024 * @author Einar Ólason */ @@ -30,6 +30,7 @@ class OASISCoupled { void setMetadata(const ModelMetadata& metadata) { // Set the "OASIS time" (seconds since start) to zero + // TODO: We need to figure out if this is ok to do when restarting OASISTime = 0; // Set the communicators @@ -38,6 +39,7 @@ class OASISCoupled { MPI_Comm localComm; MPI_Comm coupledComm; + // compName could be configurable, but I don't see the need const std::string compName = "nextsim"; OASIS_CHECK_ERR(oasis_c_init_comp_with_comm( &compID, compName.c_str(), OASIS_COUPLED, metadata.mpiComm)); @@ -75,6 +77,8 @@ class OASISCoupled { OASIS_CHECK_ERR(oasis_c_def_partition( &partitionID, OASIS_Box_Params, &partInfo[0], globalSize, compName.c_str())); + // TODO: Writing out grid information should be possible, but optional + // def_var and end_def calls are called by the child class } From be7905d2b0a3e179c8e66a100d83239dc3ceeb57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Tue, 27 Aug 2024 13:26:40 +0200 Subject: [PATCH 18/78] Minor cleaning Just sorting out and gathering some output strings. --- CMakeLists.txt | 2 +- physics/src/include/OASISCoupled.hpp | 2 ++ .../OceanBoundaryModule/OASISCoupledOcean.cpp | 16 +++++++--------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 18c4694e5..ac7067b5d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,7 +158,7 @@ if (ENABLE_OASIS) target_link_libraries(nextsimlib PUBLIC oasis.cbind mct mpeu psmile.MPI1 scrip netcdff) target_include_directories(nextsimlib PRIVATE ${OASIS_INCLUDES}) else () - message(FATAL_ERROR "Not building with OASIS support, because MPI is not enabled" .) + message(FATAL_ERROR "Cannot build with OASIS support, because MPI is not enabled" .) endif () endif () diff --git a/physics/src/include/OASISCoupled.hpp b/physics/src/include/OASISCoupled.hpp index 8363974e6..1ac041e5d 100644 --- a/physics/src/include/OASISCoupled.hpp +++ b/physics/src/include/OASISCoupled.hpp @@ -21,6 +21,8 @@ class OASISCoupled { ~OASISCoupled() { OASIS_CHECK_ERR(!oasis_c_terminate()); } #else ~OASISCoupled() { } + const std::string OASISError + = "Cannot access OASIS interface, as OASIS support was not activated at compile time.\n"; #endif virtual std::string getName() const { return "OASISCoupled"; } diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index a8dd75ee1..cb73e98a5 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -1,7 +1,7 @@ /*! * @file OASISCoupledOcean.cpp * - * @date Sep 26, 2022 + * @date 27 Aug 2024 * @author Tim Spain * @author Einar Ólason */ @@ -40,8 +40,7 @@ void OASISCoupledOcean::setMetadata(const ModelMetadata& metadata) // OASIS finalising definition OASIS_CHECK_ERR(oasis_c_enddef()); #else - std::string message = __func__ + std::string(": OASIS support not compiled in.\n"); - throw std::runtime_error(message); + throw std::runtime_error(std::string(__func__) + ": " + OASISError); #endif } @@ -87,8 +86,7 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) Module::getImplementation().update(tst); #else - std::string message = __func__ + std::string(": OASIS support not compiled in.\n"); - throw std::runtime_error(message); + throw std::runtime_error(std::string(__func__) + ": " + OASISError); #endif } @@ -110,7 +108,7 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(EMPKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &emp[0], OASIS_No_Restart, &kinfo)); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(QSWKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); @@ -130,8 +128,7 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) // Increment the "OASIS" time by the number of seconds in the time step updateOASISTime(tst); #else - std::string message = __func__ + std::string(": OASIS support not compiled in.\n"); - throw std::runtime_error(message); + throw std::runtime_error(std::string(__func__) + ": " + OASISError); #endif } @@ -177,7 +174,8 @@ OASISCoupledOcean::HelpMap& OASISCoupledOcean::getHelpText(HelpMap& map, bool ge { SSHConfigKey, ConfigType::STRING, {}, SSHKeyDefault, "", "The field name for sea surface height used in namcouple" }, { MLDConfigKey, ConfigType::STRING, {}, MLDKeyDefault, "", - "The field name for the thickness of the first ocean layer in namcouple (if that's defined)." }, + "The field name for the thickness of the first ocean layer in namcouple (if that's " + "defined)." }, { TauXConfigKey, ConfigType::STRING, {}, TauXKeyDefault, "", "The field name for the x-component of ice-ocean stress in namcouple." }, { TauYConfigKey, ConfigType::STRING, {}, TauYKeyDefault, "", From 9411b8310c45a44b09969101aefd7f20f4ce7df9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Mon, 9 Sep 2024 14:06:24 +0200 Subject: [PATCH 19/78] Move common OASIS calls out of OASISCoupled class The OASIS calls, init_comp, get_localcomm, terminate, def_partition, and end_def can only be called once, so they must be moved out of the OASISCoupled class and it's children. This makes the program structure less nice, but I can't think of a better way to do it. It also leaves OASISCoupled with only OASISTime and updateOASISTime(), but I suppose that it's worth having anyway. --- core/src/Model.cpp | 10 ++- core/src/PrognosticData.cpp | 11 ++- core/src/include/Model.hpp | 5 +- core/src/include/ModelMetadata.hpp | 47 +++++++++++- core/src/main.cpp | 21 +++++- physics/src/include/OASISCoupled.hpp | 75 ++----------------- .../OceanBoundaryModule/OASISCoupledOcean.cpp | 20 ++--- 7 files changed, 102 insertions(+), 87 deletions(-) diff --git a/core/src/Model.cpp b/core/src/Model.cpp index 2cea08c60..17cd46963 100644 --- a/core/src/Model.cpp +++ b/core/src/Model.cpp @@ -1,6 +1,6 @@ /*! * @file Model.cpp - * @date 12 Aug 2021 + * @date 09 Sep 2024 * @author Tim Spain * @author Kacper Kornet */ @@ -47,6 +47,9 @@ static std::map modelConfigKeyMap = { { Model::MISSINGVALUE_KEY, "model.missing_value" }, { Model::RESTARTPERIOD_KEY, "model.restart_period" }, { Model::RESTARTOUTFILE_KEY, "model.restart_file" }, +#ifdef USE_OASIS + { Model::WRITEOASISGRID_KEY, "oasis.write_grid" }, +#endif }; // Merge the configuration from ModelConfig into the Model keyMap. @@ -136,6 +139,11 @@ void Model::configure() // Get the coordinates from the ModelState for persistence m_etadata.extractCoordinates(initialState); +#ifdef USE_OASIS + const bool writeOasisGrid = Configured::getConfiguration(keyMap.at(WRITEOASISGRID_KEY), false); + m_etadata.initOasis(writeOasisGrid); +#endif + modelStep.setData(pData); modelStep.setMetadata(m_etadata); modelStep.setRestartDetails(restartPeriod, finalFileName); diff --git a/core/src/PrognosticData.cpp b/core/src/PrognosticData.cpp index 2e7806b9b..3faa8f390 100644 --- a/core/src/PrognosticData.cpp +++ b/core/src/PrognosticData.cpp @@ -1,7 +1,7 @@ /*! * @file PrognosticData.cpp * - * @date 1 Jul 2024 + * @date 09 Sep 2024 * @author Tim Spain * @author Einar Ólason */ @@ -12,6 +12,10 @@ #include "include/Module.hpp" #include "include/gridNames.hpp" +#ifdef USE_OASIS +#include +#endif + namespace Nextsim { PrognosticData::PrognosticData() @@ -78,6 +82,11 @@ void PrognosticData::setMetadata(const Nextsim::ModelMetadata& metadata) { pAtmBdy->setMetadata(metadata); pOcnBdy->setMetadata(metadata); + +#ifdef USE_OASIS + // OASIS finalising definition - can only be called once + OASIS_CHECK_ERR(oasis_c_enddef()); +#endif } void PrognosticData::update(const TimestepTime& tst) diff --git a/core/src/include/Model.hpp b/core/src/include/Model.hpp index 83eaab5e6..b71050f10 100644 --- a/core/src/include/Model.hpp +++ b/core/src/include/Model.hpp @@ -1,6 +1,6 @@ /*! * @file Model.hpp - * @date 12 Aug 2021 + * @date 09 Sep 2024 * @author Tim Spain * @author Kacper Kornet */ @@ -49,6 +49,9 @@ class Model : public Configured { // Other Model configuration keys, not to be written to the restart file. RESTARTPERIOD_KEY, RESTARTOUTFILE_KEY, +#ifdef USE_OASIS + WRITEOASISGRID_KEY, +#endif }; ConfigMap getConfig() const; diff --git a/core/src/include/ModelMetadata.hpp b/core/src/include/ModelMetadata.hpp index ad4956c7a..e9e424732 100644 --- a/core/src/include/ModelMetadata.hpp +++ b/core/src/include/ModelMetadata.hpp @@ -1,7 +1,7 @@ /*! * @file ModelMetadata.hpp * - * @date Jun 29, 2022 + * @date 09 Sep 2024 * @author Tim Spain */ @@ -18,6 +18,9 @@ #ifdef USE_MPI #include #endif +#ifdef USE_OASIS +#include +#endif namespace Nextsim { @@ -42,6 +45,10 @@ class ModelMetadata { ModelMetadata() = default; #endif +#ifdef USE_OASIS + int OASISPartitionId; +#endif + /*! * @brief Sets the initial or current model time * @@ -99,6 +106,44 @@ class ModelMetadata { int localCornerX, localCornerY, localExtentX, localExtentY, globalExtentX, globalExtentY; #endif +#ifdef USE_OASIS + void initOasis(const bool writeOasisGrid) + { + // Set the partitioning + /* From the manual: "[ig_paral is a] vector of integers describing the local grid partition + * in the global index space; has a different expression depending on the type of the + * partition; in OASIS3-MCT, 5 types of partition are supported: Serial (no partition), + * Apple, Box, Orange, and Points" - it looks like we should use "Box", so partInfo[0] = 2 + * (aka. ig_paral). + * + * #Box partition# + * Each partition is a rectangular region of the global domain, described by the global + * offset of its upper left corner, and its local extents in the X and Y dimensions. The + * global extent in the X dimension must also be given. In this case, we have ig_paral(1:5): + * - ig_paral(1) = 2 (indicates a Box partition) + * - ig_paral(2) = the upper left corner global offset + * - ig paral(3) = the local extent in x + * - ig_paral(4) = the local extent in y + * - ig_paral(5) = the global extent in x. + * + * metdatata contains: localCornerX, localCornerY, localExtentX, localExtentY, + * globalExtentX, globalExtentY; + */ + // TODO: The contents of metadata is not certain! + const int offset = globalExtentX * localCornerY + localCornerX; + const std::vector partInfo + = { OASIS_Box, offset, localExtentX, localExtentY, globalExtentX }; + + const int globalSize = globalExtentX * globalExtentY; + const std::string compName = "nextsim"; // Not useful for any setups we have in mind + OASIS_CHECK_ERR(oasis_c_def_partition( + &OASISPartitionId, OASIS_Box_Params, &partInfo[0], globalSize, compName.c_str())); + + // TODO: Writing out grid information should be possible, but optional + if (writeOasisGrid) { } + } +#endif + private: TimePoint m_time; ConfigMap m_config; diff --git a/core/src/main.cpp b/core/src/main.cpp index bb94b4044..03497eeb7 100644 --- a/core/src/main.cpp +++ b/core/src/main.cpp @@ -1,6 +1,6 @@ /*! * @file main.cpp - * @date 11 Aug 2021 + * @date 09 Sep 2024 * @author Tim Spain * @author Kacper Kornet */ @@ -9,6 +9,9 @@ #ifdef USE_MPI #include #endif +#ifdef USE_OASIS +#include +#endif #include "include/CommandLineParser.hpp" #include "include/ConfigurationHelpPrinter.hpp" @@ -20,7 +23,18 @@ int main(int argc, char* argv[]) { #ifdef USE_MPI + MPI_Comm modelCommunicator; MPI_Init(&argc, &argv); +#ifdef USE_OASIS + /* We must call these oasis routines before any MPI communication takes place, to make sure we + * have the right communicator, i.e. modelCommunictor and not MPI_COMM_WORLD. */ + int compID; // Not actually used. Only useful for debugging + const std::string compName = "nextsim"; // Not useful for any setups we have in mind + OASIS_CHECK_ERR(oasis_c_init_comp(&compID, compName.c_str(), OASIS_COUPLED)); + OASIS_CHECK_ERR(oasis_c_get_localcomm(&modelCommunicator)); +#else + modelCommunicator = MPI_COMM_WORLD; +#endif // USE_OASIS #endif // USE_MPI // Pass the command line to Configurator to handle @@ -49,7 +63,7 @@ int main(int argc, char* argv[]) } else { // Construct the Model #ifdef USE_MPI - Nextsim::Model model(MPI_COMM_WORLD); + Nextsim::Model model(modelCommunicator); #else Nextsim::Model model; #endif @@ -59,6 +73,9 @@ int main(int argc, char* argv[]) model.run(); } #ifdef USE_MPI +#ifdef USE_OASIS + OASIS_CHECK_ERR(oasis_c_terminate()); +#endif MPI_Finalize(); #endif diff --git a/physics/src/include/OASISCoupled.hpp b/physics/src/include/OASISCoupled.hpp index 1ac041e5d..a4cdf8191 100644 --- a/physics/src/include/OASISCoupled.hpp +++ b/physics/src/include/OASISCoupled.hpp @@ -1,14 +1,13 @@ /*! * @file OASISCoupled.hpp * - * @date 29 Aug 2024 + * @date 09 Sep 2024 * @author Einar Ólason */ #ifndef OASISCOUPLED_HPP #define OASISCOUPLED_HPP -#include "include/ModelMetadata.hpp" #ifdef USE_OASIS #include #endif @@ -17,82 +16,20 @@ namespace Nextsim { class OASISCoupled { public: -#ifdef USE_OASIS - ~OASISCoupled() { OASIS_CHECK_ERR(!oasis_c_terminate()); } -#else - ~OASISCoupled() { } - const std::string OASISError - = "Cannot access OASIS interface, as OASIS support was not activated at compile time.\n"; -#endif - virtual std::string getName() const { return "OASISCoupled"; } #ifdef USE_OASIS - int partitionID, OASISTime; - void setMetadata(const ModelMetadata& metadata) - { - // Set the "OASIS time" (seconds since start) to zero - // TODO: We need to figure out if this is ok to do when restarting - OASISTime = 0; - - // Set the communicators - // Some of these three may need to be global to the class - int compID; - MPI_Comm localComm; - MPI_Comm coupledComm; - - // compName could be configurable, but I don't see the need - const std::string compName = "nextsim"; - OASIS_CHECK_ERR(oasis_c_init_comp_with_comm( - &compID, compName.c_str(), OASIS_COUPLED, metadata.mpiComm)); - - // Eric didn't include those, but it seems they should be necessary - OASIS_CHECK_ERR(oasis_c_get_localcomm(&localComm)); - OASIS_CHECK_ERR(oasis_c_create_couplcomm(OASIS_COUPLED, localComm, &coupledComm)); - - // Set the partitioning - /* From the manual: "[ig_paral is a] vector of integers describing the local grid partition - * in the global index space; has a different expression depending on the type of the - * partition; in OASIS3-MCT, 5 types of partition are supported: Serial (no partition), - * Apple, Box, Orange, and Points" - it looks like we should use "Box", so partInfo[0] = 2 - * (aka. ig_paral). - * - * #Box partition# - * Each partition is a rectangular region of the global domain, described by the global - * offset of its upper left corner, and its local extents in the X and Y dimensions. The - * global extent in the X dimension must also be given. In this case, we have ig_paral(1:5): - * - ig_paral(1) = 2 (indicates a Box partition) - * - ig_paral(2) = the upper left corner global offset - * - ig paral(3) = the local extent in x - * - ig_paral(4) = the local extent in y - * - ig_paral(5) = the global extent in x. - * - * metdatata contains: localCornerX, localCornerY, localExtentX, localExtentY, - * globalExtentX, globalExtentY; - */ - // TODO: The contents of metadata is not certain! - const int offset = metadata.globalExtentX * metadata.localCornerY + metadata.localCornerX; - const std::vector partInfo = { OASIS_Box, offset, metadata.localExtentX, - metadata.localExtentY, metadata.globalExtentX }; - - const int globalSize = metadata.globalExtentX * metadata.globalExtentY; - OASIS_CHECK_ERR(oasis_c_def_partition( - &partitionID, OASIS_Box_Params, &partInfo[0], globalSize, compName.c_str())); - - // TODO: Writing out grid information should be possible, but optional + int OASISTime; - // def_var and end_def calls are called by the child class - } + // Set the "OASIS time" (seconds since start) to zero + OASISCoupled() { OASISTime = 0; } // Increment the "OASIS" time by the number of seconds in the time step + // Could be any time unit // Must be called at the end of the child class' update or updateAfter call. void updateOASISTime(const TimestepTime& tst) { OASISTime += tst.step.seconds(); } #else - virtual void setMetadata(const ModelMetadata& metadata) - { - std::string message = __func__ + std::string(": OASIS support not compiled in.\n"); - throw std::runtime_error(message); - } + const std::string OASISError = std::string(": OASIS support not compiled in.\n"); #endif }; diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index cb73e98a5..00234d038 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -1,7 +1,7 @@ /*! * @file OASISCoupledOcean.cpp * - * @date 27 Aug 2024 + * @date 09 Sep 2024 * @author Tim Spain * @author Einar Ólason */ @@ -15,30 +15,26 @@ namespace Nextsim { void OASISCoupledOcean::setMetadata(const ModelMetadata& metadata) { - // The parent class knows how to set the communicators and partitions - OASISCoupled::setMetadata(metadata); - #ifdef USE_OASIS // OASIS defining variable /* Populate the couplingId map with the id string and number pair. We need to do this seperately * for the input (get) and output (put) variables. */ + // TODO: coplingID should be a map of where the pair is idNumber and + // pointer to the ModelArray for (std::string idString : cplStringsIn) { int idNumber; - OASIS_CHECK_ERR(oasis_c_def_var( - &idNumber, idString.c_str(), partitionID, bundleSize, OASIS_IN, OASIS_DOUBLE)); + OASIS_CHECK_ERR(oasis_c_def_var(&idNumber, idString.c_str(), metadata.OASISPartitionId, + bundleSize, OASIS_IN, OASIS_DOUBLE)); couplingId[idString] = idNumber; } for (std::string idString : cplStringsOut) { int idNumber; - OASIS_CHECK_ERR(oasis_c_def_var( - &idNumber, idString.c_str(), partitionID, bundleSize, OASIS_OUT, OASIS_DOUBLE)); + OASIS_CHECK_ERR(oasis_c_def_var(&idNumber, idString.c_str(), metadata.OASISPartitionId, + bundleSize, OASIS_OUT, OASIS_DOUBLE)); couplingId[idString] = idNumber; } - - // OASIS finalising definition - OASIS_CHECK_ERR(oasis_c_enddef()); #else throw std::runtime_error(std::string(__func__) + ": " + OASISError); #endif @@ -108,7 +104,7 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(EMPKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &emp[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(QSWKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); From f7b404aa427c48e8fbc3582cc23d24f351dfc7de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Mon, 9 Sep 2024 20:36:01 +0200 Subject: [PATCH 20/78] Comments where the grid writing should be This needs to be figured out, but it's not a priority. Grid writing is not necessary for the type of coupling we'll start with. --- core/src/include/ModelMetadata.hpp | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/core/src/include/ModelMetadata.hpp b/core/src/include/ModelMetadata.hpp index e9e424732..2f3d0ebf3 100644 --- a/core/src/include/ModelMetadata.hpp +++ b/core/src/include/ModelMetadata.hpp @@ -140,7 +140,34 @@ class ModelMetadata { &OASISPartitionId, OASIS_Box_Params, &partInfo[0], globalSize, compName.c_str())); // TODO: Writing out grid information should be possible, but optional - if (writeOasisGrid) { } + if (writeOasisGrid) { + /* This needs to be figured out, but it's not a priority. Grid writing is + * not necessary for the type of coupling we'll start with. + + const std::string gridName = "nxts"; + + int flag = 1; + OASIS_CHECK_ERR(oasis_c_start_grids_writing(&flag)); + + OASIS_CHECK_ERR(oasis_c_write_grid( + gridName.c_str(), nx, ny, nx_loc, ny_loc, lon, lat, OASISPartitionId)); + OASIS_CHECK_ERR(oasis_c_write_corner( + gridName.c_str(), nx, ny, nx_loc, ny_loc, clo, cla, OASISPartitionId)); + OASIS_CHECK_ERR(oasis_c_write_area( + gridName.c_str(), nx, ny, nx_loc, ny_loc, area, OASISPartitionId)); + OASIS_CHECK_ERR(oasis_c_write_mask( + gridName.c_str(), nx, ny, nx_loc, ny_loc, angle, OASISPartitionId)); + + std::string companion = "land area fraction"; + OASIS_CHECK_ERR(oasis_c_write_frac( + gridName.c_str(), nx, ny, nx_loc, ny_loc, mask, OASISPartitionId), + companion.c_str()); + companion = "land sea mask"; + OASIS_CHECK_ERR(oasis_c_write_mask( + gridName.c_str(), nx, ny, nx_loc, ny_loc, mask, OASISPartitionId), + companion.c_str()); + */ + } } #endif From 058e86a5ac27483538fae5951030f24930f76c49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Mon, 9 Sep 2024 20:43:25 +0200 Subject: [PATCH 21/78] Move initOasis to ModelMetadata.cpp The implementation should be in the implementation file. --- core/src/ModelMetadata.cpp | 68 +++++++++++++++++++++++++++++- core/src/include/ModelMetadata.hpp | 63 +-------------------------- 2 files changed, 68 insertions(+), 63 deletions(-) diff --git a/core/src/ModelMetadata.cpp b/core/src/ModelMetadata.cpp index d30597ec6..6e7db6f3a 100644 --- a/core/src/ModelMetadata.cpp +++ b/core/src/ModelMetadata.cpp @@ -1,7 +1,7 @@ /*! * @file ModelMetadata.cpp * - * @date 21 August 2024 + * @date 09 Sep 2024 * @author Tim Spain */ @@ -102,4 +102,70 @@ ModelState& ModelMetadata::affixCoordinates(ModelState& state) const } return state; } + +#ifdef USE_OASIS +void ModelMetadata::initOasis(const bool writeOasisGrid) +{ + // Set the partitioning + /* From the manual: "[ig_paral is a] vector of integers describing the local grid partition + * in the global index space; has a different expression depending on the type of the + * partition; in OASIS3-MCT, 5 types of partition are supported: Serial (no partition), + * Apple, Box, Orange, and Points" - it looks like we should use "Box", so partInfo[0] = 2 + * (aka. ig_paral). + * + * #Box partition# + * Each partition is a rectangular region of the global domain, described by the global + * offset of its upper left corner, and its local extents in the X and Y dimensions. The + * global extent in the X dimension must also be given. In this case, we have ig_paral(1:5): + * - ig_paral(1) = 2 (indicates a Box partition) + * - ig_paral(2) = the upper left corner global offset + * - ig paral(3) = the local extent in x + * - ig_paral(4) = the local extent in y + * - ig_paral(5) = the global extent in x. + * + * metdatata contains: localCornerX, localCornerY, localExtentX, localExtentY, + * globalExtentX, globalExtentY; + */ + // TODO: The contents of metadata is not certain! + const int offset = globalExtentX * localCornerY + localCornerX; + const std::vector partInfo + = { OASIS_Box, offset, localExtentX, localExtentY, globalExtentX }; + + const int globalSize = globalExtentX * globalExtentY; + const std::string compName = "nextsim"; // Not useful for any setups we have in mind + OASIS_CHECK_ERR(oasis_c_def_partition( + &OASISPartitionId, OASIS_Box_Params, &partInfo[0], globalSize, compName.c_str())); + + // TODO: Writing out grid information should be possible, but optional + if (writeOasisGrid) { + /* This needs to be figured out, but it's not a priority. Grid writing is + * not necessary for the type of coupling we'll start with. + + const std::string gridName = "nxts"; + + int flag = 1; + OASIS_CHECK_ERR(oasis_c_start_grids_writing(&flag)); + + OASIS_CHECK_ERR(oasis_c_write_grid( + gridName.c_str(), nx, ny, nx_loc, ny_loc, lon, lat, OASISPartitionId)); + OASIS_CHECK_ERR(oasis_c_write_corner( + gridName.c_str(), nx, ny, nx_loc, ny_loc, clo, cla, OASISPartitionId)); + OASIS_CHECK_ERR(oasis_c_write_area( + gridName.c_str(), nx, ny, nx_loc, ny_loc, area, OASISPartitionId)); + OASIS_CHECK_ERR(oasis_c_write_mask( + gridName.c_str(), nx, ny, nx_loc, ny_loc, angle, OASISPartitionId)); + + std::string companion = "land area fraction"; + OASIS_CHECK_ERR(oasis_c_write_frac( + gridName.c_str(), nx, ny, nx_loc, ny_loc, mask, OASISPartitionId), + companion.c_str()); + companion = "land sea mask"; + OASIS_CHECK_ERR(oasis_c_write_mask( + gridName.c_str(), nx, ny, nx_loc, ny_loc, mask, OASISPartitionId), + companion.c_str()); + */ + } +} +#endif + } /* namespace Nextsim */ diff --git a/core/src/include/ModelMetadata.hpp b/core/src/include/ModelMetadata.hpp index 2f3d0ebf3..79013c09f 100644 --- a/core/src/include/ModelMetadata.hpp +++ b/core/src/include/ModelMetadata.hpp @@ -107,68 +107,7 @@ class ModelMetadata { #endif #ifdef USE_OASIS - void initOasis(const bool writeOasisGrid) - { - // Set the partitioning - /* From the manual: "[ig_paral is a] vector of integers describing the local grid partition - * in the global index space; has a different expression depending on the type of the - * partition; in OASIS3-MCT, 5 types of partition are supported: Serial (no partition), - * Apple, Box, Orange, and Points" - it looks like we should use "Box", so partInfo[0] = 2 - * (aka. ig_paral). - * - * #Box partition# - * Each partition is a rectangular region of the global domain, described by the global - * offset of its upper left corner, and its local extents in the X and Y dimensions. The - * global extent in the X dimension must also be given. In this case, we have ig_paral(1:5): - * - ig_paral(1) = 2 (indicates a Box partition) - * - ig_paral(2) = the upper left corner global offset - * - ig paral(3) = the local extent in x - * - ig_paral(4) = the local extent in y - * - ig_paral(5) = the global extent in x. - * - * metdatata contains: localCornerX, localCornerY, localExtentX, localExtentY, - * globalExtentX, globalExtentY; - */ - // TODO: The contents of metadata is not certain! - const int offset = globalExtentX * localCornerY + localCornerX; - const std::vector partInfo - = { OASIS_Box, offset, localExtentX, localExtentY, globalExtentX }; - - const int globalSize = globalExtentX * globalExtentY; - const std::string compName = "nextsim"; // Not useful for any setups we have in mind - OASIS_CHECK_ERR(oasis_c_def_partition( - &OASISPartitionId, OASIS_Box_Params, &partInfo[0], globalSize, compName.c_str())); - - // TODO: Writing out grid information should be possible, but optional - if (writeOasisGrid) { - /* This needs to be figured out, but it's not a priority. Grid writing is - * not necessary for the type of coupling we'll start with. - - const std::string gridName = "nxts"; - - int flag = 1; - OASIS_CHECK_ERR(oasis_c_start_grids_writing(&flag)); - - OASIS_CHECK_ERR(oasis_c_write_grid( - gridName.c_str(), nx, ny, nx_loc, ny_loc, lon, lat, OASISPartitionId)); - OASIS_CHECK_ERR(oasis_c_write_corner( - gridName.c_str(), nx, ny, nx_loc, ny_loc, clo, cla, OASISPartitionId)); - OASIS_CHECK_ERR(oasis_c_write_area( - gridName.c_str(), nx, ny, nx_loc, ny_loc, area, OASISPartitionId)); - OASIS_CHECK_ERR(oasis_c_write_mask( - gridName.c_str(), nx, ny, nx_loc, ny_loc, angle, OASISPartitionId)); - - std::string companion = "land area fraction"; - OASIS_CHECK_ERR(oasis_c_write_frac( - gridName.c_str(), nx, ny, nx_loc, ny_loc, mask, OASISPartitionId), - companion.c_str()); - companion = "land sea mask"; - OASIS_CHECK_ERR(oasis_c_write_mask( - gridName.c_str(), nx, ny, nx_loc, ny_loc, mask, OASISPartitionId), - companion.c_str()); - */ - } - } + void initOasis(const bool writeOasisGrid); #endif private: From 7f8c5879705866276557173c96e3b975edd4b9dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Mon, 9 Sep 2024 20:58:31 +0200 Subject: [PATCH 22/78] Minor cleaning OASIS_CHECK_ERR doesn't work on oasis_c_put and oasis_c_get. Move dimension0 and dimension1 to be global to the class. --- .../OceanBoundaryModule/OASISCoupledOcean.cpp | 70 +++++++++++-------- .../include/OASISCoupledOcean.hpp | 5 +- 2 files changed, 43 insertions(+), 32 deletions(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 00234d038..62f85b380 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -46,25 +46,27 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) #ifdef USE_OASIS int kinfo; - const int dimension0 = ModelArray::dimensions(ModelArray::Type::H)[0]; - const int dimension1 = ModelArray::dimensions(ModelArray::Type::H)[1]; - OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSTKey), OASISTime, dimension0, dimension1, - bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sst[0], &kinfo)); + oasis_c_get(couplingId.at(SSTKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, + OASIS_COL_MAJOR, &sst[0], &kinfo); + OASIS_CHECK_ERR(kinfo); - OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSSKey), OASISTime, dimension0, dimension1, - bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sss[0], &kinfo)); + oasis_c_get(couplingId.at(SSSKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, + OASIS_COL_MAJOR, &sss[0], &kinfo); + OASIS_CHECK_ERR(kinfo); - OASIS_CHECK_ERR(oasis_c_get(couplingId.at(UOceanKey), OASISTime, dimension0, dimension1, - bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &u[0], &kinfo)); + oasis_c_get(couplingId.at(UOceanKey), OASISTime, dimension0, dimension1, bundleSize, + OASIS_DOUBLE, OASIS_COL_MAJOR, &u[0], &kinfo); + OASIS_CHECK_ERR(kinfo); - OASIS_CHECK_ERR(oasis_c_get(couplingId.at(VOceanKey), OASISTime, dimension0, dimension1, - bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &v[0], &kinfo)); + oasis_c_get(couplingId.at(VOceanKey), OASISTime, dimension0, dimension1, bundleSize, + OASIS_DOUBLE, OASIS_COL_MAJOR, &v[0], &kinfo); + OASIS_CHECK_ERR(kinfo); // TODO: Implement ssh reading and passing to dynamics! - // OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::SSHKey], OASISTime, dimension0, - // dimension1, - // bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &ssh[0], &kinfo)); + // oasis_c_get(cplIdIn[couplingIdIn::SSHKey], OASISTime, dimension0, dimension1, bundleSize, + // OASIS_DOUBLE, OASIS_COL_MAJOR, &ssh[0], &kinfo); + // OASIS_CHECK_ERR(kinfo); if (couplingId.find(SSHKey) != couplingId.end()) { OASIS_CHECK_ERR(oasis_c_get(couplingId.at(VOceanKey), OASISTime, dimension0, dimension1, @@ -90,36 +92,42 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) { #ifdef USE_OASIS int kinfo; - const int dimension0 = ModelArray::dimensions(ModelArray::Type::H)[0]; - const int dimension1 = ModelArray::dimensions(ModelArray::Type::H)[1]; // TODO We still need the the actual data HField dummy; dummy.resize(); dummy.setData(0.); - OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauXKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + oasis_c_put(couplingId.at(TauXKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, + OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo); + OASIS_CHECK_ERR(kinfo); - OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauYKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + oasis_c_put(couplingId.at(TauYKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, + OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo); + OASIS_CHECK_ERR(kinfo); - OASIS_CHECK_ERR(oasis_c_put(couplingId.at(EMPKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + oasis_c_put(couplingId.at(EMPKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, + OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo); + OASIS_CHECK_ERR(kinfo); - OASIS_CHECK_ERR(oasis_c_put(couplingId.at(QSWKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + oasis_c_put(couplingId.at(QSWKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, + OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo); + OASIS_CHECK_ERR(kinfo); - OASIS_CHECK_ERR(oasis_c_put(couplingId.at(QNoSunKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + oasis_c_put(couplingId.at(QNoSunKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, + OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo); + OASIS_CHECK_ERR(kinfo); - OASIS_CHECK_ERR(oasis_c_put(couplingId.at(SFluxKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + oasis_c_put(couplingId.at(SFluxKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, + OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo); + OASIS_CHECK_ERR(kinfo); - OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauModKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + oasis_c_put(couplingId.at(TauModKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, + OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo); + OASIS_CHECK_ERR(kinfo); - OASIS_CHECK_ERR(oasis_c_put(couplingId.at(CIceKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + oasis_c_put(couplingId.at(CIceKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, + OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo); + OASIS_CHECK_ERR(kinfo); // Increment the "OASIS" time by the number of seconds in the time step updateOASISTime(tst); diff --git a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp index 75bd91715..02cc37be6 100644 --- a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp +++ b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp @@ -1,7 +1,7 @@ /*! * @file OASISCoupledOcean.hpp * - * @date Sep 26, 2022 + * @date 09 Sep 2024 * @author Tim Spain * @author Einar Ólason */ @@ -80,6 +80,9 @@ class OASISCoupledOcean : public IOceanBoundary, int bundleSize = 1; // Always "unbundled", as per the OASIS manual double firstLayerDepth = FIRST_LAYER_DEPTH; + const int dimension0 = ModelArray::dimensions(ModelArray::Type::H)[0]; + const int dimension1 = ModelArray::dimensions(ModelArray::Type::H)[1]; + SlabOcean slabOcean; void updateTf(size_t i, const TimestepTime& tst) From d266c17fa582f0635fd609a36244149be25364ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Tue, 10 Sep 2024 09:51:21 +0200 Subject: [PATCH 23/78] Moving oasis init & get_localcomm Moves the calls to oasis_c_init_comp and oasis_c_get_localcomm to after configuration, as suggested by Andrea. This makes for cleaner code and opens the possibility to decide if we use oasis through the config file (although this is not yet implemented). --- core/src/main.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/core/src/main.cpp b/core/src/main.cpp index 03497eeb7..92f96c841 100644 --- a/core/src/main.cpp +++ b/core/src/main.cpp @@ -1,6 +1,6 @@ /*! * @file main.cpp - * @date 09 Sep 2024 + * @date 10 Sep 2024 * @author Tim Spain * @author Kacper Kornet */ @@ -23,18 +23,8 @@ int main(int argc, char* argv[]) { #ifdef USE_MPI - MPI_Comm modelCommunicator; MPI_Init(&argc, &argv); -#ifdef USE_OASIS - /* We must call these oasis routines before any MPI communication takes place, to make sure we - * have the right communicator, i.e. modelCommunictor and not MPI_COMM_WORLD. */ - int compID; // Not actually used. Only useful for debugging - const std::string compName = "nextsim"; // Not useful for any setups we have in mind - OASIS_CHECK_ERR(oasis_c_init_comp(&compID, compName.c_str(), OASIS_COUPLED)); - OASIS_CHECK_ERR(oasis_c_get_localcomm(&modelCommunicator)); -#else - modelCommunicator = MPI_COMM_WORLD; -#endif // USE_OASIS + MPI_Comm modelCommunicator = MPI_COMM_WORLD; #endif // USE_MPI // Pass the command line to Configurator to handle @@ -63,6 +53,14 @@ int main(int argc, char* argv[]) } else { // Construct the Model #ifdef USE_MPI +#ifdef USE_OASIS + /* We must call these oasis routines before any MPI communication takes place, to make sure + * we have the right communicator, i.e. modelCommunictor and not MPI_COMM_WORLD. */ + int compID; // Not actually used. Only useful for debugging + const std::string compName = "nextsim"; // Not useful for any setups we have in mind + OASIS_CHECK_ERR(oasis_c_init_comp(&compID, compName.c_str(), OASIS_COUPLED)); + OASIS_CHECK_ERR(oasis_c_get_localcomm(&modelCommunicator)); +#endif // USE_OASIS Nextsim::Model model(modelCommunicator); #else Nextsim::Model model; From a3877b0309ad6eb634003d3d808ac63a5f9e2ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Tue, 10 Sep 2024 13:35:09 +0200 Subject: [PATCH 24/78] Minor cleaning Move #include from ModelMetadata.hpp to ModelMetadata.cpp. Remove an unused #include from ConfigOutput_test.cpp. Som automatic clang-format changes as well. --- core/src/ModelMetadata.cpp | 5 ++++- core/src/include/ModelMetadata.hpp | 5 +---- core/test/ConfigOutput_test.cpp | 9 ++++----- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/core/src/ModelMetadata.cpp b/core/src/ModelMetadata.cpp index 6e7db6f3a..39340e08f 100644 --- a/core/src/ModelMetadata.cpp +++ b/core/src/ModelMetadata.cpp @@ -1,7 +1,7 @@ /*! * @file ModelMetadata.cpp * - * @date 09 Sep 2024 + * @date 10 Sep 2024 * @author Tim Spain */ @@ -16,6 +16,9 @@ #include #include #endif +#ifdef USE_OASIS +#include +#endif namespace Nextsim { diff --git a/core/src/include/ModelMetadata.hpp b/core/src/include/ModelMetadata.hpp index 79013c09f..043d941a8 100644 --- a/core/src/include/ModelMetadata.hpp +++ b/core/src/include/ModelMetadata.hpp @@ -1,7 +1,7 @@ /*! * @file ModelMetadata.hpp * - * @date 09 Sep 2024 + * @date 10 Sep 2024 * @author Tim Spain */ @@ -18,9 +18,6 @@ #ifdef USE_MPI #include #endif -#ifdef USE_OASIS -#include -#endif namespace Nextsim { diff --git a/core/test/ConfigOutput_test.cpp b/core/test/ConfigOutput_test.cpp index 36a65f820..e284e0c42 100644 --- a/core/test/ConfigOutput_test.cpp +++ b/core/test/ConfigOutput_test.cpp @@ -1,7 +1,7 @@ /*! * @file ConfigOutput_test.cpp * - * @date 11 May 2023 + * @date 10 Sep 2024 * @author Tim Spain */ @@ -17,7 +17,6 @@ #include "include/FileCallbackCloser.hpp" #include "include/IStructure.hpp" #include "include/ModelArray.hpp" -#include "include/ModelArrayRef.hpp" #include "include/ModelComponent.hpp" #include "include/ModelMetadata.hpp" #include "include/ModelState.hpp" @@ -30,8 +29,8 @@ #include #include -#include #include +#include const std::string test_files_dir = TEST_FILES_DIR; #ifdef USE_MPI @@ -54,10 +53,10 @@ TEST_CASE("Test periodic output") #ifdef USE_MPI if (test_rank == 0) { - ModelArray::setDimension(ModelArray::Dimension::X, nx, 1, 0); + ModelArray::setDimension(ModelArray::Dimension::X, nx, 1, 0); } if (test_rank == 1) { - ModelArray::setDimension(ModelArray::Dimension::X, nx, 1, 1); + ModelArray::setDimension(ModelArray::Dimension::X, nx, 1, 1); } ModelArray::setDimension(ModelArray::Dimension::Y, ny, ny, 0); ModelArray::setDimension(ModelArray::Dimension::Z, NZLevels::get(), NZLevels::get(), 0); From 10ffd762e069e3d2d938a595751e72d2a4438fa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Tue, 10 Sep 2024 15:08:40 +0200 Subject: [PATCH 25/78] Change target_include_directories to PUBLIC Everything within the if (ENABLE_OASIS) clause everything should be PUBLIC, but target_include_directories was PRIVATE. This is now corrected. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ac7067b5d..f79e85bc2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,7 +156,7 @@ if (ENABLE_OASIS) target_link_directories(nextsimlib PUBLIC ${OASIS_LIBRARIES}) target_link_libraries(nextsimlib PUBLIC oasis.cbind mct mpeu psmile.MPI1 scrip netcdff) - target_include_directories(nextsimlib PRIVATE ${OASIS_INCLUDES}) + target_include_directories(nextsimlib PUBLIC ${OASIS_INCLUDES}) else () message(FATAL_ERROR "Cannot build with OASIS support, because MPI is not enabled" .) endif () @@ -168,7 +168,7 @@ if(WITH_THREADS) target_link_libraries(nextsimlib PUBLIC OpenMP::OpenMP_CXX) endif() -# Set an empty list of sources netcdf netcdfff +# Set an empty list of sources set(NextsimSources "") # Set an empty list of include directories set(NextsimIncludeDirs "") From 6b2a99f31423b6756dfb20c75f078c5d64ae05d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Tue, 10 Sep 2024 15:49:49 +0200 Subject: [PATCH 26/78] Revert removing OASIS_CHECK_ERR from get and put calls It seems I misunderstood what they return and the role of kinfo (see Andrea's comment to commit 7f8c587 on github). --- .../OceanBoundaryModule/OASISCoupledOcean.cpp | 67 ++++++++----------- 1 file changed, 27 insertions(+), 40 deletions(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 62f85b380..a969b8b17 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -1,7 +1,7 @@ /*! * @file OASISCoupledOcean.cpp * - * @date 09 Sep 2024 + * @date 10 Sep 2024 * @author Tim Spain * @author Einar Ólason */ @@ -47,26 +47,21 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) int kinfo; - oasis_c_get(couplingId.at(SSTKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, - OASIS_COL_MAJOR, &sst[0], &kinfo); - OASIS_CHECK_ERR(kinfo); + OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSTKey), OASISTime, dimension0, dimension1, + bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sst[0], &kinfo)); - oasis_c_get(couplingId.at(SSSKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, - OASIS_COL_MAJOR, &sss[0], &kinfo); - OASIS_CHECK_ERR(kinfo); + OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSSKey), OASISTime, dimension0, dimension1, + bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sss[0], &kinfo)); - oasis_c_get(couplingId.at(UOceanKey), OASISTime, dimension0, dimension1, bundleSize, - OASIS_DOUBLE, OASIS_COL_MAJOR, &u[0], &kinfo); - OASIS_CHECK_ERR(kinfo); + OASIS_CHECK_ERR(oasis_c_get(couplingId.at(UOceanKey), OASISTime, dimension0, dimension1, + bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &u[0], &kinfo)); - oasis_c_get(couplingId.at(VOceanKey), OASISTime, dimension0, dimension1, bundleSize, - OASIS_DOUBLE, OASIS_COL_MAJOR, &v[0], &kinfo); - OASIS_CHECK_ERR(kinfo); + OASIS_CHECK_ERR(oasis_c_get(couplingId.at(VOceanKey), OASISTime, dimension0, dimension1, + bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &v[0], &kinfo)); // TODO: Implement ssh reading and passing to dynamics! - // oasis_c_get(cplIdIn[couplingIdIn::SSHKey], OASISTime, dimension0, dimension1, bundleSize, - // OASIS_DOUBLE, OASIS_COL_MAJOR, &ssh[0], &kinfo); - // OASIS_CHECK_ERR(kinfo); + // OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::SSHKey], OASISTime, dimension0, dimension1, + // bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &ssh[0], &kinfo)); if (couplingId.find(SSHKey) != couplingId.end()) { OASIS_CHECK_ERR(oasis_c_get(couplingId.at(VOceanKey), OASISTime, dimension0, dimension1, @@ -97,37 +92,29 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) HField dummy; dummy.resize(); dummy.setData(0.); - oasis_c_put(couplingId.at(TauXKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, - OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo); - OASIS_CHECK_ERR(kinfo); + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauXKey), OASISTime, dimension0, dimension1, 1, + OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - oasis_c_put(couplingId.at(TauYKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, - OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo); - OASIS_CHECK_ERR(kinfo); + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauYKey), OASISTime, dimension0, dimension1, 1, + OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - oasis_c_put(couplingId.at(EMPKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, - OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo); - OASIS_CHECK_ERR(kinfo); + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(EMPKey), OASISTime, dimension0, dimension1, 1, + OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - oasis_c_put(couplingId.at(QSWKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, - OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo); - OASIS_CHECK_ERR(kinfo); + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(QSWKey), OASISTime, dimension0, dimension1, 1, + OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - oasis_c_put(couplingId.at(QNoSunKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, - OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo); - OASIS_CHECK_ERR(kinfo); + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(QNoSunKey), OASISTime, dimension0, dimension1, 1, + OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - oasis_c_put(couplingId.at(SFluxKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, - OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo); - OASIS_CHECK_ERR(kinfo); + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(SFluxKey), OASISTime, dimension0, dimension1, 1, + OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - oasis_c_put(couplingId.at(TauModKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, - OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo); - OASIS_CHECK_ERR(kinfo); + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauModKey), OASISTime, dimension0, dimension1, 1, + OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); - oasis_c_put(couplingId.at(CIceKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, - OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo); - OASIS_CHECK_ERR(kinfo); + OASIS_CHECK_ERR(oasis_c_put(couplingId.at(CIceKey), OASISTime, dimension0, dimension1, 1, + OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); // Increment the "OASIS" time by the number of seconds in the time step updateOASISTime(tst); From 133aca249d32dfa0419647fc7e0a114734bca793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Tue, 10 Sep 2024 16:04:35 +0200 Subject: [PATCH 27/78] Add netcdf hooks to the oasis bit of CMakeLists.txt As per Andrea's suggestion in a comment on commit be7905d on github. --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f79e85bc2..75a29f4a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -154,8 +154,9 @@ if (ENABLE_OASIS) pkg_search_module(NETCDF REQUIRED netcdf-fortran) target_compile_definitions(nextsimlib PUBLIC USE_OASIS) + target_link_directories(nextsimlib PUBLIC ${NETCDF_LIBRARY_DIRS}) target_link_directories(nextsimlib PUBLIC ${OASIS_LIBRARIES}) - target_link_libraries(nextsimlib PUBLIC oasis.cbind mct mpeu psmile.MPI1 scrip netcdff) + target_link_libraries(nextsimlib PUBLIC oasis.cbind mct mpeu psmile.MPI1 scrip netcdff netcdf) target_include_directories(nextsimlib PUBLIC ${OASIS_INCLUDES}) else () message(FATAL_ERROR "Cannot build with OASIS support, because MPI is not enabled" .) From 92755397aab7543749f8169528cc7e32fb914f9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Fri, 13 Sep 2024 08:54:50 +0200 Subject: [PATCH 28/78] Small changes to FindOASIS.cmake As suggested by Andrea on github PR #641. I don't understand why the case matters in the name of FindOASIS.cmake, but apparently it does for him, although it doesn't for me. It should have been ${OASIS_DIR}/include all along. --- cmake/{Findoasis.cmake => FindOASIS.cmake} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename cmake/{Findoasis.cmake => FindOASIS.cmake} (99%) diff --git a/cmake/Findoasis.cmake b/cmake/FindOASIS.cmake similarity index 99% rename from cmake/Findoasis.cmake rename to cmake/FindOASIS.cmake index d39c9d4bf..ecf2d3478 100644 --- a/cmake/Findoasis.cmake +++ b/cmake/FindOASIS.cmake @@ -9,7 +9,7 @@ get_filename_component (OASIS_LIBRARIES "${OASIS_LIBRARIES}" PATH) set (OASIS_DIR "${OASIS_LIBRARIES}/../") cmake_path(NORMAL_PATH OASIS_DIR) -find_path (OASIS_INCLUDES NAMES oasis_c.h HINTS ${OASIS_DIR}/inc) +find_path (OASIS_INCLUDES NAMES oasis_c.h HINTS ${OASIS_DIR}/include) # handle the QUIETLY and REQUIRED arguments and set OASIS_FOUND to TRUE if all listed variables are TRUE include (FindPackageHandleStandardArgs) From bf7cd78d6b53fd7e42dfe97cd6451f5eac2eba1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Fri, 13 Sep 2024 09:08:36 +0200 Subject: [PATCH 29/78] First steps in implementing a unit test for OASISCoupledOcean A skeleton of a unit test in OASISCoupledOcean_test.cpp and some changes in the accompanying CMakeLists.txt to make it compile. Added MainMPI.cpp so that the test starts. We still need a namcouple file and the actual ins-and-outs of the test. --- physics/test/CMakeLists.txt | 6 ++++ physics/test/MainMPI.cpp | 30 +++++++++++++++++++ physics/test/OASISCoupledOcean_test.cpp | 40 +++++++++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 physics/test/MainMPI.cpp create mode 100644 physics/test/OASISCoupledOcean_test.cpp diff --git a/physics/test/CMakeLists.txt b/physics/test/CMakeLists.txt index 2b01dcc4d..0a204ad34 100644 --- a/physics/test/CMakeLists.txt +++ b/physics/test/CMakeLists.txt @@ -37,6 +37,12 @@ if(ENABLE_MPI) PRIVATE USE_MPI TEST_FILES_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\" ) target_link_libraries(testTOPAZOcn_MPI1 PRIVATE nextsimlib doctest::doctest) + if (ENABLE_OASIS) + add_executable(testOASISCoupledOcean_MPI2 "OASISCoupledOcean_test.cpp" "MainMPI.cpp") + target_include_directories(testOASISCoupledOcean_MPI2 PRIVATE "${ModulesRoot}/OceanBoundaryModule") + target_compile_definitions(testOASISCoupledOcean_MPI2 PRIVATE TEST_FILES_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\") + target_link_libraries(testOASISCoupledOcean_MPI2 PRIVATE nextsimlib doctest::doctest) + endif () else() add_executable(testERA5Atm "ERA5Atm_test.cpp") target_compile_definitions(testERA5Atm PRIVATE TEST_FILES_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\") diff --git a/physics/test/MainMPI.cpp b/physics/test/MainMPI.cpp new file mode 100644 index 000000000..5fde9684e --- /dev/null +++ b/physics/test/MainMPI.cpp @@ -0,0 +1,30 @@ +/*! + * @file MainMPI.cpp + * + * @date 13 Sep 2024 + * @author Einar Ólason + * + * This file is required so that the MPI enabled doc-test OASISCoupledOcean_test.cpp (and any + * others) can run. + */ + +#define DOCTEST_CONFIG_IMPLEMENT + +#include + +int main(int argc, char** argv) +{ + doctest::mpi_init_thread(argc, argv, MPI_THREAD_MULTIPLE); + + doctest::Context ctx; + ctx.setOption("reporters", "MpiConsoleReporter"); + ctx.setOption("reporters", "MpiFileReporter"); + ctx.setOption("force-colors", true); + ctx.applyCommandLine(argc, argv); + + int test_result = ctx.run(); + + doctest::mpi_finalize(); + + return test_result; +} diff --git a/physics/test/OASISCoupledOcean_test.cpp b/physics/test/OASISCoupledOcean_test.cpp new file mode 100644 index 000000000..1c4109728 --- /dev/null +++ b/physics/test/OASISCoupledOcean_test.cpp @@ -0,0 +1,40 @@ +/*! + * @file OASISCoupledOcean_test.cpp + * + * @date 13 Sep 2024 + * @author Einar Ólason + */ + +#include + +#include "include/OASISCoupledOcean.hpp" + +namespace Nextsim { + +TEST_SUITE_BEGIN("OASISCoupledOcean"); +MPI_TEST_CASE("OASIS init put and get", 2) +{ + MPI_Comm modelCommunicator; + int compID; // Not actually used. Only useful for debugging + const std::string compName = "nextsim"; // Not useful for any setups we have in mind + OASIS_CHECK_ERR(oasis_c_init_comp(&compID, compName.c_str(), OASIS_COUPLED)); + OASIS_CHECK_ERR(oasis_c_get_localcomm(&modelCommunicator)); + + ModelArray::setDimensions(ModelArray::Type::H, { 1, 1 }); + ModelArray::setDimensions(ModelArray::Type::Z, { 1, 1, 1 }); + + HField cice(ModelArray::Type::H); + cice = 1.0; + ModelComponent::getStore().registerArray(Protected::C_ICE, &cice, RO); + OASISCoupledOcean ocpl; + ModelMetadata metadata; + + ocpl.setData(ModelState::DataMap()); + ocpl.setMetadata(metadata); + ocpl.updateBefore(TimestepTime()); + ocpl.updateAfter(TimestepTime()); + + OASIS_CHECK_ERR(oasis_c_terminate()); +} +TEST_SUITE_END(); +} \ No newline at end of file From 27a72db26f8843add933a045f7a2a2a62201caec Mon Sep 17 00:00:00 2001 From: Andrea Piacentini Date: Mon, 16 Sep 2024 15:37:17 +0200 Subject: [PATCH 30/78] Issue #459 : update input and output coupling field labels lists at configuration. --- .../src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index a969b8b17..4428acd0f 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -141,9 +141,14 @@ void OASISCoupledOcean::configure() CIceKey = Configured::getConfiguration(CIceConfigKey, CIceKeyDefault); firstLayerDepth = Configured::getConfiguration(layerDepthConfigKey, FIRST_LAYER_DEPTH); + + cplStringsIn + = { SSTKey, SSSKey, UOceanKey, VOceanKey, SSHKey }; if (Configured::getConfiguration(exchangeFirstLayerConfigKey, EXCHANGE_FIRST_LAYER)) { cplStringsIn.push_back(MLDKey); } + cplStringsOut + = { TauXKey, TauYKey, TauModKey, EMPKey, QNoSunKey, QSWKey, SFluxKey, CIceKey }; } OASISCoupledOcean::HelpMap& OASISCoupledOcean::getHelpText(HelpMap& map, bool getAll) From 83cdb2999400b1f344d245eb38877adcbeab8141 Mon Sep 17 00:00:00 2001 From: Andrea Piacentini Date: Mon, 16 Sep 2024 15:40:02 +0200 Subject: [PATCH 31/78] Issue #459 : complete first working version of the unit test for OASIS coupling --- .../OceanBoundaryModule/OASISCoupledOcean.cpp | 8 +- physics/test/OASISCoupledOcean_test.cpp | 30 +++++++- physics/test/generate_OASIS_NetCDF.sh | 75 +++++++++++++++++++ 3 files changed, 107 insertions(+), 6 deletions(-) create mode 100755 physics/test/generate_OASIS_NetCDF.sh diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 4428acd0f..8a580bab4 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -60,11 +60,11 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &v[0], &kinfo)); // TODO: Implement ssh reading and passing to dynamics! - // OASIS_CHECK_ERR(oasis_c_get(cplIdIn[couplingIdIn::SSHKey], OASISTime, dimension0, dimension1, + // OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSHKey), OASISTime, dimension0, dimension1, // bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &ssh[0], &kinfo)); - if (couplingId.find(SSHKey) != couplingId.end()) { - OASIS_CHECK_ERR(oasis_c_get(couplingId.at(VOceanKey), OASISTime, dimension0, dimension1, + if (couplingId.find(MLDKey) != couplingId.end()) { + OASIS_CHECK_ERR(oasis_c_get(couplingId.at(MLDKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &mld[0], &kinfo)); } else { mld = firstLayerDepth; @@ -91,7 +91,7 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) // TODO We still need the the actual data HField dummy; dummy.resize(); - dummy.setData(0.); + dummy.setData(184.); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauXKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); diff --git a/physics/test/OASISCoupledOcean_test.cpp b/physics/test/OASISCoupledOcean_test.cpp index 1c4109728..7a8f32d30 100644 --- a/physics/test/OASISCoupledOcean_test.cpp +++ b/physics/test/OASISCoupledOcean_test.cpp @@ -12,7 +12,7 @@ namespace Nextsim { TEST_SUITE_BEGIN("OASISCoupledOcean"); -MPI_TEST_CASE("OASIS init put and get", 2) +MPI_TEST_CASE("OASIS init put and get", 1) { MPI_Comm modelCommunicator; int compID; // Not actually used. Only useful for debugging @@ -23,18 +23,44 @@ MPI_TEST_CASE("OASIS init put and get", 2) ModelArray::setDimensions(ModelArray::Type::H, { 1, 1 }); ModelArray::setDimensions(ModelArray::Type::Z, { 1, 1, 1 }); + double sstIn = -1.84; + double sssIn = 28.0; + //double mldIn = 14.8; + double uIn = -0.14; + double vIn = 0.71; + HField cice(ModelArray::Type::H); cice = 1.0; ModelComponent::getStore().registerArray(Protected::C_ICE, &cice, RO); OASISCoupledOcean ocpl; ModelMetadata metadata; + const std::vector partInfo = { OASIS_Serial, 1, 1 }; + OASIS_CHECK_ERR(oasis_c_def_partition( + &metadata.OASISPartitionId, OASIS_Serial_Params, &partInfo[0], OASIS_No_Gsize, compName.c_str())); ocpl.setData(ModelState::DataMap()); + ocpl.configure(); ocpl.setMetadata(metadata); + OASIS_CHECK_ERR(oasis_c_enddef()); + ocpl.updateBefore(TimestepTime()); + ModelArrayRef sst(ModelComponent::getStore()); + ModelArrayRef sss(ModelComponent::getStore()); + ModelArrayRef u(ModelComponent::getStore()); + ModelArrayRef v(ModelComponent::getStore()); + std::cout << "Received SST at time " << ocpl.OASISTime << ": " << sst[0] << std::endl ; + std::cout << "Received SSS at time " << ocpl.OASISTime << ": " << sss[0] << std::endl ; + std::cout << "Received OCEAN_U at time " << ocpl.OASISTime << ": " << u[0] << std::endl ; + std::cout << "Received OCEAN_V at time " << ocpl.OASISTime << ": " << v[0] << std::endl ; + REQUIRE(sst[0] == sstIn); + REQUIRE(sss[0] == sssIn); + REQUIRE(u[0] == uIn); + REQUIRE(v[0] == vIn); + //REQUIRE(mld[0] == mldIn); + ocpl.updateAfter(TimestepTime()); OASIS_CHECK_ERR(oasis_c_terminate()); } TEST_SUITE_END(); -} \ No newline at end of file +} diff --git a/physics/test/generate_OASIS_NetCDF.sh b/physics/test/generate_OASIS_NetCDF.sh new file mode 100755 index 000000000..a7b265826 --- /dev/null +++ b/physics/test/generate_OASIS_NetCDF.sh @@ -0,0 +1,75 @@ +#!/usr/bin/bash + +InVars=(I_SST:-1.84 I_SSS:28 I_Uocn:-0.14 I_Vocn:0.71) +# Optionally add +# InVars+=(I_SSH:14.8 I_MLD:14.8) + +OutVars=(I_taux I_tauy I_taumod I_fwflux I_rsnos I_rsso I_sfi I_conc) + +cat > namcouple < tmp.cdl <> namcouple + +done + +cat >> namcouple <> namcouple <> namcouple From 9db81c8d3b5a32d59f9326205b7ac0b84faff373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Wed, 12 Feb 2025 09:54:39 +0100 Subject: [PATCH 32/78] Get the code to compile after merge I needed to create a default constructor for the SlabOcean class, and registerUnique the IIceOceanHeatFlux and IFreezingPoint modules in OASISCoupledOcean.cpp. I also changed /usr/bin/bash to /bin/bash in generate_OASIS_NetCDF.sh. --- physics/src/include/SlabOcean.hpp | 4 ++- .../OceanBoundaryModule/OASISCoupledOcean.cpp | 21 ++++++++++----- .../include/OASISCoupledOcean.hpp | 26 ++++++------------- physics/test/generate_OASIS_NetCDF.sh | 2 +- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/physics/src/include/SlabOcean.hpp b/physics/src/include/SlabOcean.hpp index fd885b74c..91585c914 100644 --- a/physics/src/include/SlabOcean.hpp +++ b/physics/src/include/SlabOcean.hpp @@ -1,7 +1,7 @@ /*! * @file SlabOcean.hpp * - * @date 10 Feb 2025 + * @date 12 Feb 2025 * @author Tim Spain */ @@ -23,6 +23,8 @@ namespace Nextsim { */ class SlabOcean : public ModelComponent, public Configured { public: + SlabOcean() = delete; + SlabOcean(ModelArrayReferenceStore& coupingArrays) : qdw(ModelArray::Type::H) , fdw(ModelArray::Type::H) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 8a580bab4..7ee9fa109 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -1,15 +1,17 @@ /*! * @file OASISCoupledOcean.cpp * - * @date 10 Sep 2024 + * @date 12 Feb 2025 * @author Tim Spain * @author Einar Ólason */ #include "include/OASISCoupledOcean.hpp" + +#include "include/Finalizer.hpp" +#include "include/IFreezingPoint.hpp" #include "include/IIceOceanHeatFlux.hpp" -#include "include/Module.hpp" -#include "include/constants.hpp" +#include "include/NextsimModule.hpp" namespace Nextsim { @@ -125,6 +127,9 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) void OASISCoupledOcean::configure() { + Finalizer::registerUnique(Module::finalize); + Finalizer::registerUnique(Module::finalize); + SSTKey = Configured::getConfiguration(SSTConfigKey, SSTKeyDefault); SSSKey = Configured::getConfiguration(SSSConfigKey, SSSKeyDefault); UOceanKey = Configured::getConfiguration(UOceanConfigKey, UOceanKeyDefault); @@ -142,13 +147,11 @@ void OASISCoupledOcean::configure() firstLayerDepth = Configured::getConfiguration(layerDepthConfigKey, FIRST_LAYER_DEPTH); - cplStringsIn - = { SSTKey, SSSKey, UOceanKey, VOceanKey, SSHKey }; + cplStringsIn = { SSTKey, SSSKey, UOceanKey, VOceanKey, SSHKey }; if (Configured::getConfiguration(exchangeFirstLayerConfigKey, EXCHANGE_FIRST_LAYER)) { cplStringsIn.push_back(MLDKey); } - cplStringsOut - = { TauXKey, TauYKey, TauModKey, EMPKey, QNoSunKey, QSWKey, SFluxKey, CIceKey }; + cplStringsOut = { TauXKey, TauYKey, TauModKey, EMPKey, QNoSunKey, QSWKey, SFluxKey, CIceKey }; } OASISCoupledOcean::HelpMap& OASISCoupledOcean::getHelpText(HelpMap& map, bool getAll) @@ -198,4 +201,8 @@ OASISCoupledOcean::HelpMap& OASISCoupledOcean::getHelpRecursive(HelpMap& map, bo return getHelpText(map, getAll); } +void OASISCoupledOcean::updateTf(const size_t i, const TimestepTime& tst) +{ + tf[i] = Module::getImplementation()(sss[i]); +} } /* namespace Nextsim */ diff --git a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp index 02cc37be6..415dffa86 100644 --- a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp +++ b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp @@ -1,7 +1,7 @@ /*! * @file OASISCoupledOcean.hpp * - * @date 09 Sep 2024 + * @date 12 Feb 2025 * @author Tim Spain * @author Einar Ólason */ @@ -9,11 +9,10 @@ #ifndef OASISCOUPLEDOCEAN_HPP #define OASISCOUPLEDOCEAN_HPP -#include "include/IFreezingPoint.hpp" #include "include/IOceanBoundary.hpp" -#include "include/Module.hpp" + +#include "include/Configured.hpp" #include "include/OASISCoupled.hpp" -#include "include/SlabOcean.hpp" namespace Nextsim { @@ -56,15 +55,11 @@ static const std::string SFluxConfigKey = ".salt_flux"; static const std::string CIceConfigKey = ".sea_ice_concentration"; //* Ocean boundary data values that are hardcoded. -class OASISCoupledOcean : public IOceanBoundary, - public OASISCoupled, - public Configured { +class OASISCoupledOcean final : public IOceanBoundary, + public OASISCoupled, + public Configured { public: - OASISCoupledOcean() - : IOceanBoundary() - { - } - ~OASISCoupledOcean() { OASISCoupled::~OASISCoupled(); } + ~OASISCoupledOcean() override { OASISCoupled::~OASISCoupled(); } std::string getName() const override { return moduleName; } void updateBefore(const TimestepTime& tst) override; @@ -83,12 +78,7 @@ class OASISCoupledOcean : public IOceanBoundary, const int dimension0 = ModelArray::dimensions(ModelArray::Type::H)[0]; const int dimension1 = ModelArray::dimensions(ModelArray::Type::H)[1]; - SlabOcean slabOcean; - - void updateTf(size_t i, const TimestepTime& tst) - { - tf[i] = Module::getImplementation()(sss[i]); - } + void updateTf(size_t i, const TimestepTime& tst); // A map to relate the strings in the namcouple file to the numbers def_var spits out std::map couplingId; diff --git a/physics/test/generate_OASIS_NetCDF.sh b/physics/test/generate_OASIS_NetCDF.sh index a7b265826..340247514 100755 --- a/physics/test/generate_OASIS_NetCDF.sh +++ b/physics/test/generate_OASIS_NetCDF.sh @@ -1,4 +1,4 @@ -#!/usr/bin/bash +#!/bin/bash InVars=(I_SST:-1.84 I_SSS:28 I_Uocn:-0.14 I_Vocn:0.71) # Optionally add From 1e3eee33220e4557bf70c326f5c59a1efa76b481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Wed, 12 Feb 2025 14:40:25 +0100 Subject: [PATCH 33/78] Put actual data in the oasis put calls Populate the oasis put calls with data that's now available in the model. I also added the ability to take the square root of a ModelArray, and a test of this. I needed this for NEMO's requirement for the magnitude of the ocean surface stress. I didn't use m_couplingArrays and I'm not sure how to do that well. There's a lot of repeat code here and this should be reduced. --- core/src/ModelArray.cpp | 9 ++- core/src/include/ModelArray.hpp | 6 +- core/test/ModelArray_test.cpp | 7 ++- .../OceanBoundaryModule/OASISCoupledOcean.cpp | 57 ++++++++++--------- .../include/OASISCoupledOcean.hpp | 6 +- 5 files changed, 50 insertions(+), 35 deletions(-) diff --git a/core/src/ModelArray.cpp b/core/src/ModelArray.cpp index 189a3a6e9..fca390754 100644 --- a/core/src/ModelArray.cpp +++ b/core/src/ModelArray.cpp @@ -1,7 +1,7 @@ /*! * @file ModelArray.cpp * - * @date Feb 24, 2022 + * @date 12 Feb 2025 * @author Tim Spain */ @@ -175,6 +175,13 @@ ModelArray& ModelArray::clampBelow(const ModelArray& minArr) return *this; } +ModelArray ModelArray::sqrt() +{ + ModelArray sqrted = ModelArray(type); + sqrted.m_data.array() = m_data.array().sqrt(); + return sqrted; +} + void ModelArray::setData(double value) { resize(); diff --git a/core/src/include/ModelArray.hpp b/core/src/include/ModelArray.hpp index d78b11d4f..648d91c25 100644 --- a/core/src/include/ModelArray.hpp +++ b/core/src/include/ModelArray.hpp @@ -1,7 +1,7 @@ /*! * @file ModelArray.hpp * - * @date 31 Oct 2024 + * @date 12 Feb 2025 * @author Tim Spain */ @@ -259,6 +259,10 @@ class ModelArray { * @param minArr the array of clamp minimum target values. */ ModelArray& clampBelow(const ModelArray& minArr); + /*! + * @brief Returns the per-element square root of the array + */ + ModelArray sqrt(); using MultiDim = std::vector; diff --git a/core/test/ModelArray_test.cpp b/core/test/ModelArray_test.cpp index 331866efb..82da2f367 100644 --- a/core/test/ModelArray_test.cpp +++ b/core/test/ModelArray_test.cpp @@ -1,7 +1,7 @@ /*! - * @file ModelData_test.cpp + * @file ModelArray_test.cpp * - * @date 24 Sep 2024 + * @date 12 Feb 2025 * @author Tim Spain */ @@ -182,6 +182,9 @@ TEST_CASE("Arithmetic tests") OneDField negative = -rhs; REQUIRE(negative[0] == -3); REQUIRE(negative[1] == 5); + OneDField squareRoot = lhs.sqrt(); + REQUIRE(squareRoot[0] == 3.); + REQUIRE(squareRoot[1] == std::sqrt(lhs[1])); double three = 3; double four = 4; diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 7ee9fa109..5389fabdb 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -20,7 +20,7 @@ void OASISCoupledOcean::setMetadata(const ModelMetadata& metadata) #ifdef USE_OASIS // OASIS defining variable - /* Populate the couplingId map with the id string and number pair. We need to do this seperately + /* Populate the couplingId map with the id string and number pair. We need to do this separately * for the input (get) and output (put) variables. */ // TODO: coplingID should be a map of where the pair is idNumber and // pointer to the ModelArray @@ -61,9 +61,8 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) OASIS_CHECK_ERR(oasis_c_get(couplingId.at(VOceanKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &v[0], &kinfo)); - // TODO: Implement ssh reading and passing to dynamics! - // OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSHKey), OASISTime, dimension0, dimension1, - // bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &ssh[0], &kinfo)); + OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSHKey), OASISTime, dimension0, dimension1, + bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &ssh[0], &kinfo)); if (couplingId.find(MLDKey) != couplingId.end()) { OASIS_CHECK_ERR(oasis_c_get(couplingId.at(MLDKey), OASISTime, dimension0, dimension1, @@ -95,28 +94,30 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) dummy.resize(); dummy.setData(184.); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauXKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &tauX[0], OASIS_No_Restart, &kinfo)); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauYKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &tauY[0], OASIS_No_Restart, &kinfo)); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(EMPKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &fwFlux[0], OASIS_No_Restart, &kinfo)); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(QSWKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &qswNet[0], OASIS_No_Restart, &kinfo)); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(QNoSunKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &qNoSun[0], OASIS_No_Restart, &kinfo)); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(SFluxKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &sFlux[0], OASIS_No_Restart, &kinfo)); + // NEMO wants this field, even if it can be deduced from tauX and tauY + const HField tauMod = (tauX * tauX + tauY * tauY).sqrt(); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauModKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &tauMod[0], OASIS_No_Restart, &kinfo)); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(CIceKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &dummy[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &cice[0], OASIS_No_Restart, &kinfo)); // Increment the "OASIS" time by the number of seconds in the time step updateOASISTime(tst); @@ -130,22 +131,22 @@ void OASISCoupledOcean::configure() Finalizer::registerUnique(Module::finalize); Finalizer::registerUnique(Module::finalize); - SSTKey = Configured::getConfiguration(SSTConfigKey, SSTKeyDefault); - SSSKey = Configured::getConfiguration(SSSConfigKey, SSSKeyDefault); - UOceanKey = Configured::getConfiguration(UOceanConfigKey, UOceanKeyDefault); - VOceanKey = Configured::getConfiguration(VOceanConfigKey, VOceanKeyDefault); - SSHKey = Configured::getConfiguration(SSHConfigKey, SSHKeyDefault); - MLDKey = Configured::getConfiguration(MLDConfigKey, MLDKeyDefault); - TauXKey = Configured::getConfiguration(TauXConfigKey, TauXKeyDefault); - TauYKey = Configured::getConfiguration(TauYConfigKey, TauYKeyDefault); - TauModKey = Configured::getConfiguration(TauModConfigKey, TauModKeyDefault); - EMPKey = Configured::getConfiguration(EMPConfigKey, EMPKeyDefault); - QNoSunKey = Configured::getConfiguration(QNoSunConfigKey, QNoSunKeyDefault); - QSWKey = Configured::getConfiguration(QSWConfigKey, QSWKeyDefault); - SFluxKey = Configured::getConfiguration(SFluxConfigKey, SFluxKeyDefault); - CIceKey = Configured::getConfiguration(CIceConfigKey, CIceKeyDefault); - - firstLayerDepth = Configured::getConfiguration(layerDepthConfigKey, FIRST_LAYER_DEPTH); + SSTKey = getConfiguration(SSTConfigKey, SSTKeyDefault); + SSSKey = getConfiguration(SSSConfigKey, SSSKeyDefault); + UOceanKey = getConfiguration(UOceanConfigKey, UOceanKeyDefault); + VOceanKey = getConfiguration(VOceanConfigKey, VOceanKeyDefault); + SSHKey = getConfiguration(SSHConfigKey, SSHKeyDefault); + MLDKey = getConfiguration(MLDConfigKey, MLDKeyDefault); + TauXKey = getConfiguration(TauXConfigKey, TauXKeyDefault); + TauYKey = getConfiguration(TauYConfigKey, TauYKeyDefault); + TauModKey = getConfiguration(TauModConfigKey, TauModKeyDefault); + EMPKey = getConfiguration(EMPConfigKey, EMPKeyDefault); + QNoSunKey = getConfiguration(QNoSunConfigKey, QNoSunKeyDefault); + QSWKey = getConfiguration(QSWConfigKey, QSWKeyDefault); + SFluxKey = getConfiguration(SFluxConfigKey, SFluxKeyDefault); + CIceKey = getConfiguration(CIceConfigKey, CIceKeyDefault); + + firstLayerDepth = getConfiguration(layerDepthConfigKey, FIRST_LAYER_DEPTH); cplStringsIn = { SSTKey, SSSKey, UOceanKey, VOceanKey, SSHKey }; if (Configured::getConfiguration(exchangeFirstLayerConfigKey, EXCHANGE_FIRST_LAYER)) { diff --git a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp index 415dffa86..88190bfeb 100644 --- a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp +++ b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp @@ -55,9 +55,9 @@ static const std::string SFluxConfigKey = ".salt_flux"; static const std::string CIceConfigKey = ".sea_ice_concentration"; //* Ocean boundary data values that are hardcoded. -class OASISCoupledOcean final : public IOceanBoundary, - public OASISCoupled, - public Configured { +class OASISCoupledOcean : public IOceanBoundary, + public OASISCoupled, + public Configured { public: ~OASISCoupledOcean() override { OASISCoupled::~OASISCoupled(); } From 28e58b4884c255d98a3a4f0c347f1c091953fd79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Wed, 12 Feb 2025 14:50:31 +0100 Subject: [PATCH 34/78] CMakeLists.txt formatting Two lines were too long for the linter's taste. --- physics/test/CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/physics/test/CMakeLists.txt b/physics/test/CMakeLists.txt index 0a204ad34..2ede198c3 100644 --- a/physics/test/CMakeLists.txt +++ b/physics/test/CMakeLists.txt @@ -39,8 +39,10 @@ if(ENABLE_MPI) target_link_libraries(testTOPAZOcn_MPI1 PRIVATE nextsimlib doctest::doctest) if (ENABLE_OASIS) add_executable(testOASISCoupledOcean_MPI2 "OASISCoupledOcean_test.cpp" "MainMPI.cpp") - target_include_directories(testOASISCoupledOcean_MPI2 PRIVATE "${ModulesRoot}/OceanBoundaryModule") - target_compile_definitions(testOASISCoupledOcean_MPI2 PRIVATE TEST_FILES_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\") + target_include_directories(testOASISCoupledOcean_MPI2 + PRIVATE "${ModulesRoot}/OceanBoundaryModule") + target_compile_definitions(testOASISCoupledOcean_MPI2 + PRIVATE TEST_FILES_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\") target_link_libraries(testOASISCoupledOcean_MPI2 PRIVATE nextsimlib doctest::doctest) endif () else() From 2cac4d58de4ede35311541cd64e7080c60611ebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Wed, 12 Feb 2025 14:52:56 +0100 Subject: [PATCH 35/78] CMakeLists.txt formatting One more line that was too long. --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 97446808c..af3ed08e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -161,7 +161,8 @@ if (ENABLE_OASIS) target_link_directories(nextsimlib PUBLIC ${NETCDF_LIBRARY_DIRS}) target_link_directories(nextsimlib PUBLIC ${OASIS_LIBRARIES}) - target_link_libraries(nextsimlib PUBLIC oasis.cbind mct mpeu psmile.MPI1 scrip netcdff netcdf) + target_link_libraries(nextsimlib + PUBLIC oasis.cbind mct mpeu psmile.MPI1 scrip netcdff netcdf) target_include_directories(nextsimlib PUBLIC ${OASIS_INCLUDES}) else () message(FATAL_ERROR "Cannot build with OASIS support, because MPI is not enabled" .) From d74a741cd216e7404502a528fb018f3d69bc3171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Wed, 12 Feb 2025 14:56:38 +0100 Subject: [PATCH 36/78] Minor cleaning Get ridd of the dummy data we had before. --- physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 5389fabdb..08152a20e 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -89,10 +89,6 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) #ifdef USE_OASIS int kinfo; - // TODO We still need the the actual data - HField dummy; - dummy.resize(); - dummy.setData(184.); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauXKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &tauX[0], OASIS_No_Restart, &kinfo)); From a616c35c7a311dc799f7135c1bcb313a2b1565c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 13 Feb 2025 07:03:30 +0100 Subject: [PATCH 37/78] Add SSH to the test Since we receive SSH now, the test didn't run without this addition. --- physics/test/OASISCoupledOcean_test.cpp | 16 +++++++++++----- physics/test/generate_OASIS_NetCDF.sh | 4 ++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/physics/test/OASISCoupledOcean_test.cpp b/physics/test/OASISCoupledOcean_test.cpp index 7a8f32d30..7d735f602 100644 --- a/physics/test/OASISCoupledOcean_test.cpp +++ b/physics/test/OASISCoupledOcean_test.cpp @@ -1,7 +1,7 @@ /*! * @file OASISCoupledOcean_test.cpp * - * @date 13 Sep 2024 + * @date 13 Feb 2025 * @author Einar Ólason */ @@ -25,9 +25,10 @@ MPI_TEST_CASE("OASIS init put and get", 1) double sstIn = -1.84; double sssIn = 28.0; - //double mldIn = 14.8; + // double mldIn = 14.8; double uIn = -0.14; double vIn = 0.71; + double sshIn = 14.8; HField cice(ModelArray::Type::H); cice = 1.0; @@ -35,8 +36,8 @@ MPI_TEST_CASE("OASIS init put and get", 1) OASISCoupledOcean ocpl; ModelMetadata metadata; const std::vector partInfo = { OASIS_Serial, 1, 1 }; - OASIS_CHECK_ERR(oasis_c_def_partition( - &metadata.OASISPartitionId, OASIS_Serial_Params, &partInfo[0], OASIS_No_Gsize, compName.c_str())); + OASIS_CHECK_ERR(oasis_c_def_partition(&metadata.OASISPartitionId, OASIS_Serial_Params, + &partInfo[0], OASIS_No_Gsize, compName.c_str())); ocpl.setData(ModelState::DataMap()); ocpl.configure(); @@ -48,15 +49,20 @@ MPI_TEST_CASE("OASIS init put and get", 1) ModelArrayRef sss(ModelComponent::getStore()); ModelArrayRef u(ModelComponent::getStore()); ModelArrayRef v(ModelComponent::getStore()); + ModelArrayRef ssh(ModelComponent::getStore()); + /* std::cout << "Received SST at time " << ocpl.OASISTime << ": " << sst[0] << std::endl ; std::cout << "Received SSS at time " << ocpl.OASISTime << ": " << sss[0] << std::endl ; std::cout << "Received OCEAN_U at time " << ocpl.OASISTime << ": " << u[0] << std::endl ; std::cout << "Received OCEAN_V at time " << ocpl.OASISTime << ": " << v[0] << std::endl ; + std::cout << "Received OCEAN_V at time " << ocpl.OASISTime << ": " << ssh[0] << std::endl ; + */ REQUIRE(sst[0] == sstIn); REQUIRE(sss[0] == sssIn); REQUIRE(u[0] == uIn); REQUIRE(v[0] == vIn); - //REQUIRE(mld[0] == mldIn); + REQUIRE(ssh[0] == sshIn); + // REQUIRE(mld[0] == mldIn); ocpl.updateAfter(TimestepTime()); diff --git a/physics/test/generate_OASIS_NetCDF.sh b/physics/test/generate_OASIS_NetCDF.sh index 340247514..54972387e 100755 --- a/physics/test/generate_OASIS_NetCDF.sh +++ b/physics/test/generate_OASIS_NetCDF.sh @@ -1,8 +1,8 @@ #!/bin/bash -InVars=(I_SST:-1.84 I_SSS:28 I_Uocn:-0.14 I_Vocn:0.71) +InVars=(I_SST:-1.84 I_SSS:28 I_Uocn:-0.14 I_Vocn:0.71 I_SSH:14.8) # Optionally add -# InVars+=(I_SSH:14.8 I_MLD:14.8) +# InVars+=(I_MLD:14.8) OutVars=(I_taux I_tauy I_taumod I_fwflux I_rsnos I_rsso I_sfi I_conc) From a570b6309986da9d7ad743b83e1fc8ea115ed51c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 13 Feb 2025 09:39:21 +0100 Subject: [PATCH 38/78] Appropriate defaults for OASISCoupledOcean.hpp The constructor didn't set any, and setting defaults for cplStringsIn and cplStringsOut in the header file doesn't make sense. --- .../include/OASISCoupledOcean.hpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp index 88190bfeb..b9ff59def 100644 --- a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp +++ b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp @@ -1,7 +1,7 @@ /*! * @file OASISCoupledOcean.hpp * - * @date 12 Feb 2025 + * @date 13 Feb 2025 * @author Tim Spain * @author Einar Ólason */ @@ -59,6 +59,7 @@ class OASISCoupledOcean : public IOceanBoundary, public OASISCoupled, public Configured { public: + OASISCoupledOcean() = default; ~OASISCoupledOcean() override { OASISCoupled::~OASISCoupled(); } std::string getName() const override { return moduleName; } @@ -85,10 +86,11 @@ class OASISCoupledOcean : public IOceanBoundary, std::string SSTKey, SSSKey, UOceanKey, VOceanKey, SSHKey, MLDKey, TauXKey, TauYKey, TauModKey, EMPKey, QNoSunKey, QSWKey, SFluxKey, CIceKey; - std::vector cplStringsIn - = { SSTKey, SSSKey, UOceanKey, VOceanKey, SSHKey }; // MLDKey can be added to this one - std::vector cplStringsOut - = { TauXKey, TauYKey, TauModKey, EMPKey, QNoSunKey, QSWKey, SFluxKey, CIceKey }; + // A map relating the namcouple strings to ModelArrayRef's + std::map fieldTags; + + std::vector cplStringsIn; + std::vector cplStringsOut; }; } /* namespace Nextsim */ From a82d3ece330b3038d0fdd78974f90760c9c4a4da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Sat, 15 Feb 2025 14:51:19 +0100 Subject: [PATCH 39/78] Expanded OASISCoupledOcean_test.cpp and bugfixes The test now takes in all the inputs we expect and I've noted what the OASIS-produced netCDF files should contain. But there's no way of testing this automatically still. Fixed a copy-paste bug and added a call to mergeFluxes in OASISCoupledOcean.cpp. --- .../OceanBoundaryModule/OASISCoupledOcean.cpp | 16 ++++- .../src/modules/include/IOceanBoundary.hpp | 6 +- physics/test/OASISCoupledOcean_test.cpp | 66 +++++++++++++++++-- 3 files changed, 80 insertions(+), 8 deletions(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 08152a20e..a2d93db01 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -1,7 +1,7 @@ /*! * @file OASISCoupledOcean.cpp * - * @date 12 Feb 2025 + * @date 15 Feb 2025 * @author Tim Spain * @author Einar Ólason */ @@ -87,6 +87,8 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) void OASISCoupledOcean::updateAfter(const TimestepTime& tst) { #ifdef USE_OASIS + mergeFluxes(tst); + int kinfo; OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauXKey), OASISTime, dimension0, dimension1, 1, @@ -117,6 +119,18 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) // Increment the "OASIS" time by the number of seconds in the time step updateOASISTime(tst); + + /* + std::cout << "The OASIS output file should contain:\n"; + std::cout << TauXKey << ": " << tauX[0] << std::endl; + std::cout << TauYKey << ": " << tauY[0] << std::endl; + std::cout << TauModKey << ": " << tauMod[0] << std::endl; + std::cout << EMPKey << ": " << fwFlux[0] << std::endl; + std::cout << QSWKey << ": " << qswNet[0] << std::endl; + std::cout << QNoSunKey << ": " << qNoSun[0] << std::endl; + std::cout << SFluxKey << ": " << sFlux[0] << std::endl; + std::cout << CIceKey << ": " << cice[0] << std::endl; + */ #else throw std::runtime_error(std::string(__func__) + ": " + OASISError); #endif diff --git a/physics/src/modules/include/IOceanBoundary.hpp b/physics/src/modules/include/IOceanBoundary.hpp index 903c8d877..1a7a1f121 100644 --- a/physics/src/modules/include/IOceanBoundary.hpp +++ b/physics/src/modules/include/IOceanBoundary.hpp @@ -1,7 +1,7 @@ /*! * @file IOceanBoundary.hpp * - * @date 13 Feb 2025 + * @date 15 Feb 2025 * @author Tim Spain */ @@ -9,8 +9,8 @@ #define IOCEANBOUNDARY_HPP #include "include/ModelComponent.hpp" -#include "include/constants.hpp" #include "include/ModelMetadata.hpp" +#include "include/constants.hpp" namespace Nextsim { @@ -179,7 +179,7 @@ class IOceanBoundary : public ModelComponent { ModelArrayRef cice; ModelArrayRef emp; ModelArrayRef tauXIO; - ModelArrayRef tauYIO; + ModelArrayRef tauYIO; ModelArrayRef newIce; ModelArrayRef deltaHice; ModelArrayRef deltaSmelt; diff --git a/physics/test/OASISCoupledOcean_test.cpp b/physics/test/OASISCoupledOcean_test.cpp index 7d735f602..add2689b0 100644 --- a/physics/test/OASISCoupledOcean_test.cpp +++ b/physics/test/OASISCoupledOcean_test.cpp @@ -1,7 +1,7 @@ /*! * @file OASISCoupledOcean_test.cpp * - * @date 13 Feb 2025 + * @date 15 Feb 2025 * @author Einar Ólason */ @@ -31,8 +31,45 @@ MPI_TEST_CASE("OASIS init put and get", 1) double sshIn = 14.8; HField cice(ModelArray::Type::H); - cice = 1.0; + cice = 0.8; ModelComponent::getStore().registerArray(Protected::C_ICE, &cice, RO); + + HField emp(ModelArray::Type::H); + emp = 1e-6; + ModelComponent::getStore().registerArray(Protected::EVAP_MINUS_PRECIP, &emp, RO); + + HField tauXIO(ModelArray::Type::H); + tauXIO = 3e-2; + ModelComponent::getStore().registerArray(Protected::IO_STRESS_X, &tauXIO); + + HField tauYIO(ModelArray::Type::H); + tauYIO = 4e-2; + ModelComponent::getStore().registerArray(Protected::IO_STRESS_Y, &tauYIO); + + HField newIce(ModelArray::Type::H); + newIce = 4e-2; + ModelComponent::getStore().registerArray(Shared::NEW_ICE, &newIce, RW); + + HField deltaHice(ModelArray::Type::H); + deltaHice = 1e-4; + ModelComponent::getStore().registerArray(Shared::DELTA_HICE, &deltaHice, RW); + + HField deltaSmelt(ModelArray::Type::H); + deltaSmelt = 1e-4; + ModelComponent::getStore().registerArray(Shared::HSNOW_MELT, &deltaSmelt, RW); + + HField qow(ModelArray::Type::H); + qow = 100; + ModelComponent::getStore().registerArray(Shared::Q_OW, &qow, RW); + + HField tauXOW(ModelArray::Type::H); + tauXOW = tauXIO; + ModelComponent::getStore().registerArray(Shared::OW_STRESS_X, &tauXOW, RW); + + HField tauYOW(ModelArray::Type::H); + tauYOW = tauYIO; + ModelComponent::getStore().registerArray(Shared::OW_STRESS_Y, &tauYOW, RW); + OASISCoupledOcean ocpl; ModelMetadata metadata; const std::vector partInfo = { OASIS_Serial, 1, 1 }; @@ -44,7 +81,11 @@ MPI_TEST_CASE("OASIS init put and get", 1) ocpl.setMetadata(metadata); OASIS_CHECK_ERR(oasis_c_enddef()); - ocpl.updateBefore(TimestepTime()); + TimePoint t1("2000-01-01T00:00:00Z"); + TimestepTime tst = { t1, Duration(600) }; + + ocpl.updateBefore(tst); + ModelArrayRef sst(ModelComponent::getStore()); ModelArrayRef sss(ModelComponent::getStore()); ModelArrayRef u(ModelComponent::getStore()); @@ -64,7 +105,24 @@ MPI_TEST_CASE("OASIS init put and get", 1) REQUIRE(ssh[0] == sshIn); // REQUIRE(mld[0] == mldIn); - ocpl.updateAfter(TimestepTime()); + ModelArrayRef qSwBase(ModelComponent::getStore()); + qSwBase[0] = -2.; + + ModelArrayRef qswow(ModelComponent::getStore()); + qswow[0] = -10.; + + ocpl.updateAfter(tst); + + /* The OASIS output file should contain: + * I_taux: 0.03 + * I_tauy: 0.04 + * I_taumod: 0.05 + * I_fwflux: 0.0609935 + * I_rsso: -3.6 + * I_rsnos: -1651.14 + * I_sfi: 0.000306278 + * I_conc: 0.8 + */ OASIS_CHECK_ERR(oasis_c_terminate()); } From 9df3368841ee15330bd913391fd4d30a9fefb690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Sat, 15 Feb 2025 18:03:56 +0100 Subject: [PATCH 40/78] Reverse the direction of heatfluxes sent through OASIS NEMO - and most ocean models, I believe - expects fluxes into the ocean to be positive, while neXtSIM uses "positive heat flux up" orientation. So, we need to send the negative of the surface heat fluxes to the ocean through OASIS. --- .../modules/OceanBoundaryModule/OASISCoupledOcean.cpp | 10 ++++++---- physics/test/OASISCoupledOcean_test.cpp | 8 ++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index a2d93db01..6f07cca62 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -100,11 +100,13 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) OASIS_CHECK_ERR(oasis_c_put(couplingId.at(EMPKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &fwFlux[0], OASIS_No_Restart, &kinfo)); + const HField qswDown = -qswNet; OASIS_CHECK_ERR(oasis_c_put(couplingId.at(QSWKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &qswNet[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &qswDown[0], OASIS_No_Restart, &kinfo)); + const HField qNoSunDown = -qNoSun; OASIS_CHECK_ERR(oasis_c_put(couplingId.at(QNoSunKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &qNoSun[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &qNoSunDown[0], OASIS_No_Restart, &kinfo)); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(SFluxKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &sFlux[0], OASIS_No_Restart, &kinfo)); @@ -126,8 +128,8 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) std::cout << TauYKey << ": " << tauY[0] << std::endl; std::cout << TauModKey << ": " << tauMod[0] << std::endl; std::cout << EMPKey << ": " << fwFlux[0] << std::endl; - std::cout << QSWKey << ": " << qswNet[0] << std::endl; - std::cout << QNoSunKey << ": " << qNoSun[0] << std::endl; + std::cout << QSWKey << ": " << qswDown[0] << std::endl; + std::cout << QNoSunKey << ": " << qNoSunDown[0] << std::endl; std::cout << SFluxKey << ": " << sFlux[0] << std::endl; std::cout << CIceKey << ": " << cice[0] << std::endl; */ diff --git a/physics/test/OASISCoupledOcean_test.cpp b/physics/test/OASISCoupledOcean_test.cpp index add2689b0..1a1ad8604 100644 --- a/physics/test/OASISCoupledOcean_test.cpp +++ b/physics/test/OASISCoupledOcean_test.cpp @@ -39,7 +39,7 @@ MPI_TEST_CASE("OASIS init put and get", 1) ModelComponent::getStore().registerArray(Protected::EVAP_MINUS_PRECIP, &emp, RO); HField tauXIO(ModelArray::Type::H); - tauXIO = 3e-2; + tauXIO = -3e-2; ModelComponent::getStore().registerArray(Protected::IO_STRESS_X, &tauXIO); HField tauYIO(ModelArray::Type::H); @@ -114,12 +114,12 @@ MPI_TEST_CASE("OASIS init put and get", 1) ocpl.updateAfter(tst); /* The OASIS output file should contain: - * I_taux: 0.03 + * I_taux: -0.03 * I_tauy: 0.04 * I_taumod: 0.05 * I_fwflux: 0.0609935 - * I_rsso: -3.6 - * I_rsnos: -1651.14 + * I_rsso: 3.6 + * I_rsnos: 1651.14 * I_sfi: 0.000306278 * I_conc: 0.8 */ From bc835ac8606eae334e7a93b05a4aa4e13e332a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Fri, 21 Mar 2025 10:16:04 +0100 Subject: [PATCH 41/78] Rename variable for default for exchanging first layer The new name uses the same naming convention as the other defaults. --- .../src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp | 6 +++--- .../OceanBoundaryModule/include/OASISCoupledOcean.hpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 6f07cca62..43d7ff0dc 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -1,7 +1,7 @@ /*! * @file OASISCoupledOcean.cpp * - * @date 15 Feb 2025 + * @date 21 Mar 2025 * @author Tim Spain * @author Einar Ólason */ @@ -161,7 +161,7 @@ void OASISCoupledOcean::configure() firstLayerDepth = getConfiguration(layerDepthConfigKey, FIRST_LAYER_DEPTH); cplStringsIn = { SSTKey, SSSKey, UOceanKey, VOceanKey, SSHKey }; - if (Configured::getConfiguration(exchangeFirstLayerConfigKey, EXCHANGE_FIRST_LAYER)) { + if (Configured::getConfiguration(exchangeFirstLayerConfigKey, exchangeFirstLayerDefault)) { cplStringsIn.push_back(MLDKey); } cplStringsOut = { TauXKey, TauYKey, TauModKey, EMPKey, QNoSunKey, QSWKey, SFluxKey, CIceKey }; @@ -173,7 +173,7 @@ OASISCoupledOcean::HelpMap& OASISCoupledOcean::getHelpText(HelpMap& map, bool ge { layerDepthConfigKey, ConfigType::NUMERIC, { "0", "∞" }, std::to_string(FIRST_LAYER_DEPTH), "m", "Depth of the first ocean model layer (if this is fixed)." }, { exchangeFirstLayerConfigKey, ConfigType::BOOLEAN, { "true", "false" }, - std::to_string(EXCHANGE_FIRST_LAYER), "", + std::to_string(exchangeFirstLayerDefault), "", "Use the thickness of the first ocean layer provided through the coupler" }, { SSTConfigKey, ConfigType::STRING, {}, SSTKeyDefault, "", "The field name for sea surface temperature used in namcouple" }, diff --git a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp index b9ff59def..ce4f750dc 100644 --- a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp +++ b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp @@ -1,7 +1,7 @@ /*! * @file OASISCoupledOcean.hpp * - * @date 13 Feb 2025 + * @date 21 Mar 2025 * @author Tim Spain * @author Einar Ólason */ @@ -22,7 +22,7 @@ static const std::string layerDepthConfigKey = moduleName + ".layer_depth"; static const std::string exchangeFirstLayerConfigKey = moduleName + ".exchange_first_layer"; static const double FIRST_LAYER_DEPTH = 1.; // There really is no sensible default here(!) -static const bool EXCHANGE_FIRST_LAYER = false; +static const bool exchangeFirstLayerDefault = false; static const std::string SSTKeyDefault = "I_SST"; static const std::string SSSKeyDefault = "I_SSS"; From b086649136cfb3fa1624e991c3918c08cbdae3e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Fri, 21 Mar 2025 11:56:59 +0100 Subject: [PATCH 42/78] Set taux and tauy in IDynamics.hpp This makes sure that we can still call IOCeanBoundary::mergeFluxes() without a crash when using DummyDynamics. --- core/src/modules/include/IDynamics.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/modules/include/IDynamics.hpp b/core/src/modules/include/IDynamics.hpp index 564aa5e12..67c19e8b0 100644 --- a/core/src/modules/include/IDynamics.hpp +++ b/core/src/modules/include/IDynamics.hpp @@ -1,7 +1,7 @@ /*! * @file IDynamics.hpp * - * @date 06 Dec 2024 + * @date 21 Mar 2025 * @author Tim Spain */ @@ -63,6 +63,8 @@ class IDynamics : public ModelComponent { uice.resize(); vice.resize(); damage.resize(); + taux.resize(); + tauy.resize(); if (!m_usesDamage) { damage = 0.; } From 7136ebbb5b4684b2bea3ad95d331910acba7231a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Fri, 21 Mar 2025 12:02:50 +0100 Subject: [PATCH 43/78] Move definition of dimension0 and 1 These were set in the header file as ModelArray::dimensions (ModelArray::Type::H)[0] and [1], but that was too early and the dimension was not correctly defined. Now, I just set these values locally in updateBefore and updateAfter. --- physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp | 4 ++++ .../modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 43d7ff0dc..f0c5fde20 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -48,6 +48,8 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) #ifdef USE_OASIS int kinfo; + const int dimension0 = ModelArray::dimensions(ModelArray::Type::H)[0]; + const int dimension1 = ModelArray::dimensions(ModelArray::Type::H)[1]; OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSTKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sst[0], &kinfo)); @@ -90,6 +92,8 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) mergeFluxes(tst); int kinfo; + const int dimension0 = ModelArray::dimensions(ModelArray::Type::H)[0]; + const int dimension1 = ModelArray::dimensions(ModelArray::Type::H)[1]; OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauXKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &tauX[0], OASIS_No_Restart, &kinfo)); diff --git a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp index ce4f750dc..3f5cfe2ee 100644 --- a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp +++ b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp @@ -76,9 +76,6 @@ class OASISCoupledOcean : public IOceanBoundary, int bundleSize = 1; // Always "unbundled", as per the OASIS manual double firstLayerDepth = FIRST_LAYER_DEPTH; - const int dimension0 = ModelArray::dimensions(ModelArray::Type::H)[0]; - const int dimension1 = ModelArray::dimensions(ModelArray::Type::H)[1]; - void updateTf(size_t i, const TimestepTime& tst); // A map to relate the strings in the namcouple file to the numbers def_var spits out From 761ebbaf812fc40de4a0dfb583b3778e8074d0fc Mon Sep 17 00:00:00 2001 From: Andrea Piacentini Date: Fri, 28 Mar 2025 15:45:07 +0100 Subject: [PATCH 44/78] [issue459] MPI/IO compliant restart filename --- core/src/DevStep.cpp | 2 ++ core/src/Model.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/core/src/DevStep.cpp b/core/src/DevStep.cpp index b3e706a5d..23da2fa99 100644 --- a/core/src/DevStep.cpp +++ b/core/src/DevStep.cpp @@ -44,6 +44,8 @@ void DevStep::iterate(const TimestepTime& tst) mData->incrementTime(tst.step); if ((m_restartPeriod.seconds() > 0) && (mData->time() >= lastOutput + m_restartPeriod)) { std::string currentFileName = mData->time().format(m_restartFileName); + // Some MPI-IO implementations does not like colon in file names + std::replace(currentFileName.begin(), currentFileName.end(), ':', '_'); pData->writeRestartFile(currentFileName, *mData); lastOutput = mData->time(); } diff --git a/core/src/Model.cpp b/core/src/Model.cpp index 909888342..10914e975 100644 --- a/core/src/Model.cpp +++ b/core/src/Model.cpp @@ -187,6 +187,8 @@ void Model::run() void Model::writeRestartFile() { std::string formattedFileName = m_etadata.time().format(finalFileName); + // Some MPI-IO implementations does not like colon in file names + std::replace(formattedFileName.begin(), formattedFileName.end(), ':', '_'); pData.writeRestartFile(formattedFileName, m_etadata); } From 7bdc52372eadc9c6ce0434e069e896cb287f03e8 Mon Sep 17 00:00:00 2001 From: Andrea Piacentini Date: Fri, 28 Mar 2025 15:46:40 +0100 Subject: [PATCH 45/78] [issue459] Enable help-config for OASISCoupledOcean --- core/src/main.cpp | 6 ++-- .../include/OASISCoupledOcean.hpp | 28 +++++++++---------- .../modules/OceanBoundaryModule/module.cfg | 2 +- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/core/src/main.cpp b/core/src/main.cpp index 8fede3b5d..e3cb4fa2b 100644 --- a/core/src/main.cpp +++ b/core/src/main.cpp @@ -67,11 +67,13 @@ int main(int argc, char* argv[]) model.configure(); // Run the Model model.run(); - } #ifdef USE_MPI #ifdef USE_OASIS - OASIS_CHECK_ERR(oasis_c_terminate()); + OASIS_CHECK_ERR(oasis_c_terminate()); +#endif #endif + } +#ifdef USE_MPI MPI_Finalize(); #endif diff --git a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp index 3f5cfe2ee..ddaedf2ed 100644 --- a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp +++ b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp @@ -39,20 +39,20 @@ static const std::string QSWKeyDefault = "I_rsso"; static const std::string SFluxKeyDefault = "I_sfi"; static const std::string CIceKeyDefault = "I_conc"; -static const std::string SSTConfigKey = ".sea_surface_temperature"; -static const std::string SSSConfigKey = ".sea_surface_salinity"; -static const std::string UOceanConfigKey = ".ocean_u_velocity"; -static const std::string VOceanConfigKey = ".ocean_v_velocity"; -static const std::string SSHConfigKey = ".sea_surface_height"; -static const std::string MLDConfigKey = ".first_ocean_layer_depth"; // This one is optional -static const std::string TauXConfigKey = ".ice_ocean_stress_x"; -static const std::string TauYConfigKey = ".ice_ocean_stress_y"; -static const std::string TauModConfigKey = ".ice_ocean_stress_modulo"; -static const std::string EMPConfigKey = ".fresh_water_flux"; -static const std::string QNoSunConfigKey = ".non_solar_heatflux"; -static const std::string QSWConfigKey = ".short_wave_flux"; -static const std::string SFluxConfigKey = ".salt_flux"; -static const std::string CIceConfigKey = ".sea_ice_concentration"; +static const std::string SSTConfigKey = moduleName + ".sea_surface_temperature"; +static const std::string SSSConfigKey = moduleName + ".sea_surface_salinity"; +static const std::string UOceanConfigKey = moduleName + ".ocean_u_velocity"; +static const std::string VOceanConfigKey = moduleName + ".ocean_v_velocity"; +static const std::string SSHConfigKey = moduleName + ".sea_surface_height"; +static const std::string MLDConfigKey = moduleName + ".first_ocean_layer_depth"; // This one is optional +static const std::string TauXConfigKey = moduleName + ".ice_ocean_stress_x"; +static const std::string TauYConfigKey = moduleName + ".ice_ocean_stress_y"; +static const std::string TauModConfigKey = moduleName + ".ice_ocean_stress_modulo"; +static const std::string EMPConfigKey = moduleName + ".fresh_water_flux"; +static const std::string QNoSunConfigKey = moduleName + ".non_solar_heatflux"; +static const std::string QSWConfigKey = moduleName + ".short_wave_flux"; +static const std::string SFluxConfigKey = moduleName + ".salt_flux"; +static const std::string CIceConfigKey = moduleName + ".sea_ice_concentration"; //* Ocean boundary data values that are hardcoded. class OASISCoupledOcean : public IOceanBoundary, diff --git a/physics/src/modules/OceanBoundaryModule/module.cfg b/physics/src/modules/OceanBoundaryModule/module.cfg index 312dc572a..9fa2f8613 100644 --- a/physics/src/modules/OceanBoundaryModule/module.cfg +++ b/physics/src/modules/OceanBoundaryModule/module.cfg @@ -35,4 +35,4 @@ has_help = false [Nextsim::OASISCoupledOcean] file_prefix = OASISCoupledOcean description = Receive and send ocean fields via the OASIS coupler -has_help = false +has_help = true From edc37588b4c0f13c5a0f293091285dfce3bbf6f9 Mon Sep 17 00:00:00 2001 From: Andrea Piacentini Date: Fri, 28 Mar 2025 15:47:25 +0100 Subject: [PATCH 46/78] [issue459] Fix ocean density --- physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index f0c5fde20..5f918dc68 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -73,7 +73,7 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) mld = firstLayerDepth; } - cpml = Water::cp * Water::rho * mld; + cpml = Water::rhoOcean * Water::cp * mld; overElements( std::bind(&OASISCoupledOcean::updateTf, this, std::placeholders::_1, std::placeholders::_2), From eae6a0808b0bf9cd373ff0c806341c648695ba4c Mon Sep 17 00:00:00 2001 From: Andrea Piacentini Date: Fri, 28 Mar 2025 15:56:03 +0100 Subject: [PATCH 47/78] [issue459] Try to please to the clang format check --- core/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main.cpp b/core/src/main.cpp index e3cb4fa2b..5a39c6003 100644 --- a/core/src/main.cpp +++ b/core/src/main.cpp @@ -69,7 +69,7 @@ int main(int argc, char* argv[]) model.run(); #ifdef USE_MPI #ifdef USE_OASIS - OASIS_CHECK_ERR(oasis_c_terminate()); + OASIS_CHECK_ERR(oasis_c_terminate()); #endif #endif } From 02416afdbacc61b49d27b94ed526f863d7a724f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 5 Jun 2025 13:20:27 +0200 Subject: [PATCH 48/78] Minor fix after merge You now have to say Configured::getConfiguration and not just getConfiguration, as before. --- .../OceanBoundaryModule/OASISCoupledOcean.cpp | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 5f918dc68..07bd7f3db 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -1,7 +1,7 @@ /*! * @file OASISCoupledOcean.cpp * - * @date 21 Mar 2025 + * @date 05 Jun 2025 * @author Tim Spain * @author Einar Ólason */ @@ -147,22 +147,22 @@ void OASISCoupledOcean::configure() Finalizer::registerUnique(Module::finalize); Finalizer::registerUnique(Module::finalize); - SSTKey = getConfiguration(SSTConfigKey, SSTKeyDefault); - SSSKey = getConfiguration(SSSConfigKey, SSSKeyDefault); - UOceanKey = getConfiguration(UOceanConfigKey, UOceanKeyDefault); - VOceanKey = getConfiguration(VOceanConfigKey, VOceanKeyDefault); - SSHKey = getConfiguration(SSHConfigKey, SSHKeyDefault); - MLDKey = getConfiguration(MLDConfigKey, MLDKeyDefault); - TauXKey = getConfiguration(TauXConfigKey, TauXKeyDefault); - TauYKey = getConfiguration(TauYConfigKey, TauYKeyDefault); - TauModKey = getConfiguration(TauModConfigKey, TauModKeyDefault); - EMPKey = getConfiguration(EMPConfigKey, EMPKeyDefault); - QNoSunKey = getConfiguration(QNoSunConfigKey, QNoSunKeyDefault); - QSWKey = getConfiguration(QSWConfigKey, QSWKeyDefault); - SFluxKey = getConfiguration(SFluxConfigKey, SFluxKeyDefault); - CIceKey = getConfiguration(CIceConfigKey, CIceKeyDefault); - - firstLayerDepth = getConfiguration(layerDepthConfigKey, FIRST_LAYER_DEPTH); + SSTKey = Configured::getConfiguration(SSTConfigKey, SSTKeyDefault); + SSSKey = Configured::getConfiguration(SSSConfigKey, SSSKeyDefault); + UOceanKey = Configured::getConfiguration(UOceanConfigKey, UOceanKeyDefault); + VOceanKey = Configured::getConfiguration(VOceanConfigKey, VOceanKeyDefault); + SSHKey = Configured::getConfiguration(SSHConfigKey, SSHKeyDefault); + MLDKey = Configured::getConfiguration(MLDConfigKey, MLDKeyDefault); + TauXKey = Configured::getConfiguration(TauXConfigKey, TauXKeyDefault); + TauYKey = Configured::getConfiguration(TauYConfigKey, TauYKeyDefault); + TauModKey = Configured::getConfiguration(TauModConfigKey, TauModKeyDefault); + EMPKey = Configured::getConfiguration(EMPConfigKey, EMPKeyDefault); + QNoSunKey = Configured::getConfiguration(QNoSunConfigKey, QNoSunKeyDefault); + QSWKey = Configured::getConfiguration(QSWConfigKey, QSWKeyDefault); + SFluxKey = Configured::getConfiguration(SFluxConfigKey, SFluxKeyDefault); + CIceKey = Configured::getConfiguration(CIceConfigKey, CIceKeyDefault); + + firstLayerDepth = Configured::getConfiguration(layerDepthConfigKey, FIRST_LAYER_DEPTH); cplStringsIn = { SSTKey, SSSKey, UOceanKey, VOceanKey, SSHKey }; if (Configured::getConfiguration(exchangeFirstLayerConfigKey, exchangeFirstLayerDefault)) { From bba76aab26b065e63de596e99b1449e970ae3b50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Fri, 6 Jun 2025 09:02:53 +0200 Subject: [PATCH 49/78] Clang-format Removes a single empty line. --- core/src/ModelMetadata.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/ModelMetadata.cpp b/core/src/ModelMetadata.cpp index 4793e6a48..f38895c02 100644 --- a/core/src/ModelMetadata.cpp +++ b/core/src/ModelMetadata.cpp @@ -1,7 +1,7 @@ /*! * @file ModelMetadata.cpp * - * @date 19 May 2025 + * @date 06 Jun 2025 * @author Tim Spain */ @@ -189,7 +189,6 @@ void ModelMetadata::initOasis(const bool writeOasisGrid) } #endif - void ModelMetadata::setTime(const TimePoint& time) { m_time = time; From db9e053976263122010b5bd9384b4c927c2e9bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 31 Jul 2025 18:02:20 +0200 Subject: [PATCH 50/78] Fix CMakeLists.txt formatting It got into a hissy fit over spaces between "if" and "(". --- CMakeLists.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e049d97f..d1931671a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -157,8 +157,8 @@ if(ENABLE_XIOS) endif() # Do we want to include the OASIS interface? It'll only work if we have MPI -if (ENABLE_OASIS) - if (ENABLE_MPI) +if(ENABLE_OASIS) + if(ENABLE_MPI) message(STATUS "Building with OASIS support") find_package(OASIS REQUIRED) pkg_search_module(NETCDF REQUIRED netcdf-fortran) @@ -169,10 +169,10 @@ if (ENABLE_OASIS) target_link_libraries(nextsimlib PUBLIC oasis.cbind mct mpeu psmile.MPI1 scrip netcdff netcdf) target_include_directories(nextsimlib PUBLIC ${OASIS_INCLUDES}) - else () + else() message(FATAL_ERROR "Cannot build with OASIS support, because MPI is not enabled" .) - endif () -endif () + endif() +endif() # OpenMP if(WITH_THREADS) From 9a3d71592c571ff43b4811fe67cea07dd40c0a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Fri, 1 Aug 2025 10:10:20 +0200 Subject: [PATCH 51/78] Dockerfile for an image with OASIS installed This should be temporary, as an OASIS install should become part of Dockerfile.devenv in the future. --- Dockerfiles/Dockerfile.oasis | 21 +++++++++ Dockerfiles/make.oasis_docker | 82 +++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 Dockerfiles/Dockerfile.oasis create mode 100644 Dockerfiles/make.oasis_docker diff --git a/Dockerfiles/Dockerfile.oasis b/Dockerfiles/Dockerfile.oasis new file mode 100644 index 000000000..df9f6b901 --- /dev/null +++ b/Dockerfiles/Dockerfile.oasis @@ -0,0 +1,21 @@ +FROM nextsimdg-dev-env:latest + +# A temproary Dockerfile with commands that should find their way into Dockerfile.devenv in the end + +# Clone the repository +RUN git clone https://gitlab.com/cerfacs/oasis3-mct.git +WORKDIR /oasis3-mct/util/make_dir + +# Set some ENV's and copy the make file include +ENV OASIS_COUPLE=/oasis3-mct +ENV OASIS_ENV=oasis_docker_spack_gcc +COPY make.oasis_docker make.$OASIS_ENV + +# make! +RUN make -f TopMakefileOasis3 static-libs shared-libs + +# make install ... of sorts +RUN cp -a $OASIS_COUPLE/build.$OASIS_ENV/lib/* /usr/local/lib +RUN cp -a $OASIS_COUPLE/build.$OASIS_ENV/include/* /usr/local/include + +WORKDIR / \ No newline at end of file diff --git a/Dockerfiles/make.oasis_docker b/Dockerfiles/make.oasis_docker new file mode 100644 index 000000000..c611b07cf --- /dev/null +++ b/Dockerfiles/make.oasis_docker @@ -0,0 +1,82 @@ +# +# Include file for OASIS3 Makefile for a Linux docker image using spack and gcc +# +############################################################################### +# +# CHAN : communication technique used in OASIS3 (MPI1/MPI2) +CHAN = MPI1 +# +# Paths for libraries, object files and binaries +# +# COUPLE : path for oasis3-mct main directory +COUPLE := $(OASIS_COUPLE) +# +# ARCHDIR : directory created when compiling +ARCHDIR := $(COUPLE)/build.$(OASIS_ENV) +# +# MPI library +MPIDIR = /opt/view/ +MPIBIN = $(MPIDIR)/bin +MPI_INCLUDE = $(MPIDIR)/include +MPILIB = -L$(MPIDIR)/lib -lmpi +MPIRUN = $(MPIBIN)/mpirun +# +# NETCDF library of the system +NETCDF_INCLUDE = /opt/view/include +NETCDF_LIBRARY = -L/opt/view/lib -Wl,-rpath,/opt/view/lib -lnetcdff -lnetcdf -lm +# +# Compiling and other commands +MAKE = gmake +F90 = $(MPIBIN)/mpif90 -I$(MPI_INCLUDE) +F = $(F90) +f90 = $(F90) +f = $(F90) +CC = $(MPIBIN)/mpicc -I$(MPI_INCLUDE) +LD = $(MPIBIN)/mpif90 $(MPILIB) +DYNOPT = -fPIC +LDDYNOPT = -shared +AR = ar +ARFLAGS = -ruv +# Fortran libraries for C linking +F2C_LIBS = -lmpi_mpifh -lgfortran -lm +# +# CPP keys and compiler options +# +CPPDEF = -Duse_comm_$(CHAN) -D__VERBOSE -DTREAT_OVERLAY +# +FCBASEFLAGS := -fallow-argument-mismatch -ffree-line-length-0 +ifeq ($(OASIS_DEBUG), ) + ifeq ($(OASIS_OPENMP), ) + FCBASEFLAGS += -O2 + else + FCBASEFLAGS += -O2 -fopenmp + endif +else + ifeq ($(OASIS_OPENMP), ) + FCBASEFLAGS += -g -fbounds-check + else + FCBASEFLAGS += -g -fbounds-check -fopenmp + endif +endif +CCBASEFLAGS := +ifeq ($(OASIS_OPENMP), ) + CCBASEFLAGS += +else + CCBASEFLAGS += -fopenmp +endif +# +# INC_DIR : includes all *mod for each library + INC_DIR = -I$(ARCHDIR)/include +# FLIBS : for toys when linking in local Makefile + FLIBS=${NETCDF_LIBRARY} +################### +# +F90FLAGS = $(FCBASEFLAGS) $(INC_DIR) $(CPPDEF) -I$(NETCDF_INCLUDE) +f90FLAGS = $(FCBASEFLAGS) $(INC_DIR) $(CPPDEF) -I$(NETCDF_INCLUDE) +FFLAGS = $(FCBASEFLAGS) $(INC_DIR) $(CPPDEF) -I$(NETCDF_INCLUDE) +fFLAGS = $(FCBASEFLAGS) $(INC_DIR) $(CPPDEF) -I$(NETCDF_INCLUDE) +CCFLAGS = $(CCBASEFLAGS) $(INC_DIR) $(CPPDEF) -I$(NETCDF_INCLUDE) +LDFLAGS = $(FCBASEFLAGS) +F2C_LDFLAGS = $(F2C_LIBS) +# +############################################################################# From afbd83e132932be6414109f35f6b6cc005d0067a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Wed, 20 Aug 2025 09:36:18 +0200 Subject: [PATCH 52/78] Remove netCDF requirements from ENABLE_OASIS case It seems we end up with a duplication which make doesn't like. I don't understand why, but at least it compiles now. Also tidied up a bit the find_library call in FindOASIS.cmake. --- CMakeLists.txt | 5 +---- cmake/FindOASIS.cmake | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d1931671a..a20a636a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -161,13 +161,10 @@ if(ENABLE_OASIS) if(ENABLE_MPI) message(STATUS "Building with OASIS support") find_package(OASIS REQUIRED) - pkg_search_module(NETCDF REQUIRED netcdf-fortran) target_compile_definitions(nextsimlib PUBLIC USE_OASIS) - target_link_directories(nextsimlib PUBLIC ${NETCDF_LIBRARY_DIRS}) target_link_directories(nextsimlib PUBLIC ${OASIS_LIBRARIES}) - target_link_libraries(nextsimlib - PUBLIC oasis.cbind mct mpeu psmile.MPI1 scrip netcdff netcdf) + target_link_libraries(nextsimlib PUBLIC mct mpeu oasis.cbind psmile.MPI1 scrip) target_include_directories(nextsimlib PUBLIC ${OASIS_INCLUDES}) else() message(FATAL_ERROR "Cannot build with OASIS support, because MPI is not enabled" .) diff --git a/cmake/FindOASIS.cmake b/cmake/FindOASIS.cmake index ecf2d3478..1eec77390 100644 --- a/cmake/FindOASIS.cmake +++ b/cmake/FindOASIS.cmake @@ -2,7 +2,7 @@ # # Please pass the -DOASIS_DIR variable to cmake (location of the OASIS libraries). -find_library (OASIS_LIBRARIES NAMES mct mpeu oasis psmile scrip HINTS ${OASIS_DIR}/lib ENV LD_LIBRARY_PATH) +find_library (OASIS_LIBRARIES NAMES mct mpeu oasis.cbind psmile.MPI1 scrip HINTS ${OASIS_DIR}/lib ENV LD_LIBRARY_PATH) get_filename_component (OASIS_LIBRARIES "${OASIS_LIBRARIES}" PATH) From 9733f670c117d65f346938f59f8f7e1bd3cd7793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 21 Aug 2025 10:31:14 +0200 Subject: [PATCH 53/78] Explicitly allow running OpenMPI as root in dev docker image Just adding an export of OMPI_ALLOW_RUN_AS_ROOT{,_CONFIRM}=1 so that you don't have to do it manually each time you want to work in the docker image. --- Dockerfiles/Dockerfile.devenv | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfiles/Dockerfile.devenv b/Dockerfiles/Dockerfile.devenv index b11682df4..cc9c7d721 100644 --- a/Dockerfiles/Dockerfile.devenv +++ b/Dockerfiles/Dockerfile.devenv @@ -50,6 +50,8 @@ COPY --from=builder /opt/views /opt/views # Create entrypoint script RUN { \ echo '#!/bin/sh'; \ + echo 'export OMPI_ALLOW_RUN_AS_ROOT=1'; \ + echo 'export OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1'; \ echo '. /opt/spack-environment/activate.sh'; \ echo 'exec "$@"'; \ } > /entrypoint.sh \ From d4c2688b16c5c2265c73295edd133366cda9bbab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 21 Aug 2025 10:31:51 +0200 Subject: [PATCH 54/78] Also compile pyoasis when compiling OASIS It's useful for testing. --- Dockerfiles/Dockerfile.oasis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfiles/Dockerfile.oasis b/Dockerfiles/Dockerfile.oasis index df9f6b901..9f9165d33 100644 --- a/Dockerfiles/Dockerfile.oasis +++ b/Dockerfiles/Dockerfile.oasis @@ -12,7 +12,7 @@ ENV OASIS_ENV=oasis_docker_spack_gcc COPY make.oasis_docker make.$OASIS_ENV # make! -RUN make -f TopMakefileOasis3 static-libs shared-libs +RUN make -f TopMakefileOasis3 static-libs shared-libs pyoasis # make install ... of sorts RUN cp -a $OASIS_COUPLE/build.$OASIS_ENV/lib/* /usr/local/lib From 7235a794015d7ddbafdcec89ee33d7f6d4ee46af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 21 Aug 2025 10:32:53 +0200 Subject: [PATCH 55/78] Install extra python libraries and modify paths in entrypoint.sh More useful things like LD_LIBRARY_PATH and PYTHONPATH being automtically set. --- Dockerfiles/Dockerfile.oasis | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Dockerfiles/Dockerfile.oasis b/Dockerfiles/Dockerfile.oasis index 9f9165d33..74d57a403 100644 --- a/Dockerfiles/Dockerfile.oasis +++ b/Dockerfiles/Dockerfile.oasis @@ -9,13 +9,22 @@ WORKDIR /oasis3-mct/util/make_dir # Set some ENV's and copy the make file include ENV OASIS_COUPLE=/oasis3-mct ENV OASIS_ENV=oasis_docker_spack_gcc +ENV OASIS_DIR=$OASIS_COUPLE/build.$OASIS_ENV COPY make.oasis_docker make.$OASIS_ENV # make! RUN make -f TopMakefileOasis3 static-libs shared-libs pyoasis -# make install ... of sorts -RUN cp -a $OASIS_COUPLE/build.$OASIS_ENV/lib/* /usr/local/lib -RUN cp -a $OASIS_COUPLE/build.$OASIS_ENV/include/* /usr/local/include +# Install python packages that pyoasis may need +RUN apt-get install -y python3-mpi4py python3-isodate + +# Append the OASIS library directory to LD_LIBRARY_PATH and append it to PYTHONPATH +# We assume the last line of the file is 'exec "$@"' and try to retain that +RUN sed -i~ '$d' /entrypoint.sh +RUN { \ + echo 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH':$OASIS_DIR/lib; \ + echo 'export PYTHONPATH=$PYTHONPATH':$OASIS_DIR/python; \ + echo 'exec "$@"'; \ + } >> /entrypoint.sh WORKDIR / \ No newline at end of file From 3b0dd20845b4608c36e7baf60642af8da9dbc744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 21 Aug 2025 10:56:52 +0200 Subject: [PATCH 56/78] Search explicitly for all the component libraries of OASIS This makes the CMakeLists.txt file look more standard and putts all essential functionality into FindOASIS.cmake. It would also catch the extremely unlikely case when some of the component libraries of OASIS are missing or in a different location from the rest. Also, a bit of reformatting. --- CMakeLists.txt | 3 +-- cmake/FindOASIS.cmake | 22 +++++++++++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a20a636a2..8474e92f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -163,8 +163,7 @@ if(ENABLE_OASIS) find_package(OASIS REQUIRED) target_compile_definitions(nextsimlib PUBLIC USE_OASIS) - target_link_directories(nextsimlib PUBLIC ${OASIS_LIBRARIES}) - target_link_libraries(nextsimlib PUBLIC mct mpeu oasis.cbind psmile.MPI1 scrip) + target_link_libraries(nextsimlib PUBLIC ${OASIS_LIBRARIES}) target_include_directories(nextsimlib PUBLIC ${OASIS_INCLUDES}) else() message(FATAL_ERROR "Cannot build with OASIS support, because MPI is not enabled" .) diff --git a/cmake/FindOASIS.cmake b/cmake/FindOASIS.cmake index 1eec77390..2aa4dad46 100644 --- a/cmake/FindOASIS.cmake +++ b/cmake/FindOASIS.cmake @@ -2,19 +2,27 @@ # # Please pass the -DOASIS_DIR variable to cmake (location of the OASIS libraries). -find_library (OASIS_LIBRARIES NAMES mct mpeu oasis.cbind psmile.MPI1 scrip HINTS ${OASIS_DIR}/lib ENV LD_LIBRARY_PATH) +# Search for all library files individually and merge to a single OASIS_LIBRARIES variable for the parent CMakeLists.txt +find_library(LIB_OASIS NAMES oasis.cbind HINTS ${OASIS_DIR}/lib ENV LD_LIBRARY_PATH) +find_library(LIB_MCT NAMES mct HINTS ${OASIS_DIR}/lib ENV LD_LIBRARY_PATH) +find_library(LIB_MPEU NAMES mpeu HINTS ${OASIS_DIR}/lib ENV LD_LIBRARY_PATH) +find_library(LIB_PSMILE NAMES psmile.MPI1 HINTS ${OASIS_DIR}/lib ENV LD_LIBRARY_PATH) +find_library(LIB_SCRIP NAMES scrip HINTS ${OASIS_DIR}/lib ENV LD_LIBRARY_PATH) -get_filename_component (OASIS_LIBRARIES "${OASIS_LIBRARIES}" PATH) +if (LIB_OASIS AND LIB_MCT AND LIB_MPEU AND LIB_PSMILE AND LIB_SCRIP) + set(OASIS_LIBRARIES ${LIB_OASIS} ${LIB_MCT} ${LIB_MPEU} ${LIB_PSMILE} ${LIB_SCRIP}) +endif () -set (OASIS_DIR "${OASIS_LIBRARIES}/../") +get_filename_component(OASIS_LIB_DIR "${LIB_OASIS}" PATH) + +set(OASIS_DIR "${OASIS_LIB_DIR}/../") cmake_path(NORMAL_PATH OASIS_DIR) -find_path (OASIS_INCLUDES NAMES oasis_c.h HINTS ${OASIS_DIR}/include) +find_path(OASIS_INCLUDES NAMES oasis_c.h HINTS ${OASIS_DIR}/include) # handle the QUIETLY and REQUIRED arguments and set OASIS_FOUND to TRUE if all listed variables are TRUE -include (FindPackageHandleStandardArgs) -find_package_handle_standard_args(OASIS DEFAULT_MSG - OASIS_LIBRARIES OASIS_INCLUDES) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(OASIS DEFAULT_MSG OASIS_DIR OASIS_LIBRARIES OASIS_INCLUDES) # message (STATUS "OASIS_LIBRARIES: ${OASIS_LIBRARIES}") # message (STATUS "OASIS_INCLUDES: ${OASIS_INCLUDES}") From 9697409f554c8289b469e18fa61890ea1826048f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Fri, 22 Aug 2025 08:59:25 +0200 Subject: [PATCH 57/78] Minor cleaning and comments for FindOASIS.cmake Search for either psmile.MP1 or psmile.MPI2, pluss some clarifying comments. --- cmake/FindOASIS.cmake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmake/FindOASIS.cmake b/cmake/FindOASIS.cmake index 2aa4dad46..19be22304 100644 --- a/cmake/FindOASIS.cmake +++ b/cmake/FindOASIS.cmake @@ -6,18 +6,20 @@ find_library(LIB_OASIS NAMES oasis.cbind HINTS ${OASIS_DIR}/lib ENV LD_LIBRARY_PATH) find_library(LIB_MCT NAMES mct HINTS ${OASIS_DIR}/lib ENV LD_LIBRARY_PATH) find_library(LIB_MPEU NAMES mpeu HINTS ${OASIS_DIR}/lib ENV LD_LIBRARY_PATH) -find_library(LIB_PSMILE NAMES psmile.MPI1 HINTS ${OASIS_DIR}/lib ENV LD_LIBRARY_PATH) +find_library(LIB_PSMILE NAMES psmile.MPI1 psmile.MPI2 HINTS ${OASIS_DIR}/lib ENV LD_LIBRARY_PATH) find_library(LIB_SCRIP NAMES scrip HINTS ${OASIS_DIR}/lib ENV LD_LIBRARY_PATH) +# Only continue if all libraries are found if (LIB_OASIS AND LIB_MCT AND LIB_MPEU AND LIB_PSMILE AND LIB_SCRIP) set(OASIS_LIBRARIES ${LIB_OASIS} ${LIB_MCT} ${LIB_MPEU} ${LIB_PSMILE} ${LIB_SCRIP}) endif () +# Set OASIS_DIR (not strictly necessary, but nice to have) get_filename_component(OASIS_LIB_DIR "${LIB_OASIS}" PATH) - set(OASIS_DIR "${OASIS_LIB_DIR}/../") cmake_path(NORMAL_PATH OASIS_DIR) +# The oasis include file should be where oasis.cbind lives find_path(OASIS_INCLUDES NAMES oasis_c.h HINTS ${OASIS_DIR}/include) # handle the QUIETLY and REQUIRED arguments and set OASIS_FOUND to TRUE if all listed variables are TRUE From 3426158fce44b6c25185ab55a2968ff6a55a7818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Mon, 25 Aug 2025 10:57:10 +0200 Subject: [PATCH 58/78] Update OASISCoupledOcean_test.cpp so that it compiles But it still crashes because of a problem with the namcouple file. --- physics/test/OASISCoupledOcean_test.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/physics/test/OASISCoupledOcean_test.cpp b/physics/test/OASISCoupledOcean_test.cpp index 1a1ad8604..67afeaded 100644 --- a/physics/test/OASISCoupledOcean_test.cpp +++ b/physics/test/OASISCoupledOcean_test.cpp @@ -21,7 +21,6 @@ MPI_TEST_CASE("OASIS init put and get", 1) OASIS_CHECK_ERR(oasis_c_get_localcomm(&modelCommunicator)); ModelArray::setDimensions(ModelArray::Type::H, { 1, 1 }); - ModelArray::setDimensions(ModelArray::Type::Z, { 1, 1, 1 }); double sstIn = -1.84; double sssIn = 28.0; @@ -32,11 +31,7 @@ MPI_TEST_CASE("OASIS init put and get", 1) HField cice(ModelArray::Type::H); cice = 0.8; - ModelComponent::getStore().registerArray(Protected::C_ICE, &cice, RO); - - HField emp(ModelArray::Type::H); - emp = 1e-6; - ModelComponent::getStore().registerArray(Protected::EVAP_MINUS_PRECIP, &emp, RO); + ModelComponent::getStore().registerArray(Shared::C_ICE_DG, &cice, RO); HField tauXIO(ModelArray::Type::H); tauXIO = -3e-2; From 8bc61b53e55a490fac51aaf5376754c2f610028b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Mon, 25 Aug 2025 10:58:25 +0200 Subject: [PATCH 59/78] Update CMakeLists.txt for testOASISCoupledOcean It used to run with two MPI processes, but that's not right (even though it should technically work). I also added an "add_test" block. --- physics/test/CMakeLists.txt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/physics/test/CMakeLists.txt b/physics/test/CMakeLists.txt index f0b28bc32..4b77cbb4a 100644 --- a/physics/test/CMakeLists.txt +++ b/physics/test/CMakeLists.txt @@ -47,12 +47,16 @@ if(ENABLE_MPI) ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} 1 testTOPAZOcn_MPI1 -ns) if (ENABLE_OASIS) - add_executable(testOASISCoupledOcean_MPI2 "OASISCoupledOcean_test.cpp" "MainMPI.cpp") - target_include_directories(testOASISCoupledOcean_MPI2 - PRIVATE "${ModulesRoot}/OceanBoundaryModule") - target_compile_definitions(testOASISCoupledOcean_MPI2 - PRIVATE TEST_FILES_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\") - target_link_libraries(testOASISCoupledOcean_MPI2 PRIVATE nextsimlib doctest::doctest) + add_executable(testOASISCoupledOcean_MPI1 "OASISCoupledOcean_test.cpp" "../../core/test/MainMPI.cpp") + target_include_directories(testOASISCoupledOcean_MPI1 PRIVATE "${ModulesRoot}/OceanBoundaryModule") + target_compile_definitions( + testOASISCoupledOcean_MPI1 + PRIVATE USE_MPI TEST_FILES_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\" + ) + target_link_libraries(testOASISCoupledOcean_MPI1 PRIVATE nextsimlib doctest::doctest) + + add_test(NAME testOASISCoupledOcean_MPI1 COMMAND + ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} 1 testOASISCoupledOcean_MPI1 -ns) endif () else() add_executable(testERA5Atm "ERA5Atm_test.cpp") From 8660e3b06b148da72216dcf51f999adff344d800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Mon, 1 Sep 2025 09:26:52 +0200 Subject: [PATCH 60/78] Add FracQsr to exchange fields NEMO sends the fraction of shortwave radiation absorbed in the first ocean layer. This is useful to have in a coupled context (probably for every ocean model), but by including it we also have to manually account for it in the stand-alone context (done now in TOPAZOcean.cpp). --- .../src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp | 5 ++++- .../OceanBoundaryModule/include/OASISCoupledOcean.hpp | 2 ++ physics/src/modules/include/IOceanBoundary.hpp | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 07bd7f3db..3e157d003 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -1,7 +1,6 @@ /*! * @file OASISCoupledOcean.cpp * - * @date 05 Jun 2025 * @author Tim Spain * @author Einar Ólason */ @@ -66,6 +65,9 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSHKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &ssh[0], &kinfo)); + OASIS_CHECK_ERR(oasis_c_get(couplingId.at(FracQSWKey), OASISTime, dimension0, dimension1, + bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &fracQSWAbs[0], &kinfo)); + if (couplingId.find(MLDKey) != couplingId.end()) { OASIS_CHECK_ERR(oasis_c_get(couplingId.at(MLDKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &mld[0], &kinfo)); @@ -152,6 +154,7 @@ void OASISCoupledOcean::configure() UOceanKey = Configured::getConfiguration(UOceanConfigKey, UOceanKeyDefault); VOceanKey = Configured::getConfiguration(VOceanConfigKey, VOceanKeyDefault); SSHKey = Configured::getConfiguration(SSHConfigKey, SSHKeyDefault); + FracQSWKey = Configured::getConfiguration(FracQSWConfigKey, FracQSWKeyDefault); MLDKey = Configured::getConfiguration(MLDConfigKey, MLDKeyDefault); TauXKey = Configured::getConfiguration(TauXConfigKey, TauXKeyDefault); TauYKey = Configured::getConfiguration(TauYConfigKey, TauYKeyDefault); diff --git a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp index ddaedf2ed..b7861d510 100644 --- a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp +++ b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp @@ -29,6 +29,7 @@ static const std::string SSSKeyDefault = "I_SSS"; static const std::string UOceanKeyDefault = "I_Uocn"; static const std::string VOceanKeyDefault = "I_Vocn"; static const std::string SSHKeyDefault = "I_SSH"; +static const std::string FracQSWKeyDefault = "I_FrcQsr"; static const std::string MLDKeyDefault = "I_MLD"; // This one is optional static const std::string TauXKeyDefault = "I_taux"; static const std::string TauYKeyDefault = "I_tauy"; @@ -44,6 +45,7 @@ static const std::string SSSConfigKey = moduleName + ".sea_surface_salinity"; static const std::string UOceanConfigKey = moduleName + ".ocean_u_velocity"; static const std::string VOceanConfigKey = moduleName + ".ocean_v_velocity"; static const std::string SSHConfigKey = moduleName + ".sea_surface_height"; +static const std::string FracQSWConfigKey = moduleName + ".frac_solar_absorbed"; static const std::string MLDConfigKey = moduleName + ".first_ocean_layer_depth"; // This one is optional static const std::string TauXConfigKey = moduleName + ".ice_ocean_stress_x"; static const std::string TauYConfigKey = moduleName + ".ice_ocean_stress_y"; diff --git a/physics/src/modules/include/IOceanBoundary.hpp b/physics/src/modules/include/IOceanBoundary.hpp index 2d4c2d0b4..4047c7c13 100644 --- a/physics/src/modules/include/IOceanBoundary.hpp +++ b/physics/src/modules/include/IOceanBoundary.hpp @@ -32,6 +32,7 @@ class IOceanBoundary : public ModelComponent { , sFlux(ModelArray::Type::H) , qswow(ModelArray::Type::H) , qswBase(ModelArray::Type::H) + , fracQSWAbs(ModelArray::Type::H) , tauX(ModelArray::Type::H) , tauY(ModelArray::Type::H) , cice(getStore()) @@ -137,7 +138,7 @@ class IOceanBoundary : public ModelComponent { void mergeFluxesElement(size_t i, const TimestepTime& tst) { // Heat fluxes - partitioned in solar and non-solar - qswNet[i] = cice[i] * qswBase[i] + (1 - cice[i]) * qswow[i]; + qswNet[i] = fracQSWAbs[i] * (cice[i] * qswBase[i] + (1 - cice[i]) * qswow[i]); qNoSun[i] = cice[i] * qio[i] + (1 - cice[i]) * qow[i] - qswNet[i]; // Mass fluxes - fresh water and salt @@ -176,6 +177,7 @@ class IOceanBoundary : public ModelComponent { HField sFlux; // Net surface ocean salt flux, kg m⁻² HField qswow; // Shortwave flux in open water W m⁻² HField qswBase; // Shortwave flux at the base of the ice W m⁻² + HField fracQSWAbs; // The fraction of solar radiation absorbed in the surface ocean layer HField tauX; // x(east)-ward total ocean stress, Pa HField tauY; // y(north)-ward total ocean stress, Pa From cd4848fc0ba50dfc8ff3b9e83b44eb76fbfae0a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Mon, 1 Sep 2025 09:28:04 +0200 Subject: [PATCH 61/78] Rename the key for sending ice cover to the ocean Our traditional setup expects it to be called "I_sic", so we'll stick to that. Calling it "I_conc" was just a mistake anyway. --- .../modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp index b7861d510..da65b5bcf 100644 --- a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp +++ b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp @@ -38,7 +38,7 @@ static const std::string EMPKeyDefault = "I_fwflux"; static const std::string QNoSunKeyDefault = "I_rsnos"; static const std::string QSWKeyDefault = "I_rsso"; static const std::string SFluxKeyDefault = "I_sfi"; -static const std::string CIceKeyDefault = "I_conc"; +static const std::string CIceKeyDefault = "I_sic"; static const std::string SSTConfigKey = moduleName + ".sea_surface_temperature"; static const std::string SSSConfigKey = moduleName + ".sea_surface_salinity"; From 1199a01b36dcbbcf515433cd70179e01ede2c4a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Mon, 1 Sep 2025 09:33:32 +0200 Subject: [PATCH 62/78] Reformatting to appease linter Split lines that were too long; ./physics/test/CMakeLists.txt ============================= ./physics/test/CMakeLists.txt:50: [C0301] Line too long (109/100) ./physics/test/CMakeLists.txt:51: [C0301] Line too long (107/100) --- physics/test/CMakeLists.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/physics/test/CMakeLists.txt b/physics/test/CMakeLists.txt index 4b77cbb4a..1f83d8786 100644 --- a/physics/test/CMakeLists.txt +++ b/physics/test/CMakeLists.txt @@ -47,11 +47,13 @@ if(ENABLE_MPI) ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} 1 testTOPAZOcn_MPI1 -ns) if (ENABLE_OASIS) - add_executable(testOASISCoupledOcean_MPI1 "OASISCoupledOcean_test.cpp" "../../core/test/MainMPI.cpp") - target_include_directories(testOASISCoupledOcean_MPI1 PRIVATE "${ModulesRoot}/OceanBoundaryModule") + add_executable(testOASISCoupledOcean_MPI1 + "OASISCoupledOcean_test.cpp" "../../core/test/MainMPI.cpp") + target_include_directories(testOASISCoupledOcean_MPI1 + PRIVATE "${ModulesRoot}/OceanBoundaryModule") target_compile_definitions( - testOASISCoupledOcean_MPI1 - PRIVATE USE_MPI TEST_FILES_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\" + testOASISCoupledOcean_MPI1 + PRIVATE USE_MPI TEST_FILES_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\" ) target_link_libraries(testOASISCoupledOcean_MPI1 PRIVATE nextsimlib doctest::doctest) From 93a5ac3e6b7f07cf1c27adfb65535f88c5f758c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Mon, 1 Sep 2025 09:38:58 +0200 Subject: [PATCH 63/78] Missing definition for FracQSWKey I had it on my disk, but didn't manage to commit it last time(!) --- .../modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp index da65b5bcf..9598d21a3 100644 --- a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp +++ b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp @@ -82,8 +82,8 @@ class OASISCoupledOcean : public IOceanBoundary, // A map to relate the strings in the namcouple file to the numbers def_var spits out std::map couplingId; - std::string SSTKey, SSSKey, UOceanKey, VOceanKey, SSHKey, MLDKey, TauXKey, TauYKey, TauModKey, - EMPKey, QNoSunKey, QSWKey, SFluxKey, CIceKey; + std::string SSTKey, SSSKey, UOceanKey, VOceanKey, SSHKey, FracQSWKey, MLDKey, TauXKey, TauYKey, + TauModKey, EMPKey, QNoSunKey, QSWKey, SFluxKey, CIceKey; // A map relating the namcouple strings to ModelArrayRef's std::map fieldTags; From e25f58a406b23d64acbb170695958f2ae2f6ba35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Mon, 1 Sep 2025 10:00:01 +0200 Subject: [PATCH 64/78] Set fracSWAbs to one in TOPAZOcean This too should have happened in a previous commit. --- physics/src/modules/OceanBoundaryModule/TOPAZOcean.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/physics/src/modules/OceanBoundaryModule/TOPAZOcean.cpp b/physics/src/modules/OceanBoundaryModule/TOPAZOcean.cpp index 0502da0ca..7d7f1bcda 100644 --- a/physics/src/modules/OceanBoundaryModule/TOPAZOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/TOPAZOcean.cpp @@ -106,6 +106,9 @@ void TOPAZOcean::setData(const ModelState::DataMap& ms) sstExt.resize(); sssExt.resize(); slabOcean.setData(ms); + + // We assume that all incoming shortwave radiation is absorbed in the mixed layer + fracQSWAbs = 1.; } void TOPAZOcean::updateTf(size_t i, const TimestepTime& tst) From 5a1a591e87102883d341828bba9ee808143122b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Tue, 2 Sep 2025 13:48:25 +0200 Subject: [PATCH 65/78] Be more careful with FracQSW I needed to add FracQSWKey to the cplStringsIn list, and to initialise the fracQSWAbs variable (.resize()). --- physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp | 2 +- physics/src/modules/include/IOceanBoundary.hpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 3e157d003..ed2a164d1 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -167,7 +167,7 @@ void OASISCoupledOcean::configure() firstLayerDepth = Configured::getConfiguration(layerDepthConfigKey, FIRST_LAYER_DEPTH); - cplStringsIn = { SSTKey, SSSKey, UOceanKey, VOceanKey, SSHKey }; + cplStringsIn = { SSTKey, SSSKey, UOceanKey, VOceanKey, SSHKey, FracQSWKey }; if (Configured::getConfiguration(exchangeFirstLayerConfigKey, exchangeFirstLayerDefault)) { cplStringsIn.push_back(MLDKey); } diff --git a/physics/src/modules/include/IOceanBoundary.hpp b/physics/src/modules/include/IOceanBoundary.hpp index 4047c7c13..7c70edfa4 100644 --- a/physics/src/modules/include/IOceanBoundary.hpp +++ b/physics/src/modules/include/IOceanBoundary.hpp @@ -95,6 +95,7 @@ class IOceanBoundary : public ModelComponent { sFlux.resize(); qswow.resize(); qswBase.resize(); + fracQSWAbs.resize(); tauX.resize(); tauY.resize(); From 3bd14b70f1676bc7c82de5728311621c7ca2f1b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Fri, 5 Sep 2025 11:42:57 +0200 Subject: [PATCH 66/78] Send mean component of cice to OASIS We were sending the full DG field to OASIS and NEMO didn't know what to do with that. So now, I only send the mean. This involves a copy, but I'm not sure if I can get around that. --- physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index ed2a164d1..e972c982a 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -122,8 +122,10 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauModKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &tauMod[0], OASIS_No_Restart, &kinfo)); + // Implicitly copy the 0th DG component (the mean) + const HField cice0 = cice; OASIS_CHECK_ERR(oasis_c_put(couplingId.at(CIceKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &cice[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &cice0[0], OASIS_No_Restart, &kinfo)); // Increment the "OASIS" time by the number of seconds in the time step updateOASISTime(tst); From 4c156a09ece9258f3a3401e48355cb57d26a4912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Fri, 5 Sep 2025 11:44:19 +0200 Subject: [PATCH 67/78] Better variable name NoOASISError is more descriptive than OASISError. The error message says OASIS was not compiled in. --- physics/src/include/OASISCoupled.hpp | 2 +- .../modules/OceanBoundaryModule/OASISCoupledOcean.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/physics/src/include/OASISCoupled.hpp b/physics/src/include/OASISCoupled.hpp index a4cdf8191..a34530f85 100644 --- a/physics/src/include/OASISCoupled.hpp +++ b/physics/src/include/OASISCoupled.hpp @@ -29,7 +29,7 @@ class OASISCoupled { // Must be called at the end of the child class' update or updateAfter call. void updateOASISTime(const TimestepTime& tst) { OASISTime += tst.step.seconds(); } #else - const std::string OASISError = std::string(": OASIS support not compiled in.\n"); + const std::string NoOASISError = std::string(": OASIS support not compiled in.\n"); #endif }; diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index e972c982a..fe6fca9de 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -37,7 +37,7 @@ void OASISCoupledOcean::setMetadata(const ModelMetadata& metadata) couplingId[idString] = idNumber; } #else - throw std::runtime_error(std::string(__func__) + ": " + OASISError); + throw std::runtime_error(std::string(__func__) + ": " + NoOASISError); #endif } @@ -65,8 +65,8 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSHKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &ssh[0], &kinfo)); - OASIS_CHECK_ERR(oasis_c_get(couplingId.at(FracQSWKey), OASISTime, dimension0, dimension1, - bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &fracQSWAbs[0], &kinfo)); + OASIS_CHECK_ERR(oasis_c_get(couplingId.at(FracQSWKey), OASISTime, dimension0, dimension1, + bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &fracQSWAbs[0], &kinfo)); if (couplingId.find(MLDKey) != couplingId.end()) { OASIS_CHECK_ERR(oasis_c_get(couplingId.at(MLDKey), OASISTime, dimension0, dimension1, @@ -84,7 +84,7 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) Module::getImplementation().update(tst); #else - throw std::runtime_error(std::string(__func__) + ": " + OASISError); + throw std::runtime_error(std::string(__func__) + ": " + NoOASISError); #endif } @@ -142,7 +142,7 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) std::cout << CIceKey << ": " << cice[0] << std::endl; */ #else - throw std::runtime_error(std::string(__func__) + ": " + OASISError); + throw std::runtime_error(std::string(__func__) + ": " + NoOASISError); #endif } From 7a55edcee4dff3d01832d3ecdfee4941d322e593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Fri, 19 Sep 2025 12:02:28 +0200 Subject: [PATCH 68/78] Add vector rotation in send and receive calls for OASIS Read in the azimuth angle from the restart file and use this to rotate the ocean velocity and ice-ocean stress vectors on receive and send, respectively. Also, clang-format auto changes. --- core/src/ModelArray.cpp | 14 +++++++++++ core/src/include/ModelArray.hpp | 8 ++++++ physics/src/include/OASISCoupled.hpp | 20 +++++++++++++++ .../OceanBoundaryModule/OASISCoupledOcean.cpp | 25 ++++++++++++++++--- .../include/OASISCoupledOcean.hpp | 4 ++- 5 files changed, 66 insertions(+), 5 deletions(-) diff --git a/core/src/ModelArray.cpp b/core/src/ModelArray.cpp index 69661171b..23ff6f3e9 100644 --- a/core/src/ModelArray.cpp +++ b/core/src/ModelArray.cpp @@ -182,6 +182,20 @@ ModelArray ModelArray::sqrt() return sqrted; } +ModelArray ModelArray::sin() +{ + auto sined = ModelArray(type); + sined.m_data.array() = m_data.array().sin(); + return sined; +} + +ModelArray ModelArray::cos() +{ + auto cosed = ModelArray(type); + cosed.m_data.array() = m_data.array().cos(); + return cosed; +} + void ModelArray::setData(double value) { resize(); diff --git a/core/src/include/ModelArray.hpp b/core/src/include/ModelArray.hpp index 5e711c638..431a368c8 100644 --- a/core/src/include/ModelArray.hpp +++ b/core/src/include/ModelArray.hpp @@ -287,6 +287,14 @@ class ModelArray { * @brief Returns the per-element square root of the array */ ModelArray sqrt(); + /*! + * @brief Returns the per-element sin of the array + */ + ModelArray sin(); + /*! + * @brief Returns the per-element cos of the array + */ + ModelArray cos(); using MultiDim = std::vector; diff --git a/physics/src/include/OASISCoupled.hpp b/physics/src/include/OASISCoupled.hpp index a34530f85..0a1c3f4b5 100644 --- a/physics/src/include/OASISCoupled.hpp +++ b/physics/src/include/OASISCoupled.hpp @@ -12,6 +12,9 @@ #include #endif +#include "include/ModelArray.hpp" +#include "include/ModelState.hpp" + namespace Nextsim { class OASISCoupled { @@ -31,6 +34,23 @@ class OASISCoupled { #else const std::string NoOASISError = std::string(": OASIS support not compiled in.\n"); #endif + +protected: + void rotateVectorToGreenland( + const ModelArray& uIn, const ModelArray& vIn, ModelArray& uOut, ModelArray& vOut) const + { + uOut = uIn * cosAngle - vIn * sinAngle; + vOut = uIn * sinAngle + vIn * cosAngle; + }; + + void rotateVectorFromGreenland( + const ModelArray& uIn, const ModelArray& vIn, ModelArray& uOut, ModelArray& vOut) const + { + uOut = uIn * cosAngle + vIn * sinAngle; + vOut = -uIn * sinAngle + vIn * cosAngle; + }; + + ModelArray cosAngle, sinAngle; }; } diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index fe6fca9de..5db9478c6 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -14,6 +14,16 @@ namespace Nextsim { +void OASISCoupledOcean::setData(const ModelState::DataMap& ms) +{ + IOceanBoundary::setData(ms); + + // Precalculate cos and sin of the azimut angle to use in the rotateVector... functions + ModelArray gridAzimuthAngle = ms.at(gridAzimuthName); + cosAngle = gridAzimuthAngle.cos(); + sinAngle = gridAzimuthAngle.sin(); +} + void OASISCoupledOcean::setMetadata(const ModelMetadata& metadata) { #ifdef USE_OASIS @@ -56,11 +66,13 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSSKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sss[0], &kinfo)); + ModelArray uOnGrid; OASIS_CHECK_ERR(oasis_c_get(couplingId.at(UOceanKey), OASISTime, dimension0, dimension1, - bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &u[0], &kinfo)); + bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &uOnGrid[0], &kinfo)); + ModelArray vOnGrid; OASIS_CHECK_ERR(oasis_c_get(couplingId.at(VOceanKey), OASISTime, dimension0, dimension1, - bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &v[0], &kinfo)); + bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &vOnGrid[0], &kinfo)); OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSHKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &ssh[0], &kinfo)); @@ -76,6 +88,7 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) } cpml = Water::rhoOcean * Water::cp * mld; + rotateVectorToGreenland(uOnGrid, vOnGrid, u, v); overElements( std::bind(&OASISCoupledOcean::updateTf, this, std::placeholders::_1, std::placeholders::_2), @@ -97,11 +110,13 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) const int dimension0 = ModelArray::dimensions(ModelArray::Type::H)[0]; const int dimension1 = ModelArray::dimensions(ModelArray::Type::H)[1]; + ModelArray tauXToGreenland; OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauXKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &tauX[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &tauXToGreenland[0], OASIS_No_Restart, &kinfo)); + ModelArray tauYToGreenland; OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauYKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &tauY[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &tauYToGreenland[0], OASIS_No_Restart, &kinfo)); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(EMPKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &fwFlux[0], OASIS_No_Restart, &kinfo)); @@ -127,6 +142,8 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) OASIS_CHECK_ERR(oasis_c_put(couplingId.at(CIceKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &cice0[0], OASIS_No_Restart, &kinfo)); + rotateVectorFromGreenland(tauXToGreenland, tauYToGreenland, tauX, tauY); + // Increment the "OASIS" time by the number of seconds in the time step updateOASISTime(tst); diff --git a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp index 9598d21a3..f3cb97cbf 100644 --- a/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp +++ b/physics/src/modules/OceanBoundaryModule/include/OASISCoupledOcean.hpp @@ -46,7 +46,8 @@ static const std::string UOceanConfigKey = moduleName + ".ocean_u_velocity"; static const std::string VOceanConfigKey = moduleName + ".ocean_v_velocity"; static const std::string SSHConfigKey = moduleName + ".sea_surface_height"; static const std::string FracQSWConfigKey = moduleName + ".frac_solar_absorbed"; -static const std::string MLDConfigKey = moduleName + ".first_ocean_layer_depth"; // This one is optional +static const std::string MLDConfigKey + = moduleName + ".first_ocean_layer_depth"; // This one is optional static const std::string TauXConfigKey = moduleName + ".ice_ocean_stress_x"; static const std::string TauYConfigKey = moduleName + ".ice_ocean_stress_y"; static const std::string TauModConfigKey = moduleName + ".ice_ocean_stress_modulo"; @@ -67,6 +68,7 @@ class OASISCoupledOcean : public IOceanBoundary, std::string getName() const override { return moduleName; } void updateBefore(const TimestepTime& tst) override; void updateAfter(const TimestepTime& tst) override; + void setData(const ModelState::DataMap& ms) override; void setMetadata(const ModelMetadata& metadata) override; void configure() override; From 4d8fdf451d8cd6bb4affc39d143e7d1ecff45a8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Tue, 23 Sep 2025 15:13:52 +0200 Subject: [PATCH 69/78] Rotate the ice-ocean stress vectors before sending Previously, I sent random values and then rotated afterwards. It made no sense but should do now! --- .../OceanBoundaryModule/OASISCoupledOcean.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 5db9478c6..d2b5e491b 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -66,11 +66,11 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSSKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sss[0], &kinfo)); - ModelArray uOnGrid; + HField uOnGrid; OASIS_CHECK_ERR(oasis_c_get(couplingId.at(UOceanKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &uOnGrid[0], &kinfo)); - ModelArray vOnGrid; + HField vOnGrid; OASIS_CHECK_ERR(oasis_c_get(couplingId.at(VOceanKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &vOnGrid[0], &kinfo)); @@ -106,17 +106,19 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) #ifdef USE_OASIS mergeFluxes(tst); + HField tauXOnGrid; + HField tauYOnGrid; + rotateVectorFromGreenland(tauX, tauY, tauXOnGrid, tauYOnGrid); + int kinfo; const int dimension0 = ModelArray::dimensions(ModelArray::Type::H)[0]; const int dimension1 = ModelArray::dimensions(ModelArray::Type::H)[1]; - ModelArray tauXToGreenland; OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauXKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &tauXToGreenland[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &tauXOnGrid[0], OASIS_No_Restart, &kinfo)); - ModelArray tauYToGreenland; OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauYKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &tauYToGreenland[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &tauYOnGrid[0], OASIS_No_Restart, &kinfo)); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(EMPKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &fwFlux[0], OASIS_No_Restart, &kinfo)); @@ -142,8 +144,6 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) OASIS_CHECK_ERR(oasis_c_put(couplingId.at(CIceKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &cice0[0], OASIS_No_Restart, &kinfo)); - rotateVectorFromGreenland(tauXToGreenland, tauYToGreenland, tauX, tauY); - // Increment the "OASIS" time by the number of seconds in the time step updateOASISTime(tst); From e5b72e7740366b0b7f0b43e6dcd7fd93b87d3c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Wed, 24 Sep 2025 10:24:35 +0200 Subject: [PATCH 70/78] Resize ModelArrays before oasis_get This is to prevent memory corruption through a buffer overrun. We just pass a pointer to the first element. If we don't resize, then OASIS will happily write into parts of memory that haven't been reserved by Eigen. --- physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index d2b5e491b..9af5752e4 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -66,11 +66,11 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSSKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sss[0], &kinfo)); - HField uOnGrid; + HField uOnGrid; uOnGrid.resize(); OASIS_CHECK_ERR(oasis_c_get(couplingId.at(UOceanKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &uOnGrid[0], &kinfo)); - HField vOnGrid; + HField vOnGrid; vOnGrid.resize(); OASIS_CHECK_ERR(oasis_c_get(couplingId.at(VOceanKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &vOnGrid[0], &kinfo)); From 9d12d5ab935665e92d86be6b393ab52c4c14fe64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 25 Sep 2025 09:48:31 +0200 Subject: [PATCH 71/78] Switch the rotation functions Murphy strikes again, I guess. --- physics/src/include/OASISCoupled.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/physics/src/include/OASISCoupled.hpp b/physics/src/include/OASISCoupled.hpp index 0a1c3f4b5..5f8bb81f2 100644 --- a/physics/src/include/OASISCoupled.hpp +++ b/physics/src/include/OASISCoupled.hpp @@ -13,7 +13,7 @@ #endif #include "include/ModelArray.hpp" -#include "include/ModelState.hpp" +#include "include/Time.hpp" namespace Nextsim { @@ -36,14 +36,14 @@ class OASISCoupled { #endif protected: - void rotateVectorToGreenland( + void rotateVectorFromGreenland( const ModelArray& uIn, const ModelArray& vIn, ModelArray& uOut, ModelArray& vOut) const { uOut = uIn * cosAngle - vIn * sinAngle; vOut = uIn * sinAngle + vIn * cosAngle; }; - void rotateVectorFromGreenland( + void rotateVectorToGreenland( const ModelArray& uIn, const ModelArray& vIn, ModelArray& uOut, ModelArray& vOut) const { uOut = uIn * cosAngle + vIn * sinAngle; From 3be6952dea944773b73140012617a7435d610e77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 2 Oct 2025 12:34:01 +0200 Subject: [PATCH 72/78] Interpolation to and from U/V points This is quite hackish, but should work. We'll want to do this much more nicely in the future! --- physics/src/include/OASISCoupled.hpp | 64 +++++++++++++++++++ .../OceanBoundaryModule/OASISCoupledOcean.cpp | 11 ++-- 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/physics/src/include/OASISCoupled.hpp b/physics/src/include/OASISCoupled.hpp index 5f8bb81f2..0980c7034 100644 --- a/physics/src/include/OASISCoupled.hpp +++ b/physics/src/include/OASISCoupled.hpp @@ -51,6 +51,70 @@ class OASISCoupled { }; ModelArray cosAngle, sinAngle; + + /*! + * Interpolate fields on U and V points of a C-grid to the centre point of the grid. The U and V + * points should be placed on the right and above the centre point. + * + * @param uIn Input U field on the U-point + * @param vIn Input V field on the V-point + * @param uOut U field on centre point + * @param vOut V field on centre point + */ + void interpCURtoA(const UField& uIn, const VField& vIn, UField& uOut, VField& vOut) const + { + // First the interior + for (size_t i = 1; i < uIn.dimensions()[0]; ++i) { + for (size_t j = 1; j < uIn.dimensions()[1]; ++j) { + uOut(i, j) = 0.5 * (uIn(i, j) + uIn(i - 1, j)); + vOut(i, j) = 0.5 * (vIn(i, j) + vIn(i, j - 1)); + } + } + + // Then the bottom row + for (size_t i = 1; i < uIn.dimensions()[0]; ++i) + uOut(i, 0) = 0.5 * (uIn(i, 0) + uIn(i - 1, 0)); + + // Then the first column + for (size_t j = 1; j < uIn.dimensions()[1]; ++j) + vOut(0, j) = 0.5 * (vIn(0, j) + vIn(0, j - 1)); + + // Then the bottom left corner + uOut(0, 0) = 0.5 * uIn(0, 0); + vOut(0, 0) = 0.5 * vIn(0, 0); + } + + /*! + * Interpolate fields on the centre point of the grid to the U and V points of a C-grid. The U + * and V points should be placed on the right and above the centre point. + * + * @param uIn Input U field on the centre point + * @param vIn Input V field on the centre point + * @param uOut U field on the U-point + * @param vOut V field on the V-point + */ + void interpAtoCUR(const UField& uIn, const VField& vIn, UField& uOut, VField& vOut) const + { + // First the interior + for (size_t i = 1; i < uIn.dimensions()[0] - 1; ++i) { + for (size_t j = 1; j < uIn.dimensions()[1] - 1; ++j) { + uOut(i, j) = 0.5 * (uIn(i, j) + uIn(i + 1, j)); + vOut(i, j) = 0.5 * (vIn(i, j) + vIn(i, j + 1)); + } + } + + // Then the top row + for (size_t i = 1; i < uIn.dimensions()[0]; ++i) + uOut(i, 0) = 0.5 * (uIn(i, 0) + uIn(i - 1, 0)); + + // Then the first column + for (size_t j = 1; j < uIn.dimensions()[1]; ++j) + vOut(0, j) = 0.5 * (vIn(0, j) + vIn(0, j - 1)); + + // Then the bottom left corner + uOut(0, 0) = 0.5 * uIn(0, 0); + vOut(0, 0) = 0.5 * vIn(0, 0); + } }; } diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 9af5752e4..a2a616949 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -88,7 +88,9 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) } cpml = Water::rhoOcean * Water::cp * mld; - rotateVectorToGreenland(uOnGrid, vOnGrid, u, v); + HField uOnAGrid, vOnAGrid; + interpCURtoA(uOnCGrid, vOnCGrid, uOnAGrid, vOnAGrid); + rotateVectorToGreenland(uOnAGrid, vOnAGrid, u, v); overElements( std::bind(&OASISCoupledOcean::updateTf, this, std::placeholders::_1, std::placeholders::_2), @@ -106,9 +108,10 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) #ifdef USE_OASIS mergeFluxes(tst); - HField tauXOnGrid; - HField tauYOnGrid; - rotateVectorFromGreenland(tauX, tauY, tauXOnGrid, tauYOnGrid); + UField tauXOnCGrid, tauXRotated; + VField tauYOnCGrid, tauYRotated; + rotateVectorFromGreenland(tauX, tauY, tauXRotated, tauYRotated); + interpAtoCUR(tauXRotated, tauYRotated, tauXOnCGrid, tauYOnCGrid); int kinfo; const int dimension0 = ModelArray::dimensions(ModelArray::Type::H)[0]; From f27b30bf8ab20904c691339d84be608cf552b621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 2 Oct 2025 13:42:35 +0200 Subject: [PATCH 73/78] Fix stupid bugs introduced in last commit My testing setup is too complicated, so I didn't catch this last time. --- .../OceanBoundaryModule/OASISCoupledOcean.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index a2a616949..ad4340655 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -66,13 +66,13 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSSKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sss[0], &kinfo)); - HField uOnGrid; uOnGrid.resize(); + HField uOnCGrid; uOnCGrid.resize(); OASIS_CHECK_ERR(oasis_c_get(couplingId.at(UOceanKey), OASISTime, dimension0, dimension1, - bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &uOnGrid[0], &kinfo)); + bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &uOnCGrid[0], &kinfo)); - HField vOnGrid; vOnGrid.resize(); + HField vOnCGrid; vOnCGrid.resize(); OASIS_CHECK_ERR(oasis_c_get(couplingId.at(VOceanKey), OASISTime, dimension0, dimension1, - bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &vOnGrid[0], &kinfo)); + bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &vOnCGrid[0], &kinfo)); OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSHKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &ssh[0], &kinfo)); @@ -118,10 +118,10 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) const int dimension1 = ModelArray::dimensions(ModelArray::Type::H)[1]; OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauXKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &tauXOnGrid[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &tauXOnCGrid[0], OASIS_No_Restart, &kinfo)); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauYKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &tauYOnGrid[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &tauYOnCGrid[0], OASIS_No_Restart, &kinfo)); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(EMPKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &fwFlux[0], OASIS_No_Restart, &kinfo)); From 6c3a5b4768a8c6124c12c8917f925542a878212f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Mon, 6 Oct 2025 08:34:55 +0200 Subject: [PATCH 74/78] Minor cleaning clang-format and replace std::bind with lambda --- .../modules/OceanBoundaryModule/OASISCoupledOcean.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index ad4340655..d11652eb8 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -66,11 +66,13 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSSKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sss[0], &kinfo)); - HField uOnCGrid; uOnCGrid.resize(); + HField uOnCGrid; + uOnCGrid.resize(); OASIS_CHECK_ERR(oasis_c_get(couplingId.at(UOceanKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &uOnCGrid[0], &kinfo)); - HField vOnCGrid; vOnCGrid.resize(); + HField vOnCGrid; + vOnCGrid.resize(); OASIS_CHECK_ERR(oasis_c_get(couplingId.at(VOceanKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &vOnCGrid[0], &kinfo)); @@ -92,9 +94,7 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) interpCURtoA(uOnCGrid, vOnCGrid, uOnAGrid, vOnAGrid); rotateVectorToGreenland(uOnAGrid, vOnAGrid, u, v); - overElements( - std::bind(&OASISCoupledOcean::updateTf, this, std::placeholders::_1, std::placeholders::_2), - TimestepTime()); + overElements([this](size_t i, const TimestepTime& tsTime) { this->updateTf(i, tsTime); }, tst); Module::getImplementation().update(tst); From 76f4facbc8aa88e5cc26950fadada2a2cdc2c9eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Tue, 7 Oct 2025 09:34:20 +0200 Subject: [PATCH 75/78] Resize arrays before use This should avoid memory problems. --- physics/src/include/OASISCoupled.hpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/physics/src/include/OASISCoupled.hpp b/physics/src/include/OASISCoupled.hpp index 0980c7034..7a37636ca 100644 --- a/physics/src/include/OASISCoupled.hpp +++ b/physics/src/include/OASISCoupled.hpp @@ -39,6 +39,10 @@ class OASISCoupled { void rotateVectorFromGreenland( const ModelArray& uIn, const ModelArray& vIn, ModelArray& uOut, ModelArray& vOut) const { + // Make sure the outputs have the right size + uOut.resize(); + vOut.resize(); + uOut = uIn * cosAngle - vIn * sinAngle; vOut = uIn * sinAngle + vIn * cosAngle; }; @@ -46,6 +50,10 @@ class OASISCoupled { void rotateVectorToGreenland( const ModelArray& uIn, const ModelArray& vIn, ModelArray& uOut, ModelArray& vOut) const { + // Make sure the outputs have the right size + uOut.resize(); + vOut.resize(); + uOut = uIn * cosAngle + vIn * sinAngle; vOut = -uIn * sinAngle + vIn * cosAngle; }; @@ -63,6 +71,10 @@ class OASISCoupled { */ void interpCURtoA(const UField& uIn, const VField& vIn, UField& uOut, VField& vOut) const { + // Make sure the outputs have the right size + uOut.resize(); + vOut.resize(); + // First the interior for (size_t i = 1; i < uIn.dimensions()[0]; ++i) { for (size_t j = 1; j < uIn.dimensions()[1]; ++j) { @@ -95,6 +107,10 @@ class OASISCoupled { */ void interpAtoCUR(const UField& uIn, const VField& vIn, UField& uOut, VField& vOut) const { + // Make sure the outputs have the right size + uOut.resize(); + vOut.resize(); + // First the interior for (size_t i = 1; i < uIn.dimensions()[0] - 1; ++i) { for (size_t j = 1; j < uIn.dimensions()[1] - 1; ++j) { From dc681bda70d959706f85d03cc40c1143c972a237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Wed, 8 Oct 2025 13:27:02 +0200 Subject: [PATCH 76/78] Use HField for everything I was using ModelArray, UField, VField, and HField a bit interchangebly. This should be ok (but I'm not sure), but it's confusing. So now, it's HField everywhere. --- physics/src/include/OASISCoupled.hpp | 10 +++++----- .../modules/OceanBoundaryModule/OASISCoupledOcean.cpp | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/physics/src/include/OASISCoupled.hpp b/physics/src/include/OASISCoupled.hpp index 7a37636ca..33a1eb158 100644 --- a/physics/src/include/OASISCoupled.hpp +++ b/physics/src/include/OASISCoupled.hpp @@ -37,7 +37,7 @@ class OASISCoupled { protected: void rotateVectorFromGreenland( - const ModelArray& uIn, const ModelArray& vIn, ModelArray& uOut, ModelArray& vOut) const + const HField& uIn, const HField& vIn, HField& uOut, HField& vOut) const { // Make sure the outputs have the right size uOut.resize(); @@ -48,7 +48,7 @@ class OASISCoupled { }; void rotateVectorToGreenland( - const ModelArray& uIn, const ModelArray& vIn, ModelArray& uOut, ModelArray& vOut) const + const HField& uIn, const HField& vIn, HField& uOut, HField& vOut) const { // Make sure the outputs have the right size uOut.resize(); @@ -58,7 +58,7 @@ class OASISCoupled { vOut = -uIn * sinAngle + vIn * cosAngle; }; - ModelArray cosAngle, sinAngle; + HField cosAngle, sinAngle; /*! * Interpolate fields on U and V points of a C-grid to the centre point of the grid. The U and V @@ -69,7 +69,7 @@ class OASISCoupled { * @param uOut U field on centre point * @param vOut V field on centre point */ - void interpCURtoA(const UField& uIn, const VField& vIn, UField& uOut, VField& vOut) const + void interpCURtoA(const HField& uIn, const HField& vIn, HField& uOut, HField& vOut) const { // Make sure the outputs have the right size uOut.resize(); @@ -105,7 +105,7 @@ class OASISCoupled { * @param uOut U field on the U-point * @param vOut V field on the V-point */ - void interpAtoCUR(const UField& uIn, const VField& vIn, UField& uOut, VField& vOut) const + void interpAtoCUR(const HField& uIn, const HField& vIn, HField& uOut, HField& vOut) const { // Make sure the outputs have the right size uOut.resize(); diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index d11652eb8..65ffe1f2f 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -19,7 +19,7 @@ void OASISCoupledOcean::setData(const ModelState::DataMap& ms) IOceanBoundary::setData(ms); // Precalculate cos and sin of the azimut angle to use in the rotateVector... functions - ModelArray gridAzimuthAngle = ms.at(gridAzimuthName); + HField gridAzimuthAngle = ms.at(gridAzimuthName); cosAngle = gridAzimuthAngle.cos(); sinAngle = gridAzimuthAngle.sin(); } @@ -108,8 +108,8 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) #ifdef USE_OASIS mergeFluxes(tst); - UField tauXOnCGrid, tauXRotated; - VField tauYOnCGrid, tauYRotated; + HField tauXOnCGrid, tauXRotated; + HField tauYOnCGrid, tauYRotated; rotateVectorFromGreenland(tauX, tauY, tauXRotated, tauYRotated); interpAtoCUR(tauXRotated, tauYRotated, tauXOnCGrid, tauYOnCGrid); From ce7e4d2f328fd89df619212929ebcc641057ba7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Thu, 9 Oct 2025 10:04:37 +0200 Subject: [PATCH 77/78] Debugging output for the coupling code Added Logged::debug messages to check if keys and ids make sense. --- .../OceanBoundaryModule/OASISCoupledOcean.cpp | 45 ++++++++++++++----- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 65ffe1f2f..2b137caa8 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -38,6 +38,8 @@ void OASISCoupledOcean::setMetadata(const ModelMetadata& metadata) OASIS_CHECK_ERR(oasis_c_def_var(&idNumber, idString.c_str(), metadata.OASISPartitionId, bundleSize, OASIS_IN, OASIS_DOUBLE)); couplingId[idString] = idNumber; + + Logged::debug("OASISCoupledOcean: " + idString + " has id " + std::to_string(idNumber)); } for (std::string idString : cplStringsOut) { @@ -45,6 +47,8 @@ void OASISCoupledOcean::setMetadata(const ModelMetadata& metadata) OASIS_CHECK_ERR(oasis_c_def_var(&idNumber, idString.c_str(), metadata.OASISPartitionId, bundleSize, OASIS_OUT, OASIS_DOUBLE)); couplingId[idString] = idNumber; + + Logged::debug("OASISCoupledOcean: " + idString + " has id " + std::to_string(idNumber)); } #else throw std::runtime_error(std::string(__func__) + ": " + NoOASISError); @@ -60,29 +64,43 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) const int dimension0 = ModelArray::dimensions(ModelArray::Type::H)[0]; const int dimension1 = ModelArray::dimensions(ModelArray::Type::H)[1]; + Logged::debug("OASISCoupledOcean: Receiving " + SSTKey + " with id " + + std::to_string(couplingId.at(SSTKey))); OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSTKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sst[0], &kinfo)); + Logged::debug("OASISCoupledOcean: Receiving " + SSSKey + " with id " + + std::to_string(couplingId.at(SSSKey))); OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSSKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sss[0], &kinfo)); HField uOnCGrid; uOnCGrid.resize(); + Logged::debug("OASISCoupledOcean: Receiving " + UOceanKey + " with id " + + std::to_string(couplingId.at(UOceanKey))); OASIS_CHECK_ERR(oasis_c_get(couplingId.at(UOceanKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &uOnCGrid[0], &kinfo)); HField vOnCGrid; vOnCGrid.resize(); + Logged::debug("OASISCoupledOcean: Receiving " + VOceanKey + " with id " + + std::to_string(couplingId.at(VOceanKey))); OASIS_CHECK_ERR(oasis_c_get(couplingId.at(VOceanKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &vOnCGrid[0], &kinfo)); + Logged::debug("OASISCoupledOcean: Receiving " + SSHKey + " with id " + + std::to_string(couplingId.at(SSHKey))); OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSHKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &ssh[0], &kinfo)); + Logged::debug("OASISCoupledOcean: Receiving " + FracQSWKey + " with id " + + std::to_string(couplingId.at(FracQSWKey))); OASIS_CHECK_ERR(oasis_c_get(couplingId.at(FracQSWKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &fracQSWAbs[0], &kinfo)); if (couplingId.find(MLDKey) != couplingId.end()) { + Logged::debug("OASISCoupledOcean: Receiving " + MLDKey + " with id " + + std::to_string(couplingId.at(MLDKey))); OASIS_CHECK_ERR(oasis_c_get(couplingId.at(MLDKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &mld[0], &kinfo)); } else { @@ -117,50 +135,55 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) const int dimension0 = ModelArray::dimensions(ModelArray::Type::H)[0]; const int dimension1 = ModelArray::dimensions(ModelArray::Type::H)[1]; + Logged::debug("OASISCoupledOcean: Sending " + TauXKey + " with id " + + std::to_string(couplingId.at(TauXKey))); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauXKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &tauXOnCGrid[0], OASIS_No_Restart, &kinfo)); + Logged::debug("OASISCoupledOcean: Sending " + TauYKey + " with id " + + std::to_string(couplingId.at(TauYKey))); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauYKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &tauYOnCGrid[0], OASIS_No_Restart, &kinfo)); + Logged::debug("OASISCoupledOcean: Sending " + EMPKey + " with id " + + std::to_string(couplingId.at(EMPKey))); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(EMPKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &fwFlux[0], OASIS_No_Restart, &kinfo)); const HField qswDown = -qswNet; + Logged::debug("OASISCoupledOcean: Sending " + QSWKey + " with id " + + std::to_string(couplingId.at(QSWKey))); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(QSWKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &qswDown[0], OASIS_No_Restart, &kinfo)); const HField qNoSunDown = -qNoSun; + Logged::debug("OASISCoupledOcean: Sending " + QNoSunKey + " with id " + + std::to_string(couplingId.at(QNoSunKey))); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(QNoSunKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &qNoSunDown[0], OASIS_No_Restart, &kinfo)); + Logged::debug("OASISCoupledOcean: Sending " + SFluxKey + " with id " + + std::to_string(couplingId.at(SFluxKey))); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(SFluxKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &sFlux[0], OASIS_No_Restart, &kinfo)); // NEMO wants this field, even if it can be deduced from tauX and tauY const HField tauMod = (tauX * tauX + tauY * tauY).sqrt(); + Logged::debug("OASISCoupledOcean: Sending " + TauModKey + " with id " + + std::to_string(couplingId.at(TauModKey))); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauModKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &tauMod[0], OASIS_No_Restart, &kinfo)); // Implicitly copy the 0th DG component (the mean) const HField cice0 = cice; + Logged::debug("OASISCoupledOcean: Sending " + CIceKey + " with id " + + std::to_string(couplingId.at(CIceKey))); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(CIceKey), OASISTime, dimension0, dimension1, 1, OASIS_DOUBLE, OASIS_COL_MAJOR, &cice0[0], OASIS_No_Restart, &kinfo)); // Increment the "OASIS" time by the number of seconds in the time step updateOASISTime(tst); - /* - std::cout << "The OASIS output file should contain:\n"; - std::cout << TauXKey << ": " << tauX[0] << std::endl; - std::cout << TauYKey << ": " << tauY[0] << std::endl; - std::cout << TauModKey << ": " << tauMod[0] << std::endl; - std::cout << EMPKey << ": " << fwFlux[0] << std::endl; - std::cout << QSWKey << ": " << qswDown[0] << std::endl; - std::cout << QNoSunKey << ": " << qNoSunDown[0] << std::endl; - std::cout << SFluxKey << ": " << sFlux[0] << std::endl; - std::cout << CIceKey << ": " << cice[0] << std::endl; - */ #else throw std::runtime_error(std::string(__func__) + ": " + NoOASISError); #endif From d5d8650b172e0d96d8f130270db80332330e8a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20=C3=96rn=20=C3=93lason?= Date: Wed, 15 Oct 2025 08:43:43 +0200 Subject: [PATCH 78/78] Remove A->C and C->A grid interpolations We'll ask OASIS to do it for us - at least until we have access to the underlying CG fields. --- physics/src/include/OASISCoupled.hpp | 72 ------------------- .../OceanBoundaryModule/OASISCoupledOcean.cpp | 24 +++---- 2 files changed, 10 insertions(+), 86 deletions(-) diff --git a/physics/src/include/OASISCoupled.hpp b/physics/src/include/OASISCoupled.hpp index 33a1eb158..96876500e 100644 --- a/physics/src/include/OASISCoupled.hpp +++ b/physics/src/include/OASISCoupled.hpp @@ -59,78 +59,6 @@ class OASISCoupled { }; HField cosAngle, sinAngle; - - /*! - * Interpolate fields on U and V points of a C-grid to the centre point of the grid. The U and V - * points should be placed on the right and above the centre point. - * - * @param uIn Input U field on the U-point - * @param vIn Input V field on the V-point - * @param uOut U field on centre point - * @param vOut V field on centre point - */ - void interpCURtoA(const HField& uIn, const HField& vIn, HField& uOut, HField& vOut) const - { - // Make sure the outputs have the right size - uOut.resize(); - vOut.resize(); - - // First the interior - for (size_t i = 1; i < uIn.dimensions()[0]; ++i) { - for (size_t j = 1; j < uIn.dimensions()[1]; ++j) { - uOut(i, j) = 0.5 * (uIn(i, j) + uIn(i - 1, j)); - vOut(i, j) = 0.5 * (vIn(i, j) + vIn(i, j - 1)); - } - } - - // Then the bottom row - for (size_t i = 1; i < uIn.dimensions()[0]; ++i) - uOut(i, 0) = 0.5 * (uIn(i, 0) + uIn(i - 1, 0)); - - // Then the first column - for (size_t j = 1; j < uIn.dimensions()[1]; ++j) - vOut(0, j) = 0.5 * (vIn(0, j) + vIn(0, j - 1)); - - // Then the bottom left corner - uOut(0, 0) = 0.5 * uIn(0, 0); - vOut(0, 0) = 0.5 * vIn(0, 0); - } - - /*! - * Interpolate fields on the centre point of the grid to the U and V points of a C-grid. The U - * and V points should be placed on the right and above the centre point. - * - * @param uIn Input U field on the centre point - * @param vIn Input V field on the centre point - * @param uOut U field on the U-point - * @param vOut V field on the V-point - */ - void interpAtoCUR(const HField& uIn, const HField& vIn, HField& uOut, HField& vOut) const - { - // Make sure the outputs have the right size - uOut.resize(); - vOut.resize(); - - // First the interior - for (size_t i = 1; i < uIn.dimensions()[0] - 1; ++i) { - for (size_t j = 1; j < uIn.dimensions()[1] - 1; ++j) { - uOut(i, j) = 0.5 * (uIn(i, j) + uIn(i + 1, j)); - vOut(i, j) = 0.5 * (vIn(i, j) + vIn(i, j + 1)); - } - } - - // Then the top row - for (size_t i = 1; i < uIn.dimensions()[0]; ++i) - uOut(i, 0) = 0.5 * (uIn(i, 0) + uIn(i - 1, 0)); - - // Then the first column - for (size_t j = 1; j < uIn.dimensions()[1]; ++j) - vOut(0, j) = 0.5 * (vIn(0, j) + vIn(0, j - 1)); - - // Then the bottom left corner - uOut(0, 0) = 0.5 * uIn(0, 0); - vOut(0, 0) = 0.5 * vIn(0, 0); - } }; } diff --git a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp index 2b137caa8..ba51cd329 100644 --- a/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/OASISCoupledOcean.cpp @@ -74,19 +74,19 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) OASIS_CHECK_ERR(oasis_c_get(couplingId.at(SSSKey), OASISTime, dimension0, dimension1, bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &sss[0], &kinfo)); - HField uOnCGrid; - uOnCGrid.resize(); + HField uOnGrid; + uOnGrid.resize(); Logged::debug("OASISCoupledOcean: Receiving " + UOceanKey + " with id " + std::to_string(couplingId.at(UOceanKey))); OASIS_CHECK_ERR(oasis_c_get(couplingId.at(UOceanKey), OASISTime, dimension0, dimension1, - bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &uOnCGrid[0], &kinfo)); + bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &uOnGrid[0], &kinfo)); - HField vOnCGrid; - vOnCGrid.resize(); + HField vOnGrid; + vOnGrid.resize(); Logged::debug("OASISCoupledOcean: Receiving " + VOceanKey + " with id " + std::to_string(couplingId.at(VOceanKey))); OASIS_CHECK_ERR(oasis_c_get(couplingId.at(VOceanKey), OASISTime, dimension0, dimension1, - bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &vOnCGrid[0], &kinfo)); + bundleSize, OASIS_DOUBLE, OASIS_COL_MAJOR, &vOnGrid[0], &kinfo)); Logged::debug("OASISCoupledOcean: Receiving " + SSHKey + " with id " + std::to_string(couplingId.at(SSHKey))); @@ -108,9 +108,7 @@ void OASISCoupledOcean::updateBefore(const TimestepTime& tst) } cpml = Water::rhoOcean * Water::cp * mld; - HField uOnAGrid, vOnAGrid; - interpCURtoA(uOnCGrid, vOnCGrid, uOnAGrid, vOnAGrid); - rotateVectorToGreenland(uOnAGrid, vOnAGrid, u, v); + rotateVectorToGreenland(uOnGrid, vOnGrid, u, v); overElements([this](size_t i, const TimestepTime& tsTime) { this->updateTf(i, tsTime); }, tst); @@ -126,10 +124,8 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) #ifdef USE_OASIS mergeFluxes(tst); - HField tauXOnCGrid, tauXRotated; - HField tauYOnCGrid, tauYRotated; + HField tauXRotated, tauYRotated; rotateVectorFromGreenland(tauX, tauY, tauXRotated, tauYRotated); - interpAtoCUR(tauXRotated, tauYRotated, tauXOnCGrid, tauYOnCGrid); int kinfo; const int dimension0 = ModelArray::dimensions(ModelArray::Type::H)[0]; @@ -138,12 +134,12 @@ void OASISCoupledOcean::updateAfter(const TimestepTime& tst) Logged::debug("OASISCoupledOcean: Sending " + TauXKey + " with id " + std::to_string(couplingId.at(TauXKey))); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauXKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &tauXOnCGrid[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &tauXRotated[0], OASIS_No_Restart, &kinfo)); Logged::debug("OASISCoupledOcean: Sending " + TauYKey + " with id " + std::to_string(couplingId.at(TauYKey))); OASIS_CHECK_ERR(oasis_c_put(couplingId.at(TauYKey), OASISTime, dimension0, dimension1, 1, - OASIS_DOUBLE, OASIS_COL_MAJOR, &tauYOnCGrid[0], OASIS_No_Restart, &kinfo)); + OASIS_DOUBLE, OASIS_COL_MAJOR, &tauYRotated[0], OASIS_No_Restart, &kinfo)); Logged::debug("OASISCoupledOcean: Sending " + EMPKey + " with id " + std::to_string(couplingId.at(EMPKey)));