Coverage for src/methodsnm/fe.py: 83%

52 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-27 13:22 +0000

1from abc import ABC, abstractmethod 

2import numpy as np 

3from numpy import array 

4from methodsnm.diff import AD 

5 

6class FE(ABC): 

7 """ 

8 This is the base class for all **scalar** finite element classes.  

9 It provides a template for the evaluate method. 

10 """ 

11 ndof = None # number of degrees of freedom 

12 order = None # polynomial order 

13 eltype = None # e.g. "segment", "triangle", ... 

14 dim = None # dimension of the domain 

15 def __init__(self): 

16 pass 

17 

18 @abstractmethod 

19 def _evaluate_id(self, ip): 

20 """ 

21 Evaluates the finite element at the given integration point. 

22 

23 Parameters: 

24 ip (numpy.array): The integration point at which to evaluate the finite element. 

25 

26 Returns: 

27 numpy.array: The values of the finite element basis fcts. at the given integration point. 

28 """ 

29 raise Exception("Not implemented - Base class should not be used") 

30 

31 def _evaluate_id_array(self, ips): 

32 """ 

33 Evaluates the finite element at multiple integration points at once. 

34 Base class implementation is a simple loop over the integration points. 

35 Performance gains can only be obtained by overwriting this method. 

36 

37 Parameters: 

38 ips (numpy.array): The integration point at which to evaluate the finite element. 

39 

40 Returns: 

41 numpy.ndarray: The values of the finite element at the given integration points. 

42 shape: (len(ips), ndof) 

43 """ 

44 ret = np.empty((len(ips), self.ndof )) 

45 for i in range(len(ips)): 

46 ret[i,:] = self._evaluate_id(ips[i]) 

47 return ret 

48 

49 def _evaluate_deriv_array(self, ips): 

50 """ 

51 Evaluates the derivative of finite element at multiple integration points at once. 

52 Base class implementation is a simple loop over the integration points. 

53 Performance gains can only be obtained by overwriting this method. 

54 

55 Parameters: 

56 ips (numpy.array): The integration point at which to evaluate the finite element. 

57 

58 Returns: 

59 numpy.ndarray: The values of the finite element at the given integration points. 

60 shape: (len(ips), dim, ndof) 

61 """ 

62 ret = np.empty((len(ips), self.dim, self.ndof)) 

63 for i in range(len(ips)): 

64 ret[i,:,:] = self._evaluate_deriv(ips[i]) 

65 return ret 

66 

67 @abstractmethod 

68 def _evaluate_deriv(self, ip) -> np.ndarray: 

69 """ 

70 Evaluates the derivative of a finite element at the given integration point. 

71 

72 Parameters: 

73 ip (numpy.array): The integration point at which to evaluate the finite element. 

74 

75 Returns: 

76 numpy.array: The values of the derivative of the finite element basis fcts. at the given integration point. 

77 shape (dim, ndof) 

78 """ 

79 raise Exception("Not implemented - Base class should not be used") 

80 

81 def evaluate(self, ip, deriv=False): 

82 """ 

83 Evaluates the (derivative of) finite element at given integration point(s). 

84 

85 Parameters: 

86 ip (numpy.array): The integration point(s) at which to evaluate the finite element. 

87 deriv (bool): Whether to evaluate the derivative of the finite element (or identity). 

88 

89 Returns: 

90 numpy.array: The values of the finite element basis fcts. at the given integration point. 

91 shape: (ndof) (for single ip)  

92 or (dim, ndof) (for single ip and deriv = True) 

93 or (len(ip), ndof) (for multiple ips) 

94 or (len(ip), dim, ndof) (for multiple ips and deriv = True) 

95 """ 

96 if isinstance(ip, np.ndarray): 

97 if ip.ndim == 1: 

98 if deriv: 

99 return self._evaluate_deriv(ip) 

100 else: 

101 return self._evaluate_id(ip) 

102 else: 

103 if deriv: 

104 return self._evaluate_deriv_array(ip) 

105 else: 

106 return self._evaluate_id_array(ip) 

107 else: 

108 raise Exception("Invalid input") 

109 

110 

111 

112class Lagrange_FE(FE): 

113 """ 

114 This class represents a Lagrange finite element. 

115 A Lagrange finite element associates the dofs with the nodes of the element. 

116 """ 

117 nodes = None 

118 

119 def __str__(self): 

120 return f"Lagrange-FE-obj(order={self.order},nodes={self.ndof})" 

121 

122 

123 

124class Node_FE(FE): 

125 """ 

126 FE for a point (node). 

127 """ 

128 

129 def __init__(self): 

130 self.eltype = "point" 

131 self.dim = 0 

132 self.ndof = 1 

133 

134 def _evaluate_id(self, ip): 

135 return np.ones((1,)) 

136 

137 def _evaluate_id_array(self, ip): 

138 return np.ones((len(ip),1)) 

139 

140 def _evaluate_deriv(self, ip): 

141 raise Exception("Derivative of node FE should not be called")