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

1from pySDC.core.errors import DataError 

2 

3 

4class MultiComponentContainer(object): 

5 r""" 

6 Datatype with multiple components, each one an object of its own. 

7 

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. 

11 

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: 

14 

15 ``` 

16 class rhs_fenics_mesh(MultiComponentContainer): 

17 components = ['impl', 'expl'] 

18 component_type = fenics_mesh 

19 ``` 

20 

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 """ 

25 

26 components = [] 

27 component_type = None 

28 

29 def __init__(self, init, val=0.0): 

30 """ 

31 Initialization routine 

32 

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)) 

43 

44 def _apply(self, other, operation): 

45 """ 

46 Apply ``operation`` to each component of this datatype and the matching one of ``other``. 

47 

48 Args: 

49 other: another instance of this datatype 

50 operation (callable): takes the two components and returns the new one 

51 

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)}') 

57 

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 

62 

63 def __add__(self, other): 

64 """ 

65 Overloading the addition operator 

66 

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) 

75 

76 def __sub__(self, other): 

77 """ 

78 Overloading the subtraction operator 

79 

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) 

88 

89 def __rmul__(self, other): 

90 """ 

91 Overloading the right multiply by factor operator 

92 

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