Source code for psyclone.domain.lfric.lfric_global_reductions

# -----------------------------------------------------------------------------
# BSD 3-Clause License
#
# Copyright (c) 2017-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.
# -----------------------------------------------------------------------------
# Authors R. W. Ford, A. R. Porter and S. Siso, STFC Daresbury Lab


'''Provides the LFRic-specific global reduction nodes.'''

from psyclone.domain.common.psylayer import GlobalReduction
from psyclone.errors import GenerationError
from psyclone.lfric import LFRicKernelArgument
from psyclone.psyGen import InvokeSchedule
from psyclone.psyir.nodes import (
    Assignment, Call, Node, Reference, StructureReference)
from psyclone.psyir.symbols import (
    ContainerSymbol, DataSymbol, DataTypeSymbol, ImportInterface,
    UnresolvedType)


class _LFRicGlobalReduction(GlobalReduction):
    '''
    LFRic-specific class representing a GlobalReduction which can be added to
    and manipulated in a schedule. Cannot be directly instantiated - use one
    of the specific sub-classes instead.

    :param scalar: the kernel argument for which to perform a global reduction.
    :param parent: the parent node of this node in the PSyIR.

    :raises GenerationError: if the scalar is not of real or integer
                             intrinsic type.
    '''
    _text_name = "LFRicGlobalReduction"
    # Needs to be set in sub-class.
    _reduction_type = ""

    def __new__(cls, *args, **kwargs):
        # We can't rely on abc.ABC to prevent this class from being
        # instantiated as it doesn't have any abstract methods.
        if cls is _LFRicGlobalReduction:
            raise TypeError(
                f"Only sub-classes of '{cls.__name__}' may be instantiated")
        return GlobalReduction.__new__(cls)

    def __init__(self, scalar: LFRicKernelArgument, parent: Node = None):
        # Initialise the parent class
        super().__init__(scalar, parent=parent)
        # Check scalar intrinsic types that this class supports.
        if scalar.intrinsic_type not in ["real", "integer"]:
            raise GenerationError(
                f"{self._text_name} only supports real or integer scalars, "
                f"but argument '{scalar.name}' in Kernel '{scalar.call.name}' "
                f"has '{scalar.intrinsic_type}' intrinsic type.")

    def lower_to_language_level(self) -> Node:
        ''':returns: this node lowered to language-level PSyIR.

        '''
        # Get the name of the LFRic scalar type and the module from which
        # to import it.
        name = self._scalar.name
        type_name = self._scalar.data_type
        mod_name = self._scalar.module_name

        # Get the symbols from the given names.
        symtab = self.ancestor(InvokeSchedule).symbol_table
        # The Container from which to import the scalar type.
        scal_mod = symtab.find_or_create(mod_name, symbol_type=ContainerSymbol)
        # The scalar type.
        scal_type = symtab.find_or_create(type_name,
                                          symbol_type=DataTypeSymbol,
                                          datatype=UnresolvedType(),
                                          interface=ImportInterface(scal_mod))
        # An instance of scalar type that we will use to get the global
        # min/max/sum.
        red_name = symtab.find_or_create_tag(f"global_{self._reduction_type}",
                                             symbol_type=DataSymbol,
                                             datatype=scal_type)
        tmp_var = symtab.lookup(name)

        # Assign the value of the local scalar to the new scalar_type quantity
        assign1 = Assignment.create(
            lhs=StructureReference.create(red_name, ["value"]),
            rhs=Reference(tmp_var)
        )
        assign1.preceding_comment = f"Perform global {self._reduction_type}"
        self.parent.addchild(assign1, self.position)
        # Use the 'get_xxx' method to compute the global xxx (min/max/sum).
        assign2 = Assignment.create(
            lhs=Reference(tmp_var),
            rhs=Call.create(StructureReference.create(
                red_name, [f"get_{self._reduction_type}"]))
        )
        return self.replace_with(assign2)


[docs] class LFRicGlobalSum(_LFRicGlobalReduction): ''' LFRic-specific global sum class which can be added to and manipulated in a schedule. ''' _text_name = "LFRicGlobalSum" _reduction_type = "sum"
[docs] class LFRicGlobalMin(_LFRicGlobalReduction): ''' LFRic-specific global min class which can be added to and manipulated in a schedule. Represents finding the global minimum value of a scalar. ''' _text_name = "LFRicGlobalMin" _reduction_type = "min"
[docs] class LFRicGlobalMax(_LFRicGlobalReduction): ''' LFRic-specific global max class which can be added to and manipulated in a schedule. Represents finding the global maximum value of a scalar. ''' _text_name = "LFRicGlobalMax" _reduction_type = "max"
# For AutoAPI documentation generation. __all__ = ["LFRicGlobalSum", "LFRicGlobalMin", "LFRicGlobalMax"]