Reference Guide  2.5.0
lfric_arg_descriptor.py
1 # -----------------------------------------------------------------------------
2 # BSD 3-Clause License
3 #
4 # Copyright (c) 2017-2024, Science and Technology Facilities Council.
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are met:
9 #
10 # * Redistributions of source code must retain the above copyright notice, this
11 # list of conditions and the following disclaimer.
12 #
13 # * Redistributions in binary form must reproduce the above copyright notice,
14 # this list of conditions and the following disclaimer in the documentation
15 # and/or other materials provided with the distribution.
16 #
17 # * Neither the name of the copyright holder nor the names of its
18 # contributors may be used to endorse or promote products derived from
19 # this software without specific prior written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 # POSSIBILITY OF SUCH DAMAGE.
33 # -----------------------------------------------------------------------------
34 # Authors R. W. Ford, A. R. Porter, S. Siso and N. Nobre, STFC Daresbury Lab
35 # Modified I. Kavcic and A. Coughtrie, Met Office
36 # Modified by J. Henrichs, Bureau of Meteorology
37 
38 '''
39 This module contains the LFRicArgDescriptor class and related constants
40 and properties.
41 '''
42 
43 # Imports
44 
45 import os
46 
47 from psyclone.configuration import Config
48 from psyclone.core.access_type import AccessType
49 # Importing from psyclone.domain.lfric only creates circular
50 # import with __init__
51 from psyclone.domain.lfric.lfric_constants import LFRicConstants
52 from psyclone.errors import InternalError
53 import psyclone.expression as expr
54 from psyclone.parse.kernel import Descriptor, get_stencil, get_mesh
55 from psyclone.parse.utils import ParseError
56 
57 # API configuration
58 API = "dynamo0.3"
59 
60 
62  '''
63  This class captures the information specified in one of LFRic API argument
64  descriptors (scalars, fields and operators).
65 
66  :param arg_type: LFRic API valid argument type (scalar, \
67  field or operator).
68  :type arg_type: :py:class:`psyclone.expression.FunctionVar` or \
69  :py:class:`psyclone.expression.BinaryOperator`
70  :param str operates_on: value of operates_on from the parsed kernel \
71  metadata (used for validation).
72  :param int metadata_index: position of this argument in the list of \
73  arguments specified in the metadata.
74 
75  :raises ParseError: if a 'meta_arg' entry is not of 'arg_type' type.
76  :raises ParseError: if the first argument of a 'meta_arg' entry is not \
77  one of LFRic API valid argument types.
78  :raises ParseError: if the second argument of a 'meta_arg' entry is not \
79  one of LFRic API valid data types.
80  :raises ParseError: if a 'meta_arg' entry has fewer than 3 args.
81  :raises ParseError: if the third 'meta_arg' entry is not a valid \
82  access descriptor.
83  :raises InternalError: if the operates_on from the parsed kernel \
84  metadata is not 'cell_column' or 'dof'.
85  :raises InternalError: if all the metadata checks fail to catch an \
86  invalid argument type.
87 
88  '''
89  # pylint: disable=too-many-instance-attributes
90 
91  # ----------------------------------------------------------------------- #
92 
93  def __init__(self, arg_type, operates_on, metadata_index):
94  # pylint: disable=too-many-branches, too-many-statements
95  self._arg_type_arg_type = arg_type
96  # Initialise properties
97  self._argument_type_argument_type_argument_type = None
98  self._data_type_data_type = None
99  self._function_space_to_function_space_to = None
100  self._function_space_from_function_space_from = None
101  self._function_space_function_space = None
102  self._function_spaces_function_spaces = []
103  # Set vector size to 1 (scalars set it to 0 in their validation)
104  self._vector_size_vector_size = 1
105  # Initialise other internal arguments
106  self._access_type_access_type = None
107  self._function_space1_function_space1 = None
108  self._function_space2_function_space2 = None
109  self._stencil_stencil_stencil = None
110  self._mesh_mesh_mesh = None
111  self._nargs_nargs = 0
112 
113  # Check for the correct argument type descriptor
114  if arg_type.name != 'arg_type':
115  raise ParseError(
116  f"In the LFRic API each 'meta_arg' entry must be of type "
117  f"'arg_type', but found '{arg_type.name}'.")
118 
119  # Check the first argument descriptor. If it is a binary operator
120  # then it has to be a field vector with an "*n" appended where "*"
121  # is a binary operator and "n > 1" is a vector size. If it is a
122  # variable then it can be one of the other allowed argument types.
123  argtype = None
124  separator = None
125  if isinstance(arg_type.args[0], expr.BinaryOperator):
126  argtype = arg_type.args[0].toks[0]
127  separator = arg_type.args[0].toks[1]
128  else:
129  argtype = arg_type.args[0]
130 
131  const = LFRicConstants()
132  # First check for a valid argument type. It has to be a variable
133  # (FunctionVar expression) and have a valid LFRic API argument name.
134  if isinstance(argtype, expr.FunctionVar) and argtype.name in \
135  const.VALID_ARG_TYPE_NAMES:
136  self._argument_type_argument_type_argument_type = argtype.name
137  else:
138  raise ParseError(
139  f"In the LFRic API the 1st argument of a 'meta_arg' entry "
140  f"should be a valid argument type (one of "
141  f"{const.VALID_ARG_TYPE_NAMES}), but found '{argtype}' in "
142  f"'{arg_type}'.")
143 
144  # Check for a valid vector size in case of a binary
145  # operator expression
146  if separator:
147  self._validate_vector_size_validate_vector_size(separator, arg_type)
148 
149  # Check number of args - we require at least three
150  self._nargs_nargs = len(arg_type.args)
151  min_nargs = 3
152  if self._nargs_nargs < min_nargs:
153  raise ParseError(
154  f"In the LFRic API each 'meta_arg' entry must have at least "
155  f"{min_nargs} args, but found {self._nargs} in '{arg_type}'.")
156 
157  # The 2nd arg is the Fortran primitive type of the argument data
158  dtype = arg_type.args[1].name
159  if dtype in const.VALID_ARG_DATA_TYPES:
160  self._data_type_data_type = dtype
161  else:
162  raise ParseError(
163  f"In the LFRic API the 2nd argument of a 'meta_arg' entry "
164  f"should be a valid data type (one of "
165  f"{const.VALID_ARG_DATA_TYPES}), but found '{dtype}' in "
166  f"'{arg_type}'.")
167 
168  # The 3rd arg is an access descriptor. Allowed accesses for each
169  # argument type are dealt with in the related _init methods.
170  # Convert from GH_* names to the generic access type
171  api_config = Config.get().api_conf(API)
172  access_mapping = api_config.get_access_mapping()
173  prop_ind = 2
174  try:
175  self._access_type_access_type = access_mapping[arg_type.args[prop_ind].name]
176  except KeyError as err:
177  valid_names = api_config.get_valid_accesses_api()
178  raise ParseError(
179  f"In the LFRic API argument {prop_ind+1} of a 'meta_arg' entry"
180  f" must be a valid access descriptor (one of {valid_names}), "
181  f"but found '{arg_type.args[prop_ind].name}' in "
182  f"'{arg_type}'.") from err
183 
184  # Check for the allowed iteration spaces from the parsed kernel
185  # metadata
186  if operates_on not in const.VALID_ITERATION_SPACES:
187  raise InternalError(
188  f"Expected operates_on in the kernel metadata to be one of "
189  f"{const.VALID_ITERATION_SPACES} but got '{operates_on}'.")
190 
191  # FIELD, OPERATOR and SCALAR argument type descriptors and checks
192  if self._argument_type_argument_type_argument_type in const.VALID_FIELD_NAMES:
193  # Validate field arguments
194  self._init_field_init_field(arg_type, operates_on)
195 
196  elif self._argument_type_argument_type_argument_type in const.VALID_OPERATOR_NAMES:
197  # Validate operator arguments
198  self._init_operator_init_operator(arg_type)
199 
200  elif self._argument_type_argument_type_argument_type in const.VALID_SCALAR_NAMES:
201  # Validate scalar arguments
202  self._init_scalar_init_scalar(arg_type)
203 
204  else:
205  # We should never get to here if the checks are tight enough
206  raise InternalError(
207  f"Failed argument validation for the 'meta_arg' entry "
208  f"'{arg_type}', should not get to here.")
209 
210  # Initialise the parent class
211  super().__init__(self._access_type_access_type, self._function_space1_function_space1,
212  metadata_index, stencil=self._stencil_stencil_stencil,
213  mesh=self._mesh_mesh_mesh,
214  argument_type=self._argument_type_argument_type_argument_type)
215 
216  def _validate_vector_size(self, separator, arg_type):
217  '''
218  Validates descriptors for field vector arguments and populates
219  vector properties accordingly.
220 
221  :param str separator: operator in a binary expression.
222  :param arg_type: LFRic API field (vector) argument type.
223  :type arg_type: :py:class:`psyclone.expression.FunctionVar`
224 
225  :raises ParseError: if the field vector notation does not use \
226  the '*' operator.
227  :raises ParseError: if the field vector notation is not in the \
228  correct format '(field*n)' where 'n' is \
229  an integer.
230  :raises ParseError: if the field vector notation is used for the \
231  vector size of less than 2.
232  :raises ParseError: if the field vector notation is used for an \
233  argument that is not a field.
234 
235  '''
236  # Check that the operator is correct
237  if separator != "*":
238  raise ParseError(
239  f"In the LFRic API the 1st argument of a 'meta_arg' "
240  f"entry may be a field vector but if so must use '*' as "
241  f"the separator in the format 'field*n', but found "
242  f"'{separator}' in '{arg_type}'.")
243 
244  # Now try to find the vector size for a field vector and return
245  # an error if it is not an integer number...
246  try:
247  vectsize = int(arg_type.args[0].toks[2])
248  except TypeError as err:
249  raise ParseError(
250  f"In the LFRic API, the field vector notation must be in the "
251  f"format 'field*n' where 'n' is an integer, but the following "
252  f"'{arg_type.args[0].toks[2]}' was found in "
253  f"'{arg_type}'.") from err
254  # ... or it is less than 2 (1 is the default for all fields)...
255 
256  const = LFRicConstants()
257  if vectsize < 2:
258  raise ParseError(
259  f"In the LFRic API the 1st argument of a 'meta_arg' entry may "
260  f"be a field vector with format 'field*n' where n is an "
261  f"integer > 1. However, found n = {vectsize} in '{arg_type}'.")
262  # ... and set the vector size if all checks pass
263  self._vector_size_vector_size = vectsize
264 
265  # Check that no other arguments than fields use vector notation
266  if self._argument_type_argument_type_argument_type not in \
267  const.VALID_FIELD_NAMES and self._vector_size_vector_size:
268  raise ParseError(
269  f"In the LFRic API, vector notation is only supported for "
270  f"{const.VALID_FIELD_NAMES} argument types but found "
271  f"'{arg_type.args[0]}'.")
272 
273  def _init_field(self, arg_type, operates_on):
274  '''
275  Validates metadata descriptors for field arguments and
276  initialises field argument properties accordingly.
277 
278  :param arg_type: LFRic API field (vector) argument type.
279  :type arg_type: :py:class:`psyclone.expression.FunctionVar`
280  :param operates_on: value of operates_on from the parsed kernel \
281  metadata (used for validation).
282  :type operates_on: str
283 
284  :raises InternalError: if argument type other than a field is \
285  passed in.
286  :raises ParseError: if there are fewer than 4 metadata arguments.
287  :raises ParseError: if there are more than 5 metadata arguments.
288  :raises ParseError: if a field argument has an invalid data type.
289  :raises ParseError: if the 4th argument is not a valid function space.
290  :raises ParseError: if the optional 5th argument is not a stencil \
291  specification or a mesh identifier (for \
292  inter-grid kernels).
293  :raises ParseError: if a field passed to a kernel that operates on \
294  DoFs does not have a valid access \
295  (one of [READ, WRITE, READWRITE]).
296  :raises ParseError: if a field on a discontinuous function space \
297  passed to a kernel that operates on cell-columns \
298  does not have a valid access (one of \
299  [READ, WRITE, READWRITE]).
300  :raises ParseError: if a field on a continuous function space \
301  passed to a kernel that operates on cell-columns \
302  does not have a valid access (one of [READ, WRITE,\
303  INC, READINC]).
304  :raises ParseError: if the kernel operates on the domain and is \
305  passed a field on a continuous space.
306  :raises InternalError: if an invalid value for operates_on is \
307  passed in.
308  :raises ParseError: if a field with a stencil access is not read-only.
309  :raises ParseError: if a field with a stencil access is passed to a \
310  kernel that operates on the domain.
311 
312  '''
313  # pylint: disable=too-many-locals, too-many-statements
314  # pylint: disable=too-many-branches
315  const = LFRicConstants()
316 
317  # Check whether something other than a field is passed in
318  if self._argument_type_argument_type_argument_type not in const.VALID_FIELD_NAMES:
319  raise InternalError(
320  f"Expected a field argument but got an argument of type "
321  f"'{arg_type.args[0]}'.")
322 
323  # There must be at least 4 arguments
324  nargs_field_min = 4
325  if self._nargs_nargs < nargs_field_min:
326  raise ParseError(
327  "In the LFRic API each 'meta_arg' entry must have at least "
328  f"{nargs_field_min} arguments if its first argument is of "
329  f"{const.VALID_FIELD_NAMES} type, but found {self._nargs} in "
330  f"'{arg_type}'.")
331  # There must be at most 5 arguments
332  nargs_field_max = 5
333  if self._nargs_nargs > nargs_field_max:
334  raise ParseError(
335  f"In the LFRic API each 'meta_arg' entry must have at most "
336  f"{nargs_field_max} arguments if its first argument is of "
337  f"{const.VALID_FIELD_NAMES} type, but found {self._nargs} in "
338  f"'{arg_type}'.")
339 
340  # Check whether an invalid data type for a field argument is passed in.
341  if self._data_type_data_type not in const.VALID_FIELD_DATA_TYPES:
342  raise ParseError(
343  f"In the LFRic API the allowed data types for field arguments "
344  f"are one of {const.VALID_FIELD_DATA_TYPES}, but found "
345  f"'{self._data_type}' in '{arg_type}'.")
346 
347  # The 4th argument must be a valid function-space name
348  prop_ind = 3
349  if arg_type.args[prop_ind].name not in \
350  const.VALID_FUNCTION_SPACE_NAMES:
351  raise ParseError(
352  f"In the LFRic API argument {prop_ind+1} of a 'meta_arg' "
353  f"field entry must be a valid function-space name (one of "
354  f"{const.VALID_FUNCTION_SPACE_NAMES}) if its first argument "
355  f"is of {const.VALID_FIELD_NAMES} type, but found "
356  f"'{arg_type.args[prop_ind].name}' in '{arg_type}'.")
357  self._function_space1_function_space1 = arg_type.args[prop_ind].name
358 
359  # The optional 5th argument is either a stencil specification
360  # or a mesh identifier (for inter-grid kernels)
361  prop_ind = 4
362  if self._nargs_nargs == nargs_field_max:
363  try:
364  if "stencil" in str(arg_type.args[prop_ind]):
365  self._stencil_stencil_stencil = get_stencil(
366  arg_type.args[prop_ind],
367  const.VALID_STENCIL_TYPES)
368  elif "mesh" in str(arg_type.args[prop_ind]):
369  self._mesh_mesh_mesh = get_mesh(arg_type.args[prop_ind],
370  const.VALID_MESH_TYPES)
371  else:
372  raise ParseError("Unrecognised metadata entry")
373  except ParseError as err:
374  raise ParseError(
375  f"In the LFRic API argument {prop_ind+1} of a 'meta_arg' "
376  f"field entry must be either a valid stencil specification"
377  f" or a mesh identifier (for inter-grid kernels). However,"
378  f" entry '{arg_type}' raised the following error: "
379  f"{err}.") from err
380 
381  # Test allowed accesses for fields
382  field_disc_accesses = [AccessType.READ, AccessType.WRITE,
383  AccessType.READWRITE]
384  # Note that although WRITE is permitted for fields on continuous
385  # function spaces, kernels that specify this must guarantee to write
386  # the same value to any given shared entity, independent of iteration.
387  field_cont_accesses = [AccessType.READ, AccessType.WRITE,
388  AccessType.INC, AccessType.READINC]
389  # Convert generic access types to GH_* names for error messages
390  api_config = Config.get().api_conf(API)
391  rev_access_mapping = api_config.get_reverse_access_mapping()
392  # Create a list of allowed accesses for use in error messages
393  fld_disc_acc_msg = [rev_access_mapping[acc] for acc in
394  field_disc_accesses]
395  fld_cont_acc_msg = [rev_access_mapping[acc] for acc in
396  field_cont_accesses]
397  # Joint lists of valid function spaces for continuous fields
398  fld_cont_spaces = (const.CONTINUOUS_FUNCTION_SPACES +
399  const.VALID_ANY_SPACE_NAMES)
400 
401  # Check accesses for kernels that operate on DoFs
402  if operates_on == "dof":
403  if self._access_type_access_type not in field_disc_accesses:
404  raise ParseError(
405  f"In the LFRic API, allowed field accesses for a kernel "
406  f"that operates on DoFs are {fld_disc_acc_msg}, but found "
407  f"'{rev_access_mapping[self._access_type]}' for "
408  f"'{self._function_space1.lower()}' in '{arg_type}'.")
409  # Check accesses for kernels that operate on cell-columns or the
410  # domain
411  elif operates_on in ["cell_column", "domain"]:
412  # Fields on discontinuous function spaces
413  if (self._function_space1_function_space1.lower() in
414  const.VALID_DISCONTINUOUS_NAMES and
415  self._access_type_access_type not in field_disc_accesses):
416  raise ParseError(
417  f"In the LFRic API, allowed accesses for fields on "
418  f"discontinuous function spaces that are arguments to "
419  f"kernels that operate on either cell-columns or the "
420  f"domain are {fld_disc_acc_msg}, but found "
421  f"'{rev_access_mapping[self._access_type]}' for "
422  f"'{self._function_space1.lower()}' in '{arg_type}'.")
423  # Fields on continuous function spaces
424  if self._function_space1_function_space1.lower() in fld_cont_spaces:
425  if operates_on == "domain":
426  raise ParseError(
427  f"In the LFRic API, kernels that operate on the domain"
428  f" only accept field arguments on discontinuous "
429  f"function spaces but found "
430  f"'{self._function_space1.lower()}' in '{arg_type}'")
431 
432  if self._access_type_access_type not in field_cont_accesses:
433  raise ParseError(
434  f"In the LFRic API, allowed accesses for fields on "
435  f"continuous function spaces that are arguments to "
436  f"kernels that operate on cell-columns are "
437  f"{fld_cont_acc_msg}, but found "
438  f"'{rev_access_mapping[self._access_type]}' for "
439  f"'{self._function_space1.lower()}' in '{arg_type}'.")
440  # Raise an InternalError for an invalid value of operates-on
441  else:
442  raise InternalError(
443  f"Invalid operates_on '{operates_on}' in the kernel metadata "
444  f"(expected one of {const.VALID_ITERATION_SPACES}).")
445 
446  # Test allowed accesses for fields that have stencil specification
447  if self._stencil_stencil_stencil:
448  if self._access_type_access_type != AccessType.READ:
449  raise ParseError(
450  f"In the LFRic API a field with a stencil access must be "
451  f"read-only ('{rev_access_mapping[AccessType.READ]}'), "
452  f"but found '{rev_access_mapping[self._access_type]}' in "
453  f"'{arg_type}'.")
454  if operates_on == "domain":
455  raise ParseError(
456  f"In the LFRic API, kernels that operate on the domain "
457  f"are not permitted to have arguments with a stencil "
458  f"access but found: '{arg_type}'")
459 
460  def _init_operator(self, arg_type):
461  '''
462  Validates metadata descriptors for operator arguments and
463  initialises operator argument properties accordingly.
464 
465  :param arg_type: LFRic API operator argument type.
466  :type arg_type: :py:class:`psyclone.expression.FunctionVar`
467 
468  :raises InternalError: if argument type other than an operator is \
469  passed in.
470  :raises ParseError: if there are not exactly 5 metadata arguments.
471  :raises ParseError: if an operator argument has an invalid data type.
472  :raises ParseError: if the function space to- is not one of the \
473  valid function spaces.
474  :raises ParseError: if the function space from- is not one of the \
475  valid function spaces.
476  :raises ParseError: if the operator argument has an invalid access.
477 
478  '''
479  const = LFRicConstants()
480  # Check whether something other than an operator is passed in
481  if self._argument_type_argument_type_argument_type not in const.VALID_OPERATOR_NAMES:
482  raise InternalError(
483  f"Expected an operator argument but got an argument of type "
484  f"'{self._argument_type}'.")
485 
486  # We expect 5 arguments with the 4th and 5th each being a
487  # function space
488  nargs_operator = 5
489  if self._nargs_nargs != nargs_operator:
490  raise ParseError(
491  f"In the LFRic API each 'meta_arg' entry must have "
492  f"{nargs_operator} arguments if its first argument is an "
493  f"operator (one of {const.VALID_OPERATOR_NAMES}), but found "
494  f"{self._nargs} in '{arg_type}'.")
495 
496  # Check whether an invalid data type for an operator argument is passed
497  # in. The only valid data type for operators in LFRic API is "gh_real".
498  if self._data_type_data_type not in const.VALID_OPERATOR_DATA_TYPES:
499  raise ParseError(
500  f"In the LFRic API the allowed data types for operator "
501  f"arguments are one of {const.VALID_OPERATOR_DATA_TYPES}, but "
502  f"found '{self._data_type}' in '{arg_type}'.")
503 
504  # Operator arguments need to have valid to- and from- function spaces
505  # Check for a valid to- function space
506  prop_ind = 3
507  if arg_type.args[prop_ind].name not in \
508  const.VALID_FUNCTION_SPACE_NAMES:
509  raise ParseError(
510  f"In the LFRic API argument {prop_ind+1} of a 'meta_arg' "
511  f"operator entry must be a valid function-space name (one of "
512  f"{const.VALID_FUNCTION_SPACE_NAMES}), but found "
513  f"'{arg_type.args[prop_ind].name}' in '{arg_type}'.")
514  self._function_space1_function_space1 = arg_type.args[prop_ind].name
515  # Check for a valid from- function space
516  prop_ind = 4
517  if arg_type.args[prop_ind].name not in \
518  const.VALID_FUNCTION_SPACE_NAMES:
519  raise ParseError(
520  f"In the LFRic API argument {prop_ind+1} of a 'meta_arg' "
521  f"operator entry must be a valid function-space name (one of "
522  f"{const.VALID_FUNCTION_SPACE_NAMES}), but found "
523  f"'{arg_type.args[prop_ind].name}' in '{arg_type}'.")
524  self._function_space2_function_space2 = arg_type.args[prop_ind].name
525 
526  # Test allowed accesses for operators
527  operator_accesses = [AccessType.READ, AccessType.WRITE,
528  AccessType.READWRITE]
529  # Convert generic access types to GH_* names for error messages
530  api_config = Config.get().api_conf(API)
531  rev_access_mapping = api_config.get_reverse_access_mapping()
532  op_acc_msg = [rev_access_mapping[acc] for acc in operator_accesses]
533  if self._access_type_access_type not in operator_accesses:
534  raise ParseError(
535  f"In the LFRic API, allowed accesses for operators are "
536  f"{op_acc_msg} because they behave as discontinuous "
537  f"quantities, but found "
538  f"'{rev_access_mapping[self._access_type]}' in '{arg_type}'.")
539 
540  def _init_scalar(self, arg_type):
541  '''
542  Validates metadata descriptors for scalar arguments and
543  initialises scalar argument properties accordingly.
544 
545  :param arg_type: LFRic API scalar argument type.
546  :type arg_type: :py:class:`psyclone.expression.FunctionVar`
547 
548  :raises InternalError: if argument type other than a scalar is \
549  passed in.
550  :raises ParseError: if there are not exactly 3 metadata arguments.
551  :raises InternalError: if a scalar argument has an invalid data type.
552  :raises ParseError: if scalar arguments do not have a read-only or
553  a reduction access.
554  :raises ParseError: if a scalar argument that is not a real \
555  scalar has a reduction access.
556 
557  '''
558  const = LFRicConstants()
559  # Check whether something other than a scalar is passed in
560  if self._argument_type_argument_type_argument_type not in const.VALID_SCALAR_NAMES:
561  raise InternalError(
562  f"Expected a scalar argument but got an argument of type "
563  f"'{arg_type.args[0]}'.")
564 
565  # There must be 3 argument descriptors to describe a scalar.
566  nargs_scalar = 3
567  if self._nargs_nargs != nargs_scalar:
568  raise ParseError(
569  f"In the LFRic API each 'meta_arg' entry must have "
570  f"{nargs_scalar} arguments if its first argument is "
571  f"'gh_scalar', but found {self._nargs} in '{arg_type}'.")
572 
573  # Check whether an invalid data type for a scalar argument is passed
574  # in. Valid data types for scalars are valid data types in LFRic API.
575  if self._data_type_data_type not in const.VALID_SCALAR_DATA_TYPES:
576  raise InternalError(
577  f"Expected one of {const.VALID_SCALAR_DATA_TYPES} as the "
578  f"scalar data type but got '{self._data_type}'.")
579 
580  # Test allowed accesses for scalars (read_only or reduction)
581  scalar_accesses = [AccessType.READ] + \
582  AccessType.get_valid_reduction_modes()
583  # Convert generic access types to GH_* names for error messages
584  api_config = Config.get().api_conf(API)
585  rev_access_mapping = api_config.get_reverse_access_mapping()
586  if self._access_type_access_type not in scalar_accesses:
587  api_specific_name = rev_access_mapping[self._access_type_access_type]
588  valid_reductions = AccessType.get_valid_reduction_names()
589  raise ParseError(
590  f"In the LFRic API scalar arguments must have read-only "
591  f"('gh_read') or a reduction {valid_reductions} access but "
592  f"found '{api_specific_name}' in '{arg_type}'.")
593  # Reduction access is currently only valid for real scalar arguments
594  if self._data_type_data_type != "gh_real" and self._access_type_access_type in \
595  AccessType.get_valid_reduction_modes():
596  raise ParseError(
597  f"In the LFRic API a reduction access "
598  f"'{self._access_type.api_specific_name()}' is only valid "
599  f"with a real scalar argument, but a scalar argument with "
600  f"'{self._data_type}' data type was found in '{arg_type}'.")
601 
602  # Scalars don't have vector size
603  self._vector_size_vector_size = 0
604 
605  @property
606  def data_type(self):
607  '''
608  :returns: intrinsic Fortran (primitive) type of the argument data.
609  :rtype: str
610 
611  '''
612  return self._data_type_data_type
613 
614  @property
615  def function_space_to(self):
616  '''
617  Returns the "to" function space for an operator. This is
618  the first function space specified in the metadata.
619 
620  :returns: "to" function space for an operator.
621  :rtype: str
622 
623  :raises InternalError: if this is not an operator.
624 
625  '''
626  const = LFRicConstants()
627  if self._argument_type_argument_type_argument_type in const.VALID_OPERATOR_NAMES:
628  return self._function_space1_function_space1
629  raise InternalError(
630  f"In the LFRic API 'function_space_to' only makes sense for one "
631  f"of {const.VALID_OPERATOR_NAMES}, but this is a "
632  f"'{self._argument_type}'.")
633 
634  @property
636  '''
637  Returns the "from" function space for an operator. This is
638  the second function space specified in the metadata.
639 
640  :returns: "from" function space for an operator.
641  :rtype: str
642 
643  :raises InternalError: if this is not an operator.
644 
645  '''
646  const = LFRicConstants()
647  if self._argument_type_argument_type_argument_type in const.VALID_OPERATOR_NAMES:
648  return self._function_space2_function_space2
649  raise InternalError(
650  f"In the LFRic API 'function_space_from' only makes sense for one "
651  f"of {const.VALID_OPERATOR_NAMES}, but this is a "
652  f"'{self._argument_type}'.")
653 
654  @property
655  def function_space(self):
656  '''
657  Returns the function space name related to this kernel argument
658  depending on the argument type: a single function space for a field,
659  function_space_from for an operator and nothing for a scalar.
660 
661  :returns: function space relating to this kernel argument or \
662  None (for a scalar).
663  :rtype: str or NoneType
664 
665  :raises InternalError: if an invalid argument type is passed in.
666 
667  '''
668  const = LFRicConstants()
669  if self._argument_type_argument_type_argument_type in const.VALID_FIELD_NAMES:
670  return self._function_space1_function_space1
671  if self._argument_type_argument_type_argument_type in const.VALID_OPERATOR_NAMES:
672  return self._function_space2_function_space2
673  if self._argument_type_argument_type_argument_type in const.VALID_SCALAR_NAMES:
674  return None
675  raise InternalError(f"Expected a valid argument type but got "
676  f"'{self._argument_type}'.")
677 
678  @property
679  def function_spaces(self):
680  '''
681  Returns the function space names related to this kernel argument
682  as a list depending on the argument type: one function space for
683  a field, both function spaces ("to"- and then "from"-) for an
684  operator and an empty list for a scalar.
685 
686  :returns: function space names related to this kernel argument.
687  :rtype: list of str
688 
689  :raises InternalError: if an invalid argument type is passed in.
690 
691  '''
692  const = LFRicConstants()
693  if self._argument_type_argument_type_argument_type in const.VALID_FIELD_NAMES:
694  return [self.function_spacefunction_spacefunction_space]
695  if self._argument_type_argument_type_argument_type in const.VALID_OPERATOR_NAMES:
696  # Return to before from to maintain expected ordering
697  return [self.function_space_tofunction_space_to, self.function_space_fromfunction_space_from]
698  if self._argument_type_argument_type_argument_type in const.VALID_SCALAR_NAMES:
699  return []
700  raise InternalError(f"Expected a valid argument type but got "
701  f"'{self._argument_type}'.")
702 
703  @property
704  def vector_size(self):
705  '''
706  Returns the vector size of the argument. This will be 1 if ``*n``
707  has not been specified for all argument types except scalars
708  (their vector size is set to 0).
709 
710  :returns: vector size of the argument.
711  :rtype: int
712 
713  '''
714  return self._vector_size_vector_size
715 
716  def __str__(self):
717  '''
718  Creates a string representation of the argument descriptor. This
719  is type and access for scalars with the addition of function
720  space(s) for fields and operators.
721 
722  :returns: string representation of the argument descriptor.
723  :rtype: str
724 
725  :raises InternalError: if an invalid argument type is passed in.
726 
727  '''
728  const = LFRicConstants()
729  res = "LFRicArgDescriptor object" + os.linesep
730  res += f" argument_type[0]='{self._argument_type}'"
731  if self._vector_size_vector_size > 1:
732  res += "*" + str(self._vector_size_vector_size)
733  res += os.linesep
734  res += f" data_type[1]='{self._data_type}'" + os.linesep
735  res += (f" access_descriptor[2]="
736  f"'{self._access_type.api_specific_name()}'"
737  + os.linesep)
738  if self._argument_type_argument_type_argument_type in const.VALID_FIELD_NAMES:
739  res += (f" function_space[3]='{self._function_space1}'"
740  + os.linesep)
741  elif self._argument_type_argument_type_argument_type in const.VALID_OPERATOR_NAMES:
742  res += (f" function_space_to[3]='{self._function_space1}'"
743  + os.linesep)
744  res += (f" function_space_from[4]='{self._function_space2}'"
745  + os.linesep)
746  elif self._argument_type_argument_type_argument_type in const.VALID_SCALAR_NAMES:
747  pass # We have nothing to add if we're a scalar
748  else: # We should never get to here
749  raise InternalError(f"Expected a valid argument type but got "
750  f"'{self._argument_type}'.")
751  return res
752 
753 
754 # Documentation utils: The list of module members that we wish AutoAPI to
755 # generate documentation for (see https://psyclone-ref.readthedocs.io).
756 __all__ = [
757  'LFRicArgDescriptor']