diff --git a/external/lfric_infrastructure/src/kernel_metadata/argument_mod.f90 b/external/lfric_infrastructure/src/kernel_metadata/argument_mod.f90 index 134d5a3b78..a5bf654350 100644 --- a/external/lfric_infrastructure/src/kernel_metadata/argument_mod.f90 +++ b/external/lfric_infrastructure/src/kernel_metadata/argument_mod.f90 @@ -60,6 +60,7 @@ module argument_mod integer, public, parameter :: GH_FIELD = 507 integer, public, parameter :: GH_OPERATOR = 735 integer, public, parameter :: GH_COLUMNWISE_OPERATOR = 841 + integer, public, parameter :: GH_SCALAR_ARRAY = 973 !> @} !> @defgroup data_type Enumeration of argument data type property descriptors. diff --git a/src/psyclone/domain/lfric/__init__.py b/src/psyclone/domain/lfric/__init__.py index 0c19c0d34e..1cf86ec9b5 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, L. Turner and O. Brunt, Met Office +# Modified: I. Kavcic, L. Turner, O. Brunt and A. Pirrie, Met Office # R. W. Ford and A. R. Porter, STFC Daresbury Lab '''Module for the LFRic domain. @@ -71,6 +71,7 @@ from psyclone.domain.lfric.lfric_run_time_checks import LFRicRunTimeChecks from psyclone.domain.lfric.lfric_invokes import LFRicInvokes from psyclone.domain.lfric.lfric_scalar_args import LFRicScalarArgs +from psyclone.domain.lfric.lfric_scalar_array_args import LFRicScalarArrayArgs from psyclone.domain.lfric.lfric_loop_bounds import LFRicLoopBounds from psyclone.domain.lfric.lfric_kern_metadata import LFRicKernMetadata from psyclone.domain.lfric.lfric_psy import LFRicPSy @@ -105,5 +106,6 @@ 'LFRicPSy', 'LFRicRunTimeChecks', 'LFRicScalarArgs', + 'LFRicScalarArrayArgs', 'LFRicStencils', 'LFRicSymbolTable'] diff --git a/src/psyclone/domain/lfric/arg_ordering.py b/src/psyclone/domain/lfric/arg_ordering.py index a587647ca1..094f5032c5 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, A. Coughtrie and L. Turner, Met Office +# Modified I. Kavcic, A. Coughtrie, L. Turner, and A. Pirrie, Met Office # Modified J. Henrichs, Bureau of Meteorology '''This module implements the base class for managing arguments to @@ -459,7 +459,7 @@ def generate(self, var_accesses=None): self.operator(arg, var_accesses=var_accesses) elif arg.argument_type == "gh_columnwise_operator": self.cma_operator(arg, var_accesses=var_accesses) - elif arg.is_scalar: + elif arg.is_scalar or arg.is_scalar_array: self.scalar(arg, var_accesses=var_accesses) else: raise GenerationError( @@ -770,11 +770,11 @@ def scalar(self, scalar_arg, var_accesses=None): ''' const = LFRicConstants() - if not scalar_arg.is_scalar: + if not (scalar_arg.is_scalar or scalar_arg.is_scalar_array): raise InternalError( f"Expected argument type to be one of " - f"{const.VALID_SCALAR_NAMES} but got " - f"'{scalar_arg.argument_type}'") + f"{const.VALID_SCALAR_NAMES + const.VALID_ARRAY_NAMES}" + f" but got '{scalar_arg.argument_type}'") if scalar_arg.is_literal: # If we have a literal, do not add it to the variable access @@ -785,7 +785,8 @@ def scalar(self, scalar_arg, var_accesses=None): var_accesses.add_access(Signature(scalar_arg.precision), AccessType.CONSTANT, self._kern) else: - self.append(scalar_arg.name, var_accesses, mode=scalar_arg.access, + self.append(scalar_arg.name, var_accesses, + mode=scalar_arg.access, metadata_posn=scalar_arg.metadata_index) def fs_common(self, function_space, var_accesses=None): diff --git a/src/psyclone/domain/lfric/kern_call_acc_arg_list.py b/src/psyclone/domain/lfric/kern_call_acc_arg_list.py index a4d40a5137..276500ae69 100644 --- a/src/psyclone/domain/lfric/kern_call_acc_arg_list.py +++ b/src/psyclone/domain/lfric/kern_call_acc_arg_list.py @@ -261,7 +261,8 @@ def fs_intergrid(self, function_space, var_accesses=None): def scalar(self, scalar_arg, var_accesses=None): ''' Override the default implementation as there's no need to specify - scalars for an OpenACC data region. + scalar values for an OpenACC data region. But there is a need for + ScalarArrays. :param scalar_arg: the kernel argument. :type scalar_arg: :py:class:`psyclone.lfric.LFRicKernelArgument` @@ -271,6 +272,13 @@ def scalar(self, scalar_arg, var_accesses=None): :py:class:`psyclone.core.VariablesAccessMap`] ''' + # TODO: Add implementation of OpenACC data region for ScalarArrays + # If the argument is a simple scalar value then doesn't need values + # added to OpenACC region + if scalar_arg.is_scalar_array: + raise NotImplementedError( + "OpenACC data regions are not currently supported for arrays" + " of scalars.") # ============================================================================ diff --git a/src/psyclone/domain/lfric/kern_call_arg_list.py b/src/psyclone/domain/lfric/kern_call_arg_list.py index 9e7ed10376..2448c634e9 100644 --- a/src/psyclone/domain/lfric/kern_call_arg_list.py +++ b/src/psyclone/domain/lfric/kern_call_arg_list.py @@ -252,6 +252,11 @@ def scalar(self, scalar_arg, if scalar_arg.is_literal: self.psyir_append(scalar_arg.psyir_expression()) else: + if scalar_arg.is_scalar_array: + # If it's a ScalarArray we need to add the dimensions + # array to the call + dims_sym = self._symtab.lookup("dims_" + scalar_arg.name) + self.psyir_append(Reference(dims_sym)) sym = self._symtab.lookup(scalar_arg.name) self.psyir_append(Reference(sym)) diff --git a/src/psyclone/domain/lfric/kern_stub_arg_list.py b/src/psyclone/domain/lfric/kern_stub_arg_list.py index 4cfc9a5bc6..24891e8908 100644 --- a/src/psyclone/domain/lfric/kern_stub_arg_list.py +++ b/src/psyclone/domain/lfric/kern_stub_arg_list.py @@ -32,16 +32,18 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors R. W. Ford, A. R. Porter and S. Siso, STFC Daresbury Lab -# Modified I. Kavcic, A. Coughtrie and L. Turner, Met Office +# Modified I. Kavcic, A. Coughtrie, L. Turner and A. Pirrie, Met Office # Modified J. Henrichs, Bureau of Meteorology '''This module implements a class that creates the argument list for a kernel subroutine. ''' -from typing import Optional +from typing import Optional, TYPE_CHECKING from psyclone.core import VariablesAccessMap +if TYPE_CHECKING: + from psyclone.lfric import LFRicKernelArgument from psyclone.domain.lfric.arg_ordering import ArgOrdering from psyclone.domain.lfric.lfric_constants import LFRicConstants from psyclone.errors import InternalError @@ -133,6 +135,34 @@ def cma_operator(self, arg, var_accesses=None): _local_args += [bandwidth, alpha, beta, gamma_m, gamma_p] self.extend(_local_args, var_accesses) + def scalar(self, + scalar_arg: 'LFRicKernelArgument', + var_accesses: Optional[VariablesAccessMap] = None): + '''Add the name associated with the scalar argument to the argument + list and optionally add this scalar to the variable access + information. + + :param scalar_arg: the kernel argument. + :param var_accesses: optional VariablesAccessMap instance that + stores information about variable accesses. + + :raises InternalError: if the argument is not a recognised scalar type. + + ''' + const = LFRicConstants() + if not (scalar_arg.is_scalar or scalar_arg.is_scalar_array): + raise InternalError( + f"Expected argument type to be one of " + f"{const.VALID_SCALAR_NAMES + const.VALID_ARRAY_NAMES}" + f" but got '{scalar_arg.argument_type}'") + + if scalar_arg.is_scalar: + self.append(scalar_arg.name, var_accesses) + else: + # ScalarArray + self.append("dims_" + scalar_arg.name, var_accesses) + self.append(scalar_arg.name, var_accesses) + def field_vector(self, argvect, var_accesses=None): '''Add the field vector associated with the argument 'argvect' to the argument list. If supplied it also stores these accesses to the diff --git a/src/psyclone/domain/lfric/lfric_invoke.py b/src/psyclone/domain/lfric/lfric_invoke.py index 2e938d5d79..3299870215 100644 --- a/src/psyclone/domain/lfric/lfric_invoke.py +++ b/src/psyclone/domain/lfric/lfric_invoke.py @@ -32,7 +32,8 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors R. W. Ford, A. R. Porter and S. Siso, STFC Daresbury Lab -# Modified I. Kavcic, A. Coughtrie, L. Turner and O. Brunt, Met Office +# Modified I. Kavcic, A. Coughtrie, L. Turner, O. Brunt +# and A. Pirrie, Met Office # Modified J. Henrichs, Bureau of Meteorology # Modified A. B. G. Chalk and N. Nobre, STFC Daresbury Lab @@ -93,10 +94,13 @@ def __init__(self, alg_invocation, idx, invokes): from psyclone.domain.lfric import ( LFRicCellIterators, LFRicHaloDepths, LFRicLoopBounds, LFRicRunTimeChecks, - LFRicScalarArgs, LFRicFields, LFRicDofmaps, LFRicStencils) + LFRicScalarArgs, LFRicScalarArrayArgs, LFRicFields, LFRicDofmaps, + LFRicStencils) self.scalar_args = LFRicScalarArgs(self) + self.scalar_array_args = LFRicScalarArrayArgs(self) + # Initialise our Invoke stencil information self.stencil = LFRicStencils(self) @@ -274,7 +278,8 @@ def setup_psy_layer_symbols(self): ''' # Declare all quantities required by this PSy routine (Invoke) - for entities in [self.scalar_args, self.fields, self.lma_ops, + for entities in [self.scalar_args, self. scalar_array_args, + self.fields, self.lma_ops, self.stencil, self.meshes, self.function_spaces, self.dofmaps, self.cma_ops, self.boundary_conditions, self.evaluators, diff --git a/src/psyclone/domain/lfric/lfric_kern.py b/src/psyclone/domain/lfric/lfric_kern.py index f89d107c92..aa6973dd2d 100644 --- a/src/psyclone/domain/lfric/lfric_kern.py +++ b/src/psyclone/domain/lfric/lfric_kern.py @@ -32,7 +32,8 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors R. W. Ford, A. R. Porter and S. Siso, STFC Daresbury Lab -# Modified I. Kavcic, A. Coughtrie, L. Turner and O. Brunt, Met Office +# Modified I. Kavcic, A. Coughtrie, L. Turner, O. Brunt +# and A. Pirrie, Met Office # Modified J. Henrichs, Bureau of Meteorology # Modified A. B. G. Chalk and N. Nobre, STFC Daresbury Lab @@ -196,7 +197,8 @@ def load_meta(self, ktype): elif descriptor.argument_type.lower() == "gh_field": pre = "field_" elif (descriptor.argument_type.lower() in - const.VALID_SCALAR_NAMES): + (const.VALID_SCALAR_NAMES + + const.VALID_ARRAY_NAMES)): if descriptor.data_type.lower() == "gh_real": pre = "rscalar_" elif descriptor.data_type.lower() == "gh_integer": @@ -208,6 +210,9 @@ def load_meta(self, ktype): f"Expected one of {const.VALID_SCALAR_DATA_TYPES} " f"data types for a scalar argument but found " f"'{descriptor.data_type}'.") + if (descriptor.argument_type.lower() in + const.VALID_ARRAY_NAMES): + pre += "array_" else: raise GenerationError( f"LFRicKern.load_meta() expected one of " @@ -773,15 +778,16 @@ def gen_stub(self) -> Container: # Import here to avoid circular dependency # pylint: disable=import-outside-toplevel from psyclone.domain.lfric import ( - LFRicCellIterators, LFRicScalarArgs, LFRicFields, - LFRicDofmaps, LFRicStencils) + LFRicCellIterators, LFRicScalarArgs, LFRicScalarArrayArgs, + LFRicFields, LFRicDofmaps, LFRicStencils) from psyclone.lfric import ( LFRicFunctionSpaces, LFRicCMAOperators, LFRicBoundaryConditions, LFRicLMAOperators, LFRicMeshProperties, LFRicBasisFunctions, LFRicReferenceElement) for entities in [LFRicCellIterators, LFRicDofmaps, LFRicFunctionSpaces, - LFRicCMAOperators, LFRicScalarArgs, LFRicFields, - LFRicLMAOperators, LFRicStencils, LFRicBasisFunctions, + LFRicCMAOperators, LFRicScalarArgs, + LFRicScalarArrayArgs, LFRicFields, LFRicLMAOperators, + LFRicStencils, LFRicBasisFunctions, LFRicBoundaryConditions, LFRicReferenceElement, LFRicMeshProperties]: entities(self).stub_declarations() diff --git a/src/psyclone/domain/lfric/lfric_loop.py b/src/psyclone/domain/lfric/lfric_loop.py index 6131b49f64..d296023bda 100644 --- a/src/psyclone/domain/lfric/lfric_loop.py +++ b/src/psyclone/domain/lfric/lfric_loop.py @@ -696,7 +696,7 @@ def _halo_read_access(self, arg): ''' const = LFRicConstants() - if arg.is_scalar or arg.is_operator: + if arg.is_scalar or arg.is_operator or arg.is_scalar_array: # Scalars and operators do not have halos return False if arg.is_field: diff --git a/src/psyclone/domain/lfric/lfric_scalar_array_args.py b/src/psyclone/domain/lfric/lfric_scalar_array_args.py new file mode 100644 index 0000000000..d8c4fe174f --- /dev/null +++ b/src/psyclone/domain/lfric/lfric_scalar_array_args.py @@ -0,0 +1,225 @@ +# ----------------------------------------------------------------------------- +# BSD 3-Clause License +# +# Copyright (c) 2025, 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. +# ----------------------------------------------------------------------------- +# Author A. Pirrie, Met Office + +''' +This module contains the LFRicScalarArrayArgs class which handles the +declarations of ScalarArray arguments to the kernel found in either +an Invoke or a Kernel stub. +''' + +# Imports +from collections import Counter +from typing import Union + +from psyclone.psyir.frontend.fparser2 import INTENT_MAPPING +from psyclone.domain.lfric import (LFRicCollection, LFRicConstants, LFRicTypes, + LFRicKern, LFRicInvoke) +from psyclone.errors import GenerationError, InternalError +from psyclone.psyGen import FORTRAN_INTENT_NAMES +from psyclone.psyir.nodes import Literal, ArrayReference +from psyclone.psyir.symbols import (DataSymbol, ArrayType, + INTEGER_TYPE, ArgumentInterface) + +# pylint: disable=too-many-branches + + +class LFRicScalarArrayArgs(LFRicCollection): + ''' + Handles the declarations of ScalarArray kernel arguments appearing in + either an Invoke or a Kernel stub. + + ''' + def __init__(self, node: Union[LFRicKern, LFRicInvoke]): + super().__init__(node) + + # Initialise dictionaries of 'real', 'integer' and 'logical' + # ScalarArray arguments by data type and intent + self._scalar_array_args = {} + for intent in FORTRAN_INTENT_NAMES: + self._scalar_array_args[intent] = [] + + def invoke_declarations(self): + ''' + Create argument lists and declarations for all ScalarArray arguments + in an Invoke. + + :raises InternalError: for unsupported argument intrinsic types. + :raises GenerationError: if the same ScalarArray argument has + different data types in different Kernel + calls within the same Invoke. + + ''' + super().invoke_declarations() + # Create dictionary of all ScalarArray arguments for validation. + # The dictionary keys are intent values, i.e., 'in', 'out', and + # 'inout', with the values being arrays of ScalarArray arguments. + const = LFRicConstants() + self._scalar_array_args = self._invoke.unique_declns_by_intent( + const.VALID_ARRAY_NAMES) + # Filter ScalarArray arguments by intent and intrinsic type + real_scalar_arrays = self._invoke.unique_declns_by_intent( + const.VALID_ARRAY_NAMES, + intrinsic_type=const.MAPPING_DATA_TYPES["gh_real"]) + integer_scalar_arrays = self._invoke.unique_declns_by_intent( + const.VALID_ARRAY_NAMES, + intrinsic_type=const.MAPPING_DATA_TYPES["gh_integer"]) + logical_scalar_arrays = self._invoke.unique_declns_by_intent( + const.VALID_ARRAY_NAMES, + intrinsic_type=const.MAPPING_DATA_TYPES["gh_logical"]) + + for intent in FORTRAN_INTENT_NAMES: + # scal contains all ScalarArray arguments of all intents and + # intrinsic types whereas decl_scal contains all ScalarArray + # arguments of all intents with valid intrinsic types. + scal = [arg.declaration_name + for arg in self._scalar_array_args[intent]] + decl_scal = [arg.declaration_name for arg in + real_scalar_arrays[intent]] + decl_scal += [arg.declaration_name for arg in + integer_scalar_arrays[intent]] + decl_scal += [arg.declaration_name for arg in + logical_scalar_arrays[intent]] + + # Check for unsupported intrinsic types + scal_inv = sorted(set(scal) - set(decl_scal)) + if scal_inv: + raise InternalError( + f"Found unsupported intrinsic types for the ScalarArray " + f"arguments {scal_inv} to Invoke '{self._invoke.name}'. " + f"Supported types are {const.VALID_INTRINSIC_TYPES}.") + # Check that the same ScalarArray name is not found in either of + # 'real', 'integer' or 'logical' ScalarArray lists (for instance + # if passed to one kernel as a 'real' and to another kernel as an + # 'logical' ScalarArray) + scal_multi_type = [item for item, count in + Counter(decl_scal).items() if count > 1] + if scal_multi_type: + raise GenerationError( + f"ScalarArray argument(s) {scal_multi_type} in Invoke " + f"'{self._invoke.name}' have different metadata for data " + f"type ({list(const.MAPPING_DATA_TYPES.keys())}) in " + f"different kernels. This is invalid.") + + # Create declarations + self._create_declarations() + + def stub_declarations(self): + ''' + Create and add declarations for all ScalarArray arguments in + a Kernel stub. + + :raises InternalError: for an unsupported argument data type. + + ''' + super().stub_declarations() + # Extract all ScalarArray arguments + for arg in self.kernel_calls[0].arguments.args: + if arg.is_scalar_array: + self._scalar_array_args[arg.intent].append(arg) + + const = LFRicConstants() + # Filter ScalarArray arguments by intent and data type + for intent in FORTRAN_INTENT_NAMES: + for arg in self._scalar_array_args[intent]: + # Distinguish whether they are ScalarArrays + if (arg.descriptor.data_type not in + const.VALID_SCALAR_DATA_TYPES): + raise InternalError( + f"Found an unsupported data type " + f"'{arg.descriptor.data_type}' for the " + f"ScalarArray argument '{arg.declaration_name}'" + f". Supported types are " + f"{const.VALID_SCALAR_DATA_TYPES}.") + + # Create declarations + self._create_declarations() + + def _create_declarations(self): + ''' + Create the symbols for the ScalarArray arguments. + + ''' + type_map = {"real": LFRicTypes("LFRicRealScalarDataType")(), + "integer": LFRicTypes("LFRicIntegerScalarDataType")(), + "logical": LFRicTypes("LFRicLogicalScalarDataType")()} + # ScalarArray arguments + for intent in FORTRAN_INTENT_NAMES: + for arg in self._scalar_array_args[intent]: + if arg.intent in FORTRAN_INTENT_NAMES: + if arg._array_ndims >= 1: + # Create the dimensions array symbol + dims_array_symbol = self.symtab.find_or_create( + "dims_" + arg.name, + symbol_type=DataSymbol, + datatype=ArrayType( + LFRicTypes("LFRicIntegerScalarDataType")(), + [arg._array_ndims])) + dims_array_symbol.interface = ArgumentInterface( + INTENT_MAPPING[intent]) + self.symtab.append_argument(dims_array_symbol) + # Create list of dims_array references + sym_list = [ArrayReference.create( + dims_array_symbol, + [Literal(str(idx), INTEGER_TYPE)]) + for idx in range(1, arg._array_ndims + 1)] + if not self._kernel: + # For code generation, find ScalarArray tag + # and convert it to an ArrayType + array_symbol = self.symtab.lookup_with_tag( + "AlgArgs_" + arg.name) + array_symbol.datatype = ArrayType( + type_map[arg.intrinsic_type], + sym_list) + # Replace the symbol with itself to ensure + # the ScalarArray is generated after the + # dimensions array to avoid compilation errors + self.symtab.swap(array_symbol, array_symbol) + else: + # For stub generation, create the symbol + array_symbol = self.symtab.find_or_create( + arg.name, + symbol_type=DataSymbol, + datatype=ArrayType( + type_map[arg.intrinsic_type], + sym_list)) + array_symbol.interface = ArgumentInterface( + INTENT_MAPPING[intent]) + self.symtab.append_argument(array_symbol) + + +# ---------- Documentation utils -------------------------------------------- # +# The list of module members that we wish AutoAPI to generate +# documentation for. +__all__ = ['LFRicScalarArrayArgs'] diff --git a/src/psyclone/lfric.py b/src/psyclone/lfric.py index a662e81149..0e77fb2d8c 100644 --- a/src/psyclone/lfric.py +++ b/src/psyclone/lfric.py @@ -32,7 +32,8 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors R. W. Ford, A. R. Porter and S. Siso, STFC Daresbury Lab -# Modified I. Kavcic, A. Coughtrie, L. Turner and O. Brunt, Met Office +# Modified I. Kavcic, A. Coughtrie, L. Turner, O. Brunt +# and A. Pirrie, Met Office # Modified J. Henrichs, Bureau of Meteorology # Modified A. B. G. Chalk and N. Nobre, STFC Daresbury Lab @@ -56,8 +57,7 @@ from psyclone.domain.lfric.lfric_builtins import LFRicBuiltIn from psyclone.domain.lfric import ( FunctionSpace, KernCallAccArgList, KernCallArgList, LFRicCollection, - LFRicConstants, LFRicSymbolTable, LFRicKern, - LFRicTypes, LFRicLoop) + LFRicConstants, LFRicSymbolTable, LFRicKern, LFRicTypes, LFRicLoop) from psyclone.domain.lfric.lfric_invoke_schedule import LFRicInvokeSchedule from psyclone.errors import GenerationError, InternalError, FieldNotFoundError from psyclone.parse.kernel import getkerneldescriptors @@ -1479,8 +1479,8 @@ def initialise(self, cursor: int) -> int: ''' init_cursor = cursor for arg in self._invoke.psy_unique_vars: - # We don't have proxies for scalars - if arg.is_scalar: + # We don't have proxies for scalars or arrays of scalars. + if arg.is_scalar or arg.is_scalar_array: continue const = LFRicConstants() @@ -1934,7 +1934,7 @@ def __init__(self, invoke, unique_psy_vars): # kernels in this invoke. self._first_var = None for var in unique_psy_vars: - if not var.is_scalar: + if not (var.is_scalar or var.is_scalar_array): self._first_var = var break @@ -5675,6 +5675,8 @@ def __init__(self, kernel_args, arg_meta_data, arg_info, call, check=True): # any-space function spaces. self._kernel_args = kernel_args self._vector_size = arg_meta_data.vector_size + # The number of dimensions (for a ScalarArray) + self._array_ndims = arg_meta_data.array_ndims self._argument_type = arg_meta_data.argument_type self._stencil = None if arg_meta_data.mesh: @@ -5888,7 +5890,7 @@ def _init_data_type_properties(self, arg_info, check=True): # The collection datatype is not recognised or supported. alg_datatype = None - if self.is_scalar: + if self.is_scalar or self.is_scalar_array: self._init_scalar_properties(alg_datatype, alg_precision, check) elif self.is_field: @@ -6146,6 +6148,15 @@ def is_scalar(self): const = LFRicConstants() return self._argument_type in const.VALID_SCALAR_NAMES + @property + def is_scalar_array(self) -> bool: + ''' + :returns: True if this kernel argument represents a + ScalarArray, False otherwise. + ''' + const = LFRicConstants() + return self._argument_type in const.VALID_ARRAY_NAMES + @property def is_field(self): ''' @@ -6254,7 +6265,7 @@ def psyir_expression(self): f"PSyIR contains one or more References.") return lit - if self.is_scalar: + if self.is_scalar or self.is_scalar_array: try: scalar_sym = symbol_table.lookup(self.name) except KeyError: @@ -6509,7 +6520,7 @@ def _find_or_create_type(mod_name: str, symbol_type=ContainerSymbol) )) - if self.is_scalar: + if self.is_scalar or self.is_scalar_array: # Find or create the DataType for the appropriate scalar type. if self.intrinsic_type == "real": prim_type = ScalarType.Intrinsic.REAL @@ -6539,7 +6550,12 @@ def _find_or_create_type(mod_name: str, kind_name, INTEGER_TYPE, interface=ImportInterface(constants_container)) symtab.add(kind_symbol) - return ScalarType(prim_type, Reference(kind_symbol)) + dts = ScalarType(prim_type, Reference(kind_symbol)) + if self.is_scalar_array and self._array_ndims >= 1: + # ScalarArray needs to have a number of dimensions + # greater than or equal to one + return ArrayType(dts, [self._array_ndims]) + return dts if self.is_field or self.is_operator: # Find or create the DataTypeSymbol for the appropriate diff --git a/src/psyclone/tests/domain/lfric/arg_ordering_test.py b/src/psyclone/tests/domain/lfric/arg_ordering_test.py index ae0a615a92..200fbece56 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 and L. Turner, Met Office +# Modified I. Kavcic, L. Turner and A. Pirrie, Met Office # Modified J. Henrichs, Bureau of Meteorology ''' This module tests the LFric classes based on ArgOrdering.''' @@ -214,7 +214,8 @@ def test_kernel_stub_invalid_scalar_argument(): with pytest.raises(InternalError) as excinfo: create_arg_list.scalar(arg) const = LFRicConstants() - assert (f"Expected argument type to be one of {const.VALID_SCALAR_NAMES} " + assert (f"Expected argument type to be one of " + f"{const.VALID_SCALAR_NAMES + const.VALID_ARRAY_NAMES} " f"but got 'invalid'" in str(excinfo.value)) 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 467b6b2b8a..5dcaf22d30 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 @@ -241,3 +241,28 @@ def test_lfric_field(): assert "f2_data: READ" in var_info assert "m1_data: READ" in var_info assert "m2_data: READ" in var_info + + +def test_lfric_scalar(): + '''Check that the scalar method throws a NotImplementedError for + ScalarArrays. + + ''' + # Use the OpenACC transforms to create the required kernels + acc_par_trans = ACCParallelTrans() + acc_enter_trans = ACCEnterDataTrans() + _, invoke = get_invoke("28.scalar_array_invoke.f90", + "lfric", + idx=0, dist_mem=False) + sched = invoke.schedule + acc_par_trans.apply(sched.children) + acc_enter_trans.apply(sched) + + # Find the first kernel: + kern = invoke.schedule.walk(psyGen.CodedKern)[0] + create_acc_arg_list = KernCallAccArgList(kern) + var_accesses = VariablesAccessMap() + with pytest.raises(NotImplementedError) as excinfo: + create_acc_arg_list.generate(var_accesses=var_accesses) + assert ("OpenACC data regions are not currently supported for arrays" + " of scalars.") in str(excinfo) diff --git a/src/psyclone/tests/domain/lfric/lfric_scalar_codegen_test.py b/src/psyclone/tests/domain/lfric/lfric_scalar_codegen_test.py index f0f1dd82da..338103419c 100644 --- a/src/psyclone/tests/domain/lfric/lfric_scalar_codegen_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_scalar_codegen_test.py @@ -32,7 +32,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors R. W. Ford, A. R. Porter and S. Siso, STFC Daresbury Lab; -# I. Kavcic and A. Coughtrie, Met Office; +# I. Kavcic, A. Coughtrie and A. Pirrie, Met Office; # C. M. Maynard, Met Office/University of Reading; # J. Henrichs, Bureau of Meteorology. @@ -45,6 +45,7 @@ from psyclone.parse.algorithm import parse from psyclone.psyGen import PSyFactory from psyclone.tests.lfric_build import LFRicBuild +from psyclone.tests.utilities import get_invoke # Constants BASE_PATH = os.path.join( @@ -603,3 +604,89 @@ def test_three_scalars(tmpdir): " map_w3(:,cell))\n") assert expected in generated_code assert LFRicBuild(tmpdir).code_compiles(psy) + + +def test_scalar_array(tmpdir): + ''' Tests that we generate correct code when a kernel has all three + types of valid scalar array argument: 'real', 'integer' and 'logical'. + + ''' + psy, invoke = get_invoke("28.scalar_array_invoke.f90", TEST_API, idx=0) + + generated_code = str(psy.gen) + expected = ( + "module scalar_array_invoke_psy\n" + " use constants_mod\n" + " use field_mod, only : field_proxy_type, field_type\n" + " implicit none\n" + " public\n" + "\n" + " contains\n" + " subroutine invoke_0_testkern_scalar_array_type(afield, " + "real_array, logical_array, integer_array, a_scalar, " + "dims_real_array, dims_logical_array, dims_integer_array)\n" + " use mesh_mod, only : mesh_type\n" + " use testkern_scalar_array_mod, only : " + "testkern_scalar_array_code\n" + " type(field_type), intent(in) :: afield\n" + " integer(kind=i_def), intent(in) :: a_scalar\n" + " integer(kind=i_def), dimension(2), intent(in) :: " + "dims_real_array\n" + " real(kind=r_def), dimension(dims_real_array(1)," + "dims_real_array(2)), intent(in) :: real_array\n" + " integer(kind=i_def), dimension(1), intent(in) :: " + "dims_logical_array\n" + " logical(kind=l_def), dimension(dims_logical_array(1)), " + "intent(in) :: logical_array\n" + " integer(kind=i_def), dimension(4), intent(in) :: " + "dims_integer_array\n" + " integer(kind=i_def), dimension(dims_integer_array(1)," + "dims_integer_array(2),dims_integer_array(3),dims_integer_array(4)), " + "intent(in) :: integer_array\n" + " integer(kind=i_def) :: cell\n" + " type(mesh_type), pointer :: mesh => null()\n" + " integer(kind=i_def) :: max_halo_depth_mesh\n" + " real(kind=r_def), pointer, dimension(:) :: afield_data => " + "null()\n" + " integer(kind=i_def) :: nlayers_afield\n" + " integer(kind=i_def) :: ndf_w1\n" + " integer(kind=i_def) :: undf_w1\n" + " integer(kind=i_def), pointer :: map_w1(:,:) => null()\n" + " type(field_proxy_type) :: afield_proxy\n" + " integer(kind=i_def) :: loop0_start\n" + " integer(kind=i_def) :: loop0_stop\n" + "\n" + " ! Initialise field and/or operator proxies\n" + " afield_proxy = afield%get_proxy()\n" + " afield_data => afield_proxy%data\n" + "\n" + " ! Initialise number of layers\n" + " nlayers_afield = afield_proxy%vspace%get_nlayers()\n" + "\n" + " ! Create a mesh object\n" + " mesh => afield_proxy%vspace%get_mesh()\n" + " max_halo_depth_mesh = mesh%get_halo_depth()\n" + "\n" + " ! Look-up dofmaps for each function space\n" + " map_w1 => afield_proxy%vspace%get_whole_dofmap()\n" + "\n" + " ! Initialise number of DoFs for w1\n" + " ndf_w1 = afield_proxy%vspace%get_ndf()\n" + " undf_w1 = afield_proxy%vspace%get_undf()\n" + "\n" + " ! Set-up all of the loop bounds\n" + " loop0_start = 1\n" + " loop0_stop = mesh%get_last_halo_cell(1)\n" + "\n" + " ! Call kernels and communication routines\n" + " if (afield_proxy%is_dirty(depth=1)) then\n" + " call afield_proxy%halo_exchange(depth=1)\n" + " end if\n" + " do cell = loop0_start, loop0_stop, 1\n" + " call testkern_scalar_array_code(nlayers_afield, " + "afield_data, dims_real_array, real_array, dims_logical_array, " + "logical_array, dims_integer_array, integer_array, a_scalar, " + "ndf_w1, undf_w1, map_w1(:,cell))\n" + ) + assert expected in generated_code + assert LFRicBuild(tmpdir).code_compiles(psy) 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 02febf13ce..24872e7d91 100644 --- a/src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_scalar_mdata_test.py @@ -32,7 +32,8 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors R. W. Ford, A. R. Porter, S. Siso and N. Nobre, STFC Daresbury Lab; -# I. Kavcic, A. Coughtrie, L. Turner and O. Brunt, Met Office; +# I. Kavcic, A. Coughtrie, L. Turner, O. Brunt +# and A. Pirrie, Met Office; # C. M. Maynard, Met Office/University of Reading; # J. Henrichs, Bureau of Meteorology. @@ -48,7 +49,7 @@ from psyclone.domain.lfric import (LFRicArgDescriptor, LFRicConstants, LFRicKern, LFRicKernMetadata, - LFRicScalarArgs) + LFRicScalarArgs, LFRicScalarArrayArgs) from psyclone.errors import InternalError, GenerationError from psyclone.parse.algorithm import parse from psyclone.parse.utils import ParseError @@ -331,6 +332,30 @@ def test_lfricscalars_call_err1(): in str(err.value)) +def test_lfricscalararray_call_err1(): + ''' Check that the LFRicScalarArrayArgs constructor raises the + expected internal error if it encounters an unrecognised + intrinsic type of ScalarArray when generating a kernel call. + + ''' + _, invoke_info = parse( + os.path.join(BASE_PATH, + "28.scalar_array_invoke.f90"), + api=TEST_API) + psy = PSyFactory(TEST_API, distributed_memory=True).create(invoke_info) + invoke = psy.invokes.invoke_list[0] + kernel = invoke.schedule.coded_kernels()[0] + # Sabotage the scalar argument to make it have an invalid intrinsic type + scalar_arr_arg = kernel.arguments.args[1] + scalar_arr_arg._intrinsic_type = "double-type" + with pytest.raises(InternalError) as err: + LFRicScalarArrayArgs(invoke).invoke_declarations() + assert ("Found unsupported intrinsic types for the ScalarArray arguments " + "['real_array'] to Invoke 'invoke_0_testkern_scalar_array_type'. " + "Supported types are ['real', 'integer', 'logical']." + in str(err.value)) + + def test_lfricscalarargs_mp(): '''Check that the precision of a new scalar integer datatype is declared in the psy-layer. @@ -513,3 +538,23 @@ def test_scalar_different_data_types_invoke(): f"'invoke_real_and_integer_scalars' have different metadata for " f"data type ({const.VALID_SCALAR_DATA_TYPES}) in different " f"kernels. This is invalid." in str(excinfo.value)) + + +def test_scalar_array_different_data_types_invoke(): + ''' Tests that the same scalar cannot have different data types + in different kernels within the same Invoke. + + ''' + _, invoke_info = parse( + os.path.join(BASE_PATH, + "28.1_multikernel_invokes_scalar_array_invalid.f90"), + api=TEST_API) + psy = PSyFactory(TEST_API, distributed_memory=False).create(invoke_info) + + const = LFRicConstants() + with pytest.raises(GenerationError) as excinfo: + _ = psy.gen + assert (f"ScalarArray argument(s) ['b'] in Invoke " + f"'invoke_real_and_logical_scalars' have different metadata for " + f"data type ({const.VALID_SCALAR_DATA_TYPES}) in different " + f"kernels. This is invalid." in str(excinfo.value)) 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 edfdfda32e..e936bcc0b0 100644 --- a/src/psyclone/tests/domain/lfric/lfric_scalar_stubgen_test.py +++ b/src/psyclone/tests/domain/lfric/lfric_scalar_stubgen_test.py @@ -32,7 +32,8 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors: R. W. Ford, A. R. Porter, S. Siso and N. Nobre, STFC Daresbury Lab; -# I. Kavcic, A. Coughtrie, L. Turner and O. Brunt, Met Office; +# I. Kavcic, A. Coughtrie, L. Turner, O. Brunt, +# and A. Pirrie, Met Office; # C. M. Maynard, Met Office/University of Reading; # J. Henrichs, Bureau of Meteorology. @@ -46,7 +47,8 @@ from fparser import api as fpapi from psyclone.domain.lfric import (LFRicConstants, LFRicKern, - LFRicKernMetadata, LFRicScalarArgs) + LFRicKernMetadata, LFRicScalarArgs, + LFRicScalarArrayArgs) from psyclone.errors import InternalError from psyclone.gen_kernel_stub import generate from psyclone.parse.utils import ParseError @@ -82,6 +84,29 @@ def test_lfricscalars_stub_err(): f"{const.VALID_SCALAR_DATA_TYPES}." in str(err.value)) +def test_lfricscalararray_stub_err(): + ''' Check that LFRicScalarArrayArgs.stub_declarations() raises the + expected internal error if it encounters an unrecognised data + type of a scalar argument when generating a kernel stub. + + ''' + ast = fpapi.parse(os.path.join(BASE_PATH, + "testkern_scalar_array_mod.f90"), + ignore_comments=False) + metadata = LFRicKernMetadata(ast) + kernel = LFRicKern() + kernel.load_meta(metadata) + # Sabotage the scalar argument to make it have an invalid data type + arg = kernel.arguments.args[1] + arg.descriptor._data_type = "gh_invalid_scalar" + with pytest.raises(InternalError) as err: + LFRicScalarArrayArgs(kernel).stub_declarations() + const = LFRicConstants() + assert (f"Found an unsupported data type 'gh_invalid_scalar' for the " + f"ScalarArray argument 'rscalar_array_2'. Supported types are " + f"{const.VALID_SCALAR_DATA_TYPES}." in str(err.value)) + + def test_stub_generate_with_scalars(): ''' Check that the stub generate produces the expected output when the kernel has scalar arguments. ''' @@ -136,3 +161,46 @@ def test_stub_generate_with_scalar_sums_err(): "A user-supplied LFRic kernel must not write/update a scalar " "argument but kernel 'simple_with_reduction_type' has a scalar " "argument with 'gh_sum' access." in str(err.value)) + + +def test_stub_generate_with_scalar_array(): + ''' Check that the stub generate produces the expected output when + the kernel has ScalarArray arguments. ''' + result = generate( + os.path.join(BASE_PATH, "testkern_scalar_array_mod.f90"), + api=TEST_API) + + expected = """\ +module testkern_scalar_array_mod + implicit none + public + + contains + subroutine testkern_scalar_array_code(nlayers, field_1_w1, \ +dims_rscalar_array_2, rscalar_array_2, dims_lscalar_array_3, \ +lscalar_array_3, dims_iscalar_array_4, iscalar_array_4, \ +iscalar_5, ndf_w1, undf_w1, map_w1) + use constants_mod + integer(kind=i_def), intent(in) :: nlayers + integer(kind=i_def), intent(in) :: ndf_w1 + integer(kind=i_def), dimension(ndf_w1), intent(in) :: map_w1 + integer(kind=i_def), intent(in) :: undf_w1 + integer(kind=i_def), intent(in) :: iscalar_5 + integer(kind=i_def), dimension(2), intent(in) :: dims_rscalar_array_2 + real(kind=r_def), dimension(dims_rscalar_array_2(1),\ +dims_rscalar_array_2(2)), intent(in) :: rscalar_array_2 + integer(kind=i_def), dimension(1), intent(in) :: dims_lscalar_array_3 + logical(kind=l_def), dimension(dims_lscalar_array_3(1)), intent(in) :: \ +lscalar_array_3 + integer(kind=i_def), dimension(4), intent(in) :: dims_iscalar_array_4 + integer(kind=i_def), dimension(dims_iscalar_array_4(1),\ +dims_iscalar_array_4(2),dims_iscalar_array_4(3),\ +dims_iscalar_array_4(4)), intent(in) :: iscalar_array_4 + real(kind=r_def), dimension(undf_w1), intent(inout) :: field_1_w1 + + + end subroutine testkern_scalar_array_code + +end module testkern_scalar_array_mod +""" + assert expected == result diff --git a/src/psyclone/tests/lfric_test.py b/src/psyclone/tests/lfric_test.py index 7bfc88dc97..a7a3ea5dfb 100644 --- a/src/psyclone/tests/lfric_test.py +++ b/src/psyclone/tests/lfric_test.py @@ -32,7 +32,8 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- # Authors R. W. Ford, A. R. Porter and S. Siso, STFC Daresbury Lab -# Modified I. Kavcic, A. Coughtrie, L. Turner and O. Brunt, Met Office, +# Modified I. Kavcic, A. Coughtrie, L. Turner, O. Brunt, +# and A. Pirrie Met Office, # C. M. Maynard, Met Office/University of Reading, # J. Henrichs, Bureau of Meteorology. @@ -1702,6 +1703,30 @@ class for a scalar. "not." in str(info.value)) +def test_lfrickernelargument_idtp_scalar_array(): + '''Test the _init_data_type_properties method in the LFRicKernelArgument + class for a ScalarArray. + + ''' + # Use one of the examples to create an instance of + # LFRicKernelArgument that describes a ScalarArray. + _, invoke_info = parse(os.path.join(BASE_PATH, + "28.scalar_array_invoke.f90"), + api=TEST_API) + psy = PSyFactory(TEST_API, distributed_memory=False).create(invoke_info) + scalar_argument = psy.invokes.invoke_list[0].schedule.args[1] + assert not scalar_argument.is_scalar + assert scalar_argument.is_scalar_array + + scalar_argument = psy.invokes.invoke_list[0].schedule.args[2] + assert not scalar_argument.is_scalar + assert scalar_argument.is_scalar_array + + scalar_argument = psy.invokes.invoke_list[0].schedule.args[3] + assert not scalar_argument.is_scalar + assert scalar_argument.is_scalar_array + + def test_lfrickernelargument_idtp_reduction(): '''Test the _init_data_type_properties method in the LFRicKernelArgument class for a scalar reduction. diff --git a/src/psyclone/tests/test_files/lfric/28.1_multikernel_invokes_scalar_array_invalid.f90 b/src/psyclone/tests/test_files/lfric/28.1_multikernel_invokes_scalar_array_invalid.f90 new file mode 100644 index 0000000000..c6d05387f0 --- /dev/null +++ b/src/psyclone/tests/test_files/lfric/28.1_multikernel_invokes_scalar_array_invalid.f90 @@ -0,0 +1,62 @@ +! ----------------------------------------------------------------------------- +! BSD 3-Clause License +! +! Copyright (c) 2025, 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. +!------------------------------------------------------------------------------- +! Author A. Pirrie, Met Office + +program multikernel_invokes_scalar_array_invalid + + ! Description: two kernel calls with the first kernel using a real + ! ScalarArray and the second kernel usign a logical ScalarArray + ! incorrectly passing a ScalarArray that would need to be both real + ! and logical. This is provided from a module to stop PSyclone's type + ! checking from raising an exception. + use constants_mod, only: i_def, r_def, l_def + use field_mod, only: field_type + use testkern_scalar_array_mod, only: testkern_scalar_array_type + + use unknown_mod, only : b + + implicit none + + type(field_type) :: afield + real(r_def), dimension(50, 100) :: real_array + logical(l_def), dimension(10) :: logical_array + integer(i_def), dimension(2, 5, 10, 8) :: integer_array + integer(i_def) :: a_scalar + + call invoke(name = "real_and_logical_scalars", & + testkern_scalar_array_type(afield,b,logical_array,integer_array,a_scalar), & + testkern_scalar_array_type(afield,real_array,b,integer_array,a_scalar) & + ) + +end program multikernel_invokes_scalar_array_invalid diff --git a/src/psyclone/tests/test_files/lfric/28.scalar_array_invoke.f90 b/src/psyclone/tests/test_files/lfric/28.scalar_array_invoke.f90 new file mode 100644 index 0000000000..94900c9352 --- /dev/null +++ b/src/psyclone/tests/test_files/lfric/28.scalar_array_invoke.f90 @@ -0,0 +1,55 @@ +! ----------------------------------------------------------------------------- +! BSD 3-Clause License +! +! Copyright (c) 2025, 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. +!------------------------------------------------------------------------------- +! Author A. Pirrie, Met Office + +program scalar_array_invoke + + ! Description: single function specified in an invoke call + use constants_mod, only: i_def, r_def, l_def + use field_mod, only: field_type + use testkern_scalar_array_mod, only: testkern_scalar_array_type + + implicit none + + type(field_type) :: afield + real(kind=r_def), dimension(50, 100) :: real_array + logical(kind=l_def), dimension(10) :: logical_array + integer(kind=i_def), dimension(2, 5, 10, 8) :: integer_array + integer(kind=i_def) :: a_scalar + + call invoke( & + testkern_scalar_array_type(afield,real_array,logical_array,integer_array,a_scalar) & + ) + +end program scalar_array_invoke diff --git a/src/psyclone/tests/test_files/lfric/testkern_scalar_array_mod.f90 b/src/psyclone/tests/test_files/lfric/testkern_scalar_array_mod.f90 new file mode 100644 index 0000000000..5bf16d0100 --- /dev/null +++ b/src/psyclone/tests/test_files/lfric/testkern_scalar_array_mod.f90 @@ -0,0 +1,81 @@ +! ----------------------------------------------------------------------------- +! BSD 3-Clause License +! +! Copyright (c) 2025, 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. +! ----------------------------------------------------------------------------- +! Author A. Pirrie, Met Office + +module testkern_scalar_array_mod + + use constants_mod + use argument_mod + use fs_continuity_mod + use kernel_mod + + implicit none + + type, extends(kernel_type) :: testkern_scalar_array_type + type(arg_type), dimension(5) :: meta_args = & + (/ arg_type(gh_field, gh_real, gh_inc, w1), & + arg_type(gh_scalar_array, gh_real, gh_read, 2 ), & + arg_type(gh_scalar_array, gh_logical, gh_read, 1 ), & + arg_type(gh_scalar_array, gh_integer, gh_read, 4 ), & + arg_type(gh_scalar, gh_integer, gh_read ) & + /) + integer :: operates_on = cell_column + contains + procedure, nopass :: code => testkern_scalar_array_code + end type testkern_scalar_array_type + +contains + + subroutine testkern_scalar_array_code(nlayers, afield, & + dims_rarray, real_array, & + dims_larray, logical_array, & + dims_iarray, integer_array, & + a_scalar, ndf_w1, undf_w1, & + map_w1) + implicit none + + integer(kind=i_def), intent(in) :: nlayers + integer(kind=i_def), intent(in), dimension(2) :: dims_rarray + real(kind=r_def), intent(in), dimension(dims_rarray(1),dims_rarray(2)) :: real_array + integer(kind=i_def), intent(in), dimension(1) :: dims_larray + logical(kind=l_def), intent(in), dimension(dims_larray(1)) :: logical_array + integer(kind=i_def), intent(in), dimension(4) :: dims_iarray + integer(kind=i_def), intent(in), dimension(dims_iarray(1),dims_iarray(2),dims_iarray(3),dims_iarray(4)) :: integer_array + integer(kind=i_def), intent(in) :: a_scalar + integer(kind=i_def), intent(in) :: ndf_w1 + integer(kind=i_def), intent(in) :: undf_w1 + real(kind=r_def), intent(inout), dimension(undf_w1) :: afield + integer(kind=i_def), intent(in), dimension(ndf_w1) :: map_w1 + + end subroutine testkern_scalar_array_code + +end module testkern_scalar_array_mod