From 557ce3160133e83fa9bca718f374f920541d5a86 Mon Sep 17 00:00:00 2001 From: Sergi Siso Date: Thu, 14 May 2026 14:40:34 +0100 Subject: [PATCH 1/9] #3432 Initial split_kwargs implementation --- src/psyclone/psyGen.py | 45 +++++++- src/psyclone/tests/psyGen_test.py | 101 +++++++++++++++++- .../transformations/transformations_test.py | 2 +- 3 files changed, 145 insertions(+), 3 deletions(-) diff --git a/src/psyclone/psyGen.py b/src/psyclone/psyGen.py index d568273c65..ae7f004238 100644 --- a/src/psyclone/psyGen.py +++ b/src/psyclone/psyGen.py @@ -2344,6 +2344,11 @@ class Transformation(metaclass=abc.ABCMeta): "User guide for more details." ) + #: List of transformations called inside this one that need to be + #: considered by the split_kwargs infrastructure when propagating + #: transformation options + _SUB_TRANSFORMATIONS = [] + @property def name(self): ''' @@ -2353,6 +2358,36 @@ def name(self): ''' return type(self).__name__ + def split_kwargs( + self, + kwargs: dict[str, Any], + ) -> tuple[dict[str, Any]]: + ''' + :param kwargs: the list of kwargs to split. + + :returns: a tuple of the kwargs dictionaries that are valid for this + transformation and every other transformation listed in the + _SUB_TRANSFORMATIONS list. The first kwargs (the ones for itself) + will also include any key that is not valid in any of the other + transformation (e.g. unsupported options). + ''' + # The first kwargs starts with all the items + first_dict = dict(kwargs) + # The following kwargs start empty + other_dicts = [{} for _ in self._SUB_TRANSFORMATIONS] + + # Now copy each valid item into the transformation-specific kwargs + # and delete them from the first one if they are valid somewhere + # else but not in the self options + for key in kwargs: + for idx, trans in enumerate(self._SUB_TRANSFORMATIONS): + if key in trans.get_valid_options(): + other_dicts[idx][key] = kwargs[key] + if key not in type(self).get_valid_options(): + del first_dict[key] + + return tuple([first_dict] + other_dicts) + @abc.abstractmethod def apply(self, node, options=None, **kwargs): '''Abstract method that applies the transformation. This function @@ -2509,10 +2544,18 @@ def validate_options(self, **kwargs): for invalid in invalid_options: invalid_options_detail.append(f"'{invalid}'") invalid_options_list = ", ".join(invalid_options_detail) + extra_options = "" + if self._SUB_TRANSFORMATIONS: + sub_trans_names = [ + tr.__name__ for tr in self._SUB_TRANSFORMATIONS + ] + extra_options = ( + f" or any other options supported in {sub_trans_names}" + ) raise ValueError(f"'{type(self).__name__}' received invalid " f"options [{invalid_options_list}]. " f"Valid options are " - f"'{list(valid_options.keys())}.") + f"{list(valid_options.keys())}{extra_options}.") if len(wrong_types.keys()) > 0: wrong_types_detail = [] for name in wrong_types.keys(): diff --git a/src/psyclone/tests/psyGen_test.py b/src/psyclone/tests/psyGen_test.py index 293015946b..47216f1ad5 100644 --- a/src/psyclone/tests/psyGen_test.py +++ b/src/psyclone/tests/psyGen_test.py @@ -205,6 +205,105 @@ def apply(self, node, valid: bool = True): "Valid options are '['valid']." in str(excinfo.value)) +def test_transformation_split_kwargs(): + ''' Test that the kwargs can be split when they can be propagated to + multiple sub-transformations. ''' + + class Called1Trans(Transformation): + ''' Transformation Example''' + def apply( + self, + node, + test_option: bool = True, + common_option: bool = True, + **kwargs + ): + self.validate( + node, + test_option=test_option, + common_option=common_option, + **kwargs + ) + # Asserts to prove that the True value was propagated until here + assert test_option + assert common_option + + class Called2Trans(Transformation): + ''' Transformation Example''' + def apply( + self, + node, + test2_option: bool = False, + common_option: bool = False, + **kwargs + ): + self.validate( + node, + test2_option=test2_option, + common_option=common_option, + **kwargs + ) + # Asserts to prove that the True value was propagated until here + assert test2_option + assert common_option + + class TestMetaTrans(Transformation): + ''' MetaTrans Example''' + _trans1 = Called1Trans + _trans2 = Called2Trans + _SUB_TRANSFORMATIONS = [Called1Trans, Called2Trans] + + def validate(self, node, **kwargs): + self_kwargs, tr1_kwargs, tr2_kwargs = self.split_kwargs(kwargs) + self._trans1().validate(node, tr1_kwargs) + self._trans2().validate(node, tr2_kwargs) + self.validate_options(**self_kwargs) + + super().validate(node, **self_kwargs) + + def apply( + self, + node, + meta_option: bool = True, + common_option: bool = True, + **kwargs + ): + # If we want a keyword argument to not be exclusively consumed by + # this transformation and propagate it, put it back into kwargs + kwargs['common_option'] = common_option + # If we want to consume it use it by name + self.validate( + node, + meta_option=meta_option, + **kwargs) + + self._trans1().apply(node, **kwargs) + self._trans2().apply(node, **kwargs) + + # Asserts to prove that the True value was propagated until here + assert meta_option + assert common_option + + test = TestMetaTrans() + test.apply(Node(), meta_option=True, common_option=True, + test_option=True, test2_option=True) + test.validate(Node(), meta_option=True, common_option=True, + test_option=True, test2_option=True) + + with pytest.raises(ValueError) as err: + test.apply(Node(), invalid=True) + assert ("'TestMetaTrans' received invalid options ['invalid']. Valid " + "options are ['meta_option', 'common_option'] or any other " + "options supported in ['Called1Trans', 'Called2Trans']." + == str(err.value)) + with pytest.raises(ValueError) as err: + test.validate(Node(), invalid=True) + assert ("'TestMetaTrans' received invalid options ['invalid']. Valid " + "options are ['meta_option', 'common_option'] or any other " + "options supported in ['Called1Trans', 'Called2Trans']." + == str(err.value)) + + def test_transformation_apply_deprecation_message(capsys): '''Test that passing the options dict to the Transformation.apply function gets the expected deprecation message.''' @@ -313,7 +412,7 @@ def apply(self, node, valid: bool = True, options=None): with pytest.raises(ValueError) as excinfo: instance.validate_options(not_valid=True) assert ("'TestTrans' received invalid options ['not_valid']. " - "Valid options are '['valid', 'options']." in str(excinfo.value)) + "Valid options are ['valid', 'options']." in str(excinfo.value)) # TransInfo class unit tests diff --git a/src/psyclone/tests/psyir/transformations/transformations_test.py b/src/psyclone/tests/psyir/transformations/transformations_test.py index 3698f90949..00eaaedc8d 100644 --- a/src/psyclone/tests/psyir/transformations/transformations_test.py +++ b/src/psyclone/tests/psyir/transformations/transformations_test.py @@ -576,7 +576,7 @@ def test_omploop_trans_new_options(sample_psyir): with pytest.raises(ValueError) as excinfo: omplooptrans.apply(tree.walk(Loop)[0], fakeoption1=1, fakeoption2=2) assert ("'OMPLoopTrans' received invalid options ['fakeoption1', " - "'fakeoption2']. Valid options are '['node_type_check', " + "'fakeoption2']. Valid options are ['node_type_check', " "'verbose', 'collapse', 'force', 'ignore_dependencies_for', " "'privatise_arrays', 'sequential', 'nowait', 'reduction_ops', " "'force_private', 'options', 'reprod', 'enable_reductions']." From 879bf5d61f6afee37f68e91dc4739ff8bc27ae93 Mon Sep 17 00:00:00 2001 From: Sergi Siso Date: Thu, 14 May 2026 15:07:06 +0100 Subject: [PATCH 2/9] #3432 Add split_kwargs in maximal_region_trans --- .../maximal_omp_parallel_region_trans.py | 1 + .../transformations/maximal_region_trans.py | 24 ++++++++++++------- .../maximal_region_trans_test.py | 3 +++ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/psyclone/psyir/transformations/maximal_omp_parallel_region_trans.py b/src/psyclone/psyir/transformations/maximal_omp_parallel_region_trans.py index a4d51a995d..030b197bc5 100644 --- a/src/psyclone/psyir/transformations/maximal_omp_parallel_region_trans.py +++ b/src/psyclone/psyir/transformations/maximal_omp_parallel_region_trans.py @@ -65,6 +65,7 @@ class MaximalOMPParallelRegionTrans(MaximalRegionTrans): the discussion on #3205 for more detail.''' # The type of parallel transformation to be applied to the input region. _transformation = OMPParallelTrans + _SUB_TRANSFORMATIONS = [OMPParallelTrans] # Tuple of statement nodes allowed inside the _transformation _allowed_contiguous_statements = ( OMPTaskwaitDirective, diff --git a/src/psyclone/psyir/transformations/maximal_region_trans.py b/src/psyclone/psyir/transformations/maximal_region_trans.py index 2642df88a3..011906b3bf 100644 --- a/src/psyclone/psyir/transformations/maximal_region_trans.py +++ b/src/psyclone/psyir/transformations/maximal_region_trans.py @@ -31,12 +31,13 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- -# Authors A. B. G. Chalk, STFC Daresbury Lab +# Author: A. B. G. Chalk, STFC Daresbury Lab +# Modified: S. Siso, STFC Daresbury Lab '''This module contains the MaximalRegionTrans.''' import abc -from typing import Union +from typing import Union, Any from psyclone.psyir.nodes import ( Node, @@ -148,6 +149,7 @@ def _can_be_in_region(self, node: Node) -> bool: def _compute_transformable_sections( self, node_list: list[Node], trans: Transformation, + trans_kwargs: dict[str, Any] ) -> list[list[Node]]: ''' Computes the sections of the input node_list to apply the @@ -155,6 +157,7 @@ def _compute_transformable_sections( :param node_list: The node_list passed into this Transformation. :param trans: The transformation applied to the regions found. + :param trans_kwargs: The kwargs applied to the transformation. :returns: The list of node_lists to apply this class' _transformation class to. ''' @@ -168,7 +171,7 @@ def _compute_transformable_sections( # Check that validation still succeeds if we add this child # to the current block. try: - trans.validate(current_block + [child]) + trans.validate(current_block + [child], **trans_kwargs) current_block.append(child) except TransformationError: # If validation now fails, then don't add this to the @@ -189,17 +192,17 @@ def _compute_transformable_sections( # Need to recurse on some node types if isinstance(child, IfBlock): if_blocks = self._compute_transformable_sections( - child.if_body, trans + child.if_body, trans, trans_kwargs ) all_blocks.extend(if_blocks) if child.else_body: else_blocks = self._compute_transformable_sections( - child.else_body, trans + child.else_body, trans, trans_kwargs ) all_blocks.extend(else_blocks) if isinstance(child, (Loop, WhileLoop)): loop_blocks = self._compute_transformable_sections( - child.loop_body, trans + child.loop_body, trans, trans_kwargs ) all_blocks.extend(loop_blocks) # If any nodes are left in the current block at the end of the @@ -220,7 +223,8 @@ def validate(self, nodes: Union[Node, Schedule, list[Node]], **kwargs): same parent and aren't consecutive. ''' - self.validate_options(**kwargs) + self_kwargs, _ = self.split_kwargs(kwargs) + self.validate_options(**self_kwargs) node_list = self.get_node_list(nodes) node_parent = node_list[0].parent @@ -247,11 +251,13 @@ def apply(self, nodes: Union[Node, Schedule, list[Node]], **kwargs): # Call validate. self.validate(nodes, **kwargs) + _, tr_kwargs = self.split_kwargs(kwargs) par_trans = self._transformation() - all_blocks = self._compute_transformable_sections(node_list, par_trans) + all_blocks = self._compute_transformable_sections( + node_list, par_trans, tr_kwargs) # Apply the transformation to all of the blocks found. for block in all_blocks: - par_trans.apply(block) + par_trans.apply(block, **tr_kwargs) diff --git a/src/psyclone/tests/psyir/transformations/maximal_region_trans_test.py b/src/psyclone/tests/psyir/transformations/maximal_region_trans_test.py index ad62f9c510..9f719eec45 100644 --- a/src/psyclone/tests/psyir/transformations/maximal_region_trans_test.py +++ b/src/psyclone/tests/psyir/transformations/maximal_region_trans_test.py @@ -55,6 +55,7 @@ class MaxParTrans(MaximalRegionTrans): ''' Dummy class to test MaxParallelRegionTrans' functionality. ''' # The apply function will do OMPParallelTrans around allowed regions. _transformation = OMPParallelTrans + _SUB_TRANSFORMATIONS = [OMPParallelTrans] # We're only allowing assignment because its straightforward to test with. _allowed_contiguous_statements = (Assignment, ) # Should parallelise any found region that contains an assignment. @@ -265,6 +266,7 @@ def apply(self, node, **kwargs): class OneParTrans(MaximalRegionTrans): '''Dummy MaximalRegionTrans that uses our FakeTrans''' _transformation = Faketrans + _SUB_TRANSFORMATIONS = [Faketrans] _allowed_contiguous_statements = (Assignment, ) _required_nodes = (Assignment, ) @@ -322,6 +324,7 @@ def apply(self, node: Assignment, **kwargs): class OneParTrans(MaximalRegionTrans): '''Dummy MaximalRegionTrans that uses our FakeTrans''' _transformation = Faketrans + _SUB_TRANSFORMATIONS = [Faketrans] _allowed_contiguous_statements = (Assignment, ) _required_nodes = (Assignment, ) From 2ac235c12fb733223d8445cd69a880bcbcc483ee Mon Sep 17 00:00:00 2001 From: Sergi Siso Date: Fri, 15 May 2026 10:41:26 +0100 Subject: [PATCH 3/9] #3432 Improve split_kwargs documentation and tests --- src/psyclone/psyGen.py | 3 ++- src/psyclone/tests/psyGen_test.py | 15 +++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/psyclone/psyGen.py b/src/psyclone/psyGen.py index ae7f004238..24317f44dd 100644 --- a/src/psyclone/psyGen.py +++ b/src/psyclone/psyGen.py @@ -2369,7 +2369,8 @@ def split_kwargs( transformation and every other transformation listed in the _SUB_TRANSFORMATIONS list. The first kwargs (the ones for itself) will also include any key that is not valid in any of the other - transformation (e.g. unsupported options). + transformation (this is done to ensure one of the validate_options + reports invalid options when those are provided). ''' # The first kwargs starts with all the items first_dict = dict(kwargs) diff --git a/src/psyclone/tests/psyGen_test.py b/src/psyclone/tests/psyGen_test.py index 47216f1ad5..0541c24549 100644 --- a/src/psyclone/tests/psyGen_test.py +++ b/src/psyclone/tests/psyGen_test.py @@ -228,6 +228,9 @@ def apply( assert test_option assert common_option + def validate(self, node, **kwargs): + self.validate_options(**kwargs) + class Called2Trans(Transformation): ''' Transformation Example''' def apply( @@ -247,6 +250,9 @@ def apply( assert test2_option assert common_option + def validate(self, node, **kwargs): + self.validate_options(**kwargs) + class TestMetaTrans(Transformation): ''' MetaTrans Example''' _trans1 = Called1Trans @@ -255,8 +261,8 @@ class TestMetaTrans(Transformation): def validate(self, node, **kwargs): self_kwargs, tr1_kwargs, tr2_kwargs = self.split_kwargs(kwargs) - self._trans1().validate(node, tr1_kwargs) - self._trans2().validate(node, tr2_kwargs) + self._trans1().validate(node, **tr1_kwargs) + self._trans2().validate(node, **tr2_kwargs) self.validate_options(**self_kwargs) super().validate(node, **self_kwargs) @@ -276,9 +282,10 @@ def apply( node, meta_option=meta_option, **kwargs) + _, tr1_kwargs, tr2_kwargs = self.split_kwargs(kwargs) - self._trans1().apply(node, **kwargs) - self._trans2().apply(node, **kwargs) + self._trans1().apply(node, **tr1_kwargs) + self._trans2().apply(node, **tr2_kwargs) # Asserts to prove that the True value was propagated until here assert meta_option From 1b2c6d167fe5a9faceaac9f4a48a98f822a68174 Mon Sep 17 00:00:00 2001 From: Sergi Siso Date: Fri, 15 May 2026 12:50:42 +0100 Subject: [PATCH 4/9] #3432 Add documentation for the split_kwargs functionality --- doc/developer_guide/transformations.rst | 44 +++++++++++++++++++++++++ src/psyclone/psyGen.py | 7 ++-- src/psyclone/tests/psyGen_test.py | 9 +++-- 3 files changed, 50 insertions(+), 10 deletions(-) diff --git a/doc/developer_guide/transformations.rst b/doc/developer_guide/transformations.rst index 801ea98a75..19b31333ea 100644 --- a/doc/developer_guide/transformations.rst +++ b/doc/developer_guide/transformations.rst @@ -567,4 +567,48 @@ the ``ParallelLoopTrans`` class for reference): 7. Repeat this process for any classes that the class inherits from. +Transformations options and meta-transformations +================================================ + +Sometimes it is useful to implement transformation that in turn call one or +multiple other transformations. In these cases we often want to propagate the +kwargs not only to the superclass (by inheritance) but also to the internally +used transformations, but we cannot pass the whole kwargs everywhere because +many options will only be valid for certain transformations. + +To easily decide which options provide to each transformation we are developing +the concept of ``_SUB_TRANSFORMATIONS``. Populating this class attribute allows +`self.split_kwargs(**kwargs)` to return the set of valid kwargs for itself and +each of the listed transformations, while maintaining the `validate_options` +functionality. Typically both the apply and the validate need to split the +kwargs as in the example below: + +.. code-block:: python + + class TestMetaTrans(Transformation): + ''' MetaTrans Example''' + _trans1 = Called1Trans + _trans2 = Called2Trans + _SUB_TRANSFORMATIONS = [Called1Trans, Called2Trans] + + def validate(self, node, **kwargs): + self_kwargs, tr1_kwargs, tr2_kwargs = self.split_kwargs(**kwargs) + self._trans1().validate(node, **tr1_kwargs) + self._trans2().validate(node, **tr2_kwargs) + self.validate_options(**self_kwargs) + super().validate(node, **self_kwargs) + + def apply(self, node, my_option): + # Omitted code before using the subtransformations... + _, tr1_kwargs, tr2_kwargs = self.split_kwargs( + my_option=my_options, **kwargs) + self._trans1().apply(node, **tr1_kwargs) + self._trans2().apply(node, **tr2_kwargs) + +Note that the TestMetaTrans docstring won't mention the SUB_TRANSFORMATIONS +options, therefore it is currently recommended to explicitly mention the +relevant internal transformations in the apply docstring with a +``:py:class:`` tag (to create a link). TODO #3330 will explore automating +this step. + .. footbibliography:: diff --git a/src/psyclone/psyGen.py b/src/psyclone/psyGen.py index 24317f44dd..1a046fa3af 100644 --- a/src/psyclone/psyGen.py +++ b/src/psyclone/psyGen.py @@ -2358,10 +2358,7 @@ def name(self): ''' return type(self).__name__ - def split_kwargs( - self, - kwargs: dict[str, Any], - ) -> tuple[dict[str, Any]]: + def split_kwargs(self, **kwargs) -> tuple[dict[str, Any]]: ''' :param kwargs: the list of kwargs to split. @@ -2387,7 +2384,7 @@ def split_kwargs( if key not in type(self).get_valid_options(): del first_dict[key] - return tuple([first_dict] + other_dicts) + return first_dict, *other_dicts @abc.abstractmethod def apply(self, node, options=None, **kwargs): diff --git a/src/psyclone/tests/psyGen_test.py b/src/psyclone/tests/psyGen_test.py index 0541c24549..03a036eaf4 100644 --- a/src/psyclone/tests/psyGen_test.py +++ b/src/psyclone/tests/psyGen_test.py @@ -260,7 +260,7 @@ class TestMetaTrans(Transformation): _SUB_TRANSFORMATIONS = [Called1Trans, Called2Trans] def validate(self, node, **kwargs): - self_kwargs, tr1_kwargs, tr2_kwargs = self.split_kwargs(kwargs) + self_kwargs, tr1_kwargs, tr2_kwargs = self.split_kwargs(**kwargs) self._trans1().validate(node, **tr1_kwargs) self._trans2().validate(node, **tr2_kwargs) self.validate_options(**self_kwargs) @@ -274,15 +274,14 @@ def apply( common_option: bool = True, **kwargs ): - # If we want a keyword argument to not be exclusively consumed by - # this transformation and propagate it, put it back into kwargs - kwargs['common_option'] = common_option # If we want to consume it use it by name self.validate( node, meta_option=meta_option, + common_option=common_option, **kwargs) - _, tr1_kwargs, tr2_kwargs = self.split_kwargs(kwargs) + _, tr1_kwargs, tr2_kwargs = self.split_kwargs( + meta_option=meta_option, common_option=common_option, **kwargs) self._trans1().apply(node, **tr1_kwargs) self._trans2().apply(node, **tr2_kwargs) From a36bcaf0affcb9c0f78c891e4382955c019a78b2 Mon Sep 17 00:00:00 2001 From: Sergi Siso Date: Fri, 15 May 2026 13:02:22 +0100 Subject: [PATCH 5/9] Replace bitbucket.org/apeg with github.com/stfc --- doc/user_guide/profiling.rst | 4 ++-- examples/gocean/eg5/profile/Makefile | 4 ++-- examples/gocean/eg5/profile/README.md | 4 ++-- examples/nemo/eg3/Makefile | 2 +- examples/nemo/eg3/README.md | 2 +- lib/profiling/README.md | 2 +- lib/profiling/dl_timer/Makefile | 6 +++--- lib/profiling/dl_timer/README.md | 4 ++-- lib/profiling/dl_timer/dl_timer.f90 | 2 +- tutorial/practicals/generic/2_profiling/Makefile | 2 +- tutorial/practicals/generic/2_profiling/README.md | 2 +- .../training/gocean/2.12-GameOfLife-profiling/README.md | 2 +- 12 files changed, 18 insertions(+), 18 deletions(-) diff --git a/doc/user_guide/profiling.rst b/doc/user_guide/profiling.rst index 7025377a2d..61180694c7 100644 --- a/doc/user_guide/profiling.rst +++ b/doc/user_guide/profiling.rst @@ -107,9 +107,9 @@ libraries that come with PSyclone: for each MPI process), and not thread-safe. ``lib/profiling/dl_timer`` - This wrapper uses the apeg-dl_timer library. In order to use + This wrapper uses the dl_timer library. In order to use this wrapper, you must download and install the dl_timer library - from https://bitbucket.org/apeg/dl_timer. This library has + from https://github.com/stfc/dl_timer. This library has various compile-time options and may be built with MPI or OpenMP support. Additional link options might therefore be required (e.g. enabling OpenMP, or linking with MPI). diff --git a/examples/gocean/eg5/profile/Makefile b/examples/gocean/eg5/profile/Makefile index a6067569d7..549a422051 100644 --- a/examples/gocean/eg5/profile/Makefile +++ b/examples/gocean/eg5/profile/Makefile @@ -108,9 +108,9 @@ tau: $(INF_LIB) tau_lib $(NAME).tau # The dl-timer library is available from: -# https://bitbucket.org/apeg/dl_timer/src/master/ +# https://github.com/stfc/dl_timer/src/master/ # As default assume dl_timer is installed 'next' to PSyclone. -DL_TIMER_ROOT?=$(ROOT_DIR)/../apeg-dl_timer +DL_TIMER_ROOT?=$(ROOT_DIR)/../dl_timer # Needs to link with openmp because the dl_timer library needs openmp. dl_timer: $(MAKE) WRAPPER_DIR="$(PROFILE_DIR)/dl_timer" \ diff --git a/examples/gocean/eg5/profile/README.md b/examples/gocean/eg5/profile/README.md index 70f8a145c0..45e6835b15 100644 --- a/examples/gocean/eg5/profile/README.md +++ b/examples/gocean/eg5/profile/README.md @@ -56,8 +56,8 @@ The location of the dl_esm_inf infrastructure library, it defaults to which is the version included in PSyclone. ### DL_TIMER_ROOT: -The location of the apeg-dl_timer library. It defaults to -``../../../../../apeg-dl_timer``, i.e. it is assumed that apeg-dl_timer +The location of the dl_timer library. It defaults to +``../../../../../dl_timer``, i.e. it is assumed that dl_timer is installed next to PSyclone. Note that until Issue #730 is complete, executing this example will fail as the labels produced by PSyclone are longer than diff --git a/examples/nemo/eg3/Makefile b/examples/nemo/eg3/Makefile index 901f6a218a..90b0ee14aa 100644 --- a/examples/nemo/eg3/Makefile +++ b/examples/nemo/eg3/Makefile @@ -37,7 +37,7 @@ # Makefile for the 3rd NEMO example. Uses PSyclone (which must # be installed) to generate Fortran with OpenACC directives for # the tracer-advection benchmark. The 'compile' target requires -# the dl_timer library (bitbucket.org/apeg/dl_timer) which must +# the dl_timer library (github.com/stfc/dl_timer) which must # be installed and the location specified in DL_TIMER_ROOT below. # # The compiler to use must be specified via the F90 environment diff --git a/examples/nemo/eg3/README.md b/examples/nemo/eg3/README.md index 22e9dceabd..e14722759e 100644 --- a/examples/nemo/eg3/README.md +++ b/examples/nemo/eg3/README.md @@ -50,7 +50,7 @@ demonstrate how to obtain good performance. Since `tra_adv.F90` is instrumented for use with the dl_timer library, this library is also required. It is available from -[bitbucket.org/apeg/dl_timer](https://bitbucket.org/apeg/dl_timer). +[bitbucket.org/apeg/dl_timer](https://github.com/stfc/dl_timer). Once dl_timer has been downloaded, the supplied Makefile must be edited to supply the location of the library. The compiler and flags diff --git a/lib/profiling/README.md b/lib/profiling/README.md index 2708d61ab2..c9180d513e 100644 --- a/lib/profiling/README.md +++ b/lib/profiling/README.md @@ -52,7 +52,7 @@ Example output: This is a wrapper library that maps the [PSyclone profiling API]( https://psyclone.readthedocs.io/en/latest/user_guide/profiling.html#profiling) to the dl_timer API. A copy of dl_timer can be downloaded from -https://bitbucket.org/apeg/dl_timer. +https://github.com/stfc/dl_timer. The PSyclone dl_timer wrapper library uses the ``ProfileData`` type and dl_timer's timer_register function to store the module/region name and diff --git a/lib/profiling/dl_timer/Makefile b/lib/profiling/dl_timer/Makefile index c68bb4f228..d1fef04d5c 100644 --- a/lib/profiling/dl_timer/Makefile +++ b/lib/profiling/dl_timer/Makefile @@ -38,11 +38,11 @@ # Compiler and compiler flags F90 ?= gfortran F90FLAGS ?= -# Path to the 'apeg/dl_timer' installation. It defaults to the assumed -# location of the apeg/dl_timer library next to a clone of PSyclone +# Path to the 'dl_timer' installation. It defaults to the assumed +# location of the dl_timer library next to a clone of PSyclone # repository. Overwrite for a custom location. DL_TIMER_ROOT ?= $(PSYDATA_LIB_DIR)/../../dl_timer -# Path to the 'apeg/dl_timer' "include" files. Overwrite for a custom location. +# Path to the 'stfc/dl_timer' "include" files. Overwrite for a custom location. DL_TIMER_INCLUDE ?= $(DL_TIMER_ROOT)/src # Path to the PSyclone wrapper libraries. It defaults to the relative path to # the top-level 'lib' directory. Overwrite for a custom location. diff --git a/lib/profiling/dl_timer/README.md b/lib/profiling/dl_timer/README.md index 72111899c1..9bfdf71bbc 100644 --- a/lib/profiling/dl_timer/README.md +++ b/lib/profiling/dl_timer/README.md @@ -7,7 +7,7 @@ dl_timer API. This library is **thread-safe**. ## Dependencies The library dl_timer must be installed, which can be downloaded from -https://bitbucket.org/apeg/dl_timer. +https://github.com/stfc/dl_timer. This profiling library uses the [PSyData API]( https://psyclone.readthedocs.io/en/latest/user_guide/psy_data.html) to interface with @@ -37,7 +37,7 @@ To compile the PSyclone wrapper library for dl_timer, one of the following two ``Makefile`` variables must be set to specify the path to the dl_timer installation: -- ``DL_TIMER_ROOT``, the path to the ``apeg-dl_timer`` directory in which +- ``DL_TIMER_ROOT``, the path to the ``stfc/dl_timer`` directory in which dl_timer is compiled. It defaults to ``./../../../../dl_timer`` in the ``Makefile`` (i.e., it assumes dl_timer is installed next to a PSyclone clone). diff --git a/lib/profiling/dl_timer/dl_timer.f90 b/lib/profiling/dl_timer/dl_timer.f90 index 7f3b473c9e..f3d0d1a246 100644 --- a/lib/profiling/dl_timer/dl_timer.f90 +++ b/lib/profiling/dl_timer/dl_timer.f90 @@ -34,7 +34,7 @@ !> An implementation of the PSyData API for profiling, which wraps the use -!> of the dl_timer library (https://bitbucket.org/apeg/dl_timer). +!> of the dl_timer library (https://github.com/stfc/dl_timer). module profile_psy_data_mod diff --git a/tutorial/practicals/generic/2_profiling/Makefile b/tutorial/practicals/generic/2_profiling/Makefile index ca297992b5..8926086cd2 100644 --- a/tutorial/practicals/generic/2_profiling/Makefile +++ b/tutorial/practicals/generic/2_profiling/Makefile @@ -57,7 +57,7 @@ PROFILE_LINK ?= # use profile_psy_data_mod, only : profile_PSyDataInit # call profile_PSyDataInit() # -# (git clone https://bitbucket.org/apeg/dl_timer.git +# (git clone https://github.com/stfc/dl_timer.git # cd dl_timer; . compiler_setup/gnu_setup.sh; make sm_lib) #PROFILE_WRAPPER_DIR ?= ../../../../lib/profiling/dl_timer #PROFILE_WRAPPER_LIB ?= ${PROFILE_WRAPPER_DIR}/libdl_timer_psy.a diff --git a/tutorial/practicals/generic/2_profiling/README.md b/tutorial/practicals/generic/2_profiling/README.md index 3220cb75cf..356e361aa4 100644 --- a/tutorial/practicals/generic/2_profiling/README.md +++ b/tutorial/practicals/generic/2_profiling/README.md @@ -197,7 +197,7 @@ transformation script to perform finer-grained profiling. a different PSyData wrapper library. For CPU, the next step up from the 'simple_timing' library we have used so far is 'dl_timer' which is available from - [bitbucket](https://bitbucket.org/apeg/dl_timer/src/master/). You + [bitbucket](https://github.com/stfc/dl_timer/src/master/). You will need to obtain the source for this library: ``` git clone https://github.com/stfc/dl_timer.git diff --git a/tutorial/training/gocean/2.12-GameOfLife-profiling/README.md b/tutorial/training/gocean/2.12-GameOfLife-profiling/README.md index 088ba0c473..83f39edbb9 100644 --- a/tutorial/training/gocean/2.12-GameOfLife-profiling/README.md +++ b/tutorial/training/gocean/2.12-GameOfLife-profiling/README.md @@ -66,7 +66,7 @@ to be added. PSyclone provides interfaces to `DrHook`, the NVIDIA Tools Extension library (NVTX), the LFRic-timer (the timer functionality included in LFRic), the Tuning and Analysis Utilities `Tau` (https://www.cs.uoregon.edu/research/tau/home.php), -and `dl_timer` (https://bitbucket.org/apeg/dl_timer). If you have any +and `dl_timer` (https://github.com/stfc/dl_timer). If you have any of these libraries available, you only need to modify the compiler options to point to the right PSyclone wrapper library, and in the linking step add the wrapper library and your profiling library. From bb358cb86fd13d4c1313eff9dee96a0a88268906 Mon Sep 17 00:00:00 2001 From: Sergi Siso Date: Fri, 15 May 2026 13:22:40 +0100 Subject: [PATCH 6/9] #3432 Use **kwargs in maximal_region_trans --- src/psyclone/psyir/transformations/maximal_region_trans.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/psyclone/psyir/transformations/maximal_region_trans.py b/src/psyclone/psyir/transformations/maximal_region_trans.py index 011906b3bf..813820dc2a 100644 --- a/src/psyclone/psyir/transformations/maximal_region_trans.py +++ b/src/psyclone/psyir/transformations/maximal_region_trans.py @@ -223,7 +223,7 @@ def validate(self, nodes: Union[Node, Schedule, list[Node]], **kwargs): same parent and aren't consecutive. ''' - self_kwargs, _ = self.split_kwargs(kwargs) + self_kwargs, _ = self.split_kwargs(**kwargs) self.validate_options(**self_kwargs) node_list = self.get_node_list(nodes) @@ -251,7 +251,7 @@ def apply(self, nodes: Union[Node, Schedule, list[Node]], **kwargs): # Call validate. self.validate(nodes, **kwargs) - _, tr_kwargs = self.split_kwargs(kwargs) + _, tr_kwargs = self.split_kwargs(**kwargs) par_trans = self._transformation() From 3a1e171803519b78bf5bb2d7fd3e3040469658db Mon Sep 17 00:00:00 2001 From: Sergi Siso Date: Fri, 15 May 2026 14:04:09 +0100 Subject: [PATCH 7/9] #3432 Fix more links --- examples/gocean/eg5/profile/Makefile | 2 +- examples/nemo/eg3/README.md | 4 ++-- tutorial/practicals/generic/2_profiling/README.md | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/examples/gocean/eg5/profile/Makefile b/examples/gocean/eg5/profile/Makefile index 549a422051..a531dd111b 100644 --- a/examples/gocean/eg5/profile/Makefile +++ b/examples/gocean/eg5/profile/Makefile @@ -108,7 +108,7 @@ tau: $(INF_LIB) tau_lib $(NAME).tau # The dl-timer library is available from: -# https://github.com/stfc/dl_timer/src/master/ +# https://github.com/stfc/dl_timer # As default assume dl_timer is installed 'next' to PSyclone. DL_TIMER_ROOT?=$(ROOT_DIR)/../dl_timer # Needs to link with openmp because the dl_timer library needs openmp. diff --git a/examples/nemo/eg3/README.md b/examples/nemo/eg3/README.md index e14722759e..47e71a10cf 100644 --- a/examples/nemo/eg3/README.md +++ b/examples/nemo/eg3/README.md @@ -49,8 +49,8 @@ directives using PSyclone with the NEMO API. It is not intended to demonstrate how to obtain good performance. Since `tra_adv.F90` is instrumented for use with the dl_timer library, -this library is also required. It is available from -[bitbucket.org/apeg/dl_timer](https://github.com/stfc/dl_timer). +this library is also required. It is available on +[github](https://github.com/stfc/dl_timer). Once dl_timer has been downloaded, the supplied Makefile must be edited to supply the location of the library. The compiler and flags diff --git a/tutorial/practicals/generic/2_profiling/README.md b/tutorial/practicals/generic/2_profiling/README.md index 356e361aa4..45f15dda66 100644 --- a/tutorial/practicals/generic/2_profiling/README.md +++ b/tutorial/practicals/generic/2_profiling/README.md @@ -196,8 +196,7 @@ transformation script to perform finer-grained profiling. 3. If you have time, you may want to try repeating this exercise using a different PSyData wrapper library. For CPU, the next step up from the 'simple_timing' library we have used so far is 'dl_timer' which - is available from - [bitbucket](https://github.com/stfc/dl_timer/src/master/). You + is available from [github](https://github.com/stfc/dl_timer/). You will need to obtain the source for this library: ``` git clone https://github.com/stfc/dl_timer.git From 016b797d13c48e38cb8298d3332c291eb19a12d1 Mon Sep 17 00:00:00 2001 From: Sergi Siso Date: Fri, 15 May 2026 14:15:34 +0100 Subject: [PATCH 8/9] #3432 Fix ProfileTrans in nemo utils.py --- examples/nemo/scripts/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/nemo/scripts/utils.py b/examples/nemo/scripts/utils.py index 40005514a5..a3894fcbdb 100755 --- a/examples/nemo/scripts/utils.py +++ b/examples/nemo/scripts/utils.py @@ -499,6 +499,7 @@ class MaximalProfilingOutsideDirectivesTrans(MaximalRegionTrans): # (which would create unclosed hooks). _allowed_contiguous_statements = (Assignment, Call, CodeBlock) _transformation = ProfileTrans + _SUB_TRANSFORMATIONS = [ProfileTrans] def _satisfies_minimum_region_rules(self, region: list[Node]) -> bool: '''Returns whether the provided node list satisfies the From 99df5b357f894da4900d519f400e91fa7f70eb28 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Fri, 15 May 2026 15:43:17 +0100 Subject: [PATCH 9/9] #3432 update changelog --- changelog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changelog b/changelog index 616bc39a84..0cc6df4660 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,6 @@ + 20) PR #3433 for 3432. Introduces initial support for sub transformations + and option handling. + 19) PR #3404 for 3403. Modernises the remaining runme examples. 18) PR #3410 towards #2668. Update more Transformations to use kwargs.