Reference Guide  2.5.0
psyclone.domain.gocean.transformations.gocean_const_loop_bounds_trans.GOConstLoopBoundsTrans Class Reference
Inheritance diagram for psyclone.domain.gocean.transformations.gocean_const_loop_bounds_trans.GOConstLoopBoundsTrans:
Collaboration diagram for psyclone.domain.gocean.transformations.gocean_const_loop_bounds_trans.GOConstLoopBoundsTrans:

Public Member Functions

def __str__ (self)
 
def name (self)
 
def validate (self, node, options=None)
 
def apply (self, node, options=None)
 

Detailed Description

 Use of a common constant variable for each loop bound within
a GOInvokeSchedule. By deafault, PSyclone generates loops where
the bounds are obtained by de-referencing a field object, e.g.:

.. code-block:: fortran

  DO j = my_field%grid%internal%ystart, my_field%grid%internal%ystop

Some compilers are able to produce more efficient code if they are
provided with information on the relative trip-counts of the loops
within an Invoke. With constant loop bounds, PSyclone generates code
like:

.. code-block:: fortran

  ny = my_field%grid%subdomain%internal%ystop
  ...
  DO j = 1, ny-1

In practice, the application of the constant loop bounds transformation
looks something like, e.g.:

>>> from psyclone.parse.algorithm import parse
>>> from psyclone.psyGen import PSyFactory
>>> import os
>>> TEST_API = "gocean1.0"
>>> _, info = parse(os.path.join("tests", "test_files", "gocean1p0",
...                              "single_invoke.f90"),
...                 api=TEST_API)
>>> psy = PSyFactory(TEST_API).create(info)
>>> invoke = psy.invokes.get('invoke_0_compute_cu')
>>> schedule = invoke.schedule
>>>
>>> from psyclone.transformations import GOConstLoopBoundsTrans
>>> clbtrans = GOConstLoopBoundsTrans()
>>>
>>> clbtrans.apply(schedule)
>>> print(schedule.view())

Definition at line 50 of file gocean_const_loop_bounds_trans.py.

Member Function Documentation

◆ apply()

def psyclone.domain.gocean.transformations.gocean_const_loop_bounds_trans.GOConstLoopBoundsTrans.apply (   self,
  node,
  options = None 
)
 Modify the GOcean kernel loops in a GOInvokeSchedule to use
common constant loop bound variables.

:param node: the GOInvokeSchedule of which all loops will get the \
    constant loop bounds.
:type node: :py:class:`psyclone.gocean1p0.GOInvokeSchedule`
:param options: a dictionary with options for transformations.
:type options: Optional[Dict[str, Any]]

Reimplemented from psyclone.psyGen.Transformation.

Definition at line 176 of file gocean_const_loop_bounds_trans.py.

176  def apply(self, node, options=None):
177  ''' Modify the GOcean kernel loops in a GOInvokeSchedule to use
178  common constant loop bound variables.
179 
180  :param node: the GOInvokeSchedule of which all loops will get the \
181  constant loop bounds.
182  :type node: :py:class:`psyclone.gocean1p0.GOInvokeSchedule`
183  :param options: a dictionary with options for transformations.
184  :type options: Optional[Dict[str, Any]]
185 
186  '''
187  self.validate(node, options=options)
188 
189  i_stop = node.symbol_table.new_symbol(
190  "istop", symbol_type=DataSymbol, datatype=INTEGER_TYPE)
191  j_stop = node.symbol_table.new_symbol(
192  "jstop", symbol_type=DataSymbol, datatype=INTEGER_TYPE)
193 
194  # Get a field argument from the argument list (we checked there
195  # is at least one on the validation method)
196  for arg in node.symbol_table.argument_list:
197  if isinstance(arg.datatype, DataTypeSymbol):
198  if arg.datatype.name == "r2d_field":
199  field = arg
200  break
201 
202  # Look-up the loop bounds using the first field object in the
203  # list
204  api_config = Config.get().api_conf("gocean1.0")
205  xstop = api_config.grid_properties["go_grid_xstop"].fortran \
206  .format(field)
207  ystop = api_config.grid_properties["go_grid_ystop"].fortran \
208  .format(field)
209 
210  # Add the assignments of the bounds to their variables at the
211  # beginning of the invoke.
212  assign1 = Assignment.create(
213  Reference(i_stop),
214  StructureReference.create(
215  field, xstop.split('%')[1:]))
216  assign1.preceding_comment = "Look-up loop bounds"
217  assign2 = Assignment.create(
218  Reference(j_stop),
219  StructureReference.create(
220  field, ystop.split('%')[1:]))
221  node.children.insert(0, assign1)
222  node.children.insert(1, assign2)
223 
224  # Fortran reader needed to parse constructed bound expressions
225  fortran_reader = FortranReader()
226 
227  # Update all GOLoop bounds with the new variables
228  for loop in node.walk(GOLoop):
229 
230  # Chose the variable depending if it is an inner or outer loop
231  if loop.loop_type == "inner":
232  stop = i_stop.name
233  elif loop.loop_type == "outer":
234  stop = j_stop.name
235  else:
236  raise InternalError(
237  f"Found a loop with loop_type '{loop.loop_type}' but the "
238  f"only expected values are 'inner' or 'outer'.")
239 
240  # Get the bounds map
241  bounds = loop.bounds_lookup[loop.index_offset][loop.field_space][
242  loop.iteration_space][loop.loop_type]
243 
244  # Set the lower bound
245  start_expr = bounds["start"].format(start='2', stop=stop)
246  start_expr = "".join(start_expr.split()) # Remove white spaces
247  # This common case is a bit of compile-time computation
248  # but it helps with fixing all of the test cases.
249  if start_expr == "2-1":
250  start_expr = "1"
251  psyir = fortran_reader.psyir_from_expression(
252  start_expr, node.symbol_table)
253  loop.children[0].replace_with(psyir)
254 
255  # Set the upper bound
256  stop_expr = bounds["stop"].format(start='2', stop=stop)
257  stop_expr = "".join(stop_expr.split()) # Remove white spaces
258  if stop_expr == "2-1":
259  stop_expr = "1"
260  psyir = fortran_reader.psyir_from_expression(
261  stop_expr, node.symbol_table)
262  loop.children[1].replace_with(psyir)

References psyclone.domain.lfric.kernel.lfric_kernel_metadata.LFRicKernelMetadata.validate(), psyclone.transformations.MoveTrans.validate(), psyclone.transformations.Dynamo0p3AsyncHaloExchangeTrans.validate(), psyclone.domain.common.transformations.alg_invoke_2_psy_call_trans.AlgInvoke2PSyCallTrans.validate(), psyclone.domain.common.transformations.alg_trans.AlgTrans.validate(), psyclone.domain.common.transformations.kernel_module_inline_trans.KernelModuleInlineTrans.validate(), psyclone.domain.common.transformations.raise_psyir_2_alg_trans.RaisePSyIR2AlgTrans.validate(), psyclone.domain.gocean.transformations.gocean_const_loop_bounds_trans.GOConstLoopBoundsTrans.validate(), psyclone.domain.gocean.transformations.gocean_move_iteration_boundaries_inside_kernel_trans.GOMoveIterationBoundariesInsideKernelTrans.validate(), psyclone.domain.gocean.transformations.gocean_opencl_trans.GOOpenCLTrans.validate(), psyclone.domain.gocean.transformations.raise_psyir_2_gocean_kern_trans.RaisePSyIR2GOceanKernTrans.validate(), psyclone.domain.lfric.transformations.lfric_alg_invoke_2_psy_call_trans.LFRicAlgInvoke2PSyCallTrans.validate(), psyclone.domain.lfric.transformations.raise_psyir_2_lfric_kern_trans.RaisePSyIR2LFRicKernTrans.validate(), psyclone.domain.nemo.transformations.create_nemo_invoke_schedule_trans.CreateNemoInvokeScheduleTrans.validate(), psyclone.domain.nemo.transformations.create_nemo_psy_trans.CreateNemoPSyTrans.validate(), psyclone.domain.nemo.transformations.nemo_allarrayrange2loop_trans.NemoAllArrayRange2LoopTrans.validate(), psyclone.domain.nemo.transformations.nemo_arrayrange2loop_trans.NemoArrayRange2LoopTrans.validate(), psyclone.domain.nemo.transformations.nemo_outerarrayrange2loop_trans.NemoOuterArrayRange2LoopTrans.validate(), psyclone.psyad.transformations.assignment_trans.AssignmentTrans.validate(), psyclone.psyGen.Transformation.validate(), psyclone.psyir.transformations.acc_update_trans.ACCUpdateTrans.validate(), psyclone.psyir.transformations.allarrayaccess2loop_trans.AllArrayAccess2LoopTrans.validate(), psyclone.psyir.transformations.arrayaccess2loop_trans.ArrayAccess2LoopTrans.validate(), psyclone.psyir.transformations.arrayrange2loop_trans.ArrayRange2LoopTrans.validate(), psyclone.psyir.transformations.chunk_loop_trans.ChunkLoopTrans.validate(), psyclone.psyir.transformations.fold_conditional_return_expressions_trans.FoldConditionalReturnExpressionsTrans.validate(), psyclone.psyir.transformations.hoist_local_arrays_trans.HoistLocalArraysTrans.validate(), psyclone.psyir.transformations.hoist_loop_bound_expr_trans.HoistLoopBoundExprTrans.validate(), psyclone.psyir.transformations.hoist_trans.HoistTrans.validate(), psyclone.psyir.transformations.inline_trans.InlineTrans.validate(), psyclone.psyir.transformations.intrinsics.array_reduction_base_trans.ArrayReductionBaseTrans.validate(), psyclone.psyir.transformations.intrinsics.dotproduct2code_trans.DotProduct2CodeTrans.validate(), psyclone.psyir.transformations.intrinsics.intrinsic2code_trans.Intrinsic2CodeTrans.validate(), psyclone.psyir.transformations.intrinsics.matmul2code_trans.Matmul2CodeTrans.validate(), psyclone.psyir.transformations.loop_swap_trans.LoopSwapTrans.validate(), psyclone.psyir.transformations.loop_tiling_2d_trans.LoopTiling2DTrans.validate(), psyclone.psyir.transformations.loop_trans.LoopTrans.validate(), psyclone.psyir.transformations.omp_task_trans.OMPTaskTrans.validate(), psyclone.psyir.transformations.omp_taskwait_trans.OMPTaskwaitTrans.validate(), psyclone.psyir.transformations.parallel_loop_trans.ParallelLoopTrans.validate(), psyclone.psyir.transformations.reference2arrayrange_trans.Reference2ArrayRangeTrans.validate(), psyclone.psyir.transformations.replace_induction_variables_trans.ReplaceInductionVariablesTrans.validate(), psyclone.transformations.OMPDeclareTargetTrans.validate(), psyclone.transformations.DynamoOMPParallelLoopTrans.validate(), psyclone.transformations.Dynamo0p3OMPLoopTrans.validate(), psyclone.transformations.GOceanOMPLoopTrans.validate(), psyclone.transformations.Dynamo0p3RedundantComputationTrans.validate(), psyclone.transformations.Dynamo0p3KernelConstTrans.validate(), psyclone.transformations.ACCRoutineTrans.validate(), psyclone.transformations.KernelImportsToArguments.validate(), psyclone.domain.gocean.transformations.gocean_loop_fuse_trans.GOceanLoopFuseTrans.validate(), psyclone.domain.lfric.transformations.lfric_loop_fuse_trans.LFRicLoopFuseTrans.validate(), psyclone.psyir.transformations.loop_fuse_trans.LoopFuseTrans.validate(), psyclone.domain.gocean.transformations.gocean_extract_trans.GOceanExtractTrans.validate(), psyclone.domain.lfric.transformations.lfric_extract_trans.LFRicExtractTrans.validate(), psyclone.psyir.transformations.extract_trans.ExtractTrans.validate(), psyclone.psyir.transformations.nan_test_trans.NanTestTrans.validate(), psyclone.psyir.transformations.read_only_verify_trans.ReadOnlyVerifyTrans.validate(), psyclone.transformations.ParallelRegionTrans.validate(), psyclone.transformations.OMPParallelTrans.validate(), psyclone.transformations.ACCParallelTrans.validate(), psyclone.transformations.ACCKernelsTrans.validate(), psyclone.transformations.ACCDataTrans.validate(), psyclone.psyir.transformations.psy_data_trans.PSyDataTrans.validate(), psyclone.psyir.transformations.region_trans.RegionTrans.validate(), and psyclone.transformations.ACCEnterDataTrans.validate().

Here is the call graph for this function:

◆ name()

def psyclone.domain.gocean.transformations.gocean_const_loop_bounds_trans.GOConstLoopBoundsTrans.name (   self)
:returns: the name of the Transformation as a string.
:rtype: str

Reimplemented from psyclone.psyGen.Transformation.

Definition at line 95 of file gocean_const_loop_bounds_trans.py.

95  def name(self):
96  '''
97  :returns: the name of the Transformation as a string.
98  :rtype: str
99 
100  '''
101  return "GOConstLoopBoundsTrans"
102 
Here is the caller graph for this function:

◆ validate()

def psyclone.domain.gocean.transformations.gocean_const_loop_bounds_trans.GOConstLoopBoundsTrans.validate (   self,
  node,
  options = None 
)
Checks if it is valid to apply the GOConstLoopBoundsTrans
transform.

:param node: the GOInvokeSchedule to transform.
:type node: :py:class:`psyclone.gocean1p0.GOInvokeSchedule`
:param options: a dictionary with options for transformations.
:type options: Optional[Dict[str, Any]]

:raises TransformationError: if the supplied node is not a \
    GOInvokeSchedule.
:raises TransformationError: if the supplied schedule has loops with \
    a loop with loop_type different than 'inner' or 'outer'.
:raises TransformationError: if the supplied schedule has loops with \
    attributes for index_offsets, field_space, iteration_space and \
    loop_type that don't appear in the GOLoop.bounds_lookup table.
:raises TransformationError: if the supplied schedule doesn't have a \
    field argument.

Reimplemented from psyclone.psyGen.Transformation.

Definition at line 103 of file gocean_const_loop_bounds_trans.py.

103  def validate(self, node, options=None):
104  '''Checks if it is valid to apply the GOConstLoopBoundsTrans
105  transform.
106 
107  :param node: the GOInvokeSchedule to transform.
108  :type node: :py:class:`psyclone.gocean1p0.GOInvokeSchedule`
109  :param options: a dictionary with options for transformations.
110  :type options: Optional[Dict[str, Any]]
111 
112  :raises TransformationError: if the supplied node is not a \
113  GOInvokeSchedule.
114  :raises TransformationError: if the supplied schedule has loops with \
115  a loop with loop_type different than 'inner' or 'outer'.
116  :raises TransformationError: if the supplied schedule has loops with \
117  attributes for index_offsets, field_space, iteration_space and \
118  loop_type that don't appear in the GOLoop.bounds_lookup table.
119  :raises TransformationError: if the supplied schedule doesn't have a \
120  field argument.
121 
122  '''
123  if not isinstance(node, GOInvokeSchedule):
124  raise TransformationError(
125  f"GOConstLoopBoundsTrans can only be applied to "
126  f"'GOInvokeSchedule' but found '{type(node).__name__}'.")
127 
128  for loop in node.walk(GOLoop):
129  if loop.loop_type not in ["inner", "outer"]:
130  raise TransformationError(
131  f"GOConstLoopBoundsTrans can not transform a loop with "
132  f"loop_type '{loop.loop_type}', only 'inner' or 'outer' "
133  f"loop_type values are expected.")
134 
135  if loop.index_offset not in loop.bounds_lookup:
136  raise TransformationError(
137  f"GOConstLoopBoundsTrans can not transform a loop with "
138  f"index_offset '{loop.index_offset}' because it is not in "
139  f"the bounds lookup table, the available index_offset "
140  f"values are {list(loop.bounds_lookup.keys())}.")
141 
142  table = loop.bounds_lookup[loop.index_offset]
143  if loop.field_space not in table:
144  raise TransformationError(
145  f"GOConstLoopBoundsTrans can not transform a loop with "
146  f"field_space '{loop.field_space}' because it is not in "
147  f"the bounds lookup table, the available field_space "
148  f"values are {list(table.keys())}.")
149 
150  table = table[loop.field_space]
151  if loop.iteration_space not in table:
152  raise TransformationError(
153  f"GOConstLoopBoundsTrans can not transform a loop with "
154  f"iteration_space '{loop.iteration_space}' because it is "
155  f"not in the bounds lookup table, the available "
156  f"iteration_space values are {list(table.keys())}.")
157 
158  table = table[loop.iteration_space]
159  if loop.loop_type not in table:
160  raise TransformationError(
161  f"GOConstLoopBoundsTrans can not transform a loop with "
162  f"loop_type '{loop.loop_type}' because it is not in the "
163  f"bounds lookup table, the available loop_type values are "
164  f"{list(table.keys())}.")
165 
166  # Make sure the Invoke has at least one field argument
167  for arg in node.symbol_table.argument_list:
168  if isinstance(arg.datatype, DataTypeSymbol):
169  if arg.datatype.name == "r2d_field":
170  break
171  else:
172  raise TransformationError(
173  f"GOConstLoopBoundsTrans can not transform invoke "
174  f"'{node.name}' because it does not have any field arguments.")
175 
Here is the caller graph for this function:

The documentation for this class was generated from the following file: