-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SCC: Support for bounds aliases and derived type members as bounds #250
Conversation
Documentation for this branch can be viewed at https://sites.ecmwf.int/docs/loki/250/index.html |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #250 +/- ##
==========================================
- Coverage 94.95% 94.95% -0.01%
==========================================
Files 153 153
Lines 32052 32086 +34
==========================================
+ Hits 30436 30466 +30
- Misses 1616 1620 +4
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
Can I push a small fix to the dependency transform here or shall I file a separate PR for that? |
I would hold off or make it a separate PR. That transforms has been reshuffled a bit on the new Scheduler branch. |
85ffadc
to
3dd95f8
Compare
@reuterbal I have rebased and fixed this PR and #249, please let me know when I can push changes to both. |
I've merged the backlog of already reviewed PRs now, so now is likely a good point to rebase your PRs. Many thanks! |
This has a conflict now with #270, so I am turning this into a draft until that is reviewed and merged. |
77ffee9
to
ae61311
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this looks great!
I've left a few comments but no fundamental show stoppers.
bounds_aliases : list or tuple of strings | ||
String representations of alternative bounds variables that are | ||
used to define loop ranges. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really great, I was missing that functionality (and hacked around in the Fortran instead... 😬 )
loki/tools/util.py
Outdated
def resolve_typebound_var(name, variable_map): | ||
""" | ||
A small convenience utility to resolve type-bound variables. | ||
|
||
Parameters | ||
---------- | ||
name : str | ||
The full name of the variable to be resolved, e.g., a%b%c%d. | ||
variable_map : dict | ||
A map of the variables defined in the current scope. | ||
""" | ||
|
||
name_parts = name.split('%', maxsplit=1) | ||
if (var := variable_map.get(name_parts[0], None)) and len(name_parts) > 1: | ||
var = var.get_derived_type_member(name_parts[1]) | ||
return var |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer that an expression-specific utility like this lives in the loki.expression
or loki.ir
package. Could we not make this a method of Scope
, for example, with the option to provide the variable_map
as an optional argument. It could by default be initialized to self.variable_map
but for frequent calls, providing as an argument speeds things up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, that would be much cleaner, thanks! I assume you mean ProgramUnit
though rather than Scope
, because that is where the variables
and variable_map
are computed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, thanks, ProgramUnit
would be the right place.
variables = routine.variables | ||
bounds = as_tuple(horizontal.bounds[0]) | ||
bounds += as_tuple(horizontal.bounds[1]) | ||
try: | ||
if bounds[0] not in variables: | ||
raise RuntimeError(f'No horizontal start variable found in {routine.name}') | ||
if bounds[1] not in variables: | ||
raise RuntimeError(f'No horizontal end variable found in {routine.name}') | ||
except RuntimeError as exc: | ||
if horizontal._bounds_aliases: | ||
bounds = as_tuple(horizontal._bounds_aliases[0]) | ||
bounds += as_tuple(horizontal._bounds_aliases[1]) | ||
if bounds[0].split('%', maxsplit=1)[0] not in variables: | ||
raise RuntimeError(f'No horizontal start variable found in {routine.name}') from exc | ||
if bounds[1].split('%', maxsplit=1)[0] not in variables: | ||
raise RuntimeError(f'No horizontal end variable found in {routine.name}') from exc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks a bit long-winded, with the use of exceptions instead of if
s. I would add a method bounds_expressions
to Dimension
, e.g.:
>>> d = Dimension('test_dim_alias', bounds=('istart', 'iend'), bounds_aliases=('bnds%start', 'bnds%end'))
>>> d.bounds_expressions
(('istart', 'bnds%start'), ('iend', 'bnds%end'))
and then rewrite the method as:
variables = routine.variables | |
bounds = as_tuple(horizontal.bounds[0]) | |
bounds += as_tuple(horizontal.bounds[1]) | |
try: | |
if bounds[0] not in variables: | |
raise RuntimeError(f'No horizontal start variable found in {routine.name}') | |
if bounds[1] not in variables: | |
raise RuntimeError(f'No horizontal end variable found in {routine.name}') | |
except RuntimeError as exc: | |
if horizontal._bounds_aliases: | |
bounds = as_tuple(horizontal._bounds_aliases[0]) | |
bounds += as_tuple(horizontal._bounds_aliases[1]) | |
if bounds[0].split('%', maxsplit=1)[0] not in variables: | |
raise RuntimeError(f'No horizontal start variable found in {routine.name}') from exc | |
if bounds[1].split('%', maxsplit=1)[0] not in variables: | |
raise RuntimeError(f'No horizontal end variable found in {routine.name}') from exc | |
variable_map = routine.variable_map | |
bounds = () | |
for name, bounds in zip(['start', 'end'], horizontal.bounds_expressions): | |
for bound in bounds: | |
if bound in variable_map: | |
bounds += (bound,) | |
break | |
else: | |
raise RuntimeError(f'No horizontol {name} variable matching {horizontal.bounds_expressions[0]} found in {routine.name}') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Many many thanks for this 🙏 this is much cleaner and simpler than what I original had 👌
@@ -275,7 +293,7 @@ def process_kernel(self, routine): | |||
return | |||
|
|||
# check for horizontal loop bounds in subroutine symbol table | |||
self.check_horizontal_var(routine, self.horizontal) | |||
bounds = self.check_horizontal_var(routine, self.horizontal) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be sensible to rename this utility to get_horizontal_bound_var
or something similar? Also, instead of routine
you could directly pass the variable_map
, I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I agree with the name change, but I'll keep the routine as an argument because I would like to also print the routine name to have a more informative error message.
demote_locals = item.config.get('demote_locals', self.demote_local_arrays) | ||
self.process_kernel(routine, demote_locals=demote_locals) | ||
preserve_arrays = item.config.get('preserve_arrays', []) | ||
self.process_kernel(routine, demote_locals=demote_locals, preserve_arrays=preserve_arrays) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These options should be documented. Would you mind adding a short paragraph in the class docstring?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with what @reuterbal pointed out, and have nothing further to add. I can confirm that this runs fine with ec-physics regression.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Many thanks, very happy with this now!
This is the final PR needed to transform the SL. It consists of a series of fixes to the SCC transforms, the biggest of which is the ability to use aliased components of a derived-type as the horizontal loop bounds.
NB: this is stacked on top of #249.