Source code for psyclone.domain.lfric.lfric_scalar_array_args

# -----------------------------------------------------------------------------
# BSD 3-Clause License
#
# Copyright (c) 2025-2026, 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,
                                    ScalarType, ArgumentInterface)

# pylint: disable=too-many-branches


[docs] class LFRicScalarArrayArgs(LFRicCollection): ''' Handles the declarations of ScalarArray kernel arguments appearing in either an Invoke or a Kernel stub. :param node: the Invoke or Kernel stub for which to manage the \ ScalarArray arguments. ''' def __init__(self, node: Union[LFRicKern, LFRicInvoke]): super().__init__(node) # Initialise a dictionary keyed by intent ('in', 'out', 'inout') # with values to be populated with lists of ScalarArray arguments self._scalar_array_args = {} for intent in FORTRAN_INTENT_NAMES: self._scalar_array_args[intent] = []
[docs] 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}' is/are passed to more than one " f"kernel and the kernel metadata for the corresponding " f"arguments specifies different intrinsic types.") # Create declarations self._create_declarations()
[docs] 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() const = LFRicConstants() type_map = {"real": LFRicTypes("LFRicRealScalarDataType")(), "integer": LFRicTypes("LFRicIntegerScalarDataType")(), "logical": LFRicTypes("LFRicLogicalScalarDataType")()} # Extract all ScalarArray arguments for arg in self.kernel_calls[0].arguments.args: if arg.is_scalar_array: # Check whether the ScalarArrays are of a supported data # type 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}.") self._scalar_array_args[arg.intent].append(arg) # Create declarations for intent in FORTRAN_INTENT_NAMES: for arg in self._scalar_array_args[intent]: # Create the dimensions array symbol dims_array_symbol = self.symtab.find_or_create_tag( tag="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), ScalarType.integer_type())]) for idx in range(1, arg._array_ndims + 1)] # Create the symbol array_symbol = self.symtab.find_or_create_tag( tag=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)
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]: # Create the dimensions array symbol dims_array_symbol = self.symtab.find_or_create_tag( tag="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), ScalarType.integer_type())]) for idx in range(1, arg._array_ndims + 1)] # Find the 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 # TODO: #2202 may allow this to be removed self.symtab.swap(array_symbol, array_symbol) 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']