Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
37) PR #3295 for #3292. Add command-line flag to permit config-file
settings to be overridden and extend possible settings for the
runtime warnings option.

36) PR #3308. Update Spack environment to use updated compiler versions.

35) PR #3202. Update nvidia compiler module version.
Expand Down
8 changes: 6 additions & 2 deletions config/psyclone.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,12 @@ precision_map = i_def: 4,
r_bl: 8,
r_um: 8

# Specify whether we generate code to perform runtime correctness checks
RUN_TIME_CHECKS = false
# Specify whether we generate code to perform runtime correctness checks.
# Allowed values:
# none: no run time checks are added
# warn: run time checks are added, violations will create a warning
# error: run time checks are added, violations will create an error and abort
RUN_TIME_CHECKS = none

# Number of ANY_SPACE and ANY_DISCONTINUOUS_SPACE function spaces
NUM_ANY_SPACE = 10
Expand Down
25 changes: 23 additions & 2 deletions doc/user_guide/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ and an optional API specific section, for example for the
r_tran: 8,
r_bl: 8,
r_um: 8
RUN_TIME_CHECKS = false
RUN_TIME_CHECKS = none
NUM_ANY_SPACE = 10
NUM_ANY_DISCONTINUOUS_SPACE = 10

Expand Down Expand Up @@ -207,7 +207,8 @@ precision_map Captures the value of the actual precisions in
bytes, see :ref:`lfric-precision-map`

RUN_TIME_CHECKS Specifies whether to generate run-time validation
checks, see :ref:`lfric-run-time-checks`.
checks, see :ref:`lfric-run-time-checks`. Must be
one of `none`, `warn` or `error`.

NUM_ANY_SPACE Sets the number of ``ANY_SPACE`` function spaces
in LFRic, see :ref:`lfric-num-any-spaces`.
Expand Down Expand Up @@ -237,3 +238,23 @@ grid-properties This key contains definitions to access various grid
in the :ref:`gocean-configuration-grid-properties`
section of the GOcean1.0 chapter.
======================= =======================================================


Overwriting Config Settings on the Command Line
-----------------------------------------------

PSyclone provides the command line option ``--config-opts`` to overwrite
settings in the configuration file. This can be convenient to test different
scenarios without having to maintain a config files for each of them.

The option takes a space-separated list of ``key=value`` pairs, for
example:
::

psyclone --config-opts="run_time_checks=warn reprod_pad_size=27" ...

This will overwrite the settings for ``run_time_checks`` and ``reprod_pad_size``
in the configuration file. You can overwrite any setting in any section (without
having to specify the section). Capitalisation of the keys is ignored.
If an invalid key is specified, an exception will be raised and
execution will be aborted.
11 changes: 10 additions & 1 deletion doc/user_guide/lfric.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3948,7 +3948,16 @@ Run-time Checks
PSyclone performs static consistency checks where possible. When this
is not possible PSyclone can generate run-time checks. As there may be
performance costs associated with run-time checks they may be switched
on or off by the `RUN_TIME_CHECKS` option in the configuration file.
on or off by the `RUN_TIME_CHECKS` option in the configuration file
(or by using the ``--config-opts`` command line option to overwrite
the setting in the configuration file). The value of `RUN_TIME_CHECKS`
must be one of:

- `none` No runtime checks will be added (default)
- `warn` Runtime checks will be added, and violations will cause a warning
message to be logged.
- `error` Runtime checks will be added, and violations will cause an error
message to be logged. The application will then abort.

Currently run-time checks can be generated to:

Expand Down
8 changes: 6 additions & 2 deletions doc/user_guide/psyclone_command.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ by the command:


> psyclone -h
usage: psyclone [-h] [-v] [-c CONFIG] [-s SCRIPT] [--enable-cache] [-l {off,all,output}]
[-p {invokes,routines,kernels}]
usage: psyclone [-h] [-v] [-c CONFIG] [-s SCRIPT] [--enable-cache] [--config-opts CONFIG_OPTS]
[-l {off,all,output}] [-p {invokes,routines,kernels}]
[-o OUTPUT_FILE] [-api DSL] [-oalg OUTPUT_ALGORITHM_FILE] [-opsy OUTPUT_PSY_FILE]
[-okern OUTPUT_KERNEL_PATH] [-dm] [-nodm]
[--kernel-renaming {multiple,single}]
Expand All @@ -80,6 +80,10 @@ by the command:
--enable-cache whether to enable caching of imported module dependencies (if
enabled, it will generate a .psycache file of each imported module in
the same location as the imported source file).
--config-opts CONFIG_OPTS
Settings that will overwrite values in the config file as a
space-separated list of key=value pairs. Example:
--config-opts "reproducible_reductions=true run_time_checks=warn".
-l {off,all,output}, --limit {off,all,output}
limit the Fortran line length to 132 characters (default 'off'). Use
'all' to apply limit to both input and output Fortran. Use 'output'
Expand Down
70 changes: 53 additions & 17 deletions src/psyclone/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
import os
import re
import sys
from typing import Union
from typing import Optional, Union

import psyclone
from psyclone.errors import PSycloneError, InternalError
Expand Down Expand Up @@ -243,12 +243,23 @@ def __init__(self):
self._backend_intrinsic_named_kwargs = False

# -------------------------------------------------------------------------
def load(self, config_file=None):
'''Loads a configuration file.
def load(self,
config_file: Optional[str] = None,
overwrite: Optional[str] = None) -> None:
'''Loads a configuration file. The optional 'overwrite' parameter
is a space-separated string of key=value pairs which will overwrite
values in the config files (e.g.
"reproducible_reductions=true run_time_checks=true")

:param str config_file: Override default configuration file to read.
:raises ConfigurationError: if there are errors or inconsistencies in \
the specified config file.
:param config_file: Override default configuration file to read.
:param overwrite: Optional string of key-value pairs that will
overwrite settings in the config file.

:raises ConfigurationError: if there are errors or inconsistencies in
the specified config file.

:raises ConfigurationError: if a user-provided overwrite string
contains an invalid key.
'''
# pylint: disable=too-many-branches, too-many-statements
if config_file:
Expand Down Expand Up @@ -286,6 +297,23 @@ def load(self, config_file=None):
raise ConfigurationError(
"Configuration file has no [DEFAULT] section", config=self)

if overwrite:
# If overwrite information is specified, parse this information
# and use it to overwrite the settings just read:
pairs = overwrite.split()
for pair in pairs:
key, value = pair.split("=")
for section in self._config:
if key in self._config[section]:
self._config[section][key] = value
break
else:
logger = logging.getLogger(__name__)
msg = (f"Attempt to overwrite unknown configuration "
f"option: '{pair}'.")
logger.error(msg)
raise ConfigurationError(msg)

# The call to the 'read' method above populates a dictionary.
# All of the entries in that dict are unicode strings so here
# we pull out the values we want and deal with any type
Expand Down Expand Up @@ -916,8 +944,8 @@ def __init__(self, config, section):
self._config = config
# Initialise redundant computation setting
self._compute_annexed_dofs = None
# Initialise run_time_checks setting
self._run_time_checks = None
# Initialise run_time_checks setting - one of "none", "warn", "error"
self._run_time_checks = "none"
# Initialise LFRic datatypes' default kinds (precisions) settings
self._supported_fortran_datatypes = []
self._default_kind = {}
Expand Down Expand Up @@ -954,15 +982,23 @@ def __init__(self, config, section):
config=self._config) from err

# Parse setting for run_time_checks flag
try:
self._run_time_checks = section.getboolean(
"run_time_checks")
except ValueError as err:
raise ConfigurationError(
f"Error while parsing RUN_TIME_CHECKS in the "
f"'[{section.name}]' section of the configuration file "
f"'{config.filename}': {str(err)}.",
config=self._config) from err
self._run_time_checks = section["run_time_checks"].lower()
if self._run_time_checks not in ["none", "warn", "error"]:
# Test for old-style boolean value:
try:
self._run_time_checks = section.getboolean("run_time_checks")
except ValueError as err:
raise ConfigurationError(
f"Error while parsing RUN_TIME_CHECKS in the "
f"'[{section.name}]' section of the configuration file "
f"'{config.filename}': Found '{self._run_time_checks}', "
f" must be one of 'none', 'warn', 'error'.",
config=self._config) from err
if self._run_time_checks:
# True - old behaviour is to create an error (and abort)
self._run_time_checks = "error"
else:
self._run_time_checks = "none"

# Parse setting for the supported Fortran datatypes. No
# need to check whether the keyword is found as it is
Expand Down
57 changes: 37 additions & 20 deletions src/psyclone/domain/lfric/lfric_run_time_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,22 +66,30 @@ def invoke_declarations(self):

'''
super().invoke_declarations()
if Config.get().api_conf("lfric").run_time_checks:
# Only add if run-time checks are requested
const = LFRicConstants()
csym = self.symtab.find_or_create(
const.UTILITIES_MOD_MAP["logging"]["module"],
symbol_type=ContainerSymbol
)
self.symtab.find_or_create(
"log_event", symbol_type=RoutineSymbol,
interface=ImportInterface(csym)
)
self.symtab.find_or_create(
"LOG_LEVEL_ERROR", symbol_type=DataSymbol,
datatype=UnresolvedType(),
interface=ImportInterface(csym)
)
api_conf = Config.get().api_conf("lfric")

# Only add if run-time checks are requested
if api_conf.run_time_checks == "none":
return

const = LFRicConstants()
csym = self.symtab.find_or_create(
const.UTILITIES_MOD_MAP["logging"]["module"],
symbol_type=ContainerSymbol
)
self.symtab.find_or_create(
"log_event", symbol_type=RoutineSymbol,
interface=ImportInterface(csym)
)
if api_conf.run_time_checks == "error":
log_level = "LOG_LEVEL_ERROR"
else:
log_level = "LOG_LEVEL_WARNING"
self.symtab.find_or_create(
log_level, symbol_type=DataSymbol,
datatype=UnresolvedType(),
interface=ImportInterface(csym)
)

def _check_field_fs(self, cursor: int) -> int:
'''
Expand Down Expand Up @@ -140,7 +148,7 @@ def _check_field_fs(self, cursor: int) -> int:

if_condition = None
for name in function_space_names:
if arg._vector_size > 1:
if arg.vector_size > 1:
call = Call.create(ArrayOfStructuresReference.create(
field_symbol, [Literal('1', INTEGER_TYPE)],
["which_function_space"]))
Expand All @@ -163,6 +171,11 @@ def _check_field_fs(self, cursor: int) -> int:
BinaryOperation.Operator.AND, if_condition, cmp
)

if Config.get().api_conf("lfric").run_time_checks == "error":
log_level = "LOG_LEVEL_ERROR"
else:
log_level = "LOG_LEVEL_WARNING"

if_body = Call.create(
symtab.lookup("log_event"),
[Literal(f"In alg '{self._invoke.invokes.psy.orig_name}' "
Expand All @@ -172,7 +185,7 @@ def _check_field_fs(self, cursor: int) -> int:
f"not compatible with the function space "
f"specified in the kernel metadata '{fs_name}'.",
CHARACTER_TYPE),
Reference(symtab.lookup("LOG_LEVEL_ERROR"))])
Reference(symtab.lookup(log_level))])

ifblock = IfBlock.create(if_condition, [if_body])
self._invoke.schedule.addchild(ifblock, cursor)
Expand Down Expand Up @@ -225,13 +238,17 @@ def _check_field_ro(self, cursor: int) -> int:
first = True
for field, call in modified_fields:
if_condition = field.generate_method_call("is_readonly")
if Config.get().api_conf("lfric").run_time_checks == "error":
log_level = "LOG_LEVEL_ERROR"
else:
log_level = "LOG_LEVEL_WARNING"
if_body = Call.create(
symtab.lookup("log_event"),
[Literal(f"In alg '{self._invoke.invokes.psy.orig_name}' "
f"invoke '{self._invoke.name}', field '{field.name}' "
f"is on a read-only function space but is modified "
f"by kernel '{call.name}'.", CHARACTER_TYPE),
Reference(symtab.lookup("LOG_LEVEL_ERROR"))])
Reference(symtab.lookup(log_level))])

ifblock = IfBlock.create(if_condition, [if_body])
self._invoke.schedule.addchild(ifblock, cursor)
Expand All @@ -254,7 +271,7 @@ def initialise(self, cursor: int) -> int:
:returns: Updated cursor value.

'''
if not Config.get().api_conf("lfric").run_time_checks:
if Config.get().api_conf("lfric").run_time_checks == "none":
# Run-time checks are not requested.
return cursor

Expand Down
9 changes: 8 additions & 1 deletion src/psyclone/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,13 @@ def main(arguments):
'enabled, it will generate a .psycache file of each imported '
'module in the same location as the imported source file).'
)
parser.add_argument(
'--config-opts',
help='Settings that will overwrite values in the config file as a '
'space-separated list of key=value pairs. Example:'
'--config-opts "reproducible_reductions=true '
'run_time_checks=warn".'
)
parser.add_argument(
'-l', '--limit', dest='limit', default='off',
choices=['off', 'all', 'output'],
Expand Down Expand Up @@ -645,7 +652,7 @@ def main(arguments):

# If no config file name is specified, args.config is none
# and config will load the default config file.
Config.get().load(args.config)
Config.get().load(args.config, args.config_opts)

# Check whether a PSyKAl API has been specified.
if args.psykal_dsl is None:
Expand Down
Loading
Loading