Skip to content

Commit 790bed8

Browse files
authored
Merge branch 'master' into 3330_meta_transform
2 parents 41ac481 + 075f2ec commit 790bed8

14 files changed

Lines changed: 207 additions & 68 deletions

changelog

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2) PR #3455 for #3170. Adds support for fields and operators with
2+
explicit real32 and real64 kinds.
3+
14
1) PR #3472 for #3471. Let the transformations get_options method
25
account for sub-transformation options.
36

config/psyclone.cfg

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ precision_map = i_def: 4,
9191
r_solver: 4,
9292
r_tran: 8,
9393
r_bl: 8,
94-
r_um: 8
94+
r_um: 8,
95+
real64: 8,
96+
real32: 4
9597

9698
# Specify whether we generate code to perform runtime correctness checks.
9799
# Allowed values:

doc/user_guide/lfric.rst

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ Mixed Precision
432432

433433
The LFRic API supports the ability to specify the precision required
434434
by the model via precision variables. To make use of this, the code
435-
developer must declare scalars, arrays, fields and operators in the algorithm
435+
developer must declare scalars, arrays, fields and operators in the Algorithm
436436
layer with the required LFRic-supported precision. In the current
437437
implementation there are two supported precisions for ``REAL`` data and
438438
one each for ``INTEGER`` and ``LOGICAL`` data. The actual precision used in
@@ -471,6 +471,10 @@ associated kernel metadata description and their precision:
471471
+--------------------------+---------------------------------------+-----------+
472472
| R_TRAN_FIELD_TYPE | GH_FIELD, GH_REAL | R_TRAN |
473473
+--------------------------+---------------------------------------+-----------+
474+
| FIELD_REAL32_TYPE | GH_FIELD, GH_REAL | REAL32 |
475+
+--------------------------+---------------------------------------+-----------+
476+
| FIELD_REAL64_TYPE | GH_FIELD, GH_REAL | REAL64 |
477+
+--------------------------+---------------------------------------+-----------+
474478
| INTEGER_FIELD_TYPE | GH_FIELD, GH_INTEGER | I_DEF |
475479
+--------------------------+---------------------------------------+-----------+
476480
| OPERATOR_TYPE | GH_OPERATOR, GH_REAL | R_DEF |
@@ -479,13 +483,18 @@ associated kernel metadata description and their precision:
479483
+--------------------------+---------------------------------------+-----------+
480484
| R_TRAN_OPERATOR_TYPE | GH_OPERATOR, GH_REAL | R_TRAN |
481485
+--------------------------+---------------------------------------+-----------+
486+
| OPERATOR_REAL32_TYPE | GH_OPERATOR, GH_REAL | REAL32 |
487+
+--------------------------+---------------------------------------+-----------+
488+
| OPERATOR_REAL64_TYPE | GH_OPERATOR, GH_REAL | REAL64 |
489+
+--------------------------+---------------------------------------+-----------+
482490
| COLUMNWISE_OPERATOR_TYPE | GH_COLUMNWISE_OPERATOR, GH_REAL | R_SOLVER |
483491
+--------------------------+---------------------------------------+-----------+
484492

485493
As can be seen from the above table, the kernel metadata does not
486494
capture all of the precision options. For example, from the metadata
487495
it is not possible to determine whether a ``REAL`` scalar, ``REAL`` field
488-
or ``REAL`` operator has precision ``R_DEF``, ``R_SOLVER`` or ``R_TRAN``.
496+
or ``REAL`` operator has precision ``R_DEF``, ``R_SOLVER``, ``R_TRAN``
497+
or one of the Fortran intrinsic precisions (``REAL32``, ``REAL64``).
489498

490499
If a scalar, array, field, or operator is specified with a particular
491500
precision in the algorithm layer then any associated kernels that it
@@ -601,6 +610,10 @@ outlined in the table below:
601610
+-------------------------+------------------+--------------+
602611
| ``r_tran_field_type`` | ``real`` | ``r_tran`` |
603612
+-------------------------+------------------+--------------+
613+
| ``field_real32_type`` | ``real`` | ``real32`` |
614+
+-------------------------+------------------+--------------+
615+
| ``field_real64_type`` | ``real`` | ``real64`` |
616+
+-------------------------+------------------+--------------+
604617
| ``integer_field_type`` | ``integer`` | ``i_def`` |
605618
+-------------------------+------------------+--------------+
606619

@@ -701,7 +714,8 @@ a message that indicates the problem.
701714
| Fortran Datatype | Supported Precision |
702715
+==================+==========================+
703716
| ``real`` | ``r_def``, ``r_bl``, |
704-
| | ``r_solver``, ``r_tran`` |
717+
| | ``r_solver``, ``r_tran``,|
718+
| | ``real32``, ``real64`` |
705719
+------------------+--------------------------+
706720
| ``integer`` | ``i_def`` |
707721
+------------------+--------------------------+
@@ -731,6 +745,10 @@ outlined in the table below:
731745
+----------------------------+------------------+--------------+
732746
| ``r_tran_operator_type`` | ``real`` | ``r_tran`` |
733747
+----------------------------+------------------+--------------+
748+
| ``operator_real32_type`` | ``real`` | ``real32`` |
749+
+----------------------------+------------------+--------------+
750+
| ``operator_real64_type`` | ``real`` | ``real64`` |
751+
+----------------------------+------------------+--------------+
734752

735753
.. _lfric-mixed-precision-cma-operators:
736754

src/psyclone/domain/lfric/lfric_constants.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,11 @@ def __init__(self) -> None:
384384

385385
# ---------- Infrastructure module maps -------------------------------
386386

387+
# Those kind symbols used in LFRic that are actually
388+
# Fortran intrinsics (and thus don't come from constants_mod).
389+
LFRicConstants.INTRINSIC_KINDS = ("real32", "real64")
390+
LFRicConstants.FORTRAN_ISO_MOD_NAME = "iso_fortran_env"
391+
387392
# Dictionary allowing us to look-up the name of the Fortran module,
388393
# type and proxy-type associated with each LFRic data structure type.
389394
# Data structure type mandates its proxy name, Fortran intrinsic type
@@ -420,6 +425,18 @@ def __init__(self) -> None:
420425
"proxy_type": "r_bl_field_proxy_type",
421426
"intrinsic": "real",
422427
"kind": "r_bl"},
428+
# 'real'-valued field with explicit 32-bit precision
429+
"r_32_field": {"module": "field_real32_mod",
430+
"type": "field_real32_type",
431+
"proxy_type": "field_real32_proxy_type",
432+
"intrinsic": "real",
433+
"kind": "real32"},
434+
# 'real'-valued field with explicit 64-bit precision
435+
"r_64_field": {"module": "field_real64_mod",
436+
"type": "field_real64_type",
437+
"proxy_type": "field_real64_proxy_type",
438+
"intrinsic": "real",
439+
"kind": "real64"},
423440
# 'integer'-valued field with data of kind 'i_def'
424441
"integer_field": {"module": "integer_field_mod",
425442
"type": "integer_field_type",
@@ -432,6 +449,18 @@ def __init__(self) -> None:
432449
"proxy_type": "operator_proxy_type",
433450
"intrinsic": "real",
434451
"kind": "r_def"},
452+
# 'real'-valued operator with real32 data
453+
"r_32_operator": {"module": "operator_real32_mod",
454+
"type": "operator_real32_type",
455+
"proxy_type": "operator_real32_proxy_type",
456+
"intrinsic": "real",
457+
"kind": "real32"},
458+
# 'real'-valued operator with real32 data
459+
"r_64_operator": {"module": "operator_real64_mod",
460+
"type": "operator_real64_type",
461+
"proxy_type": "operator_real64_proxy_type",
462+
"intrinsic": "real",
463+
"kind": "real64"},
435464
# 'real'-valued operator with data of kind 'r_solver'
436465
"r_solver_operator": {
437466
"module": "r_solver_operator_mod",
@@ -454,6 +483,13 @@ def __init__(self) -> None:
454483
"intrinsic": "real",
455484
"kind": "r_solver"}}
456485

486+
# Construct a reverse map from type to the name of the LFRic
487+
# data type for all real field types.
488+
LFRicConstants.REAL_DATA_TYPE_RMAP = {}
489+
for key, value in LFRicConstants.DATA_TYPE_MAP.items():
490+
if value["intrinsic"] == "real" and "field" in key:
491+
LFRicConstants.REAL_DATA_TYPE_RMAP[value["type"]] = key
492+
457493
# Mapping from a vector type used in the algorithm-layer to
458494
# the actual type used in the PSy-layer.
459495
LFRicConstants.FIELD_VECTOR_TO_FIELD_MAP = {

src/psyclone/domain/lfric/lfric_driver_creator.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ def __init__(self, region_name: Optional[tuple[str, str]] = None) -> None:
7272
# -------------------------------------------------------------------------
7373
def handle_precision_symbols(self, symbol_table: SymbolTable) -> None:
7474
'''This function adds an import of the various precision
75-
symbols used by LFRic from the constants_mod module.
75+
symbols used by LFRic from the constants_mod module. It also adds
76+
imports of the real32 and real64 intrinsic kinds.
7677
7778
:param symbol_table: the symbol table to which the precision symbols
7879
must be added.
@@ -87,15 +88,27 @@ def handle_precision_symbols(self, symbol_table: SymbolTable) -> None:
8788
# does not exist at all in LFRic, but is still in LFRic's psyclone.cfg
8889
# file. TODO #2018 and
8990
# https://code.metoffice.gov.uk/trac/lfric/ticket/4674
91+
names_to_skip = ["r_quad", "r_phys"] + list(const.INTRINSIC_KINDS)
9092
api_config = Config.get().api_conf("lfric")
9193
all_precisions = [name for name in api_config.precision_map
92-
if name not in ["r_quad", "r_phys"]]
94+
if name not in names_to_skip]
9395
for prec_name in all_precisions:
9496
symbol_table.new_symbol(prec_name,
9597
tag=f"{prec_name}@{mod_name}",
9698
symbol_type=DataSymbol,
9799
datatype=ScalarType.integer_type(),
98100
interface=ImportInterface(constant_mod))
101+
# Intrinsic kind symbols are imported into constants_mod but are
102+
# private to it so have to be handled separately.
103+
iso_mod = ContainerSymbol(const.FORTRAN_ISO_MOD_NAME,
104+
is_intrinsic=True)
105+
symbol_table.add(iso_mod)
106+
for prec_name in const.INTRINSIC_KINDS:
107+
symbol_table.new_symbol(prec_name,
108+
tag=f"{prec_name}@{iso_mod.name}",
109+
symbol_type=DataSymbol,
110+
datatype=ScalarType.integer_type(),
111+
interface=ImportInterface(iso_mod))
99112

100113
# -------------------------------------------------------------------------
101114
def verify_and_cleanup_psyir(self, extract_region: Node) -> None:

src/psyclone/domain/lfric/lfric_types.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,9 @@ def add_precision_symbol(table: SymbolTable,
608608
table then add it. Also ensure that the Container symbol from which it
609609
is imported is in the table.
610610
611+
Also supports Fortran intrinsic kinds imported from the
612+
iso_fortran_env module.
613+
611614
:param table: the symbol table to use.
612615
:param name: name of the LFRic precision symbol to add to table.
613616
@@ -624,7 +627,13 @@ def add_precision_symbol(table: SymbolTable,
624627
raise ValueError(f"'{name}' is not a recognised LFRic precision.")
625628

626629
const = LFRicConstants()
627-
mod_name = const.UTILITIES_MOD_MAP["constants"]["module"]
630+
if name in const.INTRINSIC_KINDS:
631+
# This is an intrinsic precision (e.g. real64)
632+
mod_name = const.FORTRAN_ISO_MOD_NAME
633+
is_intrinsic = True
634+
else:
635+
mod_name = const.UTILITIES_MOD_MAP["constants"]["module"]
636+
is_intrinsic = False
628637

629638
sym = table.lookup(name, otherwise=None)
630639

@@ -638,7 +647,8 @@ def add_precision_symbol(table: SymbolTable,
638647
return sym
639648

640649
constants_mod = table.find_or_create(mod_name,
641-
symbol_type=ContainerSymbol)
650+
symbol_type=ContainerSymbol,
651+
is_intrinsic=is_intrinsic)
642652
sym = DataSymbol(name, ScalarType.integer_type(),
643653
interface=ImportInterface(constants_mod))
644654
table.add(sym)

src/psyclone/lfric.py

Lines changed: 48 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5954,22 +5954,22 @@ def _init_scalar_properties(
59545954
self._precision = const.SCALAR_PRECISION_MAP[
59555955
self.intrinsic_type]
59565956

5957-
def _init_field_properties(self, alg_datatype, check=True):
5957+
def _init_field_properties(self,
5958+
alg_datatype: Optional[str],
5959+
check: bool = True) -> None:
59585960
'''Set up the properties of this field using algorithm datatype
59595961
information if it is available.
59605962
5961-
:param alg_datatype: the datatype of this argument as \
5962-
specified in the algorithm layer or None if it is not \
5963-
known.
5964-
:type alg_datatype: str or NoneType
5965-
:param bool check: whether to use the algorithm \
5963+
:param alg_datatype: the datatype of this argument as
5964+
specified in the algorithm layer or None if it is not known.
5965+
:param check: whether to use the algorithm
59665966
information. Optional argument that defaults to True.
59675967
5968-
:raises GenerationError: if the datatype for a gh_field \
5968+
:raises GenerationError: if the datatype for a gh_field
59695969
could not be found in the algorithm layer.
5970-
:raises GenerationError: if the datatype specified in the \
5970+
:raises GenerationError: if the datatype specified in the
59715971
algorithm layer is inconsistent with the kernel metadata.
5972-
:raises InternalError: if the intrinsic type of the field is \
5972+
:raises InternalError: if the intrinsic type of the field is
59735973
not supported (i.e. is not real or integer).
59745974
59755975
'''
@@ -5990,20 +5990,14 @@ def _init_field_properties(self, alg_datatype, check=True):
59905990
if not check:
59915991
# Use the default as we are ignoring any algorithm info
59925992
argtype = "field"
5993-
elif alg_datatype == "field_type":
5994-
argtype = "field"
5995-
elif alg_datatype == "r_bl_field_type":
5996-
argtype = "r_bl_field"
5997-
elif alg_datatype == "r_solver_field_type":
5998-
argtype = "r_solver_field"
5999-
elif alg_datatype == "r_tran_field_type":
6000-
argtype = "r_tran_field"
60015993
else:
6002-
raise GenerationError(
6003-
f"The metadata for argument '{self.name}' in kernel "
6004-
f"'{self._call.name}' specifies that this is a real "
6005-
f"field, however it is declared as a "
6006-
f"'{alg_datatype}' in the algorithm code.")
5994+
argtype = const.REAL_DATA_TYPE_RMAP.get(alg_datatype, None)
5995+
if not argtype:
5996+
raise GenerationError(
5997+
f"The metadata for argument '{self.name}' in kernel "
5998+
f"'{self._call.name}' specifies that this is a real "
5999+
f"field, however it is declared as a "
6000+
f"'{alg_datatype}' in the algorithm code.")
60076001

60086002
elif self.intrinsic_type == "integer":
60096003
if check and alg_datatype != "integer_field_type":
@@ -6023,20 +6017,21 @@ def _init_field_properties(self, alg_datatype, check=True):
60236017
self._proxy_data_type = const.DATA_TYPE_MAP[argtype]["proxy_type"]
60246018
self._module_name = const.DATA_TYPE_MAP[argtype]["module"]
60256019

6026-
def _init_operator_properties(self, alg_datatype, check=True):
6020+
def _init_operator_properties(self,
6021+
alg_datatype: Optional[str],
6022+
check: bool = True) -> None:
60276023
'''Set up the properties of this operator using algorithm datatype
60286024
information if it is available.
60296025
6030-
:param alg_datatype: the datatype of this argument as \
6031-
specified in the algorithm layer or None if it is not \
6032-
known.
6033-
:type alg_datatype: str or NoneType
6034-
:param bool check: whether to use the algorithm \
6035-
information. Optional argument that defaults to True.
6036-
:raises GenerationError: if the datatype for a gh_operator \
6037-
could not be found in the algorithm layer (and check is \
6026+
:param alg_datatype: the datatype of this argument as specified in
6027+
the algorithm layer or None if it is not known.
6028+
:param check: whether to use the algorithm information. Optional
6029+
argument that defaults to True.
6030+
6031+
:raises GenerationError: if the datatype for a gh_operator
6032+
could not be found in the algorithm layer (and check is
60386033
True).
6039-
:raises GenerationError: if the datatype specified in the \
6034+
:raises GenerationError: if the datatype specified in the
60406035
algorithm layer is inconsistent with the kernel metadata.
60416036
:raises InternalError: if this argument is not an operator.
60426037
@@ -6048,9 +6043,8 @@ def _init_operator_properties(self, alg_datatype, check=True):
60486043
# Use the default as we are ignoring any algorithm info
60496044
argtype = "operator"
60506045
elif not alg_datatype:
6051-
# Raise an exception as we require algorithm
6052-
# information to determine the precision of the
6053-
# operator
6046+
# Raise an exception as we require algorithm information
6047+
# to determine the precision of the operator
60546048
raise GenerationError(
60556049
f"It was not possible to determine the operator type "
60566050
f"from the algorithm layer for argument '{self.name}' "
@@ -6061,6 +6055,10 @@ def _init_operator_properties(self, alg_datatype, check=True):
60616055
argtype = "r_solver_operator"
60626056
elif alg_datatype == "r_tran_operator_type":
60636057
argtype = "r_tran_operator"
6058+
elif alg_datatype == "operator_real64_type":
6059+
argtype = "r_64_operator"
6060+
elif alg_datatype == "operator_real32_type":
6061+
argtype = "r_32_operator"
60646062
else:
60656063
raise GenerationError(
60666064
f"The metadata for argument '{self.name}' in kernel "
@@ -6480,17 +6478,25 @@ def _find_or_create_type(mod_name: str,
64806478
raise NotImplementedError(
64816479
f"Unsupported scalar type '{self.intrinsic_type}'")
64826480

6481+
const = LFRicConstants()
64836482
kind_name = self.precision
64846483
try:
64856484
kind_symbol = symtab.lookup(kind_name)
64866485
except KeyError:
6487-
mod_map = LFRicConstants().UTILITIES_MOD_MAP
6488-
const_mod = mod_map["constants"]["module"]
6489-
constants_container = symtab.find_or_create(
6490-
const_mod, symbol_type=ContainerSymbol)
6491-
kind_symbol = DataSymbol(
6492-
kind_name, ScalarType.integer_type(),
6493-
interface=ImportInterface(constants_container))
6486+
if kind_name.lower() in const.INTRINSIC_KINDS:
6487+
# This is an intrinsic kind (e.g. real32) so it comes
6488+
# from the intrinsic ISO module.
6489+
cntr_sym = symtab.find_or_create(
6490+
const.FORTRAN_ISO_MOD_NAME,
6491+
symbol_type=ContainerSymbol,
6492+
is_intrinsic=True)
6493+
else:
6494+
mod_map = const.UTILITIES_MOD_MAP
6495+
const_mod = mod_map["constants"]["module"]
6496+
cntr_sym = symtab.find_or_create(
6497+
const_mod, symbol_type=ContainerSymbol)
6498+
kind_symbol = DataSymbol(kind_name, ScalarType.integer_type(),
6499+
interface=ImportInterface(cntr_sym))
64946500
symtab.add(kind_symbol)
64956501
dts = ScalarType(prim_type, Reference(kind_symbol))
64966502
if self.is_scalar_array and self._array_ndims >= 1:

0 commit comments

Comments
 (0)