Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Xios handler as a singleton #763

Open
wants to merge 7 commits into
base: issue756_xios-calendar-increment
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/test_suite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]

jobs:

Expand Down
8 changes: 6 additions & 2 deletions core/src/ModelMetadata.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*!
* @file ModelMetadata.cpp
*
* @date 10 Dec 2024
* @date 02 Jan 2025
* @author Tim Spain <timothy.spain@nersc.no>
*/

Expand All @@ -10,6 +10,9 @@
#include "include/IStructure.hpp"
#include "include/NextsimModule.hpp"
#include "include/gridNames.hpp"
#ifdef USE_XIOS
#include "include/Xios.hpp"
#endif

#ifdef USE_MPI
#include <ncDim.h>
Expand Down Expand Up @@ -108,10 +111,10 @@ void ModelMetadata::setTime(const TimePoint& time)
{
m_time = time;
#ifdef USE_XIOS
Xios* xiosHandler = Xios::getInstance();
if (!xiosHandler->isInitialized()) {
throw std::runtime_error("ModelMetadata: Xios handler has not been initialized");
}
xiosHandler->setCalendarStep(0);
xiosHandler->setCalendarStart(time);
#endif
}
Expand All @@ -120,6 +123,7 @@ void ModelMetadata::incrementTime(const Duration& step)
{
m_time += step;
#ifdef USE_XIOS
Xios* xiosHandler = Xios::getInstance();
if (!xiosHandler->isInitialized()) {
throw std::runtime_error("ModelMetadata: Xios handler has not been initialized");
}
Expand Down
15 changes: 13 additions & 2 deletions core/src/Xios.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @file Xios.cpp
* @author Tom Meltzer <tdm39@cam.ac.uk>
* @author Joe Wallwork <jw2423@cam.ac.uk>
* @date 10 Dec 2024
* @date 02 Jan 2025
* @brief XIOS interface implementation
* @details
*
Expand Down Expand Up @@ -62,7 +62,14 @@ Xios::Xios(const std::string dt, const std::string contextid, const std::string
}

//! Destructor
Xios::~Xios() { finalize(); }
Xios::~Xios()
{
finalize();
if (instancePtr != nullptr) {
delete instancePtr;
instancePtr = nullptr;
}
}

//! Close XIOS context definition once xml config has been read and calendar settings updated
void Xios::close_context_definition()
Expand Down Expand Up @@ -1648,6 +1655,10 @@ void Xios::read(const std::string fieldId, ModelArray& modelarray)
throw std::invalid_argument("Only ModelArrays of dimension 2, 3, or 4 are supported");
}
}

// Create the singleton instance
Xios* Xios::instancePtr = nullptr;

}

#endif
18 changes: 1 addition & 17 deletions core/src/include/ModelMetadata.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*!
* @file ModelMetadata.hpp
*
* @date 09 Dec 2024
* @date 02 Jan 2025
* @author Tim Spain <timothy.spain@nersc.no>
*/

Expand All @@ -12,10 +12,6 @@
#include "include/ModelArray.hpp"
#include "include/ModelState.hpp"
#include "include/Time.hpp"
#ifdef USE_XIOS
#include "include/Xios.hpp"
#endif

#include <string>

#ifdef USE_MPI
Expand Down Expand Up @@ -102,15 +98,6 @@ class ModelMetadata {
int localCornerX, localCornerY, localExtentX, localExtentY, globalExtentX, globalExtentY;
#endif

#ifdef USE_XIOS
/*
* @brief Set pointer to Xios handler instance.
*
* @param xiosPtr Pointer to set
*/
inline void setXiosHandler(Xios* xiosPtr) { xiosHandler = xiosPtr; }
#endif

private:
TimePoint m_time;
ConfigMap m_config;
Expand All @@ -129,9 +116,6 @@ class ModelMetadata {
#ifdef USE_MPI
const std::string bboxName = "bounding_boxes";
#endif
#ifdef USE_XIOS
Xios* xiosHandler = nullptr;
#endif
};

} /* namespace Nextsim */
Expand Down
29 changes: 27 additions & 2 deletions core/src/include/Xios.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @file Xios.hpp
* @author Tom Meltzer <tdm39@cam.ac.uk>
* @author Joe Wallwork <jw2423@cam.ac.uk>
* @date 10 Dec 2024
* @date 02 Jan 2025
* @brief XIOS interface header
* @details
*
Expand Down Expand Up @@ -30,12 +30,37 @@ namespace Nextsim {
void enableXios();

class Xios : public Configured<Xios> {
public:
private:
static Xios* instancePtr;

// Private constructor
Xios(const std::string dt = "P0-0T01:00:00", const std::string contextid = "nextSIM-DG",
const std::string starttime = "1970-01-01T00:00:00Z",
const std::string calendartype = "Gregorian");

public:
Xios(const Xios& xiosHandler) = delete;
~Xios();

/*
* Define Xios handler Singleton
*
* NOTE: The arguments will only be used the first time this is called.
*/
inline static Xios* getInstance(const std::string dt = "P0-0T01:00:00",
const std::string contextId = "nextSIM-DG",
const std::string startTime = "1970-01-01T00:00:00Z",
const std::string calendarType = "Gregorian")
{
if (isNull()) {
instancePtr = new Xios(dt, contextId, startTime, calendarType);
}
return instancePtr;
};

/* Utility for checking if the singleton has been created */
inline static bool isNull() { return (instancePtr == nullptr); }

/* Initialization and finalization */
void close_context_definition();
void context_finalize();
Expand Down
16 changes: 12 additions & 4 deletions core/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,21 @@ if(ENABLE_MPI)
)
target_link_libraries(testXiosFile_MPI2 PRIVATE nextsimlib doctest::doctest)

add_executable(testXiosReadWrite_MPI2 "XiosReadWrite_test.cpp" "MainMPI.cpp")
target_compile_definitions(testXiosReadWrite_MPI2 PRIVATE USE_XIOS)
add_executable(testXiosRead_MPI2 "XiosRead_test.cpp" "MainMPI.cpp")
target_compile_definitions(testXiosRead_MPI2 PRIVATE USE_XIOS)
target_include_directories(
testXiosReadWrite_MPI2
testXiosRead_MPI2
PRIVATE "${MODEL_INCLUDE_DIR}" "${XIOS_INCLUDE_LIST}" "${ModulesRoot}/StructureModule"
)
target_link_libraries(testXiosReadWrite_MPI2 PRIVATE nextsimlib doctest::doctest)
target_link_libraries(testXiosRead_MPI2 PRIVATE nextsimlib doctest::doctest)

add_executable(testXiosWrite_MPI2 "XiosWrite_test.cpp" "MainMPI.cpp")
target_compile_definitions(testXiosWrite_MPI2 PRIVATE USE_XIOS)
target_include_directories(
testXiosWrite_MPI2
PRIVATE "${MODEL_INCLUDE_DIR}" "${XIOS_INCLUDE_LIST}" "${ModulesRoot}/StructureModule"
)
target_link_libraries(testXiosWrite_MPI2 PRIVATE nextsimlib doctest::doctest)
else()
add_executable(testRectGrid_MPI3 "RectGrid_test.cpp" "MainMPI.cpp")
target_compile_definitions(
Expand Down
12 changes: 7 additions & 5 deletions core/test/ConfigOutput_test.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*!
* @file ConfigOutput_test.cpp
*
* @date 11 Dec 2024
* @date 02 Jan 2025
* @author Tim Spain <timothy.spain@nersc.no>
*/

Expand All @@ -25,6 +25,9 @@
#include "include/NZLevels.hpp"
#include "include/NextsimModule.hpp"
#include "include/gridNames.hpp"
#ifdef USE_XIOS
#include "include/Xios.hpp"
#endif

#include <ncDim.h>
#include <ncFile.h>
Expand Down Expand Up @@ -100,10 +103,9 @@ TEST_CASE("Test periodic output")
ModelMetadata meta;
#ifdef USE_XIOS
enableXios();
Xios xiosHandler("P0-0T01:00:00", "test1");
xiosHandler.setCalendarOrigin(TimePoint("1970-01-01T00:00:00Z"));
meta.setXiosHandler(&xiosHandler);
xiosHandler.close_context_definition();
Xios* xiosHandler = Xios::getInstance("P0-0T01:00:00", "test1");
xiosHandler->setCalendarOrigin(TimePoint("1970-01-01T00:00:00Z"));
xiosHandler->close_context_definition();
#endif
meta.setTime(TimePoint("2020-01-01T00:00:00Z"));

Expand Down
30 changes: 10 additions & 20 deletions core/test/ParaGrid_test.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*!
* @file ParaGrid_test.cpp
*
* @date 10 Dec 2024
* @date 02 Jan 2025
* @author Tim Spain <timothy.spain@nersc.no>
*/

Expand All @@ -23,6 +23,9 @@
#include "include/ParaGridIO.hpp"
#include "include/ParametricGrid.hpp"
#include "include/gridNames.hpp"
#ifdef USE_XIOS
#include "include/Xios.hpp"
#endif

#include <cmath>
#include <filesystem>
Expand Down Expand Up @@ -210,10 +213,9 @@ TEST_CASE("Write and read a ModelState-based ParaGrid restart file")
ModelMetadata metadata;
#ifdef USE_XIOS
enableXios();
Xios xiosHandler("P0-0T01:00:00", "test1a");
xiosHandler.setCalendarOrigin(TimePoint("1970-01-01T00:00:00Z"));
metadata.setXiosHandler(&xiosHandler);
xiosHandler.close_context_definition();
Xios* xiosHandler = Xios::getInstance("P0-0T01:00:00");
xiosHandler->setCalendarOrigin(TimePoint("1970-01-01T00:00:00Z"));
xiosHandler->close_context_definition();
#endif
metadata.setTime(TimePoint("2000-01-01T00:00:00Z"));
// The coordinates are passed through the metadata object as affix
Expand Down Expand Up @@ -258,9 +260,6 @@ TEST_CASE("Write and read a ModelState-based ParaGrid restart file")

#ifdef USE_MPI
ModelMetadata metadataIn(partitionFilename, test_comm);
#ifdef USE_XIOS
metadataIn.setXiosHandler(&xiosHandler);
#endif
metadataIn.setTime(TimePoint(dateString));
ModelState ms = gridIn.getModelState(filename, metadataIn);
#else
Expand Down Expand Up @@ -413,10 +412,7 @@ TEST_CASE("Write a diagnostic ParaGrid file")
ModelMetadata metadata;
#ifdef USE_XIOS
enableXios();
Xios xiosHandler("P0-0T01:00:00", "test2");
xiosHandler.setCalendarOrigin(TimePoint("1970-01-01T00:00:00Z"));
metadata.setXiosHandler(&xiosHandler);
xiosHandler.close_context_definition();
Xios* xiosHandler = Xios::getInstance();
#endif
metadata.setTime(TimePoint("2000-01-01T00:00:00Z"));
// The coordinates are passed through the metadata object as affix
Expand Down Expand Up @@ -556,10 +552,7 @@ TEST_CASE("Check an exception is thrown for an invalid file name")
ModelMetadata metadataIn(partitionFilename, test_comm);
#ifdef USE_XIOS
enableXios();
Xios xiosHandler("P0-0T01:00:00", "test4");
xiosHandler.setCalendarOrigin(TimePoint("1970-01-01T00:00:00Z"));
metadataIn.setXiosHandler(&xiosHandler);
xiosHandler.close_context_definition();
Xios* xiosHandler = Xios::getInstance();
#endif
metadataIn.setTime(TimePoint(dateString));
REQUIRE_THROWS(state = gridIn.getModelState(longRandomFilename, metadataIn));
Expand Down Expand Up @@ -614,10 +607,7 @@ TEST_CASE("Check if a file with the old dimension names can be read")
ModelMetadata metadata;
#ifdef USE_XIOS
enableXios();
Xios xiosHandler("P0-0T01:00:00", "test5");
xiosHandler.setCalendarOrigin(TimePoint("1970-01-01T00:00:00Z"));
metadata.setXiosHandler(&xiosHandler);
xiosHandler.close_context_definition();
Xios* xiosHandler = Xios::getInstance();
#endif
metadata.setMpiMetadata(test_comm);
if (metadata.mpiMyRank == 0) {
Expand Down
36 changes: 18 additions & 18 deletions core/test/XiosAxis_test.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*!
* @file XiosAxis_test.cpp
* @author Joe Wallwork <jw2423@cam.ac.uk>
* @date 03 Dec 2024
* @date 02 Jan 2025
* @brief Tests for XIOS axes
* @details
* This test is designed to test axis functionality of the C++ interface
Expand Down Expand Up @@ -29,34 +29,34 @@ MPI_TEST_CASE("TestXiosAxis", 2)
{
enableXios();

// Initialize an Xios instance called xios_handler
Xios xios_handler;
REQUIRE(xios_handler.isInitialized());
REQUIRE(xios_handler.getClientMPISize() == 2);
// Get the Xios singleton instance and check it's initialized
Xios* xiosHandler = Xios::getInstance();
REQUIRE(xiosHandler->isInitialized());
REQUIRE(xiosHandler->getClientMPISize() == 2);

// --- Tests for axis API
const std::string axisId = { "axis_A" };
REQUIRE_THROWS_WITH(xios_handler.getAxisSize(axisId), "Xios: Undefined axis 'axis_A'");
REQUIRE_THROWS_WITH(xios_handler.getAxisValues(axisId), "Xios: Undefined axis 'axis_A'");
xios_handler.createAxis(axisId);
REQUIRE_THROWS_WITH(xios_handler.createAxis(axisId), "Xios: Axis 'axis_A' already exists");
REQUIRE_THROWS_WITH(xiosHandler->getAxisSize(axisId), "Xios: Undefined axis 'axis_A'");
REQUIRE_THROWS_WITH(xiosHandler->getAxisValues(axisId), "Xios: Undefined axis 'axis_A'");
xiosHandler->createAxis(axisId);
REQUIRE_THROWS_WITH(xiosHandler->createAxis(axisId), "Xios: Axis 'axis_A' already exists");
// Axis size
REQUIRE_THROWS_WITH(xios_handler.getAxisSize(axisId), "Xios: Undefined size for axis 'axis_A'");
REQUIRE_THROWS_WITH(xiosHandler->getAxisSize(axisId), "Xios: Undefined size for axis 'axis_A'");
const size_t axisSize { 2 };
xios_handler.setAxisSize(axisId, axisSize);
REQUIRE(xios_handler.getAxisSize(axisId) == axisSize);
xiosHandler->setAxisSize(axisId, axisSize);
REQUIRE(xiosHandler->getAxisSize(axisId) == axisSize);
// Axis values
REQUIRE_THROWS_WITH(
xios_handler.getAxisValues(axisId), "Xios: Undefined values for axis 'axis_A'");
REQUIRE_THROWS_WITH(xios_handler.setAxisValues(axisId, { 0.0, 1.0, 2.0 }),
xiosHandler->getAxisValues(axisId), "Xios: Undefined values for axis 'axis_A'");
REQUIRE_THROWS_WITH(xiosHandler->setAxisValues(axisId, { 0.0, 1.0, 2.0 }),
"Xios: Size incompatible with values for axis 'axis_A'");
std::vector<double> axisValues { 0.0, 1.0 };
xios_handler.setAxisValues(axisId, axisValues);
std::vector<double> axis_A = xios_handler.getAxisValues(axisId);
xiosHandler->setAxisValues(axisId, axisValues);
std::vector<double> axis_A = xiosHandler->getAxisValues(axisId);
REQUIRE(axis_A[0] == doctest::Approx(0.0));
REQUIRE(axis_A[1] == doctest::Approx(1.0));

xios_handler.close_context_definition();
xios_handler.context_finalize();
xiosHandler->close_context_definition();
xiosHandler->context_finalize();
}
}
Loading
Loading