Skip to content

Commit 3536d24

Browse files
authored
Move calendar, axis, and grid config to XML (#993)
# Move calendar, axis, and grid config to XML Towards #850 ### Task List - [x] Defined the tests that specify a complete and functioning change (*It may help to create a [design specification & test specification](../../../wiki/Specification-Template)*) - [x] Implemented the source code change that satisfies the tests - [x] Documented the feature by providing worked example - [x] Updated the README or other documentation - [x] Completed the pre-Request checklist below --- # Change Description Currently, the `iodef.xml` file for configuring XIOS consists only of information related to errors and logs. Given that there are several pieces of information we know ahead of time, we can define them compactly in the XML file, thereby cutting down the XIOS handler API. This PR moves the following over and and removes associated getters and setters: * Calendar type (Gregorian) * Calendar origin (Unix epoch) * Axes (`DGAxis`, `DGSAxis`, `VertexAxis`). The sizes of the axes is known at compile time, so I used Jinja syntax to insert the values specified for the CMake. * Grids (`HGrid`, `DGGrid`, `DGSGrid`, `VertexGrid`, `CGGrid`) in terms of their associated axes. Unfortunately, domains can't be easily defined ahead of time because the global sizes are required. --- # Test Description The grid test became redundant and so was dropped. --- # Documentation Impact The XIOS docs are updated to mention both the XML configuration and the configuration via nextSIM-DG. --- ### Pre-Request Checklist - [x] The requirements of this pull request are fully captured in an issue or design specification and are linked and summarised in the description of this PR - [x] No new warnings are generated - [x] The documentation has been updated (or an issue has been created to track the corresponding change) - [x] Methods and Tests are commented such that they can be understood without having to obtain additional context - [x] This PR/Issue is labelled as a bug/feature/enhancement/breaking change - [x] This change conforms to the conventions described in the README
2 parents 92fe955 + 9a79181 commit 3536d24

16 files changed

+146
-384
lines changed

.github/workflows/test_suite.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,14 @@ jobs:
146146
with:
147147
persist-credentials: false
148148

149+
- name: Python installs
150+
run: |
151+
apt -y update
152+
apt install -y python3-venv
153+
python3 -m venv ~/nextsim
154+
. $HOME/nextsim/bin/activate
155+
pip install jinja2
156+
149157
- name: build domain_decomp
150158
run: |
151159
. /opt/spack-environment/activate.sh
@@ -163,6 +171,7 @@ jobs:
163171
export OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1
164172
export OMPI_MCA_rmaps_base_oversubscribe=1
165173
. /opt/spack-environment/activate.sh
174+
. $HOME/nextsim/bin/activate
166175
# provide path to domain_decomp binary
167176
export PATH="${PATH}:${PWD}/domain_decomp/build"
168177
mkdir -p build && cd build

core/src/ParaGridIO_Xios.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ ParaGridIO::ParaGridIO(ParametricGrid& grid)
3535
{
3636
Xios& xiosHandler = Xios::getInstance();
3737
xiosHandler.setupDomains();
38-
xiosHandler.setupAxes();
3938
xiosHandler.setupGrids();
4039
}
4140

@@ -52,7 +51,6 @@ ModelState ParaGridIO::getModelState(const std::string& filePath)
5251
}
5352

5453
// Get all variables in the file and load them into a new ModelState
55-
const bool readAccess = true;
5654
for (std::string fieldId : xiosHandler.configGetInputRestartFieldNames()) {
5755
ModelArray::Type type = xiosHandler.getFieldType(fieldId);
5856
if (type == ModelArray::Type::H) {
@@ -120,7 +118,6 @@ ModelState ParaGridIO::readForcingTimeStatic(
120118
}
121119

122120
// Get all forcings and load them into a new ModelState
123-
const bool readAccess = true;
124121
std::set<std::string> forcingFieldIds = xiosHandler.configGetForcingFieldNames();
125122
for (const std::string& fieldId : forcings) {
126123
if (!xiosHandler.getFieldReadAccess(fieldId)) {

core/src/Xios.cpp

Lines changed: 5 additions & 197 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,6 @@
4040
#include <regex>
4141
#include <string>
4242

43-
#ifndef DGCOMP
44-
#define DGCOMP 6 // Define to prevent errors from static analysis tools
45-
#error "Number of DG components (DGCOMP) not defined" // But throw an error anyway
46-
#endif
47-
48-
#ifndef DGSTRESSCOMP
49-
#define DGSTRESSCOMP 8 // Define to prevent errors from static analysis tools
50-
#error "Number of DG stress components (DGSTRESSCOMP) not defined" // But throw an error anyway
51-
#endif
52-
5343
#ifndef CGDEGREE
5444
#define CGDEGREE 2 // Define to prevent errors from static analysis tools
5545
#error "CG degree (CGDEGREE) not defined" // But throw an error anyway
@@ -255,21 +245,17 @@ void Xios::configureServer()
255245
// Initialize 'nextSIM-DG' context
256246
cxios_context_initialize(contextId.c_str(), contextId.length(), &clientComm_F);
257247

258-
// Initialize calendar wrapper for 'nextSIM-DG' context
248+
// Set the calendar timestep for the 'nextSIM-DG' context
249+
// NOTE: The calendar itself is set up in iodef.xml
259250
cxios_get_current_calendar_wrapper(&clientCalendar);
260-
cxios_set_calendar_wrapper_type(clientCalendar, calendarType.c_str(), calendarType.length());
261251
ModelMetadata& metadata = ModelMetadata::getInstance();
262252
cxios_set_calendar_wrapper_timestep(
263253
clientCalendar, convertDurationToXios(metadata.stepLength()));
264-
cxios_create_calendar(clientCalendar);
265254
cxios_update_calendar_timestep(clientCalendar);
266255
if (!cxios_is_defined_calendar_wrapper_timestep(clientCalendar)) {
267256
throw std::runtime_error("Xios: Calendar timestep has not been set");
268257
}
269258

270-
// Set default calendar origin
271-
setCalendarOrigin(TimePoint("1970-01-01T00:00:00Z")); // Unix epoch
272-
273259
// Set start time from configuration file
274260
setCalendarStart(metadata.startTime());
275261
}
@@ -357,17 +343,6 @@ cxios_duration Xios::convertDurationToXios(const Duration duration)
357343
return cxios_duration({ 0.0, 0.0, 0.0, 0.0, 0.0, duration.seconds() });
358344
}
359345

360-
/*!
361-
* Set calendar origin
362-
*
363-
* @param origin
364-
*/
365-
void Xios::setCalendarOrigin(const TimePoint origin)
366-
{
367-
cxios_date datetime = convertStringToXiosDatetime(origin.format(), true);
368-
cxios_set_calendar_wrapper_date_time_origin(clientCalendar, datetime);
369-
}
370-
371346
/*!
372347
* Set calendar start date
373348
*
@@ -391,21 +366,6 @@ void Xios::setCalendarStep(const int stepNumber) { cxios_update_calendar(stepNum
391366
*/
392367
void Xios::incrementCalendar() { setCalendarStep(getCalendarStep() + 1); }
393368

394-
/*!
395-
* Get calendar origin
396-
*
397-
* @return calendar origin
398-
*/
399-
TimePoint Xios::getCalendarOrigin()
400-
{
401-
if (!cxios_is_defined_calendar_wrapper_time_origin(clientCalendar)) {
402-
throw std::runtime_error("Xios: Calendar origin has not been set");
403-
}
404-
cxios_date calendar_origin;
405-
cxios_get_calendar_wrapper_date_time_origin(clientCalendar, &calendar_origin);
406-
return TimePoint(convertXiosDatetimeToString(calendar_origin, true));
407-
}
408-
409369
/*!
410370
* Get calendar start date
411371
*
@@ -440,22 +400,6 @@ TimePoint Xios::getCurrentDate()
440400
return TimePoint(convertXiosDatetimeToString(xiosDate, true));
441401
}
442402

443-
/*!
444-
* Get the axis_definition group
445-
*
446-
* @return a pointer to the XIOS CAxisGroup object
447-
*/
448-
xios::CAxisGroup* Xios::getAxisGroup()
449-
{
450-
const std::string groupId = "axis_definition";
451-
xios::CAxisGroup* group = NULL;
452-
cxios_axisgroup_handle_create(&group, groupId.c_str(), groupId.length());
453-
if (!group) {
454-
throw std::runtime_error("Xios: Null pointer for group 'axis_definition'");
455-
}
456-
return group;
457-
}
458-
459403
/*!
460404
* Get the axis associated with a given ID
461405
*
@@ -477,47 +421,6 @@ xios::CAxis* Xios::getAxis(const std::string axisId)
477421
return axis;
478422
}
479423

480-
/*!
481-
* Create an axis with some ID.
482-
*
483-
* @param the axis ID
484-
*/
485-
void Xios::createAxis(const std::string axisId)
486-
{
487-
bool exists;
488-
cxios_axis_valid_id(&exists, axisId.c_str(), axisId.length());
489-
if (exists) {
490-
throw std::runtime_error("Xios: Axis '" + axisId + "' already exists");
491-
}
492-
xios::CAxis* axis = NULL;
493-
cxios_xml_tree_add_axis(getAxisGroup(), &axis, axisId.c_str(), axisId.length());
494-
if (!axis) {
495-
throw std::runtime_error("Xios: Null pointer for axis '" + axisId + "'");
496-
}
497-
cxios_axis_valid_id(&exists, axisId.c_str(), axisId.length());
498-
if (!exists) {
499-
throw std::runtime_error("Xios: Failed to create axis '" + axisId + "'");
500-
}
501-
}
502-
503-
/*!
504-
* Set the size of a given axis (the number of global points)
505-
*
506-
* @param the axis ID
507-
* @param the size to set
508-
*/
509-
void Xios::setAxisSize(const std::string axisId, const size_t size)
510-
{
511-
xios::CAxis* axis = getAxis(axisId);
512-
if (cxios_is_defined_axis_n_glo(axis)) {
513-
Logged::warning("Xios: Size already set for axis '" + axisId + "'");
514-
}
515-
cxios_set_axis_n_glo(axis, (int)size);
516-
if (!cxios_is_defined_axis_n_glo(axis)) {
517-
throw std::runtime_error("Xios: Failed to set size for axis '" + axisId + "'");
518-
}
519-
}
520-
521424
/*!
522425
* Get the size of a given axis (the number of global points)
523426
*
@@ -705,8 +608,8 @@ void Xios::setupDomains()
705608
auto& metadata = ModelMetadata::getInstance();
706609

707610
ModelArray::setNComponents(ModelArray::Type::VERTEX, ModelArray::nCoords);
708-
ModelArray::setNComponents(ModelArray::Type::DG, DGCOMP);
709-
ModelArray::setNComponents(ModelArray::Type::DGSTRESS, DGSTRESSCOMP);
611+
ModelArray::setNComponents(ModelArray::Type::DG, getAxisSize("DGAxis"));
612+
ModelArray::setNComponents(ModelArray::Type::DGSTRESS, getAxisSize("DGSAxis"));
710613
for (auto entry : domainIds) {
711614
ModelArray::Type type = entry.first;
712615
const std::string domainId = entry.second;
@@ -830,29 +733,6 @@ void Xios::setupDomains()
830733
}
831734
}
832735

833-
/*!
834-
* @brief Create XIOS axes for each ModelArray type
835-
*
836-
* @details This function sets up the XIOS axes for each field type based on the configuration
837-
* in the axisIds map and in the ModelArray class.
838-
*/
839-
void Xios::setupAxes()
840-
{
841-
for (auto entry : axisIds) {
842-
ModelArray::Type type = entry.first;
843-
const std::string axisId = entry.second;
844-
ModelArray::Dimension dim = ModelArray::componentMap.at(type);
845-
createAxis(axisId);
846-
setAxisSize(axisId, ModelArray::size(dim));
847-
xios::CAxis* axis = getAxis(axisId);
848-
const std::string axisName = axisNames[axisId];
849-
cxios_set_axis_dim_name(axis, axisName.c_str(), axisName.length());
850-
if (!cxios_is_defined_axis_dim_name(axis)) {
851-
throw std::runtime_error("Xios: Failed to set name for axis '" + axisId + "'");
852-
}
853-
}
854-
}
855-
856736
/*!
857737
* @brief Create XIOS grids for each ModelArray type
858738
*
@@ -865,35 +745,13 @@ void Xios::setupGrids()
865745
for (auto entry : gridIds) {
866746
ModelArray::Type type = entry.first;
867747
const std::string gridId = entry.second;
868-
createGrid(gridId);
869748
xios::CGrid* grid = getGrid(gridId);
870-
if (axisIds.count(type) > 0) {
871-
const std::string axisId = axisIds[type];
872-
xios::CAxis* axis = getAxis(axisId);
873-
cxios_xml_tree_add_axistogrid(grid, &axis, axisId.c_str(), axisId.length());
874-
}
875749
const std::string domainId = domainIds[type];
876750
xios::CDomain* domain = getDomain(domainId);
877751
cxios_xml_tree_add_domaintogrid(grid, &domain, domainId.c_str(), domainId.length());
878752
}
879753
}
880754

881-
/*!
882-
* Get the grid_definition group
883-
*
884-
* @return a pointer to the XIOS CGridGroup object
885-
*/
886-
xios::CGridGroup* Xios::getGridGroup()
887-
{
888-
const std::string groupId = "grid_definition";
889-
xios::CGridGroup* group = NULL;
890-
cxios_gridgroup_handle_create(&group, groupId.c_str(), groupId.length());
891-
if (!group) {
892-
throw std::runtime_error("Xios: Null pointer for group 'grid_definition'");
893-
}
894-
return group;
895-
}
896-
897755
/*!
898756
* Get the grid associated with a given ID
899757
*
@@ -915,56 +773,6 @@ xios::CGrid* Xios::getGrid(const std::string gridId)
915773
return grid;
916774
}
917775

918-
/*!
919-
* Create a grid with some ID
920-
*
921-
* @param the grid ID
922-
*/
923-
void Xios::createGrid(const std::string gridId)
924-
{
925-
bool exists;
926-
cxios_grid_valid_id(&exists, gridId.c_str(), gridId.length());
927-
if (exists) {
928-
throw std::runtime_error("Xios: Grid '" + gridId + "' already exists");
929-
}
930-
xios::CGrid* grid = NULL;
931-
cxios_xml_tree_add_grid(getGridGroup(), &grid, gridId.c_str(), gridId.length());
932-
if (!grid) {
933-
throw std::runtime_error("Xios: Null pointer for grid '" + gridId + "'");
934-
}
935-
cxios_grid_valid_id(&exists, gridId.c_str(), gridId.length());
936-
if (!exists) {
937-
throw std::runtime_error("Xios: Failed to create grid '" + gridId + "'");
938-
}
939-
cxios_set_grid_name(grid, gridId.c_str(), gridId.length());
940-
if (!cxios_is_defined_grid_name(grid)) {
941-
throw std::runtime_error("Xios: Failed to set name for grid '" + gridId + "'");
942-
}
943-
}
944-
945-
/*!
946-
* Associate an axis with a grid
947-
*
948-
* @param the grid ID
949-
* @param the axis ID
950-
*/
951-
void Xios::gridAddAxis(const std::string gridId, const std::string axisId)
952-
{
953-
xios::CAxis* axis = getAxis(axisId);
954-
cxios_xml_tree_add_axistogrid(getGrid(gridId), &axis, axisId.c_str(), axisId.length());
955-
}
956-
957-
/*!
958-
* Get all axis IDs associated with a given grid
959-
*
960-
* @param the grid ID
961-
* @return all axis IDs associated with the grid
962-
*/
963-
std::vector<std::string> Xios::getGridAxisIds(const std::string gridId)
964-
{
965-
return getGrid(gridId)->getAxisList();
966-
}
967-
968776
/*!
969777
* Get the field_definition group
970778
*
@@ -1603,7 +1411,7 @@ std::vector<std::string> Xios::fileGetFieldIds(const std::string fileId)
16031411
{
16041412
std::vector<xios::CField*> fields = getFile(fileId)->getAllFields();
16051413
std::vector<std::string> fieldIds(fields.size());
1606-
for (int i = 0; i < fields.size(); i++) {
1414+
for (size_t i = 0; i < fields.size(); i++) {
16071415
fieldIds[i] = fields[i]->getId();
16081416
}
16091417
return fieldIds;

core/src/generate_iodef.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""Generate an XML file for XIOS from a Jinja2 template based on user input."""
2+
3+
import argparse
4+
import os
5+
6+
from jinja2 import Environment, FileSystemLoader
7+
8+
# Parse user input
9+
parser = argparse.ArgumentParser()
10+
parser.add_argument("DGCOMP", type=int, default=6, help="Number of DG components")
11+
parser.add_argument(
12+
"DGSTRESSCOMP", type=int, default=8, help="Number of DG stress components"
13+
)
14+
parser.add_argument("infile", type=str, help="Input filename")
15+
parser.add_argument("outfile", type=str, help="Output filename")
16+
parsed_args = parser.parse_args()
17+
template_dict = vars(parsed_args)
18+
19+
# Set up Jinja2 environment and render template
20+
path, infile = os.path.split(template_dict.pop("infile"))
21+
env = Environment(loader=FileSystemLoader(path), autoescape=True)
22+
template = env.get_template(infile)
23+
24+
# Generate the XML file
25+
outfile = template_dict.pop("outfile")
26+
output = template.render(template_dict)
27+
with open(outfile, "w+") as f:
28+
f.write(output)

0 commit comments

Comments
 (0)