Skip to content

Commit

Permalink
#1010 Bring to master
Browse files Browse the repository at this point in the history
  • Loading branch information
sergisiso committed Jan 29, 2025
2 parents 0614d93 + 06e0257 commit d7fcd56
Show file tree
Hide file tree
Showing 75 changed files with 1,179 additions and 558 deletions.
12 changes: 12 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@
13) PR #2862 for #2858. Change to PSyIR frontend and backend to ensure
existing parentheses in expressions are preserved.

14) PR #2825 for #2278 and #2849. Signatures AccessType now indentifies
INQUIRY and TYPE_INFO, for inquiry intrinsic accesses and precision symbols.

15) PR #2855 for #2853. Allows the Fortran standard used by fparser to
be set in the config file and ensure it is used consistently.

16) PR #2857 for 2854. Excludes character assignments from ACC KERNELS
regions to avoid issues in NEMO.

17) PR #2829 for #2826. Adds a --config command-line argument to the
PSyAD script (to replicate that for psyclone).

release 3.0.0 6th of December 2024

1) PR #2477 for #2463. Add support for Fortran Namelist statements.
Expand Down
4 changes: 4 additions & 0 deletions config/psyclone.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ OCL_DEVICES_PER_NODE = 1
# and will not produce a warning message if they cannot be found.
IGNORE_MODULES = netcdf, mpi

# The Fortran standard to use when parsing files. This value is passed
# to fparser. Valid values are f2003 and f2008
FORTRAN_STANDARD = f2008

# Settings specific to the LFRic API
# ==================================
[lfric]
Expand Down
46 changes: 20 additions & 26 deletions doc/developer_guide/dependency.rst
Original file line number Diff line number Diff line change
Expand Up @@ -263,16 +263,17 @@ DataAccess class i.e. the `_field_write_arguments()` and
Variable Accesses
=================

Especially in the NEMO API, it is not possible to rely on pre-defined
kernel information to determine dependencies between loops. So an additional,
somewhat lower-level API has been implemented that can be used to determine
variable accesses (READ, WRITE etc.), which is based on the PSyIR information.
The only exception to this is if a kernel is called, in which case the
metadata for the kernel declaration will be used to determine the variable
accesses for the call statement. The information about all variable usage
of a PSyIR node or a list of nodes can be gathered by creating an object of
type `psyclone.core.VariablesAccessInfo`.
This class uses a `Signature` object to keep track of the variables used.
When using PSyclone with generic Fortran code, it is not possible to
rely on pre-defined kernel information to determine dependencies
between loops. So an additional, somewhat lower-level API has been
implemented that can be used to determine variable accesses (READ,
WRITE etc.), which is based on the PSyIR information. The only
exception to this is if a kernel is called, in which case the metadata
for the kernel declaration will be used to determine the variable
accesses for the call statement. The information about all variable
usage of a PSyIR node or a list of nodes can be gathered by creating
an object of type `psyclone.core.VariablesAccessInfo`. This class
uses a `Signature` object to keep track of the variables used.

Signature
---------
Expand All @@ -289,6 +290,14 @@ a single component.
:members:
:special-members: __hash__, __eq__, __lt__

AccessType
----------

An individual access to a ``Signature`` is described by an instance of the
``AccessType`` enumeration:

.. autoclass:: psyclone.core.access_type.AccessType
:members:

VariablesAccessInfo
-------------------
Expand Down Expand Up @@ -319,22 +328,7 @@ instance is holding information about.
VariablesAccessInfo Options
+++++++++++++++++++++++++++

By default, `VariablesAccessInfo` will not report the first argument of
the PSyIR operators `lbound`, `ubound`, or `size` as read accesses,
since these functions do not actually access the content of the array,
they only query the size. If these accesses are required (e.g. in kernel
extraction this could be important if an array is only used in these
intrinsic - a driver would still need these arrays in order to query
the size), the optional `options` parameter of the `VariablesAccessInfo`
constructor can be used: add the key
`COLLECT-ARRAY-SHAPE-READS` and set it to true::

vai = VariablesAccessInfo(options={'COLLECT-ARRAY-SHAPE-READS': True})

In this case all arrays specified as first parameter to one of the
PSyIR operators above will be reported as read access.

Fortran also allows to rename a symbol locally when it is being imported,
Fortran allows an imported symbol to be renamed locally
(`use some_mod, only: renamed => original_name`). Depending on use case,
it might be useful to get the non-local, original name. By default,
`VariablesAccessInfo` will report the local name (i.e. the renamed name),
Expand Down
2 changes: 1 addition & 1 deletion doc/psyad/user_guide/implementation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ tangent-linear code it is first transformed into equivalent inline
code before the code is transformed to its adjoint form. The PSyIR
``DotProduct2CodeTrans`` or ``Matmul2CodeTrans`` transformations are
used to perform these manipulations. See the
:ref:`user_guide:available_trans` section of the user guide for more
:ref:`user_guide:sec_transformations_available` section of the user guide for more
information on these transformations.

.. note:: At the moment all ``dot_product`` and ``matmul`` intrinsics
Expand Down
12 changes: 11 additions & 1 deletion doc/psyad/user_guide/psyad_command.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ by the command:
.. parsed-literal::
>>> psyad -h
usage: psyad [-h] [-oad OAD] [-v] [-t] [-api API] [-coord-arg COORD_ARG] [-panel-id-arg PANEL_ID_ARG] [-otest TEST_FILENAME] -a ACTIVE [ACTIVE ...] -- filename
usage: psyad [-h] [-oad OAD] [-c CONFIG] [-v] [-t] [-api API] [-coord-arg COORD_ARG] [-panel-id-arg PANEL_ID_ARG] [-otest TEST_FILENAME] -a ACTIVE [ACTIVE ...] -- filename
Run the PSyclone adjoint code generator on a tangent-linear kernel file
Expand All @@ -73,6 +73,8 @@ by the command:
-h, --help show this help message and exit
-a ACTIVE [ACTIVE ...], --active ACTIVE [ACTIVE ...]
name of active variables
-c CONFIG, --config CONFIG
config file with PSyclone specific options
-v, --verbose increase the verbosity of the output
-t, --gen-test generate a standalone unit test for the adjoint code
-api API the PSyclone API that the TL kernel conforms to (if any)
Expand Down Expand Up @@ -207,3 +209,11 @@ internally you can specify the ``-v`` option. For example
::

> psyad -a var1 var2 -oad ad_kern.f90 -v tl_kern.f90

Configuration Options
---------------------

By default PSyAD uses the same configuration file used by PSyclone. To
use a custom configuration file use the ``--config`` command-line option.
Further documentation of the configuration options can be found in
:ref:`user_guide:configuration`.
4 changes: 4 additions & 0 deletions doc/user_guide/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ section e.g.:
REPROD_PAD_SIZE = 8
PSYIR_ROOT_NAME = psyir_tmp
VALID_PSY_DATA_PREFIXES = profile, extract
FORTRAN_STANDARD = f2008

and an optional API specific section, for example for the
``lfric`` section:
Expand Down Expand Up @@ -176,6 +177,9 @@ BACKEND_CHECKS_ENABLED Optional (defaults to True). Whether or not the PSyIR
backend should validate the tree that it is passed.
Can be overridden by the ``--backend`` command-line
flag (see :ref:`backend-options`).
FORTRAN_STANDARD Optional (defaults to f2008). The Fortran standard str
that should be used by fparser. Valid values are
f2003 and f2008.
======================= ======================================================= ===========

Common Sections
Expand Down
5 changes: 4 additions & 1 deletion examples/nemo/eg5/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,14 @@ kernels: extract_kernels.py
$(PSYCLONE) -l all -s ./extract_kernels.py -o psy.f90 ../code/tra_adv.F90

$(EXTRACT_DIR)/$(LIB_NAME):
make -C $(EXTRACT_DIR)
${MAKE} -C $(EXTRACT_DIR)

# Compilation uses the 'kernels' transformed code
psy.f90: kernels
psy.o: $(EXTRACT_DIR)/$(LIB_NAME)

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

allclean: clean
${MAKE} -C ${EXTRACT_DIR} allclean
11 changes: 1 addition & 10 deletions examples/nemo/eg5/extract_kernels.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,7 @@ def trans(psyir):
if not isinstance(kern, Loop):
continue
try:
# TODO #2080: once this is fixed, the option can be removed
# The example contains array expressions, e.g.:
# zwx(:,:,jpk) = 0.e0
# PSyclone represents this internally using Range with LBOUND
# and UBOUND intrinsics and currently this results in several
# occurrences of zws on the left hand side, which will trigger
# an exception in the dependency analysis. Therefore, disable
# the collection of read accesses for the shape of an array.
extract.apply(kern,
options={"COLLECT-ARRAY-SHAPE-READS": False})
extract.apply(kern)
except TransformationError as err:
# Typically that's caused by a kernel having a CodeBlock
# inside.
Expand Down
9 changes: 6 additions & 3 deletions examples/nemo/eg6/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ run: compile
./dummy

dummy: psy.o $(READ_ONLY_CHECK_DIR)/$(LIB_NAME)
$(F90) psy.o -o dummy $(READ_ONLY_CHECK_DIR)/$(LIB_NAME) $(LDFLAGS)
$(F90) $(F90FLAGS) psy.o -o dummy $(READ_ONLY_CHECK_DIR)/$(LIB_NAME) $(LDFLAGS)

transform: kernels

Expand All @@ -73,11 +73,14 @@ kernels: read_only_check.py
$(PSYCLONE) -l all -s ./read_only_check.py -o psy.f90 dummy.f90

$(READ_ONLY_CHECK_DIR)/$(LIB_NAME):
make -C $(READ_ONLY_CHECK_DIR)
$(MAKE) -C $(READ_ONLY_CHECK_DIR)

# Compilation uses the 'kernels' transformed code
psy.f90: dummy.f90 read_only_check.py
psy.f90: dummy.f90 read_only_check.py kernels
psy.o: $(READ_ONLY_CHECK_DIR)/$(LIB_NAME)

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

allclean: clean
${MAKE} -C $(READ_ONLY_CHECK_DIR) allclean
2 changes: 1 addition & 1 deletion lib/read_only/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ read_only_base.o: psy_data_base.o
clean:
rm -f read_only_base.f90 psy_data_base.f90 *.o *.mod

allclean:
allclean: clean
$(MAKE) -C dl_esm_inf allclean
$(MAKE) -C lfric allclean
$(MAKE) -C generic allclean
Binary file modified psyclone.pdf
Binary file not shown.
19 changes: 19 additions & 0 deletions src/psyclone/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ def __init__(self):
# checks which can be useful in the case of unimplemented features.
self._backend_checks_enabled = True

# The Fortran standard that fparser should use
self._fortran_standard = None

# -------------------------------------------------------------------------
def load(self, config_file=None):
'''Loads a configuration file.
Expand Down Expand Up @@ -369,6 +372,16 @@ def load(self, config_file=None):
for module_name in ignore_modules:
mod_manager.add_ignore_module(module_name)

# Get the Fortran standard to use:
self._fortran_standard = \
self._config['DEFAULT'].get("FORTRAN_STANDARD", "f2008").lower()
valid_standard = ["f2003", "f2008"]
if self._fortran_standard not in valid_standard:
raise ConfigurationError(f"Invalid Fortran standard "
f"'{self._fortran_standard}' specified "
f"in config file. Must be one of"
f"{valid_standard}")

# Set the flag that the config file has been loaded now.
Config._HAS_CONFIG_BEEN_INITIALISED = True

Expand Down Expand Up @@ -698,6 +711,12 @@ def ocl_devices_per_node(self):
:rtype: int'''
return self._ocl_devices_per_node

@property
def fortran_standard(self) -> str:
''':returns: The Fortran standard to be used by fparser.
'''
return self._fortran_standard

def get_default_keys(self):
'''Returns all keys from the default section.
:returns list: List of all keys of the default section as strings.
Expand Down
61 changes: 45 additions & 16 deletions src/psyclone/core/access_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,46 +44,66 @@
class AccessType(Enum):
'''A simple enum-class for the various valid access types.
'''

#: Data associated with the symbol is read.
READ = 1
#: Data associated with the symbols is written.
WRITE = 2
#: Data associated with the symbol is both read and written (e.g. is passed
#: to a routine with intent(inout)).
READWRITE = 3
#: Incremented from more than one cell column (see the LFRic API section
#: of the User Guide).
INC = 4
#: Read before incrementing. Requires that the outermost halo be clean (see
#: the LFRic API section of the User Guide).
READINC = 5
#: Is the output of a SUM reduction.
SUM = 6
# This is used internally to indicate unknown access type of
# a variable, e.g. when a variable is passed to a subroutine
# and the access type of this variable in the subroutine
# is unknown
#: This is used internally to indicate unknown access type of
#: a variable, e.g. when a variable is passed to a subroutine
#: and the access type of this variable in the subroutine
#: is unknown.
#: TODO #2863 - VariablesAccessInfo does not currently consider
#: UNKNOWN accesses and it should!
UNKNOWN = 7
#: A symbol representing a routine is called.
CALL = 8
#: The property/ies of a symbol is/are queried but the data it
#: represents is not accessed (e.g. 'var' in SIZE(var, dim=1)).
INQUIRY = 9
#: The symbol is used to access its type information (available at compile
#: time) - e.g. precision values such as 'wp' in 1.0_wp.
TYPE_INFO = 10

def __str__(self):
def __str__(self) -> str:
'''Convert to a string representation, returning just the
enum (e.g. 'WRITE')..
:return: API name for this string.
:rtype: str
enum (e.g. 'WRITE').
'''
# pylint complains without str() that the return type is not a str
return str(self.name)

def api_specific_name(self):
def api_specific_name(self) -> str:
'''This convenience function returns the name of the type in the
current API. E.g. in a lfric API, WRITE --> "gh_write"
current API. E.g. in the lfric API, WRITE --> "gh_write". If no
mapping is available then the generic name is returned.
:returns: The API specific name.
:rtype: str
'''
api_config = Config.get().api_conf()
rev_access_mapping = api_config.get_reverse_access_mapping()
return rev_access_mapping[self]
return rev_access_mapping.get(self, str(self).lower())

@staticmethod
def from_string(access_string):
def from_string(access_string: str):
'''Convert a string (e.g. "read") into the corresponding
AccessType enum value (AccessType.READ).
:param str access_string: Access type as string.
:param access_string: Access type as a string.
:returns: Corresponding AccessType enum.
:Raises: ValueError if access_string is not a valid access type.
:rtype: :py:class:`psyclone.core.access_type.AccessType`
:raises ValueError: if access_string is not a valid access type.
'''
for access in AccessType:
if access.name == access_string.upper():
Expand Down Expand Up @@ -127,6 +147,15 @@ def get_valid_reduction_names():
return [access.api_specific_name() for access in
AccessType.get_valid_reduction_modes()]

@staticmethod
def non_data_accesses():
'''
:returns: all access types that do not touch any data associated with
a symbol.
:rtype: list[:py:class:`psyclone.core.AccessType`]
'''
return [AccessType.CALL, AccessType.TYPE_INFO, AccessType.INQUIRY]


# ---------- Documentation utils -------------------------------------------- #
# The list of module members that we wish AutoAPI to generate
Expand Down
Loading

0 comments on commit d7fcd56

Please sign in to comment.