Reference Guide  2.5.0
lfric_kern_metadata.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 and S. Siso, STFC Daresbury Lab
35 # Modified I. Kavcic, A. Coughtrie, L. Turner and O. Brunt, Met Office
36 # Modified J. Henrichs, Bureau of Meteorology
37 # Modified A. B. G. Chalk and N. Nobre, STFC Daresbury Lab
38 
39 ''' This module implements the PSyclone LFRic API by capturing the Kernel
40 subroutine code and metadata describing the subroutine for the LFRic API.'''
41 
42 from collections import OrderedDict
43 import fparser
44 
45 from psyclone import psyGen
46 from psyclone.core import AccessType
47 from psyclone.domain.lfric.lfric_builtins import BUILTIN_MAP
48 from psyclone.domain.lfric import LFRicArgDescriptor, LFRicConstants
49 from psyclone.dynamo0p3 import (DynFuncDescriptor03, MeshPropertiesMetaData,
50  RefElementMetaData)
51 from psyclone.errors import InternalError
52 from psyclone.parse.kernel import getkerneldescriptors, KernelType
53 from psyclone.parse.utils import ParseError
54 
55 
57  '''
58  Captures the Kernel subroutine code and metadata describing
59  the subroutine for the LFRic API.
60 
61  :param ast: fparser1 AST for the kernel.
62  :type ast: :py:class:`fparser.block_statements.BeginSource`
63  :param str name: the name of this kernel.
64 
65  :raises ParseError: if the metadata does not conform to the
66  rules for the LFRic API.
67  '''
68  # pylint: disable=too-many-instance-attributes
69  def __init__(self, ast, name=None):
70  # pylint: disable=too-many-branches, too-many-locals
71 
72  KernelType.__init__(self, ast, name=name)
73 
74  # The type of CMA operation this kernel performs (or None if
75  # no CMA operators are involved)
76  self._cma_operation_cma_operation = None
77 
78  # Query the metadata for the evaluator shape(s) (only required if
79  # kernel uses quadrature or an evaluator). If it is not
80  # present then 'eval_shapes' will be an empty list.
81  shape = self.get_integer_variableget_integer_variable('gh_shape')
82  if not shape:
83  # There's no scalar 'gh_shape' - is it present as an array?
84  self._eval_shapes_eval_shapes = self.get_integer_arrayget_integer_array('gh_shape')
85  else:
86  self._eval_shapes_eval_shapes = [shape]
87 
88  # The list of function space names for which an evaluator is
89  # required. We set this up below once we have processed the
90  # metadata describing the kernel arguments.
91  self._eval_targets_eval_targets = []
92 
93  # Whether or not this is an inter-grid kernel (i.e. has a mesh
94  # specified for each [field] argument). This property is
95  # set to True if all the checks in '_validate_inter_grid()' pass.
96  self._is_intergrid_is_intergrid = False
97 
98  # Parse the 'arg_type' metadata
99  self._arg_descriptors_arg_descriptors_arg_descriptors = []
100  for idx, arg_type in enumerate(self._inits_inits):
101  self._arg_descriptors_arg_descriptors_arg_descriptors.append(
102  LFRicArgDescriptor(arg_type, self.iterates_overiterates_over, idx))
103 
104  # Get a list of the Type declarations in the metadata
105  type_declns = [cline for cline in self._ktype_ktype.content if
106  isinstance(cline, fparser.one.typedecl_statements.Type)]
107 
108  # Parse the 'func_type' metadata if it exists
109  func_types = []
110  for line in type_declns:
111  for entry in line.selector:
112  if entry == "func_type":
113  func_types = getkerneldescriptors(
114  name, line, var_name="meta_funcs",
115  var_type="func_type")
116  break
117 
118  self._func_descriptors_func_descriptors = []
119  # Populate a list of function descriptor objects which we
120  # return via the 'func_descriptors' method.
121  arg_fs_names = []
122  for descriptor in self._arg_descriptors_arg_descriptors_arg_descriptors:
123  arg_fs_names.extend(descriptor.function_spaces)
124  used_fs_names = []
125  need_evaluator = False
126  for func_type in func_types:
127  descriptor = DynFuncDescriptor03(func_type)
128  fs_name = descriptor.function_space_name
129  # Check that function space names in 'meta_funcs' are specified in
130  # 'meta_args'.
131  if fs_name not in arg_fs_names:
132  raise ParseError(
133  f"In the LFRic API all function spaces specified in "
134  f"'meta_funcs' must exist in 'meta_args', but '{fs_name}' "
135  f"breaks this rule in ...\n'{self._ktype.content}'.")
136  if fs_name not in used_fs_names:
137  used_fs_names.append(fs_name)
138  else:
139  raise ParseError(
140  f"In the LFRic API function spaces specified in "
141  f"'meta_funcs' must be unique, but '{fs_name}' is "
142  f"replicated.")
143 
144  const = LFRicConstants()
145  # Check that a valid shape has been specified if
146  # this function space requires a basis or differential basis
147  for op_name in descriptor.operator_names:
148  if op_name in const.VALID_EVALUATOR_NAMES:
149  need_evaluator = True
150  if not self._eval_shapes_eval_shapes:
151  raise ParseError(
152  f"In the LFRic API any kernel requiring "
153  f"quadrature or an evaluator "
154  f"({const.VALID_EVALUATOR_NAMES}) must also "
155  f"supply the shape of that evaluator by setting "
156  f"'gh_shape' in the kernel metadata but "
157  f"this is missing for kernel '{self.name}'")
158  shape_set = set(self._eval_shapes_eval_shapes)
159  if not shape_set.issubset(
160  set(const.VALID_EVALUATOR_SHAPES)):
161  raise ParseError(
162  f"In the LFRic API a kernel requiring either "
163  f"quadrature or an evaluator must request one or "
164  f"more valid 'gh_shapes' (one of "
165  f"{const.VALID_EVALUATOR_SHAPES}) but got "
166  f"'{self._eval_shapes}' for kernel '{self.name}'")
167 
168  self._func_descriptors_func_descriptors.append(descriptor)
169 
170  # Check to see whether the optional 'gh_evaluator_targets'
171  # has been supplied. This lists the function spaces for which
172  # any evaluators (gh_shape=gh_evaluator) should be provided.
173  _targets = self.get_integer_arrayget_integer_array('gh_evaluator_targets')
174  if not _targets and \
175  self._eval_shapes_eval_shapes and "gh_evaluator" in self._eval_shapes_eval_shapes:
176  # Use the FS of the kernel arguments that are updated
177  write_accesses = AccessType.all_write_accesses()
178  write_args = psyGen.args_filter(self._arg_descriptors_arg_descriptors_arg_descriptors,
179  arg_accesses=write_accesses)
180  # We want the 'to' space of any operator arguments so get
181  # the first FS associated with the kernel argument.
182  _targets = [arg.function_spaces[0] for arg in write_args]
183  # Ensure that '_eval_targets' entries are not duplicated
184  for target in _targets:
185  if target not in self._eval_targets_eval_targets:
186  self._eval_targets_eval_targets.append(target)
187 
188  # Does this kernel require any properties of the reference element?
189  self.reference_elementreference_element = RefElementMetaData(self.namename, type_declns)
190 
191  # Does this kernel require any properties of the mesh?
192  self.meshmesh = MeshPropertiesMetaData(self.namename, type_declns)
193 
194  # Perform further checks that the metadata we've parsed
195  # conforms to the rules for this API
196  self._validate_validate(need_evaluator)
197 
198  def _validate(self, need_evaluator):
199  '''
200  Check that the metadata conforms to LFRic rules for a user-provided
201  kernel or a built-in.
202 
203  :param bool need_evaluator: whether this kernel requires an
204  evaluator/quadrature.
205  :raises ParseError: if the kernel metadata specifies writing to the
206  read-only function space.
207  :raises ParseError: if a user-supplied LFRic kernel updates/writes
208  to a scalar argument.
209  :raises ParseError: if a kernel does not have at least one argument
210  that is updated/written to.
211  :raises ParseError: if a kernel does not require basis or
212  differential basis functions but specifies one
213  or more gh_shapes.
214  :raises ParseError: if a kernel does not require basis or
215  differential basis functions but specifies
216  gh_evaluator_targets.
217  :raises ParseError: if a kernel specifies gh_evaluator_targets
218  but does not need an evaluator.
219  :raises ParseError: if a kernel requires an evaluator on a
220  specific function space but does not have an
221  argument on that space.
222  :raises ParseError: if a kernel that has LMA operator arguments
223  also has a field argument with an invalid
224  data type (other than 'gh_real').
225 
226  '''
227  # pylint: disable=too-many-branches
228  # We must have at least one argument that is written to
229  const = LFRicConstants()
230  write_count = 0
231  for arg in self._arg_descriptors_arg_descriptors_arg_descriptors:
232  if arg.access != AccessType.READ:
233  write_count += 1
234  # We must not write to a field on a read-only function space
235  if arg.argument_type in const.VALID_FIELD_NAMES \
236  and arg.function_spaces[0] in \
237  const.READ_ONLY_FUNCTION_SPACES:
238  raise ParseError(
239  f"Found kernel metadata in '{self.name}' that "
240  f"specifies writing to the read-only function space "
241  f"'{arg.function_spaces[0]}'.")
242 
243  # We must not write to scalar arguments if it's not a
244  # built-in
245  if self.namename not in BUILTIN_MAP and \
246  arg.argument_type in const.VALID_SCALAR_NAMES:
247  raise ParseError(
248  f"A user-supplied LFRic kernel must not write/update "
249  f"a scalar argument but kernel '{self.name}' has a "
250  f"scalar argument with "
251  f"'{arg.access.api_specific_name()}' access.")
252  if write_count == 0:
253  raise ParseError(f"An LFRic kernel must have at least one "
254  f"argument that is updated (written to) but "
255  f"found none for kernel '{self.name}'.")
256 
257  # Check that no shape has been supplied if no basis or
258  # differential basis functions are required for the kernel
259  if not need_evaluator and self._eval_shapes_eval_shapes:
260  raise ParseError(
261  f"Kernel '{self.name}' specifies one or more 'gh_shapes' "
262  f"({self._eval_shapes}) but does not need an evaluator because"
263  f" no basis or differential basis functions are required")
264  # Check that 'gh_evaluator_targets' is only present if required
265  if self._eval_targets_eval_targets:
266  if not need_evaluator:
267  raise ParseError(
268  f"Kernel '{self.name}' specifies 'gh_evaluator_targets' "
269  f"({self._eval_targets}) but does not need an evaluator "
270  f"because no basis or differential basis functions are "
271  f"required")
272  if "gh_evaluator" not in self._eval_shapes_eval_shapes:
273  raise ParseError(
274  f"Kernel '{self.name}' specifies 'gh_evaluator_targets' "
275  f"({self._eval_targets}) but does not need an evaluator "
276  f"because gh_shape={self._eval_shapes}")
277  # Check that there is a kernel argument on each of the
278  # specified spaces...
279  # Create a list (set) of the function spaces associated with
280  # the kernel arguments
281  fs_list = set()
282  for arg in self._arg_descriptors_arg_descriptors_arg_descriptors:
283  fs_list.update(arg.function_spaces)
284  # Check each evaluator_target against this list
285  for eval_fs in self._eval_targets_eval_targets:
286  if eval_fs not in fs_list:
287  raise ParseError(
288  f"Kernel '{self.name}' specifies that an evaluator is "
289  f"required on '{eval_fs}' but does not have an "
290  f"argument on this space.")
291 
292  # If we have an LMA operator as argument then only field arguments
293  # with 'gh_real' data type are permitted
294  lma_ops = psyGen.args_filter(self._arg_descriptors_arg_descriptors_arg_descriptors,
295  arg_types=["gh_operator"])
296  if lma_ops:
297  for arg in self._arg_descriptors_arg_descriptors_arg_descriptors:
298  if (arg.argument_type in const.VALID_FIELD_NAMES
299  and arg.data_type != "gh_real"):
300  raise ParseError(
301  f"In the LFRic API a kernel that has an LMA operator "
302  f"argument must only have field arguments with "
303  f"'gh_real' data type but kernel '{self.name}' has a "
304  f"field argument with '{arg.data_type}' data type.")
305 
306  # If we have a columnwise operator as argument then we need to
307  # identify the operation that this kernel performs (one of
308  # assemble, apply/apply-inverse and matrix-matrix)
309  cwise_ops = psyGen.args_filter(self._arg_descriptors_arg_descriptors_arg_descriptors,
310  arg_types=["gh_columnwise_operator"])
311  if cwise_ops:
312  self._cma_operation_cma_operation = self._identify_cma_op_identify_cma_op(cwise_ops)
313 
314  # Perform checks for inter-grid kernels
315  self._validate_inter_grid_validate_inter_grid()
316 
317  # Perform checks for a kernel with operates_on == domain
318  self._validate_operates_on_domain_validate_operates_on_domain(need_evaluator)
319 
320  def _validate_inter_grid(self):
321  '''
322  Checks that the kernel metadata obeys the rules for LFRic inter-grid
323  kernels. If none of the kernel arguments has a mesh associated with it
324  then it is not an inter-grid kernel and this routine silently returns.
325 
326  :raises: ParseError: if metadata breaks inter-grid rules.
327  '''
328  # pylint: disable=too-many-branches
329  # Dictionary of meshes associated with arguments (for inter-grid
330  # kernels). Keys are the meshes, values are lists of function spaces
331  # of the corresponding field arguments.
332  mesh_dict = OrderedDict()
333  # Whether or not any field args are missing the 'mesh_arg' specifier
334  missing_mesh = False
335  # If this is an inter-grid kernel then it must only have field
336  # arguments. Keep a record of any non-field arguments for the benefit
337  # of a verbose error message.
338  non_field_arg_types = set()
339  const = LFRicConstants()
340  for arg in self._arg_descriptors_arg_descriptors_arg_descriptors:
341  # Collect info so that we can check inter-grid kernels
342  if arg.argument_type in const.VALID_FIELD_NAMES:
343  if arg.mesh:
344  # Argument has a mesh associated with it so this must
345  # be an inter-grid kernel
346  if arg.mesh in mesh_dict:
347  mesh_dict[arg.mesh].append(arg.function_space)
348  else:
349  mesh_dict[arg.mesh] = [arg.function_space]
350  else:
351  # Record the fact that we have a field without a
352  # mesh specifier (in case this is an inter-grid kernel)
353  missing_mesh = True
354  else:
355  # Inter-grid kernels are only permitted to have field args
356  # so collect a list of other types
357  non_field_arg_types.add(arg.argument_type)
358 
359  mesh_list = mesh_dict.keys()
360  if not mesh_list:
361  # There are no meshes associated with any of the arguments so
362  # this is not an inter-grid kernel
363  return
364 
365  if len(const.VALID_MESH_TYPES) != 2:
366  # Sanity check that nobody has messed with the number of
367  # grid types that we recognise. This is here because the
368  # implementation assumes that there are just two grids
369  # (coarse and fine).
370  raise InternalError(
371  f"The implementation of inter-grid support in the LFRic "
372  f"API assumes there are exactly two mesh types but "
373  f"LFRicConstants.VALID_MESH_TYPES contains "
374  f"{len(const.VALID_MESH_TYPES)}: {const.VALID_MESH_TYPES}")
375  if len(mesh_list) != len(const.VALID_MESH_TYPES):
376  raise ParseError(
377  f"Inter-grid kernels in the LFRic API must have at least "
378  f"one field argument on each of the mesh types "
379  f"({const.VALID_MESH_TYPES}). However, kernel {self.name} has "
380  f"arguments only on {[str(name) for name in mesh_list]}")
381  # Inter-grid kernels must only have field arguments
382  if non_field_arg_types:
383  raise ParseError(
384  f"Inter-grid kernels in the LFRic API are only permitted "
385  f"to have field arguments but kernel {self.name} also has "
386  f"arguments of type "
387  f"{[str(name) for name in non_field_arg_types]}")
388  # Check that all arguments have a mesh specified
389  if missing_mesh:
390  raise ParseError(
391  f"Inter-grid kernels in the LFRic API must specify which mesh "
392  f"each field argument is on but kernel {self.name} has at "
393  f"least one field argument for which 'mesh_arg' is missing.")
394  # Check that arguments on different meshes are on different
395  # function spaces. We do this by checking that no function space
396  # is listed as being associated with (arguments on) both meshes.
397  fs_sets = []
398  for mesh in mesh_dict:
399  fs_sets.append(set(mesh_dict[mesh]))
400  # Check that the sets of spaces (one for each mesh type) have
401  # no intersection
402  fs_common = fs_sets[0] & fs_sets[1]
403  if fs_common:
404  raise ParseError(
405  f"In the LFRic API field arguments to inter-grid kernels "
406  f"must be on different function spaces if they are on "
407  f"different meshes. However kernel {self.name} has a field on "
408  f"function space(s) {[str(name) for name in fs_common]} on "
409  f"each of the mesh types {[str(name) for name in mesh_list]}.")
410  # Finally, record that this is a valid inter-grid kernel
411  self._is_intergrid_is_intergrid = True
412 
413  def _identify_cma_op(self, cwise_ops):
414  '''
415  Identify and return the type of CMA-operator-related operation
416  this kernel performs (one of "assemble", "apply" or "matrix-matrix")
417 
418  :param cwise_ops: all column-wise operator arguments in a kernel.
419  :type cwise_ops: list of str
420 
421  :returns: the type of CMA-operator-related operation that this
422  kernel performs.
423  :rtype: str
424 
425  :raises ParseError: if the kernel metadata does not conform to the
426  LFRic rules for a kernel with a CMA operator.
427 
428  '''
429  # pylint: disable=too-many-branches
430  const = LFRicConstants()
431  for arg in self._arg_descriptors_arg_descriptors_arg_descriptors:
432  # No vector arguments are permitted
433  if arg.vector_size > 1:
434  raise ParseError(
435  f"Kernel '{self.name}' takes a CMA operator but has a "
436  f"vector argument '{arg.argument_type}*{arg.vector_size}'."
437  f" This is forbidden.")
438  # No stencil accesses are permitted
439  if arg.stencil:
440  raise ParseError(
441  f"Kernel '{self.name}' takes a CMA operator but has an "
442  f"argument with a stencil access "
443  f"('{arg.stencil['type']}'). This is forbidden.")
444  # Only field arguments with 'gh_real' data type are permitted
445  if (arg.argument_type in const.VALID_FIELD_NAMES and
446  arg.data_type != "gh_real"):
447  raise ParseError(
448  f"In the LFRic API a kernel that takes a CMA operator "
449  f"argument must only have field arguments with 'gh_real' "
450  f"data type but kernel '{self.name}' has a field argument "
451  f"with '{arg.data_type}' data type.")
452 
453  # Count the number of CMA operators that are written to
454  write_count = 0
455  for cop in cwise_ops:
456  if cop.access in AccessType.all_write_accesses():
457  write_count += 1
458 
459  if write_count == 0:
460  # This kernel only reads from CMA operators and must
461  # therefore be an apply (or apply-inverse). It must
462  # have one CMA operator, one read-only field and one
463  # written field as arguments
464  if len(cwise_ops) != 1:
465  raise ParseError(
466  f"In the LFRic API a kernel that applies a CMA operator "
467  f"must only have one such operator in its list of "
468  f"arguments but found {len(cwise_ops)} for kernel "
469  f"'{self.name}'.")
470  cma_op = cwise_ops[0]
471  if len(self._arg_descriptors_arg_descriptors_arg_descriptors) != 3:
472  raise ParseError(
473  f"In the LFRic API a kernel that applies a CMA operator "
474  f"must have 3 arguments (the operator and two fields) but "
475  f"kernel '{self.name}' has {len(self._arg_descriptors)} "
476  f"arguments.")
477  # Check that the other two arguments are fields
478  farg_read = psyGen.args_filter(
479  self._arg_descriptors_arg_descriptors_arg_descriptors,
480  arg_types=const.VALID_FIELD_NAMES,
481  arg_accesses=[AccessType.READ])
482  write_accesses = AccessType.all_write_accesses()
483  farg_write = psyGen.args_filter(
484  self._arg_descriptors_arg_descriptors_arg_descriptors,
485  arg_types=const.VALID_FIELD_NAMES,
486  arg_accesses=write_accesses)
487  if len(farg_read) != 1:
488  raise ParseError(
489  f"Kernel '{self.name}' has a read-only CMA operator. In "
490  f"order to apply it the kernel must have one read-only "
491  f"field argument.")
492  if len(farg_write) != 1:
493  raise ParseError(
494  f"Kernel '{self.name}' has a read-only CMA operator. In "
495  f"order to apply it the kernel must write to one field "
496  f"argument.")
497  # Check that the function spaces match up
498  if farg_read[0].function_space != cma_op.function_space_from:
499  raise ParseError(
500  f"Kernel '{self.name}' applies a CMA operator but the "
501  f"function space of the field argument it reads from "
502  f"('{farg_read[0].function_space}') does not match the "
503  f"'from' space of the operator "
504  f"('{cma_op.function_space_from}').")
505  if farg_write[0].function_space != cma_op.function_space_to:
506  raise ParseError(
507  f"Kernel '{self.name}' applies a CMA operator but the "
508  f"function space of the field argument it writes to "
509  f"('{farg_write[0].function_space}') does not match the "
510  f"'to' space of the operator "
511  f"('{cma_op.function_space_to}').")
512  # This is a valid CMA-apply or CMA-apply-inverse kernel
513  return "apply"
514 
515  if write_count == 1:
516  # This kernel writes to a single CMA operator and therefore
517  # must either be assembling a CMA operator
518  # or performing a matrix-matrix operation...
519  # The kernel must not write to any args other than the CMA
520  # operator
521  write_accesses = AccessType.all_write_accesses()
522  write_args = psyGen.args_filter(self._arg_descriptors_arg_descriptors_arg_descriptors,
523  arg_accesses=write_accesses)
524  if len(write_args) > 1:
525  # Remove the one CMA operator from the list of arguments
526  # that are written to so that we can produce a nice
527  # error message
528  for arg in write_args[:]:
529  if arg.argument_type == 'gh_columnwise_operator':
530  write_args.remove(arg)
531  break
532  raise ParseError(
533  f"Kernel '{self.name}' writes to a column-wise operator "
534  f"but also writes to "
535  f"{[str(arg.argument_type) for arg in write_args]} "
536  f"argument(s). This is not allowed.")
537  if len(cwise_ops) == 1:
538 
539  # If this is a valid assembly kernel then we need at least one
540  # read-only LMA operator
541  lma_read_ops = psyGen.args_filter(
542  self._arg_descriptors_arg_descriptors_arg_descriptors,
543  arg_types=["gh_operator"],
544  arg_accesses=[AccessType.READ])
545  if lma_read_ops:
546  return "assembly"
547  raise ParseError(
548  f"Kernel '{self.name}' has a single column-wise operator "
549  f"argument but does not conform to the rules for an "
550  f"Assembly kernel because it does not have any read-only "
551  f"LMA operator arguments.")
552  # A valid matrix-matrix kernel must only have CMA operators
553  # and scalars as arguments.
554  scalar_args = psyGen.args_filter(
555  self._arg_descriptors_arg_descriptors_arg_descriptors,
556  arg_types=const.VALID_SCALAR_NAMES)
557  if (len(scalar_args) + len(cwise_ops)) != \
558  len(self._arg_descriptors_arg_descriptors_arg_descriptors):
559  raise ParseError(
560  f"A column-wise matrix-matrix kernel must have only "
561  f"column-wise operators and scalars as arguments but "
562  f"kernel '{self.name}' has: "
563  f"{[str(a.argument_type) for a in self._arg_descriptors]}."
564  )
565  return "matrix-matrix"
566  raise ParseError(
567  f"An LFRic kernel cannot update more than one CMA (column-wise) "
568  f"operator but kernel '{self.name}' updates {write_count}.")
569 
570  def _validate_operates_on_domain(self, need_evaluator):
571  '''
572  Check whether a kernel that has operates_on == domain obeys
573  the rules for the LFRic API.
574 
575  :raises ParseError: if the kernel metadata does not obey the rules
576  for an LFRic kernel with operates_on = domain.
577  '''
578  if self.iterates_overiterates_over != "domain":
579  return
580 
581  const = LFRicConstants()
582  # A kernel which operates on the 'domain' is currently restricted
583  # to only accepting scalar and field arguments.
584  valid_arg_types = const.VALID_SCALAR_NAMES + const.VALID_FIELD_NAMES
585  for arg in self._arg_descriptors_arg_descriptors_arg_descriptors:
586  if arg.argument_type not in valid_arg_types:
587  raise ParseError(
588  f"In the LFRic API a kernel which operates on the 'domain'"
589  f" is only permitted to accept scalar and field arguments "
590  f"but the metadata for kernel '{self.name}' includes an "
591  f"argument of type '{arg.argument_type}'")
592 
593  if need_evaluator:
594  raise ParseError(
595  f"In the LFRic API a kernel that operates on the 'domain' "
596  f"cannot be passed basis/differential basis functions but the "
597  f"metadata for kernel '{self.name}' contains an entry for "
598  f"'meta_funcs'")
599 
600  if self.reference_elementreference_element.properties:
601  raise ParseError(
602  f"Kernel '{self.name}' operates on the domain but requests "
603  f"properties of the reference element "
604  f"({self.reference_element.properties}). This is not "
605  f"permitted in the LFRic API.")
606 
607  if self.meshmesh.properties:
608  raise ParseError(
609  f"Kernel '{self.name}' operates on the domain but requests "
610  f"properties of the mesh ({self.mesh.properties}). This is "
611  f"not permitted in the LFRic API.")
612 
613  if self._is_intergrid_is_intergrid:
614  raise ParseError(
615  f"Kernel '{self.name}' operates on the domain but has fields "
616  f"on different mesh resolutions (inter-grid). This is not "
617  f"permitted in the LFRic API.")
618 
619  @property
620  def func_descriptors(self):
621  '''
622  Returns metadata about the function spaces within a
623  Kernel. This metadata is provided within Kernel code via the
624  meta_funcs variable. Information is returned as a list of
625  DynFuncDescriptor03 objects, one for each function space. '''
626  return self._func_descriptors_func_descriptors
627 
628  @property
629  def cma_operation(self):
630  '''
631  Returns the type of CMA operation identified from the kernel
632  metadata (one of 'assembly', 'apply' or 'matrix-matrix') or
633  None if the kernel does not involve CMA operators '''
634  return self._cma_operation_cma_operation
635 
636  @property
637  def eval_shapes(self):
638  '''
639  Returns the shape(s) of evaluator required by this kernel or an
640  empty string if none.
641 
642  :return: the shape(s) of the evaluator (one of VALID_EVALUATOR_SHAPES)
643  or an empty list if the kernel does not require one.
644  :rtype: list
645 
646  '''
647  return self._eval_shapes_eval_shapes
648 
649  @property
650  def eval_targets(self):
651  '''
652  Returns the list of function spaces upon which any evaluator must be
653  provided. This list is obtained from the GH_EVALUATOR_TARGETS metadata
654  entry (if present). If this is not specified in the metadata then
655  we default to providing evaluators on all of the function spaces
656  associated with the arguments which this kernel updates.
657 
658  :return: list of the names of the function spaces (as they appear in
659  kernel metadata) upon which any evaluator must be provided.
660  :rtype: list of str
661  '''
662  return self._eval_targets_eval_targets
663 
664  @property
665  def is_intergrid(self):
666  '''
667  Returns whether or not this is an inter-grid kernel.
668 
669  :return: True if kernel is an inter-grid kernel, False otherwise
670  :rtype: bool
671  '''
672  return self._is_intergrid_is_intergrid
673 
674 
675 # ---------- Documentation utils -------------------------------------------- #
676 # The list of module members that we wish AutoAPI to generate
677 # documentation for. (See https://psyclone-ref.readthedocs.io)
678 __all__ = ['LFRicKernMetadata']
def get_integer_array(self, name)
Definition: kernel.py:939
def get_integer_variable(self, name)
Definition: kernel.py:906