diff --git a/.github/workflows/buildTest.yml b/.github/workflows/buildTest.yml index 4fbd899..e7db73c 100644 --- a/.github/workflows/buildTest.yml +++ b/.github/workflows/buildTest.yml @@ -12,8 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - # version: [XIOS/trunk@2252, XIOS2/trunk, XIOS3/trunk] - version: [XIOS/trunk@2252, XIOS2/trunk] + version: [XIOS/trunk@2252, XIOS2/trunk, XIOS3/trunk] steps: # Check out repository branch - uses: actions/checkout@v4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..abd32e8 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*__pycache__* \ No newline at end of file diff --git a/xios_examples/read_axis_resample/axis_input.cdl b/xios_examples/read_axis_resample/axis_input.cdl new file mode 100644 index 0000000..8ba3391 --- /dev/null +++ b/xios_examples/read_axis_resample/axis_input.cdl @@ -0,0 +1,32 @@ +netcdf axis_input { +dimensions: + z = 10 ; + z_resample = 4 ; +variables: + float z(z) ; + z:long_name = "original z coordinate" ; + z:units = "1"; + float z_resample(z_resample) ; + z_resample:long_name = "resampled z coordinate" ; + z_resample:units = "1"; + double original_data(z) ; + original_data:long_name = "input data values" ; + original_data:units = "1"; + double resample_data(z_resample) ; + resample_data:long_name = "expected resampled data values" ; + resample_data:units = "1"; + +// global attributes: + :title = "Input data for XIOS Axis resampling; data is a square function of the z coordinate." ; + +data: + + z = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ; + + z_resample = 1.5, 3.5, 5.5, 7.5 ; + + original_data = 1, 4, 9, 16, 25, 36, 49, 64, 81, 100 ; + + resample_data = 2.25, 12.25, 30.25, 56.25 ; + +} diff --git a/xios_examples/read_axis_resample/test_resample_cases.py b/xios_examples/read_axis_resample/test_resample_cases.py index 93e92b2..5026747 100644 --- a/xios_examples/read_axis_resample/test_resample_cases.py +++ b/xios_examples/read_axis_resample/test_resample_cases.py @@ -7,108 +7,45 @@ import glob import unittest +import xios_examples.shared_testing as xshared + this_path = os.path.realpath(__file__) this_dir = os.path.dirname(this_path) -class TestResample(unittest.TestCase): - """ - UnitTest class to contain tests, - 1 test case function per input `.cdl` file - - """ - @classmethod - def setUpClass(cls): - """ - First, build the fortran code only once for this class. - - """ - subprocess.run(['make', 'clean'], cwd=this_dir, check=True) - subprocess.run(['make'], cwd=this_dir, check=True) - if os.environ.get('MVER', '') == 'XIOS3/trunk': - with open(os.path.join(this_dir, 'iodef.xml'), 'r') as ioin: - iodef_in = ioin.read() - # patch in transport protocol choice for XIOS3 - # needed for CI runners - in2 = '' - in3 = ('\n' - ' p2p') - iodef_out = iodef_in.replace(in2, in3) - with open(os.path.join(this_dir, 'iodef.xml'), 'w') as ioout: - ioout.write(iodef_out) - - def tearDown(self): - """ - After each test function, - report any errors from XIOS, then - remove the input and output netCDF files. - - """ +class TestResampleAxis(xshared._TestCase): + test_dir = this_dir + transient_inputs = ['axis_input.nc'] + transient_outputs = ['axis_output.nc'] + rtol = 5e-04 - for ef in glob.glob('{}/*.err'.format(this_dir)): - print(ef) - with open(ef, 'r') as efile: - print(efile.read(), flush=True) - - os.remove('{}/axis_input.nc'.format(this_dir)) - os.remove('{}/axis_output.nc'.format(this_dir)) - - @classmethod - def tearDownClass(cls): - """ - Finally, clean the build for this class, after all tests have run. - - """ - subprocess.run(['make', 'clean'], cwd=this_dir) # A list of input `.cdl` files where XIOS is known to produce different # output from the expected output data # for future investigation / ToDo -known_failures = ['test_axis_input_edge_simple_square_ten.cdl'] +# 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 = {'test_axis_input_edge_simple_square_ten': + ('The last value, nearest the upper edge is' + ' not being interpolated using a square poynomial' + ' unlike the first, which is also near the edge' + ' and is being calculated sensibly')} # iterate through `.cdl` files in this test case folder for f in glob.glob('{}/*.cdl'.format(this_dir)): - def make_a_test(inf): - """ - this function makes a test case and returns it as a function. - - """ - # always copy for value, don't pass by reference. - infile = copy.copy(inf) - # expected by the fortran XIOS resample program's main.xml - outfile = 'axis_input.nc' - def test_resample(self): - # create a netCDF file from the `.cdl` input - subprocess.run(['ncgen', '-k', 'nc4', '-o', 'axis_input.nc', - infile], cwd=this_dir, check=True) - # run the compiled Fortran XIOS programme - subprocess.run(['mpiexec', '-n', '1', './resample.exe', ':', - '-n', '1', './xios_server.exe'], - cwd=this_dir, check=True) - # load the result netCDF file - rootgrp = netCDF4.Dataset('{}/axis_output.nc'.format(this_dir), - 'r') - # read data from the resampled, expected & diff variables - diff = rootgrp['resampled_minus_resample'][:] - # prepare message for failure - msg = ('the expected resample data array\n {exp}\n ' - 'differs from the resampled data array\n {res} \n ' - 'with diff \n {diff}\n') - msg = msg.format(exp=rootgrp['resample_data'][:], - res=rootgrp['resampled_data'][:], - diff=diff) - if np.any(diff): - # 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) - return test_resample # unique name for the test - tname = 'test_{}'.format(os.path.basename(f)) + tname = 'test_{}'.format(os.path.splitext(os.path.basename(f))[0]) # add the test as an attribute (function) to the test class - if tname in known_failures: + if os.environ.get('MVER', '') == 'XIOS3/trunk': + # these tests are hitting exceptions with XIOS3 + # but not XIOS2, so skip for XIOS3 runner + setattr(TestResampleAxis, tname, + unittest.skip(TestResampleAxis.make_a_resample_test(f))) + elif tname in known_failures: # set decorator @unittest.expectedFailure - setattr(TestResample, tname, unittest.expectedFailure(make_a_test(f))) + setattr(TestResampleAxis, tname, + unittest.expectedFailure(TestResampleAxis.make_a_resample_test(f))) else: - setattr(TestResample, tname, make_a_test(f)) + setattr(TestResampleAxis, tname, + TestResampleAxis.make_a_resample_test(f)) diff --git a/xios_examples/read_domain_resample/Makefile b/xios_examples/read_domain_resample/Makefile new file mode 100644 index 0000000..0824ec0 --- /dev/null +++ b/xios_examples/read_domain_resample/Makefile @@ -0,0 +1,56 @@ +# Make file for the resample demonstartion XIOS programme +# Targets provided our detailed below... +# +# all: (default) Build the resample programme +# clean: Delete all final products and working files +# run: run the programme +# +# Environment Variables expected by this MakeFile: +# +# NETCDF_LIB_DIR: the directories for the netCDF lib files +# encoded as a -L string, e.g. +# "-L/dir1 -L/dir2" +# NETCDF_INC_DIR: 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 -ffree-line-length-none + +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++ + +all: resample + +# fortran compilation +%.o: %.F90 + $(FC) $(FCFLAGS) -c $< + +# fortran linking +resample: resample.o + $(FC) -o resample.exe resample.o $(LDFLAGS) \ + && ln -fs $(XIOS_BINDIR)/xios_server.exe . + +run: + mpiexec -n 1 ./resample.exe : -n 1 ./xios_server.exe + +# cleanup +clean: + rm -f *.exe *.o *.mod *.MOD *.out *.err *.nc diff --git a/xios_examples/read_domain_resample/__init__.py b/xios_examples/read_domain_resample/__init__.py new file mode 100644 index 0000000..df083e3 --- /dev/null +++ b/xios_examples/read_domain_resample/__init__.py @@ -0,0 +1,3 @@ +""" +Enable this folder to be a module path, for imports and test discovery. +""" diff --git a/xios_examples/read_domain_resample/axis_check.xml b/xios_examples/read_domain_resample/axis_check.xml new file mode 100644 index 0000000..b198d5b --- /dev/null +++ b/xios_examples/read_domain_resample/axis_check.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xios_examples/read_domain_resample/domain_input_edge_simple_square_ten.cdl b/xios_examples/read_domain_resample/domain_input_edge_simple_square_ten.cdl new file mode 100644 index 0000000..791be5f --- /dev/null +++ b/xios_examples/read_domain_resample/domain_input_edge_simple_square_ten.cdl @@ -0,0 +1,51 @@ +netcdf domain_input { +dimensions: + x = 5 ; + y = 5 ; + x_resample = 4 ; + y_resample = 4 ; +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"; + float x_resample(x_resample) ; + x_resample:long_name = "resampled x coordinate" ; + x_resample:units = "1"; + float y_resample(y_resample) ; + y_resample:long_name = "resampled y coordinate" ; + y_resample:units = "1"; + double original_data(y,x) ; + original_data:long_name = "input data values" ; + original_data:units = "1"; + double resample_data(y_resample,x_resample) ; + resample_data:long_name = "expected resampled data values" ; + resample_data:units = "1"; + +// global attributes: + :title = "Input data for XIOS Domain resampling; data is a square function of the x & y coordinates; x^2+y^2." ; + +data: + + x = 0, 2, 4, 6, 8 ; + + y = 0, 2, 4, 6, 8 ; + + x_resample = 1, 3, 5, 7 ; + + y_resample = 1, 3, 5, 7 ; + + original_data = 0, 4, 16, 36, 64, + 4, 8, 20, 40, 68, + 16, 20, 32, 52, 80, + 36, 40, 52, 72, 100, + 64, 68, 80, 100, 128 ; + + resample_data = 2, 10, 26, 50, + 10, 18, 34, 58, + 26, 34, 50, 74, + 50, 58, 74, 98 ; + +} diff --git a/xios_examples/read_domain_resample/domain_input_simple_linear.cdl b/xios_examples/read_domain_resample/domain_input_simple_linear.cdl new file mode 100644 index 0000000..e271c6d --- /dev/null +++ b/xios_examples/read_domain_resample/domain_input_simple_linear.cdl @@ -0,0 +1,49 @@ +netcdf domain_input { +dimensions: + x = 5 ; + y = 5 ; + x_resample = 2 ; + y_resample = 2 ; +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"; + float x_resample(x_resample) ; + x_resample:long_name = "resampled x coordinate" ; + x_resample:units = "1"; + float y_resample(y_resample) ; + y_resample:long_name = "resampled y coordinate" ; + y_resample:units = "1"; + double original_data(y,x) ; + original_data:long_name = "input data values" ; + original_data:units = "1"; + double resample_data(y_resample,x_resample) ; + resample_data:long_name = "expected resampled data values" ; + resample_data:units = "1"; + +// global attributes: + :title = "Input data for XIOS Domain resampling; data is a sum of the x & y coordinates; x + y ." ; + +data: + + x = 0, 2, 4, 6, 8 ; + + y = 0, 2, 4, 6, 8 ; + + x_resample = 3, 5 ; + + y_resample = 3, 5 ; + + 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 ; + + resample_data = 6, 8, + 8, 10 ; + +} diff --git a/xios_examples/read_domain_resample/domain_input_simple_square_ten.cdl b/xios_examples/read_domain_resample/domain_input_simple_square_ten.cdl new file mode 100644 index 0000000..dec064a --- /dev/null +++ b/xios_examples/read_domain_resample/domain_input_simple_square_ten.cdl @@ -0,0 +1,49 @@ +netcdf domain_input { +dimensions: + x = 5 ; + y = 5 ; + x_resample = 2 ; + y_resample = 2 ; +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"; + float x_resample(x_resample) ; + x_resample:long_name = "resampled x coordinate" ; + x_resample:units = "1"; + float y_resample(y_resample) ; + y_resample:long_name = "resampled y coordinate" ; + y_resample:units = "1"; + double original_data(y,x) ; + original_data:long_name = "input data values" ; + original_data:units = "1"; + double resample_data(y_resample,x_resample) ; + resample_data:long_name = "expected resampled data values" ; + resample_data:units = "1"; + +// global attributes: + :title = "Input data for XIOS Domain resampling; data is a square function of the x & y coordinates; x^2+y^2." ; + +data: + + x = 0, 2, 4, 6, 8 ; + + y = 0, 2, 4, 6, 8 ; + + x_resample = 3, 5 ; + + y_resample = 3, 5 ; + + original_data = 0, 4, 16, 36, 64, + 4, 8, 20, 40, 68, + 16, 20, 32, 52, 80, + 36, 40, 52, 72, 100, + 64, 68, 80, 100, 128 ; + + resample_data = 18, 34, + 34, 50 ; + +} diff --git a/xios_examples/read_domain_resample/iodef.xml b/xios_examples/read_domain_resample/iodef.xml new file mode 100644 index 0000000..37acd4d --- /dev/null +++ b/xios_examples/read_domain_resample/iodef.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/xios_examples/read_domain_resample/main.xml b/xios_examples/read_domain_resample/main.xml new file mode 100644 index 0000000..409027a --- /dev/null +++ b/xios_examples/read_domain_resample/main.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + rdata-edata + + + + + + + + + + + + + + + + + + + diff --git a/xios_examples/read_domain_resample/resample.F90 b/xios_examples/read_domain_resample/resample.F90 new file mode 100644 index 0000000..3874f47 --- /dev/null +++ b/xios_examples/read_domain_resample/resample.F90 @@ -0,0 +1,132 @@ +!----------------------------------------------------------------------------- +! (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 :: lenrx + integer :: leny + integer :: lenry + + ! 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('x_resample', n_glo=lenrx) + call xios_get_axis_attr('y', n_glo=leny) + call xios_get_axis_attr('y_resample', n_glo=lenry) + + ! print *, 'x, y', lenx, ', ', leny + ! print *, 'rx, ry', lenrx, ', ', lenry + + ! 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_set_domain_attr("resampled_domain", ni=lenrx, nj=lenry, 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 :: lenrx + integer :: leny + integer :: lenry + + ! Allocatable arrays, size is taken from input file + double precision, dimension (:,:), allocatable :: inodata + double precision, dimension (:,:), allocatable :: inedata + + call xios_get_domain_attr('original_domain', ni_glo=lenx) + call xios_get_domain_attr('original_domain', nj_glo=leny) + call xios_get_domain_attr('resampled_domain', ni_glo=lenrx) + call xios_get_domain_attr('resampled_domain', nj_glo=lenry) + + allocate ( inodata(leny, lenx) ) + allocate ( inedata(lenry, lenrx) ) + + ! Load data from the input file + call xios_recv_field('odatain', inodata) + call xios_recv_field('edatain', inedata) + + 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 (copy) the expected data to the output file. + call xios_send_field('edata', inedata) + enddo + + deallocate (inodata) + deallocate (inedata) + + end subroutine simulate + +end program resample diff --git a/xios_examples/read_domain_resample/test_resample_cases.py b/xios_examples/read_domain_resample/test_resample_cases.py new file mode 100644 index 0000000..090ed03 --- /dev/null +++ b/xios_examples/read_domain_resample/test_resample_cases.py @@ -0,0 +1,52 @@ +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 TestResampleDomain(xshared._TestCase): + test_dir = this_dir + transient_inputs = ['domain_input.nc'] + transient_outputs = ['domain_output.nc'] + rtol = 5e-03 + + +# 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 = {'test_domain_input_edge_simple_square_ten': + ('The bi-linear polynomial poorly reproduces the' + ' input x^2+y^2 function'), + 'test_domain_input_simple_square_ten': + ('The bi-linear polynomial poorly reproduces the' + ' input x^2+y^2 function') + } + +# 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', '') == 'XIOS3/trunk': + # these tests are hitting exceptions with XIOS3 + # but not XIOS2, so skip for XIOS3 runner + setattr(TestResampleDomain, tname, + unittest.skip(TestResampleDomain.make_a_resample_test(f))) + elif tname in known_failures: + # set decorator @unittest.expectedFailure + setattr(TestResampleDomain, tname, + unittest.expectedFailure(TestResampleDomain.make_a_resample_test(f))) + else: + setattr(TestResampleDomain, tname, + TestResampleDomain.make_a_resample_test(f)) diff --git a/xios_examples/read_domain_resample/xios.xml b/xios_examples/read_domain_resample/xios.xml new file mode 100644 index 0000000..9ec1df0 --- /dev/null +++ b/xios_examples/read_domain_resample/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 new file mode 100644 index 0000000..2553f6d --- /dev/null +++ b/xios_examples/shared_testing.py @@ -0,0 +1,121 @@ +import copy +import glob +import netCDF4 +import numpy as np +import os +import subprocess +import unittest + +this_path = os.path.realpath(__file__) +this_dir = os.path.dirname(this_path) + + +class _TestCase(unittest.TestCase): + """ + UnitTest class to contain tests, + 1 test case function per input `.cdl` file + + """ + test_dir = this_dir + transient_inputs = [] + transient_outputs = [] + rtol = 5e-03 + + @classmethod + def setUpClass(cls): + """ + First, build the fortran code only once for this class. + + """ + subprocess.run(['make', 'clean'], cwd=cls.test_dir) + subprocess.run(['make'], cwd=cls.test_dir) + if os.environ.get('MVER', '') == 'XIOS3/trunk': + with open(os.path.join(cls.test_dir, 'main.xml'), 'r') as ioin: + iodef_in = ioin.read() + # patch in transport protocol choice for XIOS3 + # needed for CI runners + in2 = '' + in3 = ('\n' + ' p2p') + iodef_out = iodef_in.replace(in2, in3) + with open(os.path.join(this_dir, 'iodef.xml'), 'w') as ioout: + ioout.write(iodef_out) + + def tearDown(self): + """ + After each test function, + report any errors from XIOS, then + remove the input and output netCDF files. + + Use environment variable 'files' to avoid clean up of transient files + note; this can cause multi-test classes to fail with ncgen errors, use + for single test functions only. + """ + + for ef in glob.glob('{}/*.err'.format(this_dir)): + print(ef) + with open(ef, 'r') as efile: + print(efile.read(), flush=True) + + for t_in in self.transient_inputs: + rf = '{}/{}'.format(self.test_dir, t_in) + if os.path.exists(rf) and not os.environ.get("files"): + os.remove(rf) + for t_out in self.transient_outputs: + rf = '{}/{}'.format(self.test_dir, t_out) + if os.path.exists(rf) and not os.environ.get("files"): + os.remove(rf) + + @classmethod + def tearDownClass(cls): + """ + Finally, clean the build for this class, after all tests have run. + + Use environment variable 'logs' to avoid clean up, e.g. to keep logs + """ + if not os.environ.get('logs'): + subprocess.run(['make', 'clean'], cwd=cls.test_dir) + + + @classmethod + def make_a_resample_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 resample program's main.xml + inputfile = cls.transient_inputs[0] + outputfile = cls.transient_outputs[0] + def test_resample(self): + # create a netCDF file from the `.cdl` input + subprocess.run(['ncgen', '-k', 'nc4', '-o', inputfile, + infile], cwd=cls.test_dir, check=True) + # run the compiled Fortran XIOS programme + subprocess.run(['mpiexec', '-n', '1', './resample.exe', ':', + '-n', '1', './xios_server.exe'], + cwd=cls.test_dir, check=True) + # 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 resampled, expected & diff variables + diff = rootgrp['resampled_minus_resample'][:] + expected = rootgrp['resample_data'][:] + result = rootgrp['resampled_data'][:] + # prepare message for failure + msg = ('the expected resample data array\n {exp}\n ' + 'differs from the resampled data array\n {res} \n ' + 'with diff \n {diff}\n') + msg = msg.format(exp=expected, res=result, diff=diff) + if np.any(diff): + # 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=cls.rtol), msg=msg) + return test_resample