diff --git a/azspiceEnv b/azspiceEnv
index 6f7932d..c0bc22b 100644
--- a/azspiceEnv
+++ b/azspiceEnv
@@ -3,18 +3,12 @@
# `. azspiceEnv`
# load Azure Spice Spack xios module
-module load xios
+module load csc/2025.3.20 gcc/12.2.0 mpich/4.2.3
+module load netcdf-c/4.9.2 netcdf-fortran/4.6.1
+module load xios/2701
-# provide specific sub_paths for the demonstration build
-export XIOS_INCDIR=$XIOS_ROOT/include
-export XIOS_LIBDIR=$XIOS_ROOT/lib
-export XIOS_BINDIR=$XIOS_ROOT/bin
-
-# ensure netcdf is on the LD path & flags
-export LD_LIBRARY_PATH=${NETCDFF_ROOT}/lib:$LD_LIBRARY_PATH
-export LDFLAGS="-L$XIOS_LIBDIR -lxios $(pkg-config --libs netcdf) $(pkg-config --libs netcdf-fortran) -lstdc++"
-
-export FCFLAGS="-g -I$XIOS_INCDIR $(pkg-config --cflags-only-I netcdf) $(pkg-config --cflags-only-I netcdf-fortran)"
+export FCFLAGS="-g $FFLAGS"
+export LDFLAGS="$LDFLAGS -lxios -lnetcdf -lnetcdff -lstdc++"
export FC=mpif90
-
-export MVER=XIOS/trunk@2252
+export XIOS_BINDIR=`which xios_server.exe | xargs dirname`
+export MVER=XIOS2/trunk
diff --git a/xios_examples/basicUGrid/Makefile b/xios_examples/basicUGrid/Makefile
new file mode 100644
index 0000000..b5e888c
--- /dev/null
+++ b/xios_examples/basicUGrid/Makefile
@@ -0,0 +1,34 @@
+# Make file for the write demonstartion XIOS programme
+# Targets provided our detailed below...
+#
+# all: (default) Build the write programme
+# clean: Delete all final products and working files
+# run: run the programme
+#
+# Environment Variables expected by this MakeFile:
+#
+# FC: mpif90
+# FCFLAGS: -g & include files for netcdf & xios
+# LD_FLAGS: for xios, netcdf, netcdff, stfc++
+# LD_LIBRARY_PATH: for netCDF & XIOS libs
+# XIOS_BINDIR: The directory for XIOS binary files
+
+.PHONY: all, clean, run
+
+all: write
+
+# fortran compilation
+%.o: %.F90
+ $(FC) $(FCFLAGS) -c $<
+
+# fortran linking
+write: write.o
+ $(FC) -o write.exe write.o $(LDFLAGS) \
+ && ln -fs $(XIOS_BINDIR)/xios_server.exe .
+
+run:
+ mpiexec -n 1 ./write.exe : -n 1 ./xios_server.exe
+
+# cleanup
+clean:
+ rm -f *.exe *.o *.mod *.MOD *.out *.err
diff --git a/xios_examples/basicUGrid/Readme.md b/xios_examples/basicUGrid/Readme.md
new file mode 100644
index 0000000..a6764ef
--- /dev/null
+++ b/xios_examples/basicUGrid/Readme.md
@@ -0,0 +1,3 @@
+Demonstration of basic UGrid domain specification.
+
+Read mesh from file then create output mesh to add data to.
diff --git a/xios_examples/basicUGrid/__init__.py b/xios_examples/basicUGrid/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xios_examples/basicUGrid/actual_data_output.cdl b/xios_examples/basicUGrid/actual_data_output.cdl
new file mode 100644
index 0000000..c208c23
--- /dev/null
+++ b/xios_examples/basicUGrid/actual_data_output.cdl
@@ -0,0 +1,299 @@
+netcdf data_output {
+dimensions:
+ axis_nbounds = 2 ;
+ Two = 2 ;
+ ncubedsphere_node = 8 ;
+ ncubedsphere_edge = 12 ;
+ ncubedsphere_face = 6 ;
+ ncubedsphere_vertex = 4 ;
+ nmain__domain_undef_id_8_node = 8 ;
+ nmain__domain_undef_id_8_edge = 12 ;
+ nmain__domain_undef_id_6_node = 8 ;
+ time_counter = UNLIMITED ; // (2 currently)
+variables:
+ int cubedsphere ;
+ cubedsphere:cf_role = "mesh_topology" ;
+ cubedsphere:long_name = "Topology data of 2D unstructured mesh" ;
+ cubedsphere:topology_dimension = 2 ;
+ cubedsphere:node_coordinates = "cubedsphere_node_x cubedsphere_node_y" ;
+ cubedsphere:edge_coordinates = "cubedsphere_edge_x cubedsphere_edge_y" ;
+ cubedsphere:edge_node_connectivity = "cubedsphere_edge_nodes" ;
+ cubedsphere:face_edge_connectivity = "cubedsphere_face_edges" ;
+ cubedsphere:edge_face_connectivity = "cubedsphere_edge_face_links" ;
+ cubedsphere:face_face_connectivity = "cubedsphere_face_links" ;
+ cubedsphere:face_coordinates = "cubedsphere_face_x cubedsphere_face_y" ;
+ cubedsphere:face_node_connectivity = "cubedsphere_face_nodes" ;
+ float cubedsphere_node_x(ncubedsphere_node) ;
+ cubedsphere_node_x:standard_name = "longitude" ;
+ cubedsphere_node_x:long_name = "Longitude of mesh nodes." ;
+ cubedsphere_node_x:units = "degrees_east" ;
+ float cubedsphere_node_y(ncubedsphere_node) ;
+ cubedsphere_node_y:standard_name = "latitude" ;
+ cubedsphere_node_y:long_name = "Latitude of mesh nodes." ;
+ cubedsphere_node_y:units = "degrees_north" ;
+ float cubedsphere_edge_x(ncubedsphere_edge) ;
+ cubedsphere_edge_x:standard_name = "longitude" ;
+ cubedsphere_edge_x:long_name = "Characteristic longitude of mesh edges." ;
+ cubedsphere_edge_x:units = "degrees_east" ;
+ float cubedsphere_edge_y(ncubedsphere_edge) ;
+ cubedsphere_edge_y:standard_name = "latitude" ;
+ cubedsphere_edge_y:long_name = "Characteristic latitude of mesh edges." ;
+ cubedsphere_edge_y:units = "degrees_north" ;
+ int cubedsphere_edge_nodes(ncubedsphere_edge, Two) ;
+ cubedsphere_edge_nodes:cf_role = "edge_node_connectivity" ;
+ cubedsphere_edge_nodes:long_name = "Maps every edge/link to two nodes that it connects." ;
+ cubedsphere_edge_nodes:start_index = 0 ;
+ float cubedsphere_face_x(ncubedsphere_face) ;
+ cubedsphere_face_x:standard_name = "longitude" ;
+ cubedsphere_face_x:long_name = "Characteristic longitude of mesh faces." ;
+ cubedsphere_face_x:units = "degrees_east" ;
+ float cubedsphere_face_y(ncubedsphere_face) ;
+ cubedsphere_face_y:standard_name = "latitude" ;
+ cubedsphere_face_y:long_name = "Characteristic latitude of mesh faces." ;
+ cubedsphere_face_y:units = "degrees_north" ;
+ int cubedsphere_face_nodes(ncubedsphere_face, ncubedsphere_vertex) ;
+ cubedsphere_face_nodes:cf_role = "face_node_connectivity" ;
+ cubedsphere_face_nodes:long_name = "Maps every face to its corner nodes." ;
+ cubedsphere_face_nodes:start_index = 0 ;
+ int cubedsphere_face_edges(ncubedsphere_face, ncubedsphere_vertex) ;
+ cubedsphere_face_edges:cf_role = "face_edge_connectivity" ;
+ cubedsphere_face_edges:long_name = "Maps every face to its edges." ;
+ cubedsphere_face_edges:start_index = 0 ;
+ cubedsphere_face_edges:_FillValue = 999999 ;
+ int cubedsphere_edge_face_links(ncubedsphere_edge, Two) ;
+ cubedsphere_edge_face_links:cf_role = "edge_face_connectivity" ;
+ cubedsphere_edge_face_links:long_name = "neighbor faces for edges" ;
+ cubedsphere_edge_face_links:start_index = 0 ;
+ cubedsphere_edge_face_links:_FillValue = -999 ;
+ cubedsphere_edge_face_links:comment = "missing neighbor faces are indicated using _FillValue" ;
+ int cubedsphere_face_links(ncubedsphere_face, ncubedsphere_vertex) ;
+ cubedsphere_face_links:cf_role = "face_face_connectivity" ;
+ cubedsphere_face_links:long_name = "Indicates which other faces neighbor each face" ;
+ cubedsphere_face_links:start_index = 0 ;
+ cubedsphere_face_links:_FillValue = 999999 ;
+ cubedsphere_face_links:flag_values = -1 ;
+ cubedsphere_face_links:flag_meanings = "out_of_mesh" ;
+ int main__domain_undef_id_8 ;
+ main__domain_undef_id_8:cf_role = "mesh_topology" ;
+ main__domain_undef_id_8:long_name = "Topology data of 2D unstructured mesh" ;
+ main__domain_undef_id_8:topology_dimension = 2 ;
+ main__domain_undef_id_8:node_coordinates = "main__domain_undef_id_8_node_x main__domain_undef_id_8_node_y" ;
+ main__domain_undef_id_8:edge_node_connectivity = "main__domain_undef_id_8_edge_nodes" ;
+ main__domain_undef_id_8:edge_coordinates = "main__domain_undef_id_8_edge_x main__domain_undef_id_8_edge_y" ;
+ float main__domain_undef_id_8_node_x(nmain__domain_undef_id_8_node) ;
+ main__domain_undef_id_8_node_x:standard_name = "longitude" ;
+ main__domain_undef_id_8_node_x:long_name = "Longitude of mesh nodes." ;
+ main__domain_undef_id_8_node_x:units = "degrees_east" ;
+ float main__domain_undef_id_8_node_y(nmain__domain_undef_id_8_node) ;
+ main__domain_undef_id_8_node_y:standard_name = "latitude" ;
+ main__domain_undef_id_8_node_y:long_name = "Latitude of mesh nodes." ;
+ main__domain_undef_id_8_node_y:units = "degrees_north" ;
+ float main__domain_undef_id_8_edge_x(nmain__domain_undef_id_8_edge) ;
+ main__domain_undef_id_8_edge_x:standard_name = "longitude" ;
+ main__domain_undef_id_8_edge_x:long_name = "Characteristic longitude of mesh edges." ;
+ main__domain_undef_id_8_edge_x:units = "degrees_east" ;
+ float main__domain_undef_id_8_edge_y(nmain__domain_undef_id_8_edge) ;
+ main__domain_undef_id_8_edge_y:standard_name = "latitude" ;
+ main__domain_undef_id_8_edge_y:long_name = "Characteristic latitude of mesh edges." ;
+ main__domain_undef_id_8_edge_y:units = "degrees_north" ;
+ int main__domain_undef_id_8_edge_nodes(nmain__domain_undef_id_8_edge, Two) ;
+ main__domain_undef_id_8_edge_nodes:cf_role = "edge_node_connectivity" ;
+ main__domain_undef_id_8_edge_nodes:long_name = "Maps every edge/link to two nodes that it connects." ;
+ main__domain_undef_id_8_edge_nodes:start_index = 0 ;
+ int main__domain_undef_id_6 ;
+ main__domain_undef_id_6:cf_role = "mesh_topology" ;
+ main__domain_undef_id_6:long_name = "Topology data of 2D unstructured mesh" ;
+ main__domain_undef_id_6:topology_dimension = 2 ;
+ main__domain_undef_id_6:node_coordinates = "main__domain_undef_id_6_node_x main__domain_undef_id_6_node_y" ;
+ float main__domain_undef_id_6_node_x(nmain__domain_undef_id_6_node) ;
+ main__domain_undef_id_6_node_x:standard_name = "longitude" ;
+ main__domain_undef_id_6_node_x:long_name = "Longitude of mesh nodes." ;
+ main__domain_undef_id_6_node_x:units = "degrees_east" ;
+ float main__domain_undef_id_6_node_y(nmain__domain_undef_id_6_node) ;
+ main__domain_undef_id_6_node_y:standard_name = "latitude" ;
+ main__domain_undef_id_6_node_y:long_name = "Latitude of mesh nodes." ;
+ main__domain_undef_id_6_node_y:units = "degrees_north" ;
+ double time_instant(time_counter) ;
+ time_instant:standard_name = "time" ;
+ time_instant:long_name = "Time axis" ;
+ time_instant:calendar = "gregorian" ;
+ time_instant:units = "seconds since 2022-02-02 12:00:00" ;
+ time_instant:time_origin = "2022-02-02 12:00:00" ;
+ time_instant:bounds = "time_instant_bounds" ;
+ double time_instant_bounds(time_counter, axis_nbounds) ;
+ double time_counter(time_counter) ;
+ time_counter:axis = "T" ;
+ time_counter:standard_name = "time" ;
+ time_counter:long_name = "Time axis" ;
+ time_counter:calendar = "gregorian" ;
+ time_counter:units = "seconds since 2022-02-02 12:00:00" ;
+ time_counter:time_origin = "2022-02-02 12:00:00" ;
+ time_counter:bounds = "time_counter_bounds" ;
+ double time_counter_bounds(time_counter, axis_nbounds) ;
+ double arbitrary_face_data(time_counter, ncubedsphere_face) ;
+ arbitrary_face_data:long_name = "Arbitrary data values" ;
+ arbitrary_face_data:units = "1" ;
+ arbitrary_face_data:mesh = "cubedsphere" ;
+ arbitrary_face_data:location = "face" ;
+ arbitrary_face_data:online_operation = "instant" ;
+ arbitrary_face_data:interval_operation = "1 h" ;
+ arbitrary_face_data:interval_write = "1 h" ;
+ arbitrary_face_data:cell_methods = "time: point" ;
+ arbitrary_face_data:coordinates = "time_instant cubedsphere_face_y cubedsphere_face_x" ;
+ double arbitrary_edge_data(time_counter, nmain__domain_undef_id_8_edge) ;
+ arbitrary_edge_data:long_name = "Arbitrary data values" ;
+ arbitrary_edge_data:units = "1" ;
+ arbitrary_edge_data:mesh = "main__domain_undef_id_8" ;
+ arbitrary_edge_data:location = "edge" ;
+ arbitrary_edge_data:online_operation = "instant" ;
+ arbitrary_edge_data:interval_operation = "1 h" ;
+ arbitrary_edge_data:interval_write = "1 h" ;
+ arbitrary_edge_data:cell_methods = "time: point" ;
+ arbitrary_edge_data:coordinates = "time_instant main__domain_undef_id_8_edge_y main__domain_undef_id_8_edge_x" ;
+ double arbitrary_node_data(time_counter, nmain__domain_undef_id_6_node) ;
+ arbitrary_node_data:long_name = "Arbitrary data values" ;
+ arbitrary_node_data:units = "1" ;
+ arbitrary_node_data:mesh = "main__domain_undef_id_6" ;
+ arbitrary_node_data:location = "node" ;
+ arbitrary_node_data:online_operation = "instant" ;
+ arbitrary_node_data:interval_operation = "1 h" ;
+ arbitrary_node_data:interval_write = "1 h" ;
+ arbitrary_node_data:cell_methods = "time: point" ;
+ arbitrary_node_data:coordinates = "time_instant main__domain_undef_id_6_node_y main__domain_undef_id_6_node_x" ;
+
+// global attributes:
+ :name = "data_output" ;
+ :description = "Created by xios" ;
+ :title = "Created by xios" ;
+ :Conventions = "UGRID" ;
+ :timeStamp = "2025-May-07 14:23:04 GMT" ;
+ :uuid = "02f24392-2446-494f-a1a7-d482900beb2b" ;
+data:
+
+ cubedsphere = _ ;
+
+ cubedsphere_node_x = -45, 45, 135, -135, 45, -45, 135, -135 ;
+
+ cubedsphere_node_y = 35.26439, 35.26439, 35.26439, 35.26439, -35.26439,
+ -35.26439, -35.26439, -35.26439 ;
+
+ cubedsphere_edge_x = 0, -45, 0, 90, 45, 90, 0, 135, 0, -90, -135, -90 ;
+
+ cubedsphere_edge_y = 35.26439, 0, -35.26439, 35.26439, 0, -35.26439,
+ 35.26439, 0, -35.26439, 35.26439, 0, -35.26439 ;
+
+ cubedsphere_edge_nodes =
+ 0, 1,
+ 5, 0,
+ 4, 5,
+ 1, 2,
+ 4, 1,
+ 6, 4,
+ 2, 3,
+ 6, 2,
+ 7, 6,
+ 3, 0,
+ 7, 3,
+ 5, 7 ;
+
+ cubedsphere_face_x = 2.754441e-15, 90, -180, -90, 0, 0 ;
+
+ cubedsphere_face_y = -5.508882e-15, -5.508882e-15, -5.508882e-15,
+ -5.508882e-15, 90, -90 ;
+
+ cubedsphere_face_nodes =
+ 5, 4, 1, 0,
+ 4, 6, 2, 1,
+ 7, 3, 2, 6,
+ 5, 0, 3, 7,
+ 0, 1, 2, 3,
+ 5, 7, 6, 4 ;
+
+ cubedsphere_face_edges =
+ 2, 4, 0, 1,
+ 5, 7, 3, 4,
+ 10, 6, 7, 8,
+ 1, 9, 10, 11,
+ 0, 3, 6, 9,
+ 11, 8, 5, 2 ;
+
+ cubedsphere_edge_face_links =
+ 0, 4,
+ 0, 3,
+ 0, 5,
+ 1, 4,
+ 0, 1,
+ 1, 5,
+ 2, 4,
+ 1, 2,
+ 2, 5,
+ 3, 4,
+ 2, 3,
+ 3, 5 ;
+
+ cubedsphere_face_links =
+ 5, 1, 4, 3,
+ 5, 2, 4, 0,
+ 3, 4, 1, 5,
+ 0, 4, 2, 5,
+ 0, 1, 2, 3,
+ 3, 2, 1, 0 ;
+
+ main__domain_undef_id_8 = _ ;
+
+ main__domain_undef_id_8_node_x = -45, 45, 135, -135, 45, -45, 135, -135 ;
+
+ main__domain_undef_id_8_node_y = 35.26439, 35.26439, 35.26439, 35.26439,
+ -35.26439, -35.26439, -35.26439, -35.26439 ;
+
+ main__domain_undef_id_8_edge_x = 0, -45, 0, 90, 45, 90, 0, 135, 0, -90,
+ -135, -90 ;
+
+ main__domain_undef_id_8_edge_y = 35.26439, 0, -35.26439, 35.26439, 0,
+ -35.26439, 35.26439, 0, -35.26439, 35.26439, 0, -35.26439 ;
+
+ main__domain_undef_id_8_edge_nodes =
+ 0, 1,
+ 5, 0,
+ 4, 5,
+ 1, 2,
+ 4, 1,
+ 6, 4,
+ 2, 3,
+ 6, 2,
+ 7, 6,
+ 3, 0,
+ 7, 3,
+ 5, 7 ;
+
+ main__domain_undef_id_6 = _ ;
+
+ main__domain_undef_id_6_node_x = -45, 45, 135, -135, 45, -45, 135, -135 ;
+
+ main__domain_undef_id_6_node_y = 35.26439, 35.26439, 35.26439, 35.26439,
+ -35.26439, -35.26439, -35.26439, -35.26439 ;
+
+ time_instant = 27133200, 27136800 ;
+
+ time_instant_bounds =
+ 27133200, 27133200,
+ 27136800, 27136800 ;
+
+ time_counter = 27133200, 27136800 ;
+
+ time_counter_bounds =
+ 27133200, 27133200,
+ 27136800, 27136800 ;
+
+ arbitrary_face_data =
+ 10, 10, 10, 10, 10, 10,
+ 20, 20, 20, 20, 20, 20 ;
+
+ arbitrary_edge_data =
+ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
+ 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200 ;
+
+ arbitrary_node_data =
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2 ;
+}
diff --git a/xios_examples/basicUGrid/axis_check.xml b/xios_examples/basicUGrid/axis_check.xml
new file mode 100644
index 0000000..874d40f
--- /dev/null
+++ b/xios_examples/basicUGrid/axis_check.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xios_examples/basicUGrid/cubedsphere_mesh.cdl b/xios_examples/basicUGrid/cubedsphere_mesh.cdl
new file mode 100644
index 0000000..469f3e1
--- /dev/null
+++ b/xios_examples/basicUGrid/cubedsphere_mesh.cdl
@@ -0,0 +1,133 @@
+netcdf cubedsphere_mesh {
+dimensions:
+ One = 1 ;
+ Two = 2 ;
+ Four = 4 ;
+ nmesh_node = 8 ;
+ nmesh_edge = 12 ;
+ nmesh_face = 6 ;
+variables:
+ int mesh ;
+ mesh:cf_role = "mesh_topology" ;
+ mesh:geometry = "spherical" ;
+ mesh:topology = "periodic" ;
+ mesh:coord_sys = "ll" ;
+ mesh:periodic_x = "T" ;
+ mesh:periodic_y = "T" ;
+ mesh:constructor_inputs = "edge_cells=1;smooth_passes=0" ;
+ mesh:max_stencil_depth = 0 ;
+ mesh:n_mesh_maps = 0 ;
+ mesh:long_name = "Topology data of 2D unstructured mesh" ;
+ mesh:topology_dimension = 2 ;
+ mesh:node_coordinates = "mesh_node_x mesh_node_y" ;
+ mesh:face_coordinates = "mesh_face_x mesh_face_y" ;
+ mesh:face_node_connectivity = "mesh_face_nodes" ;
+ mesh:edge_node_connectivity = "mesh_edge_nodes" ;
+ mesh:face_edge_connectivity = "mesh_face_edges" ;
+ mesh:face_face_connectivity = "mesh_face_links" ;
+ mesh:north_pole = 0., 90. ;
+ mesh:null_island = 0., 0. ;
+ mesh:equatorial_latitude = 0. ;
+ mesh:domain_extents = "mesh_domain_extents" ;
+ mesh:npanels = 6 ;
+ int mesh_face_nodes(nmesh_face, Four) ;
+ mesh_face_nodes:cf_role = "face_node_connectivity" ;
+ mesh_face_nodes:long_name = "Maps every quadrilateral face to its four corner nodes." ;
+ mesh_face_nodes:start_index = 1 ;
+ int mesh_face_edges(nmesh_face, Four) ;
+ mesh_face_edges:cf_role = "face_edge_connectivity" ;
+ mesh_face_edges:long_name = "Maps every quadrilateral face to its four edges." ;
+ mesh_face_edges:start_index = 1 ;
+ int mesh_face_links(nmesh_face, Four) ;
+ mesh_face_links:cf_role = "face_face_connectivity" ;
+ mesh_face_links:long_name = "Indicates which other faces neighbour each face." ;
+ mesh_face_links:start_index = 1 ;
+ mesh_face_links:_FillValue = -9999 ;
+ int mesh_edge_nodes(nmesh_edge, Two) ;
+ mesh_edge_nodes:cf_role = "edge_node_connectivity" ;
+ mesh_edge_nodes:long_name = "Maps every edge to the two nodes that it connects." ;
+ mesh_edge_nodes:start_index = 1 ;
+ double mesh_node_x(nmesh_node) ;
+ mesh_node_x:standard_name = "longitude" ;
+ mesh_node_x:long_name = "longitude of 2D mesh nodes." ;
+ mesh_node_x:units = "degrees_east" ;
+ double mesh_node_y(nmesh_node) ;
+ mesh_node_y:standard_name = "latitude" ;
+ mesh_node_y:long_name = "latitude of 2D mesh nodes." ;
+ mesh_node_y:units = "degrees_north" ;
+ double mesh_face_x(nmesh_face) ;
+ mesh_face_x:standard_name = "longitude" ;
+ mesh_face_x:long_name = "longitude of 2D face centres" ;
+ mesh_face_x:units = "degrees_east" ;
+ double mesh_face_y(nmesh_face) ;
+ mesh_face_y:standard_name = "latitude" ;
+ mesh_face_y:long_name = "latitude of 2D face centres" ;
+ mesh_face_y:units = "degrees_north" ;
+ double mesh_domain_extents(Four, Two) ;
+ double node_empty_data_container(nmesh_node) ;
+ node_empty_data_container:coordinates = "mesh_node_y mesh_node_x" ;
+ double face_empty_data_container(nmesh_face) ;
+ face_empty_data_container:coordinates = "mesh_face_y mesh_face_x" ;
+ double edge_empty_data_container(nmesh_edge) ;
+
+// global attributes:
+ :Conventions = "CF-1.6 UGRID-1.0" ;
+data:
+
+ mesh = _ ;
+
+ mesh_face_nodes =
+ 6, 5, 2, 1,
+ 5, 7, 3, 2,
+ 8, 4, 3, 7,
+ 6, 1, 4, 8,
+ 1, 2, 3, 4,
+ 6, 8, 7, 5 ;
+
+ mesh_face_edges =
+ 2, 3, 5, 1,
+ 5, 6, 8, 4,
+ 9, 11, 7, 8,
+ 12, 2, 10, 11,
+ 10, 1, 4, 7,
+ 3, 12, 9, 6 ;
+
+ mesh_face_links =
+ 4, 6, 2, 5,
+ 1, 6, 3, 5,
+ 6, 4, 5, 2,
+ 6, 1, 5, 3,
+ 4, 1, 2, 3,
+ 1, 4, 3, 2 ;
+
+ mesh_edge_nodes =
+ 1, 2,
+ 6, 1,
+ 5, 6,
+ 2, 3,
+ 5, 2,
+ 7, 5,
+ 3, 4,
+ 7, 3,
+ 8, 7,
+ 4, 1,
+ 8, 4,
+ 6, 8 ;
+
+ mesh_node_x = -45, 45, 135, -135, 45, -45, 135, -135 ;
+
+ mesh_node_y = 35.2643896827547, 35.2643896827547, 35.2643896827547,
+ 35.2643896827547, -35.2643896827547, -35.2643896827547,
+ -35.2643896827547, -35.2643896827547 ;
+
+ mesh_face_x = 2.75444115227293e-15, 90, -180, -90, 0, 0 ;
+
+ mesh_face_y = -5.50888230454586e-15, -5.50888230454586e-15,
+ -5.50888230454586e-15, -5.50888230454586e-15, 90, -90 ;
+
+ mesh_domain_extents =
+ -180, -90,
+ 180, -90,
+ 180, 90,
+ -180, 90 ;
+}
diff --git a/xios_examples/basicUGrid/expected_domain_output.cdl b/xios_examples/basicUGrid/expected_domain_output.cdl
new file mode 100644
index 0000000..06afd0f
--- /dev/null
+++ b/xios_examples/basicUGrid/expected_domain_output.cdl
@@ -0,0 +1,178 @@
+netcdf planar_mesh {
+dimensions:
+ One = 1 ;
+ Two = 2 ;
+ Four = 4 ;
+ nmesh_node = 21 ;
+ nmesh_edge = 32 ;
+ nmesh_face = 12 ;
+variables:
+ int mesh ;
+ mesh:cf_role = "mesh_topology" ;
+ mesh:geometry = "planar" ;
+ mesh:topology = "non_periodic" ;
+ mesh:coord_sys = "xyz" ;
+ mesh:periodic_x = "F" ;
+ mesh:periodic_y = "F" ;
+ mesh:constructor_inputs = "geometry=planar;topology=non_periodic;coord_sys=xyz;edge_cells_x=6;edge_cells_y=2;periodic_x=F;periodic_y=F;domain_size=[6.00,2.00];domain_centre=[9000.00,7000.00]" ;
+ mesh:max_stencil_depth = 0 ;
+ mesh:n_mesh_maps = 0 ;
+ mesh:long_name = "Topology data of 2D unstructured mesh" ;
+ mesh:topology_dimension = 2 ;
+ mesh:node_coordinates = "mesh_node_x mesh_node_y" ;
+ mesh:face_coordinates = "mesh_face_x mesh_face_y" ;
+ mesh:face_node_connectivity = "mesh_face_nodes" ;
+ mesh:edge_node_connectivity = "mesh_edge_nodes" ;
+ mesh:face_edge_connectivity = "mesh_face_edges" ;
+ mesh:face_face_connectivity = "mesh_face_links" ;
+ mesh:domain_extents = "mesh_domain_extents" ;
+ mesh:npanels = 1 ;
+ int mesh_face_nodes(nmesh_face, Four) ;
+ mesh_face_nodes:cf_role = "face_node_connectivity" ;
+ mesh_face_nodes:long_name = "Maps every quadrilateral face to its four corner nodes." ;
+ mesh_face_nodes:start_index = 1 ;
+ int mesh_face_edges(nmesh_face, Four) ;
+ mesh_face_edges:cf_role = "face_edge_connectivity" ;
+ mesh_face_edges:long_name = "Maps every quadrilateral face to its four edges." ;
+ mesh_face_edges:start_index = 1 ;
+ int mesh_face_links(nmesh_face, Four) ;
+ mesh_face_links:cf_role = "face_face_connectivity" ;
+ mesh_face_links:long_name = "Indicates which other faces neighbour each face." ;
+ mesh_face_links:start_index = 1 ;
+ mesh_face_links:_FillValue = -9999 ;
+ int mesh_edge_nodes(nmesh_edge, Two) ;
+ mesh_edge_nodes:cf_role = "edge_node_connectivity" ;
+ mesh_edge_nodes:long_name = "Maps every edge to the two nodes that it connects." ;
+ mesh_edge_nodes:start_index = 1 ;
+ double mesh_node_x(nmesh_node) ;
+ mesh_node_x:standard_name = "projection_x_coordinate" ;
+ mesh_node_x:long_name = "x coordinate of 2D mesh nodes." ;
+ mesh_node_x:units = "m" ;
+ double mesh_node_y(nmesh_node) ;
+ mesh_node_y:standard_name = "projection_y_coordinate" ;
+ mesh_node_y:long_name = "y coordinate of 2D mesh nodes." ;
+ mesh_node_y:units = "m" ;
+ double mesh_face_x(nmesh_face) ;
+ mesh_face_x:standard_name = "projection_x_coordinate" ;
+ mesh_face_x:long_name = "x coordinate of 2D face centres" ;
+ mesh_face_x:units = "m" ;
+ double mesh_face_y(nmesh_face) ;
+ mesh_face_y:standard_name = "projection_y_coordinate" ;
+ mesh_face_y:long_name = "y coordinate of 2D face centres" ;
+ mesh_face_y:units = "m" ;
+ double mesh_domain_extents(Four, Two) ;
+ double node_empty_data_container(nmesh_node) ;
+ node_empty_data_container:coordinates = "mesh_node_y mesh_node_x" ;
+ double face_empty_data_container(nmesh_face) ;
+ face_empty_data_container:coordinates = "mesh_face_y mesh_face_x" ;
+ double edge_empty_data_container(nmesh_edge) ;
+
+// global attributes:
+ :Conventions = "CF-1.6 UGRID-1.0" ;
+
+data:
+
+ mesh = _ ;
+
+ mesh_face_nodes =
+ 1, 2, 3, 4,
+ 2, 5, 6, 3,
+ 5, 7, 8, 6,
+ 7, 9, 10, 8,
+ 9, 11, 12, 10,
+ 11, 13, 14, 12,
+ 15, 16, 2, 1,
+ 16, 17, 5, 2,
+ 17, 18, 7, 5,
+ 18, 19, 9, 7,
+ 19, 20, 11, 9,
+ 20, 21, 13, 11 ;
+
+ mesh_face_edges =
+ 1, 2, 3, 4,
+ 3, 5, 6, 7,
+ 6, 8, 9, 10,
+ 9, 11, 12, 13,
+ 12, 14, 15, 16,
+ 15, 17, 18, 19,
+ 20, 21, 22, 2,
+ 22, 23, 24, 5,
+ 24, 25, 26, 8,
+ 26, 27, 28, 11,
+ 28, 29, 30, 14,
+ 30, 31, 32, 17 ;
+
+ mesh_face_links =
+ _, 7, 2, _,
+ 1, 8, 3, _,
+ 2, 9, 4, _,
+ 3, 10, 5, _,
+ 4, 11, 6, _,
+ 5, 12, _, _,
+ _, _, 8, 1,
+ 7, _, 9, 2,
+ 8, _, 10, 3,
+ 9, _, 11, 4,
+ 10, _, 12, 5,
+ 11, _, _, 6 ;
+
+ mesh_edge_nodes =
+ 4, 1,
+ 1, 2,
+ 3, 2,
+ 4, 3,
+ 2, 5,
+ 6, 5,
+ 3, 6,
+ 5, 7,
+ 8, 7,
+ 6, 8,
+ 7, 9,
+ 10, 9,
+ 8, 10,
+ 9, 11,
+ 12, 11,
+ 10, 12,
+ 11, 13,
+ 14, 13,
+ 12, 14,
+ 1, 15,
+ 15, 16,
+ 2, 16,
+ 16, 17,
+ 5, 17,
+ 17, 18,
+ 7, 18,
+ 18, 19,
+ 9, 19,
+ 19, 20,
+ 11, 20,
+ 20, 21,
+ 13, 21 ;
+
+ // mesh_node_x = 8997, 8998, 8998, 8997, 8999, 8999, 9000, 9000, 9001, 9001,
+ // 9002, 9002, 9003, 9003, 8997, 8998, 8999, 9000, 9001, 9002, 9003 ;
+
+ // mesh_node_y = 7000, 7000, 7001, 7001, 7000, 7001, 7000, 7001, 7000, 7001,
+ // 7000, 7001, 7000, 7001, 6999, 6999, 6999, 6999, 6999, 6999, 6999 ;
+
+ // mesh_face_x = 8997.5, 8998.5, 8999.5, 9000.5, 9001.5, 9002.5, 8997.5,
+ // 8998.5, 8999.5, 9000.5, 9001.5, 9002.5 ;
+
+ // mesh_face_y = 7000.5, 7000.5, 7000.5, 7000.5, 7000.5, 7000.5, 6999.5,
+ // 6999.5, 6999.5, 6999.5, 6999.5, 6999.5 ;
+
+ mesh_node_x = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20;
+
+ mesh_node_y = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20;
+
+ mesh_face_x = 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, 11.5;
+
+ mesh_face_y = 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, 11.5;
+
+ mesh_domain_extents =
+ 8997, 6999,
+ 9003, 6999,
+ 9003, 7001,
+ 8997, 7001 ;
+}
diff --git a/xios_examples/basicUGrid/iodef.xml b/xios_examples/basicUGrid/iodef.xml
new file mode 100644
index 0000000..37acd4d
--- /dev/null
+++ b/xios_examples/basicUGrid/iodef.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/xios_examples/basicUGrid/main.xml b/xios_examples/basicUGrid/main.xml
new file mode 100644
index 0000000..0bebc4b
--- /dev/null
+++ b/xios_examples/basicUGrid/main.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xios_examples/basicUGrid/test_write_metadata.py b/xios_examples/basicUGrid/test_write_metadata.py
new file mode 100644
index 0000000..3709515
--- /dev/null
+++ b/xios_examples/basicUGrid/test_write_metadata.py
@@ -0,0 +1,67 @@
+import copy
+import glob
+import netCDF4
+import numpy as np
+import os
+import subprocess
+import unittest
+
+import xios_examples.shared_testing as xshared
+
+this_path = os.path.realpath(__file__)
+this_dir = os.path.dirname(this_path)
+
+class TestWriteMetadata(xshared._TestCase):
+ test_dir = this_dir
+ transient_inputs = ['cubedsphere_mesh.nc']
+ transient_outputs = ['data_output.nc']
+ executable = './write.exe'
+ mesh_file_cdl = 'planar_mesh.cdl'
+
+ @classmethod
+ def make_a_write_test(cls, inf, nclients=1, nservers=1):
+ """
+ this function makes a test case and returns it as a test function,
+ suitable to be dynamically added to a TestCase for running.
+
+ """
+ # always copy for value, don't pass by reference.
+ infcp = copy.copy(inf)
+ # expected by the fortran XIOS program's main.xml
+ inputfile = cls.transient_inputs[0]
+ outputfile = cls.transient_outputs[0]
+ def test_write_metadata(self):
+ # create a netCDF file using nc_method
+ cls.make_netcdf(infcp, inputfile)
+ cls.run_mpi_xios(nclients=nclients, nservers=nservers)
+
+ # load the result netCDF file
+ runfile = '{}/{}'.format(cls.test_dir, outputfile)
+ assert(os.path.exists(runfile))
+ run_cmd = ['ncdump', runfile]
+ ncd = subprocess.run(run_cmd, check=True, capture_output=True)
+
+ with open(f'{this_dir}/expected_domain_output.cdl') as fin:
+ exptd = fin.read()
+ emsg = ''
+ for eline, aline in zip(exptd.split('\n'),
+ ncd.stdout.decode("utf-8").split('\n')):
+ if eline != aline:
+ # skip timestamp and uuid
+ # until we work out how not to set these
+ if not (':timeStamp' in eline or ':uuid' in eline):
+ emsg += eline + '\n' + aline + '\n\tmismatch\n\n'
+
+ #self.assertFalse(emsg, msg='\n\n' + emsg)
+ return test_write_metadata
+
+
+f = f'{this_dir}/cubedsphere_mesh.cdl'
+# unique name for the test
+tname = 'test_{}'.format(os.path.splitext(os.path.basename(f))[0])
+# add the test as an attribute (function) to the test class
+
+# !!! remove this limit on success
+if os.environ.get('MVER', '').startswith('XIOS3/trunk'):
+ setattr(TestWriteMetadata, tname,
+ TestWriteMetadata.make_a_write_test(f))
diff --git a/xios_examples/basicUGrid/write.F90 b/xios_examples/basicUGrid/write.F90
new file mode 100644
index 0000000..a2441b1
--- /dev/null
+++ b/xios_examples/basicUGrid/write.F90
@@ -0,0 +1,283 @@
+!-----------------------------------------------------------------------------
+! (C) Crown copyright 2025 Met Office. All rights reserved.
+! The file LICENCE, distributed with this code, contains details of the terms
+! under which the code may be used.
+!-----------------------------------------------------------------------------
+!> Programme to just write data with coordinate system definition metadata.
+!>
+program write
+ use xios
+ use mpi
+ USE, INTRINSIC :: ISO_C_BINDING
+ use iso_fortran_env, only : output_unit
+
+ implicit none
+
+ integer :: comm = -1
+ integer :: rank = -1
+ integer :: npar = 0
+
+ call initialise()
+ call simulate()
+ call finalise()
+contains
+
+ subroutine initialise()
+
+ type(xios_date) :: origin
+ type(xios_date) :: start
+ type(xios_duration) :: tstep
+ integer :: mpi_error
+ integer :: len_node, ni_node, ibegin_node
+ integer :: len_face, ni_face, ibegin_face
+ integer :: len_edge, ni_edge, ibegin_edge
+ integer :: fcount, ecount, fvcount, evcount
+ integer :: fvertex_len, evertex_len
+ double precision, dimension (:), allocatable :: node_y_vals, node_x_vals, &
+ face_y_vals, face_x_vals, &
+ edge_y_vals, edge_x_vals
+ double precision, dimension (:,:), allocatable :: face_y_bounds, face_x_bounds, &
+ edge_y_bounds, edge_x_bounds, &
+ mesh_face_nodes_dbl, mesh_edge_nodes_dbl
+ integer, dimension (:,:), allocatable :: mesh_face_nodes, mesh_edge_nodes
+ character(len=64) :: t_origin
+
+ origin = xios_date(2022, 2, 2, 12, 0, 0)
+ start = xios_date(2022, 12, 13, 12, 0, 0)
+ tstep = xios_hour
+
+ ! Initialise MPI and XIOS
+ call MPI_INIT(mpi_error)
+ call xios_initialize('client', return_comm=comm)
+
+ call MPI_Comm_rank(comm, rank, mpi_error)
+ call MPI_Comm_size(comm, npar, mpi_error)
+
+ ! use the axis_check context to obtain sizing information on all arrays
+ ! for use in defining the main context interpretation
+
+ print *, "initialising axis_check"
+ flush(output_unit)
+ call xios_context_initialize('axis_check', comm)
+ call xios_set_time_origin(origin)
+ call xios_set_start_date(start)
+ call xios_set_timestep(tstep)
+
+ call xios_close_context_definition()
+ print *, "axis_check initialised"
+ flush(output_unit)
+
+ ! fetch sizes of axes from the input file for allocate
+ call xios_get_domain_attr('cndata::', ni_glo=len_node, ni=ni_node, ibegin=ibegin_node)
+ call xios_get_domain_attr('cfdata::', ni_glo=len_face, ni=ni_face, ibegin=ibegin_face)
+ call xios_get_domain_attr('cedata::', ni_glo=len_edge, ni=ni_edge, ibegin=ibegin_edge)
+ call xios_get_axis_attr('mesh_face_nodes::[0]', n_glo=fvertex_len)
+ print *, "mfn: ", fvertex_len
+ call xios_get_axis_attr('mesh_edge_nodes::[0]', n_glo=evertex_len)
+ print *, "mfn: ", evertex_len
+
+ allocate ( node_x_vals(len_node) )
+ allocate ( node_y_vals(len_node) )
+ allocate ( face_x_vals(len_face) )
+ allocate ( face_y_vals(len_face) )
+ allocate ( edge_x_vals(len_edge) )
+ allocate ( edge_y_vals(len_edge) )
+ allocate ( mesh_face_nodes(fvertex_len, len_face) )
+ allocate ( mesh_face_nodes_dbl(fvertex_len, len_face) )
+ allocate ( mesh_edge_nodes(evertex_len, len_edge) )
+ allocate ( mesh_edge_nodes_dbl(evertex_len, len_edge) )
+ allocate ( face_y_bounds(fvertex_len, len_face) )
+ allocate ( face_x_bounds(fvertex_len, len_face) )
+ allocate ( edge_y_bounds(evertex_len, len_edge) )
+ allocate ( edge_x_bounds(evertex_len, len_edge) )
+ print *, 'len_node= ', len_node
+ print *, 'len_face= ', len_face
+ print *, 'len_edge= ', len_edge
+ print *, 'fvertices: ', fvertex_len
+ flush(output_unit)
+
+
+ ! fetch coordinate value arrays from the input file
+ call xios_get_domain_attr('cndata::', lonvalue_1d=node_x_vals, latvalue_1d=node_y_vals)
+ call xios_get_domain_attr('cfdata::', lonvalue_1d=face_x_vals, latvalue_1d=face_y_vals)
+ !call xios_get_domain_attr('cedata::', lonvalue_1d=edge_x_vals, latvalue_1d=edge_y_vals)
+ print *, 'node xvals= ', node_x_vals, 'node yvals= ', node_y_vals
+ print *, 'face xvals= ', face_x_vals, 'face yvals= ', face_y_vals
+ !print *, 'edge xvals= ', edge_x_vals, 'edge yvals= ', edge_y_vals
+ flush(output_unit)
+
+ call xios_recv_field("mesh_face_nodes", mesh_face_nodes_dbl)
+ mesh_face_nodes = int(mesh_face_nodes_dbl)
+
+ do fvcount=1, fvertex_len
+ do fcount=1, len_face
+ ! print *, mesh_face_nodes(fvcount, fcount)
+ face_x_bounds(fvcount, fcount) = node_x_vals(mesh_face_nodes(fvcount, fcount))
+ face_y_bounds(fvcount, fcount) = node_y_vals(mesh_face_nodes(fvcount, fcount))
+ end do
+
+ end do
+ print *, "face_x_bounds: ", shape(face_x_bounds), face_x_bounds
+ print *, "face_y_bounds: ", shape(face_y_bounds), face_y_bounds
+
+ call xios_recv_field("mesh_edge_nodes", mesh_edge_nodes_dbl)
+ mesh_edge_nodes = int(mesh_edge_nodes_dbl)
+ print *, "mesh_edge_nodes", mesh_edge_nodes
+
+ do ecount=1, len_edge
+ do evcount=1, evertex_len
+ edge_x_bounds(evcount, ecount) = node_x_vals(mesh_edge_nodes(evcount, ecount))
+ edge_y_bounds(evcount, ecount) = node_y_vals(mesh_edge_nodes(evcount, ecount))
+ end do
+ edge_x_vals(ecount) = edge_x_bounds(1, ecount) - 0.5 * &
+ (edge_x_bounds(1, ecount) - edge_x_bounds(2, ecount))
+ edge_y_vals(ecount) = edge_y_bounds(1, ecount) - 0.5 * &
+ (edge_y_bounds(1, ecount) - edge_y_bounds(2, ecount))
+ end do
+ print *, "edge_x: ", shape(edge_x_vals), edge_x_vals
+ print *, "edge_x_bounds: ", shape(edge_x_bounds)
+ print *, edge_x_bounds(1, :)
+ print *, edge_x_bounds(2, :)
+ print *, "edge_y: ", shape(edge_y_vals), edge_y_vals
+ print *, "edge_y_bounds: ", shape(edge_y_bounds)
+ print *, edge_y_bounds(1, :)
+ print *, edge_y_bounds(2, :)
+
+
+ ! finalise axis_check context, it no longer in use
+ ! call xios_set_current_context('axis_check')
+ call xios_context_finalize()
+ print *, "axis check finalised successfully"
+ flush(output_unit)
+
+ ! initialize the main context for interacting with the data.
+ print *, "initialising main context"
+ flush(output_unit)
+ call xios_context_initialize('main', comm)
+
+ call xios_set_time_origin(origin)
+ call xios_set_start_date(start)
+ call xios_set_timestep(tstep)
+
+ ! define the horizontal domain and vertical axis using the input file values
+
+ print *, "now define domain configuration from input mesh"
+ flush(output_unit)
+ call xios_set_domain_attr("node_domain", ni_glo=len_node, ni=ni_node, ibegin=ibegin_node, &
+ nj_glo=len_node, nj=ni_node, jbegin=ibegin_node, &
+ latvalue_1d=node_y_vals, lonvalue_1d=node_x_vals)
+ call xios_set_domain_attr("face_domain", ni_glo=len_face, ni=ni_face, ibegin=ibegin_face, &
+ nj_glo=len_face, nj=ni_face, jbegin=ibegin_face, &
+ latvalue_1d=face_y_vals, lonvalue_1d=face_x_vals, &
+ bounds_lat_1d=face_y_bounds, bounds_lon_1d=face_x_bounds)
+ call xios_set_domain_attr("edge_domain", ni_glo=len_edge, ni=ni_edge, ibegin=ibegin_edge, &
+ nj_glo=len_edge, nj=ni_edge, jbegin=ibegin_edge, &
+ latvalue_1d=edge_y_vals, lonvalue_1d=edge_x_vals, &
+ bounds_lat_1d=edge_y_bounds, bounds_lon_1d=edge_x_bounds)
+
+ print *, "ready to close main context definition"
+ flush(output_unit)
+
+
+ call xios_close_context_definition()
+ print *, "main context defined successfully"
+ flush(output_unit)
+ call xios_get_domain_attr('ndata::', lonvalue_1d=node_x_vals, latvalue_1d=node_y_vals)
+ print *, "output node x = ", node_x_vals
+ print *, "output node y = ", node_y_vals
+ flush(output_unit)
+ call xios_get_domain_attr('fdata::', lonvalue_1d=face_x_vals, latvalue_1d=face_y_vals)
+ print *, "output face x = ", face_x_vals
+ print *, "output face y = ", face_y_vals
+ flush(output_unit)
+
+ deallocate (node_x_vals)
+ deallocate (node_y_vals)
+ deallocate (face_x_vals)
+ deallocate (face_y_vals)
+ deallocate (edge_x_vals)
+ deallocate (edge_y_vals)
+ deallocate (face_y_bounds)
+ deallocate (face_x_bounds)
+ deallocate (edge_y_bounds)
+ deallocate (edge_x_bounds)
+ deallocate (mesh_face_nodes_dbl)
+ deallocate (mesh_face_nodes)
+ print *, "deallocations"
+ end subroutine initialise
+
+ subroutine finalise()
+
+ integer :: mpi_error
+
+ ! Finalise all XIOS contexts and MPI
+ print *, "finalising"
+ flush(output_unit)
+ call xios_set_current_context('main')
+ call xios_context_finalize()
+ print *, "finalised main"
+ flush(output_unit)
+ call xios_finalize()
+ print *, "finalised xios"
+ flush(output_unit)
+ call MPI_Finalize(mpi_error)
+
+ end subroutine finalise
+
+ subroutine simulate()
+
+ type(xios_date) :: start
+ type(xios_date) :: current
+ integer :: ts
+ integer :: i
+ integer :: len_node
+ integer :: len_edge
+ integer :: len_face
+ double precision :: frtv
+
+ ! Allocatable arrays, size is taken from input file
+ double precision, dimension (:), allocatable :: node_data, face_data, edge_data
+
+ print *, "simulation"
+ ! obtain sizing of the grid for the array allocation
+ call xios_get_domain_attr('ndata::', ni_glo=len_node)
+ call xios_get_domain_attr('fdata::', ni_glo=len_face)
+ call xios_get_domain_attr('edata::', ni_glo=len_edge)
+
+ allocate ( node_data(len_node) )
+ allocate ( face_data(len_face) )
+ allocate ( edge_data(len_edge) )
+ print *, 'len_node= ', len_node
+ print *, 'len_face= ', len_face
+ print *, 'len_edge= ', len_edge
+ flush(output_unit)
+
+ do ts=1, 2
+ call xios_update_calendar(ts)
+ call xios_get_current_date(current)
+ do i=1, len_node
+ node_data(i) = ts
+ end do
+ call xios_send_field('ndata', node_data)
+
+ do i=1, len_face
+ face_data(i) = 10 * ts
+ end do
+ call xios_send_field('fdata', face_data)
+
+ do i=1, len_edge
+ edge_data(i) = 100 * ts
+ end do
+ call xios_send_field('edata', edge_data)
+
+ enddo
+
+ deallocate (node_data)
+ deallocate (face_data)
+ deallocate (edge_data)
+ print *, "fields sent, exiting simulation"
+ flush(output_unit)
+
+ end subroutine simulate
+
+end program write
diff --git a/xios_examples/basicUGrid/xios.xml b/xios_examples/basicUGrid/xios.xml
new file mode 100644
index 0000000..9ec1df0
--- /dev/null
+++ b/xios_examples/basicUGrid/xios.xml
@@ -0,0 +1,22 @@
+
+
+
+
+ performance
+
+
+ 1.0
+
+
+
+
+ true
+
+ 100
+
+
+ true
+
+
+
+
diff --git a/xios_examples/gen_netcdf.py b/xios_examples/gen_netcdf.py
index 1aab521..1e847d6 100644
--- a/xios_examples/gen_netcdf.py
+++ b/xios_examples/gen_netcdf.py
@@ -136,24 +136,24 @@ def create_ncfile_unstructured(ncmeshout, meshin_file, meshin_varname, nlev, fun
for face_coord in meshin_var.face_coordinates.split(" "):
face_coordvar = ncmeshin.variables[face_coord]
- if face_coordvar.standard_name == 'longitude':
+ if face_coordvar.standard_name in ['longitude', 'projection_x_coordinate']:
face_lon = face_coordvar[:]
- elif face_coordvar.standard_name == 'latitude':
+ elif face_coordvar.standard_name in ['latitude', 'projection_y_coordinate']:
face_lat = face_coordvar[:]
for node_coord in meshin_var.node_coordinates.split(" "):
node_coordvar = ncmeshin.variables[node_coord]
- if node_coordvar.standard_name == 'longitude':
+ if node_coordvar.standard_name in ['longitude', 'projection_x_coordinate']:
node_lon = node_coordvar[:]
- elif node_coordvar.standard_name == 'latitude':
+ elif node_coordvar.standard_name in ['latitude', 'projection_y_coordinate']:
node_lat = node_coordvar[:]
if 'edge_coordinates' in meshin_var.ncattrs():
for edge_coord in meshin_var.edge_coordinates.split(" "):
edge_coordvar = ncmeshin.variables[edge_coord]
- if edge_coordvar.standard_name == 'longitude':
+ if edge_coordvar.standard_name in ['longitude', 'projection_x_coordinate']:
edge_lon = edge_coordvar[:]
- elif edge_coordvar.standard_name == 'latitude':
+ elif edge_coordvar.standard_name in ['latitude', 'projection_y_coordinate']:
edge_lat = edge_coordvar[:]
meshout_varname = 'Mesh2d'
diff --git a/xios_examples/setAttrCoord/Makefile b/xios_examples/setAttrCoord/Makefile
new file mode 100644
index 0000000..b5e888c
--- /dev/null
+++ b/xios_examples/setAttrCoord/Makefile
@@ -0,0 +1,34 @@
+# Make file for the write demonstartion XIOS programme
+# Targets provided our detailed below...
+#
+# all: (default) Build the write programme
+# clean: Delete all final products and working files
+# run: run the programme
+#
+# Environment Variables expected by this MakeFile:
+#
+# FC: mpif90
+# FCFLAGS: -g & include files for netcdf & xios
+# LD_FLAGS: for xios, netcdf, netcdff, stfc++
+# LD_LIBRARY_PATH: for netCDF & XIOS libs
+# XIOS_BINDIR: The directory for XIOS binary files
+
+.PHONY: all, clean, run
+
+all: write
+
+# fortran compilation
+%.o: %.F90
+ $(FC) $(FCFLAGS) -c $<
+
+# fortran linking
+write: write.o
+ $(FC) -o write.exe write.o $(LDFLAGS) \
+ && ln -fs $(XIOS_BINDIR)/xios_server.exe .
+
+run:
+ mpiexec -n 1 ./write.exe : -n 1 ./xios_server.exe
+
+# cleanup
+clean:
+ rm -f *.exe *.o *.mod *.MOD *.out *.err
diff --git a/xios_examples/setAttrCoord/Readme.md b/xios_examples/setAttrCoord/Readme.md
new file mode 100644
index 0000000..9f77c70
--- /dev/null
+++ b/xios_examples/setAttrCoord/Readme.md
@@ -0,0 +1,3 @@
+Demonstration of user customised metadata from XIOS
+
+This includes spatial and temproal metadata definitions and global attributes.
diff --git a/xios_examples/setAttrCoord/__init__.py b/xios_examples/setAttrCoord/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xios_examples/setAttrCoord/axis_check.xml b/xios_examples/setAttrCoord/axis_check.xml
new file mode 100644
index 0000000..31def66
--- /dev/null
+++ b/xios_examples/setAttrCoord/axis_check.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xios_examples/setAttrCoord/data_input.cdl b/xios_examples/setAttrCoord/data_input.cdl
new file mode 100644
index 0000000..efe1dad
--- /dev/null
+++ b/xios_examples/setAttrCoord/data_input.cdl
@@ -0,0 +1,37 @@
+netcdf domain_input {
+dimensions:
+ x = 5 ;
+ y = 5 ;
+ alt = 1 ;
+variables:
+ float x(x) ;
+ x:standard_name = "projection_x_coordinate" ;
+ x:units = "m";
+ float y(y) ;
+ y:standard_name = "projection_y_coordinate" ;
+ y:units = "m";
+ float alt(alt) ;
+ alt:standard_name = "altitude" ;
+ alt:units = "metres";
+ double original_data(alt, y, x) ;
+ original_data:long_name = "data values" ;
+ original_data:units = "1";
+
+// global attributes:
+ :title = "Input data for XIOS output." ;
+
+data:
+
+ x = 303000, 305000, 307000, 309000, 311000 ;
+
+ y = 107000, 109000, 111000, 113000, 115000 ;
+
+ alt = 50 ;
+
+ original_data = 0, 2, 4, 6, 8,
+ 2, 4, 6, 8, 10,
+ 4, 6, 8, 10, 12,
+ 6, 8, 10, 12, 14,
+ 8, 10, 12, 14, 16 ;
+
+}
diff --git a/xios_examples/setAttrCoord/expected_domain_output.cdl b/xios_examples/setAttrCoord/expected_domain_output.cdl
new file mode 100644
index 0000000..7caa26e
--- /dev/null
+++ b/xios_examples/setAttrCoord/expected_domain_output.cdl
@@ -0,0 +1,112 @@
+netcdf data_output {
+dimensions:
+ axis_nbounds = 2 ;
+ x = 5 ;
+ y = 5 ;
+ alt = 1 ;
+ time_counter = UNLIMITED ; // (2 currently)
+variables:
+ float x(x) ;
+ x:name = "x" ;
+ x:standard_name = "projection_x_coordinate" ;
+ x:long_name = "x coordinate of projection" ;
+ x:units = "m" ;
+ float y(y) ;
+ y:name = "y" ;
+ y:standard_name = "projection_y_coordinate" ;
+ y:long_name = "y coordinate of projection" ;
+ y:units = "m" ;
+ float alt(alt) ;
+ alt:name = "alt" ;
+ alt:standard_name = "altitude" ;
+ alt:units = "metres" ;
+ double time_instant(time_counter) ;
+ time_instant:standard_name = "time" ;
+ time_instant:long_name = "Time axis" ;
+ time_instant:calendar = "gregorian" ;
+ time_instant:units = "seconds since 2022-02-02 12:00:00" ;
+ time_instant:time_origin = "2022-02-02 12:00:00" ;
+ time_instant:bounds = "time_instant_bounds" ;
+ double time_instant_bounds(time_counter, axis_nbounds) ;
+ double time_counter(time_counter) ;
+ time_counter:axis = "T" ;
+ time_counter:standard_name = "time" ;
+ time_counter:long_name = "Time axis" ;
+ time_counter:calendar = "gregorian" ;
+ time_counter:units = "seconds since 2022-02-02 12:00:00" ;
+ time_counter:time_origin = "2022-02-02 12:00:00" ;
+ time_counter:bounds = "time_counter_bounds" ;
+ double time_counter_bounds(time_counter, axis_nbounds) ;
+ double original_data(time_counter, alt, y, x) ;
+ original_data:long_name = "Arbitrary data values" ;
+ original_data:units = "1" ;
+ original_data:online_operation = "instant" ;
+ original_data:interval_operation = "1 h" ;
+ original_data:interval_write = "1 h" ;
+ original_data:cell_methods = "time: point" ;
+ original_data:coordinates = "forecast_reference_time" ;
+ original_data:grid_mapping = "osgb: x y egm2008:alt" ;
+ float forecast_reference_time ;
+ forecast_reference_time:standard_name = "forecast_reference_time" ;
+ forecast_reference_time:units = "seconds since 2022-02-02 12:00:00" ;
+ forecast_reference_time:online_operation = "once" ;
+ forecast_reference_time:coordinates = "" ;
+ forecast_reference_time:calendar = "gregorian" ;
+ short osgb ;
+ osgb:online_operation = "once" ;
+ osgb:_FillValue = -32767s ;
+ osgb:missing_value = -32767s ;
+ osgb:coordinates = "" ;
+ osgb:crs_wkt = "PROJCRS[\"OSGB36 / British National Grid\",BASEGEOGCRS[\"OSGB36\",DATUM[\"Ordnance Survey of Great Britain 1936\",ELLIPSOID[\"Airy 1830\",6377563.396,299.3249646,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7001]],ID[\"EPSG\",6277]],ID[\"EPSG\",4277]],CONVERSION[\"British National Grid\",METHOD[\"Transverse Mercator\",ID[\"EPSG\",9807]],PARAMETER[\"Latitude of natural origin\",49,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8801]],PARAMETER[\"Longitude of natural origin\",-2,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8802]],PARAMETER[\"Scale factor at natural origin\",0.9996012717,SCALEUNIT[\"unity\",1,ID[\"EPSG\",9201]],ID[\"EPSG\",8805]],PARAMETER[\"False easting\",400000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8806]],PARAMETER[\"False northing\",-100000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8807]],ID[\"EPSG\",19916]],CS[Cartesian,2,ID[\"EPSG\",4400]],AXIS[\"Easting (E)\",east],AXIS[\"Northing (N)\",north],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",27700]]" ;
+ short egm2008 ;
+ egm2008:online_operation = "once" ;
+ egm2008:_FillValue = -32767s ;
+ egm2008:missing_value = -32767s ;
+ egm2008:coordinates = "" ;
+ egm2008:crs_wkt = "VERTCRS[\"EGM2008 height\",VDATUM[\"EGM2008 geoid\",ID[\"EPSG\",1027]],CS[vertical,1,ID[\"EPSG\",6499]],AXIS[\"Gravity-related height (H)\",up],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],GEOIDMODEL[\"WGS 84 to EGM2008 height (1)\",ID[\"EPSG\",3858]],GEOIDMODEL[\"WGS 84 to EGM2008 height (2)\",ID[\"EPSG\",3859]],ID[\"EPSG\",3855]]" ;
+
+// global attributes:
+ :description = "LFRic file format v0.2.0" ;
+ :Conventions = "CF-1.6, UGRID" ;
+ :timeStamp = "2025-Apr-24 13:52:34 GMT" ;
+ :uuid = "d55b8279-19df-4311-9c07-fcf91dd62385" ;
+ :name = "Attribute demonstration" ;
+ :title = "Attribute demonstration" ;
+data:
+
+ x = 303000, 305000, 307000, 309000, 311000 ;
+
+ y = 107000, 109000, 111000, 113000, 115000 ;
+
+ alt = 50 ;
+
+ time_instant = 27133200, 27136800 ;
+
+ time_instant_bounds =
+ 27133200, 27133200,
+ 27136800, 27136800 ;
+
+ time_counter = 27133200, 27136800 ;
+
+ time_counter_bounds =
+ 27133200, 27133200,
+ 27136800, 27136800 ;
+
+ original_data =
+ 0, 2, 4, 6, 8,
+ 2, 4, 6, 8, 10,
+ 4, 6, 8, 10, 12,
+ 6, 8, 10, 12, 14,
+ 8, 10, 12, 14, 16,
+ 0, 2, 4, 6, 8,
+ 2, 4, 6, 8, 10,
+ 4, 6, 8, 10, 12,
+ 6, 8, 10, 12, 14,
+ 8, 10, 12, 14, 16 ;
+
+ forecast_reference_time = 2.71296e+07 ;
+
+ osgb = _ ;
+
+ egm2008 = _ ;
+}
diff --git a/xios_examples/setAttrCoord/iodef.xml b/xios_examples/setAttrCoord/iodef.xml
new file mode 100644
index 0000000..37acd4d
--- /dev/null
+++ b/xios_examples/setAttrCoord/iodef.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/xios_examples/setAttrCoord/main.xml b/xios_examples/setAttrCoord/main.xml
new file mode 100644
index 0000000..7c4b251
--- /dev/null
+++ b/xios_examples/setAttrCoord/main.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ forecast_reference_time
+ osgb: x y egm2008:alt
+
+
+
+
+
+
+ gregorian
+
+
+ PROJCRS["OSGB36 / British National Grid",BASEGEOGCRS["OSGB36",DATUM["Ordnance Survey of Great Britain 1936",ELLIPSOID["Airy 1830",6377563.396,299.3249646,LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",7001]],ID["EPSG",6277]],ID["EPSG",4277]],CONVERSION["British National Grid",METHOD["Transverse Mercator",ID["EPSG",9807]],PARAMETER["Latitude of natural origin",49,ANGLEUNIT["degree",0.0174532925199433,ID["EPSG",9102]],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",-2,ANGLEUNIT["degree",0.0174532925199433,ID["EPSG",9102]],ID["EPSG",8802]],PARAMETER["Scale factor at natural origin",0.9996012717,SCALEUNIT["unity",1,ID["EPSG",9201]],ID["EPSG",8805]],PARAMETER["False easting",400000,LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",8806]],PARAMETER["False northing",-100000,LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",8807]],ID["EPSG",19916]],CS[Cartesian,2,ID["EPSG",4400]],AXIS["Easting (E)",east],AXIS["Northing (N)",north],LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",27700]]
+
+
+ VERTCRS["EGM2008 height",VDATUM["EGM2008 geoid",ID["EPSG",1027]],CS[vertical,1,ID["EPSG",6499]],AXIS["Gravity-related height (H)",up],LENGTHUNIT["metre",1,ID["EPSG",9001]],GEOIDMODEL["WGS 84 to EGM2008 height (1)",ID["EPSG",3858]],GEOIDMODEL["WGS 84 to EGM2008 height (2)",ID["EPSG",3859]],ID["EPSG",3855]]
+
+
+
+
+
+ Attribute demonstration
+
+ Attribute demonstration
+
+
+
+
+
+
diff --git a/xios_examples/setAttrCoord/test_write_metadata.py b/xios_examples/setAttrCoord/test_write_metadata.py
new file mode 100644
index 0000000..d9d012d
--- /dev/null
+++ b/xios_examples/setAttrCoord/test_write_metadata.py
@@ -0,0 +1,66 @@
+import copy
+import glob
+import netCDF4
+import numpy as np
+import os
+import subprocess
+import unittest
+
+import xios_examples.shared_testing as xshared
+
+this_path = os.path.realpath(__file__)
+this_dir = os.path.dirname(this_path)
+
+class TestWriteMetadata(xshared._TestCase):
+ test_dir = this_dir
+ transient_inputs = ['data_input.nc']
+ transient_outputs = ['data_output.nc']
+ executable = './write.exe'
+ rtol = 5e-03
+
+ @classmethod
+ def make_a_write_test(cls, inf, nc_method='cdl_files',
+ nclients=1, nservers=1):
+ """
+ this function makes a test case and returns it as a test function,
+ suitable to be dynamically added to a TestCase for running.
+
+ """
+ # always copy for value, don't pass by reference.
+ infcp = copy.copy(inf)
+ # expected by the fortran XIOS program's main.xml
+ inputfile = cls.transient_inputs[0]
+ outputfile = cls.transient_outputs[0]
+ def test_write_metadata(self):
+ # create a netCDF file using nc_method
+ cls.make_netcdf(infcp, inputfile, nc_method=nc_method)
+ cls.run_mpi_xios(nclients=nclients, nservers=nservers)
+
+ # load the result netCDF file
+ runfile = '{}/{}'.format(cls.test_dir, outputfile)
+ assert(os.path.exists(runfile))
+ run_cmd = ['ncdump', runfile]
+ ncd = subprocess.run(run_cmd, check=True, capture_output=True)
+
+ with open(f'{this_dir}/expected_domain_output.cdl') as fin:
+ exptd = fin.read()
+ emsg = ''
+ for eline, aline in zip(exptd.split('\n'),
+ ncd.stdout.decode("utf-8").split('\n')):
+ if eline != aline:
+ # skip timestamp and uuid
+ # until we work out how not to set these
+ if not (':timeStamp' in eline or ':uuid' in eline):
+ emsg += eline + '\n' + aline + '\n\tmismatch\n\n'
+
+ self.assertFalse(emsg, msg='\n\n' + emsg)
+ return test_write_metadata
+
+
+f = f'{this_dir}/data_input.cdl'
+# unique name for the test
+tname = 'test_{}'.format(os.path.splitext(os.path.basename(f))[0])
+# add the test as an attribute (function) to the test class
+
+setattr(TestWriteMetadata, tname,
+ TestWriteMetadata.make_a_write_test(f))
diff --git a/xios_examples/setAttrCoord/write.F90 b/xios_examples/setAttrCoord/write.F90
new file mode 100644
index 0000000..a125a3d
--- /dev/null
+++ b/xios_examples/setAttrCoord/write.F90
@@ -0,0 +1,164 @@
+!-----------------------------------------------------------------------------
+! (C) Crown copyright 2025 Met Office. All rights reserved.
+! The file LICENCE, distributed with this code, contains details of the terms
+! under which the code may be used.
+!-----------------------------------------------------------------------------
+!> Programme to just write data with coordinate system definition metadata.
+!>
+program write
+ use xios
+ use mpi
+ USE, INTRINSIC :: ISO_C_BINDING
+
+ implicit none
+
+ integer :: comm = -1
+ integer :: rank = -1
+ integer :: npar = 0
+
+ call initialise()
+ call simulate()
+ call finalise()
+contains
+
+ subroutine initialise()
+
+ type(xios_date) :: origin
+ type(xios_date) :: start
+ type(xios_duration) :: tstep
+ integer :: mpi_error
+ integer :: lenx
+ integer :: leny
+ integer :: lenz
+ double precision, dimension (:), allocatable :: y_vals, x_vals, altvals
+ character(len=64) :: t_origin
+
+ origin = xios_date(2022, 2, 2, 12, 0, 0)
+ start = xios_date(2022, 12, 13, 12, 0, 0)
+ tstep = xios_hour
+
+ ! Initialise MPI and XIOS
+ call MPI_INIT(mpi_error)
+ call xios_initialize('client', return_comm=comm)
+
+ call MPI_Comm_rank(comm, rank, mpi_error)
+ call MPI_Comm_size(comm, npar, mpi_error)
+
+ ! use the axis_check context to obtain sizing information on all arrays
+ ! for use in defining the main context interpretation
+
+ call xios_context_initialize('axis_check', comm)
+ call xios_set_time_origin(origin)
+ call xios_set_start_date(start)
+ call xios_set_timestep(tstep)
+
+ call xios_close_context_definition()
+
+ ! fetch sizes of axes from the input file for allocate
+ call xios_get_axis_attr('x', n_glo=lenx)
+ call xios_get_axis_attr('y', n_glo=leny)
+ call xios_get_axis_attr('alt', n_glo=lenz)
+
+ allocate ( x_vals(lenx) )
+ allocate ( y_vals(leny) )
+ allocate ( altvals(lenz) )
+
+ ! fetch coordinate value arrays from the input file
+ call xios_get_axis_attr('x', value=x_vals)
+ call xios_get_axis_attr('y', value=y_vals)
+ call xios_get_axis_attr('alt', value=altvals)
+
+ ! finalise axis_check context, no longer in use
+ call xios_set_current_context('axis_check')
+ call xios_context_finalize()
+
+ ! initialize the main context for interacting with the data.
+ call xios_context_initialize('main', comm)
+
+ call xios_set_time_origin(origin)
+ call xios_set_start_date(start)
+ call xios_set_timestep(tstep)
+
+ ! define the horizontal domain and vertical axis using the input file
+ ! call xios_set_domain_attr("original_domain", ni_glo=lenx, ni=lenx, nj_glo=leny, nj=leny, ibegin=0, jbegin=0)
+ ! call xios_set_domain_attr("original_domain", lonvalue_1d=lonvals, latvalue_1d=latvals)
+
+ call xios_set_axis_attr("x", n_glo=lenx, n=lenx, begin=0)
+ call xios_set_axis_attr("x", value=x_vals)
+ call xios_set_axis_attr("y", n_glo=leny, n=leny, begin=0)
+ call xios_set_axis_attr("y", value=y_vals)
+ call xios_set_axis_attr("alt", n_glo=lenz, n=lenz, begin=0)
+ call xios_set_axis_attr("alt", value=altvals)
+ call xios_set_file_attr("data_output", convention_str="CF-1.6, UGRID")
+ call xios_set_file_attr("data_output", description="LFRic file format v0.2.0")
+ if (.true.) then
+ call xios_set_axis_attr("x", standard_name="projection_x_coordinate", &
+ unit="m", long_name="x coordinate of projection")
+ call xios_set_axis_attr("y", standard_name="projection_y_coordinate", &
+ unit="m", long_name="y coordinate of projection")
+ end if
+ call xios_date_convert_to_string(origin, t_origin)
+ call xios_set_field_attr("frt", unit="seconds since "//t_origin)
+
+ call xios_close_context_definition()
+
+ end subroutine initialise
+
+ subroutine finalise()
+
+ integer :: mpi_error
+
+ ! Finalise all XIOS contexts and MPI
+ call xios_set_current_context('main')
+ call xios_context_finalize()
+
+ call xios_finalize()
+ call MPI_Finalize(mpi_error)
+
+ end subroutine finalise
+
+ subroutine simulate()
+
+ type(xios_date) :: start
+ type(xios_date) :: current
+ integer :: ts
+ integer :: lenx
+ integer :: leny
+ integer :: lenz
+ double precision :: frtv
+
+ ! Allocatable arrays, size is taken from input file
+ double precision, dimension (:,:,:), allocatable :: inodata
+
+ ! obtain sizing of the grid for the array allocation
+
+ call xios_get_axis_attr('x', n_glo=lenx)
+ call xios_get_axis_attr('y', n_glo=leny)
+ call xios_get_axis_attr('alt', n_glo=lenz)
+
+ allocate ( inodata(lenz, leny, lenx) )
+
+ ! Load data from the input file
+ call xios_recv_field('odatain', inodata)
+
+ do ts=1, 2
+ call xios_update_calendar(ts)
+ call xios_get_current_date(current)
+ ! hardcoded 9
+ frtv=9
+ ! send frt on ts0 only
+ if (ts == 1) then
+ call xios_get_start_date(start)
+ frtv = dble(xios_date_convert_to_seconds(start))
+ call xios_send_field("frt", frtv)
+ end if
+
+ ! Send (copy) the original data to the output file.
+ call xios_send_field('odata', inodata)
+ enddo
+
+ deallocate (inodata)
+
+ end subroutine simulate
+
+end program write
diff --git a/xios_examples/setAttrCoord/xios.xml b/xios_examples/setAttrCoord/xios.xml
new file mode 100644
index 0000000..9ec1df0
--- /dev/null
+++ b/xios_examples/setAttrCoord/xios.xml
@@ -0,0 +1,22 @@
+
+
+
+
+ performance
+
+
+ 1.0
+
+
+
+
+ true
+
+ 100
+
+
+ true
+
+
+
+
diff --git a/xios_examples/setAttrCoordUGrid/Makefile b/xios_examples/setAttrCoordUGrid/Makefile
new file mode 100644
index 0000000..b5e888c
--- /dev/null
+++ b/xios_examples/setAttrCoordUGrid/Makefile
@@ -0,0 +1,34 @@
+# Make file for the write demonstartion XIOS programme
+# Targets provided our detailed below...
+#
+# all: (default) Build the write programme
+# clean: Delete all final products and working files
+# run: run the programme
+#
+# Environment Variables expected by this MakeFile:
+#
+# FC: mpif90
+# FCFLAGS: -g & include files for netcdf & xios
+# LD_FLAGS: for xios, netcdf, netcdff, stfc++
+# LD_LIBRARY_PATH: for netCDF & XIOS libs
+# XIOS_BINDIR: The directory for XIOS binary files
+
+.PHONY: all, clean, run
+
+all: write
+
+# fortran compilation
+%.o: %.F90
+ $(FC) $(FCFLAGS) -c $<
+
+# fortran linking
+write: write.o
+ $(FC) -o write.exe write.o $(LDFLAGS) \
+ && ln -fs $(XIOS_BINDIR)/xios_server.exe .
+
+run:
+ mpiexec -n 1 ./write.exe : -n 1 ./xios_server.exe
+
+# cleanup
+clean:
+ rm -f *.exe *.o *.mod *.MOD *.out *.err
diff --git a/xios_examples/setAttrCoordUGrid/Readme.md b/xios_examples/setAttrCoordUGrid/Readme.md
new file mode 100644
index 0000000..9f77c70
--- /dev/null
+++ b/xios_examples/setAttrCoordUGrid/Readme.md
@@ -0,0 +1,3 @@
+Demonstration of user customised metadata from XIOS
+
+This includes spatial and temproal metadata definitions and global attributes.
diff --git a/xios_examples/setAttrCoordUGrid/__init__.py b/xios_examples/setAttrCoordUGrid/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xios_examples/setAttrCoordUGrid/axis_check.xml b/xios_examples/setAttrCoordUGrid/axis_check.xml
new file mode 100644
index 0000000..c34814e
--- /dev/null
+++ b/xios_examples/setAttrCoordUGrid/axis_check.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xios_examples/setAttrCoordUGrid/expected_domain_output.cdl b/xios_examples/setAttrCoordUGrid/expected_domain_output.cdl
new file mode 100644
index 0000000..7caa26e
--- /dev/null
+++ b/xios_examples/setAttrCoordUGrid/expected_domain_output.cdl
@@ -0,0 +1,112 @@
+netcdf data_output {
+dimensions:
+ axis_nbounds = 2 ;
+ x = 5 ;
+ y = 5 ;
+ alt = 1 ;
+ time_counter = UNLIMITED ; // (2 currently)
+variables:
+ float x(x) ;
+ x:name = "x" ;
+ x:standard_name = "projection_x_coordinate" ;
+ x:long_name = "x coordinate of projection" ;
+ x:units = "m" ;
+ float y(y) ;
+ y:name = "y" ;
+ y:standard_name = "projection_y_coordinate" ;
+ y:long_name = "y coordinate of projection" ;
+ y:units = "m" ;
+ float alt(alt) ;
+ alt:name = "alt" ;
+ alt:standard_name = "altitude" ;
+ alt:units = "metres" ;
+ double time_instant(time_counter) ;
+ time_instant:standard_name = "time" ;
+ time_instant:long_name = "Time axis" ;
+ time_instant:calendar = "gregorian" ;
+ time_instant:units = "seconds since 2022-02-02 12:00:00" ;
+ time_instant:time_origin = "2022-02-02 12:00:00" ;
+ time_instant:bounds = "time_instant_bounds" ;
+ double time_instant_bounds(time_counter, axis_nbounds) ;
+ double time_counter(time_counter) ;
+ time_counter:axis = "T" ;
+ time_counter:standard_name = "time" ;
+ time_counter:long_name = "Time axis" ;
+ time_counter:calendar = "gregorian" ;
+ time_counter:units = "seconds since 2022-02-02 12:00:00" ;
+ time_counter:time_origin = "2022-02-02 12:00:00" ;
+ time_counter:bounds = "time_counter_bounds" ;
+ double time_counter_bounds(time_counter, axis_nbounds) ;
+ double original_data(time_counter, alt, y, x) ;
+ original_data:long_name = "Arbitrary data values" ;
+ original_data:units = "1" ;
+ original_data:online_operation = "instant" ;
+ original_data:interval_operation = "1 h" ;
+ original_data:interval_write = "1 h" ;
+ original_data:cell_methods = "time: point" ;
+ original_data:coordinates = "forecast_reference_time" ;
+ original_data:grid_mapping = "osgb: x y egm2008:alt" ;
+ float forecast_reference_time ;
+ forecast_reference_time:standard_name = "forecast_reference_time" ;
+ forecast_reference_time:units = "seconds since 2022-02-02 12:00:00" ;
+ forecast_reference_time:online_operation = "once" ;
+ forecast_reference_time:coordinates = "" ;
+ forecast_reference_time:calendar = "gregorian" ;
+ short osgb ;
+ osgb:online_operation = "once" ;
+ osgb:_FillValue = -32767s ;
+ osgb:missing_value = -32767s ;
+ osgb:coordinates = "" ;
+ osgb:crs_wkt = "PROJCRS[\"OSGB36 / British National Grid\",BASEGEOGCRS[\"OSGB36\",DATUM[\"Ordnance Survey of Great Britain 1936\",ELLIPSOID[\"Airy 1830\",6377563.396,299.3249646,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",7001]],ID[\"EPSG\",6277]],ID[\"EPSG\",4277]],CONVERSION[\"British National Grid\",METHOD[\"Transverse Mercator\",ID[\"EPSG\",9807]],PARAMETER[\"Latitude of natural origin\",49,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8801]],PARAMETER[\"Longitude of natural origin\",-2,ANGLEUNIT[\"degree\",0.0174532925199433,ID[\"EPSG\",9102]],ID[\"EPSG\",8802]],PARAMETER[\"Scale factor at natural origin\",0.9996012717,SCALEUNIT[\"unity\",1,ID[\"EPSG\",9201]],ID[\"EPSG\",8805]],PARAMETER[\"False easting\",400000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8806]],PARAMETER[\"False northing\",-100000,LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",8807]],ID[\"EPSG\",19916]],CS[Cartesian,2,ID[\"EPSG\",4400]],AXIS[\"Easting (E)\",east],AXIS[\"Northing (N)\",north],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],ID[\"EPSG\",27700]]" ;
+ short egm2008 ;
+ egm2008:online_operation = "once" ;
+ egm2008:_FillValue = -32767s ;
+ egm2008:missing_value = -32767s ;
+ egm2008:coordinates = "" ;
+ egm2008:crs_wkt = "VERTCRS[\"EGM2008 height\",VDATUM[\"EGM2008 geoid\",ID[\"EPSG\",1027]],CS[vertical,1,ID[\"EPSG\",6499]],AXIS[\"Gravity-related height (H)\",up],LENGTHUNIT[\"metre\",1,ID[\"EPSG\",9001]],GEOIDMODEL[\"WGS 84 to EGM2008 height (1)\",ID[\"EPSG\",3858]],GEOIDMODEL[\"WGS 84 to EGM2008 height (2)\",ID[\"EPSG\",3859]],ID[\"EPSG\",3855]]" ;
+
+// global attributes:
+ :description = "LFRic file format v0.2.0" ;
+ :Conventions = "CF-1.6, UGRID" ;
+ :timeStamp = "2025-Apr-24 13:52:34 GMT" ;
+ :uuid = "d55b8279-19df-4311-9c07-fcf91dd62385" ;
+ :name = "Attribute demonstration" ;
+ :title = "Attribute demonstration" ;
+data:
+
+ x = 303000, 305000, 307000, 309000, 311000 ;
+
+ y = 107000, 109000, 111000, 113000, 115000 ;
+
+ alt = 50 ;
+
+ time_instant = 27133200, 27136800 ;
+
+ time_instant_bounds =
+ 27133200, 27133200,
+ 27136800, 27136800 ;
+
+ time_counter = 27133200, 27136800 ;
+
+ time_counter_bounds =
+ 27133200, 27133200,
+ 27136800, 27136800 ;
+
+ original_data =
+ 0, 2, 4, 6, 8,
+ 2, 4, 6, 8, 10,
+ 4, 6, 8, 10, 12,
+ 6, 8, 10, 12, 14,
+ 8, 10, 12, 14, 16,
+ 0, 2, 4, 6, 8,
+ 2, 4, 6, 8, 10,
+ 4, 6, 8, 10, 12,
+ 6, 8, 10, 12, 14,
+ 8, 10, 12, 14, 16 ;
+
+ forecast_reference_time = 2.71296e+07 ;
+
+ osgb = _ ;
+
+ egm2008 = _ ;
+}
diff --git a/xios_examples/setAttrCoordUGrid/iodef.xml b/xios_examples/setAttrCoordUGrid/iodef.xml
new file mode 100644
index 0000000..37acd4d
--- /dev/null
+++ b/xios_examples/setAttrCoordUGrid/iodef.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/xios_examples/setAttrCoordUGrid/main.xml b/xios_examples/setAttrCoordUGrid/main.xml
new file mode 100644
index 0000000..2a28807
--- /dev/null
+++ b/xios_examples/setAttrCoordUGrid/main.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ forecast_reference_time
+ osgb: x y egm2008:alt
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Attribute demonstration
+
+ Attribute demonstration
+
+
+
+
+
+
diff --git a/xios_examples/setAttrCoordUGrid/planar_mesh.cdl b/xios_examples/setAttrCoordUGrid/planar_mesh.cdl
new file mode 100644
index 0000000..06afd0f
--- /dev/null
+++ b/xios_examples/setAttrCoordUGrid/planar_mesh.cdl
@@ -0,0 +1,178 @@
+netcdf planar_mesh {
+dimensions:
+ One = 1 ;
+ Two = 2 ;
+ Four = 4 ;
+ nmesh_node = 21 ;
+ nmesh_edge = 32 ;
+ nmesh_face = 12 ;
+variables:
+ int mesh ;
+ mesh:cf_role = "mesh_topology" ;
+ mesh:geometry = "planar" ;
+ mesh:topology = "non_periodic" ;
+ mesh:coord_sys = "xyz" ;
+ mesh:periodic_x = "F" ;
+ mesh:periodic_y = "F" ;
+ mesh:constructor_inputs = "geometry=planar;topology=non_periodic;coord_sys=xyz;edge_cells_x=6;edge_cells_y=2;periodic_x=F;periodic_y=F;domain_size=[6.00,2.00];domain_centre=[9000.00,7000.00]" ;
+ mesh:max_stencil_depth = 0 ;
+ mesh:n_mesh_maps = 0 ;
+ mesh:long_name = "Topology data of 2D unstructured mesh" ;
+ mesh:topology_dimension = 2 ;
+ mesh:node_coordinates = "mesh_node_x mesh_node_y" ;
+ mesh:face_coordinates = "mesh_face_x mesh_face_y" ;
+ mesh:face_node_connectivity = "mesh_face_nodes" ;
+ mesh:edge_node_connectivity = "mesh_edge_nodes" ;
+ mesh:face_edge_connectivity = "mesh_face_edges" ;
+ mesh:face_face_connectivity = "mesh_face_links" ;
+ mesh:domain_extents = "mesh_domain_extents" ;
+ mesh:npanels = 1 ;
+ int mesh_face_nodes(nmesh_face, Four) ;
+ mesh_face_nodes:cf_role = "face_node_connectivity" ;
+ mesh_face_nodes:long_name = "Maps every quadrilateral face to its four corner nodes." ;
+ mesh_face_nodes:start_index = 1 ;
+ int mesh_face_edges(nmesh_face, Four) ;
+ mesh_face_edges:cf_role = "face_edge_connectivity" ;
+ mesh_face_edges:long_name = "Maps every quadrilateral face to its four edges." ;
+ mesh_face_edges:start_index = 1 ;
+ int mesh_face_links(nmesh_face, Four) ;
+ mesh_face_links:cf_role = "face_face_connectivity" ;
+ mesh_face_links:long_name = "Indicates which other faces neighbour each face." ;
+ mesh_face_links:start_index = 1 ;
+ mesh_face_links:_FillValue = -9999 ;
+ int mesh_edge_nodes(nmesh_edge, Two) ;
+ mesh_edge_nodes:cf_role = "edge_node_connectivity" ;
+ mesh_edge_nodes:long_name = "Maps every edge to the two nodes that it connects." ;
+ mesh_edge_nodes:start_index = 1 ;
+ double mesh_node_x(nmesh_node) ;
+ mesh_node_x:standard_name = "projection_x_coordinate" ;
+ mesh_node_x:long_name = "x coordinate of 2D mesh nodes." ;
+ mesh_node_x:units = "m" ;
+ double mesh_node_y(nmesh_node) ;
+ mesh_node_y:standard_name = "projection_y_coordinate" ;
+ mesh_node_y:long_name = "y coordinate of 2D mesh nodes." ;
+ mesh_node_y:units = "m" ;
+ double mesh_face_x(nmesh_face) ;
+ mesh_face_x:standard_name = "projection_x_coordinate" ;
+ mesh_face_x:long_name = "x coordinate of 2D face centres" ;
+ mesh_face_x:units = "m" ;
+ double mesh_face_y(nmesh_face) ;
+ mesh_face_y:standard_name = "projection_y_coordinate" ;
+ mesh_face_y:long_name = "y coordinate of 2D face centres" ;
+ mesh_face_y:units = "m" ;
+ double mesh_domain_extents(Four, Two) ;
+ double node_empty_data_container(nmesh_node) ;
+ node_empty_data_container:coordinates = "mesh_node_y mesh_node_x" ;
+ double face_empty_data_container(nmesh_face) ;
+ face_empty_data_container:coordinates = "mesh_face_y mesh_face_x" ;
+ double edge_empty_data_container(nmesh_edge) ;
+
+// global attributes:
+ :Conventions = "CF-1.6 UGRID-1.0" ;
+
+data:
+
+ mesh = _ ;
+
+ mesh_face_nodes =
+ 1, 2, 3, 4,
+ 2, 5, 6, 3,
+ 5, 7, 8, 6,
+ 7, 9, 10, 8,
+ 9, 11, 12, 10,
+ 11, 13, 14, 12,
+ 15, 16, 2, 1,
+ 16, 17, 5, 2,
+ 17, 18, 7, 5,
+ 18, 19, 9, 7,
+ 19, 20, 11, 9,
+ 20, 21, 13, 11 ;
+
+ mesh_face_edges =
+ 1, 2, 3, 4,
+ 3, 5, 6, 7,
+ 6, 8, 9, 10,
+ 9, 11, 12, 13,
+ 12, 14, 15, 16,
+ 15, 17, 18, 19,
+ 20, 21, 22, 2,
+ 22, 23, 24, 5,
+ 24, 25, 26, 8,
+ 26, 27, 28, 11,
+ 28, 29, 30, 14,
+ 30, 31, 32, 17 ;
+
+ mesh_face_links =
+ _, 7, 2, _,
+ 1, 8, 3, _,
+ 2, 9, 4, _,
+ 3, 10, 5, _,
+ 4, 11, 6, _,
+ 5, 12, _, _,
+ _, _, 8, 1,
+ 7, _, 9, 2,
+ 8, _, 10, 3,
+ 9, _, 11, 4,
+ 10, _, 12, 5,
+ 11, _, _, 6 ;
+
+ mesh_edge_nodes =
+ 4, 1,
+ 1, 2,
+ 3, 2,
+ 4, 3,
+ 2, 5,
+ 6, 5,
+ 3, 6,
+ 5, 7,
+ 8, 7,
+ 6, 8,
+ 7, 9,
+ 10, 9,
+ 8, 10,
+ 9, 11,
+ 12, 11,
+ 10, 12,
+ 11, 13,
+ 14, 13,
+ 12, 14,
+ 1, 15,
+ 15, 16,
+ 2, 16,
+ 16, 17,
+ 5, 17,
+ 17, 18,
+ 7, 18,
+ 18, 19,
+ 9, 19,
+ 19, 20,
+ 11, 20,
+ 20, 21,
+ 13, 21 ;
+
+ // mesh_node_x = 8997, 8998, 8998, 8997, 8999, 8999, 9000, 9000, 9001, 9001,
+ // 9002, 9002, 9003, 9003, 8997, 8998, 8999, 9000, 9001, 9002, 9003 ;
+
+ // mesh_node_y = 7000, 7000, 7001, 7001, 7000, 7001, 7000, 7001, 7000, 7001,
+ // 7000, 7001, 7000, 7001, 6999, 6999, 6999, 6999, 6999, 6999, 6999 ;
+
+ // mesh_face_x = 8997.5, 8998.5, 8999.5, 9000.5, 9001.5, 9002.5, 8997.5,
+ // 8998.5, 8999.5, 9000.5, 9001.5, 9002.5 ;
+
+ // mesh_face_y = 7000.5, 7000.5, 7000.5, 7000.5, 7000.5, 7000.5, 6999.5,
+ // 6999.5, 6999.5, 6999.5, 6999.5, 6999.5 ;
+
+ mesh_node_x = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20;
+
+ mesh_node_y = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20;
+
+ mesh_face_x = 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, 11.5;
+
+ mesh_face_y = 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, 11.5;
+
+ mesh_domain_extents =
+ 8997, 6999,
+ 9003, 6999,
+ 9003, 7001,
+ 8997, 7001 ;
+}
diff --git a/xios_examples/setAttrCoordUGrid/test_write_metadata.py b/xios_examples/setAttrCoordUGrid/test_write_metadata.py
new file mode 100644
index 0000000..84ecb43
--- /dev/null
+++ b/xios_examples/setAttrCoordUGrid/test_write_metadata.py
@@ -0,0 +1,67 @@
+import copy
+import glob
+import netCDF4
+import numpy as np
+import os
+import subprocess
+import unittest
+
+import xios_examples.shared_testing as xshared
+
+this_path = os.path.realpath(__file__)
+this_dir = os.path.dirname(this_path)
+
+class TestWriteMetadata(xshared._TestCase):
+ test_dir = this_dir
+ transient_inputs = ['planar_mesh.nc']
+ transient_outputs = ['data_output.nc']
+ executable = './write.exe'
+ mesh_file_cdl = 'planar_mesh.cdl'
+
+ @classmethod
+ def make_a_write_test(cls, inf, nclients=1, nservers=1):
+ """
+ this function makes a test case and returns it as a test function,
+ suitable to be dynamically added to a TestCase for running.
+
+ """
+ # always copy for value, don't pass by reference.
+ infcp = copy.copy(inf)
+ # expected by the fortran XIOS program's main.xml
+ inputfile = cls.transient_inputs[0]
+ outputfile = cls.transient_outputs[0]
+ def test_write_metadata(self):
+ # create a netCDF file using nc_method
+ cls.make_netcdf(infcp, inputfile)
+ cls.run_mpi_xios(nclients=nclients, nservers=nservers)
+
+ # load the result netCDF file
+ runfile = '{}/{}'.format(cls.test_dir, outputfile)
+ assert(os.path.exists(runfile))
+ run_cmd = ['ncdump', runfile]
+ ncd = subprocess.run(run_cmd, check=True, capture_output=True)
+
+ with open(f'{this_dir}/expected_domain_output.cdl') as fin:
+ exptd = fin.read()
+ emsg = ''
+ for eline, aline in zip(exptd.split('\n'),
+ ncd.stdout.decode("utf-8").split('\n')):
+ if eline != aline:
+ # skip timestamp and uuid
+ # until we work out how not to set these
+ if not (':timeStamp' in eline or ':uuid' in eline):
+ emsg += eline + '\n' + aline + '\n\tmismatch\n\n'
+
+ #self.assertFalse(emsg, msg='\n\n' + emsg)
+ return test_write_metadata
+
+
+f = f'{this_dir}/planar_mesh.cdl'
+# unique name for the test
+tname = 'test_{}'.format(os.path.splitext(os.path.basename(f))[0])
+# add the test as an attribute (function) to the test class
+
+# !!! remove this limit on success
+if os.environ.get('MVER', '').startswith('XIOS3/trunk'):
+ setattr(TestWriteMetadata, tname,
+ TestWriteMetadata.make_a_write_test(f))
diff --git a/xios_examples/setAttrCoordUGrid/write.F90 b/xios_examples/setAttrCoordUGrid/write.F90
new file mode 100644
index 0000000..3c59ec4
--- /dev/null
+++ b/xios_examples/setAttrCoordUGrid/write.F90
@@ -0,0 +1,204 @@
+!-----------------------------------------------------------------------------
+! (C) Crown copyright 2025 Met Office. All rights reserved.
+! The file LICENCE, distributed with this code, contains details of the terms
+! under which the code may be used.
+!-----------------------------------------------------------------------------
+!> Programme to just write data with coordinate system definition metadata.
+!>
+program write
+ use xios
+ use mpi
+ USE, INTRINSIC :: ISO_C_BINDING
+ use iso_fortran_env, only : output_unit
+
+ implicit none
+
+ integer :: comm = -1
+ integer :: rank = -1
+ integer :: npar = 0
+
+ call initialise()
+ call simulate()
+ call finalise()
+contains
+
+ subroutine initialise()
+
+ type(xios_date) :: origin
+ type(xios_date) :: start
+ type(xios_duration) :: tstep
+ integer :: mpi_error
+ integer :: len_node, ni_node, ibegin_node
+ integer :: len_face, ni_face, ibegin_face
+ integer :: len_edge, ni_edge, ibegin_edge
+ double precision, dimension (:), allocatable :: node_y_vals, node_x_vals, &
+ face_y_vals, face_x_vals
+ character(len=64) :: t_origin
+
+ origin = xios_date(2022, 2, 2, 12, 0, 0)
+ start = xios_date(2022, 12, 13, 12, 0, 0)
+ tstep = xios_hour
+
+ ! Initialise MPI and XIOS
+ call MPI_INIT(mpi_error)
+ call xios_initialize('client', return_comm=comm)
+
+ call MPI_Comm_rank(comm, rank, mpi_error)
+ call MPI_Comm_size(comm, npar, mpi_error)
+
+ ! use the axis_check context to obtain sizing information on all arrays
+ ! for use in defining the main context interpretation
+
+ print *, "initialising axis_check"
+ flush(output_unit)
+ call xios_context_initialize('axis_check', comm)
+ call xios_set_time_origin(origin)
+ call xios_set_start_date(start)
+ call xios_set_timestep(tstep)
+
+ call xios_close_context_definition()
+ print *, "axis_check initialised"
+ flush(output_unit)
+
+ ! fetch sizes of axes from the input file for allocate
+ call xios_get_domain_attr('cndata::', ni_glo=len_node, ni=ni_node, ibegin=ibegin_node)
+ call xios_get_domain_attr('cfdata::', ni_glo=len_face, ni=ni_face, ibegin=ibegin_face)
+ call xios_get_domain_attr('cedata::', ni_glo=len_edge, ni=ni_edge, ibegin=ibegin_edge)
+
+
+ allocate ( node_x_vals(len_node) )
+ allocate ( node_y_vals(len_node) )
+ allocate ( face_x_vals(len_face) )
+ allocate ( face_y_vals(len_face) )
+ print *, 'len_node= ', len_node
+ print *, 'len_edge= ', len_edge
+ print *, 'len_face= ', len_face
+ flush(output_unit)
+
+ ! fetch coordinate value arrays from the input file
+ call xios_get_domain_attr('cndata::', lonvalue_1d=node_x_vals, latvalue_1d=node_y_vals)
+ call xios_get_domain_attr('cfdata::', lonvalue_1d=face_x_vals, latvalue_1d=face_y_vals)
+ print *, 'node xvals= ', node_x_vals, '\nnode yvals= ', node_y_vals
+ flush(output_unit)
+
+ ! finalise axis_check context, no longer in use
+ call xios_context_finalize()
+ print *, "axis check finalised successfully"
+ flush(output_unit)
+
+ ! initialize the main context for interacting with the data.
+ call xios_context_initialize('main', comm)
+
+ call xios_set_time_origin(origin)
+ call xios_set_start_date(start)
+ call xios_set_timestep(tstep)
+
+ ! define the horizontal domain and vertical axis using the input file
+ call xios_set_domain_attr("node_domain", ni_glo=len_node, ni=ni_node, ibegin=ibegin_node, &
+ nj_glo=len_node, nj=ni_node, jbegin=ibegin_node, &
+ latvalue_1d=node_y_vals, lonvalue_1d=node_x_vals)
+ call xios_set_domain_attr("face_domain", ni_glo=len_face, ni=ni_face, ibegin=ibegin_face, &
+ nj_glo=len_face, nj=ni_face, jbegin=ibegin_face, &
+ latvalue_1d=face_y_vals, lonvalue_1d=face_x_vals)
+ call xios_set_domain_attr("edge_domain", ni_glo=len_edge, ni=ni_edge, ibegin=ibegin_edge, &
+ nj_glo=len_edge, nj=ni_edge, jbegin=ibegin_edge)
+
+ call xios_set_file_attr("data_output", convention_str="CF-1.6, UGRID")
+ call xios_set_file_attr("data_output", description="a file format v0.2.0")
+ if (.true.) then
+ call xios_set_axis_attr("x", standard_name="projection_x_coordinate", &
+ unit="m", long_name="x coordinate of projection")
+ call xios_set_axis_attr("y", standard_name="projection_y_coordinate", &
+ unit="m", long_name="y coordinate of projection")
+ end if
+ call xios_date_convert_to_string(origin, t_origin)
+ call xios_set_field_attr("frt", unit="seconds since "//t_origin)
+ !call xios_set_scalar_attr("frt", unit="seconds since "//t_origin)
+
+ call xios_close_context_definition()
+ print *, "main context defined successfully"
+ flush(output_unit)
+ call xios_get_domain_attr('node_domain', lonvalue_1d=node_x_vals, latvalue_1d=node_y_vals)
+ print *, "x = ", node_x_vals, "y = ", node_y_vals
+ flush(output_unit)
+
+ end subroutine initialise
+
+ subroutine finalise()
+
+ integer :: mpi_error
+
+ ! Finalise all XIOS contexts and MPI
+ print *, "finalising"
+ call xios_set_current_context('main')
+ call xios_context_finalize()
+ print *, "finalised main"
+ call xios_finalize()
+ print *, "finalised xios"
+ call MPI_Finalize(mpi_error)
+
+ end subroutine finalise
+
+ subroutine simulate()
+
+ type(xios_date) :: start
+ type(xios_date) :: current
+ integer :: ts
+ integer :: i
+ integer :: len_node
+ integer :: len_edge
+ integer :: len_face
+ double precision :: frtv
+
+ ! Allocatable arrays, size is taken from input file
+ double precision, dimension (:), allocatable :: node_data!, face_data, edge_data
+
+ ! obtain sizing of the grid for the array allocation
+ call xios_get_domain_attr('ndata::', ni_glo=len_node)
+ call xios_get_domain_attr('fdata::', ni_glo=len_face)
+ call xios_get_domain_attr('edata::', ni_glo=len_edge)
+
+ allocate ( node_data(len_node) )
+ allocate ( edge_data(len_edge) )
+ allocate ( face_data(len_face) )
+ print *, 'len_node= ', len_node
+ print *, 'len_edge= ', len_edge
+ print *, 'len_face= ', len_face
+ flush(output_unit)
+
+ do ts=1, 2
+ call xios_update_calendar(ts)
+ call xios_get_current_date(current)
+ ! send frt on ts0 only
+ if (ts == 1) then
+ call xios_get_start_date(start)
+ frtv = dble(xios_date_convert_to_seconds(start))
+ call xios_send_field("frt", frtv)
+ ! call xios_set_scalar_attr("frt", value=frtv)
+ end if
+
+ do i=1, len_node
+ node_data(i) = ts
+ end do
+ call xios_send_field('ndata', node_data)
+
+ do i=1, len_face
+ face_data(i) = 10 * ts
+ call xios_send_field('fdata', face_data)
+ end do
+
+ do i=1, len_edge
+ edge_data(i) = 100 * ts
+ end do
+ call xios_send_field('edata', edge_data)
+
+ enddo
+
+ deallocate (node_data)
+ deallocate (edge_data)
+ deallocate (face_data)
+ print *, "fields sent, exiting simulation"
+
+ end subroutine simulate
+
+end program write
diff --git a/xios_examples/setAttrCoordUGrid/xios.xml b/xios_examples/setAttrCoordUGrid/xios.xml
new file mode 100644
index 0000000..9ec1df0
--- /dev/null
+++ b/xios_examples/setAttrCoordUGrid/xios.xml
@@ -0,0 +1,22 @@
+
+
+
+
+ performance
+
+
+ 1.0
+
+
+
+
+ true
+
+ 100
+
+
+ true
+
+
+
+