38 '''This module contains a singleton class that manages LFRic types. '''
41 from collections
import namedtuple
42 from dataclasses
import dataclass
49 ImportInterface, INTEGER_TYPE, ScalarType)
53 '''This class implements a singleton that manages LFRic types.
54 Using the 'call' interface, you can query the data type for
57 >>> from psyclone.configuration import Config
58 >>> from psyclone.domain.lfric import LFRicTypes
59 >>> config = Config.get()
60 >>> num_dofs_class = LFRicTypes("NumberOfUniqueDofsDataSymbol")
61 >>> my_var = num_dofs_class("my_num_dofs")
62 >>> print(my_var.name)
65 It uses the __new__ function to implement the access to the internal
66 dictionary. This is done to minimise the required code for getting a
67 value, e. g. compared with ``LFRicTypes.get()("something")``, or
68 ``LFRicType.get("something")``.
81 '''The class creation function __new__ is used to actually provide
82 the object the user is querying. It is well documented that __new__
83 can return a different instance. This function will first make sure
84 that the static internal dictionary is initialised (so it acts like
85 a singleton in that regard). Then it will return the value the user
88 :param str name: the name to query for.
90 :returns: the corresponding object, which can be a class or an \
92 :rtype: object (various types)
94 :raises InternalError: if there specified name is not a name for \
95 an object managed here.
98 if not LFRicTypes._name_to_class:
102 return LFRicTypes._name_to_class[name]
103 except KeyError
as err:
104 raise InternalError(f
"Unknown LFRic type '{name}'. Valid values "
105 f
"are {LFRicTypes._name_to_class.keys()}") \
110 '''This function is only here to trick pylint into thinking that
111 the object returned from __new__ is callable, meaning that code like:
112 ``LFRicTypes("LFRicIntegerScalarDataType")()`` does not trigger
113 a pylint warning about not being callable.
119 '''This method constructs the required classes and instances, and
120 puts them into the dictionary.
124 LFRicTypes._name_to_class = {}
126 LFRicTypes._create_precision_from_const_module()
127 LFRicTypes._create_generic_scalars()
128 LFRicTypes._create_lfric_dimension()
129 LFRicTypes._create_specific_scalars()
130 LFRicTypes._create_fields()
135 for intrinsic
in const.VALID_FIELD_INTRINSIC_TYPES:
136 name = f
"{intrinsic.title()}VectorFieldDataSymbol"
137 baseclass =
LFRicTypes(f
"{intrinsic.title()}FieldDataSymbol")
138 LFRicTypes._name_to_class[name] = type(name, (baseclass, ), {})
142 def _create_precision_from_const_module():
143 '''This function implements all precisions defined in the
144 `dynamo0.3` (LFRic) domain. It adds "constants_mod" as
145 ContainerSymbol. The names are added to the global mapping.
152 api_config = Config.get().api_conf(
"dynamo0.3")
154 lfric_kinds = list(api_config.precision_map.keys())
156 constants_mod = lfric_const.UTILITIES_MOD_MAP[
"constants"][
"module"]
157 Module = namedtuple(
'Module', [
"name",
"vars"])
158 modules = [Module(constants_mod, lfric_kinds)]
161 for module_info
in modules:
162 module_name = module_info.name.lower()
164 LFRicTypes._name_to_class[module_name] = \
165 ContainerSymbol(module_info.name)
168 for module_var
in module_info.vars:
169 var_name = module_var.upper()
170 interface = ImportInterface(
LFRicTypes(module_name))
171 LFRicTypes._name_to_class[var_name] = \
172 DataSymbol(module_var, INTEGER_TYPE, interface=interface)
176 def _create_generic_scalars():
177 '''This function adds the generic data types and symbols for
178 integer, real, and booleans to the global mapping.
181 GenericScalar = namedtuple(
'GenericScalar', [
"name",
"intrinsic",
183 generic_scalar_datatypes = [
184 GenericScalar(
"LFRicIntegerScalar", ScalarType.Intrinsic.INTEGER,
186 GenericScalar(
"LFRicRealScalar", ScalarType.Intrinsic.REAL,
188 GenericScalar(
"LFRicLogicalScalar", ScalarType.Intrinsic.BOOLEAN,
192 for info
in generic_scalar_datatypes:
195 type_name = f
"{info.name}DataType"
196 LFRicTypes._create_generic_scalar_data_type(type_name,
201 symbol_name = f
"{info.name}DataSymbol"
202 LFRicTypes._create_generic_scalar_data_symbol(symbol_name,
207 def _create_generic_scalar_data_type(name, intrinsic, default_precision):
208 '''This function creates a generic scalar data type class and adds
209 it to the global mapping.
211 :param str name: name of the data type to create.
212 :param intrinsic: the intrinsic type to use.
214 :py:class:`pyclone.psyir.datatypes.ScalarType.Intrinsic`
215 :param str default_precision: the default precision this class \
216 should have if the precision is not specified.
220 def __my_generic_scalar_type_init__(self, precision=None):
222 precision = self.default_precision
223 ScalarType.__init__(self, self.intrinsic, precision)
235 LFRicTypes._name_to_class[name] = \
236 type(name, (ScalarType, ),
237 {
"__init__": __my_generic_scalar_type_init__,
238 "intrinsic": intrinsic,
239 "default_precision": default_precision})
243 def _create_generic_scalar_data_symbol(name, type_class):
244 '''This function creates a data symbol class with the specified name
245 and data type, and adds it to the global mapping.
247 :param str name: the name of the class to creates
248 :param type_class: the data type for the symbol
249 :type type_class: py:class:`pyclone.psyir.datatypes.ScalarType`
253 def __my_generic_scalar_symbol_init__(self, name, precision=None,
255 DataSymbol.__init__(self, name,
256 self.type_class(precision=precision),
262 LFRicTypes._name_to_class[name] = \
263 type(name, (DataSymbol, ),
264 {
"__init__": __my_generic_scalar_symbol_init__,
265 "type_class": type_class})
269 def _create_lfric_dimension():
270 '''This function adds the LFRicDimension class to the global mapping,
271 and creates the two instances for scalar and vector dimension.
275 class LFRicDimension(Literal):
276 '''An LFRic-specific scalar integer that captures a literal array
277 dimension which can have a value between 1 and 3, inclusive. This
278 is used for one of the dimensions in basis and differential basis
279 functions and also for the vertical-boundary dofs mask.
281 :param str value: the value of the scalar integer.
283 :raises ValueError: if the supplied value is not '1', '2' or '3'.
287 def __init__(self, value):
288 super().__init__(value,
290 if value
not in [
'1',
'2',
'3']:
291 raise ValueError(f
"An LFRic dimension object must be '1', "
292 f
"'2' or '3', but found '{value}'.")
296 LFRicTypes._name_to_class.update({
297 "LFRicDimension": LFRicDimension,
298 "LFRIC_SCALAR_DIMENSION": LFRicDimension(
"1"),
299 "LFRIC_VERTICAL_BOUNDARIES_DIMENSION": LFRicDimension(
"2"),
300 "LFRIC_VECTOR_DIMENSION": LFRicDimension(
"3")})
304 def _create_specific_scalars():
305 '''This function creates all required specific scalar, which are
306 derived from the corresponding generic classes (e.g.
307 LFRicIntegerScalarData)
316 Scalar = namedtuple(
'Scalar', [
"name",
"generic_type_name",
318 specific_scalar_datatypes = [
319 Scalar(
"CellPosition",
"LFRicIntegerScalarData", []),
320 Scalar(
"MeshHeight",
"LFRicIntegerScalarData", []),
321 Scalar(
"NumberOfCells",
"LFRicIntegerScalarData", []),
322 Scalar(
"NumberOfDofs",
"LFRicIntegerScalarData", [
"fs"]),
323 Scalar(
"NumberOfUniqueDofs",
"LFRicIntegerScalarData", [
"fs"]),
324 Scalar(
"NumberOfFaces",
"LFRicIntegerScalarData", []),
325 Scalar(
"NumberOfEdges",
"LFRicIntegerScalarData", []),
326 Scalar(
"NumberOfQrPointsInXy",
"LFRicIntegerScalarData", []),
327 Scalar(
"NumberOfQrPointsInZ",
"LFRicIntegerScalarData", []),
328 Scalar(
"NumberOfQrPointsInFaces",
"LFRicIntegerScalarData", []),
329 Scalar(
"NumberOfQrPointsInEdges",
"LFRicIntegerScalarData", [])]
331 for info
in specific_scalar_datatypes:
332 type_name = f
"{info.name}DataType"
333 LFRicTypes._name_to_class[type_name] = \
335 (
LFRicTypes(f
"{info.generic_type_name}Type"), ),
338 symbol_name = f
"{info.name}DataSymbol"
339 base_class =
LFRicTypes(f
"{info.generic_type_name}Symbol")
340 LFRicTypes._create_scalar_data_type(symbol_name, base_class,
345 def _create_scalar_data_type(class_name, base_class, parameters):
346 '''This function creates a specific scalar data type with the given
347 name, derived from the specified base class.
349 :param str class_name: name of the class to create.
350 :param base_class: the class on which to base the newly created class.
351 :type base_class: :py:class:`psyclone.psyir.symbols.DataSymbol`
352 :param parameters: additional required arguments of the constructor, \
353 which will be set as attributes in the base class.
354 :type parameters: List[str]
363 def __my_scalar_init__(self, name, *args, **kwargs):
365 for i, arg
in enumerate(args):
366 setattr(self, self.parameters[i], arg)
371 remaining_kwargs = {}
372 for key, value
in kwargs.items():
375 if key
in self.parameters:
376 setattr(self, key, value)
380 remaining_kwargs[key] = value
381 self.base_class.__init__(self, name, **remaining_kwargs)
393 LFRicTypes._name_to_class[class_name] = \
394 type(class_name, (base_class, ),
395 {
"__init__": __my_scalar_init__,
396 "base_class": base_class,
397 "parameters": parameters})
401 def _create_fields():
402 '''This function creates the data symbol and types for LFRic fields.
410 @dataclass(frozen=True)
413 Holds the properties of an LFRic array type, used when generating
414 DataSymbol and DataSymbolType classes.
416 :param name: the base name to use for the datatype and datasymbol.
417 :param scalar_type: the name of the LFRic scalar type that this is
419 :param dims: textual description of each of the dimensions.
420 :param properties: names of additional class properties that should
421 be declared in the generated datasymbol class.
429 Array(
"RealField",
"LFRicRealScalarDataType",
430 [
"number of unique dofs"], [
"fs"]),
431 Array(
"IntegerField",
"LFRicIntegerScalarDataType",
432 [
"number of unique dofs"], [
"fs"]),
433 Array(
"LogicalField",
"LFRicLogicalScalarDataType",
434 [
"number of unique dofs"], [
"fs"])]
452 Array(
"Operator",
"LFRicRealScalarDataType",
453 [
"number of dofs",
"number of dofs",
"number of cells"],
454 [
"fs_from",
"fs_to"]),
455 Array(
"DofMap",
"LFRicIntegerScalarDataType",
456 [
"number of dofs"], [
"fs"]),
457 Array(
"BasisFunctionQrXyoz",
"LFRicRealScalarDataType",
458 [
LFRicTypes(
"LFRicDimension"),
"number of dofs",
459 "number of qr points in xy",
460 "number of qr points in z"], [
"fs"]),
461 Array(
"BasisFunctionQrFace",
"LFRicRealScalarDataType",
462 [
LFRicTypes(
"LFRicDimension"),
"number of dofs",
463 "number of qr points in faces",
464 "number of faces"], [
"fs"]),
465 Array(
"BasisFunctionQrEdge",
"LFRicRealScalarDataType",
466 [
LFRicTypes(
"LFRicDimension"),
"number of dofs",
467 "number of qr points in edges",
468 "number of edges"], [
"fs"]),
469 Array(
"DiffBasisFunctionQrXyoz",
"LFRicRealScalarDataType",
470 [
LFRicTypes(
"LFRicDimension"),
"number of dofs",
471 "number of qr points in xy",
472 "number of qr points in z"], [
"fs"]),
473 Array(
"DiffBasisFunctionQrFace",
"LFRicRealScalarDataType",
474 [
LFRicTypes(
"LFRicDimension"),
"number of dofs",
475 "number of qr points in faces",
476 "number of faces"], [
"fs"]),
477 Array(
"DiffBasisFunctionQrEdge",
"LFRicRealScalarDataType",
478 [
LFRicTypes(
"LFRicDimension"),
"number of dofs",
479 "number of qr points in edges",
"number of edges"], [
"fs"]),
480 Array(
"QrWeightsInXy",
"LFRicRealScalarDataType",
481 [
"number of qr points in xy"], []),
482 Array(
"QrWeightsInZ",
"LFRicRealScalarDataType",
483 [
"number of qr points in z"], []),
484 Array(
"QrWeightsInFaces",
"LFRicRealScalarDataType",
485 [
"number of qr points in faces"], []),
486 Array(
"QrWeightsInEdges",
"LFRicRealScalarDataType",
487 [
"number of qr points in edges"], []),
488 Array(
"VerticalBoundaryDofMask",
"LFRicIntegerScalarDataType",
489 [
"number of dofs",
LFRicTypes(
"LFRicDimension")], [])
492 for array_type
in array_datatypes + field_datatypes:
493 name = f
"{array_type.name}DataType"
494 LFRicTypes._create_array_data_type_class(
495 name, len(array_type.dims),
LFRicTypes(array_type.scalar_type))
498 name = f
"{array_type.name}DataSymbol"
499 LFRicTypes._create_array_data_symbol_class(name, my_datatype_class,
500 array_type.properties)
504 def _create_array_data_type_class(name, num_dims, scalar_type):
505 '''This function create a data type class for the specified field.
507 :param str name: name of the class to create.
508 :param int num_dims: number of dimensions
509 :param scalar_type: the scalar base type for this field.
510 :type scalar_type: :py:class:`psyclone.psyir.datatypes.DataType`
517 def __my_type_init__(self, dims):
519 Constructor for the array data type class.
521 :param list dims: the shape argument for the ArrayType constructor.
524 if len(dims) != self.num_dims:
525 raise TypeError(f
"'{type(self).__name__}' expected the number "
526 f
"of supplied dimensions to be {self.num_dims}"
527 f
" but found {len(dims)}.")
528 ArrayType.__init__(self, self.scalar_class(), dims)
534 LFRicTypes._name_to_class[name] = \
535 type(name, (ArrayType, ),
536 {
"__init__": __my_type_init__,
537 "scalar_class": scalar_type,
538 "num_dims": num_dims})
542 def _create_array_data_symbol_class(name, datatype_class, parameters):
543 '''This function creates an array-data-symbol-class and adds it to
544 the internal type dictionary.
546 :param str name: the name of the class to be created.
547 :param datatype_class: the corresponding data type class.
548 :type datatype_class: :py:class:`psyclone.psyir.datatypes.DataType`
550 :param parameters: the list of additional required properties \
551 to be passed to the constructor.
552 :type parameters: List[str]
556 def __my_symbol_init__(self, name, dims, *args, **kwargs):
557 '''This is the __init__ function for the newly declared array data
558 type classes. It sets the required arguments automatically as
559 attributes of the class.
561 :param str name: the name of the data symbol to create.
562 :param list dims: the shape argument for the ArrayType constructor.
563 :param *args: other required positional parameters.
564 :param **kwargs: other required keyword parameters.
567 for i, arg
in enumerate(args):
568 setattr(self, self.parameters[i], arg)
573 remaining_kwargs = {}
574 for key, value
in kwargs.items():
577 if key
in self.parameters:
578 setattr(self, key, value)
582 remaining_kwargs[key] = value
583 DataSymbol.__init__(self, name, self.datatype_class(dims),
591 LFRicTypes._name_to_class[name] = \
592 type(name, (DataSymbol, ),
593 {
"__init__": __my_symbol_init__,
594 "datatype_class": datatype_class,
595 "parameters": parameters})