From e62aa0a77c44ff7df32130acf3bf602805be7717 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Wed, 5 Apr 2023 13:40:05 +0100 Subject: [PATCH 01/44] #2091: renaming DynKern to LFRicKern in most files --- .../domain/lfric/algorithm/lfric_alg.py | 16 +++--- src/psyclone/domain/lfric/arg_ordering.py | 2 +- .../domain/lfric/kern_call_arg_list.py | 2 +- .../domain/lfric/kern_call_invoke_arg_list.py | 2 +- .../domain/lfric/kern_stub_arg_list.py | 2 +- src/psyclone/domain/lfric/kernel_interface.py | 2 +- src/psyclone/dynamo0p3.py | 56 +++++++++---------- src/psyclone/gen_kernel_stub.py | 8 +-- src/psyclone/tests/dependency_test.py | 22 ++++---- .../tests/domain/lfric/arg_ordering_test.py | 24 ++++---- .../lfric/lfric_stencil_stubgen_test.py | 16 +++--- src/psyclone/tests/dynamo0p3_basis_test.py | 22 ++++---- src/psyclone/tests/dynamo0p3_lma_test.py | 14 ++--- .../tests/dynamo0p3_quadrature_test.py | 28 +++++----- src/psyclone/tests/dynamo0p3_stubgen_test.py | 26 ++++----- src/psyclone/tests/dynamo0p3_test.py | 16 +++--- src/psyclone/tests/dynkern_test.py | 34 +++++------ src/psyclone/tests/psyGen_test.py | 16 +++--- src/psyclone/transformations.py | 10 ++-- 19 files changed, 159 insertions(+), 159 deletions(-) diff --git a/src/psyclone/domain/lfric/algorithm/lfric_alg.py b/src/psyclone/domain/lfric/algorithm/lfric_alg.py index 4cca91f5eb..a9367761d8 100644 --- a/src/psyclone/domain/lfric/algorithm/lfric_alg.py +++ b/src/psyclone/domain/lfric/algorithm/lfric_alg.py @@ -43,7 +43,7 @@ LFRicSymbolTable, LFRicTypes) from psyclone.domain.lfric.algorithm.psyir import ( LFRicAlgorithmInvokeCall, LFRicBuiltinFunctorFactory, LFRicKernelFunctor) -from psyclone.dynamo0p3 import DynKern +from psyclone.dynamo0p3 import LFRicKern from psyclone.errors import InternalError from psyclone.parse.kernel import get_kernel_parse_tree, KernelTypeFactory from psyclone.parse.utils import ParseError @@ -411,15 +411,15 @@ def initialise_quadrature(prog, qr_sym, shape): def kernel_from_metadata(parse_tree, kernel_name): ''' Given an fparser1 parse tree for an LFRic kernel, creates and returns - a DynKern object. + a LFRicKern object. :param parse_tree: the fparser1 parse tree for the LFRic kernel. :type parse_tree: :py:class:`fparser.one.block_statements.BeginSource` :param str kernel_name: the name of the kernel contained in the \ - supplied parse tree for which a DynKern is to be created. + supplied parse tree for which a LFRicKern is to be created. - :returns: a DynKern object describing the LFRic kernel. - :rtype: :py:class:`psyclone.dynamo0p3.DynKern` + :returns: a LFRicKern object describing the LFRic kernel. + :rtype: :py:class:`psyclone.dynamo0p3.LFRicKern` :raises ValueError: if an LFRic kernel with the specified name cannot \ be found in the supplied parse tree. @@ -432,8 +432,8 @@ def kernel_from_metadata(parse_tree, kernel_name): f"Failed to find kernel '{kernel_name}' in supplied " f"code: '{parse_tree}'. Is it a valid LFRic kernel? Original " f"error was '{err}'.") from err - # Construct a DynKern using the metadata. - kern = DynKern() + # Construct a LFRicKern using the metadata. + kern = LFRicKern() kern.load_meta(ktype) return kern @@ -445,7 +445,7 @@ def construct_kernel_args(self, prog, kern): :param prog: the routine to which to add the declarations etc. :type prog: :py:class:`psyclone.psyir.nodes.Routine` :param kern: the kernel for which we are to create arguments. - :type kern: :py:class:`psyclone.dynamo0p3.DynKern` + :type kern: :py:class:`psyclone.dynamo0p3.LFRicKern` :returns: object capturing all of the kernel arguments. :rtype: :py:class:`psyclone.domain.lfric.KernCallInvokeArgList` diff --git a/src/psyclone/domain/lfric/arg_ordering.py b/src/psyclone/domain/lfric/arg_ordering.py index cb0562a21f..9b85fc3973 100644 --- a/src/psyclone/domain/lfric/arg_ordering.py +++ b/src/psyclone/domain/lfric/arg_ordering.py @@ -65,7 +65,7 @@ class ArgOrdering: a list. :param kern: the kernel call object to use. - :type kern: :py:class:`psyclone.dynamo0p3.DynKern` + :type kern: :py:class:`psyclone.dynamo0p3.LFRicKern` ''' def __init__(self, kern): diff --git a/src/psyclone/domain/lfric/kern_call_arg_list.py b/src/psyclone/domain/lfric/kern_call_arg_list.py index a64f533cc1..baaabdded5 100644 --- a/src/psyclone/domain/lfric/kern_call_arg_list.py +++ b/src/psyclone/domain/lfric/kern_call_arg_list.py @@ -72,7 +72,7 @@ class KernCallArgList(ArgOrdering): captured by the base class. :param kern: The kernel that is being called. - :type kern: :py:class:`psyclone.dynamo0p3.DynKern` + :type kern: :py:class:`psyclone.dynamo0p3.LFRicKern` ''' NdfInfo = namedtuple("NdfInfo", ["position", "function_space"]) diff --git a/src/psyclone/domain/lfric/kern_call_invoke_arg_list.py b/src/psyclone/domain/lfric/kern_call_invoke_arg_list.py index 9e9530a072..31cb3e4393 100644 --- a/src/psyclone/domain/lfric/kern_call_invoke_arg_list.py +++ b/src/psyclone/domain/lfric/kern_call_invoke_arg_list.py @@ -53,7 +53,7 @@ class KernCallInvokeArgList(ArgOrdering): kernel, according to that kernel's metadata. :param kern: the kernel object for which to determine arguments. - :type kern: :py:class:`psyclone.dynamo0p3.DynKern` + :type kern: :py:class:`psyclone.dynamo0p3.LFRicKern` :param symbol_table: the symbol table associated with the routine that \ contains the `invoke` of this kernel. :type symbol_table: :py:class:`psyclone.psyir.symbols.SymbolTable` diff --git a/src/psyclone/domain/lfric/kern_stub_arg_list.py b/src/psyclone/domain/lfric/kern_stub_arg_list.py index 3a400e24d8..2f61a5a0d6 100644 --- a/src/psyclone/domain/lfric/kern_stub_arg_list.py +++ b/src/psyclone/domain/lfric/kern_stub_arg_list.py @@ -51,7 +51,7 @@ class KernStubArgList(ArgOrdering): of the arguments is captured by the base class. :param kern: Kernel for which to create argument list. - :type kern: :py:class:`psyclone.dynamo0p3.DynKern` + :type kern: :py:class:`psyclone.dynamo0p3.LFRicKern` :raises NotImplementedError: if the kernel is inter-grid. :raises NotImplementedError: if the kernel requires properties of the \ diff --git a/src/psyclone/domain/lfric/kernel_interface.py b/src/psyclone/domain/lfric/kernel_interface.py index 1da4755c0a..e0c3eefb15 100644 --- a/src/psyclone/domain/lfric/kernel_interface.py +++ b/src/psyclone/domain/lfric/kernel_interface.py @@ -70,7 +70,7 @@ class KernelInterface(ArgOrdering): code when all of its methods are implemented. :param kern: the kernel for which to create arguments. - :type kern: :py:class:`psyclone.dynamo0p3.DynKern` + :type kern: :py:class:`psyclone.dynamo0p3.LFRicKern` ''' #: Mapping from a generic PSyIR datatype to the equivalent diff --git a/src/psyclone/dynamo0p3.py b/src/psyclone/dynamo0p3.py index d0d96c1c1c..2d7d38c55d 100644 --- a/src/psyclone/dynamo0p3.py +++ b/src/psyclone/dynamo0p3.py @@ -1146,10 +1146,10 @@ class DynCollection(): :param node: the Kernel or Invoke for which to manage variable \ declarations and initialisation. :type node: :py:class:`psyclone.dynamo0p3.DynInvoke` or \ - :py:class:`psyclone.dynamo0p3.DynKern` + :py:class:`psyclone.dynamo0p3.LFRicKern` :raises InternalError: if the supplied node is not a DynInvoke or a \ - DynKern. + LFRicKern. ''' def __init__(self, node): if isinstance(node, DynInvoke): @@ -1159,7 +1159,7 @@ def __init__(self, node): self._symbol_table = self._invoke.schedule.symbol_table # The list of kernel calls we are responsible for self._calls = node.schedule.kernels() - elif isinstance(node, DynKern): + elif isinstance(node, LFRicKern): # We are handling declarations for a Kernel stub self._invoke = None self._kernel = node @@ -1170,7 +1170,7 @@ def __init__(self, node): self._calls = [node] else: raise InternalError(f"DynCollection takes only a DynInvoke " - f"or a DynKern but got: {type(node)}") + f"or a LFRicKern but got: {type(node)}") # Whether or not the associated Invoke contains only kernels that # operate on dofs. @@ -1242,7 +1242,7 @@ class DynStencils(DynCollection): :param node: the Invoke or Kernel stub for which to provide stencil info. :type node: :py:class:`psyclone.dynamo0p3.DynInvoke` or \ - :py:class:`psyclone.dynamo0p3.DynKern` + :py:class:`psyclone.dynamo0p3.LFRicKern` :raises GenerationError: if a literal has been supplied for a stencil \ direction. @@ -1836,7 +1836,7 @@ class LFRicMeshProperties(DynCollection): kernels. :param node: kernel or invoke for which to manage mesh properties. - :type node: :py:class:`psyclone.dynamo0p3.DynKern` or \ + :type node: :py:class:`psyclone.dynamo0p3.LFRicKern` or \ :py:class:`psyclone.dynamo0p3.DynInvoke` ''' @@ -2160,7 +2160,7 @@ class DynReferenceElement(DynCollection): :param node: Kernel or Invoke for which to manage Reference-Element \ properties. - :type node: :py:class:`psyclone.dynamo0p3.DynKern` or \ + :type node: :py:class:`psyclone.dynamo0p3.LFRicKern` or \ :py:class:`psyclone.dynamo0p3.DynInvoke` :raises InternalError: if an unsupported reference-element property \ @@ -2518,7 +2518,7 @@ class DynDofmaps(DynCollection): indirection) required by an invoke. :param node: Kernel or Invoke for which to manage dofmaps. - :type node: :py:class:`psyclone.dynamo0p3.DynKern` or \ + :type node: :py:class:`psyclone.dynamo0p3.LFRicKern` or \ :py:class:`psyclone.dynamo0p3.DynInvoke` ''' @@ -3305,7 +3305,7 @@ class DynCellIterators(DynCollection): :param kern_or_invoke: the Kernel or Invoke for which to manage cell \ iterators. - :type kern_or_invoke: :py:class:`psyclone.dynamo0p3.DynKern` or \ + :type kern_or_invoke: :py:class:`psyclone.dynamo0p3.LFRicKern` or \ :py:class:`psyclone.dynamo0p3.DynInvoke` : raises GenerationError: if an Invoke has no field or operator arguments. @@ -3453,7 +3453,7 @@ class LFRicScalarArgs(DynCollection): :param node: the Invoke or Kernel stub for which to manage the scalar \ arguments. - :type node: :py:class:`psyclone.dynamo0p3.DynKern` or \ + :type node: :py:class:`psyclone.dynamo0p3.LFRicKern` or \ :py:class:`psyclone.dynamo0p3.DynInvoke` ''' @@ -3748,7 +3748,7 @@ class DynCMAOperators(DynCollection): :param node: either an Invoke schedule or a single Kernel object. :type node: :py:class:`psyclone.dynamo0p3.DynSchedule` or \ - :py:class:`psyclone.dynamo0p3.DynKern` + :py:class:`psyclone.dynamo0p3.LFRicKern` ''' # The scalar parameters that must be passed along with a CMA operator @@ -4562,7 +4562,7 @@ class DynBasisFunctions(DynCollection): for which to extract information on all required \ basis/diff-basis functions. :type node: :py:class:`psyclone.dynamo0p3.DynInvokeSchedule` or \ - :py:class:`psyclone.dynamo0p3.DynKern` + :py:class:`psyclone.dynamo0p3.LFRicKern` :raises InternalError: if a call has an unrecognised evaluator shape. @@ -4735,14 +4735,14 @@ def _setup_basis_fns_for_call(self, call): functions required by the supplied Call. :param call: the kernel call for which basis functions are required. - :type call: :py:class:`psyclone.dynamo0p3.DynKern` + :type call: :py:class:`psyclone.dynamo0p3.LFRicKern` :raises InternalError: if the supplied call is of incorrect type. :raises InternalError: if the supplied call has an unrecognised \ evaluator shape. ''' - if not isinstance(call, DynKern): - raise InternalError(f"Expected a DynKern object but got: " + if not isinstance(call, LFRicKern): + raise InternalError(f"Expected a LFRicKern object but got: " f"'{type(call)}'") const = LFRicConstants() # We need a full FunctionSpace object for each function space @@ -5466,7 +5466,7 @@ class DynBoundaryConditions(DynCollection): :param node: the Invoke or Kernel stub for which we are to handle \ any boundary conditions. :type node: :py:class:`psyclone.dynamo0p3.DynInvoke` or \ - :py:class:`psyclone.dynamo0p3.DynKern` + :py:class:`psyclone.dynamo0p3.LFRicKern` :raises GenerationError: if a kernel named "enforce_bc_code" is found \ but does not have an argument on ANY_SPACE_1. @@ -6782,7 +6782,7 @@ def halo_check_arg(field, access_types): raise GenerationError( f"In HaloInfo class, field '{field.name}' should be one of " f"{api_strings}, but found '{field.access.api_specific_name()}'") - if not isinstance(call, (LFRicBuiltIn, DynKern)): + if not isinstance(call, (LFRicBuiltIn, LFRicKern)): raise GenerationError( f"In HaloInfo class, field '{field.name}' should be from a call " f"but found {type(call)}") @@ -7175,7 +7175,7 @@ def load(self, kern): construct Loop objects for a given kernel call. :param kern: Kernel object to use to populate state of Loop - :type kern: :py:class:`psyclone.dynamo0p3.DynKern` + :type kern: :py:class:`psyclone.dynamo0p3.LFRicKern` :raises GenerationError: if the field updated by the kernel has an \ unexpected function space or if the kernel's 'operates-on' is \ @@ -7404,7 +7404,7 @@ def _upper_bound_fortran(self): if self._upper_bound_name == "ncolours": # Loop over colours - kernels = self.walk(DynKern) + kernels = self.walk(LFRicKern) if not kernels: raise InternalError( "Failed to find a kernel within a loop over colours.") @@ -7933,7 +7933,7 @@ def gen_mark_halos_clean_dirty(self, parent): parent.add(call) -class DynKern(CodedKern): +class LFRicKern(CodedKern): ''' Stores information about Dynamo Kernels as specified by the Kernel metadata and associated algorithm call. Uses this information to generate appropriate PSy layer code for the Kernel @@ -8064,7 +8064,7 @@ def load_meta(self, ktype): f"'{descriptor.data_type}'.") else: raise GenerationError( - f"DynKern.load_meta() expected one of " + f"LFRicKern.load_meta() expected one of " f"{const.VALID_ARG_TYPE_NAMES} but found " f"'{descriptor.argument_type}'") args.append(Arg("variable", pre+str(idx+1))) @@ -8195,7 +8195,7 @@ def _setup(self, ktype, module_name, args, parent, check=True): qr_args = ["nedges", "np_xyz", "weights_xyz"] else: raise InternalError(f"Unsupported quadrature shape " - f"('{shape}') found in DynKern._setup") + f"('{shape}') found in LFRicKern._setup") # Append the name of the qr argument to the names of the qr-related # variables. @@ -8227,7 +8227,7 @@ def qr_rules(self): :return: details of each of the quadrature rules required by this \ kernel. :rtype: OrderedDict containing \ - :py:class:`psyclone.dynamo0p3.DynKern.QRRule` indexed by \ + :py:class:`psyclone.dynamo0p3.LFRicKern.QRRule` indexed by \ quadrature shape. ''' return self._qr_rules @@ -8564,7 +8564,7 @@ def gen_code(self, parent): parent.add(CommentGen(parent, "")) - super(DynKern, self).gen_code(parent) + super(LFRicKern, self).gen_code(parent) def get_kernel_schedule(self): '''Returns a PSyIR Schedule representing the kernel code. The base @@ -8962,7 +8962,7 @@ class DynKernelArguments(Arguments): :param call: the kernel meta-data for which to extract argument info. :type call: :py:class:`psyclone.parse.KernelCall` :param parent_call: the kernel-call object. - :type parent_call: :py:class:`psyclone.dynamo0p3.DynKern` + :type parent_call: :py:class:`psyclone.dynamo0p3.LFRicKern` :param bool check: whether to check for consistency between the \ kernel metadata and the algorithm layer. Defaults to True. @@ -9296,7 +9296,7 @@ class DynKernelArgument(KernelArgument): the Algorithm layer. :type arg_info: :py:class:`psyclone.parse.algorithm.Arg` :param call: the kernel object with which this argument is associated. - :type call: :py:class:`psyclone.dynamo0p3.DynKern` + :type call: :py:class:`psyclone.dynamo0p3.LFRicKern` :param bool check: whether to check for consistency between the \ kernel metadata and the algorithm layer. Defaults to True. @@ -10161,7 +10161,7 @@ def create(call, parent=None): cloop = DynLoop(parent=parent, loop_type=loop_type) # The kernel itself - kern = DynKern() + kern = LFRicKern() kern.load(call, cloop.loop_body) # Add the kernel as a child of the loop @@ -10219,7 +10219,7 @@ def data_on_device(self, _): 'HaloWriteAccess', 'HaloReadAccess', 'DynLoop', - 'DynKern', + 'LFRicKern', 'FSDescriptor', 'FSDescriptors', 'DynStencil', diff --git a/src/psyclone/gen_kernel_stub.py b/src/psyclone/gen_kernel_stub.py index 21387e525b..9af9bd649e 100644 --- a/src/psyclone/gen_kernel_stub.py +++ b/src/psyclone/gen_kernel_stub.py @@ -1,7 +1,7 @@ # ----------------------------------------------------------------------------- # BSD 3-Clause License # -# Copyright (c) 2017-2022, Science and Technology Facilities Council +# Copyright (c) 2017-2023, Science and Technology Facilities Council # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -33,7 +33,7 @@ # ----------------------------------------------------------------------------- # Author R. Ford STFC Daresbury Lab # Modified work Copyright (c) 2017 by J. Henrichs, Bureau of Meteorology -# Modified: I. Kavcic, Met Office +# Modified: I. Kavcic and L. Turner, Met Office # A. R. Porter and N. Nobre, STFC Daresbury Lab ''' Contains a Python function to generate an empty kernel @@ -45,7 +45,7 @@ import os import fparser -from psyclone.dynamo0p3 import DynKern, DynKernMetadata +from psyclone.dynamo0p3 import LFRicKern, DynKernMetadata from psyclone.errors import GenerationError from psyclone.parse.utils import ParseError from psyclone.configuration import Config @@ -94,7 +94,7 @@ def generate(filename, api=""): f"Fortran: {error}.") metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) return kernel.gen_stub diff --git a/src/psyclone/tests/dependency_test.py b/src/psyclone/tests/dependency_test.py index 9fdcd836a7..235f13aa90 100644 --- a/src/psyclone/tests/dependency_test.py +++ b/src/psyclone/tests/dependency_test.py @@ -46,7 +46,7 @@ from psyclone import nemo from psyclone.core import AccessType, Signature, VariablesAccessInfo from psyclone.domain.lfric import KernCallAccArgList, KernStubArgList -from psyclone.dynamo0p3 import DynKernMetadata, DynKern +from psyclone.dynamo0p3 import DynKernMetadata, LFRicKern from psyclone.parse.algorithm import parse from psyclone.psyGen import CodedKern, PSyFactory from psyclone.psyir.nodes import Assignment, IfBlock, Loop @@ -656,7 +656,7 @@ def test_lfric_stub_args(): ''' ast = get_ast("dynamo0.3", "testkern_stencil_multi_mod.f90") metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) var_accesses = VariablesAccessInfo() create_arg_list = KernStubArgList(kernel) @@ -692,7 +692,7 @@ def test_lfric_stub_args2(): ''' ast = get_ast("dynamo0.3", "testkern_mesh_prop_face_qr_mod.F90") metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) var_accesses = VariablesAccessInfo() create_arg_list = KernStubArgList(kernel) @@ -712,7 +712,7 @@ def test_lfric_stub_args3(): ast = get_ast("dynamo0.3", "testkern_any_discontinuous_space_op_1_mod.f90") metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) var_accesses = VariablesAccessInfo() create_arg_list = KernStubArgList(kernel) @@ -731,7 +731,7 @@ def test_lfric_stub_boundary_dofs(): ''' ast = get_ast("dynamo0.3", "enforce_bc_kernel_mod.f90") metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) var_accesses = VariablesAccessInfo() create_arg_list = KernStubArgList(kernel) @@ -745,7 +745,7 @@ def test_lfric_stub_field_vector(): ''' ast = get_ast("dynamo0.3", "testkern_stencil_vector_mod.f90") metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) var_accesses = VariablesAccessInfo() create_arg_list = KernStubArgList(kernel) @@ -766,7 +766,7 @@ def test_lfric_stub_basis(): ''' ast = get_ast("dynamo0.3", "testkern_qr_eval_mod.F90") metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) var_accesses = VariablesAccessInfo() create_arg_list = KernStubArgList(kernel) @@ -787,7 +787,7 @@ def test_lfric_stub_cma_operators(): ''' ast = get_ast("dynamo0.3", "columnwise_op_mul_2scalars_kernel_mod.F90") metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) var_accesses = VariablesAccessInfo() create_arg_list = KernStubArgList(kernel) @@ -811,7 +811,7 @@ def test_lfric_stub_banded_dofmap(): ''' ast = get_ast("dynamo0.3", "columnwise_op_asm_kernel_mod.F90") metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) var_accesses = VariablesAccessInfo() create_arg_list = KernStubArgList(kernel) @@ -826,7 +826,7 @@ def test_lfric_stub_indirection_dofmap(): ''' ast = get_ast("dynamo0.3", "columnwise_op_app_kernel_mod.F90") metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) var_accesses = VariablesAccessInfo() create_arg_list = KernStubArgList(kernel) @@ -843,7 +843,7 @@ def test_lfric_stub_boundary_dofmap(): ''' ast = get_ast("dynamo0.3", "enforce_operator_bc_kernel_mod.F90") metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) var_accesses = VariablesAccessInfo() create_arg_list = KernStubArgList(kernel) diff --git a/src/psyclone/tests/domain/lfric/arg_ordering_test.py b/src/psyclone/tests/domain/lfric/arg_ordering_test.py index c5e5a477a3..cfad99385a 100644 --- a/src/psyclone/tests/domain/lfric/arg_ordering_test.py +++ b/src/psyclone/tests/domain/lfric/arg_ordering_test.py @@ -45,7 +45,7 @@ KernStubArgList, LFRicConstants, LFRicSymbolTable) from psyclone.domain.lfric.arg_ordering import ArgOrdering -from psyclone.dynamo0p3 import DynKern, DynKernMetadata, DynLoop +from psyclone.dynamo0p3 import LFRicKern, DynKernMetadata, DynLoop from psyclone.errors import GenerationError, InternalError from psyclone.parse.algorithm import parse from psyclone.psyGen import PSyFactory @@ -84,7 +84,7 @@ def test_argordering_append(): _, invoke_info = parse(full_path, api=TEST_API) psy = PSyFactory(TEST_API, distributed_memory=False).create(invoke_info) schedule = psy.invokes.invoke_list[0].schedule - kern = schedule.walk(DynKern)[0] + kern = schedule.walk(LFRicKern)[0] arg_list = ArgOrdering(kern) arg_list.append("roger") assert len(arg_list._arglist) == 1 @@ -112,7 +112,7 @@ def test_argordering_get_array_reference(): psy, _ = get_invoke("1.0.1_single_named_invoke.f90", TEST_API, 0) schedule = psy.invokes.invoke_list[0].schedule - kern = schedule.walk(DynKern)[0] + kern = schedule.walk(LFRicKern)[0] arg_list = ArgOrdering(kern) # First test access using an index, e.g. `array(1)` @@ -165,7 +165,7 @@ def test_argordering_extend(): _, invoke_info = parse(full_path, api=TEST_API) psy = PSyFactory(TEST_API, distributed_memory=False).create(invoke_info) schedule = psy.invokes.invoke_list[0].schedule - kern = schedule.walk(DynKern)[0] + kern = schedule.walk(LFRicKern)[0] arg_list = ArgOrdering(kern) arg_list.append("roger") arg_list.extend(["peggy", "nancy"]) @@ -196,7 +196,7 @@ def test_unexpected_type_error(dist_mem): psy = PSyFactory(TEST_API, distributed_memory=dist_mem).create(invoke_info) schedule = psy.invokes.invoke_list[0].schedule - kernel = schedule.walk(DynKern)[0] + kernel = schedule.walk(LFRicKern)[0] # Sabotage one of the arguments to make it have an invalid type. kernel.arguments.args[0]._argument_type = "invalid" # Now call KernCallArgList to raise an exception @@ -216,7 +216,7 @@ def test_kernel_stub_invalid_scalar_argument(): ast = get_ast(TEST_API, "testkern_one_int_scalar_mod.f90") metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) # Sabotage the scalar argument to make it have an invalid type. arg = kernel.arguments.args[1] @@ -305,7 +305,7 @@ def test_arg_ordering_mdata_index(): psy = PSyFactory(TEST_API, distributed_memory=False).create(invoke_info) schedule = psy.invokes.invoke_list[0].schedule - kernels = schedule.walk(DynKern) + kernels = schedule.walk(LFRicKern) arg_list = ArgOrdering(kernels[0]) arg_list.generate() # Scalar argument. @@ -319,7 +319,7 @@ def test_kernel_stub_ind_dofmap_errors(): are supplied to KernelStubArgList.indirection_dofmap() ''' ast = get_ast(TEST_API, "testkern_one_int_scalar_mod.f90") metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) # Now call KernStubArgList to raise an exception create_arg_list = KernStubArgList(kernel) @@ -420,7 +420,7 @@ def test_kerncallarglist_metadata_index_op_vector(): psy = PSyFactory(TEST_API, distributed_memory=False).create(invoke_info) schedule = psy.invokes.invoke_list[0].schedule - kernels = schedule.walk(DynKern) + kernels = schedule.walk(LFRicKern) arg_list = KernCallArgList(kernels[0]) arg_list.generate() # Operator @@ -440,7 +440,7 @@ def test_kernstubarglist_arglist_error(): ast = get_ast(TEST_API, "testkern_one_int_scalar_mod.f90") metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) # Now call KernStubArgList to raise an exception create_arg_list = KernStubArgList(kernel) @@ -458,7 +458,7 @@ def test_kernstubarglist_eval_shape_error(): invalid. ''' ast = get_ast(TEST_API, "testkern_qr_faces_mod.F90") metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) create_arg_list = KernStubArgList(kernel) # Break the list of qr rules @@ -479,7 +479,7 @@ def test_refelem_stub_arglist_err(): # Create the Kernel object ast = get_ast(TEST_API, "testkern_ref_elem_all_faces_mod.F90") metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) # Break the list of ref-element properties required by the Kernel kernel.reference_element.properties.append("Wrong property") diff --git a/src/psyclone/tests/domain/lfric/lfric_stencil_stubgen_test.py b/src/psyclone/tests/domain/lfric/lfric_stencil_stubgen_test.py index ca4ee4bfd1..f89078a547 100644 --- a/src/psyclone/tests/domain/lfric/lfric_stencil_stubgen_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_stencil_stubgen_test.py @@ -1,7 +1,7 @@ # ----------------------------------------------------------------------------- # BSD 3-Clause License # -# Copyright (c) 2017-2022, Science and Technology Facilities Council. +# Copyright (c) 2017-2023, Science and Technology Facilities Council. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors R. W. Ford and A. R. Porter, STFC Daresbury Lab -# Modified I. Kavcic and A. Coughtrie, Met Office +# Modified I. Kavcic, A. Coughtrie and L. Turner, Met Office # Modified J. Henrichs, Bureau of Meteorology ''' This module tests the LFRic kernel-stub generator for using pytest. ''' @@ -42,7 +42,7 @@ from fparser import api as fpapi -from psyclone.dynamo0p3 import DynKern, DynKernMetadata +from psyclone.dynamo0p3 import LFRicKern, DynKernMetadata # Constants BASE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), @@ -58,7 +58,7 @@ def test_stub_stencil_extent(): ast = fpapi.parse(os.path.join(BASE_PATH, "testkern_stencil_mod.f90"), ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) result1 = ( @@ -85,7 +85,7 @@ def test_stub_cross2d_stencil(): ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) print(generated_code) @@ -114,7 +114,7 @@ def test_stub_stencil_direction(): "testkern_stencil_xory1d_mod.f90"), ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) result1 = ( @@ -140,7 +140,7 @@ def test_stub_stencil_vector(): "testkern_stencil_vector_mod.f90"), ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) result1 = ( @@ -166,7 +166,7 @@ def test_stub_stencil_multi(): "testkern_stencil_multi_mod.f90"), ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) result1 = ( diff --git a/src/psyclone/tests/dynamo0p3_basis_test.py b/src/psyclone/tests/dynamo0p3_basis_test.py index dd40e0f9e2..2021c8afff 100644 --- a/src/psyclone/tests/dynamo0p3_basis_test.py +++ b/src/psyclone/tests/dynamo0p3_basis_test.py @@ -51,7 +51,7 @@ from psyclone.parse.utils import ParseError from psyclone.psyGen import PSyFactory from psyclone.errors import GenerationError, InternalError -from psyclone.dynamo0p3 import DynKernMetadata, DynKern +from psyclone.dynamo0p3 import DynKernMetadata, LFRicKern from psyclone.tests.lfric_build import LFRicBuild from psyclone.tests.utilities import print_diffs @@ -1444,7 +1444,7 @@ def test_basis_evaluator(): ''' ast = fpapi.parse(BASIS_EVAL, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) @@ -1571,7 +1571,7 @@ def test_basis_unsupported_space(): # Test any_space_* ast = fpapi.parse(BASIS_UNSUPPORTED_SPACE, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) with pytest.raises(GenerationError) as excinfo: _ = kernel.gen_stub @@ -1585,7 +1585,7 @@ def test_basis_unsupported_space(): code = code.replace("gh_inc", "gh_readwrite") ast = fpapi.parse(code, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) with pytest.raises(GenerationError) as excinfo: _ = kernel.gen_stub @@ -1647,7 +1647,7 @@ def test_diff_basis(): ''' ast = fpapi.parse(DIFF_BASIS, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) output = ( @@ -1813,7 +1813,7 @@ def test_diff_basis_eval(): ''' ast = fpapi.parse(DIFF_BASIS_EVAL, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) @@ -1930,7 +1930,7 @@ def test_2eval_stubgen(): " integer :: gh_evaluator_targets(2) = (/w2h, wtheta/)\n") ast = fpapi.parse(twoeval_meta, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) @@ -2041,7 +2041,7 @@ def test_diff_basis_unsupp_space(): # Test any_space_* ast = fpapi.parse(DIFF_BASIS_UNSUPPORTED_SPACE, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) with pytest.raises(GenerationError) as excinfo: _ = kernel.gen_stub @@ -2055,7 +2055,7 @@ def test_diff_basis_unsupp_space(): code = code.replace("gh_inc", "gh_readwrite") ast = fpapi.parse(code, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) with pytest.raises(GenerationError) as excinfo: _ = kernel.gen_stub @@ -2070,7 +2070,7 @@ def test_dynbasisfns_unsupp_qr(monkeypatch): shape is encountered. ''' ast = fpapi.parse(DIFF_BASIS, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) dbasis = DynBasisFunctions(kernel) monkeypatch.setattr( @@ -2087,7 +2087,7 @@ def test_dynbasisfns_declns(monkeypatch): DynBasisFunctions._basis_fn_declns can raise. ''' ast = fpapi.parse(DIFF_BASIS, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) dbasis = DynBasisFunctions(kernel) # Missing name for qr variable diff --git a/src/psyclone/tests/dynamo0p3_lma_test.py b/src/psyclone/tests/dynamo0p3_lma_test.py index d365bae459..b1cd3f18fa 100644 --- a/src/psyclone/tests/dynamo0p3_lma_test.py +++ b/src/psyclone/tests/dynamo0p3_lma_test.py @@ -1,7 +1,7 @@ # ----------------------------------------------------------------------------- # BSD 3-Clause License # -# Copyright (c) 2019-2022 Science and Technology Facilities Council. +# Copyright (c) 2019-2023 Science and Technology Facilities Council. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors R. W. Ford, A. R. Porter and N. Nobre, STFC Daresbury Lab -# Modified I. Kavcic, Met Office +# Modified I. Kavcic and L.Turner, Met Office # Modified J. Henrichs, Bureau of Meteorology ''' This module tests the support for LMA operators in the LFRic (Dynamo 0.3) @@ -50,7 +50,7 @@ from psyclone.core.access_type import AccessType from psyclone.domain.lfric import LFRicArgDescriptor, LFRicConstants from psyclone.dynamo0p3 import (DynFuncDescriptor03, DynKernMetadata, - DynKern, FunctionSpace) + LFRicKern, FunctionSpace) from psyclone.errors import GenerationError, InternalError from psyclone.parse.algorithm import parse from psyclone.parse.utils import ParseError @@ -440,7 +440,7 @@ def test_operator_arg_lfricconst_properties(monkeypatch): ast = fpapi.parse(CODE, ignore_comments=False) name = "testkern_qr_type" metadata = DynKernMetadata(ast, name=name) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) op_arg = kernel.arguments.args[3] @@ -881,7 +881,7 @@ def test_operators(): ''' ast = fpapi.parse(OPERATORS, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) output = ( @@ -973,7 +973,7 @@ def test_stub_operator_different_spaces(): # Check the original code (to- and from- spaces both continuous) ast = fpapi.parse(OPERATOR_DIFFERENT_SPACES, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) result = str(kernel.gen_stub) assert "(cell, nlayers, op_1_ncell_3d, op_1, ndf_w0, ndf_w1)" in result @@ -984,7 +984,7 @@ def test_stub_operator_different_spaces(): "(gh_operator, gh_real, gh_write, w3, any_discontinuous_space_2)", 1) ast = fpapi.parse(code, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) result = str(kernel.gen_stub) assert ("(cell, nlayers, op_1_ncell_3d, op_1, ndf_w3, ndf_adspc2_op_1)" diff --git a/src/psyclone/tests/dynamo0p3_quadrature_test.py b/src/psyclone/tests/dynamo0p3_quadrature_test.py index 1a648538c0..eff768339b 100644 --- a/src/psyclone/tests/dynamo0p3_quadrature_test.py +++ b/src/psyclone/tests/dynamo0p3_quadrature_test.py @@ -1,7 +1,7 @@ # ----------------------------------------------------------------------------- # BSD 3-Clause License # -# Copyright (c) 2017-2022, Science and Technology Facilities Council. +# Copyright (c) 2017-2023, Science and Technology Facilities Council. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Author R. W. Ford and A. R. Porter, STFC Daresbury Lab -# Modified I. Kavcic, Met Office +# Modified I. Kavcic and L. Turner, Met Office # Modified by J. Henrichs, Bureau of Meteorology ''' Module containing py.test tests for functionality related to @@ -47,7 +47,7 @@ from psyclone.configuration import Config from psyclone.domain.lfric import LFRicConstants -from psyclone.dynamo0p3 import DynKernMetadata, DynKern, DynBasisFunctions, \ +from psyclone.dynamo0p3 import DynKernMetadata, LFRicKern, DynBasisFunctions, \ qr_basis_alloc_args from psyclone.errors import InternalError from psyclone.f2pygen import ModuleGen @@ -589,7 +589,7 @@ def test_dynbasisfunctions(monkeypatch): invoke = psy.invokes.invoke_list[0] sched = invoke.schedule call = sched.children[0].loop_body[0] - assert isinstance(call, DynKern) + assert isinstance(call, LFRicKern) monkeypatch.setattr(call, "_eval_shapes", ["not-a-shape"]) with pytest.raises(InternalError) as err: _ = DynBasisFunctions(invoke) @@ -606,7 +606,7 @@ def test_dynbasisfns_setup(monkeypatch): psy = PSyFactory(API, distributed_memory=False).create(invoke_info) sched = psy.invokes.invoke_list[0].schedule call = sched.children[0].loop_body[0] - assert isinstance(call, DynKern) + assert isinstance(call, LFRicKern) dinf = DynBasisFunctions(psy.invokes.invoke_list[0]) # Now we've created a DynBasisFunctions object, monkeypatch the call # to have the wrong shape and try and call setup_basis_fns_for_call() @@ -618,7 +618,7 @@ def test_dynbasisfns_setup(monkeypatch): # something that is not a Kernel call with pytest.raises(InternalError) as err: dinf._setup_basis_fns_for_call("call") - assert "Expected a DynKern object but got: " in str(err.value) + assert "Expected a LFRicKern object but got: " in str(err.value) def test_dynbasisfns_initialise(monkeypatch): @@ -679,7 +679,7 @@ def test_dynbasisfns_dealloc(monkeypatch): psy = PSyFactory(API, distributed_memory=False).create(invoke_info) sched = psy.invokes.invoke_list[0].schedule call = sched.children[0].loop_body[0] - assert isinstance(call, DynKern) + assert isinstance(call, LFRicKern) dinf = DynBasisFunctions(psy.invokes.invoke_list[0]) mod = ModuleGen(name="testmodule") # Supply an invalid type for one of the basis functions @@ -690,14 +690,14 @@ def test_dynbasisfns_dealloc(monkeypatch): "one of 'basis' or 'diff-basis'" in str(err.value)) -def test_dynkern_setup(monkeypatch): - ''' Check that internal-consistency checks in DynKern._setup() work +def test_lfrickern_setup(monkeypatch): + ''' Check that internal-consistency checks in LFRicKern._setup() work as expected. ''' _, invoke_info = parse(os.path.join(BASE_PATH, "1.1.0_single_invoke_xyoz_qr.f90"), api=API) psy = PSyFactory(API, distributed_memory=True).create(invoke_info) - # Get hold of a DynKern object + # Get hold of a LFRicKern object schedule = psy.invokes.invoke_list[0].schedule kern = schedule.children[4].loop_body[0] # Monkeypatch a couple of __init__ routines so that we can get past @@ -713,7 +713,7 @@ def test_dynkern_setup(monkeypatch): ast = fpapi.parse(os.path.join(BASE_PATH, "testkern_qr.F90"), ignore_comments=False) name = "testkern_qr_type" - dkm = DynKernMetadata(ast, name=name) + dkm = LFRicKernMetadata(ast, name=name) # Finally, call the _setup() method with pytest.raises(InternalError) as excinfo: kern._setup(dkm, "my module", None, None) @@ -774,7 +774,7 @@ def test_qr_basis_stub(): ''' ast = fpapi.parse(BASIS, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) output = ( @@ -888,7 +888,7 @@ def test_stub_basis_wrong_shape(monkeypatch): broken ''' ast = fpapi.parse(BASIS, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) monkeypatch.setattr(kernel, "_eval_shapes", value=["gh_quadrature_wrong"]) @@ -918,7 +918,7 @@ def test_stub_dbasis_wrong_shape(monkeypatch): ast = fpapi.parse(diff_basis, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) monkeypatch.setattr(kernel, "_eval_shapes", value=["gh_quadrature_wrong"]) diff --git a/src/psyclone/tests/dynamo0p3_stubgen_test.py b/src/psyclone/tests/dynamo0p3_stubgen_test.py index 28451ebfe4..5aa8e5e7d6 100644 --- a/src/psyclone/tests/dynamo0p3_stubgen_test.py +++ b/src/psyclone/tests/dynamo0p3_stubgen_test.py @@ -46,7 +46,7 @@ from psyclone.configuration import Config from psyclone.domain.lfric import LFRicConstants -from psyclone.dynamo0p3 import DynKernMetadata, DynKern +from psyclone.dynamo0p3 import DynKernMetadata, LFRicKern from psyclone.errors import GenerationError from psyclone.gen_kernel_stub import generate @@ -71,7 +71,7 @@ def test_kernel_stub_invalid_iteration_space(): "testkern_dofs_mod.f90"), ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) with pytest.raises(GenerationError) as excinfo: _ = kernel.gen_stub @@ -159,13 +159,13 @@ def test_load_meta_wrong_type(): fparser.logging.disable(fparser.logging.CRITICAL) ast = fpapi.parse(INTENT, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() # Break the meta-data metadata.arg_descriptors[0]._argument_type = "gh_hedge" with pytest.raises(GenerationError) as excinfo: kernel.load_meta(metadata) const = LFRicConstants() - assert (f"DynKern.load_meta() expected one of {const.VALID_ARG_TYPE_NAMES}" + assert (f"LFRicKern.load_meta() expected one of {const.VALID_ARG_TYPE_NAMES}" f" but found 'gh_hedge'" in str(excinfo.value)) @@ -173,7 +173,7 @@ def test_intent(): ''' test that field intent is generated correctly for kernel stubs ''' ast = fpapi.parse(INTENT, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = kernel.gen_stub output = ( @@ -236,7 +236,7 @@ def test_spaces(): ''' ast = fpapi.parse(SPACES, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) output = ( @@ -349,7 +349,7 @@ def test_any_spaces(): ''' ast = fpapi.parse(ANY_SPACES, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) output = ( @@ -408,7 +408,7 @@ def test_vectors(): ''' test that field vectors are handled correctly for kernel stubs ''' ast = fpapi.parse(VECTORS, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = kernel.gen_stub output = ( @@ -459,7 +459,7 @@ def test_enforce_bc_kernel_stub_gen(): ast = fpapi.parse(os.path.join(BASE_PATH, "enforce_bc_kernel_mod.f90"), ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = kernel.gen_stub output = ( @@ -494,7 +494,7 @@ def test_enforce_op_bc_kernel_stub_gen(): "enforce_operator_bc_kernel_mod.F90"), ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) output = ( @@ -527,7 +527,7 @@ def test_multi_qr_stub_gen(): "testkern_2qr_mod.F90"), ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) assert ("SUBROUTINE testkern_2qr_code(nlayers, field_1_w1, field_2_w2, " @@ -571,7 +571,7 @@ def test_qr_plus_eval_stub_gen(): "testkern_qr_eval_mod.F90"), ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) gen_code = str(kernel.gen_stub) assert ( @@ -629,7 +629,7 @@ def test_sub_name(): kernel name.''' ast = fpapi.parse(SUB_NAME, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = kernel.gen_stub output = ( diff --git a/src/psyclone/tests/dynamo0p3_test.py b/src/psyclone/tests/dynamo0p3_test.py index 6d1c4000e1..a73891d231 100644 --- a/src/psyclone/tests/dynamo0p3_test.py +++ b/src/psyclone/tests/dynamo0p3_test.py @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors R. W. Ford, A. R. Porter and S. Siso, STFC Daresbury Lab -# Modified I. Kavcic and A. Coughtrie, Met Office, +# Modified I. Kavcic, A. Coughtrie and L. Turner, Met Office, # C. M. Maynard, Met Office/University of Reading, # J. Henrichs, Bureau of Meteorology. @@ -50,7 +50,7 @@ from psyclone.domain.lfric import FunctionSpace, LFRicArgDescriptor, \ LFRicConstants from psyclone.dynamo0p3 import DynACCEnterDataDirective, \ - DynBoundaryConditions, DynCellIterators, DynGlobalSum, DynKern, \ + DynBoundaryConditions, DynCellIterators, DynGlobalSum, LFRicKern, \ DynKernelArguments, DynKernMetadata, DynLoop, DynProxies, \ HaloReadAccess, KernCallArgList from psyclone.errors import FieldNotFoundError, GenerationError, InternalError @@ -876,7 +876,7 @@ def test_bc_kernel_anyspace1_only(): psy = PSyFactory(TEST_API, distributed_memory=False).create(invoke_info) invoke = psy.invokes.invoke_list[0] schedule = invoke.schedule - kernels = schedule.walk(DynKern) + kernels = schedule.walk(LFRicKern) assert kernels[0].base_name == "enforce_bc" # Ensure that none of the arguments are listed as being on ANY_SPACE_1 for fspace in kernels[0].arguments._unique_fss: @@ -897,7 +897,7 @@ def test_bc_op_kernel_wrong_args(): api=TEST_API) psy = PSyFactory(TEST_API, distributed_memory=False).create(invoke_info) invoke = psy.invokes.invoke_list[0] - kernels = invoke.schedule.walk(DynKern) + kernels = invoke.schedule.walk(LFRicKern) # Ensure that the kernel has the wrong number of arguments - duplicate # the existing argument in the list kernels[0].arguments.args.append(kernels[0].arguments.args[0]) @@ -2315,7 +2315,7 @@ def test_func_descriptor_str(): assert output in func_str -def test_dynkern_arg_for_fs(): +def test_lfrickern_arg_for_fs(): ''' Test that DynInvoke.arg_for_funcspace() raises an error if passed an invalid function space. @@ -3647,19 +3647,19 @@ def test_haloex_not_required(monkeypatch): def test_dyncollection_err1(): ''' Check that the DynCollection constructor raises the expected - error if it is not provided with a DynKern or DynInvoke. ''' + error if it is not provided with a LFRicKern or DynInvoke. ''' _, info = parse(os.path.join(BASE_PATH, "1_single_invoke.f90"), api=TEST_API) psy = PSyFactory(TEST_API, distributed_memory=True).create(info) with pytest.raises(InternalError) as err: _ = DynProxies(psy) - assert ("DynCollection takes only a DynInvoke or a DynKern but" + assert ("DynCollection takes only a DynInvoke or a LFRicKern but" in str(err.value)) def test_dyncollection_err2(monkeypatch): ''' Check that the DynCollection constructor raises the expected - error if it is not provided with a DynKern or DynInvoke. ''' + error if it is not provided with a LFRicKern or DynInvoke. ''' _, info = parse(os.path.join(BASE_PATH, "1_single_invoke.f90"), api=TEST_API) psy = PSyFactory(TEST_API, distributed_memory=True).create(info) diff --git a/src/psyclone/tests/dynkern_test.py b/src/psyclone/tests/dynkern_test.py index f64839c273..00115af219 100644 --- a/src/psyclone/tests/dynkern_test.py +++ b/src/psyclone/tests/dynkern_test.py @@ -36,8 +36,8 @@ # J. Henrichs, Bureau of Meteorology # A. R. Porter, STFC Daresbury Laboratory -'''This module tests the DynKern class within dynamo0p3 using -pytest. At the moment the tests here do not fully cover DynKern as +'''This module tests the LFRicKern class within dynamo0p3 using +pytest. At the moment the tests here do not fully cover LFRicKern as tests for other classes end up covering the rest.''' import os @@ -48,7 +48,7 @@ import psyclone from psyclone.core import AccessType from psyclone.domain.lfric import LFRicConstants, LFRicTypes -from psyclone.dynamo0p3 import DynKernMetadata, DynKern, DynLoop +from psyclone.dynamo0p3 import DynKernMetadata, LFRicKern, DynLoop from psyclone.errors import InternalError, GenerationError from psyclone.parse.algorithm import parse from psyclone.psyGen import PSyFactory @@ -91,7 +91,7 @@ def test_scalar_kernel_load_meta_err(): - ''' Check that the DynKern.load_meta() method raises the expected + ''' Check that the LFRicKern.load_meta() method raises the expected internal error if it encounters an unrecognised data type for a scalar descriptor. @@ -99,7 +99,7 @@ def test_scalar_kernel_load_meta_err(): ast = fpapi.parse(CODE, ignore_comments=False) name = "testkern_qr_type" metadata = DynKernMetadata(ast, name=name) - kernel = DynKern() + kernel = LFRicKern() # Get a scalar argument descriptor and set an invalid data type scalar_arg = metadata.arg_descriptors[5] scalar_arg._data_type = "gh_triple" @@ -111,7 +111,7 @@ def test_scalar_kernel_load_meta_err(): def test_kern_colourmap(monkeypatch): - ''' Tests for error conditions in the colourmap getter of DynKern. ''' + ''' Tests for error conditions in the colourmap getter of LFRicKern. ''' _, invoke_info = parse(os.path.join(BASE_PATH, "1_single_invoke.f90"), api=TEST_API) psy = PSyFactory(TEST_API, distributed_memory=True).create(invoke_info) @@ -129,7 +129,7 @@ def test_kern_colourmap(monkeypatch): def test_kern_ncolours(monkeypatch): - ''' Tests for error conditions in the ncolours getter of DynKern. ''' + ''' Tests for error conditions in the ncolours getter of LFRicKern. ''' _, invoke_info = parse(os.path.join(BASE_PATH, "1_single_invoke.f90"), api=TEST_API) psy = PSyFactory(TEST_API, distributed_memory=True).create(invoke_info) @@ -177,7 +177,7 @@ def test_get_kernel_schedule_mixed_precision(): _, invoke = get_invoke("26.8_mixed_precision_args.f90", TEST_API, name="invoke_0", dist_mem=False) sched = invoke.schedule - kernels = sched.walk(DynKern, stop_type=DynKern) + kernels = sched.walk(LFRicKern, stop_type=LFRicKern) # 26.8 contains an invoke of three kernels, one each at the following # precisions. kernel_precisions = ["r_def", "r_solver", "r_tran"] @@ -201,14 +201,14 @@ def test_get_kernel_sched_mixed_precision_no_match(monkeypatch): _, invoke = get_invoke("26.8_mixed_precision_args.f90", TEST_API, name="invoke_0", dist_mem=False) sched = invoke.schedule - kernels = sched.walk(DynKern, stop_type=DynKern) + kernels = sched.walk(LFRicKern, stop_type=LFRicKern) # To simplify things we just monkeypatch the 'validate_kernel_code_args' # method so that it never succeeds. def fake_validate(_1, _2): raise GenerationError("Just a test") - monkeypatch.setattr(DynKern, "validate_kernel_code_args", + monkeypatch.setattr(LFRicKern, "validate_kernel_code_args", fake_validate) with pytest.raises(GenerationError) as err: _ = kernels[0].get_kernel_schedule() @@ -234,7 +234,7 @@ def test_validate_kernel_code_args(monkeypatch): sched = kernel.get_kernel_schedule() kernel.validate_kernel_code_args(sched.symbol_table) - # Force DynKern to think that this kernel is an 'apply' kernel and + # Force LFRicKern to think that this kernel is an 'apply' kernel and # therefore does not need the mesh height argument. monkeypatch.setattr(kernel, "_cma_operation", "apply") with pytest.raises(GenerationError) as info: @@ -252,7 +252,7 @@ def test_validate_kernel_code_arg(monkeypatch): exceptions are raised. ''' - kernel = DynKern() + kernel = LFRicKern() # Kernel name needs to be set when testing exceptions. kernel._name = "dummy" read_access = ArgumentInterface(ArgumentInterface.Access.READ) @@ -369,12 +369,12 @@ def test_validate_kernel_code_arg(monkeypatch): def test_kern_last_cell_all_colours_errors(monkeypatch): ''' Tests for the checks in the last_cell_all_colours property - of DynKern. ''' + of LFRicKern. ''' _, invoke_info = parse(os.path.join(BASE_PATH, "1_single_invoke.f90"), api=TEST_API) psy = PSyFactory(TEST_API, distributed_memory=True).create(invoke_info) sched = psy.invokes.invoke_list[0].schedule - kern = sched.walk(DynKern)[0] + kern = sched.walk(LFRicKern)[0] # Kernel is not coloured. with pytest.raises(InternalError) as err: _ = kern.last_cell_all_colours_symbol @@ -389,7 +389,7 @@ def test_kern_last_cell_all_colours_errors(monkeypatch): def test_kern_last_cell_all_colours(): - ''' Tests for the last_cell_all_colours property of DynKern. ''' + ''' Tests for the last_cell_all_colours property of LFRicKern. ''' _, invoke_info = parse(os.path.join(BASE_PATH, "1_single_invoke.f90"), api=TEST_API) psy = PSyFactory(TEST_API, distributed_memory=True).create(invoke_info) @@ -406,7 +406,7 @@ def test_kern_last_cell_all_colours(): def test_kern_last_cell_all_colours_intergrid(): - ''' Test the last_cell_all_colours property for an inter-grid DynKern. ''' + ''' Test the last_cell_all_colours property for an inter-grid LFRicKern. ''' _, invoke_info = parse(os.path.join(BASE_PATH, "22.1_intergrid_restrict.f90"), api=TEST_API) @@ -424,7 +424,7 @@ def test_kern_last_cell_all_colours_intergrid(): def test_kern_all_updates_are_writes(): - ''' Tests for the 'all_updates_are_writes' property of DynKern. ''' + ''' Tests for the 'all_updates_are_writes' property of LFRicKern. ''' _, invoke_info = parse(os.path.join(BASE_PATH, "1_single_invoke.f90"), api=TEST_API) psy = PSyFactory(TEST_API, distributed_memory=True).create(invoke_info) diff --git a/src/psyclone/tests/psyGen_test.py b/src/psyclone/tests/psyGen_test.py index dfb1a7819a..b163d30c10 100644 --- a/src/psyclone/tests/psyGen_test.py +++ b/src/psyclone/tests/psyGen_test.py @@ -54,7 +54,7 @@ from psyclone.domain.common.psylayer import PSyLoop from psyclone.domain.lfric import lfric_builtins from psyclone.domain.lfric.transformations import LFRicLoopFuseTrans -from psyclone.dynamo0p3 import DynKern, DynKernMetadata, DynInvokeSchedule, \ +from psyclone.dynamo0p3 import LFRicKern, DynKernMetadata, DynInvokeSchedule, \ DynKernelArguments, DynGlobalSum from psyclone.errors import GenerationError, FieldNotFoundError, InternalError from psyclone.generator import generate @@ -456,11 +456,11 @@ def test_codedkern_node_str(): ''' ast = fpapi.parse(FAKE_KERNEL_METADATA, ignore_comments=False) metadata = DynKernMetadata(ast) - my_kern = DynKern() + my_kern = LFRicKern() my_kern.load_meta(metadata) out = my_kern.node_str() expected_output = ( - colored("CodedKern", DynKern._colour) + + colored("CodedKern", LFRicKern._colour) + " dummy_code(field_1,field_2,field_3) [module_inline=False]") assert expected_output in out @@ -668,10 +668,10 @@ def test_kern_children_validation(): accept any children. ''' - # We use a subclass (CodedKern->DynKern) to test this functionality. + # We use a subclass (CodedKern->LFRicKern) to test this functionality. ast = fpapi.parse(FAKE_KERNEL_METADATA, ignore_comments=False) metadata = DynKernMetadata(ast) - kern = DynKern() + kern = LFRicKern() kern.load_meta(metadata) with pytest.raises(GenerationError) as excinfo: @@ -764,7 +764,7 @@ def test_incremented_arg(): for descriptor in metadata.arg_descriptors: if descriptor.access == AccessType.INC: descriptor._access = AccessType.READ - my_kern = DynKern() + my_kern = LFRicKern() my_kern.load_meta(metadata) with pytest.raises(FieldNotFoundError) as excinfo: CodedKern.incremented_arg(my_kern) @@ -798,7 +798,7 @@ def test_kern_is_coloured2(): table.new_symbol(f"cell{idx}", symbol_type=DataSymbol, datatype=INTEGER_TYPE) # Create a loop nest of depth 3 containing the kernel, innermost first - my_kern = DynKern() + my_kern = LFRicKern() loops = [PSyLoop.create(table.lookup("cell0"), Literal("1", INTEGER_TYPE), Literal("10", INTEGER_TYPE), @@ -1877,7 +1877,7 @@ def test_find_w_args_hes_vec_no_dep(monkeypatch, annexed): # kernel. node_list = field_e_v1.forward_read_dependencies() assert len(node_list) == 1 - assert isinstance(node_list[0].call, DynKern) + assert isinstance(node_list[0].call, LFRicKern) # There are two halo exchanges after e_v1 which should not count # as dependencies and a read access from a kernel, so there should # be no write dependencies. diff --git a/src/psyclone/transformations.py b/src/psyclone/transformations.py index a7648afdaa..c2961bd718 100644 --- a/src/psyclone/transformations.py +++ b/src/psyclone/transformations.py @@ -49,7 +49,7 @@ from psyclone.configuration import Config from psyclone.domain.lfric import KernCallArgList, LFRicConstants from psyclone.dynamo0p3 import DynHaloExchangeEnd, DynHaloExchangeStart, \ - DynInvokeSchedule, DynKern + DynInvokeSchedule, LFRicKern from psyclone.errors import InternalError from psyclone.gocean1p0 import GOInvokeSchedule from psyclone.nemo import NemoInvokeSchedule @@ -92,7 +92,7 @@ def check_intergrid(node): ''' if not node.children: return - child_kernels = node.walk(DynKern) + child_kernels = node.walk(LFRicKern) for kern in child_kernels: if kern.is_intergrid: raise TransformationError( @@ -1960,7 +1960,7 @@ def apply(self, node, options=None): is derived. :param node: a kernel node. - :type node: :py:obj:`psyclone.psygen.DynKern` + :type node: :py:obj:`psyclone.psygen.LFRicKern` :param options: a dictionary with options for transformations. :type options: Optional[Dict[str, Any]] :param str options["cellshape"]: the shape of the cells. This is\ @@ -2118,7 +2118,7 @@ def validate(self, node, options=None): this transformation. :param node: a dynamo 0.3 kernel node. - :type node: :py:obj:`psyclone.psygen.DynKern` + :type node: :py:obj:`psyclone.psygen.LFRicKern` :param options: a dictionary with options for transformations. :type options: Optional[Dict[str, Any]] :param str options["cellshape"]: the shape of the elements/cells. @@ -2138,7 +2138,7 @@ def validate(self, node, options=None): provided (as the former needs the latter). ''' - if not isinstance(node, DynKern): + if not isinstance(node, LFRicKern): raise TransformationError( f"Error in Dynamo0p3KernelConstTrans transformation. Supplied " f"node must be a dynamo kernel but found '{type(node)}'.") From 47f541b57228c4957fcea0e97379d046ed9bd5f7 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Wed, 5 Apr 2023 15:30:20 +0100 Subject: [PATCH 02/44] #2091: and the rest of the occurrences of DynKern --- .../domain/lfric/algorithm/lfric_alg.py | 1 + src/psyclone/domain/lfric/arg_ordering.py | 2 +- .../domain/lfric/kern_call_arg_list.py | 2 +- .../domain/lfric/kern_call_invoke_arg_list.py | 2 +- .../domain/lfric/kern_stub_arg_list.py | 2 +- src/psyclone/domain/lfric/kernel_interface.py | 2 +- .../domain/lfric/lfric_adjoint_harness.py | 5 ++-- src/psyclone/tests/dependency_test.py | 4 ++-- .../domain/lfric/algorithm/lfric_alg_test.py | 9 ++++---- .../tests/domain/lfric/arg_ordering_test.py | 2 +- src/psyclone/tests/domain/lfric/conftest.py | 23 ++++++++++--------- .../domain/lfric/kern_call_arg_list_test.py | 6 ++--- .../domain/lfric/lfric_domain_kernels_test.py | 10 ++++---- .../domain/lfric/lfric_field_stubgen_test.py | 13 ++++++----- .../tests/domain/lfric/lfric_loop_test.py | 8 +++---- .../lfric/lfric_mesh_props_stubgen_test.py | 10 ++++---- .../lfric/lfric_ref_elem_stubgen_test.py | 9 ++++---- .../domain/lfric/lfric_scalar_mdata_test.py | 7 +++--- .../domain/lfric/lfric_scalar_stubgen_test.py | 7 +++--- .../tests/domain/lfric/lfric_stencil_test.py | 9 ++++---- src/psyclone/tests/dynamo0p3_basis_test.py | 2 +- src/psyclone/tests/dynamo0p3_stubgen_test.py | 4 ++-- src/psyclone/tests/dynkern_test.py | 2 +- src/psyclone/tests/psyGen_test.py | 2 +- src/psyclone/transformations.py | 4 ++-- 25 files changed, 78 insertions(+), 69 deletions(-) diff --git a/src/psyclone/domain/lfric/algorithm/lfric_alg.py b/src/psyclone/domain/lfric/algorithm/lfric_alg.py index a9367761d8..81639257a3 100644 --- a/src/psyclone/domain/lfric/algorithm/lfric_alg.py +++ b/src/psyclone/domain/lfric/algorithm/lfric_alg.py @@ -33,6 +33,7 @@ # ----------------------------------------------------------------------------- # Author: A. R. Porter, STFC Daresbury Laboratory. # Modified by: R. W. Ford, STFC Daresbury Laboratory. +# L. Turner, Met Office '''This module contains the LFRicAlg class which encapsulates tools for creating standalone LFRic algorithm-layer code. diff --git a/src/psyclone/domain/lfric/arg_ordering.py b/src/psyclone/domain/lfric/arg_ordering.py index 9b85fc3973..0fb42a7a68 100644 --- a/src/psyclone/domain/lfric/arg_ordering.py +++ b/src/psyclone/domain/lfric/arg_ordering.py @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors R. W. Ford, A. R. Porter and S. Siso, STFC Daresbury Lab -# Modified I. Kavcic and A. Coughtrie, Met Office +# Modified I. Kavcic, A. Coughtrie and L. Turner, Met Office # Modified J. Henrichs, Bureau of Meteorology '''This module implements the base class for managing arguments to diff --git a/src/psyclone/domain/lfric/kern_call_arg_list.py b/src/psyclone/domain/lfric/kern_call_arg_list.py index baaabdded5..4bcad530ee 100644 --- a/src/psyclone/domain/lfric/kern_call_arg_list.py +++ b/src/psyclone/domain/lfric/kern_call_arg_list.py @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors R. W. Ford, A. R. Porter and S. Siso, STFC Daresbury Lab -# Modified I. Kavcic and A. Coughtrie, Met Office +# Modified I. Kavcic, A. Coughtrie and L. Turner, Met Office # Modified J. Henrichs, Bureau of Meteorology '''This module implements a class that manages the argument for a kernel diff --git a/src/psyclone/domain/lfric/kern_call_invoke_arg_list.py b/src/psyclone/domain/lfric/kern_call_invoke_arg_list.py index 31cb3e4393..2f362ff9ec 100644 --- a/src/psyclone/domain/lfric/kern_call_invoke_arg_list.py +++ b/src/psyclone/domain/lfric/kern_call_invoke_arg_list.py @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors R. W. Ford, A. R. Porter and S. Siso, STFC Daresbury Lab -# Modified I. Kavcic and A. Coughtrie, Met Office +# Modified I. Kavcic, A. Coughtrie and L. Turner, Met Office # Modified J. Henrichs, Bureau of Meteorology ''' diff --git a/src/psyclone/domain/lfric/kern_stub_arg_list.py b/src/psyclone/domain/lfric/kern_stub_arg_list.py index 2f61a5a0d6..5f94cb3c3e 100644 --- a/src/psyclone/domain/lfric/kern_stub_arg_list.py +++ b/src/psyclone/domain/lfric/kern_stub_arg_list.py @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors R. W. Ford, A. R. Porter and S. Siso, STFC Daresbury Lab -# Modified I. Kavcic and A. Coughtrie, Met Office +# Modified I. Kavcic, A. Coughtrie and L. Turner, Met Office # Modified J. Henrichs, Bureau of Meteorology '''This module implements a class that creates the argument list diff --git a/src/psyclone/domain/lfric/kernel_interface.py b/src/psyclone/domain/lfric/kernel_interface.py index e0c3eefb15..e8d88a90dd 100644 --- a/src/psyclone/domain/lfric/kernel_interface.py +++ b/src/psyclone/domain/lfric/kernel_interface.py @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Author R. W. Ford, STFC Daresbury Lab -# Modified: I. Kavcic, Met Office +# Modified: I. Kavcic and L. Turner, Met Office # A. R. Porter and N. Nobre, STFC Daresbury Lab # Modified: J. Henrichs, Bureau of Meteorology diff --git a/src/psyclone/psyad/domain/lfric/lfric_adjoint_harness.py b/src/psyclone/psyad/domain/lfric/lfric_adjoint_harness.py index 2b17e7c2ff..fbec629399 100644 --- a/src/psyclone/psyad/domain/lfric/lfric_adjoint_harness.py +++ b/src/psyclone/psyad/domain/lfric/lfric_adjoint_harness.py @@ -33,6 +33,7 @@ # ----------------------------------------------------------------------------- # Authors R. W. Ford and A. R. Porter, STFC Daresbury Lab # Modified by J. Henrichs, Bureau of Meteorology +# Modified by L. Turner, Met Office ''' Provides LFRic-specific PSyclone adjoint test-harness functionality. ''' @@ -330,7 +331,7 @@ def _validate_geom_arg(kern, arg_idx, name, valid_spaces, vec_len): properties of the field that it is supposed to represent. :param kern: the kernel under consideration. - :type kern: :py:class:`psyclone.dynamo0p3.DynKern` + :type kern: :py:class:`psyclone.dynamo0p3.LFRicKern` :param in arg_idx: the 1-indexed position of the argument in the list \ defined in the kernel metadata. :param str name: the name of the argument that we are expecting. @@ -451,7 +452,7 @@ def generate_lfric_adjoint_harness(tl_psyir, coord_arg_idx=None, datatype=DeferredType(), interface=ImportInterface(adj_mod)) - # Construct a DynKern using the metadata and then use it to construct + # Construct a LFRicKern using the metadata and then use it to construct # the kernel argument list. # TODO #1806 - once we have the new PSyIR-based metadata handling then # we can pass PSyIR to this routine rather than an fparser1 parse tree. diff --git a/src/psyclone/tests/dependency_test.py b/src/psyclone/tests/dependency_test.py index 235f13aa90..9e12e8f531 100644 --- a/src/psyclone/tests/dependency_test.py +++ b/src/psyclone/tests/dependency_test.py @@ -1,7 +1,7 @@ # ----------------------------------------------------------------------------- # BSD 3-Clause License # -# Copyright (c) 2019-2022, Science and Technology Facilities Council. +# Copyright (c) 2019-2023, Science and Technology Facilities Council. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -33,7 +33,7 @@ # ----------------------------------------------------------------------------- # Author: J. Henrichs, Bureau of Meteorology # Modified: A. R. Porter and R. W. Ford STFC Daresbury Lab -# Modified: I. Kavcic, Met Office +# Modified: I. Kavcic and L. Turner, Met Office ''' Module containing py.test tests for dependency analysis.''' diff --git a/src/psyclone/tests/domain/lfric/algorithm/lfric_alg_test.py b/src/psyclone/tests/domain/lfric/algorithm/lfric_alg_test.py index 8aa1aa826c..2de7d0e08e 100644 --- a/src/psyclone/tests/domain/lfric/algorithm/lfric_alg_test.py +++ b/src/psyclone/tests/domain/lfric/algorithm/lfric_alg_test.py @@ -33,6 +33,7 @@ # ----------------------------------------------------------------------------- # Author: A. R. Porter, STFC Daresbury Lab # Modified by: R. W. Ford, STFC Daresbury Lab +# L. Turner, Met Office ''' pytest tests for the LFRic-specific algorithm-generation functionality. ''' @@ -42,7 +43,7 @@ from fparser import api as fpapi from psyclone.domain.lfric import KernCallInvokeArgList from psyclone.domain.lfric.algorithm.lfric_alg import LFRicAlg -from psyclone.dynamo0p3 import DynKern +from psyclone.dynamo0p3 import LFRicKern from psyclone.errors import InternalError from psyclone.psyir.nodes import Container, Routine from psyclone.psyir.symbols import (ContainerSymbol, DataSymbol, DeferredType, @@ -283,10 +284,10 @@ def test_kernel_from_metadata(lfric_alg): "Kernel type john does not exist'." in str(err.value)) # Valid parse tree and correct name. kern = lfric_alg.kernel_from_metadata(ptree, "testkern_type") - assert isinstance(kern, DynKern) + assert isinstance(kern, LFRicKern) -def test_construct_kernel_args(lfric_alg, prog, dynkern, fortran_writer): +def test_construct_kernel_args(lfric_alg, prog, lfrickern, fortran_writer): ''' Tests for the construct_kernel_args() function. Since this function primarily calls _create_function_spaces(), initialise_field(), KernCallInvokeArgList.generate() and initialise_quadrature(), all of which @@ -296,7 +297,7 @@ def test_construct_kernel_args(lfric_alg, prog, dynkern, fortran_writer): prog.symbol_table.new_symbol("field_type", symbol_type=DataTypeSymbol, datatype=DeferredType(), interface=ImportInterface(field_mod)) - kargs = lfric_alg.construct_kernel_args(prog, dynkern) + kargs = lfric_alg.construct_kernel_args(prog, lfrickern) assert isinstance(kargs, KernCallInvokeArgList) gen = fortran_writer(prog) diff --git a/src/psyclone/tests/domain/lfric/arg_ordering_test.py b/src/psyclone/tests/domain/lfric/arg_ordering_test.py index cfad99385a..e3f02f36ff 100644 --- a/src/psyclone/tests/domain/lfric/arg_ordering_test.py +++ b/src/psyclone/tests/domain/lfric/arg_ordering_test.py @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors R. W. Ford and A. R. Porter, STFC Daresbury Lab -# Modified I. Kavcic, Met Office +# Modified I. Kavcic and L. Turner, Met Office # Modified J. Henrichs, Bureau of Meteorology ''' This module tests the LFric classes based on ArgOrdering.''' diff --git a/src/psyclone/tests/domain/lfric/conftest.py b/src/psyclone/tests/domain/lfric/conftest.py index b59d41b024..994a597409 100644 --- a/src/psyclone/tests/domain/lfric/conftest.py +++ b/src/psyclone/tests/domain/lfric/conftest.py @@ -32,12 +32,13 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Author: A. R. Porter, STFC Daresbury Lab +# Modified by: L. Turner, Met Office ''' Module containing pytest fixtures for the LFRic-specific tests. ''' import pytest from psyclone.configuration import Config -from psyclone.dynamo0p3 import DynKern +from psyclone.dynamo0p3 import LFRicKern from psyclone.parse.kernel import get_kernel_parse_tree, KernelTypeFactory @@ -49,11 +50,11 @@ def api_setup_fixture(): Config._instance = None -@pytest.fixture(name="dynkern", scope="module") -def dynkern_fixture(): +@pytest.fixture(name="lfrickern", scope="module") +def lfrickern_fixture(): ''' - :returns: a DynKern object created from example metadata. - :rtype: :py:class:`psyclone.dynamo0p3.DynKern` + :returns: a LFRicKern object created from example metadata. + :rtype: :py:class:`psyclone.dynamo0p3.LFRicKern` ''' mdata_code = ''' module testkern_field_mod @@ -88,17 +89,17 @@ def dynkern_fixture(): kernel_metadata = get_kernel_parse_tree(mdata_code) ktype = KernelTypeFactory(api="dynamo0.3").create( kernel_metadata, name="testkern_field_type") - kern = DynKern() + kern = LFRicKern() kern.load_meta(ktype) return kern -@pytest.fixture(name="dynkern_op", scope="module") -def dynkern_op_fixture(): +@pytest.fixture(name="lfrickern_op", scope="module") +def lfrickern_op_fixture(): ''' - :returns: a DynKern object created from example metadata that includes \ + :returns: a LFRicKern object created from example metadata that includes \ an operator argument. - :rtype: :py:class:`psyclone.dynamo0p3.DynKern` + :rtype: :py:class:`psyclone.dynamo0p3.LFRicKern` ''' mdata_code = ''' module testkern_field_mod @@ -131,6 +132,6 @@ def dynkern_op_fixture(): kernel_metadata = get_kernel_parse_tree(mdata_code) ktype = KernelTypeFactory(api="dynamo0.3").create( kernel_metadata, name="testkern_field_type") - kern = DynKern() + kern = LFRicKern() kern.load_meta(ktype) return kern diff --git a/src/psyclone/tests/domain/lfric/kern_call_arg_list_test.py b/src/psyclone/tests/domain/lfric/kern_call_arg_list_test.py index df7fc8534a..d52ec82b5e 100644 --- a/src/psyclone/tests/domain/lfric/kern_call_arg_list_test.py +++ b/src/psyclone/tests/domain/lfric/kern_call_arg_list_test.py @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors R. W. Ford and A. R. Porter, STFC Daresbury Lab -# Modified I. Kavcic, Met Office +# Modified I. Kavcic and L. Turner, Met Office # Modified J. Henrichs, Bureau of Meteorology ''' This module tests the LFric KernCallArg class.''' @@ -45,7 +45,7 @@ from psyclone.domain.lfric import (KernCallArgList, LFRicConstants, LFRicSymbolTable, LFRicTypes) from psyclone.errors import GenerationError, InternalError -from psyclone.dynamo0p3 import DynKern +from psyclone.dynamo0p3 import LFRicKern from psyclone.parse.algorithm import parse from psyclone.psyGen import PSyFactory from psyclone.psyir.nodes import Literal, Loop, Reference, UnaryOperation @@ -199,7 +199,7 @@ def test_kerncallarglist_face_edge(dist_mem, fortran_writer): # Test that invalid kernel arguments raise the expected error: create_arg_list._kern._qr_rules["gh_quadrature_face"] = \ - DynKern.QRRule("invalid", "invalid", ["invalid"]) + LFRicKern.QRRule("invalid", "invalid", ["invalid"]) with pytest.raises(InternalError) as err: create_arg_list.generate() assert "Found invalid kernel argument 'invalid'" in str(err.value) diff --git a/src/psyclone/tests/domain/lfric/lfric_domain_kernels_test.py b/src/psyclone/tests/domain/lfric/lfric_domain_kernels_test.py index 53412e3689..4a9970d66f 100644 --- a/src/psyclone/tests/domain/lfric/lfric_domain_kernels_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_domain_kernels_test.py @@ -1,7 +1,7 @@ # ----------------------------------------------------------------------------- # BSD 3-Clause License # -# Copyright (c) 2020-2022, Science and Technology Facilities Council. +# Copyright (c) 2020-2023, Science and Technology Facilities Council. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Author: A. R. Porter, STFC Daresbury Lab -# Modified: I. Kavcic, Met Office +# Modified: I. Kavcic and L. Turner, Met Office # Modified: J. Henrichs, Bureau of Meteorology @@ -42,7 +42,7 @@ import os import pytest from fparser import api as fpapi -from psyclone.dynamo0p3 import DynKernMetadata, DynKern +from psyclone.dynamo0p3 import DynKernMetadata, LFRicKern from psyclone.parse.algorithm import parse from psyclone.parse.utils import ParseError from psyclone.psyGen import PSyFactory @@ -314,8 +314,8 @@ def test_psy_gen_domain_kernel(dist_mem, tmpdir, fortran_writer): # `lower_to_language_level` method in DynLoop can (likely) be removed, # and then we can just call `fortran_writer(schedule)` here. schedule = psy.invokes.invoke_list[0].schedule - # Lower the DynKern: - for kern in schedule.walk(DynKern): + # Lower the LFRicKern: + for kern in schedule.walk(LFRicKern): kern.lower_to_language_level() # Now call the loop handling method directly. out = fortran_writer.loop_node(schedule.children[0]) diff --git a/src/psyclone/tests/domain/lfric/lfric_field_stubgen_test.py b/src/psyclone/tests/domain/lfric/lfric_field_stubgen_test.py index 385de184f6..ae02ec8672 100644 --- a/src/psyclone/tests/domain/lfric/lfric_field_stubgen_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_field_stubgen_test.py @@ -1,7 +1,7 @@ # ----------------------------------------------------------------------------- # BSD 3-Clause License # -# Copyright (c) 2021-2022, Science and Technology Facilities Council. +# Copyright (c) 2021-2023, Science and Technology Facilities Council. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -34,6 +34,7 @@ # Author: I. Kavcic, Met Office # Modified: J. Henrichs, Bureau of Meteorology # Modified: R. W. Ford, STFC Daresbury Lab +# Modified: L. Turner, Met Office ''' Module containing pytest tests for kernel stub code generation and the related @@ -47,7 +48,7 @@ import fparser from fparser import api as fpapi from psyclone.domain.lfric import LFRicConstants -from psyclone.dynamo0p3 import DynKernMetadata, DynKern, LFRicFields +from psyclone.dynamo0p3 import DynKernMetadata, LFRicKern, LFRicFields from psyclone.f2pygen import ModuleGen, SubroutineGen from psyclone.errors import InternalError @@ -97,7 +98,7 @@ def test_lfricfields_stub_err(): fparser.logging.disable(fparser.logging.CRITICAL) ast = fpapi.parse(FIELD_CODE, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) # Create an empty Kernel stub module and subroutine objects psy_module = ModuleGen("testkern_2qr_int_field_mod") @@ -152,7 +153,7 @@ def test_int_field_gen_stub(): ''' ast = fpapi.parse(INTEGER_FIELD_CODE, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) output = ( @@ -216,7 +217,7 @@ def test_int_field_all_stencils_gen_stub(): os.path.join(BASE_PATH, "testkern_stencil_multi_int_field_mod.f90"), ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) output = ( @@ -281,7 +282,7 @@ def test_real_int_field_gen_stub(): "func_type(w1, gh_basis, gh_diff_basis),", 1) ast = fpapi.parse(code, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) output = ( diff --git a/src/psyclone/tests/domain/lfric/lfric_loop_test.py b/src/psyclone/tests/domain/lfric/lfric_loop_test.py index 2228e8ce3e..e52ac3f04f 100644 --- a/src/psyclone/tests/domain/lfric/lfric_loop_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_loop_test.py @@ -1,7 +1,7 @@ # ----------------------------------------------------------------------------- # BSD 3-Clause License # -# Copyright (c) 2021-2022, Science and Technology Facilities Council +# Copyright (c) 2021-2023, Science and Technology Facilities Council # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors R. W. Ford, A. R. Porter and S. Siso, STFC Daresbury Lab -# Modified I. Kavcic and A. Coughtrie, Met Office, +# Modified I. Kavcic, A. Coughtrie and L. Turner, Met Office, # C. M. Maynard, Met Office/University of Reading, # J. Henrichs, Bureau of Meteorology. @@ -48,7 +48,7 @@ from psyclone.core import AccessType from psyclone.domain.common.psylayer import PSyLoop from psyclone.domain.lfric import LFRicConstants, LFRicSymbolTable -from psyclone.dynamo0p3 import DynLoop, DynKern, DynKernMetadata +from psyclone.dynamo0p3 import DynLoop, LFRicKern, DynKernMetadata from psyclone.errors import GenerationError, InternalError from psyclone.parse.algorithm import parse from psyclone.psyGen import PSyFactory @@ -990,7 +990,7 @@ def test_null_loop(): end module testkern_mod ''', ignore_comments=False) dkm = DynKernMetadata(ast, name="testkern_type") - kern = DynKern() + kern = LFRicKern() kern.load_meta(dkm) with pytest.raises(GenerationError) as err: loop.load(kern) diff --git a/src/psyclone/tests/domain/lfric/lfric_mesh_props_stubgen_test.py b/src/psyclone/tests/domain/lfric/lfric_mesh_props_stubgen_test.py index eba9afae7b..53c2532c1f 100644 --- a/src/psyclone/tests/domain/lfric/lfric_mesh_props_stubgen_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_mesh_props_stubgen_test.py @@ -1,7 +1,7 @@ # ----------------------------------------------------------------------------- # BSD 3-Clause License # -# Copyright (c) 2020-2022, Science and Technology Facilities Council. +# Copyright (c) 2020-2023, Science and Technology Facilities Council. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Author: A. R. Porter, STFC Daresbury Laboratory -# Modified: I. Kavcic, Met Office +# Modified: I. Kavcic and L. Turner, Met Office # Modified: R. W. Ford, STFC Daresbury Laboratory ''' @@ -42,7 +42,7 @@ import os from fparser import api as fpapi -from psyclone.dynamo0p3 import DynKernMetadata, DynKern +from psyclone.dynamo0p3 import DynKernMetadata, LFRicKern # Constants @@ -86,7 +86,7 @@ def test_mesh_prop_stub_gen(): "testkern_mesh_prop_mod.F90"), ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) gen = str(kernel.gen_stub).lower() @@ -119,7 +119,7 @@ def test_mesh_props_quad_stub_gen(): properties should be placed at the end of subroutine argument list). ''' ast = fpapi.parse(MESH_PROP_MDATA, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) gen = str(kernel.gen_stub) diff --git a/src/psyclone/tests/domain/lfric/lfric_ref_elem_stubgen_test.py b/src/psyclone/tests/domain/lfric/lfric_ref_elem_stubgen_test.py index a02c11b161..b9d98bc385 100644 --- a/src/psyclone/tests/domain/lfric/lfric_ref_elem_stubgen_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_ref_elem_stubgen_test.py @@ -1,7 +1,7 @@ # ----------------------------------------------------------------------------- # BSD 3-Clause License # -# Copyright (c) 2020-2022, Science and Technology Facilities Council. +# Copyright (c) 2020-2023, Science and Technology Facilities Council. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -33,6 +33,7 @@ # ----------------------------------------------------------------------------- # Author: I. Kavcic, Met Office # Modified: R. W. Ford and A. R. Porter, STFC Daresbury Lab +# L. Turner, Met Office ''' Module containing pytest tests for the reference-element stub generation @@ -41,7 +42,7 @@ import os from fparser import api as fpapi -from psyclone.dynamo0p3 import DynKernMetadata, DynKern +from psyclone.dynamo0p3 import DynKernMetadata, LFRicKern # Constants @@ -81,7 +82,7 @@ def test_refelem_stub_gen(): "testkern_ref_elem_mod.F90"), ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) gen = str(kernel.gen_stub) @@ -130,7 +131,7 @@ def test_refelem_quad_stub_gen(): properties should be placed at the end of subroutine argument list). ''' ast = fpapi.parse(REF_ELEM_QUAD_MDATA, ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) gen = str(kernel.gen_stub) diff --git a/src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py b/src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py index e1523fd724..0075caa2f6 100644 --- a/src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py @@ -1,7 +1,7 @@ # ----------------------------------------------------------------------------- # BSD 3-Clause License # -# Copyright (c) 2017-2022, Science and Technology Facilities Council. +# Copyright (c) 2017-2023, Science and Technology Facilities Council. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -35,6 +35,7 @@ # I. Kavcic and A. Coughtrie, Met Office; # C. M. Maynard, Met Office/University of Reading; # J. Henrichs, Bureau of Meteorology. +# Modified by L. Turner, Met Office ''' Module containing pytest tests for the general LFRic scalar arguments @@ -46,7 +47,7 @@ import fparser from fparser import api as fpapi from psyclone.domain.lfric import LFRicArgDescriptor -from psyclone.dynamo0p3 import (DynKern, DynKernMetadata, +from psyclone.dynamo0p3 import (LFRicKern, DynKernMetadata, LFRicScalarArgs, LFRicConstants) from psyclone.errors import InternalError, GenerationError from psyclone.f2pygen import ModuleGen @@ -468,7 +469,7 @@ def test_scalar_arg_lfricconst_properties(monkeypatch): ast = fpapi.parse(CODE, ignore_comments=False) name = "testkern_qr_type" metadata = DynKernMetadata(ast, name=name) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) # Test 'real' scalars diff --git a/src/psyclone/tests/domain/lfric/lfric_scalar_stubgen_test.py b/src/psyclone/tests/domain/lfric/lfric_scalar_stubgen_test.py index 3ebd04d9e4..c7a67c8cc1 100644 --- a/src/psyclone/tests/domain/lfric/lfric_scalar_stubgen_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_scalar_stubgen_test.py @@ -1,7 +1,7 @@ # ----------------------------------------------------------------------------- # BSD 3-Clause License # -# Copyright (c) 2017-2022, Science and Technology Facilities Council. +# Copyright (c) 2017-2023, Science and Technology Facilities Council. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -35,6 +35,7 @@ # I. Kavcic and A. Coughtrie, Met Office; # C. M. Maynard, Met Office/University of Reading; # J. Henrichs, Bureau of Meteorology. +# Modified by: L. Turner, Met Office ''' Module containing pytest tests for kernel stub code generation for the @@ -47,7 +48,7 @@ from fparser import api as fpapi from psyclone.domain.lfric import LFRicConstants -from psyclone.dynamo0p3 import DynKernMetadata, DynKern, LFRicScalarArgs +from psyclone.dynamo0p3 import DynKernMetadata, LFRicKern, LFRicScalarArgs from psyclone.f2pygen import ModuleGen from psyclone.errors import InternalError from psyclone.gen_kernel_stub import generate @@ -71,7 +72,7 @@ def test_lfricscalars_stub_err(): "testkern_one_int_scalar_mod.f90"), ignore_comments=False) metadata = DynKernMetadata(ast) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) # Sabotage the scalar argument to make it have an invalid data type arg = kernel.arguments.args[1] diff --git a/src/psyclone/tests/domain/lfric/lfric_stencil_test.py b/src/psyclone/tests/domain/lfric/lfric_stencil_test.py index 04042ea9d9..f4a9a8e3e4 100644 --- a/src/psyclone/tests/domain/lfric/lfric_stencil_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_stencil_test.py @@ -1,7 +1,7 @@ # ----------------------------------------------------------------------------- # BSD 3-Clause License # -# Copyright (c) 2021-2022, Science and Technology Facilities Council. +# Copyright (c) 2021-2023, Science and Technology Facilities Council. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -35,6 +35,7 @@ # I. Kavcic and A. Coughtrie, Met Office, # C. M. Maynard, Met Office/University of Reading, # J. Henrichs, Bureau of Meteorology. +# Modified by: L. Turner, Met Office ''' Module containing tests of LFRic stencils through the LFRic API ''' @@ -45,7 +46,7 @@ from fparser import api as fpapi from psyclone.domain.lfric import LFRicConstants -from psyclone.dynamo0p3 import (DynKern, DynKernelArguments, +from psyclone.dynamo0p3 import (LFRicKern, DynKernelArguments, DynKernMetadata, DynStencils) from psyclone.errors import GenerationError, InternalError from psyclone.f2pygen import ModuleGen @@ -166,7 +167,7 @@ def test_stencil_field_arg_lfricconst_properties(monkeypatch): # Test 'real'-valued field of 'field_type' with stencil access ast = fpapi.parse(STENCIL_CODE, ignore_comments=False) metadata = DynKernMetadata(ast, name=name) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) stencil_arg = kernel.arguments.args[1] assert stencil_arg.module_name == "field_mod" @@ -181,7 +182,7 @@ def test_stencil_field_arg_lfricconst_properties(monkeypatch): "gh_field, gh_integer") ast = fpapi.parse(code, ignore_comments=False) metadata = DynKernMetadata(ast, name=name) - kernel = DynKern() + kernel = LFRicKern() kernel.load_meta(metadata) stencil_arg = kernel.arguments.args[1] assert stencil_arg.module_name == "integer_field_mod" diff --git a/src/psyclone/tests/dynamo0p3_basis_test.py b/src/psyclone/tests/dynamo0p3_basis_test.py index 2021c8afff..ded84eaee8 100644 --- a/src/psyclone/tests/dynamo0p3_basis_test.py +++ b/src/psyclone/tests/dynamo0p3_basis_test.py @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors: R. W. Ford, A. R. Porter and N. Nobre, STFC Daresbury Lab -# Modified: I. Kavcic, Met Office +# Modified: I. Kavcic and L. Turner, Met Office # Modified: J. Henrichs, Bureau of Meteorology ''' Module containing py.test tests for functionality related to diff --git a/src/psyclone/tests/dynamo0p3_stubgen_test.py b/src/psyclone/tests/dynamo0p3_stubgen_test.py index 5aa8e5e7d6..8aa9c70044 100644 --- a/src/psyclone/tests/dynamo0p3_stubgen_test.py +++ b/src/psyclone/tests/dynamo0p3_stubgen_test.py @@ -1,7 +1,7 @@ # ----------------------------------------------------------------------------- # BSD 3-Clause License # -# Copyright (c) 2017-2022, Science and Technology Facilities Council. +# Copyright (c) 2017-2023, Science and Technology Facilities Council. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors R. W. Ford and A. R. Porter, STFC Daresbury Lab -# Modified I. Kavcic, Met Office +# Modified I. Kavcic and L. Turner, Met Office # Modified J. Henrichs, Bureau of Meteorology ''' This module tests the LFRic (Dynamo 0.3) kernel-stub generator using diff --git a/src/psyclone/tests/dynkern_test.py b/src/psyclone/tests/dynkern_test.py index 00115af219..3f93036967 100644 --- a/src/psyclone/tests/dynkern_test.py +++ b/src/psyclone/tests/dynkern_test.py @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Author: R. W. Ford STFC Daresbury Lab -# Modified: I. Kavcic Met Office +# Modified: I. Kavcic and L. Turner, Met Office # J. Henrichs, Bureau of Meteorology # A. R. Porter, STFC Daresbury Laboratory diff --git a/src/psyclone/tests/psyGen_test.py b/src/psyclone/tests/psyGen_test.py index b163d30c10..ec0a69ad63 100644 --- a/src/psyclone/tests/psyGen_test.py +++ b/src/psyclone/tests/psyGen_test.py @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors: R. W. Ford, A. R. Porter, S. Siso and N. Nobre, STFC Daresbury Lab -# Modified I. Kavcic, Met Office +# Modified I. Kavcic and L. Turner, Met Office # ----------------------------------------------------------------------------- ''' Performs py.test tests on the psyGen module ''' diff --git a/src/psyclone/transformations.py b/src/psyclone/transformations.py index c2961bd718..3a95bbfc08 100644 --- a/src/psyclone/transformations.py +++ b/src/psyclone/transformations.py @@ -1,7 +1,7 @@ # ----------------------------------------------------------------------------- # BSD 3-Clause License # -# Copyright (c) 2017-2022, Science and Technology Facilities Council. +# Copyright (c) 2017-2023, Science and Technology Facilities Council. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -34,7 +34,7 @@ # Authors R. W. Ford, A. R. Porter, S. Siso and N. Nobre, STFC Daresbury Lab # A. B. G. Chalk STFC Daresbury Lab # J. Henrichs, Bureau of Meteorology -# Modified I. Kavcic, Met Office +# Modified I. Kavcic and L. Turner, Met Office ''' This module provides the various transformations that can be applied to PSyIR nodes. There are both general and API-specific transformation From 5d97779fd982c0855368527e00d54bdc42a4af2d Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Wed, 5 Apr 2023 15:43:32 +0100 Subject: [PATCH 03/44] #2091: reverting an unintentional DynKernMetadata -> LFRicKernMetadata change --- src/psyclone/tests/dynamo0p3_quadrature_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psyclone/tests/dynamo0p3_quadrature_test.py b/src/psyclone/tests/dynamo0p3_quadrature_test.py index eff768339b..0051ed4a62 100644 --- a/src/psyclone/tests/dynamo0p3_quadrature_test.py +++ b/src/psyclone/tests/dynamo0p3_quadrature_test.py @@ -713,7 +713,7 @@ def test_lfrickern_setup(monkeypatch): ast = fpapi.parse(os.path.join(BASE_PATH, "testkern_qr.F90"), ignore_comments=False) name = "testkern_qr_type" - dkm = LFRicKernMetadata(ast, name=name) + dkm = DynKernMetadata(ast, name=name) # Finally, call the _setup() method with pytest.raises(InternalError) as excinfo: kern._setup(dkm, "my module", None, None) From e86c3b3860da2482b3dd8da33c0a01990c6b8703 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Wed, 5 Apr 2023 16:05:20 +0100 Subject: [PATCH 04/44] #2091: missed some dynkern instances --- .../lfric/kern_call_invoke_arg_list_test.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/psyclone/tests/domain/lfric/kern_call_invoke_arg_list_test.py b/src/psyclone/tests/domain/lfric/kern_call_invoke_arg_list_test.py index fa06bc09e4..874eb7afcf 100644 --- a/src/psyclone/tests/domain/lfric/kern_call_invoke_arg_list_test.py +++ b/src/psyclone/tests/domain/lfric/kern_call_invoke_arg_list_test.py @@ -33,6 +33,7 @@ # ----------------------------------------------------------------------------- # Author: A. R. Porter, STFC Daresbury Lab # Modified: J. Henrichs, Bureau of Meteorology +# L. Turner, Met Office ''' Module containing pytest tests for the KernCallInvokeArgList class. ''' @@ -42,26 +43,26 @@ from psyclone.psyir.symbols import DataTypeSymbol, DeferredType, DataSymbol -def test_kcial_construct(dynkern): +def test_kcial_construct(lfrickern): ''' Tests for the KernCallInvokeArgList constructor. ''' with pytest.raises(TypeError) as err: - KernCallInvokeArgList(dynkern, None) + KernCallInvokeArgList(lfrickern, None) assert ("Argument 'symbol_table' must be a SymbolTable instance but got " "'NoneType'" in str(err.value)) - obj = KernCallInvokeArgList(dynkern, LFRicSymbolTable()) + obj = KernCallInvokeArgList(lfrickern, LFRicSymbolTable()) assert obj.fields == [] assert obj.scalars == [] assert obj.quadrature_objects == [] -def test_kcial_generate(dynkern): +def test_kcial_generate(lfrickern): ''' Tests for the KernCallInvokeArgList.generate() method. ''' # generate() assumes a suitably initialised symbol table so create # that here. table = LFRicSymbolTable() table.new_symbol("field_type", symbol_type=DataTypeSymbol, datatype=DeferredType()) - kcial = KernCallInvokeArgList(dynkern, table) + kcial = KernCallInvokeArgList(lfrickern, table) kcial.generate() assert len(kcial.fields) == 5 assert len(kcial.scalars) == 2 @@ -69,8 +70,8 @@ def test_kcial_generate(dynkern): kcial.generate() assert len(kcial.fields) == 5 # Check that an unsupported scalar type gives the expected error. - dynkern.arguments.args[0]._intrinsic_type = 'boolean' - kcial = KernCallInvokeArgList(dynkern, table) + lfrickern.arguments.args[0]._intrinsic_type = 'boolean' + kcial = KernCallInvokeArgList(lfrickern, table) with pytest.raises(NotImplementedError) as err: kcial.generate() assert "Scalar of type 'boolean' not supported" in str(err.value) @@ -96,10 +97,10 @@ def test_kcial_generate_operator(dynkern_op): assert opers[0][2] == "w2" -def test_kcial_not_implemented(dynkern): +def test_kcial_not_implemented(lfrickern): ''' Check all the methods that handle unsupported types of kernel argument. ''' - kcial = KernCallInvokeArgList(dynkern, LFRicSymbolTable()) + kcial = KernCallInvokeArgList(lfrickern, LFRicSymbolTable()) with pytest.raises(NotImplementedError) as err: kcial.stencil(None) assert "Stencils are not yet supported" in str(err.value) From c9c0ff367c1795cf8b437f20f260ef366d9b60cb Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Wed, 5 Apr 2023 16:24:48 +0100 Subject: [PATCH 05/44] #2091: missed some dynkern_op --- .../tests/domain/lfric/kern_call_invoke_arg_list_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/psyclone/tests/domain/lfric/kern_call_invoke_arg_list_test.py b/src/psyclone/tests/domain/lfric/kern_call_invoke_arg_list_test.py index 874eb7afcf..a0063c2935 100644 --- a/src/psyclone/tests/domain/lfric/kern_call_invoke_arg_list_test.py +++ b/src/psyclone/tests/domain/lfric/kern_call_invoke_arg_list_test.py @@ -77,7 +77,7 @@ def test_kcial_generate(lfrickern): assert "Scalar of type 'boolean' not supported" in str(err.value) -def test_kcial_generate_operator(dynkern_op): +def test_kcial_generate_operator(lfrickern_op): '''Test the generate() method correctly populates the list of operator arguments required by the kernel.''' # generate() assumes a suitably initialised symbol table so create @@ -87,7 +87,7 @@ def test_kcial_generate_operator(dynkern_op): datatype=DeferredType()) table.new_symbol("field_type", symbol_type=DataTypeSymbol, datatype=DeferredType()) - kcial = KernCallInvokeArgList(dynkern_op, table) + kcial = KernCallInvokeArgList(lfrickern_op, table) kcial.generate() opers = kcial.operators assert len(opers) == 1 From 0446ec37ed1ad6ca354ac5347f7d5cd600543160 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Thu, 6 Apr 2023 11:54:28 +0100 Subject: [PATCH 06/44] #2091: moving LFRicKern out of dynamo0p3 --- src/psyclone/domain/lfric/__init__.py | 1 + .../domain/lfric/algorithm/lfric_alg.py | 4 +- src/psyclone/domain/lfric/arg_ordering.py | 2 +- .../domain/lfric/kern_call_arg_list.py | 2 +- .../domain/lfric/kern_call_invoke_arg_list.py | 2 +- .../domain/lfric/kern_stub_arg_list.py | 2 +- src/psyclone/domain/lfric/kernel_interface.py | 2 +- src/psyclone/domain/lfric/lfric_kern.py | 947 ++++++++++++++++++ src/psyclone/dynamo0p3.py | 910 +---------------- .../domain/lfric/lfric_adjoint_harness.py | 2 +- src/psyclone/tests/domain/lfric/conftest.py | 4 +- 11 files changed, 978 insertions(+), 900 deletions(-) create mode 100644 src/psyclone/domain/lfric/lfric_kern.py diff --git a/src/psyclone/domain/lfric/__init__.py b/src/psyclone/domain/lfric/__init__.py index 49c158d52e..bff34295ed 100644 --- a/src/psyclone/domain/lfric/__init__.py +++ b/src/psyclone/domain/lfric/__init__.py @@ -60,6 +60,7 @@ MetadataToArgumentsRules from psyclone.domain.lfric.arg_index_to_metadata_index import \ ArgIndexToMetadataIndex +from psyclone.domain.lfric.lfric_kern import LFRicKern __all__ = [ 'ArgOrdering', diff --git a/src/psyclone/domain/lfric/algorithm/lfric_alg.py b/src/psyclone/domain/lfric/algorithm/lfric_alg.py index 81639257a3..f2ef2abbe1 100644 --- a/src/psyclone/domain/lfric/algorithm/lfric_alg.py +++ b/src/psyclone/domain/lfric/algorithm/lfric_alg.py @@ -420,7 +420,7 @@ def kernel_from_metadata(parse_tree, kernel_name): supplied parse tree for which a LFRicKern is to be created. :returns: a LFRicKern object describing the LFRic kernel. - :rtype: :py:class:`psyclone.dynamo0p3.LFRicKern` + :rtype: :py:class:`psyclone.domain.lfric.LFRicKern` :raises ValueError: if an LFRic kernel with the specified name cannot \ be found in the supplied parse tree. @@ -446,7 +446,7 @@ def construct_kernel_args(self, prog, kern): :param prog: the routine to which to add the declarations etc. :type prog: :py:class:`psyclone.psyir.nodes.Routine` :param kern: the kernel for which we are to create arguments. - :type kern: :py:class:`psyclone.dynamo0p3.LFRicKern` + :type kern: :py:class:`psyclone.domain.lfric.LFRicKern` :returns: object capturing all of the kernel arguments. :rtype: :py:class:`psyclone.domain.lfric.KernCallInvokeArgList` diff --git a/src/psyclone/domain/lfric/arg_ordering.py b/src/psyclone/domain/lfric/arg_ordering.py index 0fb42a7a68..f1c7acf16f 100644 --- a/src/psyclone/domain/lfric/arg_ordering.py +++ b/src/psyclone/domain/lfric/arg_ordering.py @@ -65,7 +65,7 @@ class ArgOrdering: a list. :param kern: the kernel call object to use. - :type kern: :py:class:`psyclone.dynamo0p3.LFRicKern` + :type kern: :py:class:`psyclone.domain.lfric.LFRicKern` ''' def __init__(self, kern): diff --git a/src/psyclone/domain/lfric/kern_call_arg_list.py b/src/psyclone/domain/lfric/kern_call_arg_list.py index 4bcad530ee..1f26937b57 100644 --- a/src/psyclone/domain/lfric/kern_call_arg_list.py +++ b/src/psyclone/domain/lfric/kern_call_arg_list.py @@ -72,7 +72,7 @@ class KernCallArgList(ArgOrdering): captured by the base class. :param kern: The kernel that is being called. - :type kern: :py:class:`psyclone.dynamo0p3.LFRicKern` + :type kern: :py:class:`psyclone.domain.lfric.LFRicKern` ''' NdfInfo = namedtuple("NdfInfo", ["position", "function_space"]) diff --git a/src/psyclone/domain/lfric/kern_call_invoke_arg_list.py b/src/psyclone/domain/lfric/kern_call_invoke_arg_list.py index 2f362ff9ec..99a003c2c0 100644 --- a/src/psyclone/domain/lfric/kern_call_invoke_arg_list.py +++ b/src/psyclone/domain/lfric/kern_call_invoke_arg_list.py @@ -53,7 +53,7 @@ class KernCallInvokeArgList(ArgOrdering): kernel, according to that kernel's metadata. :param kern: the kernel object for which to determine arguments. - :type kern: :py:class:`psyclone.dynamo0p3.LFRicKern` + :type kern: :py:class:`psyclone.domain.lfric.LFRicKern` :param symbol_table: the symbol table associated with the routine that \ contains the `invoke` of this kernel. :type symbol_table: :py:class:`psyclone.psyir.symbols.SymbolTable` diff --git a/src/psyclone/domain/lfric/kern_stub_arg_list.py b/src/psyclone/domain/lfric/kern_stub_arg_list.py index 5f94cb3c3e..3f86277883 100644 --- a/src/psyclone/domain/lfric/kern_stub_arg_list.py +++ b/src/psyclone/domain/lfric/kern_stub_arg_list.py @@ -51,7 +51,7 @@ class KernStubArgList(ArgOrdering): of the arguments is captured by the base class. :param kern: Kernel for which to create argument list. - :type kern: :py:class:`psyclone.dynamo0p3.LFRicKern` + :type kern: :py:class:`psyclone.domain.lfric.LFRicKern` :raises NotImplementedError: if the kernel is inter-grid. :raises NotImplementedError: if the kernel requires properties of the \ diff --git a/src/psyclone/domain/lfric/kernel_interface.py b/src/psyclone/domain/lfric/kernel_interface.py index e8d88a90dd..be10150a0d 100644 --- a/src/psyclone/domain/lfric/kernel_interface.py +++ b/src/psyclone/domain/lfric/kernel_interface.py @@ -70,7 +70,7 @@ class KernelInterface(ArgOrdering): code when all of its methods are implemented. :param kern: the kernel for which to create arguments. - :type kern: :py:class:`psyclone.dynamo0p3.LFRicKern` + :type kern: :py:class:`psyclone.domain.lfric.LFRicKern` ''' #: Mapping from a generic PSyIR datatype to the equivalent diff --git a/src/psyclone/domain/lfric/lfric_kern.py b/src/psyclone/domain/lfric/lfric_kern.py new file mode 100644 index 0000000000..610bef44fc --- /dev/null +++ b/src/psyclone/domain/lfric/lfric_kern.py @@ -0,0 +1,947 @@ +# ----------------------------------------------------------------------------- +# BSD 3-Clause License +# +# Copyright (c) 2017-2023, Science and Technology Facilities Council. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- +# Authors R. W. Ford, A. R. Porter and S. Siso, STFC Daresbury Lab +# Modified I. Kavcic and A. Coughtrie, Met Office +# Modified J. Henrichs, Bureau of Meteorology +# Modified A. B. G. Chalk and N. Nobre, STFC Daresbury Lab + +''' This module implements the PSyclone Dynamo 0.3 API by 1) + specialising the required base classes in parser.py (KernelType) and + adding a new class (DynFuncDescriptor03) to capture function descriptor + metadata and 2) specialising the required base classes in psyGen.py + (PSy, Invokes, Invoke, InvokeSchedule, Loop, Kern, Inf, Arguments and + Argument). ''' + +# Imports +from collections import OrderedDict, namedtuple + +from psyclone.configuration import Config +from psyclone.core import AccessType +from psyclone.domain.lfric import (KernCallArgList, KernStubArgList, + KernelInterface, LFRicConstants) +from psyclone.errors import GenerationError, InternalError, FieldNotFoundError +from psyclone.f2pygen import (CommentGen, DeclGen, ModuleGen, SubroutineGen, + UseGen) +from psyclone.parse.algorithm import Arg, KernelCall +from psyclone.psyGen import InvokeSchedule, CodedKern +from psyclone.psyir.frontend.fparser2 import Fparser2Reader +from psyclone.psyir.nodes import (Loop, Literal, Reference, + KernelSchedule) +from psyclone.psyir.symbols import DataSymbol, ScalarType, ArrayType + +class LFRicKern(CodedKern): + ''' Stores information about Dynamo Kernels as specified by the + Kernel metadata and associated algorithm call. Uses this + information to generate appropriate PSy layer code for the Kernel + instance or to generate a Kernel stub. + + ''' + # pylint: disable=too-many-instance-attributes + # An instance of this `namedtuple` is used to store information on each of + # the quadrature rules required by a kernel. + # + # alg_name: The actual argument text specifying the QR object in the + # Alg. layer. + # psy_name: The PSy-layer variable name for the QR object. + # kernel_args: List of kernel arguments associated with this QR rule. + + QRRule = namedtuple("QRRule", + ["alg_name", "psy_name", "kernel_args"]) + + def __init__(self): + # The super-init is called from the _setup() method which in turn + # is called from load(). + # pylint: disable=super-init-not-called + # Import here to avoid circular dependency + # pylint: disable=import-outside-toplevel + from psyclone.dynamo0p3 import DynKernelArguments + if False: # pylint: disable=using-constant-test + self._arguments = DynKernelArguments(None, None) # for pyreverse + self._parent = None + self._base_name = "" + self._func_descriptors = None + self._fs_descriptors = None + # Whether this kernel requires quadrature + self._qr_required = False + # Whether this kernel requires basis functions + self._basis_required = False + # What shapes of evaluator/quadrature this kernel requires (if any) + self._eval_shapes = [] + # The function spaces on which to *evaluate* basis/diff-basis + # functions if an evaluator is required for this kernel. Is a dict with + # (mangled) FS names as keys and associated kernel argument as value. + self._eval_targets = OrderedDict() + # Will hold a dict of QRRule namedtuple objects, one for each QR + # rule required by a kernel, indexed by shape. Needs to be ordered + # because we must preserve the ordering specified in the metadata. + self._qr_rules = OrderedDict() + self._cma_operation = None + self._is_intergrid = False # Whether this is an inter-grid kernel + # The reference-element properties required by this kernel + self._reference_element = None + # The mesh properties required by this kernel + self._mesh_properties = None + # Initialise kinds (precisions) of all kernel arguments (start + # with 'real' and 'integer' kinds) + api_config = Config.get().api_conf("dynamo0.3") + self._argument_kinds = {api_config.default_kind["real"], + api_config.default_kind["integer"]} + + def reference_accesses(self, var_accesses): + '''Get all variable access information. All accesses are marked + according to the kernel metadata + + :param var_accesses: VariablesAccessInfo instance that stores the \ + information about variable accesses. + :type var_accesses: \ + :py:class:`psyclone.core.VariablesAccessInfo` + ''' + + # Use the KernelCallArgList class, which can also provide variable + # access information: + create_arg_list = KernCallArgList(self) + create_arg_list.generate(var_accesses) + + super().reference_accesses(var_accesses) + # Set the current location index to the next location, since after + # this kernel a new statement starts. + var_accesses.next_location() + + def load(self, call, parent=None): + ''' + Sets up kernel information with the call object which is + created by the parser. This object includes information about + the invoke call and the associated kernel. + + :param call: The KernelCall object from which to extract information + about this kernel + :type call: :py:class:`psyclone.parse.algorithm.KernelCall` + :param parent: The parent node of the kernel call in the AST + we are constructing. This will be a loop. + :type parent: :py:class:`psyclone.dynamo0p3.DynLoop` + ''' + self._setup_basis(call.ktype) + self._setup(call.ktype, call.module_name, call.args, parent) + + def load_meta(self, ktype): + ''' + Sets up kernel information with the kernel type object + which is created by the parser. The object includes the + metadata describing the kernel code. + + :param ktype: the kernel meta-data object produced by the parser + :type ktype: :py:class:`psyclone.dynamo0p3.DynKernMetadata` + + :raises InternalError: for an invalid data type of a scalar argument. + :raises GenerationError: if an invalid argument type is found \ + in the kernel. + + ''' + # pylint: disable=too-many-branches + # Create a name for each argument + args = [] + const = LFRicConstants() + for idx, descriptor in enumerate(ktype.arg_descriptors): + pre = None + if descriptor.argument_type.lower() == "gh_operator": + pre = "op_" + elif descriptor.argument_type.lower() == "gh_columnwise_operator": + pre = "cma_op_" + elif descriptor.argument_type.lower() == "gh_field": + pre = "field_" + elif (descriptor.argument_type.lower() in + const.VALID_SCALAR_NAMES): + if descriptor.data_type.lower() == "gh_real": + pre = "rscalar_" + elif descriptor.data_type.lower() == "gh_integer": + pre = "iscalar_" + elif descriptor.data_type.lower() == "gh_logical": + pre = "lscalar_" + else: + raise InternalError( + f"Expected one of {const.VALID_SCALAR_DATA_TYPES} " + f"data types for a scalar argument but found " + f"'{descriptor.data_type}'.") + else: + raise GenerationError( + f"LFRicKern.load_meta() expected one of " + f"{const.VALID_ARG_TYPE_NAMES} but found " + f"'{descriptor.argument_type}'") + args.append(Arg("variable", pre+str(idx+1))) + + if descriptor.stencil: + if not descriptor.stencil["extent"]: + # Stencil size (in cells) is passed in + args.append(Arg("variable", + pre+str(idx+1)+"_stencil_size")) + if descriptor.stencil["type"] == "xory1d": + # Direction is passed in + args.append(Arg("variable", pre+str(idx+1)+"_direction")) + + # Initialise basis/diff basis so we can test whether quadrature + # or an evaluator is required + self._setup_basis(ktype) + if self._basis_required: + for shape in self._eval_shapes: + if shape in const.VALID_QUADRATURE_SHAPES: + # Add a quadrature argument for each required quadrature + # rule. + args.append(Arg("variable", "qr_"+shape)) + self._setup(ktype, "dummy_name", args, None, check=False) + + def _setup_basis(self, kmetadata): + ''' + Initialisation of the basis/diff basis information. This may be + needed before general setup so is computed in a separate method. + + :param kmetadata: The kernel meta-data object produced by the parser. + :type kmetadata: :py:class:`psyclone.dynamo0p3.DynKernMetadata` + ''' + for descriptor in kmetadata.func_descriptors: + if len(descriptor.operator_names) > 0: + self._basis_required = True + self._eval_shapes = kmetadata.eval_shapes[:] + break + + def _setup(self, ktype, module_name, args, parent, check=True): + # pylint: disable=too-many-arguments + '''Internal setup of kernel information. + + :param ktype: object holding information on the parsed metadata for \ + this kernel. + :type ktype: :py:class:`psyclone.dynamo0p3.DynKernMetadata` + :param str module_name: the name of the Fortran module that contains \ + the source of this Kernel. + :param args: list of Arg objects produced by the parser for the \ + arguments of this kernel call. + :type args: list of :py:class:`psyclone.parse.algorithm.Arg` objects + :param parent: the parent of this kernel call in the generated \ + AST (will be a loop object). + :type parent: :py:class:`psyclone.dynamo0p3.DynLoop` + :param bool check: whether to check for consistency between the \ + kernel metadata and the algorithm layer. Defaults to True. + + ''' + # Import here to avoid circular dependency + # pylint: disable=import-outside-toplevel + from psyclone.dynamo0p3 import DynKernelArguments, FSDescriptors + # pylint: disable=too-many-branches, too-many-locals + CodedKern.__init__(self, DynKernelArguments, + KernelCall(module_name, ktype, args), + parent, check) + # Remove "_code" from the name if it exists to determine the + # base name which (if dynamo0.3 naming conventions are + # followed) is used as the root for the module and subroutine + # names. + if self.name.lower().endswith("_code"): + self._base_name = self.name[:-5] + else: + # TODO: #11 add a warning here when logging is added + self._base_name = self.name + self._func_descriptors = ktype.func_descriptors + # Keep a record of the type of CMA kernel identified when + # parsing the kernel meta-data + self._cma_operation = ktype.cma_operation + self._fs_descriptors = FSDescriptors(ktype.func_descriptors) + + # Record whether or not the kernel meta-data specifies that this + # is an inter-grid kernel + self._is_intergrid = ktype.is_intergrid + + const = LFRicConstants() + # Check that all specified evaluator shapes are recognised + invalid_shapes = set(self._eval_shapes) \ + - set(const.VALID_EVALUATOR_SHAPES) + if invalid_shapes: + raise InternalError( + f"Evaluator shape(s) {list(invalid_shapes)} is/are not " + f"recognised. Must be one of {const.VALID_EVALUATOR_SHAPES}.") + + # If there are any quadrature rule(s), what are the names of the + # corresponding algorithm arguments? Can't use set() here because + # we need to preserve the ordering specified in the metadata. + qr_shapes = [shape for shape in self._eval_shapes if + shape in const.VALID_QUADRATURE_SHAPES] + + # The quadrature-related arguments to a kernel always come last so + # construct an enumerator with start value - + for idx, shape in enumerate(qr_shapes, -len(qr_shapes)): + + qr_arg = args[idx] + + # Use the InvokeSchedule symbol_table to create a unique symbol + # name for the whole Invoke. + if qr_arg.varname: + tag = "AlgArgs_" + qr_arg.text + qr_name = self.ancestor(InvokeSchedule).symbol_table.\ + find_or_create_integer_symbol(qr_arg.varname, tag=tag).name + else: + # If we don't have a name then we must be doing kernel-stub + # generation so create a suitable name. + # TODO #719 we don't yet have a symbol table to prevent + # clashes. + qr_name = "qr_"+shape.split("_")[-1] + + # Dynamo 0.3 api kernels require quadrature rule arguments to be + # passed in if one or more basis functions are used by the kernel + # and gh_shape == "gh_quadrature_***". + # if self._eval_shape == "gh_quadrature_xyz": + # self._qr_args = ["np_xyz", "weights_xyz"] + if shape == "gh_quadrature_xyoz": + qr_args = ["np_xy", "np_z", "weights_xy", "weights_z"] + # elif self._eval_shape == "gh_quadrature_xoyoz": + # qr_args = ["np_x", "np_y", "np_z", + # "weights_x", "weights_y", "weights_z"] + elif shape == "gh_quadrature_face": + qr_args = ["nfaces", "np_xyz", "weights_xyz"] + elif shape == "gh_quadrature_edge": + qr_args = ["nedges", "np_xyz", "weights_xyz"] + else: + raise InternalError(f"Unsupported quadrature shape " + f"('{shape}') found in LFRicKern._setup") + + # Append the name of the qr argument to the names of the qr-related + # variables. + qr_args = [arg + "_" + qr_name for arg in qr_args] + + self._qr_rules[shape] = self.QRRule(qr_arg.text, qr_name, qr_args) + + if "gh_evaluator" in self._eval_shapes: + # Kernel has an evaluator. If gh_evaluator_targets is present + # then that specifies the function spaces for which the evaluator + # is required. Otherwise, the FS of the updated argument(s) tells + # us upon which nodal points the evaluator will be required + for fs_name in ktype.eval_targets: + arg, fspace = self.arguments.get_arg_on_space_name(fs_name) + # Set up our dict of evaluator targets, one entry per + # target FS. + if fspace.mangled_name not in self._eval_targets: + self._eval_targets[fspace.mangled_name] = (fspace, arg) + + # Properties of the reference element required by this kernel + self._reference_element = ktype.reference_element + + # Properties of the mesh required by this kernel + self._mesh_properties = ktype.mesh + + @property + def qr_rules(self): + ''' + :return: details of each of the quadrature rules required by this \ + kernel. + :rtype: OrderedDict containing \ + :py:class:`psyclone.domain.lfric.LFRicKern.QRRule` indexed by \ + quadrature shape. + ''' + return self._qr_rules + + @property + def cma_operation(self): + ''' Returns the type of CMA operation performed by this kernel + (one of 'assembly', 'apply' or 'matrix-matrix') or None if the + the kernel does not involve CMA operators ''' + return self._cma_operation + + @property + def is_intergrid(self): + ''' + Getter for whether or not this is an inter-grid kernel call + :return: True if it is an inter-grid kernel, False otherwise + :rtype: bool + ''' + return self._is_intergrid + + @property + def colourmap(self): + ''' + Getter for the name of the colourmap associated with this kernel call. + + :returns: name of the colourmap (Fortran array). + :rtype: str + + :raises InternalError: if this kernel is not coloured or the \ + dictionary of inter-grid kernels and \ + colourmaps has not been constructed. + + ''' + if not self.is_coloured(): + raise InternalError(f"Kernel '{self.name}' is not inside a " + f"coloured loop.") + if self._is_intergrid: + invoke = self.ancestor(InvokeSchedule).invoke + if id(self) not in invoke.meshes.intergrid_kernels: + raise InternalError( + f"Colourmap information for kernel '{self.name}' has " + f"not yet been initialised") + cmap = invoke.meshes.intergrid_kernels[id(self)].\ + colourmap_symbol.name + else: + cmap = self.scope.symbol_table.lookup_with_tag("cmap").name + + return cmap + + @property + def last_cell_all_colours_symbol(self): + ''' + Getter for the symbol of the array holding the index of the last + cell of each colour. + + :returns: name of the array. + :rtype: str + + :raises InternalError: if this kernel is not coloured or the \ + dictionary of inter-grid kernels and \ + colourmaps has not been constructed. + ''' + if not self.is_coloured(): + raise InternalError(f"Kernel '{self.name}' is not inside a " + f"coloured loop.") + if self._is_intergrid: + invoke = self.ancestor(InvokeSchedule).invoke + if id(self) not in invoke.meshes.intergrid_kernels: + raise InternalError( + f"Colourmap information for kernel '{self.name}' has " + f"not yet been initialised") + return invoke.meshes.intergrid_kernels[id(self)].\ + last_cell_var_symbol + + const = LFRicConstants() + + if (self.ancestor(Loop).upper_bound_name in + const.HALO_ACCESS_LOOP_BOUNDS): + return self.scope.symbol_table.find_or_create_array( + "last_halo_cell_all_colours", 2, + ScalarType.Intrinsic.INTEGER, + tag="last_halo_cell_all_colours") + + return self.scope.symbol_table.find_or_create_array( + "last_edge_cell_all_colours", 1, + ScalarType.Intrinsic.INTEGER, + tag="last_edge_cell_all_colours") + + @property + def ncolours_var(self): + ''' + Getter for the name of the variable holding the number of colours + associated with this kernel call. + + :return: name of the variable holding the number of colours + :rtype: Union[str, NoneType] + + :raises InternalError: if this kernel is not coloured or the \ + colour-map information has not been initialised. + ''' + if not self.is_coloured(): + raise InternalError(f"Kernel '{self.name}' is not inside a " + f"coloured loop.") + if self._is_intergrid: + invoke = self.ancestor(InvokeSchedule).invoke + if id(self) not in invoke.meshes.intergrid_kernels: + raise InternalError( + f"Colourmap information for kernel '{self.name}' has " + f"not yet been initialised") + ncols_sym = \ + invoke.meshes.intergrid_kernels[id(self)].ncolours_var_symbol + if not ncols_sym: + return None + return ncols_sym.name + + return self.scope.symbol_table.lookup_with_tag("ncolour").name + + @property + def fs_descriptors(self): + ''' Returns a list of function space descriptor objects of + type FSDescriptor which contain information about the function + spaces. ''' + return self._fs_descriptors + + @property + def qr_required(self): + ''' + :return: True if this kernel requires quadrature, else returns False. + :rtype: bool + + ''' + return self._basis_required and self.qr_rules + + @property + def eval_shapes(self): + ''' + :return: the value(s) of GH_SHAPE for this kernel or an empty list \ + if none are specified. + :rtype: list + + ''' + return self._eval_shapes + + @property + def eval_targets(self): + ''' + :return: the function spaces upon which basis/diff-basis functions \ + are to be evaluated for this kernel. + :rtype: dict of (:py:class:`psyclone.domain.lfric.FunctionSpace`, \ + :py:class`psyclone.dynamo0p3.DynKernelArgument`), indexed by \ + the names of the target function spaces. + ''' + return self._eval_targets + + @property + def reference_element(self): + ''' + :returns: the reference-element properties required by this kernel. + :rtype: :py:class:`psyclone.dynamo0p3.RefElementMetaData` + ''' + return self._reference_element + + @property + def mesh(self): + ''' + :returns: the mesh properties required by this kernel. + :rtype: :py:class`psyclone.dynamo0p3.MeshPropertiesMetaData` + ''' + return self._mesh_properties + + @property + def all_updates_are_writes(self): + ''' + :returns: True if all of the arguments updated by this kernel have \ + 'GH_WRITE' access, False otherwise. + :rtype: bool + + ''' + if self.is_intergrid: + # This is not a special kernel + return False + accesses = set(arg.access for arg in self.args) + all_writes = AccessType.all_write_accesses() + all_writes.remove(AccessType.WRITE) + return (not accesses.intersection(set(all_writes))) + + def local_vars(self): + ''' Returns the names used by the Kernel that vary from one + invocation to the next and therefore require privatisation + when parallelised. ''' + return [] + + @property + def base_name(self): + ''' + :returns: a base name for this kernel. + :rtype: str + ''' + return self._base_name + + @property + def argument_kinds(self): + ''' + :returns: kinds (precisions) for all arguments in a kernel. + :rtype: set of str + + ''' + return self._argument_kinds + + @property + def gen_stub(self): + ''' + Create the fparser1 AST for a kernel stub. + + :returns: root of fparser1 AST for the stub routine. + :rtype: :py:class:`fparser.one.block_statements.Module` + + :raises GenerationError: if the supplied kernel stub does not operate \ + on a supported subset of the domain (currently only "cell_column"). + + ''' + # The operates-on/iterates-over values supported by the stub generator. + const = LFRicConstants() + supported_operates_on = const.USER_KERNEL_ITERATION_SPACES[:] + # TODO #925 Add support for 'domain' kernels + supported_operates_on.remove("domain") + + # Check operates-on (iteration space) before generating code + if self.iterates_over not in supported_operates_on: + raise GenerationError( + f"The LFRic API kernel-stub generator supports kernels that " + f"operate on one of {supported_operates_on} but found " + f"'{self.iterates_over}' in kernel '{self.name}'.") + + # Create an empty PSy layer module + psy_module = ModuleGen(self._base_name+"_mod") + + # Create the subroutine + sub_stub = SubroutineGen(psy_module, name=self._base_name+"_code", + implicitnone=True) + + # Add all the declarations + # Import here to avoid circular dependency + # pylint: disable=import-outside-toplevel + from psyclone.dynamo0p3 import (DynCellIterators, DynDofmaps, + DynFunctionSpaces, DynCMAOperators, + LFRicScalarArgs, DynBoundaryConditions, + DynLMAOperators, LFRicMeshProperties, + DynBasisFunctions, LFRicFields, + DynReferenceElement, DynStencils) + for entities in [DynCellIterators, DynDofmaps, DynFunctionSpaces, + DynCMAOperators, LFRicScalarArgs, LFRicFields, + DynLMAOperators, DynStencils, DynBasisFunctions, + DynBoundaryConditions, DynReferenceElement, + LFRicMeshProperties]: + entities(self).declarations(sub_stub) + + # Add wildcard "use" statement for all supported argument + # kinds (precisions) + sub_stub.add( + UseGen(sub_stub, + name=const.UTILITIES_MOD_MAP["constants"]["module"])) + + # Create the arglist + create_arg_list = KernStubArgList(self) + create_arg_list.generate() + + # Add the arglist + sub_stub.args = create_arg_list.arglist + + # Add the subroutine to the parent module + psy_module.add(sub_stub) + return psy_module.root + + def gen_code(self, parent): + ''' + Generates LFRic (Dynamo 0.3) specific PSy layer code for a call + to this user-supplied LFRic kernel. + + :param parent: an f2pygen object that will be the parent of \ + f2pygen objects created in this method. + :type parent: :py:class:`psyclone.f2pygen.BaseGen` + + :raises GenerationError: if this kernel does not have a supported \ + operates-on (currently only "cell_column"). + :raises GenerationError: if the loop goes beyond the level 1 \ + halo and an operator is accessed. + :raises GenerationError: if a kernel in the loop has an inc access \ + and the loop is not coloured but is within an OpenMP \ + parallel region. + + ''' + # Check operates-on (iteration space) before generating code + const = LFRicConstants() + if self.iterates_over not in const.USER_KERNEL_ITERATION_SPACES: + raise GenerationError( + f"The LFRic API supports calls to user-supplied kernels that " + f"operate on one of {const.USER_KERNEL_ITERATION_SPACES}, but " + f"kernel '{self.name}' operates on '{self.iterates_over}'.") + + # Get configuration for valid argument kinds + api_config = Config.get().api_conf("dynamo0.3") + + parent.add(DeclGen(parent, datatype="integer", + kind=api_config.default_kind["integer"], + entity_decls=["cell"])) + # Import here to avoid circular dependency + # pylint: disable=import-outside-toplevel + from psyclone.dynamo0p3 import DynLoop + parent_loop = self.ancestor(DynLoop) + + # Check whether this kernel reads from an operator + op_args = parent_loop.args_filter( + arg_types=const.VALID_OPERATOR_NAMES, + arg_accesses=[AccessType.READ, AccessType.READWRITE]) + if op_args: + # It does. We must check that our parent loop does not + # go beyond the L1 halo. + if parent_loop.upper_bound_name == "cell_halo" and \ + parent_loop.upper_bound_halo_depth > 1: + raise GenerationError( + f"Kernel '{self._name}' reads from an operator and " + f"therefore cannot be used for cells beyond the level 1 " + f"halo. However the containing loop goes out to level " + f"{parent_loop.upper_bound_halo_depth}") + + if not self.is_coloured(): + # This kernel call has not been coloured + # - is it OpenMP parallel, i.e. are we a child of + # an OpenMP directive? + if self.is_openmp_parallel(): + try: + # It is OpenMP parallel - does it have an argument + # with INC access? + arg = self.incremented_arg() + except FieldNotFoundError: + arg = None + if arg: + raise GenerationError(f"Kernel {self._name} has an " + f"argument with INC access and " + f"therefore must be coloured in " + f"order to be parallelised with " + f"OpenMP") + + parent.add(CommentGen(parent, "")) + + super(LFRicKern, self).gen_code(parent) + + def get_kernel_schedule(self): + '''Returns a PSyIR Schedule representing the kernel code. The base + class creates the PSyIR schedule on first invocation which is + then checked for consistency with the kernel metadata + here. The Schedule is just generated on first invocation, this + allows us to retain transformations that may subsequently be + applied to the Schedule. + + Once issue #935 is implemented, this routine will return the + PSyIR Schedule using LFRic-specific PSyIR where possible. + + :returns: Schedule representing the kernel code. + :rtype: :py:class:`psyclone.psyGen.KernelSchedule` + + :raises GenerationError: if no subroutine matching this kernel can \ + be found in the parse tree of the associated source code. + ''' + if self._kern_schedule: + return self._kern_schedule + + # Get the PSyIR Kernel Schedule(s) + routines = Fparser2Reader().get_routine_schedules(self.name, self.ast) + + if len(routines) == 1: + sched = routines[0] + # TODO #928: We don't validate the arguments yet because the + # validation has many false negatives. + # self.validate_kernel_code_args(sched.symbol_table) + else: + # The kernel name corresponds to an interface block. Find which + # of the routines matches the precision of the arguments. + for routine in routines: + try: + # The validity check for the kernel arguments will raise + # an exception if the precisions don't match. + self.validate_kernel_code_args(routine.symbol_table) + sched = routine + break + except GenerationError: + pass + else: + raise GenerationError( + f"Failed to find a kernel implementation with an interface" + f" that matches the invoke of '{self.name}'. (Tried " + f"routines {[item.name for item in routines]}.)") + + # TODO #935 - replace the PSyIR argument data symbols with LFRic data + # symbols. For the moment we just return the unmodified PSyIR schedule + # but this should use RaisePSyIR2LFRicKernTrans once KernelInterface + # is fully functional (#928). + ksched = KernelSchedule(sched.name, + symbol_table=sched.symbol_table.detach()) + for child in sched.pop_all_children(): + ksched.addchild(child) + sched.replace_with(ksched) + + self._kern_schedule = ksched + + return self._kern_schedule + + def validate_kernel_code_args(self, table): + '''Check that the arguments in the kernel code match the expected + arguments as defined by the kernel metadata and the LFRic + API. + + :param table: the symbol table to validate against the metadata. + :type table: :py:class:`psyclone.psyir.symbols.SymbolTable` + + :raises GenerationError: if the number of arguments indicated by the \ + kernel metadata doesn't match the actual number of arguments in \ + the symbol table. + + ''' + # Get the kernel subroutine arguments + kern_code_args = table.argument_list + + # Get the kernel code interface according to the kernel + # metadata and LFRic API + interface_info = KernelInterface(self) + interface_info.generate() + interface_args = interface_info.arglist + + # 1: Check the the number of arguments match + actual_n_args = len(kern_code_args) + expected_n_args = len(interface_args) + if actual_n_args != expected_n_args: + raise GenerationError( + f"In kernel '{self.name}' the number of arguments indicated by" + f" the kernel metadata is {expected_n_args} but the actual " + f"number of kernel arguments found is {actual_n_args}.") + + # 2: Check that the properties of each argument match. + for idx, kern_code_arg in enumerate(kern_code_args): + interface_arg = interface_args[idx] + try: + alg_idx = interface_info.metadata_index_from_actual_index(idx) + alg_arg = self.arguments.args[alg_idx] + except KeyError: + # There's no algorithm argument directly associated with this + # kernel argument. (We only care about the data associated + # with scalar, field and operator arguments.) + alg_arg = None + self._validate_kernel_code_arg(kern_code_arg, interface_arg, + alg_arg) + + def _validate_kernel_code_arg(self, kern_code_arg, interface_arg, + alg_arg=None): + '''Internal method to check that the supplied argument descriptions + match and raise appropriate exceptions if not. + + :param kern_code_arg: kernel code argument. + :type kern_code_arg: :py:class:`psyclone.psyir.symbols.DataSymbol` + :param interface_arg: expected argument. + :type interface_arg: :py:class:`psyclone.psyir.symbols.DataSymbol` + :param alg_arg: the associated argument in the Algorithm layer. Note \ + that only kernel arguments holding the data associated with \ + scalar, field and operator arguments directly correspond to \ + arguments that appear in the Algorithm layer. + :type alg_arg: \ + Optional[:py:class`psyclone.dynamo0p3.DynKernelArgument`] + + :raises GenerationError: if the contents of the arguments do \ + not match. + :raises InternalError: if an unexpected datatype is found. + + ''' + # 1: intrinsic datatype + actual_datatype = kern_code_arg.datatype.intrinsic + expected_datatype = interface_arg.datatype.intrinsic + if actual_datatype != expected_datatype: + raise GenerationError( + f"Kernel argument '{kern_code_arg.name}' has datatype " + f"'{actual_datatype}' in kernel '{self.name}' but the LFRic " + f"API expects '{expected_datatype}'.") + # 2: precision. An LFRic kernel is only permitted to have a precision + # specified by a recognised type parameter or a no. of bytes. + actual_precision = kern_code_arg.datatype.precision + if isinstance(actual_precision, DataSymbol): + # Convert precision into number of bytes to support + # mixed-precision kernels. + # TODO #1941: it would be better if the LFRic constants_mod.f90 + # was the single source of truth for precision values. + actual_precision = LFRicConstants.PRECISION_MAP[ + actual_precision.name] + elif not isinstance(actual_precision, int): + raise GenerationError( + f"An argument to an LFRic kernel must have a precision defined" + f" by either a recognised LFRic type parameter (one of " + f"{sorted(LFRicConstants.PRECISION_MAP.keys())}) or an integer" + f" number of bytes but argument '{kern_code_arg.name}' to " + f"kernel '{self.name}' has precision {actual_precision}.") + + if alg_arg: + # We have information on the corresponding argument in the + # Algorithm layer so we can check that the precision matches. + # This is used to identify the correct kernel subroutine for a + # mixed-precision kernel. + alg_precision = LFRicConstants.PRECISION_MAP[alg_arg.precision] + if alg_precision != actual_precision: + raise GenerationError( + f"Precision ({alg_precision} bytes) of algorithm-layer " + f"argument '{alg_arg.name}' does not match that " + f"({actual_precision} bytes) of the corresponding kernel " + f"subroutine argument '{kern_code_arg.name}' for kernel " + f"'{self.name}'.") + + # 3: intent + actual_intent = kern_code_arg.interface.access + expected_intent = interface_arg.interface.access + if actual_intent.name != expected_intent.name: + raise GenerationError( + f"Kernel argument '{kern_code_arg.name}' has intent " + f"'{actual_intent.name}' in kernel '{self.name}' but the " + f"LFRic API expects intent '{expected_intent.name}'.") + # 4: scalar or array + if interface_arg.is_scalar: + if not kern_code_arg.is_scalar: + raise GenerationError( + f"Argument '{kern_code_arg.name}' to kernel '{self.name}' " + f"should be a scalar according to the LFRic API, but it " + f"is not.") + elif interface_arg.is_array: + if not kern_code_arg.is_array: + raise GenerationError( + f"Argument '{kern_code_arg.name}' to kernel '{self.name}' " + f"should be an array according to the LFRic API, but it " + f"is not.") + # 4.1: array dimensions + if len(interface_arg.shape) != len(kern_code_arg.shape): + raise GenerationError( + f"Argument '{kern_code_arg.name}' to kernel '{self.name}' " + f"should be an array with {len(interface_arg.shape)} " + f"dimension(s) according to the LFRic API, but " + f"found {len(kern_code_arg.shape)}.") + for dim_idx, kern_code_arg_dim in enumerate(kern_code_arg.shape): + if not isinstance(kern_code_arg_dim, ArrayType.ArrayBounds): + continue + if (not isinstance(kern_code_arg_dim.lower, Literal) or + kern_code_arg_dim.lower.value != "1"): + raise GenerationError( + f"All array arguments to LFRic kernels must have lower" + f" bounds of 1 for all dimensions. However, array " + f"'{kern_code_arg.name}' has a lower bound of " + f"'{kern_code_arg_dim.lower}' for dimension {dim_idx}") + kern_code_arg_upper_dim = kern_code_arg_dim.upper + interface_arg_upper_dim = interface_arg.shape[dim_idx].upper + if (isinstance(kern_code_arg_upper_dim, Reference) and + isinstance(interface_arg_upper_dim, Reference) and + isinstance(kern_code_arg_upper_dim.symbol, + DataSymbol) and + isinstance(interface_arg_upper_dim.symbol, + DataSymbol)): + # Only check when there is a symbol. Unspecified + # dimensions, dimensions with scalar values, + # offsets, or dimensions that include arithmetic + # are skipped. + try: + self._validate_kernel_code_arg( + kern_code_arg_upper_dim.symbol, + interface_arg_upper_dim.symbol) + except GenerationError as info: + raise GenerationError( + f"For dimension {dim_idx+1} in array argument " + f"'{kern_code_arg.name}' to kernel '{self.name}' " + f"the following error was found: " + f"{info.args[0]}") from info + else: + raise InternalError( + f"unexpected argument type found for '{kern_code_arg.name}' in" + f" kernel '{self.name}'. Expecting a scalar or an array.") + +# ---------- Documentation utils -------------------------------------------- # +# The list of module members that we wish AutoAPI to generate +# documentation for. (See https://psyclone-ref.readthedocs.io) +__all__ = ['LFRicKern'] diff --git a/src/psyclone/dynamo0p3.py b/src/psyclone/dynamo0p3.py index 2d7d38c55d..e1f6f09be8 100644 --- a/src/psyclone/dynamo0p3.py +++ b/src/psyclone/dynamo0p3.py @@ -58,32 +58,28 @@ LFRicBuiltIn, BUILTIN_MAP) from psyclone.domain.common.psylayer import PSyLoop from psyclone.domain.lfric import (FunctionSpace, KernCallAccArgList, - KernCallArgList, KernStubArgList, - LFRicArgDescriptor, KernelInterface, - LFRicConstants, LFRicSymbolTable) + KernCallArgList, LFRicArgDescriptor, + LFRicConstants, LFRicSymbolTable, + LFRicKern) from psyclone.errors import GenerationError, InternalError, FieldNotFoundError from psyclone.f2pygen import (AllocateGen, AssignGen, CallGen, CommentGen, DeallocateGen, DeclGen, DoGen, IfThenGen, ModuleGen, SubroutineGen, TypeDeclGen, UseGen, PSyIRGen) -from psyclone.parse.algorithm import Arg, KernelCall from psyclone.parse.kernel import KernelType, getkerneldescriptors from psyclone.parse.utils import ParseError from psyclone.psyGen import (PSy, Invokes, Invoke, InvokeSchedule, Arguments, KernelArgument, HaloExchange, - GlobalSum, FORTRAN_INTENT_NAMES, DataAccess, - CodedKern) + GlobalSum, FORTRAN_INTENT_NAMES, DataAccess) from psyclone.psyir.frontend.fortran import FortranReader -from psyclone.psyir.frontend.fparser2 import Fparser2Reader from psyclone.psyir.nodes import (Loop, Literal, Schedule, Reference, ArrayReference, ACCEnterDataDirective, ACCRegionDirective, OMPRegionDirective, - Routine, ScopingNode, StructureReference, - KernelSchedule) + Routine, ScopingNode, StructureReference) from psyclone.psyir.symbols import (INTEGER_TYPE, DataSymbol, ScalarType, DeferredType, DataTypeSymbol, ContainerSymbol, ImportInterface, - ArrayType, SymbolError) + SymbolError) # pylint: disable=too-many-lines # --------------------------------------------------------------------------- # @@ -1146,7 +1142,7 @@ class DynCollection(): :param node: the Kernel or Invoke for which to manage variable \ declarations and initialisation. :type node: :py:class:`psyclone.dynamo0p3.DynInvoke` or \ - :py:class:`psyclone.dynamo0p3.LFRicKern` + :py:class:`psyclone.domain.lfric.LFRicKern` :raises InternalError: if the supplied node is not a DynInvoke or a \ LFRicKern. @@ -1242,7 +1238,7 @@ class DynStencils(DynCollection): :param node: the Invoke or Kernel stub for which to provide stencil info. :type node: :py:class:`psyclone.dynamo0p3.DynInvoke` or \ - :py:class:`psyclone.dynamo0p3.LFRicKern` + :py:class:`psyclone.domain.lfric.LFRicKern` :raises GenerationError: if a literal has been supplied for a stencil \ direction. @@ -1836,7 +1832,7 @@ class LFRicMeshProperties(DynCollection): kernels. :param node: kernel or invoke for which to manage mesh properties. - :type node: :py:class:`psyclone.dynamo0p3.LFRicKern` or \ + :type node: :py:class:`psyclone.domain.lfric.LFRicKern` or \ :py:class:`psyclone.dynamo0p3.DynInvoke` ''' @@ -2160,7 +2156,7 @@ class DynReferenceElement(DynCollection): :param node: Kernel or Invoke for which to manage Reference-Element \ properties. - :type node: :py:class:`psyclone.dynamo0p3.LFRicKern` or \ + :type node: :py:class:`psyclone.domain.lfric.LFRicKern` or \ :py:class:`psyclone.dynamo0p3.DynInvoke` :raises InternalError: if an unsupported reference-element property \ @@ -2518,7 +2514,7 @@ class DynDofmaps(DynCollection): indirection) required by an invoke. :param node: Kernel or Invoke for which to manage dofmaps. - :type node: :py:class:`psyclone.dynamo0p3.LFRicKern` or \ + :type node: :py:class:`psyclone.domain.lfric.LFRicKern` or \ :py:class:`psyclone.dynamo0p3.DynInvoke` ''' @@ -3305,7 +3301,7 @@ class DynCellIterators(DynCollection): :param kern_or_invoke: the Kernel or Invoke for which to manage cell \ iterators. - :type kern_or_invoke: :py:class:`psyclone.dynamo0p3.LFRicKern` or \ + :type kern_or_invoke: :py:class:`psyclone.domain.lfric.LFRicKern` or \ :py:class:`psyclone.dynamo0p3.DynInvoke` : raises GenerationError: if an Invoke has no field or operator arguments. @@ -3453,7 +3449,7 @@ class LFRicScalarArgs(DynCollection): :param node: the Invoke or Kernel stub for which to manage the scalar \ arguments. - :type node: :py:class:`psyclone.dynamo0p3.LFRicKern` or \ + :type node: :py:class:`psyclone.domain.lfric.LFRicKern` or \ :py:class:`psyclone.dynamo0p3.DynInvoke` ''' @@ -3748,7 +3744,7 @@ class DynCMAOperators(DynCollection): :param node: either an Invoke schedule or a single Kernel object. :type node: :py:class:`psyclone.dynamo0p3.DynSchedule` or \ - :py:class:`psyclone.dynamo0p3.LFRicKern` + :py:class:`psyclone.domain.lfric.LFRicKern` ''' # The scalar parameters that must be passed along with a CMA operator @@ -4562,7 +4558,7 @@ class DynBasisFunctions(DynCollection): for which to extract information on all required \ basis/diff-basis functions. :type node: :py:class:`psyclone.dynamo0p3.DynInvokeSchedule` or \ - :py:class:`psyclone.dynamo0p3.LFRicKern` + :py:class:`psyclone.domain.lfric.LFRicKern` :raises InternalError: if a call has an unrecognised evaluator shape. @@ -4735,7 +4731,7 @@ def _setup_basis_fns_for_call(self, call): functions required by the supplied Call. :param call: the kernel call for which basis functions are required. - :type call: :py:class:`psyclone.dynamo0p3.LFRicKern` + :type call: :py:class:`psyclone.domain.lfric.LFRicKern` :raises InternalError: if the supplied call is of incorrect type. :raises InternalError: if the supplied call has an unrecognised \ @@ -5466,7 +5462,7 @@ class DynBoundaryConditions(DynCollection): :param node: the Invoke or Kernel stub for which we are to handle \ any boundary conditions. :type node: :py:class:`psyclone.dynamo0p3.DynInvoke` or \ - :py:class:`psyclone.dynamo0p3.LFRicKern` + :py:class:`psyclone.domain.lfric.LFRicKern` :raises GenerationError: if a kernel named "enforce_bc_code" is found \ but does not have an argument on ANY_SPACE_1. @@ -7175,7 +7171,7 @@ def load(self, kern): construct Loop objects for a given kernel call. :param kern: Kernel object to use to populate state of Loop - :type kern: :py:class:`psyclone.dynamo0p3.LFRicKern` + :type kern: :py:class:`psyclone.domain.lfric.LFRicKern` :raises GenerationError: if the field updated by the kernel has an \ unexpected function space or if the kernel's 'operates-on' is \ @@ -7933,871 +7929,6 @@ def gen_mark_halos_clean_dirty(self, parent): parent.add(call) -class LFRicKern(CodedKern): - ''' Stores information about Dynamo Kernels as specified by the - Kernel metadata and associated algorithm call. Uses this - information to generate appropriate PSy layer code for the Kernel - instance or to generate a Kernel stub. - - ''' - # pylint: disable=too-many-instance-attributes - # An instance of this `namedtuple` is used to store information on each of - # the quadrature rules required by a kernel. - # - # alg_name: The actual argument text specifying the QR object in the - # Alg. layer. - # psy_name: The PSy-layer variable name for the QR object. - # kernel_args: List of kernel arguments associated with this QR rule. - QRRule = namedtuple("QRRule", - ["alg_name", "psy_name", "kernel_args"]) - - def __init__(self): - # The super-init is called from the _setup() method which in turn - # is called from load(). - # pylint: disable=super-init-not-called - if False: # pylint: disable=using-constant-test - self._arguments = DynKernelArguments(None, None) # for pyreverse - self._parent = None - self._base_name = "" - self._func_descriptors = None - self._fs_descriptors = None - # Whether this kernel requires quadrature - self._qr_required = False - # Whether this kernel requires basis functions - self._basis_required = False - # What shapes of evaluator/quadrature this kernel requires (if any) - self._eval_shapes = [] - # The function spaces on which to *evaluate* basis/diff-basis - # functions if an evaluator is required for this kernel. Is a dict with - # (mangled) FS names as keys and associated kernel argument as value. - self._eval_targets = OrderedDict() - # Will hold a dict of QRRule namedtuple objects, one for each QR - # rule required by a kernel, indexed by shape. Needs to be ordered - # because we must preserve the ordering specified in the metadata. - self._qr_rules = OrderedDict() - self._cma_operation = None - self._is_intergrid = False # Whether this is an inter-grid kernel - # The reference-element properties required by this kernel - self._reference_element = None - # The mesh properties required by this kernel - self._mesh_properties = None - # Initialise kinds (precisions) of all kernel arguments (start - # with 'real' and 'integer' kinds) - api_config = Config.get().api_conf("dynamo0.3") - self._argument_kinds = {api_config.default_kind["real"], - api_config.default_kind["integer"]} - - def reference_accesses(self, var_accesses): - '''Get all variable access information. All accesses are marked - according to the kernel metadata - - :param var_accesses: VariablesAccessInfo instance that stores the \ - information about variable accesses. - :type var_accesses: \ - :py:class:`psyclone.core.VariablesAccessInfo` - ''' - - # Use the KernelCallArgList class, which can also provide variable - # access information: - create_arg_list = KernCallArgList(self) - create_arg_list.generate(var_accesses) - - super().reference_accesses(var_accesses) - # Set the current location index to the next location, since after - # this kernel a new statement starts. - var_accesses.next_location() - - def load(self, call, parent=None): - ''' - Sets up kernel information with the call object which is - created by the parser. This object includes information about - the invoke call and the associated kernel. - - :param call: The KernelCall object from which to extract information - about this kernel - :type call: :py:class:`psyclone.parse.algorithm.KernelCall` - :param parent: The parent node of the kernel call in the AST - we are constructing. This will be a loop. - :type parent: :py:class:`psyclone.dynamo0p3.DynLoop` - ''' - self._setup_basis(call.ktype) - self._setup(call.ktype, call.module_name, call.args, parent) - - def load_meta(self, ktype): - ''' - Sets up kernel information with the kernel type object - which is created by the parser. The object includes the - metadata describing the kernel code. - - :param ktype: the kernel meta-data object produced by the parser - :type ktype: :py:class:`psyclone.dynamo0p3.DynKernMetadata` - - :raises InternalError: for an invalid data type of a scalar argument. - :raises GenerationError: if an invalid argument type is found \ - in the kernel. - - ''' - # pylint: disable=too-many-branches - # Create a name for each argument - args = [] - const = LFRicConstants() - for idx, descriptor in enumerate(ktype.arg_descriptors): - pre = None - if descriptor.argument_type.lower() == "gh_operator": - pre = "op_" - elif descriptor.argument_type.lower() == "gh_columnwise_operator": - pre = "cma_op_" - elif descriptor.argument_type.lower() == "gh_field": - pre = "field_" - elif (descriptor.argument_type.lower() in - const.VALID_SCALAR_NAMES): - if descriptor.data_type.lower() == "gh_real": - pre = "rscalar_" - elif descriptor.data_type.lower() == "gh_integer": - pre = "iscalar_" - elif descriptor.data_type.lower() == "gh_logical": - pre = "lscalar_" - else: - raise InternalError( - f"Expected one of {const.VALID_SCALAR_DATA_TYPES} " - f"data types for a scalar argument but found " - f"'{descriptor.data_type}'.") - else: - raise GenerationError( - f"LFRicKern.load_meta() expected one of " - f"{const.VALID_ARG_TYPE_NAMES} but found " - f"'{descriptor.argument_type}'") - args.append(Arg("variable", pre+str(idx+1))) - - if descriptor.stencil: - if not descriptor.stencil["extent"]: - # Stencil size (in cells) is passed in - args.append(Arg("variable", - pre+str(idx+1)+"_stencil_size")) - if descriptor.stencil["type"] == "xory1d": - # Direction is passed in - args.append(Arg("variable", pre+str(idx+1)+"_direction")) - - # Initialise basis/diff basis so we can test whether quadrature - # or an evaluator is required - self._setup_basis(ktype) - if self._basis_required: - for shape in self._eval_shapes: - if shape in const.VALID_QUADRATURE_SHAPES: - # Add a quadrature argument for each required quadrature - # rule. - args.append(Arg("variable", "qr_"+shape)) - self._setup(ktype, "dummy_name", args, None, check=False) - - def _setup_basis(self, kmetadata): - ''' - Initialisation of the basis/diff basis information. This may be - needed before general setup so is computed in a separate method. - - :param kmetadata: The kernel meta-data object produced by the parser. - :type kmetadata: :py:class:`psyclone.dynamo0p3.DynKernMetadata` - ''' - for descriptor in kmetadata.func_descriptors: - if len(descriptor.operator_names) > 0: - self._basis_required = True - self._eval_shapes = kmetadata.eval_shapes[:] - break - - def _setup(self, ktype, module_name, args, parent, check=True): - # pylint: disable=too-many-arguments - '''Internal setup of kernel information. - - :param ktype: object holding information on the parsed metadata for \ - this kernel. - :type ktype: :py:class:`psyclone.dynamo0p3.DynKernMetadata` - :param str module_name: the name of the Fortran module that contains \ - the source of this Kernel. - :param args: list of Arg objects produced by the parser for the \ - arguments of this kernel call. - :type args: list of :py:class:`psyclone.parse.algorithm.Arg` objects - :param parent: the parent of this kernel call in the generated \ - AST (will be a loop object). - :type parent: :py:class:`psyclone.dynamo0p3.DynLoop` - :param bool check: whether to check for consistency between the \ - kernel metadata and the algorithm layer. Defaults to True. - - ''' - # pylint: disable=too-many-branches, too-many-locals - CodedKern.__init__(self, DynKernelArguments, - KernelCall(module_name, ktype, args), - parent, check) - # Remove "_code" from the name if it exists to determine the - # base name which (if dynamo0.3 naming conventions are - # followed) is used as the root for the module and subroutine - # names. - if self.name.lower().endswith("_code"): - self._base_name = self.name[:-5] - else: - # TODO: #11 add a warning here when logging is added - self._base_name = self.name - self._func_descriptors = ktype.func_descriptors - # Keep a record of the type of CMA kernel identified when - # parsing the kernel meta-data - self._cma_operation = ktype.cma_operation - self._fs_descriptors = FSDescriptors(ktype.func_descriptors) - - # Record whether or not the kernel meta-data specifies that this - # is an inter-grid kernel - self._is_intergrid = ktype.is_intergrid - - const = LFRicConstants() - # Check that all specified evaluator shapes are recognised - invalid_shapes = set(self._eval_shapes) \ - - set(const.VALID_EVALUATOR_SHAPES) - if invalid_shapes: - raise InternalError( - f"Evaluator shape(s) {list(invalid_shapes)} is/are not " - f"recognised. Must be one of {const.VALID_EVALUATOR_SHAPES}.") - - # If there are any quadrature rule(s), what are the names of the - # corresponding algorithm arguments? Can't use set() here because - # we need to preserve the ordering specified in the metadata. - qr_shapes = [shape for shape in self._eval_shapes if - shape in const.VALID_QUADRATURE_SHAPES] - - # The quadrature-related arguments to a kernel always come last so - # construct an enumerator with start value - - for idx, shape in enumerate(qr_shapes, -len(qr_shapes)): - - qr_arg = args[idx] - - # Use the InvokeSchedule symbol_table to create a unique symbol - # name for the whole Invoke. - if qr_arg.varname: - tag = "AlgArgs_" + qr_arg.text - qr_name = self.ancestor(InvokeSchedule).symbol_table.\ - find_or_create_integer_symbol(qr_arg.varname, tag=tag).name - else: - # If we don't have a name then we must be doing kernel-stub - # generation so create a suitable name. - # TODO #719 we don't yet have a symbol table to prevent - # clashes. - qr_name = "qr_"+shape.split("_")[-1] - - # Dynamo 0.3 api kernels require quadrature rule arguments to be - # passed in if one or more basis functions are used by the kernel - # and gh_shape == "gh_quadrature_***". - # if self._eval_shape == "gh_quadrature_xyz": - # self._qr_args = ["np_xyz", "weights_xyz"] - if shape == "gh_quadrature_xyoz": - qr_args = ["np_xy", "np_z", "weights_xy", "weights_z"] - # elif self._eval_shape == "gh_quadrature_xoyoz": - # qr_args = ["np_x", "np_y", "np_z", - # "weights_x", "weights_y", "weights_z"] - elif shape == "gh_quadrature_face": - qr_args = ["nfaces", "np_xyz", "weights_xyz"] - elif shape == "gh_quadrature_edge": - qr_args = ["nedges", "np_xyz", "weights_xyz"] - else: - raise InternalError(f"Unsupported quadrature shape " - f"('{shape}') found in LFRicKern._setup") - - # Append the name of the qr argument to the names of the qr-related - # variables. - qr_args = [arg + "_" + qr_name for arg in qr_args] - - self._qr_rules[shape] = self.QRRule(qr_arg.text, qr_name, qr_args) - - if "gh_evaluator" in self._eval_shapes: - # Kernel has an evaluator. If gh_evaluator_targets is present - # then that specifies the function spaces for which the evaluator - # is required. Otherwise, the FS of the updated argument(s) tells - # us upon which nodal points the evaluator will be required - for fs_name in ktype.eval_targets: - arg, fspace = self.arguments.get_arg_on_space_name(fs_name) - # Set up our dict of evaluator targets, one entry per - # target FS. - if fspace.mangled_name not in self._eval_targets: - self._eval_targets[fspace.mangled_name] = (fspace, arg) - - # Properties of the reference element required by this kernel - self._reference_element = ktype.reference_element - - # Properties of the mesh required by this kernel - self._mesh_properties = ktype.mesh - - @property - def qr_rules(self): - ''' - :return: details of each of the quadrature rules required by this \ - kernel. - :rtype: OrderedDict containing \ - :py:class:`psyclone.dynamo0p3.LFRicKern.QRRule` indexed by \ - quadrature shape. - ''' - return self._qr_rules - - @property - def cma_operation(self): - ''' Returns the type of CMA operation performed by this kernel - (one of 'assembly', 'apply' or 'matrix-matrix') or None if the - the kernel does not involve CMA operators ''' - return self._cma_operation - - @property - def is_intergrid(self): - ''' - Getter for whether or not this is an inter-grid kernel call - :return: True if it is an inter-grid kernel, False otherwise - :rtype: bool - ''' - return self._is_intergrid - - @property - def colourmap(self): - ''' - Getter for the name of the colourmap associated with this kernel call. - - :returns: name of the colourmap (Fortran array). - :rtype: str - - :raises InternalError: if this kernel is not coloured or the \ - dictionary of inter-grid kernels and \ - colourmaps has not been constructed. - - ''' - if not self.is_coloured(): - raise InternalError(f"Kernel '{self.name}' is not inside a " - f"coloured loop.") - if self._is_intergrid: - invoke = self.ancestor(InvokeSchedule).invoke - if id(self) not in invoke.meshes.intergrid_kernels: - raise InternalError( - f"Colourmap information for kernel '{self.name}' has " - f"not yet been initialised") - cmap = invoke.meshes.intergrid_kernels[id(self)].\ - colourmap_symbol.name - else: - cmap = self.scope.symbol_table.lookup_with_tag("cmap").name - - return cmap - - @property - def last_cell_all_colours_symbol(self): - ''' - Getter for the symbol of the array holding the index of the last - cell of each colour. - - :returns: name of the array. - :rtype: str - - :raises InternalError: if this kernel is not coloured or the \ - dictionary of inter-grid kernels and \ - colourmaps has not been constructed. - ''' - if not self.is_coloured(): - raise InternalError(f"Kernel '{self.name}' is not inside a " - f"coloured loop.") - if self._is_intergrid: - invoke = self.ancestor(InvokeSchedule).invoke - if id(self) not in invoke.meshes.intergrid_kernels: - raise InternalError( - f"Colourmap information for kernel '{self.name}' has " - f"not yet been initialised") - return invoke.meshes.intergrid_kernels[id(self)].\ - last_cell_var_symbol - - const = LFRicConstants() - - if (self.ancestor(Loop).upper_bound_name in - const.HALO_ACCESS_LOOP_BOUNDS): - return self.scope.symbol_table.find_or_create_array( - "last_halo_cell_all_colours", 2, - ScalarType.Intrinsic.INTEGER, - tag="last_halo_cell_all_colours") - - return self.scope.symbol_table.find_or_create_array( - "last_edge_cell_all_colours", 1, - ScalarType.Intrinsic.INTEGER, - tag="last_edge_cell_all_colours") - - @property - def ncolours_var(self): - ''' - Getter for the name of the variable holding the number of colours - associated with this kernel call. - - :return: name of the variable holding the number of colours - :rtype: Union[str, NoneType] - - :raises InternalError: if this kernel is not coloured or the \ - colour-map information has not been initialised. - ''' - if not self.is_coloured(): - raise InternalError(f"Kernel '{self.name}' is not inside a " - f"coloured loop.") - if self._is_intergrid: - invoke = self.ancestor(InvokeSchedule).invoke - if id(self) not in invoke.meshes.intergrid_kernels: - raise InternalError( - f"Colourmap information for kernel '{self.name}' has " - f"not yet been initialised") - ncols_sym = \ - invoke.meshes.intergrid_kernels[id(self)].ncolours_var_symbol - if not ncols_sym: - return None - return ncols_sym.name - - return self.scope.symbol_table.lookup_with_tag("ncolour").name - - @property - def fs_descriptors(self): - ''' Returns a list of function space descriptor objects of - type FSDescriptor which contain information about the function - spaces. ''' - return self._fs_descriptors - - @property - def qr_required(self): - ''' - :return: True if this kernel requires quadrature, else returns False. - :rtype: bool - - ''' - return self._basis_required and self.qr_rules - - @property - def eval_shapes(self): - ''' - :return: the value(s) of GH_SHAPE for this kernel or an empty list \ - if none are specified. - :rtype: list - - ''' - return self._eval_shapes - - @property - def eval_targets(self): - ''' - :return: the function spaces upon which basis/diff-basis functions \ - are to be evaluated for this kernel. - :rtype: dict of (:py:class:`psyclone.domain.lfric.FunctionSpace`, \ - :py:class`psyclone.dynamo0p3.DynKernelArgument`), indexed by \ - the names of the target function spaces. - ''' - return self._eval_targets - - @property - def reference_element(self): - ''' - :returns: the reference-element properties required by this kernel. - :rtype: :py:class:`psyclone.dynamo0p3.RefElementMetaData` - ''' - return self._reference_element - - @property - def mesh(self): - ''' - :returns: the mesh properties required by this kernel. - :rtype: :py:class`psyclone.dynamo0p3.MeshPropertiesMetaData` - ''' - return self._mesh_properties - - @property - def all_updates_are_writes(self): - ''' - :returns: True if all of the arguments updated by this kernel have \ - 'GH_WRITE' access, False otherwise. - :rtype: bool - - ''' - if self.is_intergrid: - # This is not a special kernel - return False - accesses = set(arg.access for arg in self.args) - all_writes = AccessType.all_write_accesses() - all_writes.remove(AccessType.WRITE) - return (not accesses.intersection(set(all_writes))) - - def local_vars(self): - ''' Returns the names used by the Kernel that vary from one - invocation to the next and therefore require privatisation - when parallelised. ''' - return [] - - @property - def base_name(self): - ''' - :returns: a base name for this kernel. - :rtype: str - ''' - return self._base_name - - @property - def argument_kinds(self): - ''' - :returns: kinds (precisions) for all arguments in a kernel. - :rtype: set of str - - ''' - return self._argument_kinds - - @property - def gen_stub(self): - ''' - Create the fparser1 AST for a kernel stub. - - :returns: root of fparser1 AST for the stub routine. - :rtype: :py:class:`fparser.one.block_statements.Module` - - :raises GenerationError: if the supplied kernel stub does not operate \ - on a supported subset of the domain (currently only "cell_column"). - - ''' - # The operates-on/iterates-over values supported by the stub generator. - const = LFRicConstants() - supported_operates_on = const.USER_KERNEL_ITERATION_SPACES[:] - # TODO #925 Add support for 'domain' kernels - supported_operates_on.remove("domain") - - # Check operates-on (iteration space) before generating code - if self.iterates_over not in supported_operates_on: - raise GenerationError( - f"The LFRic API kernel-stub generator supports kernels that " - f"operate on one of {supported_operates_on} but found " - f"'{self.iterates_over}' in kernel '{self.name}'.") - - # Create an empty PSy layer module - psy_module = ModuleGen(self._base_name+"_mod") - - # Create the subroutine - sub_stub = SubroutineGen(psy_module, name=self._base_name+"_code", - implicitnone=True) - - # Add all the declarations - for entities in [DynCellIterators, DynDofmaps, DynFunctionSpaces, - DynCMAOperators, LFRicScalarArgs, LFRicFields, - DynLMAOperators, DynStencils, DynBasisFunctions, - DynBoundaryConditions, DynReferenceElement, - LFRicMeshProperties]: - entities(self).declarations(sub_stub) - - # Add wildcard "use" statement for all supported argument - # kinds (precisions) - sub_stub.add( - UseGen(sub_stub, - name=const.UTILITIES_MOD_MAP["constants"]["module"])) - - # Create the arglist - create_arg_list = KernStubArgList(self) - create_arg_list.generate() - - # Add the arglist - sub_stub.args = create_arg_list.arglist - - # Add the subroutine to the parent module - psy_module.add(sub_stub) - return psy_module.root - - def gen_code(self, parent): - ''' - Generates LFRic (Dynamo 0.3) specific PSy layer code for a call - to this user-supplied LFRic kernel. - - :param parent: an f2pygen object that will be the parent of \ - f2pygen objects created in this method. - :type parent: :py:class:`psyclone.f2pygen.BaseGen` - - :raises GenerationError: if this kernel does not have a supported \ - operates-on (currently only "cell_column"). - :raises GenerationError: if the loop goes beyond the level 1 \ - halo and an operator is accessed. - :raises GenerationError: if a kernel in the loop has an inc access \ - and the loop is not coloured but is within an OpenMP \ - parallel region. - - ''' - # Check operates-on (iteration space) before generating code - const = LFRicConstants() - if self.iterates_over not in const.USER_KERNEL_ITERATION_SPACES: - raise GenerationError( - f"The LFRic API supports calls to user-supplied kernels that " - f"operate on one of {const.USER_KERNEL_ITERATION_SPACES}, but " - f"kernel '{self.name}' operates on '{self.iterates_over}'.") - - # Get configuration for valid argument kinds - api_config = Config.get().api_conf("dynamo0.3") - - parent.add(DeclGen(parent, datatype="integer", - kind=api_config.default_kind["integer"], - entity_decls=["cell"])) - - parent_loop = self.ancestor(DynLoop) - - # Check whether this kernel reads from an operator - op_args = parent_loop.args_filter( - arg_types=const.VALID_OPERATOR_NAMES, - arg_accesses=[AccessType.READ, AccessType.READWRITE]) - if op_args: - # It does. We must check that our parent loop does not - # go beyond the L1 halo. - if parent_loop.upper_bound_name == "cell_halo" and \ - parent_loop.upper_bound_halo_depth > 1: - raise GenerationError( - f"Kernel '{self._name}' reads from an operator and " - f"therefore cannot be used for cells beyond the level 1 " - f"halo. However the containing loop goes out to level " - f"{parent_loop.upper_bound_halo_depth}") - - if not self.is_coloured(): - # This kernel call has not been coloured - # - is it OpenMP parallel, i.e. are we a child of - # an OpenMP directive? - if self.is_openmp_parallel(): - try: - # It is OpenMP parallel - does it have an argument - # with INC access? - arg = self.incremented_arg() - except FieldNotFoundError: - arg = None - if arg: - raise GenerationError(f"Kernel {self._name} has an " - f"argument with INC access and " - f"therefore must be coloured in " - f"order to be parallelised with " - f"OpenMP") - - parent.add(CommentGen(parent, "")) - - super(LFRicKern, self).gen_code(parent) - - def get_kernel_schedule(self): - '''Returns a PSyIR Schedule representing the kernel code. The base - class creates the PSyIR schedule on first invocation which is - then checked for consistency with the kernel metadata - here. The Schedule is just generated on first invocation, this - allows us to retain transformations that may subsequently be - applied to the Schedule. - - Once issue #935 is implemented, this routine will return the - PSyIR Schedule using LFRic-specific PSyIR where possible. - - :returns: Schedule representing the kernel code. - :rtype: :py:class:`psyclone.psyGen.KernelSchedule` - - :raises GenerationError: if no subroutine matching this kernel can \ - be found in the parse tree of the associated source code. - ''' - if self._kern_schedule: - return self._kern_schedule - - # Get the PSyIR Kernel Schedule(s) - routines = Fparser2Reader().get_routine_schedules(self.name, self.ast) - - if len(routines) == 1: - sched = routines[0] - # TODO #928: We don't validate the arguments yet because the - # validation has many false negatives. - # self.validate_kernel_code_args(sched.symbol_table) - else: - # The kernel name corresponds to an interface block. Find which - # of the routines matches the precision of the arguments. - for routine in routines: - try: - # The validity check for the kernel arguments will raise - # an exception if the precisions don't match. - self.validate_kernel_code_args(routine.symbol_table) - sched = routine - break - except GenerationError: - pass - else: - raise GenerationError( - f"Failed to find a kernel implementation with an interface" - f" that matches the invoke of '{self.name}'. (Tried " - f"routines {[item.name for item in routines]}.)") - - # TODO #935 - replace the PSyIR argument data symbols with LFRic data - # symbols. For the moment we just return the unmodified PSyIR schedule - # but this should use RaisePSyIR2LFRicKernTrans once KernelInterface - # is fully functional (#928). - ksched = KernelSchedule(sched.name, - symbol_table=sched.symbol_table.detach()) - for child in sched.pop_all_children(): - ksched.addchild(child) - sched.replace_with(ksched) - - self._kern_schedule = ksched - - return self._kern_schedule - - def validate_kernel_code_args(self, table): - '''Check that the arguments in the kernel code match the expected - arguments as defined by the kernel metadata and the LFRic - API. - - :param table: the symbol table to validate against the metadata. - :type table: :py:class:`psyclone.psyir.symbols.SymbolTable` - - :raises GenerationError: if the number of arguments indicated by the \ - kernel metadata doesn't match the actual number of arguments in \ - the symbol table. - - ''' - # Get the kernel subroutine arguments - kern_code_args = table.argument_list - - # Get the kernel code interface according to the kernel - # metadata and LFRic API - interface_info = KernelInterface(self) - interface_info.generate() - interface_args = interface_info.arglist - - # 1: Check the the number of arguments match - actual_n_args = len(kern_code_args) - expected_n_args = len(interface_args) - if actual_n_args != expected_n_args: - raise GenerationError( - f"In kernel '{self.name}' the number of arguments indicated by" - f" the kernel metadata is {expected_n_args} but the actual " - f"number of kernel arguments found is {actual_n_args}.") - - # 2: Check that the properties of each argument match. - for idx, kern_code_arg in enumerate(kern_code_args): - interface_arg = interface_args[idx] - try: - alg_idx = interface_info.metadata_index_from_actual_index(idx) - alg_arg = self.arguments.args[alg_idx] - except KeyError: - # There's no algorithm argument directly associated with this - # kernel argument. (We only care about the data associated - # with scalar, field and operator arguments.) - alg_arg = None - self._validate_kernel_code_arg(kern_code_arg, interface_arg, - alg_arg) - - def _validate_kernel_code_arg(self, kern_code_arg, interface_arg, - alg_arg=None): - '''Internal method to check that the supplied argument descriptions - match and raise appropriate exceptions if not. - - :param kern_code_arg: kernel code argument. - :type kern_code_arg: :py:class:`psyclone.psyir.symbols.DataSymbol` - :param interface_arg: expected argument. - :type interface_arg: :py:class:`psyclone.psyir.symbols.DataSymbol` - :param alg_arg: the associated argument in the Algorithm layer. Note \ - that only kernel arguments holding the data associated with \ - scalar, field and operator arguments directly correspond to \ - arguments that appear in the Algorithm layer. - :type alg_arg: \ - Optional[:py:class`psyclone.dynamo0p3.DynKernelArgument`] - - :raises GenerationError: if the contents of the arguments do \ - not match. - :raises InternalError: if an unexpected datatype is found. - - ''' - # 1: intrinsic datatype - actual_datatype = kern_code_arg.datatype.intrinsic - expected_datatype = interface_arg.datatype.intrinsic - if actual_datatype != expected_datatype: - raise GenerationError( - f"Kernel argument '{kern_code_arg.name}' has datatype " - f"'{actual_datatype}' in kernel '{self.name}' but the LFRic " - f"API expects '{expected_datatype}'.") - # 2: precision. An LFRic kernel is only permitted to have a precision - # specified by a recognised type parameter or a no. of bytes. - actual_precision = kern_code_arg.datatype.precision - if isinstance(actual_precision, DataSymbol): - # Convert precision into number of bytes to support - # mixed-precision kernels. - # TODO #1941: it would be better if the LFRic constants_mod.f90 - # was the single source of truth for precision values. - actual_precision = LFRicConstants.PRECISION_MAP[ - actual_precision.name] - elif not isinstance(actual_precision, int): - raise GenerationError( - f"An argument to an LFRic kernel must have a precision defined" - f" by either a recognised LFRic type parameter (one of " - f"{sorted(LFRicConstants.PRECISION_MAP.keys())}) or an integer" - f" number of bytes but argument '{kern_code_arg.name}' to " - f"kernel '{self.name}' has precision {actual_precision}.") - - if alg_arg: - # We have information on the corresponding argument in the - # Algorithm layer so we can check that the precision matches. - # This is used to identify the correct kernel subroutine for a - # mixed-precision kernel. - alg_precision = LFRicConstants.PRECISION_MAP[alg_arg.precision] - if alg_precision != actual_precision: - raise GenerationError( - f"Precision ({alg_precision} bytes) of algorithm-layer " - f"argument '{alg_arg.name}' does not match that " - f"({actual_precision} bytes) of the corresponding kernel " - f"subroutine argument '{kern_code_arg.name}' for kernel " - f"'{self.name}'.") - - # 3: intent - actual_intent = kern_code_arg.interface.access - expected_intent = interface_arg.interface.access - if actual_intent.name != expected_intent.name: - raise GenerationError( - f"Kernel argument '{kern_code_arg.name}' has intent " - f"'{actual_intent.name}' in kernel '{self.name}' but the " - f"LFRic API expects intent '{expected_intent.name}'.") - # 4: scalar or array - if interface_arg.is_scalar: - if not kern_code_arg.is_scalar: - raise GenerationError( - f"Argument '{kern_code_arg.name}' to kernel '{self.name}' " - f"should be a scalar according to the LFRic API, but it " - f"is not.") - elif interface_arg.is_array: - if not kern_code_arg.is_array: - raise GenerationError( - f"Argument '{kern_code_arg.name}' to kernel '{self.name}' " - f"should be an array according to the LFRic API, but it " - f"is not.") - # 4.1: array dimensions - if len(interface_arg.shape) != len(kern_code_arg.shape): - raise GenerationError( - f"Argument '{kern_code_arg.name}' to kernel '{self.name}' " - f"should be an array with {len(interface_arg.shape)} " - f"dimension(s) according to the LFRic API, but " - f"found {len(kern_code_arg.shape)}.") - for dim_idx, kern_code_arg_dim in enumerate(kern_code_arg.shape): - if not isinstance(kern_code_arg_dim, ArrayType.ArrayBounds): - continue - if (not isinstance(kern_code_arg_dim.lower, Literal) or - kern_code_arg_dim.lower.value != "1"): - raise GenerationError( - f"All array arguments to LFRic kernels must have lower" - f" bounds of 1 for all dimensions. However, array " - f"'{kern_code_arg.name}' has a lower bound of " - f"'{kern_code_arg_dim.lower}' for dimension {dim_idx}") - kern_code_arg_upper_dim = kern_code_arg_dim.upper - interface_arg_upper_dim = interface_arg.shape[dim_idx].upper - if (isinstance(kern_code_arg_upper_dim, Reference) and - isinstance(interface_arg_upper_dim, Reference) and - isinstance(kern_code_arg_upper_dim.symbol, - DataSymbol) and - isinstance(interface_arg_upper_dim.symbol, - DataSymbol)): - # Only check when there is a symbol. Unspecified - # dimensions, dimensions with scalar values, - # offsets, or dimensions that include arithmetic - # are skipped. - try: - self._validate_kernel_code_arg( - kern_code_arg_upper_dim.symbol, - interface_arg_upper_dim.symbol) - except GenerationError as info: - raise GenerationError( - f"For dimension {dim_idx+1} in array argument " - f"'{kern_code_arg.name}' to kernel '{self.name}' " - f"the following error was found: " - f"{info.args[0]}") from info - else: - raise InternalError( - f"unexpected argument type found for '{kern_code_arg.name}' in" - f" kernel '{self.name}'. Expecting a scalar or an array.") - - class FSDescriptor(): ''' Provides information about a particular function space used by a meta-funcs entry in the kernel metadata. ''' @@ -8962,7 +8093,7 @@ class DynKernelArguments(Arguments): :param call: the kernel meta-data for which to extract argument info. :type call: :py:class:`psyclone.parse.KernelCall` :param parent_call: the kernel-call object. - :type parent_call: :py:class:`psyclone.dynamo0p3.LFRicKern` + :type parent_call: :py:class:`psyclone.domain.lfric.LFRicKern` :param bool check: whether to check for consistency between the \ kernel metadata and the algorithm layer. Defaults to True. @@ -9296,7 +8427,7 @@ class DynKernelArgument(KernelArgument): the Algorithm layer. :type arg_info: :py:class:`psyclone.parse.algorithm.Arg` :param call: the kernel object with which this argument is associated. - :type call: :py:class:`psyclone.dynamo0p3.LFRicKern` + :type call: :py:class:`psyclone.domain.lfric.LFRicKern` :param bool check: whether to check for consistency between the \ kernel metadata and the algorithm layer. Defaults to True. @@ -10219,7 +9350,6 @@ def data_on_device(self, _): 'HaloWriteAccess', 'HaloReadAccess', 'DynLoop', - 'LFRicKern', 'FSDescriptor', 'FSDescriptors', 'DynStencil', diff --git a/src/psyclone/psyad/domain/lfric/lfric_adjoint_harness.py b/src/psyclone/psyad/domain/lfric/lfric_adjoint_harness.py index fbec629399..944e8787e5 100644 --- a/src/psyclone/psyad/domain/lfric/lfric_adjoint_harness.py +++ b/src/psyclone/psyad/domain/lfric/lfric_adjoint_harness.py @@ -331,7 +331,7 @@ def _validate_geom_arg(kern, arg_idx, name, valid_spaces, vec_len): properties of the field that it is supposed to represent. :param kern: the kernel under consideration. - :type kern: :py:class:`psyclone.dynamo0p3.LFRicKern` + :type kern: :py:class:`psyclone.domain.lfric.LFRicKern` :param in arg_idx: the 1-indexed position of the argument in the list \ defined in the kernel metadata. :param str name: the name of the argument that we are expecting. diff --git a/src/psyclone/tests/domain/lfric/conftest.py b/src/psyclone/tests/domain/lfric/conftest.py index 994a597409..579878987b 100644 --- a/src/psyclone/tests/domain/lfric/conftest.py +++ b/src/psyclone/tests/domain/lfric/conftest.py @@ -54,7 +54,7 @@ def api_setup_fixture(): def lfrickern_fixture(): ''' :returns: a LFRicKern object created from example metadata. - :rtype: :py:class:`psyclone.dynamo0p3.LFRicKern` + :rtype: :py:class:`psyclone.domain.lfric.LFRicKern` ''' mdata_code = ''' module testkern_field_mod @@ -99,7 +99,7 @@ def lfrickern_op_fixture(): ''' :returns: a LFRicKern object created from example metadata that includes \ an operator argument. - :rtype: :py:class:`psyclone.dynamo0p3.LFRicKern` + :rtype: :py:class:`psyclone.domain.lfric.LFRicKern` ''' mdata_code = ''' module testkern_field_mod From 716252265fde7439d60551c0bb4b15a47264c4dc Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Thu, 6 Apr 2023 14:01:56 +0100 Subject: [PATCH 07/44] #2091: changing some import locations --- src/psyclone/domain/lfric/__init__.py | 2 +- src/psyclone/domain/lfric/algorithm/lfric_alg.py | 2 +- src/psyclone/gen_kernel_stub.py | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/psyclone/domain/lfric/__init__.py b/src/psyclone/domain/lfric/__init__.py index bff34295ed..3b89f8b448 100644 --- a/src/psyclone/domain/lfric/__init__.py +++ b/src/psyclone/domain/lfric/__init__.py @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Author J. Henrichs, Bureau of Meteorology -# Modified: I. Kavcic, Met Office +# Modified: I. Kavcic and L. Turner, Met Office # R. W. Ford and A. R. Porter, STFC Daresbury Lab '''Module for the LFRic domain. diff --git a/src/psyclone/domain/lfric/algorithm/lfric_alg.py b/src/psyclone/domain/lfric/algorithm/lfric_alg.py index f2ef2abbe1..efd91e7e4b 100644 --- a/src/psyclone/domain/lfric/algorithm/lfric_alg.py +++ b/src/psyclone/domain/lfric/algorithm/lfric_alg.py @@ -44,7 +44,7 @@ LFRicSymbolTable, LFRicTypes) from psyclone.domain.lfric.algorithm.psyir import ( LFRicAlgorithmInvokeCall, LFRicBuiltinFunctorFactory, LFRicKernelFunctor) -from psyclone.dynamo0p3 import LFRicKern +from psyclone.domain.lfric import LFRicKern from psyclone.errors import InternalError from psyclone.parse.kernel import get_kernel_parse_tree, KernelTypeFactory from psyclone.parse.utils import ParseError diff --git a/src/psyclone/gen_kernel_stub.py b/src/psyclone/gen_kernel_stub.py index 9af9bd649e..961858e82b 100644 --- a/src/psyclone/gen_kernel_stub.py +++ b/src/psyclone/gen_kernel_stub.py @@ -45,7 +45,8 @@ import os import fparser -from psyclone.dynamo0p3 import LFRicKern, DynKernMetadata +from psyclone.domain.lfric import LFRicKern +from psyclone.dynamo0p3 import DynKernMetadata from psyclone.errors import GenerationError from psyclone.parse.utils import ParseError from psyclone.configuration import Config From 58b1415c6a1ac86a8486f98f65d75555d70a0ff8 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Thu, 6 Apr 2023 14:42:20 +0100 Subject: [PATCH 08/44] #2091: changing more import locations --- src/psyclone/tests/dependency_test.py | 5 +++-- src/psyclone/tests/domain/lfric/algorithm/lfric_alg_test.py | 3 +-- src/psyclone/tests/domain/lfric/arg_ordering_test.py | 4 ++-- src/psyclone/tests/domain/lfric/conftest.py | 2 +- src/psyclone/tests/domain/lfric/kern_call_arg_list_test.py | 4 ++-- .../tests/domain/lfric/lfric_domain_kernels_test.py | 3 ++- src/psyclone/tests/domain/lfric/lfric_field_stubgen_test.py | 4 ++-- src/psyclone/tests/domain/lfric/lfric_loop_test.py | 5 +++-- .../tests/domain/lfric/lfric_mesh_props_stubgen_test.py | 3 ++- .../tests/domain/lfric/lfric_ref_elem_stubgen_test.py | 3 ++- src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py | 6 +++--- .../tests/domain/lfric/lfric_scalar_stubgen_test.py | 4 ++-- 12 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/psyclone/tests/dependency_test.py b/src/psyclone/tests/dependency_test.py index 9e12e8f531..c27e8d18c2 100644 --- a/src/psyclone/tests/dependency_test.py +++ b/src/psyclone/tests/dependency_test.py @@ -45,8 +45,9 @@ from fparser.common.readfortran import FortranStringReader from psyclone import nemo from psyclone.core import AccessType, Signature, VariablesAccessInfo -from psyclone.domain.lfric import KernCallAccArgList, KernStubArgList -from psyclone.dynamo0p3 import DynKernMetadata, LFRicKern +from psyclone.domain.lfric import (KernCallAccArgList, KernStubArgList, + LFRicKern) +from psyclone.dynamo0p3 import DynKernMetadata from psyclone.parse.algorithm import parse from psyclone.psyGen import CodedKern, PSyFactory from psyclone.psyir.nodes import Assignment, IfBlock, Loop diff --git a/src/psyclone/tests/domain/lfric/algorithm/lfric_alg_test.py b/src/psyclone/tests/domain/lfric/algorithm/lfric_alg_test.py index 2de7d0e08e..45c8a464cc 100644 --- a/src/psyclone/tests/domain/lfric/algorithm/lfric_alg_test.py +++ b/src/psyclone/tests/domain/lfric/algorithm/lfric_alg_test.py @@ -41,9 +41,8 @@ import pytest from fparser import api as fpapi -from psyclone.domain.lfric import KernCallInvokeArgList +from psyclone.domain.lfric import KernCallInvokeArgList, LFRicKern from psyclone.domain.lfric.algorithm.lfric_alg import LFRicAlg -from psyclone.dynamo0p3 import LFRicKern from psyclone.errors import InternalError from psyclone.psyir.nodes import Container, Routine from psyclone.psyir.symbols import (ContainerSymbol, DataSymbol, DeferredType, diff --git a/src/psyclone/tests/domain/lfric/arg_ordering_test.py b/src/psyclone/tests/domain/lfric/arg_ordering_test.py index e3f02f36ff..bf400a7523 100644 --- a/src/psyclone/tests/domain/lfric/arg_ordering_test.py +++ b/src/psyclone/tests/domain/lfric/arg_ordering_test.py @@ -41,11 +41,11 @@ import pytest from psyclone.core import AccessType, VariablesAccessInfo, Signature -from psyclone.domain.lfric import (KernCallArgList, +from psyclone.domain.lfric import (KernCallArgList, LFRicKern, KernStubArgList, LFRicConstants, LFRicSymbolTable) from psyclone.domain.lfric.arg_ordering import ArgOrdering -from psyclone.dynamo0p3 import LFRicKern, DynKernMetadata, DynLoop +from psyclone.dynamo0p3 import DynKernMetadata, DynLoop from psyclone.errors import GenerationError, InternalError from psyclone.parse.algorithm import parse from psyclone.psyGen import PSyFactory diff --git a/src/psyclone/tests/domain/lfric/conftest.py b/src/psyclone/tests/domain/lfric/conftest.py index 579878987b..6afe4954e3 100644 --- a/src/psyclone/tests/domain/lfric/conftest.py +++ b/src/psyclone/tests/domain/lfric/conftest.py @@ -38,7 +38,7 @@ import pytest from psyclone.configuration import Config -from psyclone.dynamo0p3 import LFRicKern +from psyclone.domain.lfric import LFRicKern from psyclone.parse.kernel import get_kernel_parse_tree, KernelTypeFactory diff --git a/src/psyclone/tests/domain/lfric/kern_call_arg_list_test.py b/src/psyclone/tests/domain/lfric/kern_call_arg_list_test.py index d52ec82b5e..f7d98324ed 100644 --- a/src/psyclone/tests/domain/lfric/kern_call_arg_list_test.py +++ b/src/psyclone/tests/domain/lfric/kern_call_arg_list_test.py @@ -43,9 +43,9 @@ from psyclone.core import Signature, VariablesAccessInfo from psyclone.domain.lfric import (KernCallArgList, LFRicConstants, - LFRicSymbolTable, LFRicTypes) + LFRicSymbolTable, LFRicTypes, + LFRicKern) from psyclone.errors import GenerationError, InternalError -from psyclone.dynamo0p3 import LFRicKern from psyclone.parse.algorithm import parse from psyclone.psyGen import PSyFactory from psyclone.psyir.nodes import Literal, Loop, Reference, UnaryOperation diff --git a/src/psyclone/tests/domain/lfric/lfric_domain_kernels_test.py b/src/psyclone/tests/domain/lfric/lfric_domain_kernels_test.py index 4a9970d66f..31bdabdc65 100644 --- a/src/psyclone/tests/domain/lfric/lfric_domain_kernels_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_domain_kernels_test.py @@ -42,7 +42,8 @@ import os import pytest from fparser import api as fpapi -from psyclone.dynamo0p3 import DynKernMetadata, LFRicKern +from psyclone.dynamo0p3 import DynKernMetadata +from psyclone.domain.lfric import LFRicKern from psyclone.parse.algorithm import parse from psyclone.parse.utils import ParseError from psyclone.psyGen import PSyFactory diff --git a/src/psyclone/tests/domain/lfric/lfric_field_stubgen_test.py b/src/psyclone/tests/domain/lfric/lfric_field_stubgen_test.py index ae02ec8672..98651956c3 100644 --- a/src/psyclone/tests/domain/lfric/lfric_field_stubgen_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_field_stubgen_test.py @@ -47,8 +47,8 @@ import pytest import fparser from fparser import api as fpapi -from psyclone.domain.lfric import LFRicConstants -from psyclone.dynamo0p3 import DynKernMetadata, LFRicKern, LFRicFields +from psyclone.domain.lfric import LFRicConstants, LFRicKern +from psyclone.dynamo0p3 import DynKernMetadata, LFRicFields from psyclone.f2pygen import ModuleGen, SubroutineGen from psyclone.errors import InternalError diff --git a/src/psyclone/tests/domain/lfric/lfric_loop_test.py b/src/psyclone/tests/domain/lfric/lfric_loop_test.py index e52ac3f04f..de33083937 100644 --- a/src/psyclone/tests/domain/lfric/lfric_loop_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_loop_test.py @@ -47,8 +47,9 @@ from psyclone.configuration import Config from psyclone.core import AccessType from psyclone.domain.common.psylayer import PSyLoop -from psyclone.domain.lfric import LFRicConstants, LFRicSymbolTable -from psyclone.dynamo0p3 import DynLoop, LFRicKern, DynKernMetadata +from psyclone.domain.lfric import (LFRicConstants, LFRicSymbolTable, + LFRicKern) +from psyclone.dynamo0p3 import DynLoop, DynKernMetadata from psyclone.errors import GenerationError, InternalError from psyclone.parse.algorithm import parse from psyclone.psyGen import PSyFactory diff --git a/src/psyclone/tests/domain/lfric/lfric_mesh_props_stubgen_test.py b/src/psyclone/tests/domain/lfric/lfric_mesh_props_stubgen_test.py index 53c2532c1f..ef47426365 100644 --- a/src/psyclone/tests/domain/lfric/lfric_mesh_props_stubgen_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_mesh_props_stubgen_test.py @@ -42,7 +42,8 @@ import os from fparser import api as fpapi -from psyclone.dynamo0p3 import DynKernMetadata, LFRicKern +from psyclone.dynamo0p3 import DynKernMetadata +from psyclone.domain.lfric import LFRicKern # Constants diff --git a/src/psyclone/tests/domain/lfric/lfric_ref_elem_stubgen_test.py b/src/psyclone/tests/domain/lfric/lfric_ref_elem_stubgen_test.py index b9d98bc385..88fd09b044 100644 --- a/src/psyclone/tests/domain/lfric/lfric_ref_elem_stubgen_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_ref_elem_stubgen_test.py @@ -42,7 +42,8 @@ import os from fparser import api as fpapi -from psyclone.dynamo0p3 import DynKernMetadata, LFRicKern +from psyclone.dynamo0p3 import DynKernMetadata +from psyclone.domain.lfric import LFRicKern # Constants diff --git a/src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py b/src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py index 0075caa2f6..a80a5e7c74 100644 --- a/src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py @@ -46,9 +46,9 @@ import pytest import fparser from fparser import api as fpapi -from psyclone.domain.lfric import LFRicArgDescriptor -from psyclone.dynamo0p3 import (LFRicKern, DynKernMetadata, - LFRicScalarArgs, LFRicConstants) +from psyclone.domain.lfric import LFRicKern, LFRicArgDescriptor +from psyclone.dynamo0p3 import (DynKernMetadata, LFRicScalarArgs, + LFRicConstants) from psyclone.errors import InternalError, GenerationError from psyclone.f2pygen import ModuleGen from psyclone.parse.algorithm import parse diff --git a/src/psyclone/tests/domain/lfric/lfric_scalar_stubgen_test.py b/src/psyclone/tests/domain/lfric/lfric_scalar_stubgen_test.py index c7a67c8cc1..4d3113e9f4 100644 --- a/src/psyclone/tests/domain/lfric/lfric_scalar_stubgen_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_scalar_stubgen_test.py @@ -47,8 +47,8 @@ import pytest from fparser import api as fpapi -from psyclone.domain.lfric import LFRicConstants -from psyclone.dynamo0p3 import DynKernMetadata, LFRicKern, LFRicScalarArgs +from psyclone.domain.lfric import LFRicConstants, LFRicKern +from psyclone.dynamo0p3 import DynKernMetadata, LFRicScalarArgs from psyclone.f2pygen import ModuleGen from psyclone.errors import InternalError from psyclone.gen_kernel_stub import generate From 5535fcf61057d337c0ff6550a75a40d0e587d559 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Thu, 6 Apr 2023 14:56:20 +0100 Subject: [PATCH 09/44] #2091: hopefully the rest of the import locations --- .../tests/domain/lfric/lfric_stencil_stubgen_test.py | 2 +- src/psyclone/tests/domain/lfric/lfric_stencil_test.py | 4 ++-- src/psyclone/tests/dynamo0p3_basis_test.py | 5 ++--- src/psyclone/tests/dynamo0p3_lma_test.py | 4 ++-- src/psyclone/tests/dynamo0p3_quadrature_test.py | 4 ++-- src/psyclone/tests/dynamo0p3_stubgen_test.py | 4 ++-- src/psyclone/tests/dynamo0p3_test.py | 4 ++-- src/psyclone/tests/dynkern_test.py | 4 ++-- src/psyclone/tests/psyGen_test.py | 4 ++-- src/psyclone/transformations.py | 4 ++-- 10 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/psyclone/tests/domain/lfric/lfric_stencil_stubgen_test.py b/src/psyclone/tests/domain/lfric/lfric_stencil_stubgen_test.py index f89078a547..bd780ca461 100644 --- a/src/psyclone/tests/domain/lfric/lfric_stencil_stubgen_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_stencil_stubgen_test.py @@ -42,7 +42,7 @@ from fparser import api as fpapi -from psyclone.dynamo0p3 import LFRicKern, DynKernMetadata +from psyclone.domain.lfric import LFRicKern, DynKernMetadata # Constants BASE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), diff --git a/src/psyclone/tests/domain/lfric/lfric_stencil_test.py b/src/psyclone/tests/domain/lfric/lfric_stencil_test.py index f4a9a8e3e4..1c3ea8c3a2 100644 --- a/src/psyclone/tests/domain/lfric/lfric_stencil_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_stencil_test.py @@ -45,8 +45,8 @@ import fparser from fparser import api as fpapi -from psyclone.domain.lfric import LFRicConstants -from psyclone.dynamo0p3 import (LFRicKern, DynKernelArguments, +from psyclone.domain.lfric import LFRicKern, LFRicConstants +from psyclone.dynamo0p3 import (DynKernelArguments, DynKernMetadata, DynStencils) from psyclone.errors import GenerationError, InternalError from psyclone.f2pygen import ModuleGen diff --git a/src/psyclone/tests/dynamo0p3_basis_test.py b/src/psyclone/tests/dynamo0p3_basis_test.py index ded84eaee8..2aa554bb0d 100644 --- a/src/psyclone/tests/dynamo0p3_basis_test.py +++ b/src/psyclone/tests/dynamo0p3_basis_test.py @@ -44,14 +44,13 @@ import fparser from fparser import api as fpapi from psyclone.configuration import Config -from psyclone.domain.lfric import LFRicConstants -from psyclone.dynamo0p3 import DynBasisFunctions +from psyclone.domain.lfric import LFRicConstants, LFRicKern +from psyclone.dynamo0p3 import DynKernMetadata, DynBasisFunctions from psyclone.f2pygen import ModuleGen from psyclone.parse.algorithm import parse from psyclone.parse.utils import ParseError from psyclone.psyGen import PSyFactory from psyclone.errors import GenerationError, InternalError -from psyclone.dynamo0p3 import DynKernMetadata, LFRicKern from psyclone.tests.lfric_build import LFRicBuild from psyclone.tests.utilities import print_diffs diff --git a/src/psyclone/tests/dynamo0p3_lma_test.py b/src/psyclone/tests/dynamo0p3_lma_test.py index b1cd3f18fa..114aa6a38f 100644 --- a/src/psyclone/tests/dynamo0p3_lma_test.py +++ b/src/psyclone/tests/dynamo0p3_lma_test.py @@ -48,9 +48,9 @@ from psyclone.configuration import Config from psyclone.core.access_type import AccessType -from psyclone.domain.lfric import LFRicArgDescriptor, LFRicConstants +from psyclone.domain.lfric import LFRicKern, LFRicArgDescriptor, LFRicConstants from psyclone.dynamo0p3 import (DynFuncDescriptor03, DynKernMetadata, - LFRicKern, FunctionSpace) + FunctionSpace) from psyclone.errors import GenerationError, InternalError from psyclone.parse.algorithm import parse from psyclone.parse.utils import ParseError diff --git a/src/psyclone/tests/dynamo0p3_quadrature_test.py b/src/psyclone/tests/dynamo0p3_quadrature_test.py index 0051ed4a62..b65d5f5984 100644 --- a/src/psyclone/tests/dynamo0p3_quadrature_test.py +++ b/src/psyclone/tests/dynamo0p3_quadrature_test.py @@ -46,8 +46,8 @@ from fparser import api as fpapi from psyclone.configuration import Config -from psyclone.domain.lfric import LFRicConstants -from psyclone.dynamo0p3 import DynKernMetadata, LFRicKern, DynBasisFunctions, \ +from psyclone.domain.lfric import LFRicConstants, LFRicKern +from psyclone.dynamo0p3 import DynKernMetadata, DynBasisFunctions, \ qr_basis_alloc_args from psyclone.errors import InternalError from psyclone.f2pygen import ModuleGen diff --git a/src/psyclone/tests/dynamo0p3_stubgen_test.py b/src/psyclone/tests/dynamo0p3_stubgen_test.py index 8aa9c70044..7e6d2d7831 100644 --- a/src/psyclone/tests/dynamo0p3_stubgen_test.py +++ b/src/psyclone/tests/dynamo0p3_stubgen_test.py @@ -45,8 +45,8 @@ from fparser import api as fpapi from psyclone.configuration import Config -from psyclone.domain.lfric import LFRicConstants -from psyclone.dynamo0p3 import DynKernMetadata, LFRicKern +from psyclone.domain.lfric import LFRicConstants, LFRicKern +from psyclone.dynamo0p3 import DynKernMetadata from psyclone.errors import GenerationError from psyclone.gen_kernel_stub import generate diff --git a/src/psyclone/tests/dynamo0p3_test.py b/src/psyclone/tests/dynamo0p3_test.py index a73891d231..a391b72f87 100644 --- a/src/psyclone/tests/dynamo0p3_test.py +++ b/src/psyclone/tests/dynamo0p3_test.py @@ -48,9 +48,9 @@ from psyclone.configuration import Config from psyclone.core.access_type import AccessType from psyclone.domain.lfric import FunctionSpace, LFRicArgDescriptor, \ - LFRicConstants + LFRicConstants, LFRicKern from psyclone.dynamo0p3 import DynACCEnterDataDirective, \ - DynBoundaryConditions, DynCellIterators, DynGlobalSum, LFRicKern, \ + DynBoundaryConditions, DynCellIterators, DynGlobalSum, \ DynKernelArguments, DynKernMetadata, DynLoop, DynProxies, \ HaloReadAccess, KernCallArgList from psyclone.errors import FieldNotFoundError, GenerationError, InternalError diff --git a/src/psyclone/tests/dynkern_test.py b/src/psyclone/tests/dynkern_test.py index 3f93036967..6213442ccd 100644 --- a/src/psyclone/tests/dynkern_test.py +++ b/src/psyclone/tests/dynkern_test.py @@ -47,8 +47,8 @@ import psyclone from psyclone.core import AccessType -from psyclone.domain.lfric import LFRicConstants, LFRicTypes -from psyclone.dynamo0p3 import DynKernMetadata, LFRicKern, DynLoop +from psyclone.domain.lfric import LFRicConstants, LFRicTypes, LFRicKern +from psyclone.dynamo0p3 import DynKernMetadata, DynLoop from psyclone.errors import InternalError, GenerationError from psyclone.parse.algorithm import parse from psyclone.psyGen import PSyFactory diff --git a/src/psyclone/tests/psyGen_test.py b/src/psyclone/tests/psyGen_test.py index ec0a69ad63..072b3c9cdd 100644 --- a/src/psyclone/tests/psyGen_test.py +++ b/src/psyclone/tests/psyGen_test.py @@ -52,9 +52,9 @@ from psyclone.configuration import Config from psyclone.core.access_type import AccessType from psyclone.domain.common.psylayer import PSyLoop -from psyclone.domain.lfric import lfric_builtins +from psyclone.domain.lfric import LFRicKern, lfric_builtins from psyclone.domain.lfric.transformations import LFRicLoopFuseTrans -from psyclone.dynamo0p3 import LFRicKern, DynKernMetadata, DynInvokeSchedule, \ +from psyclone.dynamo0p3 import DynKernMetadata, DynInvokeSchedule, \ DynKernelArguments, DynGlobalSum from psyclone.errors import GenerationError, FieldNotFoundError, InternalError from psyclone.generator import generate diff --git a/src/psyclone/transformations.py b/src/psyclone/transformations.py index 3a95bbfc08..1a9158cfff 100644 --- a/src/psyclone/transformations.py +++ b/src/psyclone/transformations.py @@ -47,9 +47,9 @@ from psyclone import psyGen from psyclone.configuration import Config -from psyclone.domain.lfric import KernCallArgList, LFRicConstants +from psyclone.domain.lfric import KernCallArgList, LFRicConstants, LFRicKern from psyclone.dynamo0p3 import DynHaloExchangeEnd, DynHaloExchangeStart, \ - DynInvokeSchedule, LFRicKern + DynInvokeSchedule from psyclone.errors import InternalError from psyclone.gocean1p0 import GOInvokeSchedule from psyclone.nemo import NemoInvokeSchedule From b66f27144104b7d42860e9205da5192762717d32 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Tue, 11 Apr 2023 10:27:28 +0100 Subject: [PATCH 10/44] #2091: correcting error in imports --- src/psyclone/tests/domain/lfric/lfric_stencil_stubgen_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/psyclone/tests/domain/lfric/lfric_stencil_stubgen_test.py b/src/psyclone/tests/domain/lfric/lfric_stencil_stubgen_test.py index bd780ca461..35839f176b 100644 --- a/src/psyclone/tests/domain/lfric/lfric_stencil_stubgen_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_stencil_stubgen_test.py @@ -42,7 +42,8 @@ from fparser import api as fpapi -from psyclone.domain.lfric import LFRicKern, DynKernMetadata +from psyclone.domain.lfric import LFRicKern +from psyclone.dynamo0p3 import DynKernMetadata # Constants BASE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), From ad9feda36d8698bc8be7c3d500ecdbe66cabff80 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Tue, 18 Apr 2023 16:49:10 +0100 Subject: [PATCH 11/44] #2091: correcting path to LFRicKern in new file --- src/psyclone/domain/lfric/lfric_kern_call_factory.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/psyclone/domain/lfric/lfric_kern_call_factory.py b/src/psyclone/domain/lfric/lfric_kern_call_factory.py index ff114fd4fe..582a9c1c33 100644 --- a/src/psyclone/domain/lfric/lfric_kern_call_factory.py +++ b/src/psyclone/domain/lfric/lfric_kern_call_factory.py @@ -41,6 +41,8 @@ ''' +# Imports +from psyclone.domain.lfric import LFRicKern class LFRicKernCallFactory(): ''' Create the necessary framework for an LFRic kernel call. @@ -76,10 +78,8 @@ def create(call, parent=None): cloop = DynLoop(parent=parent, loop_type=loop_type) # The kernel itself - # Import here to avoid circular dependency - # pylint: disable=import-outside-toplevel - from psyclone.dynamo0p3 import DynKern - kern = DynKern() + + kern = LFRicKern() kern.load(call, cloop.loop_body) # Add the kernel as a child of the loop From f7858fb0afdd892a1bfeb48e74ff423a155d1e6b Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Tue, 18 Apr 2023 17:06:51 +0100 Subject: [PATCH 12/44] #2091: hopefully fixing import error --- src/psyclone/domain/lfric/lfric_kern_call_factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psyclone/domain/lfric/lfric_kern_call_factory.py b/src/psyclone/domain/lfric/lfric_kern_call_factory.py index 582a9c1c33..0dab6bfc47 100644 --- a/src/psyclone/domain/lfric/lfric_kern_call_factory.py +++ b/src/psyclone/domain/lfric/lfric_kern_call_factory.py @@ -42,7 +42,7 @@ ''' # Imports -from psyclone.domain.lfric import LFRicKern +from psyclone.domain.lfric.lfric_kern import LFRicKern class LFRicKernCallFactory(): ''' Create the necessary framework for an LFRic kernel call. From aff6c35e9e8b0f2fafc6c1c839fed10b684ba48b Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Tue, 18 Apr 2023 17:29:22 +0100 Subject: [PATCH 13/44] #2091: hopefully fixing circular dependency issue --- src/psyclone/domain/lfric/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psyclone/domain/lfric/__init__.py b/src/psyclone/domain/lfric/__init__.py index a295532e3c..de24146499 100644 --- a/src/psyclone/domain/lfric/__init__.py +++ b/src/psyclone/domain/lfric/__init__.py @@ -50,7 +50,6 @@ from psyclone.domain.lfric.kern_call_acc_arg_list import KernCallAccArgList from psyclone.domain.lfric.kern_call_invoke_arg_list import \ KernCallInvokeArgList -from psyclone.domain.lfric.lfric_kern_call_factory import LFRicKernCallFactory from psyclone.domain.lfric.kernel_interface import KernelInterface from psyclone.domain.lfric.lfric_extract_driver_creator import \ LFRicExtractDriverCreator @@ -62,6 +61,7 @@ from psyclone.domain.lfric.arg_index_to_metadata_index import \ ArgIndexToMetadataIndex from psyclone.domain.lfric.lfric_kern import LFRicKern +from psyclone.domain.lfric.lfric_kern_call_factory import LFRicKernCallFactory __all__ = [ 'ArgOrdering', From 47c0c23e6e4a86c4f9e20125bba19d52943a236e Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Thu, 20 Apr 2023 10:24:56 +0100 Subject: [PATCH 14/44] #2091: tidying up --- src/psyclone/domain/lfric/lfric_kern_call_factory.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/psyclone/domain/lfric/lfric_kern_call_factory.py b/src/psyclone/domain/lfric/lfric_kern_call_factory.py index 0dab6bfc47..a14d353c90 100644 --- a/src/psyclone/domain/lfric/lfric_kern_call_factory.py +++ b/src/psyclone/domain/lfric/lfric_kern_call_factory.py @@ -42,7 +42,7 @@ ''' # Imports -from psyclone.domain.lfric.lfric_kern import LFRicKern +from psyclone.domain.lfric import LFRicKern class LFRicKernCallFactory(): ''' Create the necessary framework for an LFRic kernel call. @@ -78,6 +78,8 @@ def create(call, parent=None): cloop = DynLoop(parent=parent, loop_type=loop_type) # The kernel itself + # Import here to avoid circular dependency + # pylint: disable=import-outside-toplevel kern = LFRicKern() kern.load(call, cloop.loop_body) From ce20e1e47d4e9d035f6d519bdb37f95606fb8e29 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Thu, 20 Apr 2023 10:27:30 +0100 Subject: [PATCH 15/44] #2091 tidying up a bit that I accidentally untidied --- src/psyclone/domain/lfric/lfric_kern_call_factory.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/psyclone/domain/lfric/lfric_kern_call_factory.py b/src/psyclone/domain/lfric/lfric_kern_call_factory.py index a14d353c90..bf28704fbd 100644 --- a/src/psyclone/domain/lfric/lfric_kern_call_factory.py +++ b/src/psyclone/domain/lfric/lfric_kern_call_factory.py @@ -78,9 +78,6 @@ def create(call, parent=None): cloop = DynLoop(parent=parent, loop_type=loop_type) # The kernel itself - # Import here to avoid circular dependency - # pylint: disable=import-outside-toplevel - kern = LFRicKern() kern.load(call, cloop.loop_body) From 209192158817bfa586fc51664be884bd8103e484 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Thu, 20 Apr 2023 15:29:39 +0100 Subject: [PATCH 16/44] #2091 changing names in the docs --- doc/developer_guide/APIs.rst | 8 ++++---- doc/developer_guide/modules.rst | 2 +- doc/developer_guide/psyir.rst | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/developer_guide/APIs.rst b/doc/developer_guide/APIs.rst index 0172cca4bc..d8b9b2d207 100644 --- a/doc/developer_guide/APIs.rst +++ b/doc/developer_guide/APIs.rst @@ -634,7 +634,7 @@ requires a check that any loop which includes a kernel that reads from an operator is limited to iterating in the halo up to depth-1. PSyclone will raise an exception if an optimisation attempts to increase the iteration space beyond this (see the ``gen_code()`` -method in the ``DynKern`` class). +method in the ``LFRicKern`` class). To alleviate the above restriction one could add a configurable depth with which to compute operators e.g. operators are always computed up to @@ -883,8 +883,8 @@ target spaces. When constructing a ``DynKernMetadata`` object from the parsed kernel metadata, the list of target function-space names (as they appear in the meta-data) is stored in ``DynKernMetadata._eval_targets``. This -information is then used in the ``DynKern._setup()`` method which -populates ``DynKern._eval_targets``. This is an ``OrderedDict`` which has +information is then used in the ``LFRicKern._setup()`` method which +populates ``LFRicKern._eval_targets``. This is an ``OrderedDict`` which has the (mangled) names of the target function spaces as keys and 2-tuples consisting of ``FunctionSpace`` and ``DynKernelArgument`` objects as values. The ``DynKernelArgument`` object provides the kernel argument @@ -894,7 +894,7 @@ holds full information on the target function space. The ``DynInvokeBasisFunctions`` class is responsible for managing the evaluators required by all of the kernels called from an Invoke. ``DynInvokeBasisFunctions._eval_targets`` collects all of the unique target -function spaces from the ``DynKern._eval_targets`` of each kernel. +function spaces from the ``LFRicKern._eval_targets`` of each kernel. ``DynInvokeBasisFunctions._basis_fns`` is a list holding information on each basis/differential basis function required by a kernel within the diff --git a/doc/developer_guide/modules.rst b/doc/developer_guide/modules.rst index 6533264f29..3790246bf3 100644 --- a/doc/developer_guide/modules.rst +++ b/doc/developer_guide/modules.rst @@ -292,6 +292,6 @@ This class is then sub-classed in order to support the generation of argument lists when *calling* kernels (``KernCallArgList``) and when *creating* kernel stubs (``KernStubArgList``). ``KernCallArgList`` is only used in ``DynKernelArguments.raw_arg_list()``. -``KernStubArgList`` is only used in ``DynKern.gen_stub()``. These +``KernStubArgList`` is only used in ``LFRicKern.gen_stub()``. These classes make use of ``DynCollection`` sub-classes in order to ensure that argument naming is consistent. diff --git a/doc/developer_guide/psyir.rst b/doc/developer_guide/psyir.rst index a71ea836ff..95ff333523 100644 --- a/doc/developer_guide/psyir.rst +++ b/doc/developer_guide/psyir.rst @@ -1036,7 +1036,7 @@ correspond to and how the arguments relate to each other (they just output strings). The logic and declaration of kernel variables is handled separately by -the ``gen_stub`` method in ``DynKern`` and the ``gen_code`` method in +the ``gen_stub`` method in ``LFRicKern`` and the ``gen_code`` method in ``DynInvoke``. In both cases these methods make use of the subclasses of ``DynCollection`` to declare variables. From bf0e3d5879b61dae40e6e378d84421a68b6d233a Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Mon, 22 May 2023 16:42:41 +0100 Subject: [PATCH 17/44] #2091 renaming dynkern_test to lfrickern_test and correcting some errors made when resolving merge conflicts --- src/psyclone/dynamo0p3.py | 5 ++--- src/psyclone/tests/{dynkern_test.py => lfrickern_test.py} | 0 2 files changed, 2 insertions(+), 3 deletions(-) rename src/psyclone/tests/{dynkern_test.py => lfrickern_test.py} (100%) diff --git a/src/psyclone/dynamo0p3.py b/src/psyclone/dynamo0p3.py index 589be91c50..13357ff2c6 100644 --- a/src/psyclone/dynamo0p3.py +++ b/src/psyclone/dynamo0p3.py @@ -65,8 +65,7 @@ from psyclone.errors import GenerationError, InternalError, FieldNotFoundError from psyclone.f2pygen import (AllocateGen, AssignGen, CallGen, CommentGen, DeallocateGen, DeclGen, DoGen, IfThenGen, - ModuleGen, SubroutineGen, TypeDeclGen, UseGen, - PSyIRGen) + ModuleGen, TypeDeclGen, UseGen, PSyIRGen) from psyclone.parse.kernel import KernelType, getkerneldescriptors from psyclone.parse.utils import ParseError @@ -5464,8 +5463,8 @@ class DynBoundaryConditions(DynCollection): :param node: the Invoke or Kernel stub for which we are to handle \ any boundary conditions. - :py:class:`psyclone.domain.lfric.LFRicKern` :type node: :py:class:`psyclone.dynamo0p3.LFRicInvoke` or \ + :py:class:`psyclone.domain.lfric.LFRicKern` :raises GenerationError: if a kernel named "enforce_bc_code" is found \ but does not have an argument on ANY_SPACE_1. diff --git a/src/psyclone/tests/dynkern_test.py b/src/psyclone/tests/lfrickern_test.py similarity index 100% rename from src/psyclone/tests/dynkern_test.py rename to src/psyclone/tests/lfrickern_test.py From e36e28c10efbcfae77f2971a7a997598e149f6aa Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Mon, 22 May 2023 17:00:19 +0100 Subject: [PATCH 18/44] 2091 adding test for LFRicKern.local_vars --- src/psyclone/tests/lfrickern_test.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/psyclone/tests/lfrickern_test.py b/src/psyclone/tests/lfrickern_test.py index 6432bf8288..3cb31016e4 100644 --- a/src/psyclone/tests/lfrickern_test.py +++ b/src/psyclone/tests/lfrickern_test.py @@ -439,3 +439,14 @@ def test_kern_all_updates_are_writes(): # Change the GH_INC to be GH_WRITE. loop.kernel.args[1]._access = AccessType.WRITE assert loop.kernel.all_updates_are_writes + + +def test_kern_local_vars(): + ''' Check that the LFRicKern.local_vars() method returns the expected + names used by the Kernel that vary from one invocation to the next. + + ''' + kernel = LFRicKern() + # Get a scalar argument descriptor and set an invalid data type + output = kernel.local_vars() + assert output == [] From 807f3c6b9adcb72d3e39b488f99c3c557e649730 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Mon, 22 May 2023 17:35:49 +0100 Subject: [PATCH 19/44] 2091 adding test for kernel argument with INC access when OpenMP is applied without colouring --- src/psyclone/tests/lfrickern_test.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/psyclone/tests/lfrickern_test.py b/src/psyclone/tests/lfrickern_test.py index 3cb31016e4..2843ae9910 100644 --- a/src/psyclone/tests/lfrickern_test.py +++ b/src/psyclone/tests/lfrickern_test.py @@ -406,7 +406,10 @@ def test_kern_last_cell_all_colours(): def test_kern_last_cell_all_colours_intergrid(): - ''' Test the last_cell_all_colours property for an inter-grid LFRicKern. ''' + ''' Test the last_cell_all_colours property for an inter-grid + LFRicKern. + + ''' _, invoke_info = parse(os.path.join(BASE_PATH, "22.1_intergrid_restrict.f90"), api=TEST_API) @@ -447,6 +450,27 @@ def test_kern_local_vars(): ''' kernel = LFRicKern() - # Get a scalar argument descriptor and set an invalid data type output = kernel.local_vars() assert output == [] + + +def test_kern_not_coloured_inc(monkeypatch): + ''' Tests that there is no kernel argument with INC access when OpenMP + is applied without colouring. + + ''' + _, invoke_info = parse(os.path.join(BASE_PATH, "1_single_invoke.f90"), + api=TEST_API) + psy = PSyFactory(TEST_API, distributed_memory=True).create(invoke_info) + sched = psy.invokes.invoke_list[0].schedule + kern = sched.walk(LFRicKern)[0] + # Kernel is not coloured. + assert kern.is_coloured() is False + # Monkeypatch the Kernel so that it appears to be OpenMP parallel. + monkeypatch.setattr(kern, "is_openmp_parallel", lambda: True) + assert kern.is_openmp_parallel() is True + with pytest.raises(GenerationError) as err: + _ = psy.gen + assert ("Kernel 'testkern_code' has an argument with INC access and " + "therefore must be coloured in order to be parallelised with " + "OpenMP.") From 9db26c549906a1817ee3d8501c4b3ee149175525 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Tue, 23 May 2023 12:03:02 +0100 Subject: [PATCH 20/44] 2091 working on last bit of test coverage, not working yet --- src/psyclone/domain/lfric/lfric_kern.py | 14 ++++++++++---- src/psyclone/tests/lfrickern_test.py | 14 +++++++++++++- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/psyclone/domain/lfric/lfric_kern.py b/src/psyclone/domain/lfric/lfric_kern.py index 610bef44fc..4cf7763c25 100644 --- a/src/psyclone/domain/lfric/lfric_kern.py +++ b/src/psyclone/domain/lfric/lfric_kern.py @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors R. W. Ford, A. R. Porter and S. Siso, STFC Daresbury Lab -# Modified I. Kavcic and A. Coughtrie, Met Office +# Modified I. Kavcic, A. Coughtrie and L. Turner, Met Office # Modified J. Henrichs, Bureau of Meteorology # Modified A. B. G. Chalk and N. Nobre, STFC Daresbury Lab @@ -700,11 +700,11 @@ def gen_code(self, parent): except FieldNotFoundError: arg = None if arg: - raise GenerationError(f"Kernel {self._name} has an " + raise GenerationError(f"Kernel '{self._name}' has an " f"argument with INC access and " f"therefore must be coloured in " f"order to be parallelised with " - f"OpenMP") + f"OpenMP.") parent.add(CommentGen(parent, "")) @@ -905,8 +905,14 @@ def _validate_kernel_code_arg(self, kern_code_arg, interface_arg, f"dimension(s) according to the LFRic API, but " f"found {len(kern_code_arg.shape)}.") for dim_idx, kern_code_arg_dim in enumerate(kern_code_arg.shape): + print(dim_idx) + print(kern_code_arg_dim) + print(kern_code_arg.shape) if not isinstance(kern_code_arg_dim, ArrayType.ArrayBounds): - continue + print('sharks') + print(dim_idx) + print(kern_code_arg_dim) + continue #sharks if (not isinstance(kern_code_arg_dim.lower, Literal) or kern_code_arg_dim.lower.value != "1"): raise GenerationError( diff --git a/src/psyclone/tests/lfrickern_test.py b/src/psyclone/tests/lfrickern_test.py index 2843ae9910..c737641a02 100644 --- a/src/psyclone/tests/lfrickern_test.py +++ b/src/psyclone/tests/lfrickern_test.py @@ -329,13 +329,25 @@ def test_validate_kernel_code_arg(monkeypatch): "dimension(s) according to the LFRic API, but found 1." in str(info.value)) + lfric_real_field_symbol4 = LFRicTypes("RealFieldDataSymbol")( + "field", dims=[Reference(undf)], + fs="w0", interface=read_access) + lfric_real_field_symbol3 = LFRicTypes("RealFieldDataSymbol")( + "field", dims=[ArrayType.ArrayBounds(2, Reference(undf))], + fs="w0", interface=read_access) + monkeypatch.setattr(lfric_real_field_symbol4.datatype, "_shape", + ['1', Reference(undf)]) + kernel._validate_kernel_code_arg(lfric_real_field_symbol4, + lfric_real_field_symbol3) + #assert ("dinosaurs" in str(info.value)) + # Lower array bound of 2 rather than 1 monkeypatch.setattr(lfric_real_field_symbol3.datatype, "_shape", [ArrayType.ArrayBounds(2, Reference(undf))]) with pytest.raises(GenerationError) as info: kernel._validate_kernel_code_arg(lfric_real_field_symbol3, lfric_real_field_symbol3) - assert ("All array arguments to LFRic kernels must have lower bounds of 1 " + assert ("zzAll array arguments to LFRic kernels must have lower bounds of 1 " "for all dimensions. However, array 'field' has a lower bound of " "'2' for dimension 0" in str(info.value)) From dbae330c7b7b9a25ae2eccd64d8b82da4b05b855 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Mon, 5 Jun 2023 10:26:41 +0100 Subject: [PATCH 21/44] #2091 commenting out failed attempt at test to get python tests passing --- src/psyclone/domain/lfric/lfric_kern.py | 10 ++++------ src/psyclone/tests/lfrickern_test.py | 26 +++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/psyclone/domain/lfric/lfric_kern.py b/src/psyclone/domain/lfric/lfric_kern.py index 4cf7763c25..864ddc3959 100644 --- a/src/psyclone/domain/lfric/lfric_kern.py +++ b/src/psyclone/domain/lfric/lfric_kern.py @@ -905,13 +905,11 @@ def _validate_kernel_code_arg(self, kern_code_arg, interface_arg, f"dimension(s) according to the LFRic API, but " f"found {len(kern_code_arg.shape)}.") for dim_idx, kern_code_arg_dim in enumerate(kern_code_arg.shape): - print(dim_idx) - print(kern_code_arg_dim) - print(kern_code_arg.shape) + #print(dim_idx) + #print(type(kern_code_arg_dim)) + #print(kern_code_arg.shape) if not isinstance(kern_code_arg_dim, ArrayType.ArrayBounds): - print('sharks') - print(dim_idx) - print(kern_code_arg_dim) + #print('sharks') continue #sharks if (not isinstance(kern_code_arg_dim.lower, Literal) or kern_code_arg_dim.lower.value != "1"): diff --git a/src/psyclone/tests/lfrickern_test.py b/src/psyclone/tests/lfrickern_test.py index c737641a02..c354f8fa8a 100644 --- a/src/psyclone/tests/lfrickern_test.py +++ b/src/psyclone/tests/lfrickern_test.py @@ -329,17 +329,19 @@ def test_validate_kernel_code_arg(monkeypatch): "dimension(s) according to the LFRic API, but found 1." in str(info.value)) - lfric_real_field_symbol4 = LFRicTypes("RealFieldDataSymbol")( - "field", dims=[Reference(undf)], - fs="w0", interface=read_access) - lfric_real_field_symbol3 = LFRicTypes("RealFieldDataSymbol")( - "field", dims=[ArrayType.ArrayBounds(2, Reference(undf))], - fs="w0", interface=read_access) - monkeypatch.setattr(lfric_real_field_symbol4.datatype, "_shape", - ['1', Reference(undf)]) - kernel._validate_kernel_code_arg(lfric_real_field_symbol4, - lfric_real_field_symbol3) - #assert ("dinosaurs" in str(info.value)) +# lfric_real_field_symbol4 = LFRicTypes("RealFieldDataSymbol")( +# "field", dims=[Reference(undf)], +# fs="w0", interface=read_access) +# lfric_real_field_symbol3 = LFRicTypes("RealFieldDataSymbol")( +# "field", dims=[ArrayType.ArrayBounds(2, Reference(undf))], +# fs="w0", interface=read_access) +# # we want to monkeypatch lfric_real_field_symbol4 so it is not +# # type ArrayType.ArrayBounds - maybe ArrayType.Extent? +# monkeypatch.setattr(lfric_real_field_symbol4.datatype, "_shape", +# [1, Reference(undf)]) +# kernel._validate_kernel_code_arg(lfric_real_field_symbol4, +# lfric_real_field_symbol3) +# #assert ("dinosaurs" in str(info.value)) # Lower array bound of 2 rather than 1 monkeypatch.setattr(lfric_real_field_symbol3.datatype, "_shape", @@ -347,7 +349,7 @@ def test_validate_kernel_code_arg(monkeypatch): with pytest.raises(GenerationError) as info: kernel._validate_kernel_code_arg(lfric_real_field_symbol3, lfric_real_field_symbol3) - assert ("zzAll array arguments to LFRic kernels must have lower bounds of 1 " + assert ("All array arguments to LFRic kernels must have lower bounds of 1 " "for all dimensions. However, array 'field' has a lower bound of " "'2' for dimension 0" in str(info.value)) From 04ad8d0cd2ff3a11c60fd965ce7e5b17275c5b1c Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Mon, 5 Jun 2023 11:12:47 +0100 Subject: [PATCH 22/44] #2091: more changes from LFRicCollection that didnt get picked up in merge conflicts --- src/psyclone/domain/lfric/lfric_collection.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/psyclone/domain/lfric/lfric_collection.py b/src/psyclone/domain/lfric/lfric_collection.py index e7ed7a6dd1..380446f943 100644 --- a/src/psyclone/domain/lfric/lfric_collection.py +++ b/src/psyclone/domain/lfric/lfric_collection.py @@ -42,7 +42,8 @@ # Imports import abc -from psyclone.domain.lfric import LFRicSymbolTable, LFRicInvoke +from psyclone.domain.lfric import [LFRicSymbolTable, LFRicInvoke, + LFRicKern] from psyclone.errors import InternalError @@ -54,16 +55,13 @@ class LFRicCollection(): :param node: the Kernel or Invoke for which to manage variable \ declarations and initialisation. :type node: :py:class:`psyclone.domain.lfric.LFRicInvoke` or \ - :py:class:`psyclone.dynamo0p3.DynKern` + :py:class:`psyclone.domain.lfric.LFRicKern` - :raises InternalError: if the supplied node is not an LFRicInvoke or a \ - DynKern. + :raises InternalError: if the supplied node is not an LFRicInvoke or an \ + LFRicKern. ''' def __init__(self, node): - # Import here to avoid circular dependency - # pylint: disable=import-outside-toplevel - from psyclone.dynamo0p3 import DynKern if isinstance(node, LFRicInvoke): # We are handling declarations/initialisations for an Invoke self._invoke = node @@ -71,7 +69,7 @@ def __init__(self, node): self._symbol_table = self._invoke.schedule.symbol_table # The list of Kernel calls we are responsible for self._calls = node.schedule.kernels() - elif isinstance(node, DynKern): + elif isinstance(node, LFRicKern): # We are handling declarations for a Kernel stub self._invoke = None self._kernel = node @@ -82,7 +80,7 @@ def __init__(self, node): self._calls = [node] else: raise InternalError(f"LFRicCollection takes only an LFRicInvoke " - f"or a DynKern but got: {type(node)}") + f"or an LFRicKern but got: {type(node)}") # Whether or not the associated Invoke contains only Kernels that # operate on DoFs. From b8445f48a801982f4175614afa55d6f665df0fa9 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Mon, 5 Jun 2023 11:20:39 +0100 Subject: [PATCH 23/44] #2091: wrong brackets! --- src/psyclone/domain/lfric/lfric_collection.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/psyclone/domain/lfric/lfric_collection.py b/src/psyclone/domain/lfric/lfric_collection.py index 380446f943..3ce95e5797 100644 --- a/src/psyclone/domain/lfric/lfric_collection.py +++ b/src/psyclone/domain/lfric/lfric_collection.py @@ -42,8 +42,8 @@ # Imports import abc -from psyclone.domain.lfric import [LFRicSymbolTable, LFRicInvoke, - LFRicKern] +from psyclone.domain.lfric import (LFRicSymbolTable, LFRicInvoke, + LFRicKern) from psyclone.errors import InternalError From 951594b476d186f69fb465a039fe713fa0deb9d9 Mon Sep 17 00:00:00 2001 From: TeranIvy Date: Mon, 24 Jul 2023 18:13:14 +0100 Subject: [PATCH 24/44] PR #2091: Added coverage for 'continue' statement --- src/psyclone/domain/lfric/lfric_kern.py | 10 ++++------ src/psyclone/tests/lfrickern_test.py | 26 ++++++++++--------------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/src/psyclone/domain/lfric/lfric_kern.py b/src/psyclone/domain/lfric/lfric_kern.py index 864ddc3959..e2cf2d2461 100644 --- a/src/psyclone/domain/lfric/lfric_kern.py +++ b/src/psyclone/domain/lfric/lfric_kern.py @@ -60,6 +60,7 @@ KernelSchedule) from psyclone.psyir.symbols import DataSymbol, ScalarType, ArrayType + class LFRicKern(CodedKern): ''' Stores information about Dynamo Kernels as specified by the Kernel metadata and associated algorithm call. Uses this @@ -905,12 +906,8 @@ def _validate_kernel_code_arg(self, kern_code_arg, interface_arg, f"dimension(s) according to the LFRic API, but " f"found {len(kern_code_arg.shape)}.") for dim_idx, kern_code_arg_dim in enumerate(kern_code_arg.shape): - #print(dim_idx) - #print(type(kern_code_arg_dim)) - #print(kern_code_arg.shape) if not isinstance(kern_code_arg_dim, ArrayType.ArrayBounds): - #print('sharks') - continue #sharks + continue if (not isinstance(kern_code_arg_dim.lower, Literal) or kern_code_arg_dim.lower.value != "1"): raise GenerationError( @@ -942,9 +939,10 @@ def _validate_kernel_code_arg(self, kern_code_arg, interface_arg, f"{info.args[0]}") from info else: raise InternalError( - f"unexpected argument type found for '{kern_code_arg.name}' in" + f"Unexpected argument type found for '{kern_code_arg.name}' in" f" kernel '{self.name}'. Expecting a scalar or an array.") + # ---------- Documentation utils -------------------------------------------- # # The list of module members that we wish AutoAPI to generate # documentation for. (See https://psyclone-ref.readthedocs.io) diff --git a/src/psyclone/tests/lfrickern_test.py b/src/psyclone/tests/lfrickern_test.py index c354f8fa8a..f3106aab97 100644 --- a/src/psyclone/tests/lfrickern_test.py +++ b/src/psyclone/tests/lfrickern_test.py @@ -311,13 +311,14 @@ def test_validate_kernel_code_arg(monkeypatch): interface=read_access) lfric_real_field_symbol2 = LFRicTypes("RealFieldDataSymbol")( "field", dims=[Reference(undf)], fs="w0", interface=read_access) - # if one of the dimensions is not a datasymbol then the arguments + # If one of the dimensions is not a datasymbol then the arguments # are not checked. kernel._validate_kernel_code_arg(lfric_real_field_symbol, lfric_real_field_symbol2) kernel._validate_kernel_code_arg(lfric_real_field_symbol2, lfric_real_field_symbol) + # Check for the correct number of array dimensions. lfric_real_field_symbol3 = LFRicTypes("RealFieldDataSymbol")( "field", dims=[Reference(undf)], fs="w0", interface=read_access) monkeypatch.setattr(lfric_real_field_symbol3.datatype, "_shape", @@ -329,19 +330,12 @@ def test_validate_kernel_code_arg(monkeypatch): "dimension(s) according to the LFRic API, but found 1." in str(info.value)) -# lfric_real_field_symbol4 = LFRicTypes("RealFieldDataSymbol")( -# "field", dims=[Reference(undf)], -# fs="w0", interface=read_access) -# lfric_real_field_symbol3 = LFRicTypes("RealFieldDataSymbol")( -# "field", dims=[ArrayType.ArrayBounds(2, Reference(undf))], -# fs="w0", interface=read_access) -# # we want to monkeypatch lfric_real_field_symbol4 so it is not -# # type ArrayType.ArrayBounds - maybe ArrayType.Extent? -# monkeypatch.setattr(lfric_real_field_symbol4.datatype, "_shape", -# [1, Reference(undf)]) -# kernel._validate_kernel_code_arg(lfric_real_field_symbol4, -# lfric_real_field_symbol3) -# #assert ("dinosaurs" in str(info.value)) + # Monkeypatch the shape of lfric_real_field_symbol3 from ArrayBounds + # to a Reference to check the 'continue' statement is triggered. + monkeypatch.setattr(lfric_real_field_symbol3.datatype, "_shape", + [Reference(undf)]) + kernel._validate_kernel_code_arg(lfric_real_field_symbol3, + lfric_real_field_symbol2) # Lower array bound of 2 rather than 1 monkeypatch.setattr(lfric_real_field_symbol3.datatype, "_shape", @@ -368,7 +362,7 @@ def test_validate_kernel_code_arg(monkeypatch): "integer number of bytes but argument 'generic_int_scalar' to kernel " "'dummy' has precision Precision.UNDEFINED" in str(info.value)) - # monkeypatch lfric_real_scalar_symbol to return that it is not a + # Monkeypatch lfric_real_scalar_symbol to return that it is not a # scalar in order to force the required exception. We do this by # changing the ScalarType as it is used when determining whether # the symbol is a scalar. @@ -377,7 +371,7 @@ def test_validate_kernel_code_arg(monkeypatch): kernel._validate_kernel_code_arg( lfric_real_scalar_symbol, lfric_real_scalar_symbol) assert ( - "unexpected argument type found for 'scalar' in kernel 'dummy'. " + "Unexpected argument type found for 'scalar' in kernel 'dummy'. " "Expecting a scalar or an array." in str(info.value)) From 7b37ac1b58bc42c4c7f47ac583248091f8c2be78 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Tue, 25 Jul 2023 17:13:27 +0100 Subject: [PATCH 25/44] #2091 moving and renaming test file --- .../tests/{lfrickern_test.py => domain/lfric/lfric_kern_test.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/psyclone/tests/{lfrickern_test.py => domain/lfric/lfric_kern_test.py} (100%) diff --git a/src/psyclone/tests/lfrickern_test.py b/src/psyclone/tests/domain/lfric/lfric_kern_test.py similarity index 100% rename from src/psyclone/tests/lfrickern_test.py rename to src/psyclone/tests/domain/lfric/lfric_kern_test.py From 06297f7728792c6d136d02ac09207c761467fe77 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Wed, 26 Jul 2023 16:55:24 +0100 Subject: [PATCH 26/44] #2091 correcting BASE_PATH in moved test file --- src/psyclone/tests/domain/lfric/lfric_kern_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/psyclone/tests/domain/lfric/lfric_kern_test.py b/src/psyclone/tests/domain/lfric/lfric_kern_test.py index 66ca8a26d6..770a0397fd 100644 --- a/src/psyclone/tests/domain/lfric/lfric_kern_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_kern_test.py @@ -58,8 +58,8 @@ from psyclone.tests.utilities import get_invoke from psyclone.transformations import Dynamo0p3ColourTrans -BASE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), - "test_files", "dynamo0p3") +BASE_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname( + os.path.abspath(__file__)))), "test_files", "dynamo0p3") TEST_API = "dynamo0.3" CODE = ''' From cf500e52680e5fb72cfce839f05c68098226ebce Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Wed, 2 Aug 2023 16:33:28 +0100 Subject: [PATCH 27/44] #2091 re-adding some tests that somehow got removed --- .../tests/domain/lfric/lfric_kern_test.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/psyclone/tests/domain/lfric/lfric_kern_test.py b/src/psyclone/tests/domain/lfric/lfric_kern_test.py index 770a0397fd..f8e6e21e07 100644 --- a/src/psyclone/tests/domain/lfric/lfric_kern_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_kern_test.py @@ -451,3 +451,33 @@ def test_kern_all_updates_are_writes(): # Change the GH_INC to be GH_WRITE. loop.kernel.args[1]._access = AccessType.WRITE assert loop.kernel.all_updates_are_writes + + +def test_kern_local_vars(): + ''' Check that the LFRicKern.local_vars() method returns the expected + names used by the Kernel that vary from one invocation to the next. + ''' + kernel = LFRicKern() + output = kernel.local_vars() + assert output == [] + + +def test_kern_not_coloured_inc(monkeypatch): + ''' Tests that there is no kernel argument with INC access when OpenMP + is applied without colouring. + ''' + _, invoke_info = parse(os.path.join(BASE_PATH, "1_single_invoke.f90"), + api=TEST_API) + psy = PSyFactory(TEST_API, distributed_memory=True).create(invoke_info) + sched = psy.invokes.invoke_list[0].schedule + kern = sched.walk(LFRicKern)[0] + # Kernel is not coloured. + assert kern.is_coloured() is False + # Monkeypatch the Kernel so that it appears to be OpenMP parallel. + monkeypatch.setattr(kern, "is_openmp_parallel", lambda: True) + assert kern.is_openmp_parallel() is True + with pytest.raises(GenerationError) as err: + _ = psy.gen + assert ("Kernel 'testkern_code' has an argument with INC access and " + "therefore must be coloured in order to be parallelised with " + "OpenMP.") From 06d3cc348faa59da85477fc56421339d975909fa Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Tue, 15 Aug 2023 16:50:33 +0100 Subject: [PATCH 28/44] #2091 catching some DynKern -> LFRicKern comments --- src/psyclone/psyad/domain/lfric/lfric_adjoint_harness.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/psyclone/psyad/domain/lfric/lfric_adjoint_harness.py b/src/psyclone/psyad/domain/lfric/lfric_adjoint_harness.py index ddfc222b35..9d9833ec17 100644 --- a/src/psyclone/psyad/domain/lfric/lfric_adjoint_harness.py +++ b/src/psyclone/psyad/domain/lfric/lfric_adjoint_harness.py @@ -518,9 +518,9 @@ def generate_lfric_adjoint_harness(tl_psyir, coord_arg_idx=None, kern = lfalg.kernel_from_metadata(parse_tree, kernel_name) # Replace generic names for fields. operators etc generated in - # DynKern with the scientific names used by the tangent-linear + # LFRicKern with the scientific names used by the tangent-linear # kernel. This makes the harness code more readable. Changing the - # names in-place within DynKern is the neatest solution given that + # names in-place within LFRicKern is the neatest solution given that # this is a legacy structure. # First raise the tangent-linear kernel PSyIR to LFRic PSyIR. This @@ -531,7 +531,7 @@ def generate_lfric_adjoint_harness(tl_psyir, coord_arg_idx=None, # Use the metadata to determine the mapping from a metadata # meta_arg index to the kernel argument index. Note, the meta_arg # index corresponds to the order of the arguments stored in - # DynKern. + # LFRicKern. index_map = ArgIndexToMetadataIndex.mapping(metadata) inv_index_map = {value: key for key, value in index_map.items()} From 7c088889dfc5cbb86f7cf8022f9b3b07914be1bf Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Tue, 15 Aug 2023 17:05:32 +0100 Subject: [PATCH 29/44] #2091 corrections from review --- src/psyclone/domain/lfric/lfric_kern.py | 6 +++--- .../tests/domain/lfric/lfric_ref_elem_stubgen_test.py | 2 +- src/psyclone/transformations.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/psyclone/domain/lfric/lfric_kern.py b/src/psyclone/domain/lfric/lfric_kern.py index e2cf2d2461..ca3faa3f1a 100644 --- a/src/psyclone/domain/lfric/lfric_kern.py +++ b/src/psyclone/domain/lfric/lfric_kern.py @@ -62,7 +62,7 @@ class LFRicKern(CodedKern): - ''' Stores information about Dynamo Kernels as specified by the + ''' Stores information about LFRic Kernels as specified by the Kernel metadata and associated algorithm call. Uses this information to generate appropriate PSy layer code for the Kernel instance or to generate a Kernel stub. @@ -262,7 +262,7 @@ def _setup(self, ktype, module_name, args, parent, check=True): KernelCall(module_name, ktype, args), parent, check) # Remove "_code" from the name if it exists to determine the - # base name which (if dynamo0.3 naming conventions are + # base name which (if LFRic naming conventions are # followed) is used as the root for the module and subroutine # names. if self.name.lower().endswith("_code"): @@ -314,7 +314,7 @@ def _setup(self, ktype, module_name, args, parent, check=True): # clashes. qr_name = "qr_"+shape.split("_")[-1] - # Dynamo 0.3 api kernels require quadrature rule arguments to be + # LFRic api kernels require quadrature rule arguments to be # passed in if one or more basis functions are used by the kernel # and gh_shape == "gh_quadrature_***". # if self._eval_shape == "gh_quadrature_xyz": diff --git a/src/psyclone/tests/domain/lfric/lfric_ref_elem_stubgen_test.py b/src/psyclone/tests/domain/lfric/lfric_ref_elem_stubgen_test.py index 88fd09b044..db432931b7 100644 --- a/src/psyclone/tests/domain/lfric/lfric_ref_elem_stubgen_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_ref_elem_stubgen_test.py @@ -42,8 +42,8 @@ import os from fparser import api as fpapi -from psyclone.dynamo0p3 import DynKernMetadata from psyclone.domain.lfric import LFRicKern +from psyclone.dynamo0p3 import DynKernMetadata # Constants diff --git a/src/psyclone/transformations.py b/src/psyclone/transformations.py index 9d5d0bc9e4..bf91d5ba98 100644 --- a/src/psyclone/transformations.py +++ b/src/psyclone/transformations.py @@ -1960,7 +1960,7 @@ def apply(self, node, options=None): is derived. :param node: a kernel node. - :type node: :py:obj:`psyclone.psygen.LFRicKern` + :type node: :py:obj:`psyclone.domain.lfric.LFRicKern` :param options: a dictionary with options for transformations. :type options: Optional[Dict[str, Any]] :param str options["cellshape"]: the shape of the cells. This is\ @@ -2117,7 +2117,7 @@ def validate(self, node, options=None): this transformation. :param node: a dynamo 0.3 kernel node. - :type node: :py:obj:`psyclone.psygen.LFRicKern` + :type node: :py:obj:`psyclone.domain.lfric.LFRicKern` :param options: a dictionary with options for transformations. :type options: Optional[Dict[str, Any]] :param str options["cellshape"]: the shape of the elements/cells. From a05eeab22cdda79fae5076d9092c435567c203d5 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Tue, 15 Aug 2023 17:31:36 +0100 Subject: [PATCH 30/44] #2091 rest of changes from review --- src/psyclone/domain/lfric/lfric_kern.py | 32 +++++++++++++------------ 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/psyclone/domain/lfric/lfric_kern.py b/src/psyclone/domain/lfric/lfric_kern.py index ca3faa3f1a..c00824da53 100644 --- a/src/psyclone/domain/lfric/lfric_kern.py +++ b/src/psyclone/domain/lfric/lfric_kern.py @@ -36,12 +36,8 @@ # Modified J. Henrichs, Bureau of Meteorology # Modified A. B. G. Chalk and N. Nobre, STFC Daresbury Lab -''' This module implements the PSyclone Dynamo 0.3 API by 1) - specialising the required base classes in parser.py (KernelType) and - adding a new class (DynFuncDescriptor03) to capture function descriptor - metadata and 2) specialising the required base classes in psyGen.py - (PSy, Invokes, Invoke, InvokeSchedule, Loop, Kern, Inf, Arguments and - Argument). ''' +''' This module implements the PSyclone LFRic API by specialising the required + base class Kern in psyGen.py ''' # Imports from collections import OrderedDict, namedtuple @@ -127,8 +123,8 @@ def reference_accesses(self, var_accesses): information about variable accesses. :type var_accesses: \ :py:class:`psyclone.core.VariablesAccessInfo` - ''' + ''' # Use the KernelCallArgList class, which can also provide variable # access information: create_arg_list = KernCallArgList(self) @@ -246,7 +242,7 @@ def _setup(self, ktype, module_name, args, parent, check=True): the source of this Kernel. :param args: list of Arg objects produced by the parser for the \ arguments of this kernel call. - :type args: list of :py:class:`psyclone.parse.algorithm.Arg` objects + :type args: List[:py:class:`psyclone.parse.algorithm.Arg`] objects :param parent: the parent of this kernel call in the generated \ AST (will be a loop object). :type parent: :py:class:`psyclone.dynamo0p3.DynLoop` @@ -369,15 +365,17 @@ def qr_rules(self): @property def cma_operation(self): - ''' Returns the type of CMA operation performed by this kernel - (one of 'assembly', 'apply' or 'matrix-matrix') or None if the - the kernel does not involve CMA operators ''' + ''' + :return: the type of CMA operation performed by this kernel + (one of 'assembly', 'apply' or 'matrix-matrix') or None + if the kernel does not involve CMA operators. + :rtype: str + ''' return self._cma_operation @property def is_intergrid(self): ''' - Getter for whether or not this is an inter-grid kernel call :return: True if it is an inter-grid kernel, False otherwise :rtype: bool ''' @@ -482,9 +480,13 @@ def ncolours_var(self): @property def fs_descriptors(self): - ''' Returns a list of function space descriptor objects of - type FSDescriptor which contain information about the function - spaces. ''' + ''' + :return: a list of function space descriptor objects of + type FSDescriptor which contain information about + the function spaces. + :rtype: list of str + + ''' return self._fs_descriptors @property From 4a5afc07bc6cfa5fda15cc184e2af3727b68265a Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Wed, 16 Aug 2023 13:25:36 +0100 Subject: [PATCH 31/44] #2091 removing local_vars from LFRicKern --- src/psyclone/domain/lfric/lfric_kern.py | 6 ------ src/psyclone/tests/domain/lfric/lfric_kern_test.py | 9 --------- 2 files changed, 15 deletions(-) diff --git a/src/psyclone/domain/lfric/lfric_kern.py b/src/psyclone/domain/lfric/lfric_kern.py index c00824da53..d3c7c58be5 100644 --- a/src/psyclone/domain/lfric/lfric_kern.py +++ b/src/psyclone/domain/lfric/lfric_kern.py @@ -551,12 +551,6 @@ def all_updates_are_writes(self): all_writes.remove(AccessType.WRITE) return (not accesses.intersection(set(all_writes))) - def local_vars(self): - ''' Returns the names used by the Kernel that vary from one - invocation to the next and therefore require privatisation - when parallelised. ''' - return [] - @property def base_name(self): ''' diff --git a/src/psyclone/tests/domain/lfric/lfric_kern_test.py b/src/psyclone/tests/domain/lfric/lfric_kern_test.py index f8e6e21e07..cf3116d37d 100644 --- a/src/psyclone/tests/domain/lfric/lfric_kern_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_kern_test.py @@ -453,15 +453,6 @@ def test_kern_all_updates_are_writes(): assert loop.kernel.all_updates_are_writes -def test_kern_local_vars(): - ''' Check that the LFRicKern.local_vars() method returns the expected - names used by the Kernel that vary from one invocation to the next. - ''' - kernel = LFRicKern() - output = kernel.local_vars() - assert output == [] - - def test_kern_not_coloured_inc(monkeypatch): ''' Tests that there is no kernel argument with INC access when OpenMP is applied without colouring. From ecd83a6757995588d869e857b795c8fba5bb5a56 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Tue, 19 Sep 2023 14:10:36 +0100 Subject: [PATCH 32/44] #2091 correcting docstring for fs_descriptors --- src/psyclone/domain/lfric/lfric_kern.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/psyclone/domain/lfric/lfric_kern.py b/src/psyclone/domain/lfric/lfric_kern.py index d3c7c58be5..419adf86ba 100644 --- a/src/psyclone/domain/lfric/lfric_kern.py +++ b/src/psyclone/domain/lfric/lfric_kern.py @@ -482,9 +482,9 @@ def ncolours_var(self): def fs_descriptors(self): ''' :return: a list of function space descriptor objects of - type FSDescriptor which contain information about + type FSDescriptors which contain information about the function spaces. - :rtype: list of str + :rtype: list of :py:class:`psyclone.FSDescriptors`. ''' return self._fs_descriptors From 430e0a297a324a321bcd34add0513a3499a40e68 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Mon, 25 Sep 2023 09:01:35 +0100 Subject: [PATCH 33/44] #2091 moving DynKern in __init__.py --- src/psyclone/domain/lfric/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psyclone/domain/lfric/__init__.py b/src/psyclone/domain/lfric/__init__.py index 1c3fe90f3e..5d4f262a10 100644 --- a/src/psyclone/domain/lfric/__init__.py +++ b/src/psyclone/domain/lfric/__init__.py @@ -61,10 +61,10 @@ MetadataToArgumentsRules from psyclone.domain.lfric.arg_index_to_metadata_index import \ ArgIndexToMetadataIndex -from psyclone.domain.lfric.lfric_kern import LFRicKern from psyclone.domain.lfric.lfric_kern_call_factory import LFRicKernCallFactory from psyclone.domain.lfric.lfric_collection import LFRicCollection from psyclone.domain.lfric.lfric_loop_bounds import LFRicLoopBounds +from psyclone.domain.lfric.lfric_kern import LFRicKern __all__ = [ From e6175f9babf9e92e52bf9f332b76848aff3d0556 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Mon, 25 Sep 2023 11:56:21 +0100 Subject: [PATCH 34/44] #2091 replacing accidentally removed LFRicKern import from merge and moving LFRicKern line in __init__.py back to original position --- src/psyclone/domain/lfric/__init__.py | 2 +- src/psyclone/dynamo0p3.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/psyclone/domain/lfric/__init__.py b/src/psyclone/domain/lfric/__init__.py index 1d0c636422..159c3fae8e 100644 --- a/src/psyclone/domain/lfric/__init__.py +++ b/src/psyclone/domain/lfric/__init__.py @@ -61,11 +61,11 @@ MetadataToArgumentsRules from psyclone.domain.lfric.arg_index_to_metadata_index import \ ArgIndexToMetadataIndex +from psyclone.domain.lfric.lfric_kern import LFRicKern from psyclone.domain.lfric.lfric_kern_call_factory import LFRicKernCallFactory from psyclone.domain.lfric.lfric_collection import LFRicCollection from psyclone.domain.lfric.lfric_scalar_args import LFRicScalarArgs from psyclone.domain.lfric.lfric_loop_bounds import LFRicLoopBounds -from psyclone.domain.lfric.lfric_kern import LFRicKern __all__ = [ diff --git a/src/psyclone/dynamo0p3.py b/src/psyclone/dynamo0p3.py index b650ee6e93..f374d56bf6 100644 --- a/src/psyclone/dynamo0p3.py +++ b/src/psyclone/dynamo0p3.py @@ -61,7 +61,8 @@ LFRicArgDescriptor, KernelInterface, LFRicCollection, LFRicConstants, LFRicSymbolTable, LFRicInvoke, - LFRicKernCallFactory, LFRicScalarArgs) + LFRicKernCallFactory, LFRicScalarArgs, + LFRicKern) from psyclone.errors import GenerationError, InternalError, FieldNotFoundError from psyclone.f2pygen import (AllocateGen, AssignGen, CallGen, CommentGen, DeallocateGen, DeclGen, DoGen, IfThenGen, From 7853810b0c693e9f7a75085ef9d04a8f38e97868 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Mon, 30 Oct 2023 14:47:18 +0000 Subject: [PATCH 35/44] #2091 correcting wonky merging --- src/psyclone/tests/dynamo0p3_test.py | 4 ++-- src/psyclone/transformations.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/psyclone/tests/dynamo0p3_test.py b/src/psyclone/tests/dynamo0p3_test.py index ccd1ae08d8..6fdbd45f7b 100644 --- a/src/psyclone/tests/dynamo0p3_test.py +++ b/src/psyclone/tests/dynamo0p3_test.py @@ -1485,7 +1485,7 @@ def test_dynkernelargument_psyir_expression(monkeypatch): api=TEST_API) psy = PSyFactory(TEST_API, distributed_memory=True).create(invoke_info) first_invoke = psy.invokes.invoke_list[0] - kern = first_invoke.schedule.walk(DynKern)[0] + kern = first_invoke.schedule.walk(LFRicKern)[0] psyir = kern.arguments.args[0].psyir_expression() assert isinstance(psyir, Reference) assert psyir.symbol.name == "mm_w0_local_stencil" @@ -1498,7 +1498,7 @@ def test_dynkernelargument_psyir_expression(monkeypatch): api=TEST_API) psy = PSyFactory(TEST_API, distributed_memory=True).create(invoke_info) first_invoke = psy.invokes.invoke_list[0] - kern = first_invoke.schedule.walk(DynKern)[0] + kern = first_invoke.schedule.walk(LFRicKern)[0] psyir = kern.arguments.args[1].psyir_expression() assert isinstance(psyir, Reference) assert psyir.symbol.name == "cma_op1_cma_matrix" diff --git a/src/psyclone/transformations.py b/src/psyclone/transformations.py index 4930745c41..9f3dc17430 100644 --- a/src/psyclone/transformations.py +++ b/src/psyclone/transformations.py @@ -51,7 +51,7 @@ from psyclone.core import Signature, VariablesAccessInfo from psyclone.domain.lfric import KernCallArgList, LFRicConstants, LFRicKern from psyclone.dynamo0p3 import LFRicHaloExchangeEnd, LFRicHaloExchangeStart, \ - DynInvokeSchedule + DynInvokeSchedule, DynLoop from psyclone.errors import InternalError from psyclone.gocean1p0 import GOInvokeSchedule from psyclone.nemo import NemoInvokeSchedule From 882d8902f8b33966979f7450c39dddaa15f72eb6 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Mon, 30 Oct 2023 14:56:42 +0000 Subject: [PATCH 36/44] #2091 more wonky merging --- src/psyclone/dynamo0p3.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/psyclone/dynamo0p3.py b/src/psyclone/dynamo0p3.py index c429719754..2f42806445 100644 --- a/src/psyclone/dynamo0p3.py +++ b/src/psyclone/dynamo0p3.py @@ -79,7 +79,8 @@ Routine, ScopingNode) from psyclone.psyir.symbols import (INTEGER_TYPE, DataSymbol, ScalarType, DeferredType, DataTypeSymbol, - ContainerSymbol, ImportInterface) + ContainerSymbol, ImportInterface, + ArrayType, UnknownFortranType) # pylint: disable=too-many-lines # --------------------------------------------------------------------------- # From f82f1ad029b47b33898dc990061c25de87d4a76b Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Mon, 30 Oct 2023 17:10:13 +0000 Subject: [PATCH 37/44] #2091 hopefully the last of the wonky merging --- src/psyclone/domain/lfric/lfric_kern.py | 51 ++++++++++++++++++------- src/psyclone/dynamo0p3.py | 6 +-- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/psyclone/domain/lfric/lfric_kern.py b/src/psyclone/domain/lfric/lfric_kern.py index 419adf86ba..7d547dfd79 100644 --- a/src/psyclone/domain/lfric/lfric_kern.py +++ b/src/psyclone/domain/lfric/lfric_kern.py @@ -254,9 +254,9 @@ def _setup(self, ktype, module_name, args, parent, check=True): # pylint: disable=import-outside-toplevel from psyclone.dynamo0p3 import DynKernelArguments, FSDescriptors # pylint: disable=too-many-branches, too-many-locals - CodedKern.__init__(self, DynKernelArguments, - KernelCall(module_name, ktype, args), - parent, check) + super().__init__(DynKernelArguments, + KernelCall(module_name, ktype, args), + parent, check) # Remove "_code" from the name if it exists to determine the # base name which (if LFRic naming conventions are # followed) is used as the root for the module and subroutine @@ -385,20 +385,18 @@ def is_intergrid(self): def colourmap(self): ''' Getter for the name of the colourmap associated with this kernel call. - :returns: name of the colourmap (Fortran array). :rtype: str - :raises InternalError: if this kernel is not coloured or the \ dictionary of inter-grid kernels and \ colourmaps has not been constructed. - ''' if not self.is_coloured(): raise InternalError(f"Kernel '{self.name}' is not inside a " f"coloured loop.") + sched = self.ancestor(InvokeSchedule) if self._is_intergrid: - invoke = self.ancestor(InvokeSchedule).invoke + invoke = sched.invoke if id(self) not in invoke.meshes.intergrid_kernels: raise InternalError( f"Colourmap information for kernel '{self.name}' has " @@ -406,7 +404,14 @@ def colourmap(self): cmap = invoke.meshes.intergrid_kernels[id(self)].\ colourmap_symbol.name else: - cmap = self.scope.symbol_table.lookup_with_tag("cmap").name + try: + cmap = sched.symbol_table.lookup_with_tag("cmap").name + except KeyError: + # We have to do this here as _init_colourmap (which calls this + # method) is only called at code-generation time. + cmap = sched.symbol_table.find_or_create_array( + "cmap", 2, ScalarType.Intrinsic.INTEGER, + tag="cmap").name return cmap @@ -415,10 +420,8 @@ def last_cell_all_colours_symbol(self): ''' Getter for the symbol of the array holding the index of the last cell of each colour. - :returns: name of the array. :rtype: str - :raises InternalError: if this kernel is not coloured or the \ dictionary of inter-grid kernels and \ colourmaps has not been constructed. @@ -426,19 +429,20 @@ def last_cell_all_colours_symbol(self): if not self.is_coloured(): raise InternalError(f"Kernel '{self.name}' is not inside a " f"coloured loop.") + if self._is_intergrid: invoke = self.ancestor(InvokeSchedule).invoke if id(self) not in invoke.meshes.intergrid_kernels: raise InternalError( f"Colourmap information for kernel '{self.name}' has " f"not yet been initialised") - return invoke.meshes.intergrid_kernels[id(self)].\ - last_cell_var_symbol + return (invoke.meshes.intergrid_kernels[id(self)]. + last_cell_var_symbol) + ubnd_name = self.ancestor(Loop).upper_bound_name const = LFRicConstants() - if (self.ancestor(Loop).upper_bound_name in - const.HALO_ACCESS_LOOP_BOUNDS): + if (ubnd_name in const.HALO_ACCESS_LOOP_BOUNDS): return self.scope.symbol_table.find_or_create_array( "last_halo_cell_all_colours", 2, ScalarType.Intrinsic.INTEGER, @@ -729,6 +733,25 @@ class creates the PSyIR schedule on first invocation which is # Get the PSyIR Kernel Schedule(s) routines = Fparser2Reader().get_routine_schedules(self.name, self.ast) + for routine in routines: + # If one of the symbols is not declared in a routine then + # this is only picked up when writing out the routine + # (raising a VisitorError), so we check here so that + # invalid code is not inlined. We use debug_string() to + # minimise the overhead. + + # TODO #2271 could potentially avoid the need for + # debug_string() within. Sergi suggests that we may be + # missing the traversal of the declaration init + # expressions and that might solve the problem. I'm not so + # sure as we are talking about unknown symbols that will + # only be resolved in the back-end (or not). If I am right + # then one option would be to use the FortranWriter, but + # that would be bigger overhead, or perhaps just the + # declarations part of FortranWriter if that is possible. + # Also see TODO issue #2336 which captures the specific + # problem in LFRic that this fixes. + routine.debug_string() if len(routines) == 1: sched = routines[0] diff --git a/src/psyclone/dynamo0p3.py b/src/psyclone/dynamo0p3.py index 2f42806445..96ef677d83 100644 --- a/src/psyclone/dynamo0p3.py +++ b/src/psyclone/dynamo0p3.py @@ -57,11 +57,9 @@ LFRicBuiltIn, BUILTIN_MAP) from psyclone.domain.common.psylayer import PSyLoop from psyclone.domain.lfric import (FunctionSpace, KernCallAccArgList, - KernCallArgList, KernStubArgList, - LFRicArgDescriptor, KernelInterface, + KernCallArgList, LFRicArgDescriptor, LFRicCollection, LFRicConstants, - LFRicSymbolTable, LFRicInvoke, - LFRicKernCallFactory, LFRicScalarArgs, + LFRicSymbolTable, LFRicKernCallFactory, LFRicKern, LFRicInvokes, LFRicTypes) from psyclone.errors import GenerationError, InternalError, FieldNotFoundError from psyclone.f2pygen import (AllocateGen, AssignGen, CallGen, CommentGen, From ba8b6dd237acc118c96ec540bfec0881e487e27a Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Mon, 30 Oct 2023 17:39:56 +0000 Subject: [PATCH 38/44] #2091 even more wonky merging --- src/psyclone/domain/lfric/lfric_kern.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/psyclone/domain/lfric/lfric_kern.py b/src/psyclone/domain/lfric/lfric_kern.py index 7d547dfd79..96ccd06e73 100644 --- a/src/psyclone/domain/lfric/lfric_kern.py +++ b/src/psyclone/domain/lfric/lfric_kern.py @@ -609,10 +609,11 @@ def gen_stub(self): # pylint: disable=import-outside-toplevel from psyclone.dynamo0p3 import (DynCellIterators, DynDofmaps, DynFunctionSpaces, DynCMAOperators, - LFRicScalarArgs, DynBoundaryConditions, + DynBoundaryConditions, DynLMAOperators, LFRicMeshProperties, DynBasisFunctions, LFRicFields, DynReferenceElement, DynStencils) + from psyclone.domain.lfric import LFRicScalarArgs for entities in [DynCellIterators, DynDofmaps, DynFunctionSpaces, DynCMAOperators, LFRicScalarArgs, LFRicFields, DynLMAOperators, DynStencils, DynBasisFunctions, From 0babc06e2a46821c352ee6207eb414a22662704e Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Mon, 30 Oct 2023 18:02:36 +0000 Subject: [PATCH 39/44] #2091: still some more wonky merging --- src/psyclone/domain/lfric/lfric_scalar_args.py | 2 +- .../tests/domain/lfric/dyn_proxies_test.py | 4 ++-- .../domain/lfric/kern_call_acc_arg_list_test.py | 14 +++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/psyclone/domain/lfric/lfric_scalar_args.py b/src/psyclone/domain/lfric/lfric_scalar_args.py index b635a85f51..889707ae43 100644 --- a/src/psyclone/domain/lfric/lfric_scalar_args.py +++ b/src/psyclone/domain/lfric/lfric_scalar_args.py @@ -62,7 +62,7 @@ class LFRicScalarArgs(LFRicCollection): :param node: the Invoke or Kernel stub for which to manage the scalar \ arguments. - :type node: :py:class:`psyclone.dynamo0p3.DynKern` or \ + :type node: :py:class:`psyclone.domain.lfric.LFRicKern` or \ :py:class:`psyclone.domain.lfric.LFRicInvoke` ''' diff --git a/src/psyclone/tests/domain/lfric/dyn_proxies_test.py b/src/psyclone/tests/domain/lfric/dyn_proxies_test.py index 86ff74611f..4b74eae707 100644 --- a/src/psyclone/tests/domain/lfric/dyn_proxies_test.py +++ b/src/psyclone/tests/domain/lfric/dyn_proxies_test.py @@ -38,7 +38,7 @@ import os import pytest from psyclone.domain.lfric import LFRicConstants -from psyclone.dynamo0p3 import DynKern, DynProxies +from psyclone.dynamo0p3 import LFRicKern, DynProxies from psyclone.errors import InternalError from psyclone.f2pygen import ModuleGen, SubroutineGen from psyclone.parse.algorithm import parse @@ -127,7 +127,7 @@ def test_initialise_errors(monkeypatch): api=TEST_API) psy = PSyFactory(TEST_API, distributed_memory=True).create(info) invoke = psy.invokes.invoke_list[0] - kern = invoke.schedule.walk(DynKern)[0] + kern = invoke.schedule.walk(LFRicKern)[0] proxies = DynProxies(invoke) amod = ModuleGen("test_mod") node = SubroutineGen(amod, name="a_sub") diff --git a/src/psyclone/tests/domain/lfric/kern_call_acc_arg_list_test.py b/src/psyclone/tests/domain/lfric/kern_call_acc_arg_list_test.py index aca2c82d89..1a783423eb 100644 --- a/src/psyclone/tests/domain/lfric/kern_call_acc_arg_list_test.py +++ b/src/psyclone/tests/domain/lfric/kern_call_acc_arg_list_test.py @@ -42,8 +42,8 @@ import pytest from psyclone.core import VariablesAccessInfo -from psyclone.domain.lfric import FunctionSpace, KernCallAccArgList -from psyclone.dynamo0p3 import DynKern +from psyclone.domain.lfric import (FunctionSpace, KernCallAccArgList, + LFRicKern) from psyclone.errors import InternalError from psyclone.parse.algorithm import parse from psyclone import psyGen @@ -57,14 +57,14 @@ def test_acc_arg_list_cell_map(dist_mem, monkeypatch): '''Test the cell_map() method.''' - # We need a DynKern in order to construct an instance of KernCallAccArgList + # We need a LFRicKern in order to construct an instance of KernCallAccArgList _, invoke_info = parse(os.path.join(BASE_PATH, "22.0_intergrid_prolong.f90"), api=TEST_API) psy = psyGen.PSyFactory(TEST_API, distributed_memory=dist_mem).create(invoke_info) schedule = psy.invokes.invoke_list[0].schedule - kern = schedule.walk(DynKern)[0] + kern = schedule.walk(LFRicKern)[0] arg_list = KernCallAccArgList(kern) # Check that the cell_map method works as expected. arg_list.cell_map() @@ -85,7 +85,7 @@ def test_stencil_2d(): name="invoke_0_testkern_stencil_multi_type", dist_mem=False) sched = invoke.schedule - kern = sched.walk(DynKern)[0] + kern = sched.walk(LFRicKern)[0] arg_list = KernCallAccArgList(kern) arg_list.stencil_2d(kern.arguments._args[1]) # This should result in the whole stencil dofmap being added as an arg. @@ -102,7 +102,7 @@ def test_fs_compulsory_field_no_cell_column(): name="invoke_0_testkern_domain_type", dist_mem=False) sched = invoke.schedule - kern = sched.walk(DynKern)[0] + kern = sched.walk(LFRicKern)[0] arg_list = KernCallAccArgList(kern) fspace = FunctionSpace("w3", kern.arguments) arg_list.fs_compulsory_field(fspace) @@ -114,7 +114,7 @@ def test_fs_intergrid(): _, invoke = get_invoke("22.2_intergrid_3levels.f90", TEST_API, name="invoke_0", dist_mem=False) sched = invoke.schedule - kernels = sched.walk(DynKern) + kernels = sched.walk(LFRicKern) prolong_kern = kernels[0] restrict_kern = kernels[2] fspace = FunctionSpace("any_space_1", restrict_kern.arguments) From e59a8f15b9d5dbb9f3bcb63657797661c5de646a Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Tue, 31 Oct 2023 10:49:59 +0000 Subject: [PATCH 40/44] #2091 still on the wonky merging --- src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py | 4 ++-- src/psyclone/tests/dynamo0p3_test.py | 5 +---- src/psyclone/transformations.py | 1 - 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py b/src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py index f48dc5a1a1..3a4f83100e 100644 --- a/src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py @@ -48,8 +48,8 @@ from fparser import api as fpapi from psyclone.domain.lfric import (LFRicKern, LFRicArgDescriptor, - LFRicScalarArgs) -from psyclone.dynamo0p3 import DynKernMetadata, LFRicConstants + LFRicScalarArgs, LFRicConstants) +from psyclone.dynamo0p3 import DynKernMetadata from psyclone.errors import InternalError, GenerationError from psyclone.f2pygen import ModuleGen from psyclone.parse.algorithm import parse diff --git a/src/psyclone/tests/dynamo0p3_test.py b/src/psyclone/tests/dynamo0p3_test.py index 6fdbd45f7b..e28acb5012 100644 --- a/src/psyclone/tests/dynamo0p3_test.py +++ b/src/psyclone/tests/dynamo0p3_test.py @@ -3671,11 +3671,8 @@ def test_haloex_not_required(monkeypatch): def test_lfriccollection_err1(): ''' Check that the LFRicCollection constructor raises the expected error if it is not provided with an LFRicKern or LFRicInvoke. ''' - _, info = parse(os.path.join(BASE_PATH, "1_single_invoke.f90"), - api=TEST_API) - psy = PSyFactory(TEST_API, distributed_memory=True).create(info) with pytest.raises(InternalError) as err: - _ = DynProxies(psy) + _ = DynProxies(None) assert ("LFRicCollection takes only an LFRicInvoke or an LFRicKern but" in str(err.value)) diff --git a/src/psyclone/transformations.py b/src/psyclone/transformations.py index 9f3dc17430..37ec69faec 100644 --- a/src/psyclone/transformations.py +++ b/src/psyclone/transformations.py @@ -47,7 +47,6 @@ from psyclone import psyGen from psyclone.configuration import Config -from psyclone.domain.lfric import KernCallArgList, LFRicConstants from psyclone.core import Signature, VariablesAccessInfo from psyclone.domain.lfric import KernCallArgList, LFRicConstants, LFRicKern from psyclone.dynamo0p3 import LFRicHaloExchangeEnd, LFRicHaloExchangeStart, \ From 990cf828b0f7f4fc662ecede94333223d03c0e97 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Tue, 31 Oct 2023 11:01:34 +0000 Subject: [PATCH 41/44] #2091 some tidying up and more wonky merge corrections --- src/psyclone/tests/domain/lfric/dyn_proxies_test.py | 5 +++-- .../tests/domain/lfric/kern_call_acc_arg_list_test.py | 7 ++++--- src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py | 1 - 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/psyclone/tests/domain/lfric/dyn_proxies_test.py b/src/psyclone/tests/domain/lfric/dyn_proxies_test.py index 4b74eae707..351480e7ca 100644 --- a/src/psyclone/tests/domain/lfric/dyn_proxies_test.py +++ b/src/psyclone/tests/domain/lfric/dyn_proxies_test.py @@ -32,13 +32,14 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Author A. R. Porter, STFC Daresbury Lab +# Modified L. Turner, Met Office ''' This module tests the DynProxies class using pytest. ''' import os import pytest -from psyclone.domain.lfric import LFRicConstants -from psyclone.dynamo0p3 import LFRicKern, DynProxies +from psyclone.domain.lfric import LFRicConstants, LFRicKern +from psyclone.dynamo0p3 import DynProxies from psyclone.errors import InternalError from psyclone.f2pygen import ModuleGen, SubroutineGen from psyclone.parse.algorithm import parse diff --git a/src/psyclone/tests/domain/lfric/kern_call_acc_arg_list_test.py b/src/psyclone/tests/domain/lfric/kern_call_acc_arg_list_test.py index 1a783423eb..8b4d87eb68 100644 --- a/src/psyclone/tests/domain/lfric/kern_call_acc_arg_list_test.py +++ b/src/psyclone/tests/domain/lfric/kern_call_acc_arg_list_test.py @@ -31,9 +31,10 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- -# Authors: J. Henrichs, Bureau of Meteorology -# R. W. Ford and A. R. Porter, STFC Daresbury Lab -# I. Kavcic, Met Office +# Authors: J. Henrichs, Bureau of Meteorology +# R. W. Ford and A. R. Porter, STFC Daresbury Lab +# I. Kavcic, Met Office +# Modified: L. Turner, Met Office ''' This module tests the LFric KernCallAccArgList class.''' diff --git a/src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py b/src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py index 3a4f83100e..b5bdde2214 100644 --- a/src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py @@ -35,7 +35,6 @@ # I. Kavcic, A. Coughtrie, L. Turner and O. Brunt, Met Office; # C. M. Maynard, Met Office/University of Reading; # J. Henrichs, Bureau of Meteorology. -# Modified by L. Turner, Met Office ''' Module containing pytest tests for the general LFRic scalar arguments From dba68599af5b9cc61a9d38f4d462012398acc1ec Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Wed, 8 Nov 2023 11:18:09 +0000 Subject: [PATCH 42/44] #2091: hopefully fixing merge bug --- src/psyclone/domain/lfric/lfric_kern.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/psyclone/domain/lfric/lfric_kern.py b/src/psyclone/domain/lfric/lfric_kern.py index 96ccd06e73..cf0305c528 100644 --- a/src/psyclone/domain/lfric/lfric_kern.py +++ b/src/psyclone/domain/lfric/lfric_kern.py @@ -547,9 +547,6 @@ def all_updates_are_writes(self): :rtype: bool ''' - if self.is_intergrid: - # This is not a special kernel - return False accesses = set(arg.access for arg in self.args) all_writes = AccessType.all_write_accesses() all_writes.remove(AccessType.WRITE) From f3fcdd7d81463f92a2761ce58abaedbfbeaa9df0 Mon Sep 17 00:00:00 2001 From: Lottie Turner Date: Wed, 8 Nov 2023 16:40:26 +0000 Subject: [PATCH 43/44] #2091: final touches on review comments --- src/psyclone/domain/lfric/lfric_kern.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/psyclone/domain/lfric/lfric_kern.py b/src/psyclone/domain/lfric/lfric_kern.py index cf0305c528..88d188bb58 100644 --- a/src/psyclone/domain/lfric/lfric_kern.py +++ b/src/psyclone/domain/lfric/lfric_kern.py @@ -82,8 +82,8 @@ def __init__(self): # pylint: disable=super-init-not-called # Import here to avoid circular dependency # pylint: disable=import-outside-toplevel - from psyclone.dynamo0p3 import DynKernelArguments if False: # pylint: disable=using-constant-test + from psyclone.dynamo0p3 import DynKernelArguments self._arguments = DynKernelArguments(None, None) # for pyreverse self._parent = None self._base_name = "" @@ -242,7 +242,7 @@ def _setup(self, ktype, module_name, args, parent, check=True): the source of this Kernel. :param args: list of Arg objects produced by the parser for the \ arguments of this kernel call. - :type args: List[:py:class:`psyclone.parse.algorithm.Arg`] objects + :type args: List[:py:class:`psyclone.parse.algorithm.Arg`] :param parent: the parent of this kernel call in the generated \ AST (will be a loop object). :type parent: :py:class:`psyclone.dynamo0p3.DynLoop` @@ -385,11 +385,14 @@ def is_intergrid(self): def colourmap(self): ''' Getter for the name of the colourmap associated with this kernel call. + :returns: name of the colourmap (Fortran array). :rtype: str + :raises InternalError: if this kernel is not coloured or the \ dictionary of inter-grid kernels and \ colourmaps has not been constructed. + ''' if not self.is_coloured(): raise InternalError(f"Kernel '{self.name}' is not inside a " @@ -420,8 +423,10 @@ def last_cell_all_colours_symbol(self): ''' Getter for the symbol of the array holding the index of the last cell of each colour. + :returns: name of the array. :rtype: str + :raises InternalError: if this kernel is not coloured or the \ dictionary of inter-grid kernels and \ colourmaps has not been constructed. @@ -488,7 +493,7 @@ def fs_descriptors(self): :return: a list of function space descriptor objects of type FSDescriptors which contain information about the function spaces. - :rtype: list of :py:class:`psyclone.FSDescriptors`. + :rtype: List[:py:class:`psyclone.FSDescriptors`]. ''' return self._fs_descriptors @@ -604,13 +609,13 @@ def gen_stub(self): # Add all the declarations # Import here to avoid circular dependency # pylint: disable=import-outside-toplevel + from psyclone.domain.lfric import LFRicScalarArgs from psyclone.dynamo0p3 import (DynCellIterators, DynDofmaps, DynFunctionSpaces, DynCMAOperators, DynBoundaryConditions, DynLMAOperators, LFRicMeshProperties, DynBasisFunctions, LFRicFields, DynReferenceElement, DynStencils) - from psyclone.domain.lfric import LFRicScalarArgs for entities in [DynCellIterators, DynDofmaps, DynFunctionSpaces, DynCMAOperators, LFRicScalarArgs, LFRicFields, DynLMAOperators, DynStencils, DynBasisFunctions, From eac784ad01cb290713ba66466a4ce19bde1275c9 Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Thu, 9 Nov 2023 12:39:19 +0000 Subject: [PATCH 44/44] #2100 update changelog and UG --- changelog | 3 +++ psyclone.pdf | Bin 1423707 -> 1423723 bytes 2 files changed, 3 insertions(+) diff --git a/changelog b/changelog index ba7a32a785..163fe7da1f 100644 --- a/changelog +++ b/changelog @@ -64,6 +64,9 @@ 24) PR #2383 towards #2256. Adds support for 'DO CONCURRENT' in the fparser2 frontend. + 25) PR #2100 for #2091. Renames DynKern to LFRicKern and moves its + implementation to domain/lfric. + release 2.4.0 29th of September 2023 1) PR #1758 for #1741. Splits the PSyData read functionality into a diff --git a/psyclone.pdf b/psyclone.pdf index 77aa124a05c3168421fd7264cee08fd8d1aae466..fd4711731e3e0077c6867c3ea410bbb8fdab1c5d 100644 GIT binary patch delta 21405 zcmagFbyQp36E=#wyA*eqV#VFv9f}4h?i$=F?(R^mxVsdJ1$UR?P~5rc`}^*{z2})ZPm;B3?bB=Rz);9gs8Hxom{8bIxF&c_n)IY8Lb(6vy`DAqyey`lw-mku_v3a`Y z`;HeeF8J;IihAhMzi0*i`1U6TG``x!n2+(A%BZ=RAtsDWkl)+={(dsOq|m9+eyZcpk(z zNjGQfO33EZeBEWHG<6y+JcyXL<&7qif~7~$%yi66o%A7fs`~*s(9J^ZER3-zF3F*g z1R7Pyqo}iuxSv-9I#EK?zBO4(CX>M%hv$O~4is`SF>^iVJ+oVhPg=aV+x{Q~e7des z>iBvi)n|TbJ>FTNL($Vgyiuxda7Ku zr<4(4S@YJOSI^EMn&=?lMHxN9qq*!7iHN=!T{VkiGUgu%&71g|7 z*kD1gvu<(~*vZIdAg}_JSiXhX&bfTuKu35t8IhG|nh_HC)y=+}m8tSh`QX6U_Cj>5 z34#|{l()WQ&2+=m>eBtnGNPV4l^N%lc^C0!$CtSWjzJ-ZK%ftE(2!sPij-uq-&%M? zXNPPX0eVa_Nf0gSE1a{WpkP=g^o^x+umfr>1z(6Tn0QwpP+9|w}+9=+TV2yxG-ULstH5<+399d;FKQ%oV6mf!VpGb z$k>cQS^PIL#h*lde0oVaP9oB3M%noTm!pCnh6%BSG}<}5 zr}qCwr(+w;2^XF~&3-5IB|EI>Gx=NRqgPw?Yixd0p);Swb=vYtEScQ^Ke|Ta-HQRb zFY=IUjXwc$YU%DFOo_DXPR#<91ncfI^{trh3XtHmL0hk?ISwxsg#~F2Nk8>UI7~E) zChJPN=02<=a3_y>2U^b!E3yVAOJ>KnZZ&xwp~Yrk0~?M);VYR#i@HhP8C9|Tn6>k~ zy_(GGcOmz|X=wR z1<73870jJHx%$2x1fD)L@X?`4rDKiMmMNAB^)Fyc2xCO9Ua}OJAaJaTG$kN=i)OS- zRy=R9kQ%J>Er&$?X!(Wig*HQSp2HfxZ4R_NaP_6U5BV|vt|`G^PLw{*UK4Wb%sr#l zP=jj{`>Iimrpep?CCrU_0psq+X--u=t%O%`|;p36otTx5!~Z|p>B$gzQD zrOro?p|nv6@p3(G3uQDbjX>AGNEdzIrdhijq%BsPZV>(M#2D0KrtlCtYTw!L5z?n) z>27LVCv&H``aZSGYY|{IdJbEQ?N8-(3b>C8wG7b`2Imi>)`zq|K^M(B-^ezbE8#D5 zl;35MkhqvSY_Ic6B^pT6#nJ@;qqu?IKpJ%nJ)LhNnWE4N_qsoFd#L(}womg91CBB~ zEHwKgFk@z;u-?CVV#QEf^^!x*1@Bas4D-~)BTQs4lz35aA8UQ<-_g>gKbqX10HR%VGn&Zbp zqjx&e4AWOMQr*TKP|~X7a!>rJgZw!7a2+1l*)hp6plcKMPm#LSft zM`fLKSg08^b+l(op$MlRxM>VTz8YM&9=7_D+=Y&J{3VR69rC-SV+2vG0(0q1enSH{ zwnD^&#R(i^3$H3RRQrT~%E*bKfpIc~cvszlf)>ss?OITlpS`?(k1#y;c@5?ZAiBM+ zxcvbUM0f!zk|NOi8V0S073$C?;h1fw<;`8m+{73oSTU}Zm>gRU!-D{Xa4THWudGnE zVE1ixJXi6pbC7!5i_v*X$})(oKg_!FC{_eISZ8s^=B6h%yszBlLj*4cv{Kdn#1M^? zhCXaxCoU7ygl%w^d6VjyG+-(0<0C+M|0UATOQHKQzD=+6P{t8={Nfs}7w^laFd%Lg zwxi%fj+GJU+$PG~;Hm~B5-1EFTw9xO?^bHQ;eXHYA(k+Ezx?s7F*tB!N*z?u!jPof zGXaLON-Z1PU34zl)XL`cd176Tpm$lasKinJgwMru0Z9I*{EdzXP2bbAN)gMlfB3c} zW_Rw3A)M4$T6T%hjcA`yMY%nO(|%3FkwEr+#U^?p6e_H2(K7}%m>K<1DANiuxT@r& zOLz``9>vaz6;4in*A?{NUr$&f5ldJPDSm?bEB2$LhmN9<$w4j>M zkU>+ ztPWqwtCZjf&OLFGa6n{=)XSD+I3+3mwr5AOe0R^g!$BH_|xVYDrGPUj} zjnoUaYP${`>BNrFKs`mlo{Z=>o`%lKjYaELVp-Y*QJz8S1YG5TpWa_JC)h~x>B$b{rpWMS&yoQN+1N>YJqfWMMFksuk5DZ~7O+IL$ESxr^ASA| z9+<(g^+dD40^1i>w$GfTFserl#m&~4)}aFtMjlz;l}Hwt3T=seBH*O0Y&Lrn2V2UJ z`F&MH0n+(X*iNDMFrglKptx8xAu;qbK}evU3c{Cppj~jG`Dq;m=4nNxQRL3+{`(!=gmzIJuZs8sWs z#Wd|wLwq}(hv|PVXe+-6IrlQl^XPc|R*0swT9#-Hhnd_#Pw=UVi}<&}%LpxRIfPv7 z=?OER^*uGry?|h@fQd(xdxk{vU|i%Lr`4%B)O*UfYAvaw?C-(BsQDGcw%>%k$LP>- z?^htYw}=mK2zsb|#IM)US9(a4kx=wxhxMC}yFn0|4#5H{xoYTQ^nC*z*M^Z^ibp^Q za_Ur<^nOJ+?DREuc!KmqM;M0m4Rj(Xxg=})db}$Xg>Z61 zAQ0SYbe>6X=3N0~q4w!c=1O>CjH#0fHgB}z>GfCtIh`dTt`YnslAzN+HF-V!g?9Wif}Jzx`Y>R7Lzux~=%=*_x?Fm(;xLtX`Y00}Q8sJy`|y zOcLA)RHE8z9ayeqxNlj1TCdl&j`L1ylRARvqXOh=3=Ck<#tw-AGF*&jJR__`fb5c* z7b_^V;XPBa8LJv6iEO*8dzqrb#NFQ^V#FV{7oTMKyi?TY)&N%412`AsEGHYfx(H95$fvvZ5Zvj@U>Z=q zX;Z-p({zx|1(|#Q7B{bgH+83p_yo*{rQlP{qqRTW{Wv1%>cyY`3C!`B(bKl!38H4E zrHoxKYQw88N#(H1FnVEzr$994h6p#R<=eF(m4R@Cf(dUd<;u=+ z&#Q-#tyCvo9G$Xn5q)`c$oI_njNlOIq1&O~`AuzanmG3;=ktO`WmwjFUcLK(|ECl| z%HuvlNKuLY@V|b+4M1w{aJXl4ZC-S~G4i`UWpxyzeYwnXE-#T|Dnks`6}~mal7y*v z?MZ2T9O{}hIpd_=jw$^8*1b8uzn3Ub-v-c2$y5G*W#!0-ZZE+?OYw3%L(Ia2FUx`WY~$HN!a2wH5Erxm42if9KB9bGDRQq z)Q`C|)T(2|bH0kXFDwh1W3yvuo;8R@Ske!757{89me?r@%Vd}GUzG%C6W&poEWy_`-$h+J)}B9Bm~vh zSy9UtJp|Lr3Yl%wjVxb8{+bW!?EG|#i%y9ILrkBJ zW|Y^}V4dZR@KOUulH>q`z-~7d;^&$V*p=r&?T#-s_73?%~xYf^z{wT_1UB`$==TSKNMaYqgUmK>nGVpW%-Ot(^m42 z{6SjHV+OtR?pi8O5by+-3mp0fice3?gmL+e%Q*X4^L0|^yCboOR){g!H=R}frVh4b z8H!CD1p&b3KlEFrLcY}k-yF`&S4a@-o-hh`prr@l6=R1@i;B^uF%mq`ELcrp4dqTs z0_jk^G3yUtwGGz0oR*PF60bBOXsrsYDo#kbuXVJNTc12pQSaXxCLMl;Mo{$;-{r!c zIe#EA4mhnv2evy%_3=p)m!Pgl$~)u(c@M(%Vursw+d$^w~3EL30x-o{c|^%slb6Xw3#2W!h+88TPK`XKKzVBb9m^$P)f zH)$jA(RSl|%b8nLe5d%=PDCOb@mejTLcy&YE`?SSD);~+xKDu5(gDu#*}vkETnOBA zfYk*a^_<8IRA(nOY9(_A_ziodHTc92pAkQJw=B_3$3V?puAnh2I$XsgBx@+EeBtf0 zectQ>Z4xtKQ0_Jb%?^*A_V|KSqo{S~KR0YMCqNrgjUWI=F&2_DZQ01 z7Ct;=oP$}pJ7Wx8VYB_(VGMNr%8%ldApMJ)~5=a6uAXYC_vk= zEXm%rRUEqq-?uOm+EZ=w^&LgEt&UCLi~W+XW+(dc4BJ^S$5Qmgpl*Xc?($N?y98e` zg4U~6N5ShCBhb+bLDBZ+Xhs+Y(}e&AK6=y4lcD6(Eo2}O2a&6hI?uGDe)Pv+8I9s~ zmRmOO{@!=jpQV^{PIXo1GV@6cFXcTV{-lATBFb>|ueyVZHumoYX=P?R8g9=(bNwuJ z_Kn$jvQF|_GJp+Kt7cq5MyDP7)K9ZN*8vLH`I%|7Qypov??=~6J&&pia~H;C_h`&t zB-x3fY}0M&dWg=Rt6z)WrE`c|5KLQrRpG_F3zApzg1x=w_=)>yn4_ym;UAS+BE(cGk_e zB-K?N0EUYgF(Vy6$s!yFzp=8NMIbMc9Y3yIhFqCk+gzzAM?cSPSJVpEd)meJiE;HX z%Q-Ne{`$Q;b{V>2SGsELPx|RYN0}TE{o%blXADRTg=0S9(w{NFcbM};`_ZSusGFuw zEcx5|lY%y(=MVT*?K^`~=%nT#pT2X2!vLHlvWs79`}^{awDVlZN%XlyvKxuIkNX&}9dBuU6A#ddh!@RL z7nV#TmDJe!a4IlJK_kD&zbP{pP=AE^CFf!Ma4K#vw>RhQ`1$988z_m6igiZ_0_?(4{vlA-MMTn37oe34QKicQ3UpQlT^!P;b-`qyH zOZ_D4-6+!}4atXRRdfk{DO2+ycSnxC_d)mk_|3}putFq@)YfV9gQVt!*1#+1g{JPl zCcK0fVQf1?RWloSDm_mpj4ZheamTEz7mC1<0K8FPam~ ztThy002bCH%C4+v0=j8qg`d+g7kMSe{y@c8D+&|hPTnZu4#R6k)9v%!X<}1u zRq5r6!^S3NhFn3xhbn#?IlkABKX-o+k8e+;Kd+IymTLsCEMf7fV0G?i1H+nw(lAV0y7*6Ogm|P9BbKhw4XacCM4>=<_xpv< zvX7#8-7u6?LHBZx4h?9qRnA_J4ry1I&!Ofpke}G5slKkHiBg3x56DILk`;=eZjEXS z;e{pk^UQbv_pO#*G4W#&(sB?nC+B=))98pf^wkAhO0cWl7_cDg7nVkdCWxyJ`9+cX z3gEvGjGCtO8dUrek-yE>Y7X%Wc5{chXdz_b(Nfc-zh3L=qEJmtf40=w(de)*gRBg< z?zI&(xjsv~*UUz=qJr-NCP+<>LJK~ugsSlZY!a)0g8hsCW1&PwRRi%jvd}w5!*e9h zT-hhu>%9xRO<@QLv@^Y2w4+7_eZz$9Bv{WzrJ6^nredN*ay%x^i4ab0R*GuAF}^ko zS|TAc16O}ViFG{HhMzze$?-p{mTPNX88A+)S?OcbyR@&8oAu{f;zl;~B?Y?NrCG zKv~mG{qdNSMn1sq_mkn1dhVio;qTg@O?;dPt(2|HtDEG8x9av*cOi?| zf2rM4vPf`UwyWKg$o^gnqMUQ`TM1qWgf{3~H;tuxkR=9=KAXJ^qX3E8T>8`(kTF$J zuTErg$c{QJ)cfHnkdrg90tS+_KGwKc_9DH-1MjyTWW|xj4k))73mx#1Bp@NL_b<>Q z2%{pbzQ>z5IV8_8p9f{4z>sAv~T&V%YKbh4w-g` z>!AxQh@FalpM(>&;|6*JvHfu0j0Mbl9@_CQCOe4+Nm*-KDBx_*pF7dI8*$r786n*1 zg{-#sv*l&C!c=G5?(7}Gt25d{S?d(ekM8(S8&_loHs<5^tqB|P+O#^}_H_InSKd2& zLh6Q`S*M7hEM&MQ=XZzca|%Rk*6p=Fefvq8U+sJ?6kvo*>E<+UHly?+rgEUsMYl!xc6l`wGajaubOl=Yg zrwO>apr0HXt*9|bu=<7+vM&68YkCxjnXF|6nK~F_zuDFx>Od{V6MuiEQ}WC;Q-<>h-w;ju}oF}G$g)eD7v^C4=^@mSFxW+ zh8gV9-<{f5;ll@M#kCV!30?FsAtFcNw$uTRSls*gx(c>dI_b}Iop(bjFsUUfg1GtX z4&tLa7pM<=wJg)|VCFs20;N-x5{Is}^a)xOV&$<(fTm(U6l1%oFGiygZ{d9&38G@5 z^?-Cf=X6&Nb0rpb`oEZlXJa>-Q|nt}zoo}ZfJ|BJ&fmUpv{;$Qm57EhP$;eO7w)GS^cygO(|>u+E`uzB6zqjuDH-BvL5o-r8p()UY<1j_0cfHCY0R zra{0p45pU5h<+xzUQQNB394To;gUCK6y_UzYCk3Tgjf6 zzg<&#@Mq<>er)CG@KW$Ptcd;c9MKz_zgc(xfQB9B0y^oR@>M26` zYNrbkoz_!&``G87iEFpou5>pJ+~lv$bzdzH^G~#0&-SmZPH1Uz@A#cyNwLoR84>S6 zv-A!1G)p*4t%OzG6c0O|?hq?4>deEI_ip7JFA0%4;hYb#BrKI((RjHcw4SX)?w#)f z)&exW4V}Wc5E?vtP)&>g*b!Cypb;6p3*B#H8%kEjX<}+-+m5DX`U#Q)(=EKoo=3>0 z_>iLnRTQv4Jo2wOY4*e*!dM#(ycdz}Pe|QX`>yW{V$S)(Zt83%luV1qLNlREd36`W#2Fq0sKO!cC8sPb%ex_-#k}Dyw2P$Py)= z(WPXSG8}Nt*L;*Z@u_|REqiBQe83D_g?>bX(osLT^6#JAfVk9Ci@_@7BZUz!KWl682LY#E%#FwZ$Ih+_OX&jo#@L9=8fN{BO zFZyd9sJxWWWI)~FOd+}Dk+^;GPtRD+T^!8mgEi~2j{@(UiO{_8Aq7WFSk6Av<{zeu zJ&T~BDAQb;l2jU?UoOqQm|YzEoc^A$K;CZW{5Q@Z5-fac%A9$?2xD3O9gHt~u4vyM z>41LK-G`P)-|3mOLOAsof;)=z2R!kSoEMF=j2h;_NIAkdR1{kSqcvWVH^tUl%aSrV znYByoHJ33xa*D%1Cv2701AKYaA2eIM$z;prv{+E3uI`NvSf6HPi`EH@2%^^KcqgAb zgno^VXIG-gyfXVnSAQAIpit^F5_%*21G(v5hcclml`L3Zcx=K64JJYIpW`Q1K4RtL z5eqZ#pejF&sPM)3)B!_SDA6w5W(P!~ir&IGoqK^F4$pPt9}xTL?Az%SY#2D{#Es~9 zklg7U9HCV?iLr|L8?W|DC8nOk8+lYTU&7tHn&u zlU{Gn=KXn<@s8i=Or#7SCCU$LT_!=6Z}Nqz*u%NajQXXom^hgFLM6EMx2qrdkE=MrlHB_-g-GseDzy&-uJ_wu#Fy5|(i5;F{lHpZrMgby8sjnHpvu{JG^SO{YLY(;v+)oB@AT@Ty0=6YMl5y;|~_5nu9W|96k`)O)(5 zg|(6jS??JfhNr#ZHOe=YO-)x1df38P6-DWoC!vF&g?O8WD#n$?GIV20eqa#@=u#ZP z$TmryCFo4ej!6`$5L7y>l3>t1H^n}f2;rx?BUHlkIA+(aqn?+CW8-NnOPlPBsv+^A3B3Z=rU_oxr7_XIqLUpCslcjHw#7e!v43{-`FDrm zjJ;xA`mfkO6E6zB5#bgnra$t4wS{wJvt7(XXtu{MZK20pp?ejcx|beAS(R} z_v)w7*Qs8KQO55VG!~P?9HA)A88PMrKdl^lrF4XUQNuu7R`>8RVehR20Sih3Y@T9r zIH4a1?K&^#?Mz;#YUIn25PD6>-%yrYJ;5v)H=7KouiQV0Tdvjda0mK2EfU@YQFw~m zVL|_|yLZ)3T(t?QB-z`6?~n9S!8K{ybLhS!PFlFrNt<8j7+)$>>0xS@{+^ zOr*gJU!sB8IQE;HWj=7nEl%{tY^hi75LLw3tpWB~9hr@gXzaqj&6`FuuVawshy<+b z-Uv2n%yEn zAjq0RXntvP(99-03%e{l^ei_@~1^XiAC_N*cVy zGoxDz_A|Ko++53dTInWcNa!+w+$RF4N9F@XWwN0=fjrAHP5p~f#GkU0qNu|Bi^t|Q z6hG6*PP-xMNAd9i6SkkXy5qBVPP=M%zDMJ02dQS^k4(bG`>vRa^TF_*X}fP7OFxKFI@NznJfBa^8fN__`Nyd#IkU4Qw{W+H?6j2Kg|q?dEdtQp zd;)U}`&Gj>VEFm@=0Sx5f1hcDyPgK6%QhfVc~fY-WaYdflkPk`7s~*a*@A27PbyY& zT)>feRgAHS!253w8l&d$Ap5&v!blbC``uVsTmfIDp<%hl$JwxL$iyTSJN)NeU)9I4 zTR7j*@KvKcyvxc2oBUw2(@UNx)ZjW#@4M?btpKCTiwen%PO{%Tr}n>cx?NO=k|KX_w8E|d)Ao%s>5_z^z-7a@-?!(4)CSH zHR&Gli%-A(BMrkk(ZYN|(njr2aQLm`0HIXHt_%b4`d5lbv5a|SFfx_72Er5J@-TFb zr!bzHaW71PbnFOuy5{*_RqC!lwYGRUVDzWAg7Jn6+9IbJ(dMc03sR}Y_1jFUmDigC z6qLD0X2F$FDrafwQ>txdbQpT#F>tBUQoi4fMdNAY&auK0s#)`k>cqA%27Nyc+}^C2m!WdfII_E(q+u2XEY9k9 z@5z`%XoCJd#ot(vt(hh2MPw9UVxBSpzKB3U|ExVoi3p2DI;On0J#1p_RY zV8H?lcI$K}j>0$`2j^!VZjN;F2weRCW48)L;G)8)@o|fDaBy*o^GI+?eBqS%%*Ds| znU|aIGrP2;q%_wTVRAvK|Gy9;BsN7$2WvL~IR^*N=XA(hOnM+qj<1`6z58Laxk7`* z#@W!+23^$Na{F4lNe-WmKona$eGhg0-e~3k6cGWzVnOg)AqZ)aE(*G-4ca|M|2I23 zvn^8aFnK{0b3vf6=$sVt7Bl^UIz4nMU!XkGHt}2D>y5$cwC_~C+kK-6Np7Rbi(jF2 zK5stTg~6Yj=11Vf()H$Hm1%=}Zrz|!gq10YK3&!T9w@qnRO#he48mSQ!*|YnfE*xk zDrP{562zVrl1BLr9H8mRAL)JpLdmdN6iVvo@y0l$Jq%=sr?h3Afj?1Lpm>lO2}t`0H_u_4|WjH4Ol{$ z4!Qxn0RiTrRNGLiM_5_TY0WY9rntt`G&UooCn&|$pPA;8HOoaeiDjCxwHCpGzGJAL9YE+4prWTT+`AC#3#^h;3>%{rOM$LT zz#%fAB0vNJ_#G4_xRFlF=1aeUi}mCDM1R=;t4 zy|J?tg+yiR(_0Jd#fvxpd5BkenuPuZ!P^TEHS3oxj|+e_F**^!<5`;^xlX#6&!pF4 zT4Ge;mqS|6l{$~((F@LH*2F4!d(hAMvffLCZy}qo0YahtUn4-uMaSnS7U)H};%m|s zk|bgRab@v8sL3P*LRx@f80jf01vcsnHUnP|GywP@9?adTYr>`&0RJ#wr(moHVoi3! z5BhwW&e*bBRkZ_TK$agbhI-tA0)P>#G)L@ZnSl)7YjycqBq&;UDZ+!M|dlSU49 zOqw|=j7c=$3>)W(CP$coXXA~eM4Chbo&yV4&*LTzMaeSjxF^V?a11=h@Zhj+zT{g0-wqp#A%{;hiMSOsc?#>YZmm^#;Li2+Y`Mq*p1Smxu z6o1MQBKk17a_8g*inj@fU=#Dn1<{lX)G(f$2a>mOh_=q2h(pW!3AuV!Zk7S-1}N1Pif16N&)6KYC;eW1TT0_!a>fs!{o3 z$tv#h7Fl1oY8|ev5iHwY0DfQg-T5sSy}4?=uK#6C1b_-49~QljNtEpP4g)Mi`i2;n zHN_M+8Lj{DQ}2xUW9An``p+kG0Ni4sk&J-+GL-&|8|8NlFqXwB6ux47#b_#Y{*p{a z4aPE}&+hN+Uj-ryDwokQrNvO<6mnk02KZzPjxET@&meLEoF4%LcLjQ9g^Z2n0c2nC zkE8{{Thg7VmP5qsqyEJf!O>u6cSq9LLNiU)tF%4n2Xz)^T zDM@dH{^|2>qTwpF2Cvi{OmEN?C1BCgYaya+-WW~?F(V@_$Ar@m3&J)C?_xrb;6TVWiWrh>c zM1f6%CgDWz7ap!!(rf$Bhd%#*24f+-DQR5(*Qg*{$^RByjHhS5JsO`>;waqfr7Tj@o5H)i5N?rKef2M#P}v#`D%C2GBrJ zE%5e1PD0CpBQ(wqc|Vceik;&%7F^!Hk{DeEXZ%A)mkoQ3;kh`x}+J--(uE?B>k0l-mDR=;TwU;g@jB~0qx)%`gaOm=L@(Un}4Us=JU)=e@VtAMsi5GqExNF2}=b3%q#_095OQ9JOx)B zvN63S1y^4KjL_WC-Lqlgmv%V5ab|VwVh8t_|M>=BQj4u@|8oL@iNLr94hVDH_;jaT z{C~`KQt{|+>3W?u!%4L&@L9knm)aR^x_K(D5F})JVJfaDP>PpAOHClYEK2;iu*eo4 z^f;EhCMwIl$XcjW#L%qia*mXR)R~-MRu@;~e3y{SLVvH{xV)zGXf;g(t|qIfT)3gk zQuRx;;3B{Qz8X(hnSKHoNLN|7p4h}?>z6JH#()i+!^WL4v?gKTDzS=^DJcgw+ZdfdK)p_i$m!N|-Gn z2t>3K!j!SW1mGjXv;w(mYN>nPISv*Bh<;cv}drLGwc%g z;C-|lBAwJIHxR%cII^I0560l1=y6rqXZ8bc+YnisH!K4o+ZaHit(+C`7V0PeXD|#< zIKjj@_MZLMtHe20o&DFW#N~6Y{0G9TR83$E7HeFlvh1m8_z#7-{u%$ZEU&eb-!Z=j zA_H)&J-}P4EmYdsst)U4a<_SBcZ*j9fwMceaa9t&JA5S7_8zNn6NJhI>_KSY?-HJX z$#QWEQl&FPQu;D$FempdZvzljRd?nsPP2_hgl(+-9~je`pkg!r{R~hX)yu`ov_gy9 zoyhyIe^JZP%T>;FCynzN&!bG4AuFnENkPJu`;1#3kXwZv%J`Fpi?*S9c6oj9qrW&1KbihAA$BP~hAuARtKqUPAu*kWdN?>A5H&#eXos zUmx+WuNlTZKU5s=OpXn|6#yi)a(s~e;BZ*E+`<{M)*x^ zkC=H)_g?}G5;;YbA|jh z2iJio^1lW%ZYw}9x9q>dslB4~zrq>Tz&~u=&z?2q&%5)*i8Ss7MGyEN9`9MHdqD3< zftcqWyJ+c4@4=k}jY5SfT~ zx}jlD$q6|w+j<3I$KDBE$%L}O1#4X@xo#kI&J<3GRQ(?&b6xaWtRY+my#THgx8^FYa+Bko=V3qY@fs=IB9?#dbOYcm_-?=e6T z=QtviqyRh4vEvqM$LA+x-1z>j;8%R$k@TLY%kzKF0&!ct$1kDT{diog$#V|~t-jKJ7m_CcyXfV|0@h-UWV- zSH&-w4*At3mUjv#kqs$CQdkqHj7GW-=j}p_@+Gh~4x}tvCmksr3v$?etA3gSN?rCW z`Ne!_)ICG`6AI3vUZe1iO01bilE#9>ME^(~u@`Jna={!mAOJ~VZ?&&vc}G95lcU>* z2$rH=>++1cE|a4hhfw&?ivNMg#VW)U1;WG|gb13TUW;;%V$Yi%F&02ma={(>A{fED z351S73ZWIsiCRk>(H7)WxDkVfG30~w*ioM+c%StA0j$xpzu`%c3w;BH!$8oYp5g#V zL6{hFSgVQjS70966@qDaiGD1*g!+mJ=hiJ-N#(SxD}j#SvGPr7Kq(|QChWw+?nf;~ z74?GT5V8CZcZ3k?1@=&}BwOqOl(B>-|&BXEM?!%V@LVLxjrFk~@p@{(2IO@7elW4F~_@{W~<5Yf4>OVJgc$&@n zOrHc$j@jYd6WJINbHvox3OJmXxo6E|mg3Q0ca|N}Tz5(@)NZh^-D;f|qS118ON%d! zZm-6!)cO-?Q6=sZr!Oe;OPnzC zlSLlMAy#bfCHJbl<7x$Y$yM7qD@;g4B#^c^gxtRFolP8~#RxYR#Q*mN{J$31NH#S% z>R7Q0cZaz6Tf%!C?SXbU#Ph#b*WdVz>RcFE#R4V5ylMZ6Qe{mM ziKH0*_E6cPYnQK8?*k!;ijd|B5>*HTG^Sl9!4mx?o0N`ZG*9F_T%+e}8QiFWt-!MF zy)}OnkfDQ8eV;{YOCoxr#Ao8Yt4u8^y(oyH86VV=Kq*ui(D*Wn3kb zmC2i{4Yjb8xW>{%&l7`9N5lLdt={R-6;BVKU7%aJRlic>rEo&oJ=xt0bx?<2SvKyw zL|oGTe9lU*GGiC~e1b%hhV4=&@5_n-SBEJJ`l^=}^&cslm_<&?kJ7a2=4+_9UGP7Z z%rer%qO(q0bWhJdBh^dIIMzITs8VP%m?coQ(6{KFhD_T+^y2LOTd|@jy;J0Hu2mr9 zm8x@ha}P6%64;w@+_HE(U9yv2#BWv6ziVapmaAhUO7hvOxFU~WLRFIv<-kz$1Np$P zWGY5*PvJB*%K)zWF0L1(=A^7P$--AI`xT$zj55rq+#jaTG zB0~;k8ABedS8=3Cbc#P_I!?XRUm#^cO|>kU9;N}#F)7n-I%O1FUdy70DpAZ5{}QOfPvH zF|y4nrq6!luf=B@hlg+a-9H>S z1M54{dr{ZC0a-v=Iw+beCn5SKD-+a_@E!V&v~uN$Yr9NR|I!o-C0m3PBsLv{S)g1L zv(2eG2-0GQo5yz}iG^UbUkFPLJiFvb=rDAXx;38Usgi^L$0m`I`oB6*GHJ zZG1=rVMUXbrzs1#%jsYNqsQJ84kvwT<4VW??hpI`dZijJ3p+hjE;S4;;72_7D%!3n z3n#UkUL1#o(-*6E?Bvz&)vU?mmM-mztm$J1ob86R>Ek&=2dpzjYvO# zWScO@<*@CgTP6X!1i!ww0@a{3x_tcsNnL(xua_sFK>kiE|DJo_=^Ob;{olkkhIt$x z@Y>BJ!n)c5c$vnubJt=E5KlEpTa3-kT^8^0y%NRqwyDoGX8DX-1~h-QuuQ;;)2KAE zO1D71MqAD=bx7wwHRk3X0^8A3PL+IdLJG2f?*+cZa=D8nzWwmY8x7FPRR+gj|Huhp zjH*j|{QoGun)~dJwaZ1Z${R+j3iUr)4JM^YRS^>(#oG2oFT(uvVMa8_3lulLy^Hk1 z72x~At1t^<{8G0Y!l)trzl$sCR9-MH7?pNZWfaOs>5O@&V%3?20~y_oOPA`R(SNvs zwCb`yp48rq7<-}#e>6R5RlYG=n$Y~w`rRYHqT?)?+EAx{uf?oNvjO%z2cpr^SoTBh zIG&C^7BC`jCov8i`%icXrsVH3ylNtEue_v?T@Cp&B6v*+<3b4<<=eswYUD@joAOjc zUmf}VfWGSUvlZF~ay)6`Z|^+LOZQRzt(**m-_bY`i!m@=!zHm~QvFy}E~srbF6e0r zC~#=%78L@#1o2O!LEXjP=@op0@aL^gqd{}$qCz%jRmR8WeXrcZ;|owAu8Kbc|MYa8 z)dQ_U?i)-6_Zek@jD>u!;hnM#Y$g#jSD>O1{M_~rSk|dy;FVjDVjQ%ONEt0837G~j zq(7-fE93@=MhE>8L)o#J?FbY$u)KI;cCJv8MSoj4y-5b|F=oTIL|sRt3KlPQL2I^6 z9!fhq>RAQ8q;gCyS8@Z-Yo>zB!L59Odx*izNYyUx;p@8Rg=vZO|XB(`xg#5=;>y2>_&+Ep+ zzGx9YZ=$@u?}Rd=xo_>kMl+821j#PoRfI)eLGR~YRF+~cFG5F!o^)YlEzE0d& zy?%FGL!Ey2^*MB%tZ?58+*N37?VoyK8iqSKI(+50-?aO!a67^ovGE@JTbpID62g#p zs@9?BfOwB9y*8TGhC-sYXdRA0e;#ZDkITe;7*F8jA@3lB%B%;xnoax3zu1z2HgGoj zF81^$5}KR+wV+x0&=>Wb#_>yLh}kh<1mDOIAoHHkuM&Xdru{vQtFuXE4qoO{ka=kOG;_Cl2(0TTP)O-yyt?WYt5l2F1>LQ!IloPE``X$!+a}!a8tA`PH&U)+UBUW>>bSRA+z`s)EG9&I7`E#0 z;(jcY?6|6Mgx5Ji7ya_Ep3FF|7FJke8NMhDjJ^VW=?JVG{9v?dlgvDsvLr1}CMtu= zgcmoLJf7n91gty7NN9%n{2`K2?Y0q*%A32vB-DhTD@c9%kWeIz+Yr1P_KZ)7V`}aTJu;2D|I-z23*~6XbV(*=&y7v+q)Cd29Y2#BAz99prcEG(bCsx_F z2xIs1fX!AUTlKV7m->0TIq_il(lq&o7ndW2(NY;_W=m~-XUH2nw=lsNlo4#W*kBid znRm6of{ZBN!P&_|{#D1mLw3Q=R*I3=$^92wB5fNbK(jG-vn#~8!c58;95ojMhu*pK z?h?m^%P6VaLV_(JNM>?cC`gKuO$%ZJ2;@j~yZaU!-$W|-r)P3{N?hgA#I$j}lU%dZ@MQ?VCsm}w~b{y0D12RBjdI)~GT`RWz1P3+n1 zL2YiR601`OtMQQ>Yz{HfK-fEME5kH{gqg`u>kBha?jD3(fM7_$pWfU(062*z-ha9TbXnOYIEDn~DB;ls|@%0kt04e~wCj zTq<+|0Aa{&?-{Mf`9*FdqE7sF>l{BI7wWAG_rmz*ZFj**O_4+mJ#PGQA4o96ce#a? zh~gnc90FS&g0Qo73QSD@*chK665Bfu`mFElakIC(!OHXNylnY8^!b; z+71C-xe3{DmEc@#K=Z}ppOu-%y+(dqyAPy<94(=nE@Qd~&&pJYgLugoa^B$Q))sx> z9dZtXv3|$ASgu&<%(|CEASXj-WA-FTYcVn4^Af)^Z2g?BN!;nA?ENPZVla*Kx|y7` zv%2*>0R)kt(Ua+}g~`Qjv1)z~6AxoU-6RWJAxVD%1`ysb^YWSDN0+TspKh}E14wT2 z7zil*k(h9&qJ2et zqVfU`v(lf(A;c%cT1I_6i%+d@f5Ilk{5m$y$@^qj(&&+Wk*NFw%xqxG3(JjeXRhna zzb-3S=p{Uxrs~`K2hKK8ytG34@&}E7Kf9chHPqPef4m2cQAOMvxTs zm4=nM!)%wu<2W0Pqo}(-_D)CP!(Z8#7ga~+AyjUp(yy{&9e`Vnzs#tBzJ!NL+7BJ9 zldiPHOc$SL|2QC(ak=q<>0@ksq!6e7aXt=Y_zum4`;tB%&5?GnBziB7bDlFe39-!3 zhs|>=DMx>c!iu#TQboc5$Ms*gHK{x|xhC3>Nhxz0q_X?cP2=EbCA0*Ced-cisAB8H z=Sb&mYyS_hP5f&&sWAZrp2$=arq1&w(7H~Lj^yb@9TkTWk+Hue{6qM%Ig4AdK~O&8 z5oBy{8SS%ukmP+lFH=z4dth|05|wAKSl$xM<-}FM6s@~C0I|Egyhu{>mXRpgJtPv$5Y%bnmLwdF zWnkcAId1vW?56CQSKEP}yS@5aCat$UZ-LTT09ZQfk z_K-Lg6Jt^!{PI$tnE`TQ-R{GRgGs_04f1s)Fkv~;%7dqyvI81(a2b?s6#^>CQO_vOF={J5hQL^Cg- zYV3BS2I-59X5J>a%R}l@IpH};s)QV-s?E2F`F{Q9sO)-Lm88;0g;XsqqN-1^bDDxV zAa(42^B+&aSt27kFk?bXqX{~^fX}ZRUu`G{_dA-K48(FFVc}IQh0f?Q%z;dj?ybuO zcCDIfzLh_{3X=)JL7zQ_wM-kqvTaA%1wP_gH>gIa3s8(|<_+NZ96Q*U3)%8f(RAr` zXbQI!n}qQ)PhdFZNyvpRB%qn>Q^{xFn>x$}V=0@55RRPOw)Gd}1PV_ddA0p=;zKu1 z7SZh)&+?i#=xbN{vUS=2;%-R%TKb8o%w(kAHiVy%m7TY$J%cYt>Y?(%JB5RbRpc7& zYWj3BT2ud#It}UFjf;<;=F{mIjQ>b+1@J`;@iDtTQfR}rTk`= z@L}F;HO!kGWVUyAlp!{o>oF48&W@nNGEW`f&BT&!i=Zn{>8yd?L#(ngtG9XOYE(b= zr|$86?7uoEqSP8Xh>>U+lK+lImRu*WF*u%s6$NJX4z5j1-HT>S_v0K+T=@}RJ$ravh3muS7 z&A(m#=NIteU+YVWy3tUv(*9~7)|Fz_r4UZB@{TTjcJl zRk*nSt@S8iUhIzvCS;CIuZ3Cm*WzuNFz_MHU^nE!Zol6Q>JW!J199>=+=B!Oi`#I6 zjByAe558@MKT%=uqvEdMz4q31T>K#uZ3srT87}Br;|2)!o#VBe7Q^}To?CGTP*s?` zg~zFZHsfNM#{q556hcwxK7Ck{-DA>*o8#Fn32pwc>q~Vq4gtOo{#Wjf-a6KLnN$NB zZ4(x*yfk}&t?kh-<9Nw2!ts7^UQZ9<^D{S>Q{!8|YxSkkgMonc?fyy5u+9ES=*|eE zG5o21EosubPb4a zU6+iI)0-nHkupAs4_t<1w@HFk?Qa3gblWPS;{YLoj-+HIul;$d@2^p9Gp8AfYqFhj zjAylsO6&!(>riB4XOxHFT%t*~(WzC_3YmadgRJ=|7Iy^#ANv%8VSsS%hyAi`%7Sx> z+oLdo72>pZx|ZML6L^70bHNIYBM15C`Q3PPGTNhL1?Luwn?gneE6$NEf3+1niTwIV z2H2mtMSd76nbodnr@Xpr(XU=+G`M}YOEy^YX?s)zdxg-PWri%J*0e1Bam+PZA+g<# zmTI7=Riw1aew8zGs+A+nYjaXZN9Px75fL10f4z%GWhbSo-9VZ6H+VHPt*H7p=O+LbMT!>|s2 zR7(2DUpmI%xRKA>IuB57hYj!0&B@!N)){*IEyAQLO6V4V0Nn zS8`cr>Yy(nAuhtO$O60;{69o<$8o_iB>g^KbvNXD@J-7)0pl33`cWiEN0mu@Bdx#5 zYlaufLTDkyZFzJ)ZAJJrhSe2+r8aj>-7261@0rw$ckUZc=y0wCxf6&omUxjy0v8pb z=pvERi)KN%5)Lw$)cJn*v|IxV8XbKl0S8VXzeU_C5&s%9gzjrG|LIvB?mw1i$6^Ly zzRpn%C)}5_=^i=>!prp1bUb=hU zbNV}Uf-TfYeuf~N+PUga~r1<{(` z5_)@oxqPnf+f)5ewmb5Bd~|*ovc%dt60I4yRB>~ljLY$WJULR9@M`Lp>bma$iZv{? zFf2WBbvM<8Oj&Ag*`@gPr)Q1r*2p57TN2y6v$S_4KDU498r_n~Jy)(SQ0}O(6F0k7 z^t%aL6EJ^XO-i%Z=*r})mu8bbl8c?S&+69bvOiji6J>IS!s;SNhxJy{#cG8lq?(o% zUH2CO*0l4npAC_t9gQkq!t39K+cTD{aEy+3>8Z6sC)0Clg^v6OPwkP83>BS_w-3!y zoI++FNvDg~&vwfg{U>=C9Xs1FHqeoxVz%If6Ko|zr!o8y}Oe1pNixa*_<;9zJ;hL&a!nq@S)AI>Do^ M2A7e!Y+(rhKR@{YG5`Po delta 21385 zcmagEWmFvR6E%ptySpd226uONm%#}R0S1}i?(PsQxCVE3cX#&yA?)P$e|OLBr#)w? zD60FZo}Opw*6j=DciQWB+JPWQ5EKX+1OtKv!8O8b(x%+#2VWsR}V>}NB%lhB6q#|dPZs3cRo7xsH>XV1>eO+Mh;q1lzIO-9=H zY}O41Pvg|_cWpf2q#q`qZBoi0XK;5yo1(qYh?VgEz1Q-#$@|OeS<5F#g}aP&DHhXb zy@8@t*KZ#8vD4ZR9|1!dX#)-4xa|b)`w6;UF48LVYFg=@>uSuYWk2(P@YzKmcwlTu z2xxfXBO2vfTv=M@M=G=jQ0x33nf7>?FhINp;o-Eo3gKJ8U%}6%!5BjJND~OyC#oS) zSjCtEpCUL-^PN%Mg^phPqW#99tKnBy1dk#{qE{Euyc(29b>_ZpFn$Vju^ETUeg-vRm<{al&D`Bh z#p|?5e5;@b^4KQ;FQ=;D`h@jHU8%((;p+T?^auEI8gUxeR0eA-FG7{AGRr zeVY2%=e{) zK%OUdP0~3kDySE&IB!?(⁡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 PxSqa*hKBWV8;Sn|^(@7X