proteus 1.9.0
C/C++/Fortran libraries
Loading...
Searching...
No Matches
ifemBasisCoefficients_wrapper.h
Go to the documentation of this file.
1#ifndef IFEM_BASIS_COEFFICIENTS_WRAPPER_H
2#define IFEM_BASIS_COEFFICIENTS_WRAPPER_H
3
4#include <array>
5#include <cmath>
6#include <stdexcept>
7
8namespace proteus
9{
10 // Solve A*x = b for a small, fixed-size N x N system via Gauss-Jordan
11 // elimination with partial pivoting. A is row-major and destroyed.
12 template <int N>
13 inline void solveSmallLinearSystem(double A[N * N], double b[N], double x[N])
14 {
15 for (int col = 0; col < N; col++)
16 {
17 int piv = col;
18 double maxval = fabs(A[col * N + col]);
19 for (int r = col + 1; r < N; r++)
20 {
21 if (fabs(A[r * N + col]) > maxval)
22 {
23 maxval = fabs(A[r * N + col]);
24 piv = r;
25 }
26 }
27 if (piv != col)
28 {
29 for (int j = 0; j < N; j++)
30 std::swap(A[col * N + j], A[piv * N + j]);
31 std::swap(b[col], b[piv]);
32 }
33 const double pivval = A[col * N + col];
34 assert(fabs(pivval) > 1.0e-14 && "Singular system in solveSmallLinearSystem");
35 const double invPiv = 1.0 / pivval;
36 for (int j = col; j < N; j++)
37 A[col * N + j] *= invPiv;
38 b[col] *= invPiv;
39 for (int r = 0; r < N; r++)
40 {
41 if (r == col)
42 continue;
43 const double factor = A[r * N + col];
44 if (factor != 0.0)
45 {
46 for (int j = col; j < N; j++)
47 A[r * N + j] -= factor * A[col * N + j];
48 b[r] -= factor * b[col];
49 }
50 }
51 }
52 for (int i = 0; i < N; i++)
53 x[i] = b[i];
54 }
55
56 // P1 linear basis solver (overload for 3 nodal values)
57 //
58 // Solves the same constraint system as
59 // proteus.ifemBasisCoefficients._solveCoefficients_P1 (nodal
60 // interpolation + interface continuity + flux jump), but numerically:
61 // the constraint matrix depends only on (x0,y0,nx,ny,ma,mb) -- not on
62 // nodal_values -- so this is a plain 6x6 linear solve with no symbolic
63 // differentiation and no Python round-trip.
64 inline std::array<double, 6> solve_ifem_basis_coefficients(
65 int basis_order,
66 double x0,
67 double y0,
68 double nx,
69 double ny,
70 double ma,
71 double mb,
72 double jf,
73 double Jit00,
74 double Jit01,
75 double Jit10,
76 double Jit11,
77 const std::array<double, 3> &nodal_values)
78 {
79 (void)basis_order; // always 1 for this overload
80 const double tx = Jit00 * nx + Jit10 * ny;
81 const double ty = Jit01 * nx + Jit11 * ny;
82
83 // unknowns: [a1,a2,a3,b1,b2,b3]
84 double A[36] = {
85 1., 0., 0., 0., 0., 0.,
86 0., 0., 0., 1., 1., 0.,
87 0., 0., 0., 1., 0., 1.,
88 -1., -x0, 0., 1., x0, 0.,
89 -1., 0., -y0, 1., 0., y0,
90 0., -ma * tx, -ma * ty, 0., mb * tx, mb * ty};
91 double rhs[6] = {nodal_values[0], nodal_values[1], nodal_values[2], 0.0, 0.0, jf};
92
93 double coeffs[6];
94 solveSmallLinearSystem<6>(A, rhs, coeffs);
95
96 std::array<double, 6> out;
97 for (int i = 0; i < 6; ++i)
98 {
99 out[i] = coeffs[i];
100 if (std::isnan(out[i]) || std::isinf(out[i]))
101 throw std::runtime_error("solve_ifem_basis_coefficients (P1) returned NaN or Inf");
102 }
103 return out;
104 }
105
106 // P2 quadratic basis solver (overload for 6 nodal values)
107 //
108 // Solves the same constraint system as
109 // proteus.ifemBasisCoefficients._solveCoefficients_P2 numerically. va/vb
110 // are full 2D quadratics, so all constraints (nodal interpolation,
111 // interface continuity, flux jump, normal-Laplacian jump) reduce to
112 // linear equations in the 12 coefficients with coefficients that are
113 // plain algebraic functions of (x0,y0,nx,ny,ma,mb) -- the "symbolic
114 // differentiation" in the Python version is just picking off constant
115 // polynomial coefficients, so it never needed to happen at runtime.
116 inline std::array<double, 12> solve_ifem_basis_coefficients(
117 int basis_order,
118 double x0,
119 double y0,
120 double nx,
121 double ny,
122 double ma,
123 double mb,
124 double jf,
125 double Jit00,
126 double Jit01,
127 double Jit10,
128 double Jit11,
129 const std::array<double, 6> &nodal_values)
130 {
131 (void)basis_order; // always 2 for this overload
132 assert(!((x0 == 0.0 && y0 == 0.0) || (x0 == 1.0 && y0 == 0.0) || (x0 == 0.0 && y0 == 1.0)) &&
133 "Interface passes through a triangle vertex");
134 assert(!(x0 > 0.5 && y0 <= 0.5) &&
135 "Invalid interface location for quadratic basis functions (should have been flipped upstream)");
136
137 const double tx = Jit00 * nx + Jit10 * ny;
138 const double ty = Jit01 * nx + Jit11 * ny;
139 const double v1 = nodal_values[0], v2 = nodal_values[1], v3 = nodal_values[2],
140 v4 = nodal_values[3], v5 = nodal_values[4], v6 = nodal_values[5];
141
142 // unknowns: [a1,a2,a3,a4,a5,a6, b1,b2,b3,b4,b5,b6]
143 double A[144] = {0.0};
144 double rhs[12] = {0.0};
145
146 // c1: va(0,0) = v1
147 A[0 * 12 + 0] = 1.0;
148 rhs[0] = v1;
149
150 // c2: vb(1,0) = v2 (x^2 term is 1 at x=1, so b5 contributes)
151 A[1 * 12 + 6] = 1.0;
152 A[1 * 12 + 7] = 1.0;
153 A[1 * 12 + 10] = 1.0;
154 rhs[1] = v2;
155
156 // c3: vb(0,1) = v3 (y^2 term is 1 at y=1, so b6 contributes)
157 A[2 * 12 + 6] = 1.0;
158 A[2 * 12 + 8] = 1.0;
159 A[2 * 12 + 11] = 1.0;
160 rhs[2] = v3;
161
162 // c4: vb(1/2,0) = v4 if x0<=1/2, else va(1/2,0) = v4
163 if (x0 <= 0.5)
164 {
165 A[3 * 12 + 6] = 1.0;
166 A[3 * 12 + 7] = 0.5;
167 A[3 * 12 + 10] = 0.25;
168 }
169 else
170 {
171 A[3 * 12 + 0] = 1.0;
172 A[3 * 12 + 1] = 0.5;
173 A[3 * 12 + 4] = 0.25;
174 }
175 rhs[3] = v4;
176
177 // c5: vb(1/2,1/2) = v5
178 A[4 * 12 + 6] = 1.0;
179 A[4 * 12 + 7] = 0.5;
180 A[4 * 12 + 8] = 0.5;
181 A[4 * 12 + 9] = 0.25;
182 A[4 * 12 + 10] = 0.25;
183 A[4 * 12 + 11] = 0.25;
184 rhs[4] = v5;
185
186 // c6: vb(0,1/2) = v6 if y0<=1/2, else va(0,1/2) = v6
187 if (y0 <= 0.5)
188 {
189 A[5 * 12 + 6] = 1.0;
190 A[5 * 12 + 8] = 0.5;
191 A[5 * 12 + 11] = 0.25;
192 }
193 else
194 {
195 A[5 * 12 + 0] = 1.0;
196 A[5 * 12 + 2] = 0.5;
197 A[5 * 12 + 5] = 0.25;
198 }
199 rhs[5] = v6;
200
201 // c7: vb(x0,0) - va(x0,0) = 0
202 A[6 * 12 + 0] = -1.0;
203 A[6 * 12 + 1] = -x0;
204 A[6 * 12 + 4] = -x0 * x0;
205 A[6 * 12 + 6] = 1.0;
206 A[6 * 12 + 7] = x0;
207 A[6 * 12 + 10] = x0 * x0;
208
209 // c8: vb(0,y0) - va(0,y0) = 0
210 A[7 * 12 + 0] = -1.0;
211 A[7 * 12 + 2] = -y0;
212 A[7 * 12 + 5] = -y0 * y0;
213 A[7 * 12 + 6] = 1.0;
214 A[7 * 12 + 8] = y0;
215 A[7 * 12 + 11] = y0 * y0;
216
217 // c9: vb(x0/2,y0/2) - va(x0/2,y0/2) = 0
218 A[8 * 12 + 0] = -1.0;
219 A[8 * 12 + 1] = -0.5 * x0;
220 A[8 * 12 + 2] = -0.5 * y0;
221 A[8 * 12 + 3] = -0.25 * x0 * y0;
222 A[8 * 12 + 4] = -0.25 * x0 * x0;
223 A[8 * 12 + 5] = -0.25 * y0 * y0;
224 A[8 * 12 + 6] = 1.0;
225 A[8 * 12 + 7] = 0.5 * x0;
226 A[8 * 12 + 8] = 0.5 * y0;
227 A[8 * 12 + 9] = 0.25 * x0 * y0;
228 A[8 * 12 + 10] = 0.25 * x0 * x0;
229 A[8 * 12 + 11] = 0.25 * y0 * y0;
230
231 // c10: mb*flux_b(x0,0) - ma*flux_a(x0,0) = jf
232 A[9 * 12 + 1] = -ma * tx;
233 A[9 * 12 + 2] = -ma * ty;
234 A[9 * 12 + 3] = -ma * x0 * ty;
235 A[9 * 12 + 4] = -ma * 2.0 * x0 * tx;
236 A[9 * 12 + 7] = mb * tx;
237 A[9 * 12 + 8] = mb * ty;
238 A[9 * 12 + 9] = mb * x0 * ty;
239 A[9 * 12 + 10] = mb * 2.0 * x0 * tx;
240 rhs[9] = jf;
241
242 // c11: mb*flux_b(0,y0) - ma*flux_a(0,y0) = jf
243 A[10 * 12 + 1] = -ma * tx;
244 A[10 * 12 + 2] = -ma * ty;
245 A[10 * 12 + 3] = -ma * y0 * tx;
246 A[10 * 12 + 5] = -ma * 2.0 * y0 * ty;
247 A[10 * 12 + 7] = mb * tx;
248 A[10 * 12 + 8] = mb * ty;
249 A[10 * 12 + 9] = mb * y0 * tx;
250 A[10 * 12 + 11] = mb * 2.0 * y0 * ty;
251 rhs[10] = jf;
252
253 // c12: mb*vb_nn(x0/2,y0/2) - ma*va_nn(x0/2,y0/2) = 0
254 // (va_nn/vb_nn are constant over the element for a quadratic, so the
255 // evaluation point doesn't actually matter)
256 A[11 * 12 + 3] = -ma * 2.0 * tx * ty;
257 A[11 * 12 + 4] = -ma * 2.0 * tx * tx;
258 A[11 * 12 + 5] = -ma * 2.0 * ty * ty;
259 A[11 * 12 + 9] = mb * 2.0 * tx * ty;
260 A[11 * 12 + 10] = mb * 2.0 * tx * tx;
261 A[11 * 12 + 11] = mb * 2.0 * ty * ty;
262
263 double coeffs[12];
264 solveSmallLinearSystem<12>(A, rhs, coeffs);
265
266 std::array<double, 12> out;
267 for (int i = 0; i < 12; ++i)
268 {
269 out[i] = coeffs[i];
270 if (std::isnan(out[i]) || std::isinf(out[i]))
271 throw std::runtime_error("solve_ifem_basis_coefficients (P2) returned NaN or Inf");
272 }
273 return out;
274 }
275}
276
277#endif
Double r
Definition Headers.h:83
Definition ADR.h:19
std::array< double, 6 > solve_ifem_basis_coefficients(int basis_order, double x0, double y0, double nx, double ny, double ma, double mb, double jf, double Jit00, double Jit01, double Jit10, double Jit11, const std::array< double, 3 > &nodal_values)
void solveSmallLinearSystem(double A[N *N], double b[N], double x[N])