Modules¶
This section describes the functionality of the various Python modules that make up PSyclone.
Module: f2pygen¶
f2pygen provides functionality for generating Fortran code from scratch and supports the addition of a use statement to an existing parse tree.
Variable Declarations¶
Three different classes are provided to support the creation of variable declarations (for intrinsic, character and derived-type variables). An example of their use might be:
>>> from psyclone.f2pygen import (ModuleGen, SubroutineGen, DeclGen,
... CharDeclGen, TypeDeclGen)
>>> module = ModuleGen(name="testmodule")
>>> sub = SubroutineGen(module, name="testsubroutine")
>>> module.add(sub)
>>> sub.add(DeclGen(sub, datatype="integer", entity_decls=["my_int"]))
>>> sub.add(CharDeclGen(sub, length="10", entity_decls=["my_char"]))
>>> sub.add(TypeDeclGen(sub, datatype="field_type", entity_decls=["ufld"]))
>>> gen = str(module.root)
>>> print(gen)
MODULE testmodule
IMPLICIT NONE
CONTAINS
SUBROUTINE testsubroutine()
TYPE(field_type) ufld
CHARACTER(LEN=10) my_char
INTEGER my_int
END SUBROUTINE testsubroutine
END MODULE testmodule
The full interface to each of these classes is detailed below:
- class psyclone.f2pygen.DeclGen(parent, datatype='', entity_decls=None, intent='', pointer=False, kind='', dimension='', allocatable=False, save=False, target=False, initial_values=None, private=False)[source]
Generates a Fortran declaration for variables of various intrinsic types (integer, real and logical). For character variables CharDeclGen should be used.
- Parameters
parent (
psyclone.f2pygen.BaseGen
) – node to which to add this declaration as a child.datatype (str) – the (intrinsic) type for this declaration.
entity_decls (list) – list of variable names to declare.
intent (str) – the INTENT attribute of this declaration.
pointer (bool) – whether or not this is a pointer declaration.
kind (str) – the KIND attribute to use for this declaration.
dimension (str) – the DIMENSION specifier (i.e. the xx in DIMENSION(xx)).
allocatable (bool) – whether this declaration is for an ALLOCATABLE quantity.
save (bool) – whether this declaration has the SAVE attribute.
target (bool) – whether this declaration has the TARGET attribute.
initial_values (list of str with same no. of elements as entity_decls) – initial value to give each variable.
private (bool) – whether this declaration has the PRIVATE attribute (default is False).
- Raises
RuntimeError – if datatype is not one of DeclGen.SUPPORTED_TYPES.
- class psyclone.f2pygen.CharDeclGen(parent, entity_decls=None, intent='', pointer=False, kind='', dimension='', allocatable=False, save=False, target=False, length='', initial_values=None, private=False)[source]
Generates a Fortran declaration for character variables.
- Parameters
parent (
psyclone.f2pygen.BaseGen
.) – node to which to add this declaration as a child.entity_decls (list) – list of variable names to declare.
intent (str) – the INTENT attribute of this declaration.
pointer (bool) – whether or not this is a pointer declaration.
kind (str) – the KIND attribute to use for this declaration.
dimension (str) – the DIMENSION specifier (i.e. the xx in DIMENSION(xx)).
allocatable (bool) – whether this declaration is for an ALLOCATABLE quantity.
save (bool) – whether this declaration has the SAVE attribute.
target (bool) – whether this declaration has the TARGET attribute.
length (str) – expression to use for the (len=xx) selector.
initial_values (list of str with same no. of elements as entity_decls) – list of initial values, one for each variable. Each of these can be either a variable name or a literal, quoted string (e.g. “‘hello’”). Default is None.
private (bool) – whether this declaration has the PRIVATE attribute.
- class psyclone.f2pygen.TypeDeclGen(parent, datatype='', entity_decls=None, intent='', pointer=False, dimension='', allocatable=False, save=False, target=False, is_class=False, private=False)[source]
Generates a Fortran declaration for variables of a derived type.
- Parameters
parent (
psyclone.f2pygen.BaseGen
) – node to which to add this declaration as a child.datatype (str) – the type for this declaration.
entity_decls (list) – list of variable names to declare.
intent (str) – the INTENT attribute of this declaration.
pointer (bool) – whether or not this is a pointer declaration.
dimension (str) – the DIMENSION specifier (i.e. the xx in DIMENSION(xx)).
allocatable (bool) – whether this declaration is for an ALLOCATABLE quantity.
save (bool) – whether this declaration has the SAVE attribute.
target (bool) – whether this declaration has the TARGET attribute.
is_class (bool) – whether this is a class rather than type declaration.
private (bool) – whether or not this declaration has the PRIVATE attribute. (Defaults to False.)
Adding code¶
f2pygen supports the addition of use statements to an existing fparser1 parse tree:
- psyclone.f2pygen.adduse(name, parent, only=False, funcnames=None)[source]¶
Adds a use statement with the specified name to the supplied object. This routine is required when modifying an existing AST (e.g. when modifying a kernel). The classes are used when creating an AST from scratch (for the PSy layer).
- Parameters
- Returns
an fparser1 Use object
- Return type
fparser.one.block_statements.Use
The PSyclone code where the adduse function was used has recently been migrated from using fparser1 to using fparser2. In recognition of this change a new version of adduse has been developed which adds use statements to an existing fparser2 parse tree. For the timebeing this new version is located in the same file it is used - alg_gen.py - but will be migrated to f2pygen (or equivalent) in the future:
- psyclone.alg_gen.adduse(location, name, only=None, funcnames=None)[source]¶
Add a Fortran ‘use’ statement to an existing fparser2 parse tree. This will be added at the first valid location before the current location.
This function should be part of the fparser2 replacement for f2pygen (which uses fparser1) but is kept here until this is developed, see issue #240.
- Parameters
location (
fparser.two.utils.Base
) – the current location (node) in the parse tree to which to add a USE.name (str) – the name of the use statement.
only (bool) – whether to include the ‘only’ clause in the use statement or not. Defaults to None which will result in only being added if funcnames has content and not being added otherwise.
funcnames (list of str) – a list of names to include in the use statement’s only list. If the list is empty or None then nothing is added. Defaults to None.
- Raises
GenerationError – if no suitable enclosing program unit is found for the provided location.
NotImplementedError – if the type of parent node is not supported.
InternalError – if the parent node does not have the expected structure.
Module: configuration¶
PSyclone uses the Python ConfigParser
class
(https://docs.python.org/3/library/configparser.html) for reading the
configuration file. This is managed by the psyclone.configuration
module which provides a Config
class. This class is a singleton, which can be (created and) accessed
using Config.get()
. Only one such instance will ever exist:
- class psyclone.configuration.Config[source]¶
Handles all configuration management. It is implemented as a singleton using a class _instance variable and a get() function.
- property api¶
Getter for the API selected by the user.
- Returns
The name of the selected API.
- Return type
- api_conf(api=None)[source]¶
Getter for the object holding API-specific configuration options.
- Parameters
api (str) – Optional, the API for which configuration details are required. If none is specified, returns the config for the default API.
- Returns
object containing API-specific configuration
- Return type
One of
psyclone.configuration.LFRicConfig
,psyclone.configuration.GOceanConfig
or None.- Raises
ConfigurationError – if api is not in the list of supported APIs.
ConfigurationError – if the config file did not contain a section for the requested API.
- property default_api¶
Getter for the default API used by PSyclone.
- Returns
default PSyclone API
- Return type
- property default_stub_api¶
Getter for the default API used by the stub generator.
- Returns
default API for the stub generator
- Return type
- property distributed_memory¶
Getter for whether or not distributed memory is enabled
- Returns
True if DM is enabled, False otherwise
- Return type
- property filename¶
Getter for the full path and name of the configuration file used to initialise this configuration object.
- Returns
full path and name of configuration file
- Return type
- static find_file()[source]¶
Static method that searches various locations for a configuration file. If the full path to an existing file has been provided in the PSYCLONE_CONFIG environment variable then that is returned. Otherwise, we search the following locations, in order:
${PWD}/.psyclone/
- if inside-a-virtual-environment:
<base-dir-of-virtual-env>/share/psyclone/
${HOME}/.local/share/psyclone/
<system-install-prefix>/share/psyclone/
- Returns
the fully-qualified path to the configuration file
- Return type
- Raises
ConfigurationError – if no config file is found
- static get(do_not_load_file=False)[source]¶
Static function that if necessary creates and returns the singleton config instance.
- Parameters
do_not_load_file (bool) – If set it will not load the default config file. This is used when handling the command line so that the user can specify the file to load.
- get_constants()[source]¶
- Returns
the constants instance of the current API.
- Return type
either
psyclone.domain.lfric.LFRicConstants
,psyclone.domain.gocean.GOceanConstants
, orpsyclone.domain.nemo.NemoConstants
- get_default_keys()[source]¶
Returns all keys from the default section. :returns list: List of all keys of the default section as strings.
- static get_repository_config_file()[source]¶
This function returns the absolute path to the config file included in the PSyclone repository. It is used by the testing framework to make sure all tests get the same config file (see tests/config_tests for the only exception). :return str: Absolute path to the config file included in the PSyclone repository.
- property include_paths¶
- Returns
the list of paths to search for Fortran include files.
- Return type
list of str.
- property kernel_naming¶
- Returns
what naming scheme to use when writing transformed kernels to file.
- Return type
- property kernel_output_dir¶
- Returns
the directory to which to write transformed kernels.
- Return type
- load(config_file=None)[source]¶
Loads a configuration file.
- Parameters
config_file (str) – Override default configuration file to read.
- Raises
ConfigurationError – if there are errors or inconsistencies in the specified config file.
- property psyir_root_name¶
Getter for the root name to use when creating PSyIR names.
- Returns
the PSyIR root name.
- Return type
- property reprod_pad_size¶
Getter for the amount of padding to use for the array required for reproducible OpenMP reductions
- Returns
padding size (no. of array elements)
- Return type
- property reproducible_reductions¶
Getter for whether reproducible reductions are enabled.
- Returns
True if reproducible reductions are enabled, False otherwise.
- Return type
- property supported_apis¶
Getter for the list of APIs supported by PSyclone.
- Returns
list of supported APIs
- Return type
list of str
- property supported_stub_apis¶
Getter for the list of APIs supported by the stub generator.
- Returns
list of supported APIs.
- Return type
list of str
- property valid_psy_data_prefixes¶
- Returns
The list of all valid class prefixes.
- Return type
list of str
The Config
class is responsible for finding the configuration file
(if no filename is passed to the constructor), parsing it and then storing
the various configuration options.
If PSyclone is started via pytest
, the environment variable
PSYCLONE_CONFIG
is set to <PSYCLONEHOME/config>
. This will
guarantee that all tests use the config file provided in the PSyclone
repository, and not a (potentially modified) user installed version.
The Config
class also stores the list of supported
APIs (Config._supported_api_list
) and the default API to use if none
is specified in either a config file or the command line
(Config._default_api
). Additionally, it performs some basic
consistency checks on the values it obtains from the configuration file.
Since the PSyclone API to use can be read from the configuration
file, it is not possible to have API-specifc sub-classes of Config
as we don’t know which API is in use before we read the file. However, the
configuration file can contain API-specific settings. These are placed in
separate sections, named for the API to which they apply, e.g.:
[dynamo0.3]
COMPUTE_ANNEXED_DOFS = false
Having parsed and stored the options from the default section of the
configuration file, the Config
constructor then creates a
dictionary using the list of supported APIs to provide the keys. The
configuration file is then checked for API-specific sections (again
using the API names from the default section) and, if any are found,
an API-specifc sub-class is created using the parsed entries from the
corresponding section. The resulting object is stored in the
dictionary under the appropriate key. The API-specific values may then
be accessed as, e.g.:
Config.get().api_conf("dynamo0.3").compute_annexed_dofs
The API-specific sub-classes exist to provide validation/type-checking and
encapsulation for API-specific options. They do not sub-class Config
directly but store a reference back to the Config
object to which they
belong.
Constants Objects¶
Each API provides a specific object that stores required constants. Most of these constants are hard-coded in the object, but some are taken from a section of the configuration file. The constants are provided as class variables, but an instance of it needs to be created (at least once) in order to make sure all class variables are initialised. It is therefore recommended to always use an instance of the corresponding constant class to access these constants. The constant objects make sure that this initialisation only happens the very first time - creating an instance is therefore very cheap.
There three constant objects can be imported as follows:
from psyclone.domain.gocean import GOceanConstants
from psyclone.domain.lfric import LFRicConstants
from psyclone.domain.nemo import NemoConstants
These objects can be used in two different ways:
If the API is known, e.g. because the constant is used in an API-specific file, an instance can simply be created and used, e.g.:
from psyclone.domain.lfric import LFRicConstants const = LFRicConstants() if var is in const.VALID_LOOP_BOUNDS_NAMES: ...This usage pattern can be seen in many API-specific files.
In some cases a value of an API-specific constant is required in a generic function. In this case the API-specific constant object can be accessed using the config file as follows:
from psyclone.configuration import Config const = Config.get().api_conf().get_constants() if some_variable is in const.VALID_INTRINSIC_TYPES: ...This pattern is used in some functions that can be called with different APIs. The following constants are used this way:
VALID_ARG_TYPE_NAMES
VALID_INTRINSIC_TYPES
VALID_SCALAR_NAMES
VALID_LOOP_TYPES
These are the only variables that are defined across all constant objects.
Module: transformations¶
As one might expect, the transformations module holds the various transformation classes that may be used to modify the Schedule of an Invoke and/or the kernels called from within it.
Note
The directory layout of PSyclone is currently being restructured. As a result of this some transformations are already in the new locations, while others have not been moved yet.
The base class for any transformation must be the class Transformation
:
- class psyclone.psyGen.Transformation(writer=None)[source]¶
Abstract baseclass for a transformation. Uses the abc module so it can not be instantiated.
- Parameters
writer (
psyclone.psyir.backend.visitor.PSyIRVisitor
) – optional argument to set the type of writer to provide to a transformation for use when constructing error messages. Defaults to FortranWriter().
- abstract apply(node, options=None)[source]¶
Abstract method that applies the transformation. This function must be implemented by each transform. As a minimum each apply function must take a node to which the transform is applied, and a dictionary of additional options, which will also be passed on to the validate functions. This dictionary is used to provide optional parameters, and also to modify the behaviour of validation of transformations: for example, if the user knows that a transformation can correctly be applied in a specific case, but the more generic code validation would not allow this. Validation functions should check for a key in the options dictionary to disable certain tests. Those keys will be documented in each apply() and validate() function.
Note that some apply() functions might take a slightly different set of parameters.
- Parameters
node (depends on actual transformation) – The node (or list of nodes) for the transformation - specific to the actual transform used.
options (dictionary of string:values or None) – a dictionary with options for transformations.
- validate(node, options=None)[source]¶
Method that validates that the input data is correct. It will raise exceptions if the input data is incorrect. This function needs to be implemented by each transformation.
The validate function can be called by the user independent of the apply() function, but it will automatically be executed as part of an apply() call.
As minimum each validate function must take a node to which the transform is applied and a dictionary of additional options. This dictionary is used to provide optional parameters and also to modify the behaviour of validation: for example, if the user knows that a transformation can correctly be applied in a specific case but the more generic code validation would not allow this. Validation functions should check for particular keys in the options dict in order to disable certain tests. Those keys will be documented in each apply() and validate() function as ‘options[“option-name”]’.
Note that some validate functions might take a slightly different set of parameters.
- Parameters
node (depends on actual transformation) – The node (or list of nodes) for the transformation - specific to the actual transform used.
options (dictionary of string:values or None) – a dictionary with options for transformations.
Those transformations that work on a region of code (e.g. enclosing
multiple kernel calls within an OpenMP region) must sub-class the
RegionTrans
class:
- class psyclone.psyir.transformations.RegionTrans(writer=None)[source]
This abstract class is a base class for all transformations that act on a list of nodes. It gives access to a validate function that makes sure that the nodes in the list are in the same order as in the original AST, no node is duplicated, and that all nodes have the same parent. We also check that all nodes to be enclosed are valid for this transformation - this requires that the sub-class populate the excluded_node_types tuple.
- get_node_list(nodes)[source]
This is a helper function for region based transformations. The parameter for any of those transformations is either a single node, a schedule, or a list of nodes. This function converts this into a list of nodes according to the parameter type. This function will always return a copy, to avoid issues e.g. if a child list of a node should be provided, and a transformation changes the order in this list (which would then also change the order of the nodes in the tree).
- Parameters
nodes (
psyclone.psyir.nodes.Node
orpsyclone.psyir.nodes.Schedule or a list of :py:obj:`psyclone.psyir.nodes.Node
) – can be a single node, a schedule or a list of nodes.- Returns
a list of nodes.
- Return type
list of
psyclone.psyir.nodes.Node
- Raises
TransformationError – if the supplied parameter is neither a single Node, nor a Schedule, nor a list of Nodes.
- validate(nodes, options=None)[source]
Checks that the nodes in node_list are valid for a region transformation.
- Parameters
nodes (subclass of
psyclone.psyir.nodes.Node
or a list of subclasses ofpsyclone.psyir.nodes.Node
) – list of PSyIR nodes or a single node.options (dictionary of string:values or None) – a dictionary with options for transformations.
options["node-type-check"] (bool) – this flag controls if the type of the nodes enclosed in the region should be tested to avoid using unsupported nodes inside a region.
- Raises
TransformationError – if the nodes in the list are not in the original order in which they are in the AST, a node is duplicated or the nodes have different parents.
TransformationError – if any of the nodes to be enclosed in the region are of an unsupported type.
TransformationError – if the parent of the supplied Nodes is not a Schedule or a Directive.
TransformationError – if the nodes are in a NEMO Schedule and the transformation acts on the child of a single-line If or Where statment.
TransformationError – if the supplied options are not a dictionary.
Finally, those transformations that act on a Kernel must sub-class the
KernelTrans
class:
In all cases, the apply method of any sub-class must ensure that the validate method of the parent class is called.
Module: psyGen¶
Provides the base classes for PSy-layer code generation.
Module: dynamo0p3¶
Specialises various classes from the psyclone.psyGen
module
in order to support the Dynamo 0.3 API.
When constructing the Fortran subroutine for either an Invoke or
Kernel stub (see Kernel-stub Generator), there are various groups of
related quantities for which variables must be declared and
(for Invokes) initialised. Each of these groupings is managed by a distinct
sub-class of the DynCollection
abstract class:
(A single base class is used for both Invokes and Kernel stubs since it
allows the code dealing with variable declarations to be shared.)
A concrete sub-class of DynCollection
must provide an
implementation of the _invoke_declarations
method. If the
quantities associated with the collection require initialisation
within the PSy layer then the initialise
method must also be
implemented. If stub-generation is to be supported for kernels that
make use of the collection type then an implementation must also be
provided for _stub_declarations.
Although instances of (sub-classes of) DynCollection
handle all
declarations and initialisation, there remains the problem of
constructing the list of arguments for a kernel (or kernel stub). The
psyclone.dynamo0p3.ArgOrdering
base class provides support for
this:
This class is then sub-classed in order to support the generation of
argument lists when calling kernels (KernCallArgList
) and when
creating kernel stubs (KernStubArgList
). KernCallArgList
is
only used in DynKernelArguments.raw_arg_list()
.
KernStubArgList
is only used in DynKern.gen_stub()
. These
classes make use of DynCollection
sub-classes in order
to ensure that argument naming is consistent.