diff --git a/xios_examples/packing_scale_offset/Makefile b/xios_examples/packing_scale_offset/Makefile
new file mode 100644
index 0000000..d0f4a3e
--- /dev/null
+++ b/xios_examples/packing_scale_offset/Makefile
@@ -0,0 +1,58 @@
+# Make file for the pack demonstartion XIOS programme
+# Targets provided our detailed below...
+#
+# all: (default) Build the pack programme
+# clean: Delete all final products and working files
+# run: run the programme
+#
+# Environment Variables expected by this MakeFile:
+#
+# NETCDF_LIBDIR: the directories for the netCDF lib files
+# encoded as a -L string, e.g.
+# "-L/dir1 -L/dir2"
+# NETCDF_INCDIR: the directories for the netCDF include files
+# encoded as a -I string, e.g.
+# "-I/dir3 -I/dir4"
+# (note, this is for consistency with the XIOS build process
+# required for the CI build machine.
+# this is not required for other directories)
+#
+# XIOS_INCDIR: The directory for XIOS include files
+# XIOS_LIBDIR: The directory for XIOS lib files
+# XIOS_BINDIR: The directory for XIOS binary files
+
+FCFLAGS = -g
+
+FC = mpif90 # compiler driver for MPI programs
+
+# compiler flag, includes
+FCFLAGS += -I$(XIOS_INCDIR) $(NETCDF_INCDIR)
+
+# loader flags
+LDFLAGS = \
+ -L$(XIOS_LIBDIR) \
+ $(NETCDF_LIBDIR) \
+ -lxios \
+ -lnetcdf \
+ -lnetcdff \
+ -lstdc++
+
+.PHONY: all, clean, run
+
+all: pack
+
+# fortran compilation
+%.o: %.F90
+ $(FC) $(FCFLAGS) -c $<
+
+# fortran linking
+pack: pack.o
+ $(FC) -o pack.exe pack.o $(LDFLAGS) \
+ && ln -fs $(XIOS_BINDIR)/xios_server.exe .
+
+run:
+ mpiexec -n 1 ./pack.exe : -n 1 ./xios_server.exe
+
+# cleanup
+clean:
+ rm -f *.exe *.o *.mod *.MOD *.out *.err *.nc
diff --git a/xios_examples/packing_scale_offset/README b/xios_examples/packing_scale_offset/README
new file mode 100644
index 0000000..e8cd0bc
--- /dev/null
+++ b/xios_examples/packing_scale_offset/README
@@ -0,0 +1,5 @@
+Offset & Scal Factor packing
+----------------------------
+
+These examples read in arbitrary data from a netCDF file and pack the data to a hard coded precision to produce integer results data with metadata defining how to recover the reduced precision floats.
+The original and packed data are written to one output NetCDF file.
diff --git a/xios_examples/packing_scale_offset/__init__.py b/xios_examples/packing_scale_offset/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/xios_examples/packing_scale_offset/axis_check.xml b/xios_examples/packing_scale_offset/axis_check.xml
new file mode 100644
index 0000000..b815db5
--- /dev/null
+++ b/xios_examples/packing_scale_offset/axis_check.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xios_examples/packing_scale_offset/domain_input_simple_linear.cdl b/xios_examples/packing_scale_offset/domain_input_simple_linear.cdl
new file mode 100644
index 0000000..2175ea7
--- /dev/null
+++ b/xios_examples/packing_scale_offset/domain_input_simple_linear.cdl
@@ -0,0 +1,31 @@
+netcdf domain_input {
+dimensions:
+ x = 5 ;
+ y = 5 ;
+variables:
+ float x(x) ;
+ x:long_name = "original x coordinate" ;
+ x:units = "1";
+ float y(y) ;
+ y:long_name = "original y coordinate" ;
+ y:units = "1";
+ double original_data(y,x) ;
+ original_data:long_name = "input data values" ;
+ original_data:units = "1";
+
+// global attributes:
+ :title = "Input data for XIOS Domain resampling; data is a sum of the x & y coordinates each multiplied by 1/3; x/3 + y/3 ." ;
+
+data:
+
+ x = 0, 2, 4, 6, 8 ;
+
+ y = 0, 2, 4, 6, 8 ;
+
+ original_data = 0, 0.6666666666666666, 1.3333333333333333, 2, 2.6666666666666665,
+ 0.6666666666666666, 1.3333333333333333, 2, 2.6666666666666665, 3.3333333333333335,
+ 1.3333333333333333, 2, 2.6666666666666665, 3.3333333333333335, 4,
+ 2, 2.6666666666666665, 3.3333333333333335, 4, 4.666666666666667,
+ 2.6666666666666665, 3.3333333333333335, 4, 4.666666666666667, 5.333333333333333 ;
+
+}
diff --git a/xios_examples/packing_scale_offset/iodef.xml b/xios_examples/packing_scale_offset/iodef.xml
new file mode 100644
index 0000000..37acd4d
--- /dev/null
+++ b/xios_examples/packing_scale_offset/iodef.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/xios_examples/packing_scale_offset/main.xml b/xios_examples/packing_scale_offset/main.xml
new file mode 100644
index 0000000..d211fc2
--- /dev/null
+++ b/xios_examples/packing_scale_offset/main.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xios_examples/packing_scale_offset/pack.F90 b/xios_examples/packing_scale_offset/pack.F90
new file mode 100644
index 0000000..8221388
--- /dev/null
+++ b/xios_examples/packing_scale_offset/pack.F90
@@ -0,0 +1,120 @@
+!-----------------------------------------------------------------------------
+! (C) Crown copyright 2020 Met Office. All rights reserved.
+! The file LICENCE, distributed with this code, contains details of the terms
+! under which the code may be used.
+!-----------------------------------------------------------------------------
+!> Read 2D data on a domain and resample using the axis_input.nc file
+!>
+program resample
+ use xios
+ use mpi
+
+ 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
+
+ ! Arbitrary datetime setup, required for XIOS but unused
+ 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()
+
+ call xios_get_axis_attr('x', n_glo=lenx)
+ call xios_get_axis_attr('y', n_glo=leny)
+
+ ! 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)
+
+ call xios_set_domain_attr("original_domain", ni=lenx, nj=leny, ibegin=0, jbegin=0)
+
+ call xios_close_context_definition()
+
+ end subroutine initialise
+
+ subroutine finalise()
+
+ integer :: mpi_error
+
+ ! Finalise all XIOS contexts and MPI
+ call xios_set_current_context('axis_check')
+ call xios_context_finalize()
+ call xios_set_current_context('main')
+ call xios_context_finalize()
+ call MPI_Comm_free(comm, mpi_error)
+ call xios_finalize()
+ call MPI_Finalize(mpi_error)
+
+ end subroutine finalise
+
+ subroutine simulate()
+
+ type(xios_date) :: current
+ integer :: ts
+ integer :: lenx
+ integer :: leny
+ integer :: i, j
+
+ ! Allocatable arrays, size is taken from input file
+ double precision, dimension (:,:), allocatable :: inodata
+
+ call xios_get_domain_attr('original_domain', ni_glo=lenx)
+ call xios_get_domain_attr('original_domain', nj_glo=leny)
+
+ allocate ( inodata(leny, lenx) )
+
+ ! Load data from the input file
+ call xios_recv_field('odatain', inodata)
+
+ do ts=1, 1
+ call xios_update_calendar(ts)
+ call xios_get_current_date(current)
+ ! Send (copy) the original data to the output file.
+ call xios_send_field('odata', inodata)
+ ! Send the original data to the output file pack target.
+ ! The `pdata` field is defined as integer with scale_factor
+ ! and `add_offset` defined, so XIOS will pack this data
+ ! on write.
+ call xios_send_field('pdata', inodata)
+ enddo
+
+ deallocate (inodata)
+
+ end subroutine simulate
+
+end program resample
diff --git a/xios_examples/packing_scale_offset/test_packing_cases.py b/xios_examples/packing_scale_offset/test_packing_cases.py
new file mode 100644
index 0000000..2b7fa79
--- /dev/null
+++ b/xios_examples/packing_scale_offset/test_packing_cases.py
@@ -0,0 +1,89 @@
+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 TestPackDomain(xshared._TestCase):
+ test_dir = this_dir
+ transient_inputs = ['domain_input.nc']
+ transient_outputs = ['domain_output.nc']
+ executable = './pack.exe'
+
+ @classmethod
+ def make_a_pack_test(cls, inf):
+ """
+ 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.
+ infile = copy.copy(inf)
+ # expected by the fortran XIOS pack program's main.xml
+ inputfile = cls.transient_inputs[0]
+ outputfile = cls.transient_outputs[0]
+ def test_pack(self):
+ # create a netCDF file from the `.cdl` input
+ subprocess.run(['ncgen', '-k', 'nc4', '-o', inputfile,
+ infile], cwd=cls.test_dir, check=True)
+ cls.run_mpi_xios()
+
+ # load the result netCDF file
+ runfile = '{}/{}'.format(cls.test_dir, outputfile)
+ assert(os.path.exists(runfile))
+ rootgrp = netCDF4.Dataset(runfile, 'r')
+ # read data from the packed, expected & diff variables
+ expected = rootgrp['original_data'][:]
+ result = rootgrp['packed_data'][:]
+ rtol = rootgrp['packed_data'].scale_factor
+ # prepare message for failure
+ msg = ('\n the packed data array\n {res}\n '
+ 'differs from the original data array\n {exp} \n '
+ 'with diff outside expected tolerance {rtol}\n {diff}\n')
+ msg = msg.format(exp=expected, res=result, rtol=rtol,
+ diff=result-expected)
+ if not np.allclose(result, expected, rtol=rtol):
+ # print message for fail case,
+ # as expected failures do not report msg.
+ print(msg)
+ # assert that all of the `diff` varaible values are zero
+ # self.assertTrue(not np.any(diff), msg=msg)
+ self.assertTrue(np.allclose(result, expected, rtol=rtol) and
+ not np.allclose(result, expected, rtol=0.1*rtol),
+ msg=msg)
+ return test_pack
+
+
+# A list of input `.cdl` files where XIOS is known to produce different
+# output from the expected output data
+# for future investigation / ToDo
+# this is a dict, where the name of the key is the name of the test
+# to register as a known_failure (tname)
+# and the value is a string explaining the failure
+# this handles FAIL conditions but NOT ERROR conditions
+known_failures = {}
+
+# iterate through `.cdl` files in this test case folder
+for f in glob.glob('{}/*.cdl'.format(this_dir)):
+ # 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
+ if os.environ.get('MVER', '').startswith('XIOS3/trunk'):
+ # these tests are hitting exceptions with XIOS3
+ # but not XIOS2, so skip for XIOS3 runner
+ setattr(TestPackDomain, tname,
+ unittest.skip(TestPackDomain.make_a_pack_test(f)))
+ elif tname in known_failures:
+ # set decorator @unittest.expectedFailure
+ setattr(TestPackDomain, tname,
+ unittest.expectedFailure(TestPackDomain.make_a_pack_test(f)))
+ else:
+ setattr(TestPackDomain, tname,
+ TestPackDomain.make_a_pack_test(f))
diff --git a/xios_examples/packing_scale_offset/xios.xml b/xios_examples/packing_scale_offset/xios.xml
new file mode 100644
index 0000000..9ec1df0
--- /dev/null
+++ b/xios_examples/packing_scale_offset/xios.xml
@@ -0,0 +1,22 @@
+
+
+
+
+ performance
+
+
+ 1.0
+
+
+
+
+ true
+
+ 100
+
+
+ true
+
+
+
+
diff --git a/xios_examples/shared_testing.py b/xios_examples/shared_testing.py
index 6447b37..111d2ce 100644
--- a/xios_examples/shared_testing.py
+++ b/xios_examples/shared_testing.py
@@ -20,6 +20,7 @@ class _TestCase(unittest.TestCase):
transient_inputs = []
transient_outputs = []
rtol = 5e-03
+ executable = './resample.exe'
@classmethod
def run_mpi_xios(cls, nclients=1, nservers=1):
@@ -27,13 +28,13 @@ def run_mpi_xios(cls, nclients=1, nservers=1):
if os.environ.get('PLATFORM', '') == 'Archer2':
run_cmd = ['srun', '--distribution=block:block', '--hint=nomultithread',
'--het-group=0', '--nodes=1', '-n', str(nclients),
- './resample.exe', ':',
+ cls.executable, ':',
'--het-group=1', '--nodes=1', '-n', str(nservers),
'./xios_server.exe']
print(' '.join(run_cmd))
subprocess.run(run_cmd,cwd=cls.test_dir, check=True)
else:
- run_cmd = ['mpiexec', '-n', str(nclients), './resample.exe', ':',
+ run_cmd = ['mpiexec', '-n', str(nclients), cls.executable, ':',
'-n', str(nservers), './xios_server.exe']
if os.environ.get('MPI_FLAVOUR', '') == 'openmpi':
# use hwthread for github ubuntu runner