From ee284580ac4dfba06a172eca8f76ac65da8f66d5 Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Thu, 11 Jun 2026 14:41:44 +0100 Subject: [PATCH 1/9] #3170 add support for real64/32 field types --- config/psyclone.cfg | 4 +++- doc/user_guide/lfric.rst | 6 +++++- src/psyclone/domain/lfric/lfric_constants.py | 12 ++++++++++++ src/psyclone/tests/domain/lfric/lfric_kern_test.py | 4 ++-- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/config/psyclone.cfg b/config/psyclone.cfg index c368420a8a..6765dab88c 100644 --- a/config/psyclone.cfg +++ b/config/psyclone.cfg @@ -91,7 +91,9 @@ precision_map = i_def: 4, r_solver: 4, r_tran: 8, r_bl: 8, - r_um: 8 + r_um: 8, + real64: 8, + real32: 4 # Specify whether we generate code to perform runtime correctness checks. # Allowed values: diff --git a/doc/user_guide/lfric.rst b/doc/user_guide/lfric.rst index 268560d12a..47f576fabf 100644 --- a/doc/user_guide/lfric.rst +++ b/doc/user_guide/lfric.rst @@ -432,7 +432,7 @@ Mixed Precision The LFRic API supports the ability to specify the precision required by the model via precision variables. To make use of this, the code -developer must declare scalars, arrays, fields and operators in the algorithm +developer must declare scalars, arrays, fields and operators in the Algorithm layer with the required LFRic-supported precision. In the current implementation there are two supported precisions for ``REAL`` data and one each for ``INTEGER`` and ``LOGICAL`` data. The actual precision used in @@ -471,6 +471,10 @@ associated kernel metadata description and their precision: +--------------------------+---------------------------------------+-----------+ | R_TRAN_FIELD_TYPE | GH_FIELD, GH_REAL | R_TRAN | +--------------------------+---------------------------------------+-----------+ +| FIELD_REAL32_TYPE | GH_FIELD, GH_REAL | REAL32 | ++--------------------------+---------------------------------------+-----------+ +| FIELD_REAL64_TYPE | GH_FIELD, GH_REAL | REAL64 | ++--------------------------+---------------------------------------+-----------+ | INTEGER_FIELD_TYPE | GH_FIELD, GH_INTEGER | I_DEF | +--------------------------+---------------------------------------+-----------+ | OPERATOR_TYPE | GH_OPERATOR, GH_REAL | R_DEF | diff --git a/src/psyclone/domain/lfric/lfric_constants.py b/src/psyclone/domain/lfric/lfric_constants.py index f0ba248c0b..c6001e943e 100644 --- a/src/psyclone/domain/lfric/lfric_constants.py +++ b/src/psyclone/domain/lfric/lfric_constants.py @@ -420,6 +420,18 @@ def __init__(self) -> None: "proxy_type": "r_bl_field_proxy_type", "intrinsic": "real", "kind": "r_bl"}, + # 'real'-valued field with explicit 32-bit precision + "r_32_field": {"module": "field_real32_mod", + "type": "field_real32_type", + "proxy_type": "field_real32_proxy_type", + "intrinsic": "real", + "kind": "real32"}, + # 'real'-valued field with explicit 64-bit precision + "r_64_field": {"module": "field_real64_mod", + "type": "field_real64_type", + "proxy_type": "field_real64_proxy_type", + "intrinsic": "real", + "kind": "real64"}, # 'integer'-valued field with data of kind 'i_def' "integer_field": {"module": "integer_field_mod", "type": "integer_field_type", diff --git a/src/psyclone/tests/domain/lfric/lfric_kern_test.py b/src/psyclone/tests/domain/lfric/lfric_kern_test.py index d827e86bb0..20f6e23584 100644 --- a/src/psyclone/tests/domain/lfric/lfric_kern_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_kern_test.py @@ -411,8 +411,8 @@ def test_validate_kernel_code_arg(monkeypatch): "following error was found: An argument to an LFRic kernel must have a" " precision defined by either a recognised LFRic type parameter (one " "of ['i_def', 'l_def', 'r_bl', 'r_def', 'r_double', 'r_ncdf', " - "'r_quad', 'r_second', 'r_single', 'r_solver', 'r_tran', " - "'r_um']) or an integer number of bytes but argument " + "'r_quad', 'r_second', 'r_single', 'r_solver', 'r_tran', 'r_um', " + "'real32', 'real64']) or an integer number of bytes but argument " "'generic_int_scalar' to kernel 'dummy' has precision " "Precision.UNDEFINED" in str(info.value)) From 804921a3164b17fd288f6a0a4d720810f9839f20 Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Thu, 11 Jun 2026 18:58:50 +0100 Subject: [PATCH 2/9] #3170 add support for real32/64 operators too [skip ci] --- src/psyclone/domain/lfric/lfric_constants.py | 23 ++++++++++ src/psyclone/domain/lfric/lfric_types.py | 5 ++- src/psyclone/lfric.py | 42 ++++++++++--------- src/psyclone/tests/lfric_test.py | 26 ++++++++---- src/psyclone/tests/parse/algorithm_test.py | 5 ++- .../lfric/26.8_mixed_precision_args.f90 | 9 +++- 6 files changed, 78 insertions(+), 32 deletions(-) diff --git a/src/psyclone/domain/lfric/lfric_constants.py b/src/psyclone/domain/lfric/lfric_constants.py index c6001e943e..ad311852de 100644 --- a/src/psyclone/domain/lfric/lfric_constants.py +++ b/src/psyclone/domain/lfric/lfric_constants.py @@ -382,6 +382,10 @@ def __init__(self) -> None: OrderedDict(zip(LFRicConstants.VALID_INTRINSIC_TYPES, ["r_def", "i_def", "l_def"])) + # Those kind symbols used in LFRic that are actually + # Fortran intrinsics (and thus don't come from constants_mod). + LFRicConstants.INTRINSIC_KINDS = ("real32", "real64") + # ---------- Infrastructure module maps ------------------------------- # Dictionary allowing us to look-up the name of the Fortran module, @@ -444,6 +448,18 @@ def __init__(self) -> None: "proxy_type": "operator_proxy_type", "intrinsic": "real", "kind": "r_def"}, + # 'real'-valued operator with real32 data + "r_32_operator": {"module": "operator_real32_mod", + "type": "operator_real32_type", + "proxy_type": "operator_real32_proxy_type", + "intrinsic": "real", + "kind": "real32"}, + # 'real'-valued operator with real32 data + "r_64_operator": {"module": "operator_real64_mod", + "type": "operator_real64_type", + "proxy_type": "operator_real64_proxy_type", + "intrinsic": "real", + "kind": "real64"}, # 'real'-valued operator with data of kind 'r_solver' "r_solver_operator": { "module": "r_solver_operator_mod", @@ -466,6 +482,13 @@ def __init__(self) -> None: "intrinsic": "real", "kind": "r_solver"}} + # Construct a reverse map from type to the name of the LFRic + # data type for all real types. + LFRicConstants.REAL_DATA_TYPE_RMAP = {} + for key, value in LFRicConstants.DATA_TYPE_MAP.items(): + if value["intrinsic"] == "real": + LFRicConstants.REAL_DATA_TYPE_RMAP[value["type"]] = key + # Mapping from a vector type used in the algorithm-layer to # the actual type used in the PSy-layer. LFRicConstants.FIELD_VECTOR_TO_FIELD_MAP = { diff --git a/src/psyclone/domain/lfric/lfric_types.py b/src/psyclone/domain/lfric/lfric_types.py index a9ac11762e..ef54ab469b 100644 --- a/src/psyclone/domain/lfric/lfric_types.py +++ b/src/psyclone/domain/lfric/lfric_types.py @@ -624,7 +624,10 @@ def add_precision_symbol(table: SymbolTable, raise ValueError(f"'{name}' is not a recognised LFRic precision.") const = LFRicConstants() - mod_name = const.UTILITIES_MOD_MAP["constants"]["module"] + if name in const.INTRINSIC_KINDS: + mod_name = "iso_fortran_env" + else: + mod_name = const.UTILITIES_MOD_MAP["constants"]["module"] sym = table.lookup(name, otherwise=None) diff --git a/src/psyclone/lfric.py b/src/psyclone/lfric.py index 95fdc40e51..3390bbe020 100644 --- a/src/psyclone/lfric.py +++ b/src/psyclone/lfric.py @@ -5990,20 +5990,14 @@ def _init_field_properties(self, alg_datatype, check=True): if not check: # Use the default as we are ignoring any algorithm info argtype = "field" - elif alg_datatype == "field_type": - argtype = "field" - elif alg_datatype == "r_bl_field_type": - argtype = "r_bl_field" - elif alg_datatype == "r_solver_field_type": - argtype = "r_solver_field" - elif alg_datatype == "r_tran_field_type": - argtype = "r_tran_field" else: - raise GenerationError( - f"The metadata for argument '{self.name}' in kernel " - f"'{self._call.name}' specifies that this is a real " - f"field, however it is declared as a " - f"'{alg_datatype}' in the algorithm code.") + argtype = const.REAL_DATA_TYPE_RMAP.get(alg_datatype, None) + if not argtype: + raise GenerationError( + f"The metadata for argument '{self.name}' in kernel " + f"'{self._call.name}' specifies that this is a real " + f"field, however it is declared as a " + f"'{alg_datatype}' in the algorithm code.") elif self.intrinsic_type == "integer": if check and alg_datatype != "integer_field_type": @@ -6484,13 +6478,21 @@ def _find_or_create_type(mod_name: str, try: kind_symbol = symtab.lookup(kind_name) except KeyError: - mod_map = LFRicConstants().UTILITIES_MOD_MAP - const_mod = mod_map["constants"]["module"] - constants_container = symtab.find_or_create( - const_mod, symbol_type=ContainerSymbol) - kind_symbol = DataSymbol( - kind_name, ScalarType.integer_type(), - interface=ImportInterface(constants_container)) + if kind_name.lower() in LFRicConstants().INTRINSIC_KINDS: + iso_env_sym = symtab.find_or_create( + "iso_fortran_env", symbol_type=ContainerSymbol, + is_intrinsic=True) + kind_symbol = DataSymbol( + kind_name, ScalarType.integer_type(), + interface=ImportInterface(iso_env_sym)) + else: + mod_map = LFRicConstants().UTILITIES_MOD_MAP + const_mod = mod_map["constants"]["module"] + constants_container = symtab.find_or_create( + const_mod, symbol_type=ContainerSymbol) + kind_symbol = DataSymbol( + kind_name, ScalarType.integer_type(), + interface=ImportInterface(constants_container)) symtab.add(kind_symbol) dts = ScalarType(prim_type, Reference(kind_symbol)) if self.is_scalar_array and self._array_ndims >= 1: diff --git a/src/psyclone/tests/lfric_test.py b/src/psyclone/tests/lfric_test.py index dcb499f3db..1e1caa4276 100644 --- a/src/psyclone/tests/lfric_test.py +++ b/src/psyclone/tests/lfric_test.py @@ -68,6 +68,7 @@ UnsupportedFortranType) from psyclone.psyir.backend.visitor import VisitorError from psyclone.tests.lfric_build import LFRicBuild +from psyclone.tests.utilities import get_invoke # constants @@ -1973,10 +1974,9 @@ class for an r_tran_operator. ''' # Use one of the test algorithms to create an instance of # LFRicKernelArgument that describes an r_tran_operator. - _, invoke_info = parse( - os.path.join(BASE_PATH, "26.8_mixed_precision_args.f90"), api=TEST_API) - psy = PSyFactory(TEST_API, distributed_memory=False).create(invoke_info) - operator_argument = psy.invokes.invoke_list[0].schedule.args[8] + psy, invoke = get_invoke("26.8_mixed_precision_args.f90", api=TEST_API, + dist_mem=False, idx=0) + operator_argument = invoke.schedule.args[8] assert operator_argument.is_operator assert operator_argument._precision == "r_tran" assert operator_argument._data_type == "r_tran_operator_type" @@ -3867,10 +3867,8 @@ def test_mixed_precision_args(tmp_path): declared in the algorithm layer. ''' - _, invoke_info = parse( - os.path.join(BASE_PATH, "26.8_mixed_precision_args.f90"), - api=TEST_API) - psy = PSyFactory(TEST_API, distributed_memory=True).create(invoke_info) + psy, _ = get_invoke("26.8_mixed_precision_args.f90", + TEST_API, idx=0, dist_mem=True) generated_code = str(psy.gen) assert ("use constants_mod, only : r_bl, r_def, r_solver, r_tran\n" @@ -3886,11 +3884,14 @@ def test_mixed_precision_args(tmp_path): use r_tran_operator_mod, only : r_tran_operator_proxy_type, \ r_tran_operator_type use r_bl_field_mod, only : r_bl_field_proxy_type, r_bl_field_type + use, intrinsic :: iso_fortran_env, only : real32 + use field_real32_mod, only : field_real32_proxy_type, field_real32_type implicit none """ in generated_code assert """subroutine invoke_0(scalar_r_def, field_r_def, operator_r_def, \ scalar_r_solver, field_r_solver, operator_r_solver, scalar_r_tran, \ -field_r_tran, operator_r_tran, scalar_r_bl, field_r_bl) +field_r_tran, operator_r_tran, scalar_r_bl, field_r_bl, scalar_real32, \ +field_real32) use mesh_mod, only : mesh_type use constants_mod, only : i_def real(kind=r_def), intent(in) :: scalar_r_def @@ -3904,6 +3905,8 @@ def test_mixed_precision_args(tmp_path): type(r_tran_operator_type), intent(in) :: operator_r_tran real(kind=r_bl), intent(in) :: scalar_r_bl type(r_bl_field_type), intent(in) :: field_r_bl + real(kind=real32), intent(in) :: scalar_real32 + type(field_real32_type), intent(in) :: field_real32 integer(kind=i_def) :: cell type(mesh_type), pointer :: mesh => null() integer(kind=i_def) :: max_halo_depth_mesh @@ -3911,6 +3914,7 @@ def test_mixed_precision_args(tmp_path): real(kind=r_solver), pointer, dimension(:) :: field_r_solver_data => null() real(kind=r_tran), pointer, dimension(:) :: field_r_tran_data => null() real(kind=r_bl), pointer, dimension(:) :: field_r_bl_data => null() + real(kind=real32), pointer, dimension(:) :: field_real32_data => null() real(kind=r_def), pointer, dimension(:,:,:) :: \ operator_r_def_local_stencil => null() real(kind=r_solver), pointer, dimension(:,:,:) :: \ @@ -3921,6 +3925,7 @@ def test_mixed_precision_args(tmp_path): integer(kind=i_def) :: nlayers_field_r_solver integer(kind=i_def) :: nlayers_field_r_tran integer(kind=i_def) :: nlayers_field_r_bl + integer(kind=i_def) :: nlayers_field_real32 integer(kind=i_def) :: ndf_w3 integer(kind=i_def) :: undf_w3 integer(kind=i_def) :: ndf_w0 @@ -3929,6 +3934,7 @@ def test_mixed_precision_args(tmp_path): type(r_solver_field_proxy_type) :: field_r_solver_proxy type(r_tran_field_proxy_type) :: field_r_tran_proxy type(r_bl_field_proxy_type) :: field_r_bl_proxy + type(field_real32_proxy_type) :: field_real32_proxy type(operator_proxy_type) :: operator_r_def_proxy type(r_solver_operator_proxy_type) :: operator_r_solver_proxy type(r_tran_operator_proxy_type) :: operator_r_tran_proxy @@ -3940,6 +3946,8 @@ def test_mixed_precision_args(tmp_path): integer(kind=i_def) :: loop2_stop integer(kind=i_def) :: loop3_start integer(kind=i_def) :: loop3_stop + integer(kind=i_def) :: loop4_start + integer(kind=i_def) :: loop4_stop """ in generated_code # Test compilation diff --git a/src/psyclone/tests/parse/algorithm_test.py b/src/psyclone/tests/parse/algorithm_test.py index 084a6df105..d1460c971b 100644 --- a/src/psyclone/tests/parse/algorithm_test.py +++ b/src/psyclone/tests/parse/algorithm_test.py @@ -247,7 +247,8 @@ def test_parser_invokeinfo_datatypes_mixed(): args1 = info.calls[0].kcalls[1].args args2 = info.calls[0].kcalls[2].args args3 = info.calls[0].kcalls[3].args - assert len(info.calls[0].kcalls) == 4 + args4 = info.calls[0].kcalls[4].args + assert len(info.calls[0].kcalls) == 5 assert args0[0]._datatype == ("real", "r_def") assert args0[1]._datatype == ("field_type", None) assert args0[2]._datatype == ("operator_type", None) @@ -259,6 +260,8 @@ def test_parser_invokeinfo_datatypes_mixed(): assert args2[2]._datatype == ("r_tran_operator_type", None) assert args3[0]._datatype == ("real", "r_bl") assert args3[1]._datatype == ("r_bl_field_type", None) + assert args4[0]._datatype == ("real", "real32") + assert args4[1]._datatype == ("field_real32_type", None) def test_parser_invokeinfo_datatypes_self(): diff --git a/src/psyclone/tests/test_files/lfric/26.8_mixed_precision_args.f90 b/src/psyclone/tests/test_files/lfric/26.8_mixed_precision_args.f90 index 2c26295563..f161e704db 100644 --- a/src/psyclone/tests/test_files/lfric/26.8_mixed_precision_args.f90 +++ b/src/psyclone/tests/test_files/lfric/26.8_mixed_precision_args.f90 @@ -41,31 +41,38 @@ program mixed_precision + use, intrinsic :: iso_fortran_env, only : real32 use constants_mod, only : r_def, r_solver, r_tran, r_bl use field_mod, only : field_type use r_solver_field_mod, only : r_solver_field_type use r_tran_field_mod, only : r_tran_field_type use r_bl_field_mod, only : r_bl_field_type + use field_real32_mod, only : field_real32_type use operator_mod, only : operator_type use r_solver_operator_mod, only : r_solver_operator_type use r_tran_operator_mod, only : r_tran_operator_type + use operator_real32_mod, only : operator_real32_type use mixed_kernel_mod, only : mixed_kernel_type real(r_def) :: Scalar_r_def real(r_solver) :: sCalar_r_solver real(r_tran) :: scAlar_r_tran real(r_bl) :: scaLar_r_bl + real(real32) :: sCalaR_Real32 type(field_type) :: fieLd_r_def type(r_solver_field_type) :: fielD_r_solver type(r_tran_field_type) :: field_r_tran type(r_bl_field_type) :: fIeld_r_bl + type(field_real32_type) :: field_real32 type(operator_type) :: operator_r_def type(r_solver_operator_type) :: operator_r_solver type(r_tran_operator_type) :: OperatoR_r_tran + type(operator_real32_type) :: oPerator_real32 call invoke(mixed_kernel_type(scalar_r_deF, field_R_def, opeRator_r_def), & mixed_kernel_type(scalar_r_solver, field_r_solver, operator_r_solver), & mixed_kernel_type(scalar_r_tran, field_r_tran, OperatoR_r_tran), & - mixed_kernel_type(scaLar_r_bl, fIeld_r_bl, OperatoR_r_tran)) + mixed_kernel_type(scaLar_r_bl, fIeld_r_bl, OperatoR_r_tran), & + mixed_kernel_type(scalar_real32, field_real32, operator_real32)) end program mixed_precision From 43d80dd879f88b2e294b90bf68b25edcc5006844 Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Fri, 12 Jun 2026 09:07:44 +0100 Subject: [PATCH 3/9] #3170 fix operator support [skip ci] --- src/psyclone/domain/lfric/lfric_constants.py | 4 +-- src/psyclone/lfric.py | 30 +++++++++++--------- src/psyclone/tests/lfric_test.py | 8 +++++- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/psyclone/domain/lfric/lfric_constants.py b/src/psyclone/domain/lfric/lfric_constants.py index ad311852de..9c92cd2eda 100644 --- a/src/psyclone/domain/lfric/lfric_constants.py +++ b/src/psyclone/domain/lfric/lfric_constants.py @@ -483,10 +483,10 @@ def __init__(self) -> None: "kind": "r_solver"}} # Construct a reverse map from type to the name of the LFRic - # data type for all real types. + # data type for all real field types. LFRicConstants.REAL_DATA_TYPE_RMAP = {} for key, value in LFRicConstants.DATA_TYPE_MAP.items(): - if value["intrinsic"] == "real": + if value["intrinsic"] == "real" and "field" in key: LFRicConstants.REAL_DATA_TYPE_RMAP[value["type"]] = key # Mapping from a vector type used in the algorithm-layer to diff --git a/src/psyclone/lfric.py b/src/psyclone/lfric.py index 3390bbe020..7fa97740b5 100644 --- a/src/psyclone/lfric.py +++ b/src/psyclone/lfric.py @@ -6017,20 +6017,21 @@ def _init_field_properties(self, alg_datatype, check=True): self._proxy_data_type = const.DATA_TYPE_MAP[argtype]["proxy_type"] self._module_name = const.DATA_TYPE_MAP[argtype]["module"] - def _init_operator_properties(self, alg_datatype, check=True): + def _init_operator_properties(self, + alg_datatype: Optional[str], + check: bool = True) -> None: '''Set up the properties of this operator using algorithm datatype information if it is available. - :param alg_datatype: the datatype of this argument as \ - specified in the algorithm layer or None if it is not \ - known. - :type alg_datatype: str or NoneType - :param bool check: whether to use the algorithm \ - information. Optional argument that defaults to True. - :raises GenerationError: if the datatype for a gh_operator \ - could not be found in the algorithm layer (and check is \ + :param alg_datatype: the datatype of this argument as specified in + the algorithm layer or None if it is not known. + :param check: whether to use the algorithm information. Optional + argument that defaults to True. + + :raises GenerationError: if the datatype for a gh_operator + could not be found in the algorithm layer (and check is True). - :raises GenerationError: if the datatype specified in the \ + :raises GenerationError: if the datatype specified in the algorithm layer is inconsistent with the kernel metadata. :raises InternalError: if this argument is not an operator. @@ -6042,9 +6043,8 @@ def _init_operator_properties(self, alg_datatype, check=True): # Use the default as we are ignoring any algorithm info argtype = "operator" elif not alg_datatype: - # Raise an exception as we require algorithm - # information to determine the precision of the - # operator + # Raise an exception as we require algorithm information + # to determine the precision of the operator raise GenerationError( f"It was not possible to determine the operator type " f"from the algorithm layer for argument '{self.name}' " @@ -6055,6 +6055,10 @@ def _init_operator_properties(self, alg_datatype, check=True): argtype = "r_solver_operator" elif alg_datatype == "r_tran_operator_type": argtype = "r_tran_operator" + elif alg_datatype == "operator_real64_type": + argtype = "r_64_operator" + elif alg_datatype == "operator_real32_type": + argtype = "r_32_operator" else: raise GenerationError( f"The metadata for argument '{self.name}' in kernel " diff --git a/src/psyclone/tests/lfric_test.py b/src/psyclone/tests/lfric_test.py index 1e1caa4276..40e0c95cad 100644 --- a/src/psyclone/tests/lfric_test.py +++ b/src/psyclone/tests/lfric_test.py @@ -3886,12 +3886,14 @@ def test_mixed_precision_args(tmp_path): use r_bl_field_mod, only : r_bl_field_proxy_type, r_bl_field_type use, intrinsic :: iso_fortran_env, only : real32 use field_real32_mod, only : field_real32_proxy_type, field_real32_type + use operator_real32_mod, only : operator_real32_proxy_type, \ +operator_real32_type implicit none """ in generated_code assert """subroutine invoke_0(scalar_r_def, field_r_def, operator_r_def, \ scalar_r_solver, field_r_solver, operator_r_solver, scalar_r_tran, \ field_r_tran, operator_r_tran, scalar_r_bl, field_r_bl, scalar_real32, \ -field_real32) +field_real32, operator_real32) use mesh_mod, only : mesh_type use constants_mod, only : i_def real(kind=r_def), intent(in) :: scalar_r_def @@ -3907,6 +3909,7 @@ def test_mixed_precision_args(tmp_path): type(r_bl_field_type), intent(in) :: field_r_bl real(kind=real32), intent(in) :: scalar_real32 type(field_real32_type), intent(in) :: field_real32 + type(operator_real32_type), intent(in) :: operator_real32 integer(kind=i_def) :: cell type(mesh_type), pointer :: mesh => null() integer(kind=i_def) :: max_halo_depth_mesh @@ -3921,6 +3924,8 @@ def test_mixed_precision_args(tmp_path): operator_r_solver_local_stencil => null() real(kind=r_tran), pointer, dimension(:,:,:) :: \ operator_r_tran_local_stencil => null() + real(kind=real32), pointer, dimension(:,:,:) :: \ +operator_real32_local_stencil => null() integer(kind=i_def) :: nlayers_field_r_def integer(kind=i_def) :: nlayers_field_r_solver integer(kind=i_def) :: nlayers_field_r_tran @@ -3938,6 +3943,7 @@ def test_mixed_precision_args(tmp_path): type(operator_proxy_type) :: operator_r_def_proxy type(r_solver_operator_proxy_type) :: operator_r_solver_proxy type(r_tran_operator_proxy_type) :: operator_r_tran_proxy + type(operator_real32_proxy_type) :: operator_real32_proxy integer(kind=i_def) :: loop0_start integer(kind=i_def) :: loop0_stop integer(kind=i_def) :: loop1_start From e0537c9c5f73ec1984ac532648006abe0fcec4fc Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Fri, 12 Jun 2026 09:21:44 +0100 Subject: [PATCH 4/9] #3170 tidy implementation --- src/psyclone/domain/lfric/lfric_constants.py | 5 +++-- src/psyclone/domain/lfric/lfric_types.py | 11 +++++++++-- src/psyclone/lfric.py | 10 +++++++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/psyclone/domain/lfric/lfric_constants.py b/src/psyclone/domain/lfric/lfric_constants.py index 9c92cd2eda..93f560f796 100644 --- a/src/psyclone/domain/lfric/lfric_constants.py +++ b/src/psyclone/domain/lfric/lfric_constants.py @@ -382,11 +382,12 @@ def __init__(self) -> None: OrderedDict(zip(LFRicConstants.VALID_INTRINSIC_TYPES, ["r_def", "i_def", "l_def"])) + # ---------- Infrastructure module maps ------------------------------- + # Those kind symbols used in LFRic that are actually # Fortran intrinsics (and thus don't come from constants_mod). LFRicConstants.INTRINSIC_KINDS = ("real32", "real64") - - # ---------- Infrastructure module maps ------------------------------- + LFRicConstants.FORTRAN_ISO_MOD_NAME = "iso_fortran_env" # Dictionary allowing us to look-up the name of the Fortran module, # type and proxy-type associated with each LFRic data structure type. diff --git a/src/psyclone/domain/lfric/lfric_types.py b/src/psyclone/domain/lfric/lfric_types.py index ef54ab469b..4a0e4e9a29 100644 --- a/src/psyclone/domain/lfric/lfric_types.py +++ b/src/psyclone/domain/lfric/lfric_types.py @@ -608,6 +608,9 @@ def add_precision_symbol(table: SymbolTable, table then add it. Also ensure that the Container symbol from which it is imported is in the table. + Also supports Fortran intrinsic kinds imported from the + iso_fortran_env module. + :param table: the symbol table to use. :param name: name of the LFRic precision symbol to add to table. @@ -625,9 +628,12 @@ def add_precision_symbol(table: SymbolTable, const = LFRicConstants() if name in const.INTRINSIC_KINDS: - mod_name = "iso_fortran_env" + # This is an intrinsic precision (e.g. real64) + mod_name = const.FORTRAN_ISO_MOD_NAME + is_intrinsic = True else: mod_name = const.UTILITIES_MOD_MAP["constants"]["module"] + is_intrinsic = False sym = table.lookup(name, otherwise=None) @@ -641,7 +647,8 @@ def add_precision_symbol(table: SymbolTable, return sym constants_mod = table.find_or_create(mod_name, - symbol_type=ContainerSymbol) + symbol_type=ContainerSymbol, + is_intrinsic=is_intrinsic) sym = DataSymbol(name, ScalarType.integer_type(), interface=ImportInterface(constants_mod)) table.add(sym) diff --git a/src/psyclone/lfric.py b/src/psyclone/lfric.py index 7fa97740b5..dcf09a52ea 100644 --- a/src/psyclone/lfric.py +++ b/src/psyclone/lfric.py @@ -6478,19 +6478,23 @@ def _find_or_create_type(mod_name: str, raise NotImplementedError( f"Unsupported scalar type '{self.intrinsic_type}'") + const = LFRicConstants() kind_name = self.precision try: kind_symbol = symtab.lookup(kind_name) except KeyError: - if kind_name.lower() in LFRicConstants().INTRINSIC_KINDS: + if kind_name.lower() in const.INTRINSIC_KINDS: + # This is an intrinsic kind (e.g. real32) so it comes + # from the intrinsic ISO module. iso_env_sym = symtab.find_or_create( - "iso_fortran_env", symbol_type=ContainerSymbol, + const.FORTRAN_ISO_MOD_NAME, + symbol_type=ContainerSymbol, is_intrinsic=True) kind_symbol = DataSymbol( kind_name, ScalarType.integer_type(), interface=ImportInterface(iso_env_sym)) else: - mod_map = LFRicConstants().UTILITIES_MOD_MAP + mod_map = const.UTILITIES_MOD_MAP const_mod = mod_map["constants"]["module"] constants_container = symtab.find_or_create( const_mod, symbol_type=ContainerSymbol) From d321fed37884f5faac77847447014fc63aa07e0d Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Fri, 12 Jun 2026 09:55:24 +0100 Subject: [PATCH 5/9] #3170 update UG --- doc/user_guide/lfric.rst | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/doc/user_guide/lfric.rst b/doc/user_guide/lfric.rst index 47f576fabf..31ab9e5147 100644 --- a/doc/user_guide/lfric.rst +++ b/doc/user_guide/lfric.rst @@ -483,13 +483,18 @@ associated kernel metadata description and their precision: +--------------------------+---------------------------------------+-----------+ | R_TRAN_OPERATOR_TYPE | GH_OPERATOR, GH_REAL | R_TRAN | +--------------------------+---------------------------------------+-----------+ +| OPERATOR_REAL32_TYPE | GH_OPERATOR, GH_REAL | REAL32 | ++--------------------------+---------------------------------------+-----------+ +| OPERATOR_REAL64_TYPE | GH_OPERATOR, GH_REAL | REAL64 | ++--------------------------+---------------------------------------+-----------+ | COLUMNWISE_OPERATOR_TYPE | GH_COLUMNWISE_OPERATOR, GH_REAL | R_SOLVER | +--------------------------+---------------------------------------+-----------+ As can be seen from the above table, the kernel metadata does not capture all of the precision options. For example, from the metadata it is not possible to determine whether a ``REAL`` scalar, ``REAL`` field -or ``REAL`` operator has precision ``R_DEF``, ``R_SOLVER`` or ``R_TRAN``. +or ``REAL`` operator has precision ``R_DEF``, ``R_SOLVER``, ``R_TRAN`` +or one of the Fortran intrinsic precisions (``REAL32``, ``REAL64``). If a scalar, array, field, or operator is specified with a particular precision in the algorithm layer then any associated kernels that it @@ -605,6 +610,10 @@ outlined in the table below: +-------------------------+------------------+--------------+ | ``r_tran_field_type`` | ``real`` | ``r_tran`` | +-------------------------+------------------+--------------+ +| ``field_real32_type`` | ``real`` | ``real32`` | ++-------------------------+------------------+--------------+ +| ``field_real64_type`` | ``real`` | ``real64`` | ++-------------------------+------------------+--------------+ | ``integer_field_type`` | ``integer`` | ``i_def`` | +-------------------------+------------------+--------------+ @@ -705,7 +714,8 @@ a message that indicates the problem. | Fortran Datatype | Supported Precision | +==================+==========================+ | ``real`` | ``r_def``, ``r_bl``, | -| | ``r_solver``, ``r_tran`` | +| | ``r_solver``, ``r_tran``,| +| | ``real32``, ``real64`` | +------------------+--------------------------+ | ``integer`` | ``i_def`` | +------------------+--------------------------+ @@ -735,6 +745,10 @@ outlined in the table below: +----------------------------+------------------+--------------+ | ``r_tran_operator_type`` | ``real`` | ``r_tran`` | +----------------------------+------------------+--------------+ +| ``operator_real32_type`` | ``real`` | ``real32`` | ++----------------------------+------------------+--------------+ +| ``operator_real64_type`` | ``real`` | ``real64`` | ++----------------------------+------------------+--------------+ .. _lfric-mixed-precision-cma-operators: From 228bd82e43246e7e7669930fb255adf3de43190e Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Fri, 12 Jun 2026 10:37:17 +0100 Subject: [PATCH 6/9] #3170 extend test to include real64 operator --- config/psyclone.cfg | 4 +-- src/psyclone/lfric.py | 30 ++++++++----------- src/psyclone/tests/lfric_test.py | 16 ++++++++-- .../lfric/26.8_mixed_precision_args.f90 | 14 ++++++--- 4 files changed, 39 insertions(+), 25 deletions(-) diff --git a/config/psyclone.cfg b/config/psyclone.cfg index 6765dab88c..0dfea232f6 100644 --- a/config/psyclone.cfg +++ b/config/psyclone.cfg @@ -92,8 +92,8 @@ precision_map = i_def: 4, r_tran: 8, r_bl: 8, r_um: 8, - real64: 8, - real32: 4 + real64: 8, + real32: 4 # Specify whether we generate code to perform runtime correctness checks. # Allowed values: diff --git a/src/psyclone/lfric.py b/src/psyclone/lfric.py index dcf09a52ea..4088675596 100644 --- a/src/psyclone/lfric.py +++ b/src/psyclone/lfric.py @@ -5954,22 +5954,22 @@ def _init_scalar_properties( self._precision = const.SCALAR_PRECISION_MAP[ self.intrinsic_type] - def _init_field_properties(self, alg_datatype, check=True): + def _init_field_properties(self, + alg_datatype: Optional[str], + check: bool = True) -> None: '''Set up the properties of this field using algorithm datatype information if it is available. - :param alg_datatype: the datatype of this argument as \ - specified in the algorithm layer or None if it is not \ - known. - :type alg_datatype: str or NoneType - :param bool check: whether to use the algorithm \ + :param alg_datatype: the datatype of this argument as + specified in the algorithm layer or None if it is not known. + :param check: whether to use the algorithm information. Optional argument that defaults to True. - :raises GenerationError: if the datatype for a gh_field \ + :raises GenerationError: if the datatype for a gh_field could not be found in the algorithm layer. - :raises GenerationError: if the datatype specified in the \ + :raises GenerationError: if the datatype specified in the algorithm layer is inconsistent with the kernel metadata. - :raises InternalError: if the intrinsic type of the field is \ + :raises InternalError: if the intrinsic type of the field is not supported (i.e. is not real or integer). ''' @@ -6486,21 +6486,17 @@ def _find_or_create_type(mod_name: str, if kind_name.lower() in const.INTRINSIC_KINDS: # This is an intrinsic kind (e.g. real32) so it comes # from the intrinsic ISO module. - iso_env_sym = symtab.find_or_create( + cntr_sym = symtab.find_or_create( const.FORTRAN_ISO_MOD_NAME, symbol_type=ContainerSymbol, is_intrinsic=True) - kind_symbol = DataSymbol( - kind_name, ScalarType.integer_type(), - interface=ImportInterface(iso_env_sym)) else: mod_map = const.UTILITIES_MOD_MAP const_mod = mod_map["constants"]["module"] - constants_container = symtab.find_or_create( + cntr_sym = symtab.find_or_create( const_mod, symbol_type=ContainerSymbol) - kind_symbol = DataSymbol( - kind_name, ScalarType.integer_type(), - interface=ImportInterface(constants_container)) + kind_symbol = DataSymbol(kind_name, ScalarType.integer_type(), + interface=ImportInterface(cntr_sym)) symtab.add(kind_symbol) dts = ScalarType(prim_type, Reference(kind_symbol)) if self.is_scalar_array and self._array_ndims >= 1: diff --git a/src/psyclone/tests/lfric_test.py b/src/psyclone/tests/lfric_test.py index 40e0c95cad..1f0469bc8e 100644 --- a/src/psyclone/tests/lfric_test.py +++ b/src/psyclone/tests/lfric_test.py @@ -3884,16 +3884,19 @@ def test_mixed_precision_args(tmp_path): use r_tran_operator_mod, only : r_tran_operator_proxy_type, \ r_tran_operator_type use r_bl_field_mod, only : r_bl_field_proxy_type, r_bl_field_type - use, intrinsic :: iso_fortran_env, only : real32 + use, intrinsic :: iso_fortran_env, only : real32, real64 use field_real32_mod, only : field_real32_proxy_type, field_real32_type use operator_real32_mod, only : operator_real32_proxy_type, \ operator_real32_type + use field_real64_mod, only : field_real64_proxy_type, field_real64_type + use operator_real64_mod, only : operator_real64_proxy_type, \ +operator_real64_type implicit none """ in generated_code assert """subroutine invoke_0(scalar_r_def, field_r_def, operator_r_def, \ scalar_r_solver, field_r_solver, operator_r_solver, scalar_r_tran, \ field_r_tran, operator_r_tran, scalar_r_bl, field_r_bl, scalar_real32, \ -field_real32, operator_real32) +field_real32, operator_real32, scalar_real64, field_real64, operator_real64) use mesh_mod, only : mesh_type use constants_mod, only : i_def real(kind=r_def), intent(in) :: scalar_r_def @@ -3910,6 +3913,9 @@ def test_mixed_precision_args(tmp_path): real(kind=real32), intent(in) :: scalar_real32 type(field_real32_type), intent(in) :: field_real32 type(operator_real32_type), intent(in) :: operator_real32 + real(kind=real64), intent(in) :: scalar_real64 + type(field_real64_type), intent(in) :: field_real64 + type(operator_real64_type), intent(in) :: operator_real64 integer(kind=i_def) :: cell type(mesh_type), pointer :: mesh => null() integer(kind=i_def) :: max_halo_depth_mesh @@ -3918,6 +3924,7 @@ def test_mixed_precision_args(tmp_path): real(kind=r_tran), pointer, dimension(:) :: field_r_tran_data => null() real(kind=r_bl), pointer, dimension(:) :: field_r_bl_data => null() real(kind=real32), pointer, dimension(:) :: field_real32_data => null() + real(kind=real64), pointer, dimension(:) :: field_real64_data => null() real(kind=r_def), pointer, dimension(:,:,:) :: \ operator_r_def_local_stencil => null() real(kind=r_solver), pointer, dimension(:,:,:) :: \ @@ -3926,11 +3933,14 @@ def test_mixed_precision_args(tmp_path): operator_r_tran_local_stencil => null() real(kind=real32), pointer, dimension(:,:,:) :: \ operator_real32_local_stencil => null() + real(kind=real64), pointer, dimension(:,:,:) :: \ +operator_real64_local_stencil => null() integer(kind=i_def) :: nlayers_field_r_def integer(kind=i_def) :: nlayers_field_r_solver integer(kind=i_def) :: nlayers_field_r_tran integer(kind=i_def) :: nlayers_field_r_bl integer(kind=i_def) :: nlayers_field_real32 + integer(kind=i_def) :: nlayers_field_real64 integer(kind=i_def) :: ndf_w3 integer(kind=i_def) :: undf_w3 integer(kind=i_def) :: ndf_w0 @@ -3940,10 +3950,12 @@ def test_mixed_precision_args(tmp_path): type(r_tran_field_proxy_type) :: field_r_tran_proxy type(r_bl_field_proxy_type) :: field_r_bl_proxy type(field_real32_proxy_type) :: field_real32_proxy + type(field_real64_proxy_type) :: field_real64_proxy type(operator_proxy_type) :: operator_r_def_proxy type(r_solver_operator_proxy_type) :: operator_r_solver_proxy type(r_tran_operator_proxy_type) :: operator_r_tran_proxy type(operator_real32_proxy_type) :: operator_real32_proxy + type(operator_real64_proxy_type) :: operator_real64_proxy integer(kind=i_def) :: loop0_start integer(kind=i_def) :: loop0_stop integer(kind=i_def) :: loop1_start diff --git a/src/psyclone/tests/test_files/lfric/26.8_mixed_precision_args.f90 b/src/psyclone/tests/test_files/lfric/26.8_mixed_precision_args.f90 index f161e704db..3c1ed71200 100644 --- a/src/psyclone/tests/test_files/lfric/26.8_mixed_precision_args.f90 +++ b/src/psyclone/tests/test_files/lfric/26.8_mixed_precision_args.f90 @@ -31,8 +31,8 @@ ! ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ! POSSIBILITY OF SUCH DAMAGE. ! ----------------------------------------------------------------------------- -! Author: R. W. Ford, STFC Daresbury Laboratory -! Modified: I. Kavcic, Met Office +! Authors: R. W. Ford and A. R. Porter, STFC Daresbury Laboratory +! I. Kavcic, Met Office ! Example of calling the same kernel with data in the algorithm layer ! that might use different precision. Scalars, fields and operators @@ -41,17 +41,19 @@ program mixed_precision - use, intrinsic :: iso_fortran_env, only : real32 + use, intrinsic :: iso_fortran_env, only : real32, real64 use constants_mod, only : r_def, r_solver, r_tran, r_bl use field_mod, only : field_type use r_solver_field_mod, only : r_solver_field_type use r_tran_field_mod, only : r_tran_field_type use r_bl_field_mod, only : r_bl_field_type use field_real32_mod, only : field_real32_type + use field_real64_mod, only : field_real64_type use operator_mod, only : operator_type use r_solver_operator_mod, only : r_solver_operator_type use r_tran_operator_mod, only : r_tran_operator_type use operator_real32_mod, only : operator_real32_type + use operator_real64_mod, only : operator_real64_type use mixed_kernel_mod, only : mixed_kernel_type real(r_def) :: Scalar_r_def @@ -59,20 +61,24 @@ program mixed_precision real(r_tran) :: scAlar_r_tran real(r_bl) :: scaLar_r_bl real(real32) :: sCalaR_Real32 + real(rEal64) :: sCalaR_Real64 type(field_type) :: fieLd_r_def type(r_solver_field_type) :: fielD_r_solver type(r_tran_field_type) :: field_r_tran type(r_bl_field_type) :: fIeld_r_bl type(field_real32_type) :: field_real32 + type(field_real64_type) :: field_real64 type(operator_type) :: operator_r_def type(r_solver_operator_type) :: operator_r_solver type(r_tran_operator_type) :: OperatoR_r_tran type(operator_real32_type) :: oPerator_real32 + type(operator_real64_type) :: oPerator_real64 call invoke(mixed_kernel_type(scalar_r_deF, field_R_def, opeRator_r_def), & mixed_kernel_type(scalar_r_solver, field_r_solver, operator_r_solver), & mixed_kernel_type(scalar_r_tran, field_r_tran, OperatoR_r_tran), & mixed_kernel_type(scaLar_r_bl, fIeld_r_bl, OperatoR_r_tran), & - mixed_kernel_type(scalar_real32, field_real32, operator_real32)) + mixed_kernel_type(scalar_real32, field_real32, operator_real32), & + mixed_kernel_type(scalar_real64, field_real64, operator_real64)) end program mixed_precision From cee7722dbfc8c7a18170390fb3f29703c90dee68 Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Fri, 12 Jun 2026 11:10:59 +0100 Subject: [PATCH 7/9] #3170 fix and extend tests --- src/psyclone/tests/parse/algorithm_test.py | 6 +++++- .../test_files/lfric/1_single_invoke_w3_only_vector.f90 | 4 +++- .../test_files/lfric/1_single_invoke_wtheta_only_vector.f90 | 4 +++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/psyclone/tests/parse/algorithm_test.py b/src/psyclone/tests/parse/algorithm_test.py index d1460c971b..28a25702bf 100644 --- a/src/psyclone/tests/parse/algorithm_test.py +++ b/src/psyclone/tests/parse/algorithm_test.py @@ -248,7 +248,8 @@ def test_parser_invokeinfo_datatypes_mixed(): args2 = info.calls[0].kcalls[2].args args3 = info.calls[0].kcalls[3].args args4 = info.calls[0].kcalls[4].args - assert len(info.calls[0].kcalls) == 5 + args5 = info.calls[0].kcalls[5].args + assert len(info.calls[0].kcalls) == 6 assert args0[0]._datatype == ("real", "r_def") assert args0[1]._datatype == ("field_type", None) assert args0[2]._datatype == ("operator_type", None) @@ -262,6 +263,9 @@ def test_parser_invokeinfo_datatypes_mixed(): assert args3[1]._datatype == ("r_bl_field_type", None) assert args4[0]._datatype == ("real", "real32") assert args4[1]._datatype == ("field_real32_type", None) + assert args5[0]._datatype == ("real", "real64") + assert args5[1]._datatype == ("field_real64_type", None) + assert args5[2]._datatype == ("operator_real64_type", None) def test_parser_invokeinfo_datatypes_self(): diff --git a/src/psyclone/tests/test_files/lfric/1_single_invoke_w3_only_vector.f90 b/src/psyclone/tests/test_files/lfric/1_single_invoke_w3_only_vector.f90 index 8cab1dd5fa..9eab1fd404 100644 --- a/src/psyclone/tests/test_files/lfric/1_single_invoke_w3_only_vector.f90 +++ b/src/psyclone/tests/test_files/lfric/1_single_invoke_w3_only_vector.f90 @@ -39,11 +39,13 @@ program single_invoke_w3_only_vector ! Description: single function in an invoke iterating over and ! reading from w3 field vectors (discontinuous) use field_mod, only: field_type + use field_real64_mod, only: field_real64_type use testkern_w3_only_vector_mod, only: testkern_w3_only_vector_type implicit none - type(field_type) :: f1(3), f2(3) + type(field_type) :: f1(3) + type(field_real64_type) :: f2(3) call invoke( & testkern_w3_only_vector_type(f1, f2) & diff --git a/src/psyclone/tests/test_files/lfric/1_single_invoke_wtheta_only_vector.f90 b/src/psyclone/tests/test_files/lfric/1_single_invoke_wtheta_only_vector.f90 index 6e4c79fc8c..07c641007f 100644 --- a/src/psyclone/tests/test_files/lfric/1_single_invoke_wtheta_only_vector.f90 +++ b/src/psyclone/tests/test_files/lfric/1_single_invoke_wtheta_only_vector.f90 @@ -38,11 +38,13 @@ program single_invoke_wtheta_only_vector ! Description: single function in an invoke iterating over and ! reading from wtheta field vectors (discontinuous) use field_mod, only: field_type + use field_real64_mod, only: field_real64_type use testkern_wtheta_only_vector_mod, only: testkern_wtheta_only_vector_type implicit none - type(field_type) :: f1(3), f2(3) + type(field_type) :: f1(3) + type(field_real64_type) :: f2(3) call invoke( & testkern_wtheta_only_vector_type(f1, f2) & From 0bc25a72c0c0ab173b2ab1117b00a06395f31d44 Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Fri, 12 Jun 2026 11:11:27 +0100 Subject: [PATCH 8/9] #3170 extend lfric driver creator to import intrinsic precisions --- .../domain/lfric/lfric_driver_creator.py | 18 +++++++++++++++--- .../domain/lfric/lfric_driver_creator_test.py | 3 ++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/psyclone/domain/lfric/lfric_driver_creator.py b/src/psyclone/domain/lfric/lfric_driver_creator.py index 41f6463d55..4bec9446f2 100644 --- a/src/psyclone/domain/lfric/lfric_driver_creator.py +++ b/src/psyclone/domain/lfric/lfric_driver_creator.py @@ -72,7 +72,8 @@ def __init__(self, region_name: Optional[tuple[str, str]] = None) -> None: # ------------------------------------------------------------------------- def handle_precision_symbols(self, symbol_table: SymbolTable) -> None: '''This function adds an import of the various precision - symbols used by LFRic from the constants_mod module. + symbols used by LFRic from the constants_mod module. It also adds + imports of the real32 and real64 intrinsic kinds. :param symbol_table: the symbol table to which the precision symbols must be added. @@ -87,16 +88,27 @@ def handle_precision_symbols(self, symbol_table: SymbolTable) -> None: # does not exist at all in LFRic, but is still in LFRic's psyclone.cfg # file. TODO #2018 and # https://code.metoffice.gov.uk/trac/lfric/ticket/4674 + names_to_skip = ["r_quad", "r_phys"] + list(const.INTRINSIC_KINDS) api_config = Config.get().api_conf("lfric") all_precisions = [name for name in api_config.precision_map - if name not in ["r_quad", "r_phys"]] + if name not in names_to_skip] for prec_name in all_precisions: symbol_table.new_symbol(prec_name, tag=f"{prec_name}@{mod_name}", symbol_type=DataSymbol, datatype=ScalarType.integer_type(), interface=ImportInterface(constant_mod)) - + # Intrinsic kind symbols are imported into constants_mod but are private + # to it so have to be handled separately. + iso_mod = ContainerSymbol(const.FORTRAN_ISO_MOD_NAME, is_intrinsic=True) + symbol_table.add(iso_mod) + for prec_name in const.INTRINSIC_KINDS: + symbol_table.new_symbol(prec_name, + tag=f"{prec_name}@{iso_mod.name}", + symbol_type=DataSymbol, + datatype=ScalarType.integer_type(), + interface=ImportInterface(iso_mod)) + # ------------------------------------------------------------------------- def verify_and_cleanup_psyir(self, extract_region: Node) -> None: """This implementation removes MPI related calls in LFRic (`set_dirty` diff --git a/src/psyclone/tests/domain/lfric/lfric_driver_creator_test.py b/src/psyclone/tests/domain/lfric/lfric_driver_creator_test.py index 46cab5f096..0a2c4fa02e 100644 --- a/src/psyclone/tests/domain/lfric/lfric_driver_creator_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_driver_creator_test.py @@ -155,7 +155,7 @@ def test_lfric_driver_dm_test(): @pytest.mark.usefixtures("change_into_tmpdir", "init_module_manager_lfric") def test_lfric_driver_import_precision(): '''Test that all required precision symbols are imported from - constants_mod''' + constants_mod and iso_fortran_env.''' psy, invoke = get_invoke("26.6_mixed_precision_solver_vector.f90", API, dist_mem=False, idx=0) @@ -172,6 +172,7 @@ def test_lfric_driver_import_precision(): assert ("use constants_mod, only : i_def, l_def, r_bl, r_def, " "r_double, r_ncdf, r_second, r_single, r_solver, " "r_tran, r_um" in driver) + assert "use, intrinsic :: iso_fortran_env, only : real32, real64" in driver for mod in ["read_kernel_data_mod", "constants_mod", "kernel_mod", "argument_mod", "log_mod", "fs_continuity_mod", From 1ffbe6b2d1faa3a91b2c8b088cb9bf5b77afcf8c Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Fri, 12 Jun 2026 11:13:00 +0100 Subject: [PATCH 9/9] #3170 fix linting --- src/psyclone/domain/lfric/lfric_driver_creator.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/psyclone/domain/lfric/lfric_driver_creator.py b/src/psyclone/domain/lfric/lfric_driver_creator.py index 4bec9446f2..4aba83329c 100644 --- a/src/psyclone/domain/lfric/lfric_driver_creator.py +++ b/src/psyclone/domain/lfric/lfric_driver_creator.py @@ -98,9 +98,10 @@ def handle_precision_symbols(self, symbol_table: SymbolTable) -> None: symbol_type=DataSymbol, datatype=ScalarType.integer_type(), interface=ImportInterface(constant_mod)) - # Intrinsic kind symbols are imported into constants_mod but are private - # to it so have to be handled separately. - iso_mod = ContainerSymbol(const.FORTRAN_ISO_MOD_NAME, is_intrinsic=True) + # Intrinsic kind symbols are imported into constants_mod but are + # private to it so have to be handled separately. + iso_mod = ContainerSymbol(const.FORTRAN_ISO_MOD_NAME, + is_intrinsic=True) symbol_table.add(iso_mod) for prec_name in const.INTRINSIC_KINDS: symbol_table.new_symbol(prec_name, @@ -108,7 +109,7 @@ def handle_precision_symbols(self, symbol_table: SymbolTable) -> None: symbol_type=DataSymbol, datatype=ScalarType.integer_type(), interface=ImportInterface(iso_mod)) - + # ------------------------------------------------------------------------- def verify_and_cleanup_psyir(self, extract_region: Node) -> None: """This implementation removes MPI related calls in LFRic (`set_dirty`