diff --git a/components/omega/configs/Default.yml b/components/omega/configs/Default.yml index 9db42e178466..90f59ddccddc 100644 --- a/components/omega/configs/Default.yml +++ b/components/omega/configs/Default.yml @@ -125,6 +125,19 @@ Omega: Contents: - State - Base + # Forcing is the external forcing in standalone mode. + # It contains the surface stress, surface fluxes + # and the surface restoring target files (if appl.). + Forcing: + UsePointerFile: false + Filename: forcing.nc + Mode: read + Precision: double + Freq: 1 + FreqUnits: OnStartup + UseStartEnd: false + Contents: + - Forcing # Restarts are used to initialize for all job submissions after the very # first startup job. We use UseStartEnd with a start time just after the # simulation start time so that omega does not attempt to use a restart diff --git a/components/omega/src/ocn/Forcing.cpp b/components/omega/src/ocn/Forcing.cpp index 5d106d3ad754..8dfd536d9f5a 100644 --- a/components/omega/src/ocn/Forcing.cpp +++ b/components/omega/src/ocn/Forcing.cpp @@ -11,6 +11,7 @@ #include "Forcing.h" #include "Field.h" +#include "IOStream.h" #include "Logging.h" #include "Pacer.h" @@ -58,6 +59,7 @@ Forcing *Forcing::create(const std::string &Name, const HorzMesh *Mesh, } // Initialize the default forcing instance and read configuration. +// Reads the forcing fields from streams at startup (for now). void Forcing::init() { if (DefaultForcing != nullptr) { return; @@ -78,6 +80,10 @@ void Forcing::init() { Config *OmegaConfig = Config::getOmegaConfig(); DefaultForcing->readConfigOptions(OmegaConfig); + // for now, forcing fields are read at start-up only. + // to be extended to include switch from standalone to coupled. + // to be moved to a Forcing->prepareForStep(SimTime) method later. + DefaultForcing->readStreamIntoArrays(); } // Return the default forcing instance. @@ -159,4 +165,32 @@ I4 Forcing::exchangeHalo() const { return Err; } +// Read forcing fields from input stream. +// To be extended with time indexing later. +void Forcing::readStreamIntoArrays() { + Error Err; + + Real FillValueReal = -999._Real; + deepCopy(SfcStressForcing.ZonalStressCell, FillValueReal); + deepCopy(SfcStressForcing.MeridStressCell, FillValueReal); + + std::string StreamName = "Forcing"; + + // Attempt to read stream; if unavailable, log and fall back to zero forcing. + Err = IOStream::read(StreamName); + if (Err.isFail()) { + LOG_INFO("Forcing: Error while reading {} stream, using zero forcing", + StreamName); + deepCopy(SfcStressForcing.ZonalStressCell, 0._Real); + deepCopy(SfcStressForcing.MeridStressCell, 0._Real); + } + + I4 HaloErr = exchangeHalo(); + if (HaloErr != 0) { + ABORT_ERROR("Forcing: Error exchanging halo for startup forcing fields"); + } + + computeAll(); +} + } // namespace OMEGA diff --git a/components/omega/src/ocn/Forcing.h b/components/omega/src/ocn/Forcing.h index 5689e0b17c02..5fdae7e550b8 100644 --- a/components/omega/src/ocn/Forcing.h +++ b/components/omega/src/ocn/Forcing.h @@ -66,6 +66,9 @@ class Forcing { /// Unregister surface stress fields from IO streams void unregisterFields() const; + /// Read forcing fields from input stream at startup + void readStreamIntoArrays(); + /// Compute all forcing variables void computeAll() const; diff --git a/components/omega/src/ocn/OceanRun.cpp b/components/omega/src/ocn/OceanRun.cpp index 6a8bb160f112..25d215045270 100644 --- a/components/omega/src/ocn/OceanRun.cpp +++ b/components/omega/src/ocn/OceanRun.cpp @@ -44,8 +44,8 @@ int ocnRun(TimeInstant &CurrTime ///< [inout] current sim time // track step count ++IStep; - // call forcing routines, anything needed pre-timestep - DefForcing->computeAll(); + // placeholder: call needed pre-timestep compute here + // (e.g. forcing routine) // do forward time step // first call to doStep can sometimes take very long diff --git a/components/omega/test/infra/IOStreamTest.cpp b/components/omega/test/infra/IOStreamTest.cpp index 1678a08470f6..07e3a834f09e 100644 --- a/components/omega/test/infra/IOStreamTest.cpp +++ b/components/omega/test/infra/IOStreamTest.cpp @@ -114,6 +114,10 @@ void initIOStreamTest(Clock *&ModelClock // Model clock // Initialize Tracers Tracers::init(); + // IOStream::validateAll() depends on Forcing::init() so Forcing fields + // are registered before stream validation. + Forcing::init(); + // Initialize Aux State AuxiliaryState::init(); diff --git a/components/omega/test/ocn/ForcingTest.cpp b/components/omega/test/ocn/ForcingTest.cpp index 13e0bdc54388..20217e1c1db1 100644 --- a/components/omega/test/ocn/ForcingTest.cpp +++ b/components/omega/test/ocn/ForcingTest.cpp @@ -62,13 +62,15 @@ struct TestSetupSphere { }; #ifdef FORCING_TEST_PLANE -constexpr Geometry Geom = Geometry::Planar; -constexpr char DefaultMeshFile[] = "OmegaPlanarMesh.nc"; -using TestSetup = TestSetupPlane; +constexpr Geometry Geom = Geometry::Planar; +constexpr char DefaultMeshFile[] = "OmegaPlanarMesh.nc"; +constexpr char DefaultForcingFile[] = "forcingPlanar.nc"; +using TestSetup = TestSetupPlane; #else -constexpr Geometry Geom = Geometry::Spherical; -constexpr char DefaultMeshFile[] = "OmegaSphereMesh.nc"; -using TestSetup = TestSetupSphere; +constexpr Geometry Geom = Geometry::Spherical; +constexpr char DefaultMeshFile[] = "OmegaSphereMesh.nc"; +constexpr char DefaultForcingFile[] = "forcingSphere.nc"; +using TestSetup = TestSetupSphere; #endif int testSfcStressForcingVars(Real RTol) { @@ -144,6 +146,10 @@ int initForcingTest(const std::string &MeshFile) { Field::init(ModelClock); IOStream::init(ModelClock); + // Select the case-specific forcing file before Forcing::init() performs + // startup stream reads. + IOStream::changeFilename("Forcing", DefaultForcingFile); + Err = Halo::init(); if (Err != 0) { ABORT_ERROR("ForcingTest: error initializing default halo"); diff --git a/components/omega/test/ocn/StateTest.cpp b/components/omega/test/ocn/StateTest.cpp index 252056ef2074..dd7359ecbc17 100644 --- a/components/omega/test/ocn/StateTest.cpp +++ b/components/omega/test/ocn/StateTest.cpp @@ -15,6 +15,7 @@ #include "Dimension.h" #include "Error.h" #include "Field.h" +#include "Forcing.h" #include "Halo.h" #include "HorzMesh.h" #include "IO.h" @@ -89,6 +90,10 @@ void initStateTest() { // Initialize tracers Tracers::init(); + // IOStream::validateAll() depends on Forcing::init() so Forcing fields + // are registered before stream validation. + Forcing::init(); + // Initialize Aux State variables AuxiliaryState::init(); @@ -406,6 +411,7 @@ int main(int argc, char *argv[]) { // Finalize Omega objects OceanState::clear(); Tracers::clear(); + Forcing::clear(); AuxiliaryState::clear(); PressureGrad::clear(); Tendencies::clear();