39 ''' This module implements the PSyclone LFRic API by capturing the Kernel
40 subroutine code and metadata describing the subroutine for the LFRic API.'''
42 from collections
import OrderedDict
45 from psyclone
import psyGen
58 Captures the Kernel subroutine code and metadata describing
59 the subroutine for the LFRic API.
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.
65 :raises ParseError: if the metadata does not conform to the
66 rules for the LFRic API.
69 def __init__(self, ast, name=None):
72 KernelType.__init__(self, ast, name=name)
100 for idx, arg_type
in enumerate(self.
_inits_inits):
105 type_declns = [cline
for cline
in self.
_ktype_ktype.content
if
106 isinstance(cline, fparser.one.typedecl_statements.Type)]
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")
123 arg_fs_names.extend(descriptor.function_spaces)
125 need_evaluator =
False
126 for func_type
in func_types:
128 fs_name = descriptor.function_space_name
131 if fs_name
not in arg_fs_names:
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)
140 f
"In the LFRic API function spaces specified in "
141 f
"'meta_funcs' must be unique, but '{fs_name}' is "
147 for op_name
in descriptor.operator_names:
148 if op_name
in const.VALID_EVALUATOR_NAMES:
149 need_evaluator =
True
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}'")
159 if not shape_set.issubset(
160 set(const.VALID_EVALUATOR_SHAPES)):
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}'")
174 if not _targets
and \
177 write_accesses = AccessType.all_write_accesses()
179 arg_accesses=write_accesses)
182 _targets = [arg.function_spaces[0]
for arg
in write_args]
184 for target
in _targets:
198 def _validate(self, need_evaluator):
200 Check that the metadata conforms to LFRic rules for a user-provided
201 kernel or a built-in.
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
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').
232 if arg.access != AccessType.READ:
235 if arg.argument_type
in const.VALID_FIELD_NAMES \
236 and arg.function_spaces[0]
in \
237 const.READ_ONLY_FUNCTION_SPACES:
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]}'.")
245 if self.
namename
not in BUILTIN_MAP
and \
246 arg.argument_type
in const.VALID_SCALAR_NAMES:
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.")
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}'.")
259 if not need_evaluator
and self.
_eval_shapes_eval_shapes:
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")
266 if not need_evaluator:
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 "
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}")
283 fs_list.update(arg.function_spaces)
286 if eval_fs
not in fs_list:
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.")
295 arg_types=[
"gh_operator"])
298 if (arg.argument_type
in const.VALID_FIELD_NAMES
299 and arg.data_type !=
"gh_real"):
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.")
310 arg_types=[
"gh_columnwise_operator"])
320 def _validate_inter_grid(self):
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.
326 :raises: ParseError: if metadata breaks inter-grid rules.
332 mesh_dict = OrderedDict()
338 non_field_arg_types = set()
342 if arg.argument_type
in const.VALID_FIELD_NAMES:
346 if arg.mesh
in mesh_dict:
347 mesh_dict[arg.mesh].append(arg.function_space)
349 mesh_dict[arg.mesh] = [arg.function_space]
357 non_field_arg_types.add(arg.argument_type)
359 mesh_list = mesh_dict.keys()
365 if len(const.VALID_MESH_TYPES) != 2:
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):
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]}")
382 if non_field_arg_types:
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]}")
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.")
398 for mesh
in mesh_dict:
399 fs_sets.append(set(mesh_dict[mesh]))
402 fs_common = fs_sets[0] & fs_sets[1]
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]}.")
413 def _identify_cma_op(self, cwise_ops):
415 Identify and return the type of CMA-operator-related operation
416 this kernel performs (one of "assemble", "apply" or "matrix-matrix")
418 :param cwise_ops: all column-wise operator arguments in a kernel.
419 :type cwise_ops: list of str
421 :returns: the type of CMA-operator-related operation that this
425 :raises ParseError: if the kernel metadata does not conform to the
426 LFRic rules for a kernel with a CMA operator.
433 if arg.vector_size > 1:
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.")
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.")
445 if (arg.argument_type
in const.VALID_FIELD_NAMES
and
446 arg.data_type !=
"gh_real"):
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.")
455 for cop
in cwise_ops:
456 if cop.access
in AccessType.all_write_accesses():
464 if len(cwise_ops) != 1:
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 "
470 cma_op = cwise_ops[0]
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)} "
478 farg_read = psyGen.args_filter(
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(
485 arg_types=const.VALID_FIELD_NAMES,
486 arg_accesses=write_accesses)
487 if len(farg_read) != 1:
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 "
492 if len(farg_write) != 1:
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 "
498 if farg_read[0].function_space != cma_op.function_space_from:
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:
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}').")
521 write_accesses = AccessType.all_write_accesses()
523 arg_accesses=write_accesses)
524 if len(write_args) > 1:
528 for arg
in write_args[:]:
529 if arg.argument_type ==
'gh_columnwise_operator':
530 write_args.remove(arg)
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:
541 lma_read_ops = psyGen.args_filter(
543 arg_types=[
"gh_operator"],
544 arg_accesses=[AccessType.READ])
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.")
554 scalar_args = psyGen.args_filter(
556 arg_types=const.VALID_SCALAR_NAMES)
557 if (len(scalar_args) + len(cwise_ops)) != \
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]}."
565 return "matrix-matrix"
567 f
"An LFRic kernel cannot update more than one CMA (column-wise) "
568 f
"operator but kernel '{self.name}' updates {write_count}.")
570 def _validate_operates_on_domain(self, need_evaluator):
572 Check whether a kernel that has operates_on == domain obeys
573 the rules for the LFRic API.
575 :raises ParseError: if the kernel metadata does not obey the rules
576 for an LFRic kernel with operates_on = domain.
584 valid_arg_types = const.VALID_SCALAR_NAMES + const.VALID_FIELD_NAMES
586 if arg.argument_type
not in valid_arg_types:
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}'")
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 "
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.")
607 if self.
meshmesh.properties:
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.")
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.")
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. '''
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 '''
639 Returns the shape(s) of evaluator required by this kernel or an
640 empty string if none.
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.
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.
658 :return: list of the names of the function spaces (as they appear in
659 kernel metadata) upon which any evaluator must be provided.
667 Returns whether or not this is an inter-grid kernel.
669 :return: True if kernel is an inter-grid kernel, False otherwise
678 __all__ = [
'LFRicKernMetadata']
def func_descriptors(self)
def _validate_inter_grid(self)
def _validate_operates_on_domain(self, need_evaluator)
def _identify_cma_op(self, cwise_ops)
def _validate(self, need_evaluator)
def get_integer_array(self, name)
def get_integer_variable(self, name)