proteus 1.9.0
C/C++/Fortran libraries
Loading...
Searching...
No Matches
equivalent_polynomials.h
Go to the documentation of this file.
1#ifndef EQUIVALENT_POLYNOMIALS_H
2#define EQUIVALENT_POLYNOMIALS_H
3#include <array>
4#include <cmath>
5#include <cassert>
6#include <cstring>
7#include <iostream>
8#include <iomanip>
9#include <stdexcept>
14
16{
17
18 template <int nSpace, int nP_ifem, int nP, int nQ, int nEBQ, bool useIfemBasis = false>
20 {
21 public:
22 Regularized(bool useExact = false)
23 {
24 (void)useExact;
25 }
26 inline int calculate(const double *phi_dof, const double *phi_nodes, const double *xi_r, double ma, double mb, double jf, bool isBoundary, bool scale)
27 {
28 (void)phi_dof;
29 (void)phi_nodes;
30 (void)xi_r;
31 (void)ma;
32 (void)mb;
33 (void)jf;
34 (void)isBoundary;
35 (void)scale;
36 return 0;
37 }
38 inline int calculate(const double *phi_dof, const double *phi_nodes, const double *xi_r, double ma, double mb, bool isBoundary, bool scale)
39 {
40 return calculate(phi_dof, phi_nodes, xi_r, ma, mb, 0.0, isBoundary, scale);
41 }
42 inline int calculate(const double *phi_dof, const double *phi_nodes, const double *xi_r, bool isBoundary)
43 {
44 return calculate(phi_dof, phi_nodes, xi_r, 1.0, 1.0, 0.0, isBoundary, false);
45 }
46 inline double *get_normal()
47 {
48 return nullptr;
49 }
50 inline void set_quad(unsigned int q)
51 {
52 (void)q;
53 }
54 inline void set_boundary_quad(unsigned int ebq)
55 {
56 (void)ebq;
57 }
58 inline double H(double eps, double phi)
59 {
60 double h;
61 if (phi > eps)
62 h = 1.0;
63 else if (phi < -eps)
64 h = 0.0;
65 else if (phi == 0.0)
66 h = 0.5;
67 else
68 h = 0.5 * (1.0 + phi / eps + sin(M_PI * phi / eps) / M_PI);
69 return h;
70 }
71 inline double ImH(double eps, double phi)
72 {
73 return 1.0 - H(eps, phi);
74 }
75 inline double D(double eps, double phi)
76 {
77 double d;
78 if (phi > eps)
79 d = 0.0;
80 else if (phi < -eps)
81 d = 0.0;
82 else
83 d = 0.5 * (1.0 + cos(M_PI * phi / eps)) / eps;
84 return d;
85 }
86 inline double VA(int i) { return -1.0; };
87 inline double VA_x(int i) { return -1.0; };
88 inline double VA_y(int i) { return -1.0; };
89 inline double VA_z(int i) { return -1.0; };
90 inline double VB(int i) { return -1.0; };
91 inline double VB_x(int i) { return -1.0; };
92 inline double VB_y(int i) { return -1.0; };
93 inline double VB_z(int i) { return -1.0; };
94 };
95
96 template <int nSpace, int nP_ifem, int nP, int nQ, int nEBQ, bool useIfemBasis = false>
97 class Simplex
98 {
99 public:
100 Simplex(bool useExact = true)
101 {
102 if (nSpace == 1)
103 assert(nDOF == nP + 1);
104 else if (nSpace == 2)
105 assert(nDOF == (nP + 1) * (nP + 2) / 2);
106 else if (nSpace == 3)
107 assert(nDOF == (nP + 1) * (nP + 2) * (nP + 3) / 6);
108 else
109 assert(false);
111 for (int i = 0; i < nSpace; i++)
112 level_set_normal[i] = 0.0;
113 level_set_normal[0] = 1.0;
114 }
115
116 inline int calculate(const double *phi_dof, const double *phi_nodes, const double *xi_r, double ma, double mb, double jf, bool isBoundary, bool scale);
117
118 inline int calculate(const double *phi_dof, const double *phi_nodes, const double *xi_r, double ma, double mb, bool isBoundary, bool scale)
119 {
120 return calculate(phi_dof, phi_nodes, xi_r, ma, mb, 0.0, isBoundary, false);
121 }
122
123 inline int calculate(const double *phi_dof, const double *phi_nodes, const double *xi_r, bool isBoundary)
124 {
125 return calculate(phi_dof, phi_nodes, xi_r, 1.0, 1.0, 0.0, isBoundary, false);
126 }
127
128 inline void set_quad(unsigned int q)
129 {
130 assert(q >= 0);
131 assert(q < nQ);
132 // The edge/corner special cases are an IFEM addition: pre-IFEM these degenerate
133 // cells still took their H/ImH/D straight from the moment fit, and forcing D = 0
134 // here removes the interface measure that MCorr's mass correction integrates
135 // over (vortex2D_exactHeaviside). Keep them on the IFEM path only.
136 if (useIfemBasis && (edge == -1 || corner == -1))
137 {
138 _H_q = 0.0;
139 _ImH_q = 1.0;
140 _D_q = 0.0;
141 }
142 else if (useIfemBasis && (edge == 1 || corner == 1))
143 {
144 _H_q = 1.0;
145 _ImH_q = 0.0;
146 _D_q = 0.0;
147 }
148 else if (inside_out)
149 {
150 // std::cout << "Inside out \t corner " << corner << "\t edge " << edge << std::endl;
151 _H_q = _ImH[q];
152 _ImH_q = _H[q];
153 _D_q = _D[q];
154 }
155 else
156 {
157 // std::cout << "corner " << corner << "\t edge " << edge << std::endl;
158 _H_q = _H[q];
159 _ImH_q = _ImH[q];
160 _D_q = _D[q];
161 }
162 // basis functions already adjusted for inside_out
163 for (int i = 0; i < nP_ifem; i++)
164 {
165 _va_q[i] = _va[q * nP_ifem + i];
166 _vb_q[i] = _vb[q * nP_ifem + i];
167 }
168 for (int i = 0; i < nP_ifem; i++)
169 {
170 _va_x_q[i] = _va_x[q * nP_ifem + i];
171 _va_y_q[i] = _va_y[q * nP_ifem + i];
172 _va_z_q[i] = _va_z[q * nP_ifem + i];
173 _vb_x_q[i] = _vb_x[q * nP_ifem + i];
174 _vb_y_q[i] = _vb_y[q * nP_ifem + i];
175 _vb_z_q[i] = _vb_z[q * nP_ifem + i];
176 }
177 }
178
179 inline void set_boundary_quad(unsigned int ebq)
180 {
181 assert(ebq >= 0);
182 assert(ebq < nEBQ);
183 // The edge/corner special cases are an IFEM addition: pre-IFEM these degenerate
184 // cells still took their H/ImH/D straight from the moment fit, and forcing D = 0
185 // here removes the interface measure that MCorr's mass correction integrates
186 // over (vortex2D_exactHeaviside). Keep them on the IFEM path only.
187 if (useIfemBasis && (edge == -1 || corner == -1))
188 {
189 _H_q = 0.0;
190 _ImH_q = 1.0;
191 _D_q = 0.0;
192 }
193 else if (useIfemBasis && (edge == 1 || corner == 1))
194 {
195 _H_q = 1.0;
196 _ImH_q = 0.0;
197 _D_q = 0.0;
198 }
199 else if (inside_out)
200 {
201 _H_q = _ImH_ebq[ebq];
202 _ImH_q = _H_ebq[ebq];
203 _D_q = _D_ebq[ebq];
204 }
205 else
206 {
207 _H_q = _H_ebq[ebq];
208 _ImH_q = _ImH_ebq[ebq];
209 _D_q = _D_ebq[ebq];
210 }
211 // basis functions already adjusted for inside_out
212 for (int i = 0; i < nP_ifem; i++)
213 {
214 _va_q[i] = _va_ebq[ebq * nP_ifem + i];
215 _vb_q[i] = _vb_ebq[ebq * nP_ifem + i];
216 }
217 for (int i = 0; i < nP_ifem; i++)
218 {
219 _va_x_q[i] = _va_x_ebq[ebq * nP_ifem + i];
220 _va_y_q[i] = _va_y_ebq[ebq * nP_ifem + i];
221 _vb_x_q[i] = _vb_x_ebq[ebq * nP_ifem + i];
222 _vb_y_q[i] = _vb_y_ebq[ebq * nP_ifem + i];
223 }
224 }
225
226 inline double *get_H() { return _H; };
227 inline double *get_ImH() { return _ImH; };
228 inline double *get_D() { return _D; };
229 inline double H(double eps, double phi) { return _H_q; };
230 inline double ImH(double eps, double phi) { return _ImH_q; };
231 inline double D(double eps, double phi) { return _D_q; };
232 inline double VA(int i) { return _va_q[i]; };
233 inline double VA_x(int i) { return _va_x_q[i]; };
234 inline double VA_y(int i) { return _va_y_q[i]; };
235 inline double VA_z(int i) { return _va_z_q[i]; };
236 inline double VB(int i) { return _vb_q[i]; };
237 inline double VB_x(int i) { return _vb_x_q[i]; };
238 inline double VB_y(int i) { return _vb_y_q[i]; };
239 inline double VB_z(int i) { return _vb_z_q[i]; };
240 inline double *get_normal()
241 {
242 return level_set_normal;
243 }
244 // Value-initialised: gf_f_cache/gf_s_cache in ADR.h are built once with
245 // assign(n, GfType()) and reused, so any entry whose calculate() was not
246 // invoked in the current pass is read with whatever the default-constructed
247 // temporary happened to hold. edge/corner in particular are *branched on*
248 // in set_quad, which turns indeterminate bytes into a discretely different
249 // H/ImH/D and a discretely different Jacobian.
250 bool inside_out = false, quad_cut = false;
251 static const unsigned int nN = nSpace + 1;
252 // Sized for both of its roles: the cut geometry needs one corrected value per
253 // topological vertex (nN), while the P2 IFEM basis needs one per trial dof
254 // (nP_ifem = 6). Non-ADR callers instantiate with nP_ifem < nN -- VOF/MCorr
255 // with 2, RANS3PF with 1 -- so sizing by nP_ifem alone left the vertex entries
256 // past the end of the array, and _calculate_cuts read them back as garbage.
257 static const unsigned int nDOF_phi = (useIfemBasis && nP_ifem > nN) ? nP_ifem : nN;
259 double cut_barycenter[3] = {0., 0., 0.};
260 int edge = 0, corner = 0;
261 bool split = false;
262 bool flip_the_cell = false;
263
264 private:
265 int P2_ifem_case = 0;
266 // These eight are the arrays VA()/VB()/VA_x()/... hand straight back to the
267 // caller, so they are the read path for the SCIFEM facet terms in ADR.h.
268 // 3567c2a3 initialised the storage arrays (_va, _va_ebq, _a*, _b*) but left
269 // this scratch row alone -- only the three scalars ahead of it got values.
270 double _H_q = 0., _ImH_q = 0., _D_q = 0., _va_q[nP_ifem] = {}, _vb_q[nP_ifem] = {},
271 _va_x_q[nP_ifem] = {}, _va_y_q[nP_ifem] = {}, _va_z_q[nP_ifem] = {},
272 _vb_x_q[nP_ifem] = {}, _vb_y_q[nP_ifem] = {}, _vb_z_q[nP_ifem] = {};
273 // Sized nDOF_phi for the same reason as phi_dof_corrected: the cut geometry
274 // permutes nN topological vertices, while the P2 IFEM basis also permutes its
275 // mid-side dofs (nP_ifem = 6). Sizing by nP_ifem alone left permutation[nN-1]
276 // unwritten for the nP_ifem < nN callers (VOF/MCorr 2, RANS3PF 1), and
277 // _calculate_cuts then indexed phi_dof_corrected with that garbage.
278 unsigned int root_node = 0, permutation[nDOF_phi] = {};
279 // Same nN-vs-nP_ifem split as phi_dof_corrected/permutation: the cut geometry
280 // needs one entry per topological vertex (the Jacobian reads nodes[(1+i)*3+I]
281 // for i < nN-1), while the P2 IFEM basis needs one per trial dof. Sizing by
282 // nP_ifem alone left the last vertex unfilled for nP_ifem < nN callers and the
283 // Jacobian was built from memory past the end of nodes[].
284 double phi[nDOF_phi] = {}, nodes[nDOF_phi * 3] = {};
285 double _a1[nP_ifem] = {}, _a2[nP_ifem] = {}, _a3[nP_ifem] = {}, _b1[nP_ifem] = {}, _b2[nP_ifem] = {}, _b3[nP_ifem] = {};
286 double _a4[nP_ifem] = {}, _a5[nP_ifem] = {}, _a6[nP_ifem] = {}, _b4[nP_ifem] = {}, _b5[nP_ifem] = {}, _b6[nP_ifem] = {};
287 double Jac[nSpace * nSpace] = {}, inv_Jac[nSpace * nSpace] = {}, det_Jac = 0.;
288 double level_set_normal[nSpace], X_0[nSpace], phys_nodes_cut[(nN - 1) * 3], THETA_01, THETA_02, THETA_31, THETA_32, phys_nodes_cut_quad_01[3], phys_nodes_cut_quad_02[3], phys_nodes_cut_quad_31[3], phys_nodes_cut_quad_32[3];
289 static const unsigned int nDOF = ((nSpace - 1) / 2) * (nSpace - 2) * (nP + 1) * (nP + 2) * (nP + 3) / 6 + (nSpace - 1) * (3 - nSpace) * (nP + 1) * (nP + 2) / 2 + (2 - nSpace) * ((3 - nSpace) / 2) * (nP + 1);
290 double Ainv[nDOF * nDOF];
291 double C_H[nDOF] = {}, C_ImH[nDOF] = {}, C_D[nDOF] = {};
292 inline int _calculate_permutation(const double *phi_dof, const double *phi_nodes);
293 inline void _calculate_cuts();
294 inline void _calculate_cuts_quad();
295 inline void _calculate_C();
296 inline void _correct_phi(const double *phi_dof, const double *phi_nodes);
297 double _H[nQ] = {}, _ImH[nQ] = {}, _D[nQ] = {}, _va[nQ * nP_ifem] = {}, _vb[nQ * nP_ifem] = {};
298 double _H_ebq[nEBQ] = {}, _ImH_ebq[nEBQ] = {}, _D_ebq[nEBQ] = {}, _va_ebq[nEBQ * nP_ifem] = {}, _vb_ebq[nEBQ * nP_ifem] = {}; // cek hack: this is confusing because we use no suffice for the q arrays and _ebq for the ebq arrays, then use _q above for generic quad point
299 double _va_x[nQ * nP_ifem] = {}, _va_y[nQ * nP_ifem] = {}, _va_z[nQ * nP_ifem] = {}, _vb_x[nQ * nP_ifem] = {}, _vb_y[nQ * nP_ifem] = {}, _vb_z[nQ * nP_ifem] = {};
300 double _va_x_ebq[nEBQ * nP_ifem] = {}, _va_y_ebq[nEBQ * nP_ifem] = {}, _va_z_ebq[nEBQ * nP_ifem] = {}, _vb_x_ebq[nEBQ * nP_ifem] = {}, _vb_y_ebq[nEBQ * nP_ifem] = {}, _vb_z_ebq[nEBQ * nP_ifem] = {};
301 inline void _calculate_basis_coefficients(const double ma, const double mb, const double jf);
302 inline void _calculate_basis(const double *xi, double *va, double *vb);
303 inline void _calculate_basis_gradients(const double *xi, double *va_x, double *va_y, double *vb_x, double *vb_y);
304 };
305
306 template <int nSpace, int nP_ifem, int nP, int nQ, int nEBQ, bool useIfemBasis>
307 inline void Simplex<nSpace, nP_ifem, nP, nQ, nEBQ, useIfemBasis>::_calculate_C()
308 {
309 double b_H[nDOF], b_ImH[nDOF], b_dH[nDOF * nSpace], b_D[nDOF * nSpace];
310 if (quad_cut)
311 {
312 _calculate_b<nP>(THETA_01, THETA_02, THETA_31, THETA_32,
313 phi_dof_corrected[permutation[0]],
314 phi_dof_corrected[permutation[1]],
315 phi_dof_corrected[permutation[2]],
316 phi_dof_corrected[permutation[3]],
317 b_H, b_ImH, b_D);
318 if (inside_out) // todo handle insdie out for H/ImH/D in a simplified/unified way
319 {
320 for (unsigned int i = 0; i < nDOF; i++)
321 {
322 b_D[i] = -b_D[i];
323 }
324 }
325 for (unsigned int i = 0; i < nDOF; i++)
326 {
327 C_H[i] = 0.0;
328 C_ImH[i] = 0.0;
329 C_D[i] = 0.0;
330 for (unsigned int j = 0; j < nDOF; j++)
331 {
332 assert(!std::isnan(Ainv[i * nDOF + j]));
333 assert(!std::isnan(b_H[j]));
334 assert(!std::isnan(b_ImH[j]));
335 assert(!std::isnan(b_D[j]));
336 C_H[i] += Ainv[i * nDOF + j] * b_H[j];
337 C_ImH[i] += Ainv[i * nDOF + j] * b_ImH[j];
338 C_D[i] += Ainv[i * nDOF + j] * b_D[j];
339 }
340 // only if direct boundary integral is used
341 // C_D[i] /= det_Jac;
342 }
343 }
344 else
345 {
346 _calculate_b<nSpace, nP>(X_0, b_H, b_ImH, b_dH);
347
348 double Jt_dphi_dx[nSpace];
349 for (unsigned int I = 0; I < nSpace; I++)
350 {
351 Jt_dphi_dx[I] = 0.0;
352 for (unsigned int J = 0; J < nSpace; J++)
353 Jt_dphi_dx[I] += Jac[J * nSpace + I] * level_set_normal[J];
354 }
355 for (unsigned int i = 0; i < nDOF; i++)
356 {
357 C_H[i] = 0.0;
358 C_ImH[i] = 0.0;
359 C_D[i] = 0.0;
360 for (unsigned int j = 0; j < nDOF; j++)
361 {
362 C_H[i] += Ainv[i * nDOF + j] * b_H[j];
363 C_ImH[i] += Ainv[i * nDOF + j] * b_ImH[j];
364 for (unsigned int I = 0; I < nSpace; I++)
365 {
366 if (fabs(Jt_dphi_dx[I]) > 0.0)
367 C_D[i] -= Ainv[i * nDOF + j] * b_dH[j * nSpace + I] / (Jt_dphi_dx[I]);
368 }
369 }
370 }
371 // The Dirac is a surface measure and must be independent of the caller's node ordering,
372 // but C_D above inherits sign(det_Jac) through Jt_dphi_dx (built from Jac). Elements
373 // handed in with negative orientation therefore produced a sign-flipped D (test_2D/3D).
374 // Correct the sign here rather than reordering the permutation: the permutation feeds
375 // solve_ifem_basis_coefficients, and reordering it changes the P2 IFEM basis.
376 if (det_Jac < 0.0)
377 for (unsigned int i = 0; i < nDOF; i++)
378 C_D[i] = -C_D[i];
379 }
380 }
381
382 template <int nSpace, int nP_ifem, int nP, int nQ, int nEBQ, bool useIfemBasis>
383 inline int Simplex<nSpace, nP_ifem, nP, nQ, nEBQ, useIfemBasis>::_calculate_permutation(const double *phi_dof, const double *phi_nodes)
384 {
385 if (flip_the_cell)
386 {
387 // std::cout << "Flipping the permutation from: " << std::endl;
388 // for (unsigned int i = 0; i < nP_ifem; i++)
389 // {
390 // std::cout << permutation[i] << " ";
391 // }
392 // std::cout << std::endl;
393 int temp = permutation[1];
394 permutation[1] = permutation[2];
395 permutation[2] = temp;
396 temp = permutation[3];
397 permutation[3] = permutation[5];
398 permutation[5] = temp;
399 flip_the_cell = false;
400
401 // std::cout << "Flipping the permutation to: " << std::endl;
402 // for (unsigned int i = 0; i < nP_ifem; i++)
403 // {
404 // std::cout << permutation[i] << " ";
405 // }
406 // std::cout << std::endl;
407 }
408 else
409 {
410 int p_i, pcount = 0, n_i, ncount = 0, z_i, zcount = 0;
411 corner = 0;
412 edge = 0;
413 root_node = 0;
414 inside_out = false;
415 quad_cut = false;
416
417 const double eps = 1.0e-8;
418
419 for (unsigned int i = 0; i < nN; i++)
420 {
421 // std::cout << "phi_nodes[" << i << "] = " << phi_nodes[i*3+0] << ", " << phi_nodes[i*3+1] << ", " << phi_nodes[i*3+2] << std::endl;
422 // std::cout << "phi_dof[" << i << "] = " << phi_dof[i] << std::endl << std::endl;
423 if (phi_dof[i] > eps)
424 {
425 if (pcount == 0)
426 p_i = i;
427 pcount += 1;
428 }
429 else if (phi_dof[i] < -eps)
430 {
431 if (ncount == 0)
432 n_i = i;
433 ncount += 1;
434 }
435 else
436 {
437 if (zcount == 0)
438 z_i = i;
439 zcount += 1;
440 }
441 }
442 // std::cout << "zcount " << zcount << "\t pcount " << pcount << "\t ncount " << ncount << std::endl;
443 if (pcount == nN)
444 {
445 // All positive: element is fully in the +1 domain
446 // std::cout << "This is a fully positive element." << std::endl;
447 return 1;
448 }
449 else if (ncount == nN)
450 {
451 // All negative: element is fully in the -1 domain
452 // std::cout << "This is a fully negative element." << std::endl;
453 return -1;
454 }
455 else if (ncount == 1)
456 {
457 if (zcount == nN - 1) // for P1 ifem, interface is on an element boundary and the element is fully in the -1 domain.
458 {
459 edge = -1;
460 // std::cout << "This is a edge case with negative side element." << std::endl;
461 }
462 else if (zcount == 1 && pcount == 1)
463 {
464 // std::cout << "This is a true split element" << std::endl;
465 split = true;
466 }
467 root_node = n_i;
468 }
469 else if (pcount == 1)
470 {
471 if (zcount == nN - 1) // for P1 ifem, interface is on an element boundary and the element is fully in the +1 domain.
472 {
473 edge = 1;
474 // std::cout << "This is a edge case with positive side element." << std::endl;
475 }
476 root_node = p_i;
477 inside_out = true;
478 }
479 else if (nSpace == 3 && pcount == 2 && ncount == 2)
480 {
481 // special case only in 3D
482 quad_cut = true;
483 root_node = n_i;
484 }
485 else
486 {
487 assert(zcount < nN - 1);
488 // std::cout << "corner case: zcount " << zcount << "\t pcount " << pcount << "\t ncount " << ncount << std::endl;
489 if (pcount && !ncount)
490 {
491 corner = 1; // The interface passes through a corner node and element is in + side
492 assert(pcount == nN - 1);
493 root_node = z_i;
494 // ADR's P2 IFEM basis needs the ma/mb swap here, but pre-IFEM this branch
495 // left inside_out false, and set_quad uses it to exchange H and ImH -- which
496 // inverts the phase fractions MCorr/VOF integrate. IFEM path only.
497 if (useIfemBasis)
498 inside_out = true;
499 }
500 else if (ncount && !pcount)
501 {
502 corner = -1; // The interface passes through a corner node and element is in - side
503 assert(ncount == nN - 1);
504 // Pre-IFEM this rooted at n_i; the IFEM basis wants z_i. Keep the old root for
505 // the non-IFEM callers so their permutation/Jacobian are unchanged.
506 root_node = useIfemBasis ? z_i : n_i;
507 }
508 else
509 assert(false);
510 }
511 // std::cout << "root_node: " << root_node << "\t inside_out: " << inside_out << "\t quad_cut: " << quad_cut << std::endl;
512 for (unsigned int i = 0; i < nDOF_phi; i++)
513 {
514 // Works for both P1 and P2 IFEM. The loop bound is nDOF_phi = max(nN, nP_ifem):
515 // the geometry always needs all nN corner entries, while P2 IFEM needs its
516 // mid-side dofs on top of them. Bounding by nP_ifem alone left the corner
517 // entries past nP_ifem unwritten whenever nP_ifem < nN (VOF/MCorr 2,
518 // RANS3PF 1), and _calculate_cuts read them back as garbage indices.
519 // the cycling modulus is nN (topological vertices): the first nN entries rotate the
520 // corner nodes starting at the root, and the P2 mid-side DOFs shadow them at offset nN.
521 // The modulus must NOT be a literal 3 -- that is only nN for a triangle, and on a
522 // tetrahedron (nN=4) it produced out-of-range indices such as 3+(2+3)%3 = 5, giving a
523 // garbage Jacobian and nan H/ImH/D (test_3D, phi = [1,1,-1,-1]).
524 if (i < nN)
525 permutation[i] = (root_node + i) % nN;
526 else
527 permutation[i] = nN + (root_node + i) % nN;
528 }
529 if (quad_cut)
530 {
531 if (phi_dof[permutation[nN - 1]] > 0.0)
532 {
533 int tmp = permutation[nN - 1];
534 if (phi_dof[permutation[nN - 2]] < 0.0)
535 {
536 permutation[nN - 1] = permutation[nN - 2];
537 permutation[nN - 2] = tmp;
538 }
539 else if (phi_dof[permutation[nN - 3]] < 0.0)
540 {
541 permutation[nN - 1] = permutation[nN - 3];
542 permutation[nN - 3] = tmp;
543 }
544 else
545 assert(false);
546 }
547 assert(phi_dof[permutation[0]] < 0.0);
548 assert(phi_dof[permutation[3]] < 0.0);
549 assert(phi_dof[permutation[1]] > 0.0);
550 assert(phi_dof[permutation[2]] > 0.0);
551 }
552 // std::cout << "pcount " << pcount << "\t ncount " << ncount << "\t zcount " << zcount << "\t root node = " << root_node << std::endl;
553 }
554 for (unsigned int i = 0; i < nDOF_phi; i++)
555 {
556 phi[i] = phi_dof[permutation[i]];
557 // // std::cout << "ref idx: " << i << "\t real idx(permutation[i]): " << permutation[i] << std::endl;
558 for (unsigned int I = 0; I < 3; I++)
559 {
560 nodes[i * 3 + I] = phi_nodes[permutation[i] * 3 + I]; // nodes always 3D
561
562 // std::cout << "nodes[" << i * 3 + I << "] = " << nodes[i * 3 + I] << std::endl;
563 }
564 }
565 double JacTest[nSpace * nSpace];
566 for (unsigned int I = 0; I < nSpace; I++)
567 {
568 for (unsigned int i = 0; i < nN - 1; i++)
569 {
570 Jac[I * nSpace + i] = nodes[(1 + i) * 3 + I] - nodes[I];
571 JacTest[I * nSpace + i] = phi_nodes[(1 + i) * 3 + I] - phi_nodes[I];
572 // std::cout << "Jac[" << I * nSpace + i << "] = " << Jac[I * nSpace + i] << std::endl;
573 // std::cout << "JacTest[" << I * nSpace + i << "] = " << JacTest[I * nSpace + i] << std::endl;
574 }
575 }
576 det_Jac = det<nSpace>(Jac);
577 double det_JacTest = det<nSpace>(JacTest);
578 /* assert(det_JacTest >= 0.0); */
579 /* assert(det_Jac >= 0.0); */
580
581 if (det_Jac < 0.0 && flip_the_cell)
582 {
583 if (quad_cut) // flip the two internal positive nodes
584 {
585 double tmp = permutation[2];
586 permutation[2] = permutation[1];
587 permutation[1] = tmp;
588 }
589 else // flip the last two nodes
590 {
591 double tmp = permutation[nN - 1];
592 permutation[nN - 1] = permutation[nN - 2];
593 permutation[nN - 2] = tmp;
594 }
595 for (unsigned int i = 0; i < nDOF_phi; i++)
596 {
597 phi[i] = phi_dof[permutation[i]];
598 for (unsigned int I = 0; I < 3; I++)
599 {
600 nodes[i * 3 + I] = phi_nodes[permutation[i] * 3 + I]; // nodes always 3D
601 }
602 }
603 for (unsigned int i = 0; i < nN - 1; i++)
604 for (unsigned int I = 0; I < nSpace; I++)
605 Jac[I * nSpace + i] = nodes[(1 + i) * 3 + I] - nodes[I];
606 det_Jac = det<nSpace>(Jac);
607 assert(det_Jac > 0);
608 if (nSpace == 1)
609 inside_out = true;
610 }
611 inv<nSpace>(Jac, inv_Jac);
612 return 0;
613 }
614
615 template <int nSpace, int nP_ifem, int nP, int nQ, int nEBQ, bool useIfemBasis>
616 inline void Simplex<nSpace, nP_ifem, nP, nQ, nEBQ, useIfemBasis>::_calculate_cuts()
617 {
618 const double eps = 1.0e-8;
619 for (unsigned int i = 0; i < nN - 1; i++)
620 {
621 if (useIfemBasis && (corner == 1 || corner == -1))
622 {
623 X_0[i] = 0.0;
624 for (unsigned int I = 0; I < 3; I++)
625 {
626 phys_nodes_cut[i * 3 + I] = nodes[I];
627 }
628 }
629 else if (phi[i + 1] * phi[0] < 0.0)
630 {
631 X_0[i] = 0.5 - 0.5 * (phi[i + 1] + phi[0]) / (phi[i + 1] - phi[0]);
632 assert(X_0[i] <= 1.0);
633 assert(X_0[i] >= 0.0);
634 for (unsigned int I = 0; I < 3; I++)
635 {
636 phys_nodes_cut[i * 3 + I] = (1 - X_0[i]) * nodes[I] + X_0[i] * nodes[(1 + i) * 3 + I];
637 // std::cout << "nodes[" << I << "] = " << nodes[I] << std::endl;
638 // std::cout << "phys_nodes_cut[" << i*3 + I << "] = " << phys_nodes_cut[i*3 + I] << std::endl << std::endl;
639 }
640 }
641 else
642 {
643 // assert(phi[i+1] < eps);
644 if (phi[i + 1] < eps)
645 {
646 X_0[i] = 1.0;
647 for (unsigned int I = 0; I < 3; I++)
648 {
649 phys_nodes_cut[i * 3 + I] = nodes[(1 + i) * 3 + I];
650 }
651 }
652 else
653 {
654 X_0[i] = 0.0;
655 for (unsigned int I = 0; I < 3; I++)
656 {
657 phys_nodes_cut[i * 3 + I] = nodes[I];
658 }
659 }
660 }
661 }
662 // std::cout << "X_0: \t" << X_0[0] << ", " << X_0[1] << std::endl;
663 // std::cout << "phys_nodes_cut: " << std::endl;
664 // for (unsigned int i = 0; i < nN - 1; i++)
665 // {
666 // std::cout << "phys_nodes_cut[" << i << "]: \t" << phys_nodes_cut[i * 3 + 0] << ", " << phys_nodes_cut[i * 3 + 1] << ", " << phys_nodes_cut[i * 3 + 2] << std::endl;
667 // }
668 // Redo the permutation for the case: (x0>0.5 and y0<=0.5)
669 if (nP_ifem == 6 && (X_0[0] > 0.5 && X_0[1] <= 0.5))
670 {
671 // std::cout << "Case (X_0[0] > 0.5 && X_0[1] <= 0.5) detected in _calculate_cuts(), flipping the cell" << std::endl;
672 flip_the_cell = true;
673 }
674 }
675
676 template <int nSpace, int nP_ifem, int nP, int nQ, int nEBQ, bool useIfemBasis>
677 inline void Simplex<nSpace, nP_ifem, nP, nQ, nEBQ, useIfemBasis>::_calculate_cuts_quad()
678 {
679 const double eps = 1.0e-8, Imeps = 1.0 - eps;
680 THETA_01 = 0.5 - 0.5 * (phi[1] + phi[0]) / (phi[1] - phi[0]);
681 THETA_02 = 0.5 - 0.5 * (phi[2] + phi[0]) / (phi[2] - phi[0]);
682 THETA_31 = 0.5 - 0.5 * (phi[1] + phi[3]) / (phi[1] - phi[3]);
683 THETA_32 = 0.5 - 0.5 * (phi[2] + phi[3]) / (phi[2] - phi[3]);
684 if ((THETA_01 < eps || THETA_01 > Imeps) || (THETA_02 < eps || THETA_02 > Imeps) || (THETA_31 < eps || THETA_31 > Imeps) || (THETA_32 < eps || THETA_32 > Imeps))
685 {
686 THETA_01 = fmin(Imeps, fmax(eps, 0.5 - 0.5 * (phi[1] + phi[0]) / (phi[1] - phi[0])));
687 THETA_02 = fmin(Imeps, fmax(eps, 0.5 - 0.5 * (phi[2] + phi[0]) / (phi[2] - phi[0])));
688 THETA_31 = fmin(Imeps, fmax(eps, 0.5 - 0.5 * (phi[1] + phi[3]) / (phi[1] - phi[3])));
689 THETA_32 = fmin(Imeps, fmax(eps, 0.5 - 0.5 * (phi[2] + phi[3]) / (phi[2] - phi[3])));
690 }
691 for (unsigned int I = 0; I < 3; I++)
692 {
693 phys_nodes_cut_quad_01[I] = (1 - THETA_01) * nodes[I] + THETA_01 * nodes[1 * 3 + I];
694 phys_nodes_cut_quad_02[I] = (1 - THETA_02) * nodes[I] + THETA_02 * nodes[2 * 3 + I];
695 phys_nodes_cut_quad_31[I] = (1 - THETA_31) * nodes[3 * 3 + I] + THETA_31 * nodes[1 * 3 + I];
696 phys_nodes_cut_quad_32[I] = (1 - THETA_32) * nodes[3 * 3 + I] + THETA_32 * nodes[2 * 3 + I];
697 }
698 }
699
700 template <int nSpace, int nP_ifem, int nP, int nQ, int nEBQ, bool useIfemBasis>
701 inline void Simplex<nSpace, nP_ifem, nP, nQ, nEBQ, useIfemBasis>::_correct_phi(const double *phi_dof, const double *phi_nodes)
702 {
703 memset(cut_barycenter, 0, 3 * sizeof(double));
704 const double one_by_nNm1 = 1.0 / (nN - 1.0);
705 if (quad_cut)
706 {
707 for (unsigned int I = 0; I < nSpace; I++)
708 cut_barycenter[I] += 0.25 * (phys_nodes_cut_quad_01[I] +
709 phys_nodes_cut_quad_02[I] +
710 phys_nodes_cut_quad_31[I] +
711 phys_nodes_cut_quad_32[I]);
712 }
713 else
714 {
715 for (unsigned int i = 0; i < nN - 1; i++)
716 {
717 assert(!std::isnan(phys_nodes_cut[i * 3 + 0]));
718 assert(!std::isnan(phys_nodes_cut[i * 3 + 1]));
719 assert(!std::isnan(phys_nodes_cut[i * 3 + 2]));
720 for (unsigned int I = 0; I < nSpace; I++)
721 cut_barycenter[I] += phys_nodes_cut[i * 3 + I] * one_by_nNm1;
722 }
723 }
724 for (unsigned int i = 0; i < nDOF_phi; i++)
725 {
726 phi_dof_corrected[i] = 0.0;
727 for (unsigned int I = 0; I < nSpace; I++)
728 {
729 phi_dof_corrected[i] += level_set_normal[I] * (phi_nodes[i * 3 + I] - cut_barycenter[I]);
730 }
731 // ensure sdf sign convention consistent with input phi
732 if (phi_dof_corrected[i] * phi_dof[i] < 0.0)
733 {
734 phi_dof_corrected[i] *= -1.0;
735 }
736 }
737 }
738
739 template <int nSpace, int nP_ifem, int nP, int nQ, int nEBQ, bool useIfemBasis>
740 inline void Simplex<nSpace, nP_ifem, nP, nQ, nEBQ, useIfemBasis>::_calculate_basis_coefficients(const double ma, const double mb, const double jf)
741 {
742 assert(nSpace == 2);
743 assert(nN == 3);
744 double nx = 0.0, ny = 0.0;
745 if (inside_out)
746 {
747 nx = -level_set_normal[0];
748 ny = -level_set_normal[1];
749 }
750 else
751 {
752 nx = level_set_normal[0];
753 ny = level_set_normal[1];
754 }
755 double Jit00 = inv_Jac[0 * nSpace + 0],
756 Jit01 = inv_Jac[1 * nSpace + 0],
757 Jit10 = inv_Jac[0 * nSpace + 1],
758 Jit11 = inv_Jac[1 * nSpace + 1];
759 double x0 = X_0[0],
760 y0 = X_0[1];
761 const double *vall = nullptr;
762
763 // std::cout << "X0 = " << x0 << std::endl << "Y0 = " << y0 << std::endl;
764 // std::cout << "NX = " << nx << std::endl << "NY = " << ny << std::endl;
765 // std::cout << "MUA = " << ma << std::endl << "MUB = " << mb << std::endl << "jf = " << jf << std::endl;
766 // std::cout << "Jit00 = " << Jit00 << std::endl << "Jit01 = " << Jit01 << std::endl << "Jit10 = " << Jit10 << std::endl << "Jit11 = " << Jit11 << std::endl;
767 // std::cout << "nx: " << nx << "\t ny: " << ny << std::endl;
768
769 switch (nP_ifem)
770 {
771 case 3:
772 {
773 static const double vall_p1[9] = {
774 1., 0., 0.,
775 0., 1., 0.,
776 0., 0., 1.};
777 vall = vall_p1;
778 for (int j = 0; j < 3; j++)
779 {
780 int i = permutation[j];
781 double v[3] = {0.0, 0.0, 0.0};
782 v[0] = vall[j * 3 + 0];
783 v[1] = vall[j * 3 + 1];
784 v[2] = vall[j * 3 + 2];
785
786 if (corner || edge)
787 {
788 _a1[i] = v[0];
789 _a2[i] = -v[0] + v[1];
790 _a3[i] = -v[0] + v[2];
791 _b1[i] = v[0];
792 _b2[i] = -v[0] + v[1];
793 _b3[i] = -v[0] + v[2];
794 }
795 else
796 {
797 // nathawani: Implement inside out case directly here.
798 const std::array<double, 3> nodal_values = {v[0], v[1], v[2]};
799 if (inside_out)
800 {
801 const std::array<double, 6> coeffs = proteus::solve_ifem_basis_coefficients(
802 1, x0, y0, nx, ny, mb, ma, jf, Jit00, Jit01, Jit10, Jit11, nodal_values);
803
804 _b1[i] = coeffs[0];
805 _b2[i] = coeffs[1];
806 _b3[i] = coeffs[2];
807 _a1[i] = coeffs[3];
808 _a2[i] = coeffs[4];
809 _a3[i] = coeffs[5];
810 }
811 else
812 {
813
814 const std::array<double, 6> coeffs = proteus::solve_ifem_basis_coefficients(
815 1, x0, y0, nx, ny, ma, mb, jf, Jit00, Jit01, Jit10, Jit11, nodal_values);
816
817 _a1[i] = coeffs[0];
818 _a2[i] = coeffs[1];
819 _a3[i] = coeffs[2];
820 _b1[i] = coeffs[3];
821 _b2[i] = coeffs[4];
822 _b3[i] = coeffs[5];
823 }
824 }
825 }
826 break;
827 }
828 case 6:
829 {
830 static const double vall_p2[36] = {
831 1., 0., 0., 0., 0., 0.,
832 0., 1., 0., 0., 0., 0.,
833 0., 0., 1., 0., 0., 0.,
834 0., 0., 0., 1., 0., 0.,
835 0., 0., 0., 0., 1., 0.,
836 0., 0., 0., 0., 0., 1.};
837 vall = vall_p2;
838
839 for (int j = 0; j < 6; j++)
840 {
841 int i = permutation[j];
842 double v[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};//, grad_va[2] = {0.0, 0.0}, grad_vb[2] = {0.0, 0.0}, grad_va_ref[2] = {0.0, 0.0}, grad_vb_ref[2] = {0.0, 0.0};
843
844 v[0] = vall[j * 6 + 0];
845 v[1] = vall[j * 6 + 1];
846 v[2] = vall[j * 6 + 2];
847 v[3] = vall[j * 6 + 3];
848 v[4] = vall[j * 6 + 4];
849 v[5] = vall[j * 6 + 5];
850
851 // std::cout << "v: " << v[0] << "\t" << v[1] << "\t" << v[2] << "\t" << v[3] << "\t" << v[4] << "\t" << v[5] << std::endl;
852 if (corner || edge)
853 {
854 _a1[i] = v[0];
855 _a2[i] = - 3 * v[0] - v[1] + 4 * v[3];
856 _a3[i] = - 3 * v[0] - v[2] + 4 * v[5];
857 _a4[i] = 4 * v[0] - 4 * v[3] + 4 * v[4] - 4 * v[5];
858 _a5[i] = 2 * v[0] + 2 * v[1] - 4 * v[3];
859 _a6[i] = 2 * v[0] + 2 * v[2] - 4 * v[5];
860 _b1[i] = v[0];
861 _b2[i] = - 3 * v[0] - v[1] + 4 * v[3];
862 _b3[i] = - 3 * v[0] - v[2] + 4 * v[5];
863 _b4[i] = 4 * v[0] - 4 * v[3] + 4 * v[4] - 4 * v[5];
864 _b5[i] = 2 * v[0] + 2 * v[1] - 4 * v[3];
865 _b6[i] = 2 * v[0] + 2 * v[2] - 4 * v[5];
866 }
867 else
868 {
869 if (inside_out)
870 {
871 const std::array<double, 6> nodal_values = {v[0], v[1], v[2], v[3], v[4], v[5]};
872 const std::array<double, 12> coeffs = proteus::solve_ifem_basis_coefficients(
873 2, x0, y0, nx, ny, mb, ma, jf, Jit00, Jit01, Jit10, Jit11, nodal_values);
874
875 _b1[i] = coeffs[0];
876 _b2[i] = coeffs[1];
877 _b3[i] = coeffs[2];
878 _b4[i] = coeffs[3];
879 _b5[i] = coeffs[4];
880 _b6[i] = coeffs[5];
881 _a1[i] = coeffs[6];
882 _a2[i] = coeffs[7];
883 _a3[i] = coeffs[8];
884 _a4[i] = coeffs[9];
885 _a5[i] = coeffs[10];
886 _a6[i] = coeffs[11];
887 }
888 else
889 {
890 const std::array<double, 6> nodal_values = {v[0], v[1], v[2], v[3], v[4], v[5]};
891 const std::array<double, 12> coeffs = proteus::solve_ifem_basis_coefficients(
892 2, x0, y0, nx, ny, ma, mb, jf, Jit00, Jit01, Jit10, Jit11, nodal_values);
893
894 _a1[i] = coeffs[0];
895 _a2[i] = coeffs[1];
896 _a3[i] = coeffs[2];
897 _a4[i] = coeffs[3];
898 _a5[i] = coeffs[4];
899 _a6[i] = coeffs[5];
900 _b1[i] = coeffs[6];
901 _b2[i] = coeffs[7];
902 _b3[i] = coeffs[8];
903 _b4[i] = coeffs[9];
904 _b5[i] = coeffs[10];
905 _b6[i] = coeffs[11];
906 }
907 }
908 }
909 break;
910 }
911 default:
912 throw std::runtime_error("Simplex::_calculate_coefficients not implemented for order > 2");
913 }
914 }
915
916 template <int nSpace, int nP_ifem, int nP, int nQ, int nEBQ, bool useIfemBasis>
917 inline void Simplex<nSpace, nP_ifem, nP, nQ, nEBQ, useIfemBasis>::_calculate_basis(const double *xi, double *va, double *vb)
918 {
919 // Switch on nP_ifem to compute basis functions at given xi
920 switch (nP_ifem)
921 {
922 case 3:
923 for (int i = 0; i < nP_ifem; i++)
924 {
925 va[i] = _a1[i] + _a2[i] * xi[0] + _a3[i] * xi[1];
926 vb[i] = _b1[i] + _b2[i] * xi[0] + _b3[i] * xi[1];
927
928 // std::cout << "\nCoefficients for real node " << i << " at quadrature point: " << xi[0] << ", " << xi[1] << std::endl;
929 // std::cout << "a: \t [" << _a1[i] << ", " << _a2[i] << ", " << _a3[i] << "]" << std::endl;
930 // std::cout << "b: \t [" << _b1[i] << ", " << _b2[i] << ", " << _b3[i] << "]" << std::endl;
931
932 // std::cout << "va[" << i << "] = " << va[i] << "\t vb[" << i << "] = " << vb[i] << std::endl;
933 }
934 break;
935 case 6:
936 for (int i = 0; i < nP_ifem; i++)
937 {
938 va[i] = _a1[i] + _a2[i] * xi[0] + _a3[i] * xi[1] + _a4[i] * xi[0] * xi[1] + _a5[i] * xi[0] * xi[0] + _a6[i] * xi[1] * xi[1];
939 vb[i] = _b1[i] + _b2[i] * xi[0] + _b3[i] * xi[1] + _b4[i] * xi[0] * xi[1] + _b5[i] * xi[0] * xi[0] + _b6[i] * xi[1] * xi[1];
940
941 // std::cout << "\nCoefficients for real node " << i << " at quadrature point: " << xi[0] << ", " << xi[1] << std::endl;
942 // std::cout << "a: \t [" << _a1[i] << ", " << _a2[i] << ", " << _a3[i] << ", " << _a4[i] << ", " << _a5[i] << ", " << _a6[i] << "]" << std::endl;
943 // std::cout << "b: \t [" << _b1[i] << ", " << _b2[i] << ", " << _b3[i] << ", " << _b4[i] << ", " << _b5[i] << ", " << _b6[i] << "]" << std::endl;
944
945 // std::cout << "va[" << i << "] = " << va[i] << "\t vb[" << i << "] = " << vb[i] << std::endl;
946
947 }
948 break;
949 default:
950 throw std::runtime_error("Simplex::_calculate_basis not implemented for order > 2");
951 }
952 }
953
954 template <int nSpace, int nP_ifem, int nP, int nQ, int nEBQ, bool useIfemBasis>
955 inline void Simplex<nSpace, nP_ifem, nP, nQ, nEBQ, useIfemBasis>::_calculate_basis_gradients(const double *xi, double *va_x, double *va_y, double *vb_x, double *vb_y)
956 {
957 // Switch on nP_ifem to compute basis functions gradients at given xi
958
959 switch (nP_ifem)
960 {
961 case 3:
962 for (int i = 0; i < nP_ifem; i++)
963 {
964 double grad_va[2] = {0.0, 0.0}, grad_vb[2] = {0.0, 0.0}, grad_va_ref[2] = {0.0, 0.0}, grad_vb_ref[2] = {0.0, 0.0};
965
966 // int i = permutation[j];
967 grad_va_ref[0] = _a2[i];
968 grad_va_ref[1] = _a3[i];
969 grad_vb_ref[0] = _b2[i];
970 grad_vb_ref[1] = _b3[i];
971 for (int I = 0; I < nSpace; I++)
972 {
973 for (int J = 0; J < nSpace; J++)
974 {
975 // // std::cout << "inv_Jac[" << I * nSpace + J << "] = " << inv_Jac[I * nSpace + J] << "\t grad_va_ref[" << J << "] = " << grad_va_ref[J] << std::endl;
976 grad_va[I] += inv_Jac[J * nSpace + I] * grad_va_ref[J];
977 grad_vb[I] += inv_Jac[J * nSpace + I] * grad_vb_ref[J];
978 }
979 }
980 // if (inside_out){
981 // va_x[i] = grad_vb[0];
982 // va_y[i] = grad_vb[1];
983 // vb_x[i] = grad_va[0];
984 // vb_y[i] = grad_va[1];
985 // }
986 // else
987 // {
988 va_x[i] = grad_va[0];
989 va_y[i] = grad_va[1];
990 vb_x[i] = grad_vb[0];
991 vb_y[i] = grad_vb[1];
992 // }
993
994 // // std::cout << "Inverse jacobian: [" << inv_Jac[0] << ", " << inv_Jac[1] << ", " << inv_Jac[2] << ", " << inv_Jac[3] << "]\n";
995
996 // // std::cout << "quadrature point: (" << xi[0] << ", " << xi[1] << ")\n";
997 // // std::cout << i << "\t Ref: \t va_x: " << grad_va_ref[0] << ", va_y: " << grad_va_ref[1]
998 // << ", vb_x: " << grad_vb_ref[0] << ", vb_y: " << grad_vb_ref[1] << std::endl;
999 // // std::cout << j << "\t Real: \t va_x: " << grad_va[0] << ", va_y: " << grad_va[1]
1000 // << ", vb_x: " << grad_vb[0] << ", vb_y: " << grad_vb[1] << std::endl;
1001 }
1002 break;
1003 case 6:
1004 for (int i = 0; i < nP_ifem; i++)
1005 {
1006 double grad_va[2] = {0.0, 0.0}, grad_vb[2] = {0.0, 0.0}, grad_va_ref[2] = {0.0, 0.0}, grad_vb_ref[2] = {0.0, 0.0};
1007
1008 // int i = permutation[j];
1009 grad_va_ref[0] = _a2[i] + _a4[i] * xi[1] + 2.0 * _a5[i] * xi[0];
1010 grad_va_ref[1] = _a3[i] + _a4[i] * xi[0] + 2.0 * _a6[i] * xi[1];
1011 grad_vb_ref[0] = _b2[i] + _b4[i] * xi[1] + 2.0 * _b5[i] * xi[0];
1012 grad_vb_ref[1] = _b3[i] + _b4[i] * xi[0] + 2.0 * _b6[i] * xi[1];
1013
1014 // std::cout << i << "Ref: \t va_x: " << grad_va_ref[0] << ", va_y: " << grad_va_ref[1] << std::endl
1015 // << ", vb_x: " << grad_vb_ref[0] << ", vb_y: " << grad_vb_ref[1] << std::endl;
1016
1017 for (int I = 0; I < nSpace; I++)
1018 {
1019 for (int J = 0; J < nSpace; J++)
1020 {
1021 // std::cout << "Jac[" << I * nSpace + J << "] = " << Jac[I * nSpace + J] << std::endl;
1022 // std::cout << "inv_Jac[" << I * nSpace + J << "] = " << inv_Jac[I * nSpace + J] << std::endl;
1023 grad_va[I] += inv_Jac[J * nSpace + I] * grad_va_ref[J];
1024 grad_vb[I] += inv_Jac[J * nSpace + I] * grad_vb_ref[J];
1025 }
1026 }
1027 // va_x[i] = grad_va[0];
1028 // va_y[i] = grad_va[1];
1029 // vb_x[i] = grad_vb[0];
1030 // vb_y[i] = grad_vb[1];
1031 // if (inside_out){
1032 // va_x[i] = grad_vb[0];
1033 // va_y[i] = grad_vb[1];
1034 // vb_x[i] = grad_va[0];
1035 // vb_y[i] = grad_va[1];
1036 // }
1037 // else
1038 // {
1039 va_x[i] = grad_va[0];
1040 va_y[i] = grad_va[1];
1041 vb_x[i] = grad_vb[0];
1042 vb_y[i] = grad_vb[1];
1043 // }
1044 }
1045 break;
1046 default:
1047 throw std::runtime_error("Simplex::_calculate_basis_gradients not implemented for order > 2");
1048 }
1049 }
1050
1051 template <int nSpace, int nP_ifem, int nP, int nQ, int nEBQ, bool useIfemBasis>
1052 inline int Simplex<nSpace, nP_ifem, nP, nQ, nEBQ, useIfemBasis>::calculate(const double *phi_dof, const double *phi_nodes, const double *xi_r, double ma, double mb, double jf, bool isBoundary, bool scale)
1053 {
1054 // initialize phi_dof_corrected -- correction can only be actually computed on cut cells
1055 for (unsigned int i = 0; i < nDOF_phi; i++)
1056 phi_dof_corrected[i] = phi_dof[i];
1057 int icase = _calculate_permutation(phi_dof, phi_nodes); // permuation, Jac,inv_Jac...
1058 if (icase == 1)
1059 {
1060 for (unsigned int q = 0; q < nQ; q++)
1061 {
1062 _H[q] = 1.0;
1063 _ImH[q] = 0.0;
1064 _D[q] = 0.0;
1065 }
1066 for (unsigned int ebq = 0; ebq < nEBQ; ebq++)
1067 {
1068 _H_ebq[ebq] = 1.0;
1069 _ImH_ebq[ebq] = 0.0;
1070 _D_ebq[ebq] = 0.0;
1071 }
1072 return icase;
1073 }
1074 else if (icase == -1)
1075 {
1076 for (unsigned int q = 0; q < nQ; q++)
1077 {
1078 _H[q] = 0.0;
1079 _ImH[q] = 1.0;
1080 _D[q] = 0.0;
1081 }
1082 for (unsigned int ebq = 0; ebq < nEBQ; ebq++)
1083 {
1084 _H_ebq[ebq] = 0.0;
1085 _ImH_ebq[ebq] = 1.0;
1086 _D_ebq[ebq] = 0.0;
1087 }
1088 return icase;
1089 }
1090 else if (nSpace == 1 && edge == 1)
1091 {
1092 // 1D only: the "edge" here is a single node, so touching zero there
1093 // is a measure-zero point, not a genuine interface -- treat this the
1094 // same as the fully-positive icase==1 case. (In 2D/3D "edge" means an
1095 // entire element edge/face has phi==0, a real nonzero-measure
1096 // interface, so this special case does not apply there.)
1097 // inside_out was set true by the pcount==1 branch above (it flips the
1098 // dir vector for the interior-cut machinery this case never reaches);
1099 // reset it so set_quad()/set_boundary_quad() don't swap H and ImH.
1100 inside_out = false;
1101 for (unsigned int q = 0; q < nQ; q++)
1102 {
1103 _H[q] = 1.0;
1104 _ImH[q] = 0.0;
1105 _D[q] = 0.0;
1106 }
1107 for (unsigned int ebq = 0; ebq < nEBQ; ebq++)
1108 {
1109 _H_ebq[ebq] = 1.0;
1110 _ImH_ebq[ebq] = 0.0;
1111 _D_ebq[ebq] = 0.0;
1112 }
1113 return edge;
1114 }
1115 else if (nSpace == 1 && edge == -1)
1116 {
1117 // same as above, but the cell is entirely non-positive.
1118 inside_out = false;
1119 for (unsigned int q = 0; q < nQ; q++)
1120 {
1121 _H[q] = 0.0;
1122 _ImH[q] = 1.0;
1123 _D[q] = 0.0;
1124 }
1125 for (unsigned int ebq = 0; ebq < nEBQ; ebq++)
1126 {
1127 _H_ebq[ebq] = 0.0;
1128 _ImH_ebq[ebq] = 1.0;
1129 _D_ebq[ebq] = 0.0;
1130 }
1131 return edge;
1132 }
1133 if (quad_cut)
1134 {
1135 _calculate_cuts_quad(); // THETA_* for quad cut in 3D
1136 _calculate_normal_quad(phys_nodes_cut_quad_01,
1137 phys_nodes_cut_quad_02,
1138 phys_nodes_cut_quad_31,
1139 phys_nodes_cut_quad_32,
1140 level_set_normal); // normal to interface
1141 }
1142 else
1143 {
1144 _calculate_cuts(); // X_0, array of interface cuts on reference simplex
1145 _calculate_normal<nSpace>(phys_nodes_cut, level_set_normal); // normal to interface
1146 }
1147 _correct_phi(phi_dof, phi_nodes);
1148 if (flip_the_cell)
1149 {
1150 _calculate_permutation(phi_dof, phi_nodes);
1151 _calculate_cuts(); // X_0, array of interface cuts on reference simplex
1152 }
1153 _calculate_C(); // coefficients of equiv poly
1154
1155 double ma_scale, mb_scale;
1156 if (scale)
1157 {
1158 // cek hack - 2D, pressure basis for discontinuous density
1159 double jump_scale = level_set_normal[1],
1160 m_average = 0.5 * (ma + mb),
1161 m_jump = 0.5 * (mb - ma);
1162 mb_scale = m_average + jump_scale * m_jump; // mb when jump_scale=1
1163 ma_scale = m_average - jump_scale * m_jump; // ma when jump_scale=1
1164 // double mb_scale=mb, ma_scale=ma;
1165 // cek hack end
1166 }
1167 else
1168 {
1169 ma_scale = ma;
1170 mb_scale = mb;
1171 }
1172 // The two-sided IFEM basis is only implemented for 2D simplices with a P1
1173 // (nP_ifem 3) or P2 (nP_ifem 6) trial space; _calculate_basis_coefficients
1174 // asserts nSpace==2 and throws for any other nP_ifem. Every non-ADR caller
1175 // aliases GeneralizedFunctions_mix<nSpace, nP, nP, ...>, so VOF/MCorr reach
1176 // here with nP_ifem 2 and RANS3PF with 1: they need only the H/ImH/D moment
1177 // fit and must not enter the basis solve. Guard on nP_ifem, not just nSpace.
1178 if (useIfemBasis)
1179 _calculate_basis_coefficients(ma_scale, mb_scale, jf);
1180 // compute the default affine map based on phi_nodes[0]
1181 double Jac_0[nSpace * nSpace];
1182 for (unsigned int i = 0; i < nN - 1; i++)
1183 for (unsigned int I = 0; I < nSpace; I++)
1184 Jac_0[I * nSpace + i] = phi_nodes[(1 + i) * 3 + I] - phi_nodes[I];
1185
1186 if (!isBoundary)
1187 {
1188 for (unsigned int q = 0; q < nQ; q++)
1189 {
1190 // Due to the permutation, the quadrature points on the reference may be rotated
1191 // map reference to physical simplex, then back to permuted reference
1192 double x[nSpace], xi[nSpace];
1193 // to physical coordinates
1194 for (unsigned int I = 0; I < nSpace; I++)
1195 {
1196 x[I] = phi_nodes[I];
1197 for (unsigned int J = 0; J < nSpace; J++)
1198 {
1199 x[I] += Jac_0[I * nSpace + J] * xi_r[q * 3 + J];
1200 }
1201 }
1202 // back to reference coordinates on possibly permuted
1203 for (unsigned int I = 0; I < nSpace; I++)
1204 {
1205 xi[I] = 0.0;
1206 for (unsigned int J = 0; J < nSpace; J++)
1207 {
1208 xi[I] += inv_Jac[I * nSpace + J] * (x[J] - nodes[J]);
1209 }
1210 }
1211 if (nSpace == 1)
1212 _calculate_polynomial_1D<nP>(xi, C_H, C_ImH, C_D, _H[q], _ImH[q], _D[q]);
1213 else if (nSpace == 2)
1214 {
1215 _calculate_polynomial_2D<nP>(xi, C_H, C_ImH, C_D, _H[q], _ImH[q], _D[q]);
1216 // Only the IFEM instantiations have a two-sided basis; see the guard on
1217 // _calculate_basis_coefficients above.
1218 if (useIfemBasis)
1219 {
1220 _calculate_basis(xi, &_va[q * nP_ifem], &_vb[q * nP_ifem]);
1221 _calculate_basis_gradients(xi, &_va_x[q * nP_ifem], &_va_y[q * nP_ifem], &_vb_x[q * nP_ifem], &_vb_y[q * nP_ifem]);
1222 }
1223 }
1224 else if (nSpace == 3)
1225 _calculate_polynomial_3D<nP>(xi, C_H, C_ImH, C_D, _H[q], _ImH[q], _D[q]);
1226 }
1227 set_quad(0);
1228 }
1229 else
1230 {
1231 for (unsigned int ebq = 0; ebq < nEBQ; ebq++)
1232 {
1233 // Due to the permutation, the quadrature points on the reference may be rotated
1234 // map reference to physical simplex, then back to permuted reference
1235 double x[nSpace], xi[nSpace];
1236 // to physical coordinates
1237 for (unsigned int I = 0; I < nSpace; I++)
1238 {
1239 x[I] = phi_nodes[I];
1240 for (unsigned int J = 0; J < nSpace; J++)
1241 {
1242 x[I] += Jac_0[I * nSpace + J] * xi_r[ebq * 3 + J];
1243 }
1244 }
1245 // back to reference coordinates on possibly permuted
1246 for (unsigned int I = 0; I < nSpace; I++)
1247 {
1248 xi[I] = 0.0;
1249 for (unsigned int J = 0; J < nSpace; J++)
1250 {
1251 xi[I] += inv_Jac[I * nSpace + J] * (x[J] - nodes[J]);
1252 }
1253 }
1254 if (nSpace == 1)
1255 _calculate_polynomial_1D<nP>(xi, C_H, C_ImH, C_D, _H_ebq[ebq], _ImH_ebq[ebq], _D_ebq[ebq]);
1256 else if (nSpace == 2)
1257 {
1258 _calculate_polynomial_2D<nP>(xi, C_H, C_ImH, C_D, _H_ebq[ebq], _ImH_ebq[ebq], _D_ebq[ebq]);
1259 // Only the IFEM instantiations have a two-sided basis; see the guard on
1260 // _calculate_basis_coefficients above.
1261 if (useIfemBasis)
1262 {
1263 _calculate_basis(xi, &_va_ebq[ebq * nP_ifem], &_vb_ebq[ebq * nP_ifem]);
1264 _calculate_basis_gradients(xi, &_va_x_ebq[ebq * nP_ifem], &_va_y_ebq[ebq * nP_ifem], &_vb_x_ebq[ebq * nP_ifem], &_vb_y_ebq[ebq * nP_ifem]);
1265 }
1266 }
1267 else if (nSpace == 3)
1268 _calculate_polynomial_3D<nP>(xi, C_H, C_ImH, C_D, _H_ebq[ebq], _ImH_ebq[ebq], _D_ebq[ebq]);
1269 }
1271 }
1272 // IFEM needs the interface normal to follow its own inside_out convention, but
1273 // pre-IFEM this flip was deliberately disabled (it sat commented out inside the
1274 // old normal_sign block) and every other consumer -- VOF/MCorr/RDLS/RANS* --
1275 // still reads get_normal() expecting the unflipped normal. Re-enabling it for
1276 // them broke MCorr's mass correction. Gate it with the rest of the IFEM path.
1277 if (useIfemBasis && inside_out)
1278 for (unsigned int I = 0; I < nSpace; I++)
1279 level_set_normal[I] *= -1.0;
1280 return icase;
1281 }
1282
1283 template <int nSpace, int nP_ifem, int nP, int nQ, int nEBQ, bool useIfemBasis = false>
1285 {
1286 public:
1293
1294 inline int calculate(const double *phi_dof, const double *phi_nodes, const double *xi_r, double ma, double mb, double jf, bool isBoundary, bool scale)
1295 {
1296
1297 if (useExact)
1298 return exact.calculate(phi_dof, phi_nodes, xi_r, ma, mb, jf, isBoundary, scale);
1299 else // for inexact just copy over local phi_dof
1300 {
1301 for (int i = 0; i < exact.nN; i++)
1302 exact.phi_dof_corrected[i] = phi_dof[i];
1303 return 1;
1304 }
1305 }
1306
1307 inline int calculate(const double *phi_dof, const double *phi_nodes, const double *xi_r, double ma, double mb, bool isBoundary, bool scale)
1308 {
1309 return calculate(phi_dof, phi_nodes, xi_r, ma, mb, 0.0, isBoundary, false);
1310 }
1311
1312 inline int calculate(const double *phi_dof, const double *phi_nodes, const double *xi_r, bool isBoundary)
1313 {
1314 return calculate(phi_dof, phi_nodes, xi_r, 1.0, 1.0, 0.0, isBoundary, false);
1315 }
1316
1317 inline double *get_normal()
1318 {
1319 if (useExact)
1320 return exact.get_normal();
1321 else
1322 return regularized.get_normal();
1323 }
1324
1325 inline void set_quad(unsigned int q)
1326 {
1327 if (useExact)
1328 exact.set_quad(q);
1329 }
1330
1331 inline void set_boundary_quad(unsigned int ebq)
1332 {
1333 if (useExact)
1335 }
1336
1337 inline double H(double eps, double phi)
1338 {
1339 if (useExact)
1340 return exact.H(eps, phi);
1341 else
1342 return regularized.H(eps, phi);
1343 }
1344
1345 inline double ImH(double eps, double phi)
1346 {
1347 if (useExact)
1348 return exact.ImH(eps, phi);
1349 else
1350 return regularized.ImH(eps, phi);
1351 }
1352
1353 inline double D(double eps, double phi)
1354 {
1355 if (useExact)
1356 return exact.D(eps, phi);
1357 else
1358 return regularized.D(eps, phi);
1359 }
1360 inline double VA(int i)
1361 {
1362 if (useExact)
1363 return exact.VA(i);
1364 else
1365 return regularized.VA(i);
1366 }
1367 inline double VA_x(int i)
1368 {
1369 if (useExact)
1370 return exact.VA_x(i);
1371 else
1372 return regularized.VA_x(i);
1373 }
1374 inline double VA_y(int i)
1375 {
1376 if (useExact)
1377 return exact.VA_y(i);
1378 else
1379 return regularized.VA_y(i);
1380 }
1381 inline double VA_z(int i)
1382 {
1383 if (useExact)
1384 return exact.VA_z(i);
1385 else
1386 return regularized.VA_z(i);
1387 }
1388 inline double VB(int i)
1389 {
1390 if (useExact)
1391 return exact.VB(i);
1392 else
1393 return regularized.VB(i);
1394 }
1395 inline double VB_x(int i)
1396 {
1397 if (useExact)
1398 return exact.VB_x(i);
1399 else
1400 return regularized.VB_x(i);
1401 }
1402 inline double VB_y(int i)
1403 {
1404 if (useExact)
1405 return exact.VB_y(i);
1406 else
1407 return regularized.VB_y(i);
1408 }
1409 inline double VB_z(int i)
1410 {
1411 if (useExact)
1412 return exact.VB_z(i);
1413 else
1414 return regularized.VB_z(i);
1415 }
1416 };
1417} // equivalent_polynomials
1418
1419#endif
Double q
Definition Headers.h:81
Double v
Definition Headers.h:95
Double phi
Definition Headers.h:76
int calculate(const double *phi_dof, const double *phi_nodes, const double *xi_r, bool isBoundary)
int calculate(const double *phi_dof, const double *phi_nodes, const double *xi_r, double ma, double mb, double jf, bool isBoundary, bool scale)
int calculate(const double *phi_dof, const double *phi_nodes, const double *xi_r, double ma, double mb, bool isBoundary, bool scale)
int calculate(const double *phi_dof, const double *phi_nodes, const double *xi_r, double ma, double mb, bool isBoundary, bool scale)
int calculate(const double *phi_dof, const double *phi_nodes, const double *xi_r, double ma, double mb, double jf, bool isBoundary, bool scale)
int calculate(const double *phi_dof, const double *phi_nodes, const double *xi_r, bool isBoundary)
double ImH(double eps, double phi)
int calculate(const double *phi_dof, const double *phi_nodes, const double *xi_r, bool isBoundary)
double ImH(double eps, double phi)
int calculate(const double *phi_dof, const double *phi_nodes, const double *xi_r, double ma, double mb, bool isBoundary, bool scale)
double D(double eps, double phi)
int calculate(const double *phi_dof, const double *phi_nodes, const double *xi_r, double ma, double mb, double jf, bool isBoundary, bool scale)
double H(double eps, double phi)
void _calculate_polynomial_2D(double *xi, double *C_H, double *C_ImH, double *C_D, double &_H, double &_ImH, double &_D)
void inv(const double *A, double *Ainv)
void _calculate_normal_quad(double *phys_nodes_cut_quad_01, double *phys_nodes_cut_quad_02, double *phys_nodes_cut_quad_31, double *phys_nodes_cut_quad_32, double *level_set_normal)
void _calculate_polynomial_1D(double *xi, double *C_H, double *C_ImH, double *C_D, double &_H, double &_ImH, double &_D)
double det(const double *A)
void _set_Ainv(double *Ainv)
void _calculate_polynomial_3D(double *xi, double *C_H, double *C_ImH, double *C_D, double &_H, double &_ImH, double &_D)
void _calculate_normal(double *phys_nodes_cut, double *level_set_normal)
void _calculate_b(double *X_0, double *b_H, double *b_ImH, double *b_dH)
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)