Reference Guide  2.5.0
lfric_kernel_metadata.py
1 # -----------------------------------------------------------------------------
2 # BSD 3-Clause License
3 #
4 # Copyright (c) 2022-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 # Author R. W. Ford, STFC Daresbury Laboratory
35 # Modifications: A. R. Porter, STFC Daresbury Laboratory
36 
37 '''Module containing the LFRicKernelMetadata
38 kernel-layer-specific class that captures the LFRic kernel metadata.
39 
40 '''
41 import inspect
42 
43 from fparser.two import Fortran2003
44 from fparser.two.utils import walk, get_child
45 
46 from psyclone.domain.lfric import LFRicConstants
48  ColumnwiseOperatorArgMetadata
49 from psyclone.domain.lfric.kernel.field_arg_metadata import FieldArgMetadata
51  FieldVectorArgMetadata
53  InterGridArgMetadata
55  InterGridVectorArgMetadata
57  OperatorArgMetadata
58 from psyclone.domain.lfric.kernel.common_metadata import CommonMetadata
60  CommonMetaArgMetadata
62  EvaluatorTargetsMetadata
64  MetaArgsMetadata
66  MetaFuncsMetadata
68  MetaMeshMetadata
70  MetaRefElementMetadata
72  OperatesOnMetadata
73 from psyclone.domain.lfric.kernel.scalar_arg_metadata import ScalarArgMetadata
74 from psyclone.domain.lfric.kernel.shapes_metadata import ShapesMetadata
75 from psyclone.errors import InternalError
76 from psyclone.parse.utils import ParseError
77 from psyclone.psyir.frontend.fortran import FortranReader
78 from psyclone.psyir.symbols import DataTypeSymbol, UnsupportedFortranType
79 
80 # pylint: disable=too-many-lines
81 # pylint: disable=too-many-instance-attributes
82 
83 
85  '''Contains LFRic kernel metadata. This class supports kernel
86  metadata creation, modification, loading from a fortran string,
87  writing to a fortran string, raising from existing language-level
88  PSyIR and lowering to language-level PSyIR.
89 
90  :param operates_on: the name of the quantity that this kernel is \
91  intended to iterate over.
92  :type operates_on: Optional[str]
93  :param shapes: if a kernel requires basis or differential-basis \
94  functions then the metadata must also specify the set of points on \
95  which these functions are required. This information is provided \
96  by the gh_shape component of the metadata.
97  :type shapes: Optional[List[str]]
98  :param evaluator_targets: the function spaces on which an \
99  evaluator is required.
100  :type evaluator_targets: Optional[List[str]]
101  :param meta_args: a list of 'meta_arg' objects which capture the \
102  metadata values of the kernel arguments.
103  :type meta_args: Optional[List[:py:class:`psyclone.domain.lfric.kernel.\
104  CommonArgMetadata`]]
105  :param meta_funcs: a list of 'meta_func' objects which capture whether \
106  quadrature or evaluator data is required for a given function space.
107  :type meta_funcs: Optional[List[:py:class:`psyclone.domain.lfric.kernel.\
108  MetaFuncsArgMetadata`]]
109  :param meta_ref_element: a kernel that requires properties \
110  of the reference element in LFRic specifies those properties \
111  through the meta_reference_element metadata entry.
112  :type meta_ref_element: Optional[:py:class:`psyclone.domain.lfric.kernel.\
113  RefElementArgMetadata`]
114  :param meta_mesh: a kernel that requires properties of the LFRic \
115  mesh object specifies those properties through the meta_mesh \
116  metadata entry.
117  :type meta_mesh: Optional[:py:class:`psyclone.domain.lfric.kernel.\
118  MetaMeshArgMetadata`]
119  :param procedure_name: the name of the kernel procedure to call.
120  :type procedure_name: Optional[str]
121  :param name: the name of the symbol to use for the metadata in \
122  language-level PSyIR.
123  :type name: Optional[str]
124 
125  '''
126  # The fparser2 class that captures this metadata.
127  fparser2_class = Fortran2003.Derived_Type_Def
128 
129  # pylint: disable=too-many-arguments
130  def __init__(self, operates_on=None, shapes=None, evaluator_targets=None,
131  meta_args=None, meta_funcs=None, meta_ref_element=None,
132  meta_mesh=None, procedure_name=None, name=None):
133  super().__init__()
134  # Initialise internal variables
135  self._operates_on_operates_on = None
136  self._shapes_shapes = None
137  self._evaluator_targets_evaluator_targets = None
138  self._meta_args_meta_args = None
139  self._meta_funcs_meta_funcs = None
140  self._meta_ref_element_meta_ref_element = None
141  self._meta_mesh_meta_mesh = None
142  self._procedure_name_procedure_name = None
143  self._name_name = None
144 
145  if operates_on is not None:
146  self._operates_on_operates_on = OperatesOnMetadata(operates_on)
147  if shapes is not None:
148  self._shapes_shapes = ShapesMetadata(shapes)
149  if evaluator_targets is not None:
150  self._evaluator_targets_evaluator_targets = EvaluatorTargetsMetadata(
151  evaluator_targets)
152  if meta_args is not None:
153  self._meta_args_meta_args = MetaArgsMetadata(meta_args)
154  if meta_funcs is not None:
155  self._meta_funcs_meta_funcs = MetaFuncsMetadata(meta_funcs)
156  if meta_ref_element is not None:
157  self._meta_ref_element_meta_ref_element = MetaRefElementMetadata(
158  meta_ref_element)
159  if meta_mesh is not None:
160  self._meta_mesh_meta_mesh = MetaMeshMetadata(meta_mesh)
161  if procedure_name is not None:
162  # Validate procedure_name via setter
163  self.procedure_nameprocedure_nameprocedure_nameprocedure_name = procedure_name
164  if name is not None:
165  # Validate name via setter
166  self.namenamenamename = name
167 
168  def _validation_error_str(self, description):
169  '''Utility to extend a validation error message in a standard form,
170  with additional context information.
171 
172  :param str description: the original error description.
173 
174  :returns: an updated error description.
175  :rtype: str
176 
177  '''
178  name = self.namenamenamename if self.namenamenamename else 'unset'
179  proc_name = self.procedure_nameprocedure_nameprocedure_nameprocedure_name if self.procedure_nameprocedure_nameprocedure_nameprocedure_name else 'unset'
180  return (
181  f"{description} in kernel metadata '{name}' for procedure "
182  f"'{proc_name}'.")
183 
184  def validate(self):
185  '''Only certain metadata combinations are allowed in LFRic. This
186  routine checks that any such constraints are respected.
187 
188  :raises ParseError: if any validation checks fail.
189 
190  '''
191  # The _get_kernel_type method returns the type of kernel that
192  # this metadata specifies (e.g. inter-grid or domain). In the
193  # process it checks that the combined metadata conforms to the
194  # constraints of the supported kernel types.
195  _ = self._get_kernel_type_get_kernel_type()
196 
197  # TODO issue #1953: Checks that are not associated with
198  # determining kernel type
199  # - A kernel must modify at least one of its arguments
200  # - Function spaces of basis functions exist in meta_args
201  # - No shape if no basis or diff basis. shape
202  # if quadrature or evaluator
203  # - evaluator_targets only if required
204  # - evaluator_targets function spaces exist
205  # - Disallow duplicate metadata in meta_args,
206  # meta_funcs etc. lists
207  # - Writing to read-only function spaces (Check within
208  # meta_arg classes?)
209 
210  def _get_kernel_type(self):
211  '''Returns the type of kernel, based on the metadata stored in this
212  instance. LFRic supports different types of kernel and the
213  type can be inferred from the kernel metadata as each kernel
214  type has different constraints on the allowed metadata values
215  and combinations. Also checks that the metadata conforms to
216  any rules associated with the particular kernel type.
217 
218  :returns: the type of kernel that this metadata describes.
219  :rtype: str
220 
221  '''
222  if self.meta_args_getmeta_args_get(
223  [InterGridArgMetadata, InterGridVectorArgMetadata]):
224  # This has to be an inter-grid kernel.
225  self._validate_intergrid_kernel_validate_intergrid_kernel()
226  return "inter-grid"
227  if self.meta_args_getmeta_args_get(ColumnwiseOperatorArgMetadata):
228  # This has to be a cma kernel.
229  cma_type = self._cma_kernel_type_cma_kernel_type()
230  return f"cma-{cma_type}"
231  if self.operates_onoperates_onoperates_onoperates_on == "domain":
232  # This has to be a domain kernel.
233  self._validate_domain_kernel_validate_domain_kernel()
234  return "domain"
235  # This has to be a general purpose kernel.
236  self._validate_general_purpose_kernel_validate_general_purpose_kernel()
237  return "general-purpose"
238 
239  def _validate_generic_kernel(self):
240  '''Validation checks common to multiple kernel types.
241 
242  :raises ParseError: if any validation checks fail.
243 
244  '''
245  # Kernel metadata with operates_on != domain must have at
246  # least one meta_args argument that is a field, field vector,
247  # intergrid field, intergrid vector field, LMA operator or CMA
248  # operator (in order to determine the appropriate iteration
249  # space).
250  if self.operates_onoperates_onoperates_onoperates_on != "domain" and not self.meta_args_getmeta_args_get(
251  [FieldArgMetadata, FieldVectorArgMetadata, OperatorArgMetadata,
252  ColumnwiseOperatorArgMetadata, InterGridArgMetadata,
253  InterGridVectorArgMetadata]):
254  raise ParseError(self._validation_error_str_validation_error_str(
255  "Kernel metadata with 'operates_on != domain' must have at "
256  "least one meta_args argument that is a field, field vector, "
257  "intergrid field, intergrid vector field, LMA operator or "
258  "CMA operator (in order to determine the appropriate "
259  "iteration space), however this metadata has none"))
260 
261  # A kernel that contains an operator argument must only
262  # accept real-valued fields.
263  operator_args = self.meta_args_getmeta_args_get(
264  [OperatorArgMetadata, ColumnwiseOperatorArgMetadata])
265  if operator_args:
266  field_args = self.meta_args_getmeta_args_get(
267  [FieldArgMetadata, FieldVectorArgMetadata,
268  InterGridArgMetadata, InterGridVectorArgMetadata])
269  for field_arg in field_args:
270  if field_arg.datatype != "gh_real":
271  raise ParseError(self._validation_error_str_validation_error_str(
272  f"Kernel metadata with a meta_args operator argument "
273  f"must only contain meta_args real-valued field "
274  f"arguments, however found a field of type "
275  f"'{field_arg.datatype}'"))
276 
277  def _validate_general_purpose_kernel(self):
278  '''Validation checks for a general purpose kernel.
279 
280  :raises ParseError: if any validation checks fail.
281 
282  '''
283  # Generic constraints.
284  self._validate_generic_kernel_validate_generic_kernel()
285 
286  # General-purpose kernels do not operate over the domain.
287  if self.operates_onoperates_onoperates_onoperates_on == "domain":
288  raise ParseError(self._validation_error_str_validation_error_str(
289  "A general purpose kernel should not operate on a domain, "
290  "however this does"))
291 
292  # General-purpose kernels with operates_on = CELL_COLUMN only
293  # accept meta_arg arguments of the following types: field,
294  # field vector, LMA operator, scalar. Scalar meta_arg
295  # arguments must be one of 'real', 'integer' or 'logical' (but
296  # this is all supported types so no need to check). Scalar
297  # meta_arg arguments must also be read only.
298  if self.operates_onoperates_onoperates_onoperates_on == "cell_column":
299  for meta_arg in self.meta_argsmeta_argsmeta_args:
300  if type(meta_arg) not in [
301  FieldArgMetadata, FieldVectorArgMetadata,
302  OperatorArgMetadata, ScalarArgMetadata]:
303  raise ParseError(self._validation_error_str_validation_error_str(
304  f"General purpose kernels with 'operates_on == "
305  f"cell_column' should only have meta_arg arguments "
306  f"of type field, field vector, LMA operator or scalar"
307  f", but found '{meta_arg.check_name}'"))
308 
309  # TODO issue #1953: constraints when operates_on == dofs
310  # 1: They must have one and only one modified (i.e. written
311  # to) argument.
312  # 2: There must be at least one field in the argument
313  # list. This is so that we know the number of DoFs to iterate
314  # over in the PSy layer.
315  # 3: Kernel arguments must be either fields or scalars (real-
316  # and/or integer-valued).
317  # 4: All field arguments to a given Built-in must be on the
318  # same function space. This is because all current Built-ins
319  # operate on DoFs and therefore all fields should have the
320  # same number. It also means that we can determine the number
321  # of DoFs uniquely when a scalar is written to;
322  # 5: Built-ins that update real-valued fields can, in general, only
323  # read from other real-valued fields, but they can take both real and
324  # integer scalar arguments (see rule 7 for exceptions);
325  # 6: Built-ins that update integer-valued fields can, in
326  # general, only read from other integer-valued fields and take
327  # integer scalar arguments (see rule 7 for exceptions);
328  # 7: The only two exceptions from the rules 5) and 6) above
329  # regarding the same data type of “write” and “read” field
330  # arguments are Built-ins that convert field data from real to
331  # integer, real_to_int_X, and from integer to real, int_to_real_X.
332 
333  def _validate_domain_kernel(self):
334  '''Validation checks for a domain kernel.
335 
336  :raises ParseError: if any validation checks fail.
337 
338  '''
339  # Generic constraints.
340  self._validate_generic_kernel_validate_generic_kernel()
341 
342  if self.operates_onoperates_onoperates_onoperates_on != "domain":
343  raise ParseError(self._validation_error_str_validation_error_str(
344  f"Domain kernels should have their operates_on metadata set "
345  f"to 'domain', but found '{self.operates_on}'"))
346 
347  # Only scalar, field and field vector arguments are permitted.
348  for meta_arg in self.meta_argsmeta_argsmeta_args:
349  if type(meta_arg) not in [
350  ScalarArgMetadata, FieldArgMetadata,
351  FieldVectorArgMetadata]:
352  raise ParseError(self._validation_error_str_validation_error_str(
353  f"Domain kernels should only have meta_arg arguments "
354  f"of type field, field vector, or scalar, but found "
355  f"'{meta_arg.check_name}'"))
356 
357  # All fields must be on discontinuous function spaces and
358  # stencil accesses are not permitted.
359  lfric_constants = LFRicConstants()
360  fields_metadata = self.meta_args_getmeta_args_get(
361  [FieldArgMetadata, FieldVectorArgMetadata])
362  for meta_arg in fields_metadata:
363  if meta_arg.function_space not in \
364  lfric_constants.DISCONTINUOUS_FUNCTION_SPACES:
365  raise ParseError(self._validation_error_str_validation_error_str(
366  f"Domain kernels meta_arg arguments of type field, or "
367  f"field vector should be on a discontinuous function "
368  f"space, but found '{meta_arg.function_space}'"))
369  if meta_arg.stencil:
370  raise ParseError(self._validation_error_str_validation_error_str(
371  f"Domain kernels meta_arg arguments of type field, or "
372  f"field vector should not have any stencil accesses, but "
373  f"found a stencil of type '{meta_arg.stencil}'"))
374 
375  # No basis/diff basis functions are allowed.
376  if self.meta_funcsmeta_funcsmeta_funcs:
377  raise ParseError(self._validation_error_str_validation_error_str(
378  "Domain kernels should not specify basis or differential "
379  "basis functions metadata, but this does"))
380 
381  # No mesh properties are allowed.
382  if self.meta_meshmeta_meshmeta_mesh:
383  raise ParseError(self._validation_error_str_validation_error_str(
384  "Domain kernels should not specify mesh property metadata, "
385  "but this does"))
386 
387  def _cma_kernel_type(self):
388  '''Determine the type of CMA (Column Matrix Assembly) kernel this is.
389 
390  :returns: the type of cma kernel this metadata respresents.
391  :rtype: str
392 
393  '''
394  if self.meta_args_getmeta_args_get(OperatorArgMetadata):
395  # Only CMA assembly kernels have an LMA operator.
396  self._validate_cma_assembly_kernel_validate_cma_assembly_kernel()
397  return "assembly"
398  if self.meta_args_getmeta_args_get(FieldArgMetadata):
399  # CMA matrix-matrix kernels do not have Field arguments.
400  self._validate_cma_apply_kernel_validate_cma_apply_kernel()
401  return "apply"
402  self._validate_cma_matrix_matrix_kernel_validate_cma_matrix_matrix_kernel()
403  return "matrix-matrix"
404 
405  def _validate_generic_cma_kernel(self):
406  '''Validation checks for a generic CMA kernel.
407 
408  :raises ParseError: if any validation checks fail.
409 
410  '''
411  # Generic constraints.
412  self._validate_generic_kernel_validate_generic_kernel()
413 
414  # Must operate on a cell_column.
415  if self.operates_onoperates_onoperates_onoperates_on != "cell_column":
416  raise ParseError(self._validation_error_str_validation_error_str(
417  f"A CMA kernel should only operate on a 'cell_column', but "
418  f"found '{self.operates_on}'"))
419 
420  # At least one CMA operator argument required.
421  cma_ops = self.meta_args_getmeta_args_get(ColumnwiseOperatorArgMetadata)
422  if not cma_ops:
423  raise ParseError(self._validation_error_str_validation_error_str(
424  "A CMA kernel should contain at least one cma operator "
425  "argument but none are specified in the meta_args metadata"))
426 
427  # No intergrid arguments allowed.
428  if self.meta_args_getmeta_args_get(
429  [InterGridArgMetadata, InterGridVectorArgMetadata]):
430  raise ParseError(self._validation_error_str_validation_error_str(
431  "A CMA kernel should not contain any intergrid arguments, "
432  "but at least one was found"))
433 
434  # No field vector arguments allowed.
435  if self.meta_args_getmeta_args_get(FieldVectorArgMetadata):
436  raise ParseError(self._validation_error_str_validation_error_str(
437  "A CMA kernel should not contain any field vector arguments, "
438  "but at least one was found"))
439 
440  # No stencils in field arguments.
441  fields_metadata = self.meta_args_getmeta_args_get(FieldArgMetadata)
442  for meta_arg in fields_metadata:
443  if meta_arg.stencil:
444  raise ParseError(self._validation_error_str_validation_error_str(
445  "A CMA kernel should not contain any fields with stencil "
446  "accesses, but at least one was found"))
447 
448  def _validate_cma_assembly_kernel(self):
449  '''Validation checks for a CMA assembly kernel.
450 
451  :raises ParseError: if any validation checks fail.
452 
453  '''
454  # Generic CMA constraints.
455  self._validate_generic_cma_kernel_validate_generic_cma_kernel()
456 
457  # One CMA operator argument.
458  cma_ops = self.meta_args_getmeta_args_get(ColumnwiseOperatorArgMetadata)
459  if len(cma_ops) != 1:
460  raise ParseError(self._validation_error_str_validation_error_str(
461  f"A CMA assembly kernel should contain one CMA operator "
462  f"argument, however {len(cma_ops)} were found"))
463 
464  # CMA operator argument must have write access.
465  if cma_ops[0].access == "gh_read":
466  raise ParseError(self._validation_error_str_validation_error_str(
467  f"A CMA assembly kernel should contain one CMA operator "
468  f"argument with write access, however it has access "
469  f"'{cma_ops[0].access}'"))
470 
471  # One or more LMA operators required.
472  if not self.meta_args_getmeta_args_get(OperatorArgMetadata):
473  raise ParseError(self._validation_error_str_validation_error_str(
474  "A CMA assembly kernel should contain at least one LMA "
475  "operator but none were found"))
476  # All arguments except the CMA argument must be read-only.
477  # pylint: disable=unidiomatic-typecheck
478  for meta_arg in self.meta_argsmeta_argsmeta_args:
479  if type(meta_arg) is not ColumnwiseOperatorArgMetadata:
480  if meta_arg.access != "gh_read":
481  raise ParseError(self._validation_error_str_validation_error_str(
482  f"A CMA assembly kernel should have all arguments as "
483  f"read-only apart from the CMA argument, but found "
484  f"non-CMA argument with access '{meta_arg.access}'"))
485  # pylint: enable=unidiomatic-typecheck
486 
487  def _validate_cma_apply_kernel(self):
488  '''Validation checks for a CMA apply kernel.
489 
490  :raises ParseError: if any validation checks fail.
491 
492  '''
493  lfric_constants = LFRicConstants()
494 
495  # Generic CMA constraints.
496  self._validate_generic_cma_kernel_validate_generic_cma_kernel()
497 
498  # Only CMA and field arguments.
499  for meta_arg in self.meta_argsmeta_argsmeta_args:
500  if type(meta_arg) not in [
501  ColumnwiseOperatorArgMetadata, FieldArgMetadata]:
502  raise ParseError(self._validation_error_str_validation_error_str(
503  f"A CMA apply kernel should only contain field or CMA "
504  f"operator arguments, but found '{meta_arg.check_name}'"))
505 
506  # One CMA operator argument.
507  cma_ops = self.meta_args_getmeta_args_get(ColumnwiseOperatorArgMetadata)
508  if len(cma_ops) != 1:
509  raise ParseError(self._validation_error_str_validation_error_str(
510  f"A CMA apply kernel should contain one CMA operator "
511  f"argument, however found {len(cma_ops)}"))
512  cma_op = cma_ops[0]
513 
514  # CMA operator argument must be read only.
515  if cma_op.access != "gh_read":
516  raise ParseError(self._validation_error_str_validation_error_str(
517  f"A CMA apply kernel should contain one CMA operator argument "
518  f"with read access, however the operator has access "
519  f"'{cma_op.access}'"))
520 
521  # Two field arguments.
522  field_args = self.meta_args_getmeta_args_get(FieldArgMetadata)
523  if not field_args:
524  raise ParseError(self._validation_error_str_validation_error_str(
525  "A CMA apply kernel should contain two field arguments, but "
526  "none were found"))
527  if len(field_args) != 2:
528  word = "were"
529  if len(field_args) == 1:
530  word = "was"
531  raise ParseError(self._validation_error_str_validation_error_str(
532  f"A CMA apply kernel should contain two field arguments, but "
533  f"{len(field_args)} {word} found"))
534 
535  # One field that is read and one field that is written.
536  if not ((field_args[0].access == "gh_read" and
537  field_args[1].access in lfric_constants.WRITE_ACCESSES) or
538  (field_args[0].access in lfric_constants.WRITE_ACCESSES and
539  field_args[1].access == "gh_read")):
540  raise ParseError(self._validation_error_str_validation_error_str(
541  f"A CMA apply kernel should contain two field arguments, one "
542  f"of which is read and the other written, but found "
543  f"'{field_args[0].access}' and '{field_args[1].access}'"))
544 
545  # The function spaces of the read and written fields must
546  # match the from and to spaces, respectively, of the CMA
547  # operator.
548  if field_args[0].access in lfric_constants.WRITE_ACCESSES:
549  writer_field = field_args[0]
550  reader_field = field_args[1]
551  else:
552  reader_field = field_args[0]
553  writer_field = field_args[1]
554  if writer_field.function_space != cma_op.function_space_to:
555  raise ParseError(self._validation_error_str_validation_error_str(
556  f"In a CMA apply kernel, the function space of the written "
557  f"field must match the function space of the CMA operator's "
558  f"'to' function space, but found "
559  f"'{writer_field.function_space}' and "
560  f"'{cma_op.function_space_to}' respectively"))
561  if reader_field.function_space != cma_op.function_space_from:
562  raise ParseError(self._validation_error_str_validation_error_str(
563  f"In a CMA apply kernel, the function space of the read "
564  f"field must match the function space of the CMA operator's "
565  f"'from' function space, but found "
566  f"'{reader_field.function_space}' and "
567  f"'{cma_op.function_space_from}' respectively"))
568 
569  def _validate_cma_matrix_matrix_kernel(self):
570  '''Validation checks for a CMA matrix-matrix kernel.
571 
572  :raises ParseError: if any validation checks fail.
573 
574  '''
575  lfric_constants = LFRicConstants()
576 
577  # Generic CMA constraints.
578  self._validate_generic_cma_kernel_validate_generic_cma_kernel()
579 
580  # Arguments must be CMA operators and, optionally, one or more scalars.
581  for meta_arg in self.meta_argsmeta_argsmeta_args:
582  if type(meta_arg) not in [
583  ColumnwiseOperatorArgMetadata, ScalarArgMetadata]:
584  raise ParseError(self._validation_error_str_validation_error_str(
585  f"A CMA matrix-matrix kernel must only contain CMA "
586  f"operators or scalars, but found "
587  f"'{meta_arg.check_name}'"))
588 
589  # Exactly one of the CMA arguments must be written to.
590  # pylint: disable=unidiomatic-typecheck
591  cma_writers = [meta_arg for meta_arg in self.meta_argsmeta_argsmeta_args if
592  type(meta_arg) is ColumnwiseOperatorArgMetadata
593  and meta_arg.access in lfric_constants.WRITE_ACCESSES]
594  # pylint: enable=unidiomatic-typecheck
595  if len(cma_writers) != 1:
596  raise ParseError(self._validation_error_str_validation_error_str(
597  f"A CMA matrix-matrix kernel must write to exactly one CMA "
598  f"operator argument, but found {len(cma_writers)} writers"))
599 
600  # All arguments other than a single CMA argument should be read
601  # only. CMA arguments have been checked. Only scalars remain
602  # and these are constrained to be read-only anyway, so no more
603  # checks are required.
604 
605  def _validate_intergrid_kernel(self):
606  '''Validation checks for an inter-grid kernel.
607 
608  :raises ParseError: if any validation checks fail.
609 
610  '''
611  # Generic constraints.
612  self._validate_generic_kernel_validate_generic_kernel()
613 
614  # Must operate on a cell_column.
615  if self.operates_onoperates_onoperates_onoperates_on != "cell_column":
616  raise ParseError(self._validation_error_str_validation_error_str(
617  f"An intergrid kernel should only operate on a "
618  f"'cell_column', but found '{self.operates_on}'"))
619 
620  # All args must be intergrid args.
621  for meta_arg in self.meta_argsmeta_argsmeta_args:
622  if not isinstance(meta_arg, InterGridArgMetadata):
623  raise ParseError(self._validation_error_str_validation_error_str(
624  f"An intergrid kernel should only have intergrid "
625  f"arguments, but found '{meta_arg.check_name}'"))
626 
627  coarse_args = [meta_arg for meta_arg in self.meta_argsmeta_argsmeta_args
628  if meta_arg.mesh_arg == "gh_coarse"]
629  # There must be at least one intergrid arg on a coarse mesh.
630  if not coarse_args:
631  raise ParseError(self._validation_error_str_validation_error_str(
632  "An intergrid kernel should have at least one intergrid "
633  "argument on the coarse mesh, but none were found"))
634 
635  # All intergrid args on the coarse mesh are on the same
636  # function space.
637  coarse_function_space = coarse_args[0].function_space
638  for coarse_arg in coarse_args[1:]:
639  if coarse_arg.function_space != coarse_function_space:
640  raise ParseError(self._validation_error_str_validation_error_str(
641  f"An intergrid kernel should have all of its arguments, "
642  f"that are on the coarse mesh, on the same function "
643  f"space. However, '{coarse_arg.function_space}' and "
644  f"'{coarse_function_space}' differ"))
645 
646  fine_args = [meta_arg for meta_arg in self.meta_argsmeta_argsmeta_args
647  if meta_arg.mesh_arg == "gh_fine"]
648  # There must be at least one intergrid arg on a fine mesh.
649  if not fine_args:
650  raise ParseError(self._validation_error_str_validation_error_str(
651  "An intergrid kernel should have at least one intergrid "
652  "argument on the fine mesh, but none were found"))
653 
654  fine_function_space = fine_args[0].function_space
655  # All intergrid args on the fine mesh are on the same
656  # function space.
657  for fine_arg in fine_args[1:]:
658  if fine_arg.function_space != fine_function_space:
659  raise ParseError(self._validation_error_str_validation_error_str(
660  f"An intergrid kernel should have all of its arguments, "
661  f"that are on the fine mesh, on the same function "
662  f"space. However, '{fine_arg.function_space}' and "
663  f"'{fine_function_space}' differ"))
664 
665  # The function space used by the args on the coarse mesh must
666  # differ from the function space used by the args on the fine
667  # mesh.
668  if coarse_function_space == fine_function_space:
669  raise ParseError(self._validation_error_str_validation_error_str(
670  f"An intergrid kernel should have different function spaces "
671  f"for the arguments on the coarse mesh and the arguments on "
672  f"the fine mesh. However, both are on "
673  f"'{coarse_function_space}'"))
674 
675  @staticmethod
676  def create_from_psyir(symbol):
677  '''Create a new instance of LFRicKernelMetadata populated with
678  metadata from a kernel in language-level PSyIR.
679 
680  :param symbol: the symbol in which the metadata is stored \
681  in language-level PSyIR.
682  :type symbol: :py:class:`psyclone.psyir.symbols.DataTypeSymbol`
683 
684  :returns: an instance of LFRicKernelMetadata.
685  :rtype: :py:class:`psyclone.domain.lfric.kernel.psyir.\
686  LFRicKernelMetadata`
687 
688  :raises TypeError: if the symbol argument is not the expected \
689  type.
690  :raises InternalError: if the datatype of the provided symbol \
691  is not the expected type.
692 
693  '''
694  if not isinstance(symbol, DataTypeSymbol):
695  raise TypeError(
696  f"Expected a DataTypeSymbol but found a "
697  f"{type(symbol).__name__}.")
698 
699  datatype = symbol.datatype
700 
701  if not isinstance(datatype, UnsupportedFortranType):
702  raise InternalError(
703  f"Expected kernel metadata to be stored in the PSyIR as "
704  f"an UnsupportedFortranType, but found "
705  f"{type(datatype).__name__}.")
706 
707  # In an UnsupportedFortranType, the declaration is stored as a
708  # string, so use create_from_fortran_string()
709  return LFRicKernelMetadata.create_from_fortran_string(
710  datatype.declaration)
711 
712  @staticmethod
713  def create_from_fparser2(fparser2_tree):
714  '''Create an instance of this class from an fparser2 tree.
715 
716  :param fparser2_tree: fparser2 tree containing the metadata \
717  for an LFRic Kernel.
718  :type fparser2_tree: \
719  :py:class:`fparser.two.Fortran2003.Derived_Type_Ref`
720 
721  :returns: an instance of LFRicKernelMetadata.
722  :rtype: :py:class:`psyclone.domain.lfric.kernel.psyir.\
723  LFRicKernelMetadata`
724 
725  :raises ParseError: if one of the meta_args entries is an \
726  unexpected type.
727  :raises ParseError: if the metadata type does not extend kernel_type.
728 
729  '''
730  LFRicKernelMetadata.check_fparser2(
731  fparser2_tree, Fortran2003.Derived_Type_Def)
732 
733  kernel_metadata = LFRicKernelMetadata()
734 
735  for fparser2_node in walk(
736  fparser2_tree, Fortran2003.Data_Component_Def_Stmt):
737  fortran_string = str(fparser2_node).lower()
738  # pylint: disable=protected-access
739  if "operates_on" in fortran_string:
740  # the value of operates on (CELL_COLUMN, ...)
741  kernel_metadata._operates_on = OperatesOnMetadata.\
742  create_from_fparser2(fparser2_node)
743  elif "meta_args" in fortran_string:
744  kernel_metadata._meta_args = MetaArgsMetadata.\
745  create_from_fparser2(fparser2_node)
746  elif "meta_funcs" in fortran_string:
747  kernel_metadata._meta_funcs = MetaFuncsMetadata.\
748  create_from_fparser2(fparser2_node)
749  elif "gh_shape" in fortran_string:
750  # the gh_shape values (gh_quadrature_XYoZ, ...)
751  kernel_metadata._shapes = ShapesMetadata.create_from_fparser2(
752  fparser2_node)
753  elif "gh_evaluator_targets" in fortran_string:
754  # the gh_evaluator_targets values (w0, w1, ...)
755  kernel_metadata._evaluator_targets = EvaluatorTargetsMetadata.\
756  create_from_fparser2(fparser2_node)
757  elif "meta_reference_element" in fortran_string:
758  kernel_metadata._meta_ref_element = MetaRefElementMetadata.\
759  create_from_fparser2(fparser2_node)
760  elif "meta_mesh" in fortran_string:
761  kernel_metadata._meta_mesh = MetaMeshMetadata.\
762  create_from_fparser2(fparser2_node)
763  else:
764  raise ParseError(
765  f"Found unexpected metadata declaration "
766  f"'{str(fparser2_node)}' in '{str(fparser2_tree)}'.")
767  # pylint: enable=protected-access
768 
769  kernel_metadata.name = fparser2_tree.children[0].children[1].tostr()
770 
771  attribute_list = fparser2_tree.children[0].children[0]
772  str_attribute_list = str(attribute_list).lower() \
773  if attribute_list else ""
774  if (attribute_list is None or
775  "extends(kernel_type)" not in str_attribute_list):
776  raise ParseError(
777  f"The metadata type declaration should extend kernel_type, "
778  f"but found '{fparser2_tree.children[0]}' in {fparser2_tree}.")
779  kernel_metadata.procedure_name = \
780  LFRicKernelMetadata._get_procedure_name(fparser2_tree)
781 
782  return kernel_metadata
783 
784  def lower_to_psyir(self):
785  '''Lower the metadata to language-level PSyIR.
786 
787  :returns: metadata as stored in language-level PSyIR.
788  :rtype: :py:class:`psyclone.psyir.symbols.DataTypeSymbol`
789 
790  '''
791  return DataTypeSymbol(
792  str(self.namenamenamename), UnsupportedFortranType(self.fortran_stringfortran_string()))
793 
794  @staticmethod
795  def _get_procedure_name(spec_part):
796  '''Internal utility that extracts the procedure name from an
797  fparser2 tree that captures LFRic metadata.
798 
799  TODO Issue #1946: potentially update the metadata to capture
800  interface names as well as the interface itself. The procedure
801  name will then no longer be optional.
802 
803  :param spec_part: the fparser2 parse tree containing the metadata.
804  :type spec_part: :py:class:`fparser.two.Fortran2003.Derived_Type_Def`
805 
806  :returns: the value of the property.
807  :rtype: Optional[str]
808 
809  :raises ParseError: if the metadata is invalid.
810 
811  '''
812  # The value of 'code' should be found in a type bound
813  # procedure (after the contains keyword)
814  type_bound_procedure = get_child(
815  spec_part, Fortran2003.Type_Bound_Procedure_Part)
816  if not type_bound_procedure:
817  return None
818  if len(type_bound_procedure.children) != 2:
819  raise ParseError(
820  f"Expecting a type-bound procedure, but found "
821  f"'{spec_part}'.")
822  specific_binding = type_bound_procedure.children[1]
823  if not isinstance(specific_binding, Fortran2003.Specific_Binding):
824  raise ParseError(
825  f"Expecting a specific binding for the type-bound "
826  f"procedure, but found '{specific_binding}' in "
827  f"'{spec_part}'.")
828  binding_name = specific_binding.children[3]
829  procedure_name = specific_binding.children[4]
830  if binding_name.string.lower() != "code" and procedure_name:
831  raise ParseError(
832  f"Expecting the type-bound procedure binding-name to be "
833  f"'code' if there is a procedure name, but found "
834  f"'{str(binding_name)}' in '{spec_part}'.")
835  if not procedure_name:
836  # Support the alternative metadata format that does
837  # not include 'code =>'
838  procedure_name = binding_name
839  return procedure_name.string
840 
841  def fortran_string(self):
842  '''
843  :returns: the metadata represented by this instance as Fortran.
844  :rtype: str
845 
846  :raises ValueError: if the values for name, meta_arg, \
847  operates_on and procedure_name have not been set.
848 
849  '''
850  if not (self.operates_onoperates_onoperates_onoperates_on and self._meta_args_meta_args and self.namenamenamename):
851  raise ValueError(
852  f"Values for operates_on, meta_args and name "
853  f"must be provided before calling the fortran_string method, "
854  f"but found '{self.operates_on}', '{self._meta_args}' "
855  f"and '{self.name}' respectively.")
856 
857  operates_on = f" {self._operates_on.fortran_string()}"
858  meta_args = f" {self._meta_args.fortran_string()}"
859 
860  shapes = ""
861  if self._shapes_shapes:
862  shapes = f" {self._shapes.fortran_string()}"
863 
864  evaluator_targets = ""
865  if self._evaluator_targets_evaluator_targets:
866  evaluator_targets = f" {self._evaluator_targets.fortran_string()}"
867 
868  meta_funcs = ""
869  if self._meta_funcs_meta_funcs:
870  meta_funcs = f" {self._meta_funcs.fortran_string()}"
871 
872  meta_ref_element = ""
873  if self._meta_ref_element_meta_ref_element:
874  meta_ref_element = f" {self._meta_ref_element.fortran_string()}"
875 
876  meta_mesh = ""
877  if self._meta_mesh_meta_mesh:
878  meta_mesh = f" {self._meta_mesh.fortran_string()}"
879 
880  # TODO Issue #1946: potentially update the metadata to capture
881  # interface names as well as the interface itself. The
882  # procedure name will then no longer be optional.
883  procedure = ""
884  if self.procedure_nameprocedure_nameprocedure_nameprocedure_name:
885  procedure = (
886  f" CONTAINS\n"
887  f" PROCEDURE, NOPASS :: {self.procedure_name}\n")
888 
889  result = (
890  f"TYPE, PUBLIC, EXTENDS(kernel_type) :: {self.name}\n"
891  f"{meta_args}"
892  f"{meta_funcs}"
893  f"{meta_ref_element}"
894  f"{meta_mesh}"
895  f"{shapes}"
896  f"{evaluator_targets}"
897  f"{operates_on}"
898  f"{procedure}"
899  f"END TYPE {self.name}\n")
900  return result
901 
902  @property
903  def kernel_type(self):
904  '''
905  :returns: the type of kernel that this is.
906  :rtype: str
907  '''
908  return self._get_kernel_type_get_kernel_type()
909 
910  @property
911  def operates_on(self):
912  '''
913  :returns: the kernel operates_on property specified by the \
914  metadata.
915  :rtype: str
916  '''
917  if self._operates_on_operates_on is None:
918  return None
919  return self._operates_on_operates_on.operates_on
920 
921  @operates_on.setter
922  def operates_on(self, value):
923  '''
924  :param str value: set the kernel operates_on property \
925  in the metadata to the specified value.
926 
927  '''
928  self._operates_on_operates_on = OperatesOnMetadata(value)
929 
930  @property
931  def shapes(self):
932  '''
933  :returns: a list of shape metadata values.
934  :rtype: Optional[List[str]]
935 
936  '''
937  if self._shapes_shapes is None:
938  return None
939  return self._shapes_shapes.shapes
940 
941  @shapes.setter
942  def shapes(self, values):
943  '''
944  :param values: set the shape metadata to the \
945  supplied list of values.
946  :type values: List[str]
947 
948  '''
949  self._shapes_shapes = ShapesMetadata(values)
950 
951  @property
952  def evaluator_targets(self):
953  '''
954  :returns: a list of evaluator_targets metadata values.
955  :rtype: Optional[List[str]]
956 
957  '''
958  if self._evaluator_targets_evaluator_targets is None:
959  return None
960  return self._evaluator_targets_evaluator_targets.evaluator_targets
961 
962  @evaluator_targets.setter
963  def evaluator_targets(self, values):
964  '''
965  :param values: set the evaluator_targets metadata to the \
966  supplied list of values.
967  :type values: List[str]
968 
969  '''
970  self._evaluator_targets_evaluator_targets = EvaluatorTargetsMetadata(values)
971 
972  @property
973  def meta_args(self):
974  '''
975  :returns: a list of 'meta_arg' objects which capture the \
976  metadata values of the kernel arguments.
977  :rtype: Optional[List[:py:class:`psyclone.domain.lfric.kernel.\
978  CommonArg`]]
979 
980  '''
981  if self._meta_args_meta_args is None:
982  return None
983  return self._meta_args_meta_args.meta_args_args
984 
985  @meta_args.setter
986  def meta_args(self, values):
987  '''
988  :param values: set the meta_args metadata to the \
989  supplied list of values.
990  :type values: List[:py:class:`psyclone.domain.lfric.kernel.\
991  CommonArg`]
992 
993  '''
994  self._meta_args_meta_args = MetaArgsMetadata(values)
995 
996  @property
997  def meta_funcs(self):
998  '''
999  :returns: a list of meta_funcs metadata values.
1000  :rtype: Optional[List[:py:class:`psyclone.domain.lfric.kernel.\
1001  MetaFuncsArgMetadata`]]
1002 
1003  '''
1004  if self._meta_funcs_meta_funcs is None:
1005  return None
1006  return self._meta_funcs_meta_funcs.meta_funcs_args
1007 
1008  @meta_funcs.setter
1009  def meta_funcs(self, values):
1010  '''
1011  :param values: set the meta_funcs metadata to the \
1012  supplied list of values.
1013  :type values: List[:py:class:`psyclone.domain.lfric.kernel.\
1014  MetaFuncsArgMetadata`]
1015 
1016  '''
1017  self._meta_funcs_meta_funcs = MetaFuncsMetadata(values)
1018 
1019  @property
1020  def meta_ref_element(self):
1021  '''
1022  :returns: a list of meta_reference_element metadata values.
1023  :rtype: Optional[List[:py:class:`psyclone.domain.lfric.kernel.\
1024  MetaRefElementArgMetadata`]]
1025 
1026  '''
1027  if self._meta_ref_element_meta_ref_element is None:
1028  return None
1029  return self._meta_ref_element_meta_ref_element.meta_ref_element_args
1030 
1031  @meta_ref_element.setter
1032  def meta_ref_element(self, values):
1033  '''
1034  :param values: set the meta_funcs metadata to the \
1035  supplied list of values.
1036  :type values: List[:py:class:`psyclone.domain.lfric.kernel.\
1037  MetaRefElementArgMetadata`]
1038 
1039  '''
1040  self._meta_ref_element_meta_ref_element = MetaRefElementMetadata(values)
1041 
1042  @property
1043  def meta_mesh(self):
1044  '''
1045  :returns: a list of meta_mesh metadata values.
1046  :rtype: Optional[List[:py:class:`psyclone.domain.lfric.kernel.\
1047  MetaMeshArgMetadata`]]
1048 
1049  '''
1050  if self._meta_mesh_meta_mesh is None:
1051  return None
1052  return self._meta_mesh_meta_mesh.meta_mesh_args
1053 
1054  @meta_mesh.setter
1055  def meta_mesh(self, values):
1056  '''
1057  :param values: set the meta_mesh metadata to the \
1058  supplied list of values.
1059  :type values: List[:py:class:`psyclone.domain.lfric.kernel.\
1060  MetaMeshArgMetadata`]
1061 
1062  '''
1063  self._meta_mesh_meta_mesh = MetaMeshMetadata(values)
1064 
1065  @property
1066  def procedure_name(self):
1067  '''
1068  :returns: the kernel procedure name specified by the metadata.
1069  :rtype: str
1070  '''
1071  return self._procedure_name_procedure_name
1072 
1073  @procedure_name.setter
1074  def procedure_name(self, value):
1075  '''
1076  :param Optional[str] value: set the kernel procedure name in the \
1077  metadata to the specified value.
1078 
1079  :raises ValueError: if the metadata has an invalid value.
1080 
1081  '''
1082  if value:
1083  try:
1084  FortranReader.validate_name(value)
1085  except (ValueError, TypeError) as err:
1086  raise ValueError(
1087  f"Expected procedure_name to be a valid Fortran name but "
1088  f"found '{value}'.") from err
1089  self._procedure_name_procedure_name = value
1090 
1091  @property
1092  def name(self):
1093  '''
1094  :returns: the name of the symbol that will contain the \
1095  metadata when lowering.
1096  :rtype: str
1097 
1098  '''
1099  return self._name_name
1100 
1101  @name.setter
1102  def name(self, value):
1103  '''
1104  :param str value: set the name of the symbol that will contain \
1105  the metadata when lowering.
1106 
1107  :raises ValueError: if the name is not valid.
1108 
1109  '''
1110  FortranReader.validate_name(value)
1111  self._name_name = value
1112 
1113  def meta_args_get(self, types):
1114  '''Return a list of meta_args entries with names that match the
1115  'types' argument.
1116 
1117  :param types: a meta_arg type or list of meta_arg types.
1118  :type names: :py:class:`psyclone.domain.lfric.kernel.\
1119  CommonMetaArgMetadata` or List[:py:class:`psyclone.domain.\
1120  lfric.kernel.CommonMetaArgMetadata`]
1121 
1122  :returns: a list of meta_args entries.
1123  :rtype: List[
1124  py:class:`psyclone.domain.lfric.kernel.CommonMetaArgMetadata`]
1125 
1126  :raises TypeError: if the types argument is not of the correct type.
1127 
1128  '''
1129  if not (isinstance(types, list) or
1130  (inspect.isclass(types) and
1131  issubclass(types, CommonMetaArgMetadata))):
1132  raise TypeError(
1133  f"Expected a subclass of CommonMetaArgMetadata or a list for "
1134  f"the 'types' argument, but found '{type(types).__name__}'.")
1135  if isinstance(types, list):
1136  my_types = types
1137  else:
1138  my_types = [types]
1139  for my_type in my_types:
1140  if not (inspect.isclass(my_type) and
1141  issubclass(my_type, CommonMetaArgMetadata)):
1142  raise TypeError(
1143  f"Expected list entries in the 'types' argument to be "
1144  f"subclasses of CommonMetaArgMetadata, but found "
1145  f"'{type(my_type).__name__}'.")
1146  if not self.meta_argsmeta_argsmeta_args:
1147  return []
1148  return [meta_arg for meta_arg in self.meta_argsmeta_argsmeta_args
1149  if type(meta_arg) in my_types]
1150 
1151  def field_meta_args_on_fs(self, arg_types, function_space):
1152  '''Utility function to return any field (plus field vector, intergrid
1153  or intergrid vector) meta_args in metadata that have the same
1154  type as those specified in arg_types and are on the function
1155  space specified in function_space.
1156 
1157  :param arg_types: meta_arg classes indicating which meta_arg \
1158  arguments to check.
1159  :type arg_types: \
1160  :py:class:`psyclone.domain.lfric.kernel.CommonMetaArgMetadata` or \
1161  List[ \
1162  :py:class:`psyclone.domain.lfric.kernel.CommonMetaArgMetadata`]
1163  :param str function_space: the specified function space.
1164 
1165  :returns: a list of meta_args.
1166  :type arg_types: List[ \
1167  :py:class:`psyclone.domain.lfric.kernel.CommonMetaArgMetadata`]
1168 
1169  '''
1170  return [arg for arg in self.meta_args_getmeta_args_get(arg_types)
1171  if arg.function_space == function_space]
1172 
1173  def operator_meta_args_on_fs(self, arg_types, function_space):
1174  '''Utility function to return any operator meta_args in metadata that
1175  have the same type as those specified in arg_types and their
1176  from or to function spaces are the same as the function space
1177  specified in function_space.
1178 
1179  :param arg_types: meta_arg classes indicating which meta_arg \
1180  arguments to check.
1181  :type arg_types: \
1182  :py:class:`psyclone.domain.lfric.kernel.CommonMetaArgMetadata` or \
1183  List[ \
1184  :py:class:`psyclone.domain.lfric.kernel.CommonMetaArgMetadata`]
1185  :param str function_space: the specified function space.
1186 
1187  :returns: a list of meta_args.
1188  :type arg_types: List[ \
1189  :py:class:`psyclone.domain.lfric.kernel.CommonMetaArgMetadata`]
1190 
1191  '''
1192  return [arg for arg in self.meta_args_getmeta_args_get(arg_types)
1193  if function_space in [arg.function_space_to,
1194  arg.function_space_from]]
1195 
1196 
1197 __all__ = ["LFRicKernelMetadata"]