Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
!-----------------------------------------------------------------------------
! (C) Crown copyright 2026 Met Office. All rights reserved.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
! (C) Crown copyright 2026 Met Office. All rights reserved.
! (C) Crown copyright Met Office. All rights reserved.

! The file LICENCE, distributed with this code, contains details of the terms
! under which the code may be used.
!-----------------------------------------------------------------------------

! Tests the LFRic-XIOS temporal reading functionality
!
program lfric_xios_temporal_interp_test

use constants_mod, only: i_timestep, r_second
use event_mod, only: event_action
use event_actor_mod, only: event_actor_type
use field_mod, only: field_type, field_proxy_type
use file_mod, only: FILE_MODE_READ, FILE_MODE_WRITE
use io_context_mod, only: callback_clock_arg
use lfric_xios_action_mod, only: advance
use lfric_xios_context_mod, only: lfric_xios_context_type
use lfric_xios_driver_mod, only: lfric_xios_initialise, lfric_xios_finalise
use lfric_xios_file_mod, only: lfric_xios_file_type, OPERATION_TIMESERIES
use lfric_xios_constants_mod, only: lx_second
use linked_list_mod, only: linked_list_type
use log_mod, only: log_event, log_level_info
use test_db_mod, only: test_db_type
use xios, only: xios_date

implicit none

type(test_db_type) :: test_db
type(lfric_xios_context_type), target, allocatable :: io_context

procedure(callback_clock_arg), pointer :: before_close
type(linked_list_type), pointer :: file_list
class(event_actor_type), pointer :: context_actor
procedure(event_action), pointer :: context_advance
type(field_type), pointer :: rfield
type(field_proxy_type) :: rproxy
type(xios_date) :: date
integer(i_timestep) :: file_freq

call test_db%initialise()
call lfric_xios_initialise( "test", test_db%comm, .false. )

! =============================== Start test ================================

allocate(io_context)
call io_context%initialise( "test_io_context", 1, 10 )

! Fixed attribute of input data
file_freq = int(60.0_r_second / test_db%clock%get_seconds_per_step(), i_timestep)

file_list => io_context%get_filelist()
call file_list%insert_item( lfric_xios_file_type( "lfric_xios_interp_input", &
xios_id="lfric_xios_interp_input", &
io_mode=FILE_MODE_READ, &
operation=OPERATION_TIMESERIES, &
freq=file_freq, &
cyclic=.true., &
update_freq=20*lx_second, &
fields_in_file=test_db%temporal_fields ) )
call file_list%insert_item( lfric_xios_file_type( "lfric_xios_interp_output", &
xios_id="lfric_xios_interp_output", &
io_mode=FILE_MODE_WRITE, &
operation=OPERATION_TIMESERIES, &
freq=1, &
fields_in_file=test_db%temporal_fields ) )

before_close => null()
call io_context%initialise_xios_context( test_db%comm, &
test_db%chi, test_db%panel_id, &
test_db%clock, test_db%calendar, &
before_close )


context_advance => advance
context_actor => io_context
call test_db%clock%add_event( context_advance, context_actor )
call io_context%set_active(.true.)

do while (test_db%clock%tick())
call test_db%temporal_fields%get_field("temporal_field", rfield)
rproxy = rfield%get_proxy()
call log_event("Valid data for this TS:", log_level_info)
print*,rproxy%data(1)
end do

deallocate(io_context)

! ============================== Finish test =================================

call lfric_xios_finalise()
call test_db%finalise()

end program lfric_xios_temporal_interp_test
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/env python3
##############################################################################
# (C) Crown copyright 2024 Met Office. All rights reserved.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# (C) Crown copyright 2024 Met Office. All rights reserved.
# (C) Crown copyright Met Office. All rights reserved.

# The file LICENCE, distributed with this code, contains details of the terms
# under which the code may be used.
##############################################################################
"""
A set of tests which exercise the temporal reading functionality provided by
the LFRic-XIOS component.
The tests cover the reading of a piece of non-cyclic temporal data with data
points ranging from 15:01 to 15:10 in 10 1-minute intervals. The model start
time is changed to change how the model interacts with the data.
"""
from testframework import TestEngine, TestFailed
from xiostest import LFRicXiosTest
from pathlib import Path
import sys

###############################################################################
class LfricXiosFullInterpTest(LFRicXiosTest): # pylint: disable=too-few-public-methods
"""
Tests the LFRic-XIOS temporal reading functionality for a full set of non-cyclic data
"""

def __init__(self):
super().__init__(command=[sys.argv[1], "cyclic_high_freq.nml"], processes=1)
self.gen_data('temporal_data.cdl', 'lfric_xios_interp_input.nc')
self.gen_data('cyclic_high_freq_kgo.cdl', 'cyclic_high_freq_kgo.nc')
self.gen_config( "cyclic_base.nml", "cyclic_high_freq.nml",
{"dt":10.0,
"timestep_end":'150'} )

def test(self, returncode: int, out: str, err: str):
"""
Test the output of the interpolation test
"""

if returncode != 0:
print(out)
raise TestFailed(f"Unexpected failure of test executable: {returncode}\n" +
f"stderr:\n" +
f"{err}")

self.plot_output(Path(self.test_working_dir, 'lfric_xios_interp_input.nc'),
Path(self.test_working_dir, 'lfric_xios_interp_output.nc'),
'temporal_field')

if not self.nc_data_match(Path(self.test_working_dir, 'lfric_xios_interp_input.nc'),
Path(self.test_working_dir, 'lfric_xios_interp_output.nc'),
'temporal_field'):
raise TestFailed("Output data does not match input data for same time values")

return "Reading and interpolating data okay..."

class LfricXiosNonSyncInterpTest(LFRicXiosTest): # pylint: disable=too-few-public-methods
"""
Tests the LFRic-XIOS temporal reading functionality for a full set of non-cyclic data
"""

def __init__(self):
super().__init__(command=[sys.argv[1], "cyclic_high_freq.nml"], processes=1)
self.gen_data('temporal_data.cdl', 'lfric_xios_interp_input.nc')
self.gen_data('cyclic_high_freq_kgo.cdl', 'cyclic_high_freq_kgo.nc')
self.gen_config( "cyclic_base.nml", "cyclic_high_freq.nml",
{"dt":10.0,
"calendar_start":"2024-01-01 15:03:20",
"timestep_end":'30'} )

def test(self, returncode: int, out: str, err: str):
"""
Test the output of the interpolation test
"""

if returncode != 0:
print(out)
raise TestFailed(f"Unexpected failure of test executable: {returncode}\n" +
f"stderr:\n" +
f"{err}")

self.plot_output(Path(self.test_working_dir, 'lfric_xios_interp_input.nc'),
Path(self.test_working_dir, 'lfric_xios_interp_output.nc'),
'temporal_field')

if not self.nc_data_match(Path(self.test_working_dir, 'lfric_xios_interp_input.nc'),
Path(self.test_working_dir, 'lfric_xios_interp_output.nc'),
'temporal_field'):
raise TestFailed("Output data does not match input data for same time values")

return "Reading and interpolating data okay..."



##############################################################################
if __name__ == "__main__":
TestEngine.run(LfricXiosFullInterpTest())
TestEngine.run(LfricXiosNonSyncInterpTest())
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran flake8 on this file and there's a fair few lint errors so please could you remove those

7 changes: 7 additions & 0 deletions components/lfric-xios/source/lfric_xios_constants_mod.f90
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,11 @@ module lfric_xios_constants_mod
!< The largest integer that can be output by XIOS
integer(kind=i_def), parameter :: xios_max_int = huge(0_int16)

!< Enums for configuring XIOS durations within LFRic
!< These must be large-ish prime numbers
integer, public, parameter :: lx_second = 107
integer, public, parameter :: lx_day = 1049
integer, public, parameter :: lx_month = 10259
integer, public, parameter :: lx_year = 100537

end module lfric_xios_constants_mod
18 changes: 14 additions & 4 deletions components/lfric-xios/source/lfric_xios_file_mod.f90
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ module lfric_xios_file_mod
logical :: context_init_read = .true.
!> Temporal controller for file
type(temporal_type) :: temporal
!> Update frequency for temporal control (enum, optional)
integer(i_def) :: update_freq = 0

!> XIOS representations
!> Internal XIOS representation of the file
Expand Down Expand Up @@ -202,11 +204,14 @@ end subroutine register_diagnostics_file
!> @param[in] fields_in_file Array of fields contained in the file
!> @param[in] is_diag Is it a diagnostics file?
!> @param[in] diag_always_on_sampling Is the always-on sampling mode selected?
!> @param[in] file_convention Enum denoting the file convention to use for the file
!> @param[in] update_freq Enum for update frequency to be passed to temporal
!! controller (optional, only relevant for time series files)
function lfric_xios_file_constructor( file_name, xios_id, io_mode, freq, &
operation, cyclic, field_group_id, &
fields_in_file, is_diag, &
diag_always_on_sampling, &
file_convention ) result(self)
file_convention, update_freq ) result(self)

implicit none

Expand All @@ -223,7 +228,7 @@ function lfric_xios_file_constructor( file_name, xios_id, io_mode, freq, &
logical(l_def), optional, intent(in) :: is_diag
logical(l_def), optional, intent(in) :: diag_always_on_sampling
integer(i_def), optional, intent(in) :: file_convention

integer(i_def), optional, intent(in) :: update_freq
type(field_collection_iterator_type) :: iter
class(field_parent_type), pointer :: fld => null()

Expand All @@ -241,6 +246,10 @@ function lfric_xios_file_constructor( file_name, xios_id, io_mode, freq, &
self%cyclic = cyclic
end if

if (present(update_freq)) then
self%update_freq = update_freq
end if

if (present(freq)) then
if (freq < 0) then ! we are going to allow freq = 0 (= no_freq)
call log_event( "XIOS files cannot have negative frequency", &
Expand Down Expand Up @@ -424,8 +433,9 @@ subroutine register_with_context(self)
! the temporal object initialiser which will tell XIOS which time entry
! to start reading from
call xios_set_attr(self%handle, cyclic=self%cyclic)
call self%temporal%initialise( self%xios_id, self%path, self%fields, &
self%frequency, self%cyclic, record_offset )
call self%temporal%initialise( self%xios_id, self%path, self%fields, &
self%frequency, self%cyclic, self%update_freq, &
record_offset )
call xios_set_attr(self%handle, record_offset=record_offset)
end if

Expand Down
Loading
Loading