Coverage for pyxfem/core/nbkalgebra2d.py: 0%

69 statements  

« prev     ^ index     » next       coverage.py v7.17.0, created at 2026-10-24 07:17 +0000

1#!/usr/bin/env python3 

2# -*- coding: utf-8 -* 

3# pylint: disable=invalid-name 

4r""" 

5Created march 2026 

6@author: nchevaug 

7 

82D Algebraic Kernel for High-Performance Numba JIT Compilation. 

9 

10Philosophy & Architecture: 

11This module provides fundamental 2D vector and symmetric tensor operations 

12specifically tailored for Numba's `nopython` mode (`@njit`). 

13 

14To achieve maximum performance and avoid the massive overhead of heap 

15allocations inside hot loops (e.g., cell or vertex iterations), this module 

16STRICTLY avoids NumPy arrays for small local variables. Instead, it relies 

17entirely on Python `Tuple` of floats. 

18 

19Why Tuples? 

20When compiled with Numba and LLVM, homogenous tuples of primitive types 

21(like `Tuple[float, float]`) are not treated as standard Python objects. 

22They are lowered directly into C-style structs passed by value, which the 

23LLVM backend maps directly into the processor's hardware registers. 

24This guarantees zero-allocation overhead, completely bypasses the Python 

25garbage collector, and allows aggressive compiler optimizations like loop 

26unrolling and SIMD vectorization (enhanced by `fastmath=True`). 

27 

28Storage Convention: 

29- Vectors (Vec2) are stored as 2-tuples: `(x, y)` 

30- Symmetric 2x2 Matrices (Sy22) are stored as 3-tuples: `(A00, A01, A11)`. 

31 This exploits the symmetry ($A_{01} = A_{10}$) to save register space 

32 and reduce floating-point operations. 

33""" 

34 

35from typing import Tuple 

36from pyxfem.core.config import USE_NUMBA 

37 

38# ========================================== 

39# STATIC ANALYSIS TYPE ALIASES (Type Hinting) 

40# ========================================== 

41# These types guide the IDE (VS Code, Pylance, MyPy) for autocompletion 

42# and static type checking. They are ignored by Numba at runtime. 

43# Vec2 = Tuple[float, float] 

44# Sy22 = Tuple[float, float, float] 

45# 

46 

47if USE_NUMBA: 

48 from math import sqrt 

49 import numba 

50 import numba.core.types as nct 

51 # pylint: disable=ungrouped-imports 

52 from pyxfem.core.config import FASTMATH, nbfloat, fzero,npfloat 

53 # ========================================== 

54 # EXPLICIT NUMBA SIGNATURE TYPES 

55 # ========================================== 

56 sy22_t = nct.UniTuple(nbfloat, 3) 

57 vec2_t = nct.UniTuple(nbfloat, 2) 

58 # Vec2 = vec2_t 

59 # Sy22 = sy22_t 

60 # Vec2 = Tuple[nbfloat, nbfloat] 

61 # Sy22 = Tuple[nbfloat, nbfloat, nbfloat] 

62 Vec2 = Tuple[npfloat, npfloat] 

63 Sy22 = Tuple[npfloat, npfloat, npfloat] 

64 Ge22 = Tuple[npfloat, npfloat, npfloat, npfloat] 

65 # ========================================== 

66 # constants 

67 # ========================================== 

68 vec2_zero: Vec2 = (fzero, fzero) 

69 sy22_zero: Sy22 = (fzero, fzero, fzero) 

70 

71 # ========================================== 

72 # 2D VECTOR OPERATIONS 

73 # ========================================== 

74 @numba.njit(inline='always', fastmath = FASTMATH) 

75 def set_vec2(index, array, v): 

76 array[index, 0], array[index, 1] = v[0], v[1] 

77 

78 @numba.njit(cache=True, fastmath = FASTMATH) 

79 def vec2_add(a: Vec2, b: Vec2) -> Vec2: 

80 r""" 

81 Adds two 2D vectors. 

82 Math: $a + b$ 

83 """ 

84 return (a[0] + b[0], a[1] + b[1]) 

85 

86 @numba.njit(cache=True, fastmath = FASTMATH) 

87 def vec2_add3(a: Vec2, b: Vec2, c: Vec2) -> Vec2: 

88 r""" 

89 Adds three 2D vectors. 

90 Math: $a + b + c$ 

91 """ 

92 return (a[0] + b[0]+c[0], a[1] + b[1]+c[1]) 

93 

94 @numba.njit(cache=True, fastmath = FASTMATH) 

95 def vec2_scal(alpha: npfloat, x: Vec2) -> Vec2: 

96 r""" 

97 Multiplies a 2D vector by a scalar. 

98 Math: $\alpha x$ 

99 """ 

100 return (alpha*x[0], alpha*x[1]) 

101 @numba.njit(cache=True, fastmath = FASTMATH) 

102 

103 def vec2_sub(a: Vec2, b: Vec2) -> Vec2: 

104 r""" 

105 Subtracts one 2D vector from another. 

106 Math: $a - b$ 

107 """ 

108 return (a[0] - b[0], a[1] - b[1]) 

109 

110 @numba.njit(cache=True, fastmath = FASTMATH) 

111 def vec2_norm(a: Vec2) -> npfloat: 

112 r""" 

113 Computes the Euclidean (L2) norm of a 2D vector. 

114 Math: $\|a\| = \sqrt{a_0^2 + a_1^2}$ 

115 """ 

116 return npfloat(sqrt((a[0]**2 +a[1]**2))) 

117 

118 @numba.njit(cache=True, fastmath = FASTMATH) 

119 def vec2_dot(a: Vec2, b: Vec2) -> npfloat: 

120 r""" 

121 Computes the dot product of two 2D vectors. 

122 Math: $a \cdot b = a_0 b_0 + a_1 b_1$ 

123 """ 

124 return a[0]*b[0] +a[1]*b[1] 

125 

126 @numba.njit(cache=True, fastmath = FASTMATH) 

127 def vec2_square(a: Vec2) -> npfloat: 

128 r""" 

129 Computes the squared norm of a 2D vector. 

130 Math: $\|a\|^2 = a_0^2 + a_1^2$ 

131 """ 

132 return a[0]**2 +a[1]**2 

133 

134 # ========================================== 

135 # 2X2 SYMMETRIC TENSOR OPERATIONS 

136 # ========================================== 

137 

138 @numba.njit(cache=True, fastmath = FASTMATH) 

139 def sy22_mv(A: Sy22, x: Vec2) -> Vec2: 

140 r""" 

141 Performs a matrix-vector multiplication for a symmetric 2x2 matrix. 

142 Math: $A x$ 

143 Note: The matrix is implicitly reconstructed from its components $(A_{00}, A_{01}, A_{11})$. 

144 """ 

145 A00, A01, A11 = A[0], A[1], A[2] 

146 x0, x1 = x[0], x[1] 

147 return (A00*x0 + A01*x1, A01*x0+ A11*x1) 

148 

149 @numba.njit(cache=True, fastmath = FASTMATH) 

150 def sy22_add(A: Sy22, B: Sy22) -> Sy22: 

151 r""" 

152 Adds two symmetric 2x2 matrices. 

153 Math: $A + B$ 

154 """ 

155 return (A[0]+B[0], A[1]+B[1], A[2]+B[2]) 

156 

157 @numba.njit(cache=True, fastmath = FASTMATH) 

158 def sy22_r(alpha: npfloat, x: Vec2) -> Sy22: 

159 r""" 

160 Computes the symmetric rank-1 update (dyadic product). 

161 Math: $\alpha (x \otimes x)$ 

162 """ 

163 A00 = alpha * x[0] * x[0] 

164 A01 = alpha * x[0] * x[1] 

165 A11 = alpha * x[1] * x[1] 

166 return (A00, A01, A11) 

167 

168 @numba.njit(cache=True, fastmath = FASTMATH) 

169 def sy22_r2(alpha: npfloat, x: Vec2, y: Vec2) -> Sy22: 

170 r""" 

171 Computes the scalar-penalized symmetric rank-2 update. 

172 Math: $\alpha (x \otimes y + y \otimes x)$ 

173 """ 

174 A00 = alpha*2*x[0] * y[0] 

175 A01 = alpha*(x[0] * y[1] + y[0] * x[1]) 

176 A11 = alpha*2*(x[1] * y[1]) 

177 return (A00, A01, A11) 

178 @numba.njit(cache=True, fastmath = FASTMATH) 

179 

180 def sy22_trace(A: Sy22) -> npfloat: 

181 r""" 

182 Computes the trace of a symmetric 2x2 matrix. 

183 Math: $\text{Tr}(A) = A_{00} + A_{11}$ 

184 """ 

185 return A[0] + A[2] 

186 

187 @numba.njit(cache=True, fastmath = FASTMATH) 

188 def sy22_smv(A: Sy22, b: Vec2) -> Vec2: 

189 r""" 

190 Solves the linear system $A x = b$ for a symmetric 2x2 matrix 

191 using explicit Cramer's rule inversion. 

192 Math: $x = A^{-1} b$ 

193 """ 

194 A00, A01, A11 = A[0], A[1], A[2] 

195 b0, b1 = b[0], b[1] 

196 detA = A00*A11 - A01*A01 

197 x0 = (A11*b0 - A01*b1)/detA 

198 x1 = (A00*b1 - A01*b0)/detA 

199 return (x0, x1) 

200 

201 # # The two following function where not used yet :need to b checked 

202 # # for efficiency 

203 # @numba.njit(cache=True) 

204 # def gemm22_numba(alpha, A, B, beta, C): 

205 # C[0,0] = beta*C[0,0] + alpha*(A[0,0]*B[0,0] + A[0,1]*B[1,0]) 

206 # C[0,1] = beta*C[0,1] + alpha*(A[0,0]*B[0,1] + A[0,1]*B[1,1]) 

207 # C[1,0] = beta*C[1,0] + alpha*(A[1,0]*B[0,0] + A[1,1]*B[1,0]) 

208 # C[1,1] = beta*C[1,1] + alpha*(A[1,0]*B[0,1] + A[1,1]*B[1,1]) 

209 # @numba.njit(cache=True) 

210 # def gemv22_numba(A,x,y, beta =fzero): 

211 # x0, x1= x[0], x[1] 

212 # y[0] = beta*y[0] + A[0,0]*x0 + A[0,1]*x1 

213 # y[1] = beta*y[1] + A[1,0]*x0 + A[1,1]*x1