Coverage for pySDC/implementations/datatype_classes/container.py: 100%
25 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-25 20:28 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-25 20:28 +0000
1from pySDC.core.errors import DataError
4class MultiComponentContainer(object):
5 r"""
6 Datatype with multiple components, each one an object of its own.
8 This is the counterpart of ``MultiComponentMeshMixin`` for backends whose data cannot simply grow an axis:
9 a FEniCS ``Function`` lives on a ``FunctionSpace`` and a PETSc ``Vec`` on a ``DMDA``, so the components
10 cannot be views into one contiguous array and have to be separate objects instead.
12 To make a specific multi-component datatype, derive from this class, list the components as strings in
13 ``components``, and say what a single component is in ``component_type``. An example:
15 ```
16 class rhs_fenics_mesh(MultiComponentContainer):
17 components = ['impl', 'expl']
18 component_type = fenics_mesh
19 ```
21 Instantiating such a datatype builds one component of ``component_type`` per name, either by copying the
22 components of another instance or by passing ``init`` and ``val`` on to each of them. The arithmetic is
23 applied component by component, so the component type is what decides which operands it accepts.
24 """
26 components = []
27 component_type = None
29 def __init__(self, init, val=0.0):
30 """
31 Initialization routine
33 Args:
34 init: either another instance of this datatype, or whatever ``component_type`` accepts
35 val: value to initialize the components with, if they are not copied
36 """
37 if isinstance(init, type(self)):
38 for name in self.components:
39 setattr(self, name, self.component_type(getattr(init, name)))
40 else:
41 for name in self.components:
42 setattr(self, name, self.component_type(init, val=val))
44 def _apply(self, other, operation):
45 """
46 Apply ``operation`` to each component of this datatype and the matching one of ``other``.
48 Args:
49 other: another instance of this datatype
50 operation (callable): takes the two components and returns the new one
52 Returns:
53 a new instance of this datatype
54 """
55 if not isinstance(other, type(self)):
56 raise DataError(f'Type error: cannot combine {type(other)} with {type(self)}')
58 me = type(self)(self)
59 for name in self.components:
60 setattr(me, name, operation(getattr(self, name), getattr(other, name)))
61 return me
63 def __add__(self, other):
64 """
65 Overloading the addition operator
67 Args:
68 other: datatype of the same type to be added
69 Raises:
70 DataError: if other is not of the same type
71 Returns:
72 sum of caller and other, component by component
73 """
74 return self._apply(other, lambda a, b: a + b)
76 def __sub__(self, other):
77 """
78 Overloading the subtraction operator
80 Args:
81 other: datatype of the same type to be subtracted
82 Raises:
83 DataError: if other is not of the same type
84 Returns:
85 difference between caller and other, component by component
86 """
87 return self._apply(other, lambda a, b: a - b)
89 def __rmul__(self, other):
90 """
91 Overloading the right multiply by factor operator
93 Args:
94 other (float): factor
95 Raises:
96 DataError: if the component type does not accept the factor
97 Returns:
98 copy of the caller scaled by the factor
99 """
100 me = type(self)(self)
101 for name in self.components:
102 setattr(me, name, other * getattr(self, name))
103 return me