Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
7b82dae
#1312: Add scalar_array to examples
mo-alistairp Jul 31, 2026
c897778
#1312: Update Makefile to try to compile eg1
mo-alistairp Aug 6, 2026
237920e
#1312: Add dims_array to the kern_call_invoke_arg_list
mo-alistairp Aug 6, 2026
a8d0806
#1312: remove reference import
mo-alistairp Aug 6, 2026
730eece
#1312: Use scalar array in conftest to test functionality in KernCall…
mo-alistairp Aug 6, 2026
fafec08
Merge branch 'master' into 1312_fix_scalar_array_functionality_invoke…
mo-alistairp Aug 7, 2026
e37fe23
#1312: Update the Makefile to include compilation
mo-alistairp Aug 11, 2026
4cad066
Merge branch '1312_fix_scalar_array_functionality_invoke_call' of git…
mo-alistairp Aug 11, 2026
7744557
Merge branch 'master' into 1312_fix_scalar_array_functionality_invoke…
mo-alistairp Aug 14, 2026
f2a4d17
#1312: Add some documentation of scalar array examples
mo-alistairp Aug 28, 2026
1b99c30
Merge branch 'master' into 1312_fix_scalar_array_functionality_invoke…
arporter Sep 3, 2026
904820c
#1312 create and initialise local arrays holding scalar-array extents
arporter Sep 4, 2026
9148f40
#1312 improve test and tweak comment
arporter Sep 4, 2026
b5a7753
Merge branch 'master' into 1312_fix_scalar_array_functionality_invoke…
arporter Sep 4, 2026
7a3e692
#1312 revert changes to invoke_arg_list and add typhints.
arporter Sep 4, 2026
e7ac709
#1312 tweak eg1 so that it links and update docs
arporter Sep 4, 2026
febf249
#1312 improve doc for eg1
arporter Sep 4, 2026
07351fc
Merge branch 'master' into 1312_fix_scalar_array_functionality_invoke…
arporter Sep 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions doc/tutorials_and_examples/lfric_examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ Example 1: Basic Operation

Basic operation of PSyclone with an ``invoke()`` containing two
kernels, one :ref:`user-supplied <lfric-kernel>`, the other a
:ref:`Built-in <lfric-built-ins>`. Code is generated both with and
:ref:`Built-in <lfric-built-ins>`. The user-supplied kernel accepts
field, scalar and scalar-array arguments. Code is generated both with and
without distributed-memory support. Also demonstrates the use of the
``-d`` flag to specify where to search for user-supplied kernel code
(see :ref:`psyclone_command` section for more details).
(see :ref:`psyclone_command` section for more details). Generated
code can be compiled (``make compile``) but not executed.

Example 2: Applying Transformations
-----------------------------------
Expand Down
5 changes: 4 additions & 1 deletion doc/user_guide/lfric.rst
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ with ``GH_SCALAR`` metadata. Scalar arguments can have ``real``,
``integer`` or ``logical`` data type in :ref:`user-defined Kernels
<lfric-kernel-valid-data-type>` (``logical`` data type is not supported
in the :ref:`LFRic Built-ins <lfric-built-ins-dtype-access>`).
See example ``examples/lfric/eg1``.

.. _lfric-array:

Expand All @@ -127,6 +128,7 @@ least rank (number of dimensions) one. Scalar arrays are identified with
``GH_SCALAR_ARRAY`` metadata. As with scalars, array arguments can have
``real``, ``integer`` or ``logical`` data type in
:ref:`user-defined Kernels <lfric-kernel-valid-data-type>`.
See example ``examples/lfric/eg1``.

.. _lfric-field:

Expand Down Expand Up @@ -1242,8 +1244,9 @@ has. More details about the supported function spaces are in subsection
For example, the metadata for a kernel that applies a column-wise
operator to a field might look like::

type(arg_type) :: meta_args(3) = (/ &
type(arg_type) :: meta_args(4) = (/ &
arg_type(GH_FIELD, GH_REAL, GH_INC, W1), &
arg_type(GH_SCALAR_ARRAY, GH_INTEGER, GH_READ, 5), &
arg_type(GH_FIELD, GH_REAL, GH_READ, W2H), &
arg_type(GH_COLUMNWISE_OPERATOR, GH_REAL, GH_READ, W1, W2H) &
/)
Expand Down
24 changes: 14 additions & 10 deletions examples/lfric/code/testkern_mod.F90
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ module testkern_mod
implicit none

type, extends(kernel_type) :: testkern_type
type(arg_type), dimension(5) :: meta_args = &
(/ arg_type(gh_scalar, gh_real, gh_read), &
arg_type(gh_field, gh_real, gh_inc, w1), &
arg_type(gh_field, gh_real, gh_read, w2), &
arg_type(gh_field, gh_real, gh_read, w2), &
arg_type(gh_field, gh_real, gh_read, w3) &
type(arg_type), dimension(6) :: meta_args = &
(/ arg_type(gh_scalar, gh_real, gh_read ), &
arg_type(gh_scalar_array, gh_real, gh_read, 2 ), &
arg_type(gh_field, gh_real, gh_inc, w1), &
arg_type(gh_field, gh_real, gh_read, w2), &
arg_type(gh_field, gh_real, gh_read, w2), &
arg_type(gh_field, gh_real, gh_read, w3) &
/)
integer :: operates_on = cell_column
contains
Expand All @@ -29,10 +30,11 @@ module testkern_mod

contains

subroutine testkern_code(nlayers, ascalar, &
fld1, fld2, fld3, fld4, &
ndf_w1, undf_w1, map_w1, &
ndf_w2, undf_w2, map_w2, &
subroutine testkern_code(nlayers, ascalar, &
dims_ascalar_array, ascalar_array, &
fld1, fld2, fld3, fld4, &
ndf_w1, undf_w1, map_w1, &
ndf_w2, undf_w2, map_w2, &
ndf_w3, undf_w3, map_w3)
implicit none

Expand All @@ -44,6 +46,8 @@ subroutine testkern_code(nlayers, ascalar, &
integer(kind=i_def), intent(in), dimension(ndf_w1) :: map_w1
integer(kind=i_def), intent(in), dimension(ndf_w2) :: map_w2
integer(kind=i_def), intent(in), dimension(ndf_w3) :: map_w3
integer(kind=i_def), intent(in), dimension(2) :: dims_ascalar_array
real(kind=r_def), intent(in), dimension(dims_ascalar_array(1), dims_ascalar_array(2)) :: ascalar_array
real(kind=r_def), intent(in) :: ascalar
real(kind=r_def), intent(inout), dimension(undf_w1) :: fld1
real(kind=r_def), intent(in), dimension(undf_w2) :: fld2
Expand Down
45 changes: 39 additions & 6 deletions examples/lfric/eg1/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,53 @@
# See the full LICENSE file in the project root for details.
# -----------------------------------------------------------------------------

include ../../common.mk
# The compiler to use may be specified via the F90 environment variable
#
# export F90=gfortran
# export F90FLAGS="-g -O0"

include ../lfric_common.mk

OBJ = testkern_mod.o single_invoke_psy.o single_invoke_alg.o

GENERATED_FILES = single_invoke_alg.f90 single_invoke_psy.f90 \
*.mod ${OBJ} *.exe

F90 ?= gfortran
F90FLAGS ?= -g -O0

.PHONY: transform compile run

# The '-d ../code' argument specifies that PSyclone should search the
# '../code' directory when looking for user-supplied kernels.
transform:
@echo "Sequential code:"
${PSYCLONE} -api lfric -d ../code -nodm ./single_invoke.x90
transform: single_invoke_alg.f90
@echo "With distributed-memory support:"
${PSYCLONE} -api lfric -d ../code ./single_invoke.x90

compile: transform
@echo "No compilation supported for lfric/eg1"
single_invoke_alg.f90 single_invoke_psy.f90: single_invoke.x90

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only comment from Codex was that modifying ../code/testkern_mod.F90 should retrigger this target as the psy-layer could be different. It is right (and very smart again, the kernel is not explicitly mentioned here so it knows what single_invoke.x90 have and that kernel metadata changes can produce changes in single_invoke_psy.f90 which is the output of this target).

However, this is a mistake that we have consistently across all examples and I would not explictly mention the specific kernel in the Makefile rule, which would fix the issue, but this would duplicate what is encoded inside the alg file. We could maybe mention the whole directory since it is explictly mentioned in the command, but make it worse the "-d" parameter is recursive. So maybe the right solution is something like https://stackoverflow.com/questions/25005637/makefile-rule-depend-on-directory-content-changes ?

I don't know. I will let you decide if its worth fixing, or leave it be, or just create a separate issue.

@echo "Sequential code:"
${PSYCLONE} -api lfric -d ../code -nodm ./single_invoke.x90 \
-oalg single_invoke_alg.f90 \
-opsy single_invoke_psy.f90

# Note that we compile and link primarily for testing purposes - the
# resulting binary won't run successfully because the example omits
# all the necessary LFRic setup.
compile: $(LFRIC_LIB) single_invoke_psy.o single_invoke_alg.o
$(F90) $(F90FLAGS) -o will_not_run.exe ${OBJ} ${LDFLAGS}

testkern_mod.o: ../code/testkern_mod.F90
$(F90) $(F90FLAGS) -c $<

%.o: %.f90
$(F90) $(F90FLAGS) -c $<

%.o: %.F90
$(F90) $(F90FLAGS) -c $<

single_invoke_alg.o: single_invoke_psy.o
single_invoke_psy.o: testkern_mod.o
testkern_mod.o: $(LFRIC_LIB)

run: compile
@echo "No run targets for lfric/eg1"
15 changes: 11 additions & 4 deletions examples/lfric/eg1/single_invoke.x90
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,25 @@

program single_invoke

! Example of the LFRic domain with a single 'invoke()' that calls two
! Example of the LFRic API with a single 'invoke()' that calls two
! kernels.
use constants_mod, only: r_def
use field_mod, only: field_type
use testkern_mod, only: testkern_type

implicit none

type(field_type) :: f1, f2, m1, m2
real(r_def) :: a
! LFRic field objects.
type(field_type) :: f1, f2, m1, m2
! A simple scalar.
real(r_def) :: a
! A 'scalar array'. The number of dimensions of this array must match
! that specified in the kernel metadata. The extents of the dimensions
! are queried in the generated PSy layer and passed to the kernel
! subroutine. They are therefore not provided to the `invoke` call.
real(r_def), dimension(50,100) :: ascalar_array

call invoke( setval_c(f1, 0.0_r_def), &
testkern_type(a, f1, f2, m1, m2) )
testkern_type(a, ascalar_array, f1, f2, m1, m2) )

end program single_invoke
9 changes: 5 additions & 4 deletions examples/lfric/eg2/multi_invoke_mod.x90
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,17 @@ program multi_invoke

implicit none

type(field_type) :: f1, f2, m1, m2
real(r_def) :: a
type(field_type) :: f1, f2, m1, m2
real(r_def) :: a
real(r_def), dimension(50,100) :: ascalar_array

call invoke( setval_c(f1, 0.0_r_def), &
setval_c(f2, 0.0_r_def), &
setval_c(m1, 0.0_r_def), &
setval_c(m2, 0.0_r_def) )
! This example artificially has two separate 'invoke()' calls. In practice
! these would be merged into a single call.
call invoke( testkern_type(a, f1, f2, m1, m2), &
testkern_type(a, f1, f2, m1, m2) )
call invoke( testkern_type(a, ascalar_array, f1, f2, m1, m2), &
testkern_type(a, ascalar_array, f1, f2, m1, m2) )

end program multi_invoke
7 changes: 3 additions & 4 deletions src/psyclone/domain/lfric/kern_call_invoke_arg_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from psyclone.core import VariablesAccessMap
from psyclone.domain.lfric.arg_ordering import ArgOrdering
from psyclone.domain.lfric.lfric_constants import LFRicConstants
from psyclone.domain.lfric.lfric_kern import LFRicKern
from psyclone.domain.lfric.lfric_types import LFRicTypes
from psyclone.psyir.symbols import (
ArrayType, DataSymbol, DataTypeSymbol, UnresolvedType, SymbolTable,
Expand All @@ -27,15 +28,13 @@ 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.domain.lfric.LFRicKern`
:param symbol_table: the symbol table associated with the routine that \
: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`

:raises TypeError: if supplied symbol table is of incorrect type.

'''
def __init__(self, kern, symbol_table):
def __init__(self, kern: LFRicKern, symbol_table: SymbolTable):
super().__init__(kern)
if not isinstance(symbol_table, SymbolTable):
raise TypeError(
Expand Down
3 changes: 2 additions & 1 deletion src/psyclone/domain/lfric/lfric_invoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,8 @@ def setup_psy_layer_symbols(self):
self.cma_ops, self.boundary_conditions,
self.function_spaces, self.evaluators,
self.reference_element_properties,
self.mesh_properties, self.loop_bounds]:
self.mesh_properties, self.loop_bounds,
self.scalar_array_args]:
cursor = entities.initialise(cursor)

if self.schedule.reductions(reprod=True):
Expand Down
56 changes: 38 additions & 18 deletions src/psyclone/domain/lfric/lfric_scalar_array_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
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.nodes import (
ArrayReference, Assignment, IntrinsicCall, Literal, Reference)
from psyclone.psyir.symbols import (DataSymbol, ArrayType,
ScalarType, ArgumentInterface)

Expand All @@ -32,7 +33,7 @@ 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 \
:param node: the Invoke or Kernel stub for which to manage the
ScalarArray arguments.
'''
def __init__(self, node: Union[LFRicKern, LFRicInvoke]):
Expand Down Expand Up @@ -179,36 +180,55 @@ def _create_declarations(self):
# 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(
# Create the symbol for the dimensions array.
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)
arg._array_ndims*[ArrayType.Extent.ATTRIBUTE])
array_symbol.interface = ArgumentInterface(
INTENT_MAPPING[intent])
self.symtab.append_argument(array_symbol)

def initialise(self, cursor: int) -> int:
'''
Add code to initialise the array holding the extent of each dimension
of any ScalarArray arguments.

:param cursor: position to add the next initialisation statements.

:returns: Updated cursor value.

'''
first = True
# For each ScalarArray argument...
for intent in FORTRAN_INTENT_NAMES:
for arg in self._scalar_array_args[intent]:
# We want dims_array = SHAPE(scalar_array)
dim_array_sym = self.symtab.lookup_with_tag("dims_" + arg.name)
array_symbol = self.symtab.lookup_with_tag(
"AlgArgs_" + arg.name)
shape_call = IntrinsicCall.create(
IntrinsicCall.Intrinsic.SHAPE,
[Reference(array_symbol)])
assign = Assignment.create(lhs=Reference(dim_array_sym),
rhs=shape_call)
if first:
assign.preceding_comment = (
"Store dimensions of ScalarArray arguments")
first = False
self._invoke.schedule.addchild(assign, cursor)
cursor += 1

return cursor


# ---------- Documentation utils -------------------------------------------- #
# The list of module members that we wish AutoAPI to generate
Expand Down
18 changes: 9 additions & 9 deletions src/psyclone/tests/domain/lfric/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ def lfrickern_fixture():
mdata_code = '''
module testkern_field_mod
type, extends(kernel_type) :: testkern_field_type
type(arg_type), meta_args(8) = &
(/ arg_type(gh_scalar, gh_real, gh_read), &
arg_type(gh_field, gh_real, gh_readinc, w0), &
arg_type(gh_field, gh_real, gh_inc, w1), &
arg_type(gh_field*3,gh_integer, gh_read, w2), &
arg_type(gh_field, gh_integer, gh_write, wtheta), &
arg_type(gh_field, gh_integer, gh_read, w3), &
arg_type(gh_scalar, gh_integer, gh_read), &
arg_type(gh_scalar, gh_logical, gh_read) &
type(arg_type), meta_args(8) = &
(/ arg_type(gh_scalar, gh_real, gh_read), &
arg_type(gh_field, gh_real, gh_readinc, w0), &
arg_type(gh_field, gh_real, gh_inc, w1), &
arg_type(gh_field*3, gh_integer, gh_read, w2), &
arg_type(gh_field, gh_integer, gh_write, wtheta), &
arg_type(gh_field, gh_integer, gh_read, w3), &
arg_type(gh_scalar_array, gh_integer, gh_read, 3), &
arg_type(gh_scalar, gh_logical, gh_read) &
/)
type(func_type), dimension(2) :: meta_funcs = &
(/ func_type(w1, gh_basis), &
Expand Down
Loading
Loading