Skip to content

Commit

Permalink
Allow timestep to be set when XIOS handler is created (#750)
Browse files Browse the repository at this point in the history
Refinement of #751

# Allow timestep to be set when XIOS handler is created

This PR makes a few minor modifications to the `Xios` handler object:
* Improve clarity of docstrings related to configuration.
* Allow the user to set the timestep, calendar type and context ID when
creating the `Xios` instance.
* Set the calendar origin and start as part of the configuration.
* Update tests accordingly.

These steps should make it easier to set up XIOS and reduce the amount
of additional boilerplate when using it to drive I/O rather than the
existing approach.
  • Loading branch information
jwallwork23 authored Dec 10, 2024
2 parents 4bb7500 + cd35f66 commit 225adcf
Show file tree
Hide file tree
Showing 10 changed files with 60 additions and 50 deletions.
40 changes: 30 additions & 10 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 19 Nov 2024
* @date 09 Dec 2024
* @brief XIOS interface implementation
* @details
*
Expand Down Expand Up @@ -45,13 +45,20 @@ void enableXios()
}

/*!
* Constructor
* Constructor: Configure an XIOS server
*
* Configure an XIOS server
* @param dt Timestep to use for the model
* @param contextid ID for the XIOS context
* @param starttime Datetime string for the start of the simulation
* @param calendartype Type of calendar to use
*/
Xios::Xios(const std::string contextId)
Xios::Xios(const std::string dt, const std::string contextid, const std::string starttime,
const std::string calendartype)
{
_contextId = contextId;
timestep = Duration(dt);
startTime = TimePoint(starttime);
contextId = contextid;
calendarType = calendartype;
configure();
}

Expand Down Expand Up @@ -99,7 +106,7 @@ void Xios::configure()
}

//! Configure calendar settings
void Xios::configureServer(const std::string calendarType)
void Xios::configureServer()
{
// Initialize XIOS Server process and store MPI communicator
clientId = "client";
Expand All @@ -112,14 +119,18 @@ void Xios::configureServer(const std::string calendarType)
MPI_Comm_size(clientComm, &mpi_size);

// Initialize 'nextSIM-DG' context
cxios_context_initialize(_contextId.c_str(), _contextId.length(), &clientComm_F);
cxios_context_initialize(contextId.c_str(), contextId.length(), &clientComm_F);

// Initialize calendar wrapper for 'nextSIM-DG' context
cxios_get_current_calendar_wrapper(&clientCalendar);
cxios_set_calendar_wrapper_type(clientCalendar, calendarType.c_str(), calendarType.length());
cxios_duration timestep { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 };
cxios_set_calendar_wrapper_timestep(clientCalendar, timestep);
cxios_set_calendar_wrapper_timestep(clientCalendar, convertDurationToXios(timestep));
cxios_create_calendar(clientCalendar);
cxios_update_calendar_timestep(clientCalendar);

// Set default calendar origin and start
setCalendarOrigin(TimePoint("1970-01-01T00:00:00Z")); // Unix epoch
setCalendarStart(TimePoint(startTime));
}

/*!
Expand All @@ -140,7 +151,7 @@ int Xios::getClientMPIRank() { return mpi_rank; }
bool Xios::isInitialized()
{
bool init = false;
cxios_context_is_initialized(_contextId.c_str(), _contextId.length(), &init);
cxios_context_is_initialized(contextId.c_str(), contextId.length(), &init);
return init;
}

Expand Down Expand Up @@ -279,6 +290,9 @@ std::string Xios::getCalendarType()
*/
TimePoint Xios::getCalendarOrigin()
{
if (!cxios_is_defined_calendar_wrapper_time_origin(clientCalendar)) {
throw std::runtime_error("Xios: Calendar origin has not been set");
}
cxios_date calendar_origin;
cxios_get_calendar_wrapper_date_time_origin(clientCalendar, &calendar_origin);
return TimePoint(convertXiosDatetimeToString(calendar_origin, true));
Expand All @@ -291,6 +305,9 @@ TimePoint Xios::getCalendarOrigin()
*/
TimePoint Xios::getCalendarStart()
{
if (!cxios_is_defined_calendar_wrapper_start_date(clientCalendar)) {
throw std::runtime_error("Xios: Calendar start date has not been set");
}
cxios_date calendar_start;
cxios_get_calendar_wrapper_date_start_date(clientCalendar, &calendar_start);
return TimePoint(convertXiosDatetimeToString(calendar_start, true));
Expand All @@ -303,6 +320,9 @@ TimePoint Xios::getCalendarStart()
*/
Duration Xios::getCalendarTimestep()
{
if (!cxios_is_defined_calendar_wrapper_timestep(clientCalendar)) {
throw std::runtime_error("Xios: Calendar timestep has not been set");
}
cxios_duration calendar_timestep;
cxios_get_calendar_wrapper_timestep(clientCalendar, &calendar_timestep);
return convertDurationFromXios(calendar_timestep);
Expand Down
13 changes: 9 additions & 4 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 18 Nov 2024
* @date 04 Dec 2024
* @brief XIOS interface header
* @details
*
Expand Down Expand Up @@ -31,7 +31,9 @@ void enableXios();

class Xios : public Configured<Xios> {
public:
Xios(const std::string contextId = "nextSIM-DG");
Xios(const std::string dt = "P0-0T01:00:00", const std::string contextid = "nextSIM-DG",
const std::string starttime = "1970-01-01T00:00:00Z",
const std::string calendartype = "Gregorian");
~Xios();

/* Initialization and finalization */
Expand All @@ -42,7 +44,7 @@ class Xios : public Configured<Xios> {

/* Configuration */
void configure() override;
void configureServer(const std::string calendarType = "Gregorian");
void configureServer();

/* MPI decomposition */
int getClientMPISize();
Expand Down Expand Up @@ -146,7 +148,10 @@ class Xios : public Configured<Xios> {
bool isEnabled;

std::string clientId;
std::string _contextId;
std::string calendarType;
std::string contextId;
Duration timestep;
TimePoint startTime;
MPI_Comm clientComm;
MPI_Fint clientComm_F;
MPI_Fint nullComm_F;
Expand Down
5 changes: 4 additions & 1 deletion core/src/include/xios_c_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @file xios_c_interface.hpp
* @author Tom Meltzer <tdm39@cam.ac.uk>
* @author Joe Wallwork <jw2423@cam.ac.uk>
* @date 12 August 2024
* @date 09 Dec 2024
* @brief C interface for XIOS library
* @details
* This interface is based on an earlier version provided by Laurent as part of
Expand Down Expand Up @@ -59,13 +59,16 @@ void cxios_get_calendar_wrapper_date_time_origin(
void cxios_get_calendar_wrapper_type(
xios::CCalendarWrapper* calendarWrapper_hdl, const char* type, int type_size);
void cxios_get_current_date(cxios_date* date);
bool cxios_is_defined_calendar_wrapper_time_origin(xios::CCalendarWrapper* calendar_wrapper_hdl);
bool cxios_is_defined_calendar_wrapper_start_date(xios::CCalendarWrapper* calendar_wrapper_hdl);
void cxios_update_calendar(int step);

// timestep methods
void cxios_set_calendar_wrapper_timestep(
xios::CCalendarWrapper* calendar_wrapper_hdl, cxios_duration timestep_c);
void cxios_get_calendar_wrapper_timestep(
xios::CCalendarWrapper* calendar_wrapper_hdl, cxios_duration* timestep_c);
bool cxios_is_defined_calendar_wrapper_timestep(xios::CCalendarWrapper* calendar_wrapper_hdl);
void cxios_update_calendar_timestep(xios::CCalendarWrapper* calendarWrapper_hdl);

// axis group methods
Expand Down
5 changes: 1 addition & 4 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 19 Nov 2024
* @date 03 Dec 2024
* @brief Tests for XIOS axes
* @details
* This test is designed to test axis functionality of the C++ interface
Expand Down Expand Up @@ -34,9 +34,6 @@ MPI_TEST_CASE("TestXiosAxis", 2)
REQUIRE(xios_handler.isInitialized());
REQUIRE(xios_handler.getClientMPISize() == 2);

// Set timestep as a minimum
xios_handler.setCalendarTimestep(Duration("P0-0T01:00:00"));

// --- Tests for axis API
const std::string axisId = { "axis_A" };
REQUIRE_THROWS_WITH(xios_handler.getAxisSize(axisId), "Xios: Undefined axis 'axis_A'");
Expand Down
9 changes: 6 additions & 3 deletions core/test/XiosCalendar_test.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*!
* @file XiosCalendar_test.cpp
* @author Joe Wallwork <jw2423@cam.ac.uk>
* @date 19 Nov 2024
* @date 04 Dec 2024
* @brief Tests for XIOS calandars
* @details
* This test is designed to test calendar functionality of the C++ interface
Expand Down Expand Up @@ -38,20 +38,23 @@ MPI_TEST_CASE("TestXiosInitialization", 2)
// Calendar type
REQUIRE(xios_handler.getCalendarType() == "Gregorian");
// Calendar origin
REQUIRE(xios_handler.getCalendarOrigin().format() == "1970-01-01T00:00:00Z"); // Default
TimePoint origin("2020-01-23T00:08:15Z");
xios_handler.setCalendarOrigin(origin);
REQUIRE(origin == xios_handler.getCalendarOrigin());
REQUIRE(origin.format() == "2020-01-23T00:08:15Z");
// Calendar start
REQUIRE(xios_handler.getCalendarStart().format() == "1970-01-01T00:00:00Z"); // Default
TimePoint start("2023-03-17T17:11:00Z");
xios_handler.setCalendarStart(start);
REQUIRE(start == xios_handler.getCalendarStart());
REQUIRE(start.format() == "2023-03-17T17:11:00Z");
// Timestep
REQUIRE(xios_handler.getCalendarTimestep().seconds() == 3600.0); // Default
Duration timestep("P0-0T01:30:00");
REQUIRE(timestep.seconds() == doctest::Approx(5400.0));
REQUIRE(timestep.seconds() == 5400.0);
xios_handler.setCalendarTimestep(timestep);
REQUIRE(xios_handler.getCalendarTimestep().seconds() == doctest::Approx(5400.0));
REQUIRE(xios_handler.getCalendarTimestep().seconds() == 5400.0);

xios_handler.close_context_definition();

Expand Down
5 changes: 1 addition & 4 deletions core/test/XiosDomain_test.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*!
* @file XiosDomain_test.cpp
* @author Joe Wallwork <jw2423@cam.ac.uk>
* @date 19 Nov 2024
* @date 03 Dec 2024
* @brief Tests for XIOS domains
* @details
* This test is designed to test domain functionality of the C++ interface
Expand Down Expand Up @@ -36,9 +36,6 @@ MPI_TEST_CASE("TestXiosDomain", 2)
REQUIRE(size == 2);
const size_t rank = xios_handler.getClientMPIRank();

// Set timestep as a minimum
xios_handler.setCalendarTimestep(Duration("P0-0T01:00:00"));

// --- Tests for domain API
const std::string domainId = "domain_A";
REQUIRE_THROWS_WITH(xios_handler.getDomainType(domainId), "Xios: Undefined domain 'domain_A'");
Expand Down
8 changes: 2 additions & 6 deletions core/test/XiosField_test.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*!
* @file XiosField_test.cpp
* @author Joe Wallwork <jw2423@cam.ac.uk>
* @date 19 Nov 2024
* @date 03 Dec 2024
* @brief Tests for XIOS axes
* @details
* This test is designed to test axis functionality of the C++ interface
Expand Down Expand Up @@ -36,10 +36,6 @@ MPI_TEST_CASE("TestXiosField", 2)
REQUIRE(size == 2);
const size_t rank = xios_handler.getClientMPIRank();

// Set timestep as a minimum
Duration timestep("P0-0T01:00:00");
xios_handler.setCalendarTimestep(timestep);

// Create an axis with two points
xios_handler.createAxis("axis_A");
xios_handler.setAxisValues("axis_A", { 0.0, 1.0 });
Expand Down Expand Up @@ -76,7 +72,7 @@ MPI_TEST_CASE("TestXiosField", 2)
xios_handler.setFieldReadAccess(fieldId, readAccess);
REQUIRE(xios_handler.getFieldReadAccess(fieldId));
// Frequency offset
Duration freqOffset = timestep;
Duration freqOffset = xios_handler.getCalendarTimestep();
xios_handler.setFieldFreqOffset(fieldId, freqOffset);
// TODO: Overload == for Duration
REQUIRE(xios_handler.getFieldFreqOffset(fieldId).seconds() == freqOffset.seconds());
Expand Down
9 changes: 3 additions & 6 deletions core/test/XiosFile_test.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*!
* @file XiosFile_test.cpp
* @author Joe Wallwork <jw2423@cam.ac.uk>
* @date 19 Nov 2024
* @date 03 Dec 2024
* @brief Tests for XIOS axes
* @details
* This test is designed to test axis functionality of the C++ interface
Expand Down Expand Up @@ -32,16 +32,12 @@ MPI_TEST_CASE("TestXiosFile", 2)
enableXios();

// Initialize an Xios instance called xios_handler
Xios xios_handler;
Xios xios_handler("P0-0T01:30:00");
REQUIRE(xios_handler.isInitialized());
const size_t size = xios_handler.getClientMPISize();
REQUIRE(size == 2);
const size_t rank = xios_handler.getClientMPIRank();

// Set timestep as a minimum
Duration timestep("P0-0T01:30:00");
xios_handler.setCalendarTimestep(timestep);

// Create a simple axis with two points
xios_handler.createAxis("axis_A");
xios_handler.setAxisValues("axis_A", { 0.0, 1.0 });
Expand Down Expand Up @@ -73,6 +69,7 @@ MPI_TEST_CASE("TestXiosFile", 2)
// Output frequency
REQUIRE_THROWS_WITH(xios_handler.getFileOutputFreq(fileId),
"Xios: Undefined output frequency for file 'output'");
Duration timestep = xios_handler.getCalendarTimestep();
xios_handler.setFileOutputFreq(fileId, timestep);
REQUIRE(xios_handler.getFileOutputFreq(fileId).seconds() == 1.5 * 60 * 60);
// Split frequency
Expand Down
5 changes: 1 addition & 4 deletions core/test/XiosGrid_test.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*!
* @file XiosGrid_test.cpp
* @author Joe Wallwork <jw2423@cam.ac.uk>
* @date 19 Nov 2024
* @date 03 Dec 2024
* @brief Tests for XIOS axes
* @details
* This test is designed to test axis functionality of the C++ interface
Expand Down Expand Up @@ -35,9 +35,6 @@ MPI_TEST_CASE("TestXiosGrid", 2)
REQUIRE(size == 2);
const size_t rank = xios_handler.getClientMPIRank();

// Set timestep as a minimum
xios_handler.setCalendarTimestep(Duration("P0-0T01:30:00"));

// Create a 4x2 horizontal domain with a partition halving the x-extent
xios_handler.createDomain("domain_XY");
xios_handler.setDomainType("domain_XY", "rectilinear");
Expand Down
11 changes: 3 additions & 8 deletions core/test/XiosReadWrite_test.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*!
* @file XiosReadWrite_test.cpp
* @author Joe Wallwork <jw2423@cam.ac.uk>
* @date 19 Nov 2024
* @date 04 Dec 2024
* @brief Tests for XIOS write method
* @details
* This test is designed to test the read and write methods of the C++
Expand Down Expand Up @@ -61,18 +61,12 @@ Xios setupXiosHandler(int dim, bool read)
} else {
label = "write";
}
Xios xios_handler(formatId(label, dim));
Xios xios_handler("P0-0T01:30:00", formatId(label, dim), "2023-03-17T17:11:00Z");
REQUIRE(xios_handler.isInitialized());
const size_t size = xios_handler.getClientMPISize();
REQUIRE(size == 2);
const size_t rank = xios_handler.getClientMPIRank();

// Calendar setup
xios_handler.setCalendarOrigin(TimePoint("2020-01-23T00:08:15Z"));
xios_handler.setCalendarStart(TimePoint("2023-03-17T17:11:00Z"));
Duration timestep("P0-0T01:30:00");
xios_handler.setCalendarTimestep(timestep);

// Set ModelArray dimensions
const size_t nx_glo = 4;
const size_t ny_glo = 2;
Expand Down Expand Up @@ -111,6 +105,7 @@ Xios setupXiosHandler(int dim, bool read)
xios_handler.setFieldOperation(fieldId, "instant");
xios_handler.setFieldGridRef(fieldId, formatId("grid", dim));
xios_handler.setFieldReadAccess(fieldId, read);
Duration timestep = xios_handler.getCalendarTimestep();
xios_handler.setFieldFreqOffset(fieldId, timestep);

// Create an file for reading/writing of field data
Expand Down

0 comments on commit 225adcf

Please sign in to comment.