OpenLB 1.7
Loading...
Searching...
No Matches
freeSurfaceHelpers.hh
Go to the documentation of this file.
1/* This file is part of the OpenLB library
2 *
3 * Copyright (C) 2021 Claudius Holeksa
4 * E-mail contact: info@openlb.net
5 * The most recent release of OpenLB can be downloaded at
6 * <http://www.openlb.net/>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this program; if not, write to the Free
20 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22*/
23
24namespace olb {
25
26namespace FreeSurface {
27
28template<typename T, typename DESCRIPTOR>
29std::enable_if_t<DESCRIPTOR::d == 2,T>
30offsetHelper(T volume, const Vector<T,DESCRIPTOR::d>& sorted_normal) any_platform {
31 T d2 = volume * sorted_normal[1] + 0.5 * sorted_normal[0];
32 if(d2 >= sorted_normal[0]){
33 return d2;
34 }
35
36 T d1 = util::sqrt(2. * sorted_normal[0] * sorted_normal[1] * volume);
37
38 return d1;
39}
40
41
42// A lot of magic numbers are happening here. Optimized algorithm taken from Moritz Lehmann
43template<typename T, typename DESCRIPTOR>
44std::enable_if_t<DESCRIPTOR::d == 3,T>
45offsetHelper(T volume, const Vector<T,DESCRIPTOR::d>& sorted_normal) any_platform {
46 T sn0_plus_sn1 = sorted_normal[0] + sorted_normal[1];
47 T sn0_times_sn1 = sorted_normal[0] * sorted_normal[1];
48 T sn2_volume = sorted_normal[2] * volume;
49
50 T min_sn0_plus_sn1_and_sn2 = util::min(sn0_plus_sn1, sorted_normal[2]);
51
52 T d5 = sn2_volume + 0.5 * sn0_plus_sn1;
53 if(d5 > min_sn0_plus_sn1_and_sn2 && d5 <= sorted_normal[2]){
54 return d5;
55 }
56
57 T d2 = 0.5 * sorted_normal[0] + 0.28867513 * util::sqrt( util::max(0., 24. * sorted_normal[1] * sn2_volume - sorted_normal[0]*sorted_normal[0]) );
58
59 if(d2 > sorted_normal[0] && d2 <= sorted_normal[1]){
60 return d2;
61 }
62
63 T d1 = std::cbrt(6.0 * sn0_times_sn1 * sn2_volume);
64 if(d1 <= sorted_normal[0]){
65 return d1;
66 }
67
68 T x3 = 81.0 * sn0_times_sn1 * (sn0_plus_sn1 - 2. * sn2_volume);
69 T y3 = util::sqrt(util::max(0., 23328. * sn0_times_sn1*sn0_times_sn1*sn0_times_sn1 - x3*x3 ));
70 T u3 = std::cbrt(x3*x3 + y3*y3);
71 T d3 = sn0_plus_sn1 - (7.5595264 * sn0_times_sn1 + 0.26456684 * u3) * (1./util::sqrt(u3)) * util::sin(0.5235988 - 0.3333334 * util::atan(y3 / x3));
72 if(d3 > sorted_normal[1] && d3 <= min_sn0_plus_sn1_and_sn2){
73 return d3;
74 }
75
76 T t4 = 9. * util::pow(sn0_plus_sn1 + sorted_normal[2], 2) - 18.;
77 T x4 = util::max(sn0_times_sn1 * sorted_normal[2] * (324. - 648. * volume), 1.1754944e-38);
78 T y4 = util::sqrt(util::max(4. * t4*t4*t4 - x4*x4, 0.));
79 T u4 = std::cbrt(x4*x4 + y4*y4);
80 T d4 = 0.5 * (sn0_plus_sn1 + sorted_normal[2]) - (0.20998684 * t4 + 0.13228342 * u4) * (1./util::sqrt(u4)) * util::sin(0.5235988- 0.3333334 * util::atan(y4/x4));
81
82 return d4;
83}
84
85// A lot of magic numbers are happening here. Optimized algorithm taken from Moritz Lehmann
86template<typename T, typename DESCRIPTOR>
88 const T sn0_p_sn1 = sn[0] + sn[1];
89 const T sn2_t_V = sn[2] * vol;
90
91 if(sn0_p_sn1 <= 2. * sn2_t_V){
92 return sn2_t_V + 0.5 * sn0_p_sn1;
93 }
94
95 const T sq_sn0 = util::pow(sn[0],2), sn1_6 = 6. * sn[1], v1 = sq_sn0 / sn1_6;
96
97 if(v1 <= sn2_t_V && sn2_t_V < v1 + 0.5 * (sn[1]-sn[0])){
98 return 0.5 *(sn[0] + util::sqrt(sq_sn0 + 8.0 * sn[1] * (sn2_t_V - v1)));
99 }
100
101 const T v6 = sn[0] * sn1_6 * sn2_t_V;
102 if(sn2_t_V < v1){
103 return std::cbrt(v6);
104 }
105
106 const T v3 = sn[2] < sn0_p_sn1 ? (util::pow(sn[2],2) * (3. * sn0_p_sn1 - sn[2]) + sq_sn0 *(sn[0] - 3.0 * sn[2]) + util::pow(sn[1],2)*(sn[1]-3.0 * sn[2])) / (sn[0] * sn1_6) : 0.5 * sn0_p_sn1;
107
108 const T sq_sn0_sq_sn1 = sq_sn0 + util::pow(sn[1],2), v6_cb_sn0_sn1 = v6 - util::pow(sn[0],3) - util::pow(sn[1],3);
109
110 const bool case34 = sn2_t_V < v3;
111 const T a = case34 ? v6_cb_sn0_sn1 : 0.5 * (v6_cb_sn0_sn1 - util::pow(sn[2], 3));
112 const T b = case34 ? sq_sn0_sq_sn1 : 0.5 * (sq_sn0_sq_sn1 + util::pow(sn[2], 2));
113 const T c = case34 ? sn0_p_sn1 : 0.5;
114 const T t = util::sqrt(util::pow(c,2) - b);
115 return c - 2.0 * t * util::sin(0.33333334 * util::asin((util::pow(c,3) - 0.5 * a - 1.5 * b * c) / util::pow(t,3)));
116}
117
118template<typename T, size_t S>
119std::array<T,S> solvePivotedLU(std::array<std::array<T,S>,S>& matrix, const std::array<T,S>& b, size_t N) {
120 std::array<T,S> x;
121 std::array<T,S> pivots;
122 for(size_t i = 0; i < S; ++i){
123 pivots[i] = i;
124 x[i] = 0.;
125 }
126
127 N = std::min(N,S);
128
129 for(size_t i = 0; i < N; ++i){
130
131 T max = 0.;
132 size_t max_index = i;
133
134 for(size_t j = i; j < N; ++j){
135 T abs = std::abs(matrix[pivots[j]][i]);
136 if(abs > max){
137 max_index = j;
138 max = abs;
139 }
140 }
141
142 if(max_index != i){
143 size_t tmp_index = pivots[i];
144 pivots[i] = pivots[max_index];
145 pivots[max_index] = tmp_index;
146 }
147
148 for(size_t j = i + 1; j < N; ++j){
149 matrix[pivots[j]][i] /= matrix[pivots[i]][i];
150
151 for(size_t k = i + 1; k < N; ++k){
152
153 matrix[pivots[j]][k] -= matrix[pivots[j]][i] * matrix[pivots[i]][k];
154 }
155 }
156 }
157
158 for(size_t i = 0; i < N; ++i){
159 x[i] = b[pivots[i]];
160
161 for(size_t j = 0; j < i; ++j){
162 x[i] -= matrix[pivots[i]][j] * x[j];
163 }
164 }
165
166 for(size_t i = N; i > 0; --i){
167 for(size_t j = i; j < N; ++j){
168 x[i-1] -= matrix[pivots[i-1]][j] * x[j];
169 }
170
171 x[i-1] /= matrix[pivots[i-1]][i-1];
172 }
173
174 return x;
175}
176
177template<typename T, typename DESCRIPTOR>
185
186template <typename CELL>
187bool isCellType(CELL& cell, const FreeSurface::Type& type) {
188 return cell.template getField<FreeSurface::CELL_TYPE>() == type;
189}
190
191template <typename CELL>
192bool hasCellFlags(CELL& cell, const FreeSurface::Flags& flags) {
193 return static_cast<bool>(cell.template getField<FreeSurface::CELL_FLAGS>() & flags);
194}
195
196template <typename CELL>
197bool hasNeighbour(CELL& cell, const FreeSurface::Type& type) {
198 using DESCRIPTOR = typename CELL::descriptor_t;
199 for(int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
200 auto cellC = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
201 if(isCellType(cellC, type)) {
202 return true;
203 }
204 }
205
206 return false;
207}
208
209
210template <typename CELL>
211bool hasNeighbourFlags(CELL& cell, const FreeSurface::Flags& flags) {
212 using DESCRIPTOR = typename CELL::descriptor_t;
213 for(int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
214 auto cellC = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
215 if(hasCellFlags(cellC, flags)) {
216 return true;
217 }
218 }
219
220 return false;
221}
222
223template <typename CELL, typename V>
224Vector<V,CELL::descriptor_t::d> computeInterfaceNormal(CELL& cell) {
226 normal[0] = 0.5 * ( getClampedEpsilon(cell.neighbor({-1, 0, 0}))
227 - getClampedEpsilon(cell.neighbor({ 1, 0, 0})));
228 normal[1] = 0.5 * ( getClampedEpsilon(cell.neighbor({ 0,-1, 0}))
229 - getClampedEpsilon(cell.neighbor({ 0, 1, 0})));
230 normal[2] = 0.5 * ( getClampedEpsilon(cell.neighbor({ 0, 0,-1}))
231 - getClampedEpsilon(cell.neighbor({ 0, 0, 1})));
232 return normal;
233}
234
235template <typename CELL, typename V>
236Vector<V,CELL::descriptor_t::d> computeParkerYoungInterfaceNormal(CELL& cell) {
237 using DESCRIPTOR = typename CELL::descriptor_t;
238
240
241 for(size_t dim = 0; dim < CELL::descriptor_t::d; ++dim){
242 normal[dim] = 0;
243 }
244
245 for(int iPop = 1; iPop < DESCRIPTOR::q; iPop++) {
246 int omega_weight = 1;
247 if(descriptors::c<DESCRIPTOR>(iPop, 0) != 0){
248 omega_weight *= 2;
249 }
250 if(descriptors::c<DESCRIPTOR>(iPop, 1) != 0){
251 omega_weight *= 2;
252 }
253
254 // For the 3D case
255 if(CELL::descriptor_t::d == 3 && descriptors::c<DESCRIPTOR>(iPop)[2] != 0){
256 omega_weight *= 2;
257 }
258
259 omega_weight /= 2;
260
261 auto cellC = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
262 V epsilon = getClampedEpsilon(cellC);
263
264 normal[0] -= omega_weight * (descriptors::c<DESCRIPTOR>(iPop, 0) * epsilon);
265 normal[1] -= omega_weight * (descriptors::c<DESCRIPTOR>(iPop, 1) * epsilon);
266 if(CELL::descriptor_t::d == 3){
267 normal[2] -= omega_weight * (descriptors::c<DESCRIPTOR>(iPop, 2) * epsilon);
268 }
269
270 }
271
272 return normal;
273}
274
275template <typename CELL, typename V>
276V getClampedEpsilon(CELL& cell) {
277
278 V epsilon = cell.template getField<FreeSurface::EPSILON>();
279 return util::max(0., util::min(1., epsilon));
280}
281
282/*
283template <typename CELL, typename V>
284V getClampedEpsilonCorrected(CELL& cell) {
285
286 if(isCellType(cell, FreeSurface::Type::Interface) && !hasNeighbour(cell, FreeSurface::Type::Gas)){
287 return 1.0;
288 }
289
290 V epsilon = cell.template getField<FreeSurface::EPSILON>();
291
292 return util::max(0., util::min(1., epsilon));
293}
294*/
295
296template<typename T, typename DESCRIPTOR>
297T calculateCubeOffset(T volume, const Vector<T,DESCRIPTOR::d>& normal) {
298 std::vector<T> abs_normal(DESCRIPTOR::d, T{0});
299 for(int i = 0; i < DESCRIPTOR::d; i++){
300 abs_normal[i] = util::abs(normal[i]);
301 }
302
303 T volume_symmetry = 0.5 - util::abs(volume - 0.5);
304
305 std::sort(abs_normal.begin(), abs_normal.end());
306
307 if constexpr (DESCRIPTOR::d == 2) {
308 abs_normal[0] = util::max(normal[0], 1e-5);
309 } else if (DESCRIPTOR::d == 3){
310 abs_normal[0] = util::max(normal[0], 1e-12);
311 abs_normal[1] = util::max(normal[1], 1e-12);
312 }
313
314 T d = offsetHelper<T,DESCRIPTOR>(volume_symmetry, abs_normal);
315
316 T sorted_normal_acc = 0;
317 for(int i = 0; i < DESCRIPTOR::d; i++){
318 sorted_normal_acc += abs_normal[i];
319 }
320
321 return std::copysign(d - 0.5 * sorted_normal_acc, volume - 0.5);
322}
323
324// Optimized version of function calculateCubeOffset for 3D
325template<typename T, typename DESCRIPTOR>
326T calculateCubeOffsetOpt(T volume, const Vector<T,DESCRIPTOR::d>& normal) {
328
329 abs_normal[0] = util::abs(normal[0]);
330 abs_normal[1] = util::abs(normal[1]);
331 abs_normal[2] = util::abs(normal[2]);
332
333 T a_l1 = abs_normal[0] + abs_normal[1] + abs_normal[2];
334
335 T volume_symmetry = 0.5 - util::abs(volume - 0.5);
336
337 olb::Vector<T,DESCRIPTOR::d> sorted_normal;
338 sorted_normal[0] = util::min(util::min(abs_normal[0], abs_normal[1]), abs_normal[2]) / a_l1;
339 sorted_normal[1] = 0.;
340 sorted_normal[2] = util::max(util::max(abs_normal[0], abs_normal[1]), abs_normal[2]) / a_l1;
341
342 sorted_normal[1] = util::max(1. - sorted_normal[0] - sorted_normal[2], 0.);
343
344 T d = offsetHelperOpt<T,DESCRIPTOR>(volume_symmetry, sorted_normal);
345
346 return a_l1 * std::copysign(0.5 - d, volume - 0.5);
347}
348
349template <typename CELL, typename V>
350V calculateSurfaceTensionCurvature(CELL& cell) {
351 using DESCRIPTOR = typename CELL::descriptor_t;
352
353 if constexpr (DESCRIPTOR::d == 2) {
354 return calculateSurfaceTensionCurvature2D(cell);
355 } else if (DESCRIPTOR::d == 3){
356 return calculateSurfaceTensionCurvature3D(cell);
357 }
358
359 return 0;
360}
361
362template <typename CELL, typename V>
363V calculateSurfaceTensionCurvature2D(CELL& cell) {
364 auto normal = computeParkerYoungInterfaceNormal(cell);
365
366 using DESCRIPTOR = typename CELL::descriptor_t;
367 {
368 V norm = 0.;
369 for(size_t i = 0; i < DESCRIPTOR::d; ++i){
370 norm += normal[i] * normal[i];
371 }
372
374
375 if(norm < 1e-6){
376 return 0.;
377 }
378
379 for(size_t i = 0; i <DESCRIPTOR::d; ++i){
380 normal[i] /= norm;
381 }
382 }
383
384 // Rotation matrix is
385 // ( n1 | -n0 )
386 // ( n0 | n1 )
387
388 // It is 2 because of the amount of fitting parameters. Not because of the dimension
389 constexpr size_t S = 2;
390 std::array<std::array<V,S>, S> lq_matrix;
391 std::array<V,S> b_rhs;
392 for(size_t i = 0; i < S; ++i){
393 for(size_t j = 0; j < S; ++j){
394 lq_matrix[i][j] = 0.;
395 }
396 b_rhs[i] = 0.;
397 }
398
399 // Offset for the plic correction
400 V origin_offset = 0.;
401 {
402 V fill_level = getClampedEpsilon(cell);
403 origin_offset = calculateCubeOffset<V,DESCRIPTOR>(fill_level, normal);
404 }
405
406 // The amount of neighbouring interfaces. if less are available we will solve a reduced curve by setting the less important parameters to zero
407 std::size_t healthy_interfaces = 0;
408
409 for (int iPop=1; iPop < DESCRIPTOR::q; ++iPop) {
410 auto cellC = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
411
412 if( !isCellType(cellC, FreeSurface::Type::Interface)
413 || !hasNeighbour(cellC, FreeSurface::Type::Gas)) {
414 continue;
415 }
416
417 ++healthy_interfaces;
418
419 V fill_level = getClampedEpsilon(cellC);
420
421 V cube_offset = calculateCubeOffset<V,DESCRIPTOR>(fill_level, normal);
422
423 V x_pos = descriptors::c<DESCRIPTOR>(iPop,0);
424 V y_pos = descriptors::c<DESCRIPTOR>(iPop,1);
425
426 // Rotation
427 V rot_x_pos = x_pos * normal[1] - y_pos * normal[0];
428 V rot_y_pos = x_pos * normal[0] + y_pos * normal[1] + (cube_offset - origin_offset);
429
430 V rot_x_pos_2 = rot_x_pos * rot_x_pos;
431 V rot_x_pos_3 = rot_x_pos_2 * rot_x_pos;
432 V rot_x_pos_4 = rot_x_pos_3 * rot_x_pos;
433
434 lq_matrix[1][1] += rot_x_pos_2;
435 lq_matrix[1][0] += rot_x_pos_3;
436 lq_matrix[0][0] += rot_x_pos_4;
437
438 b_rhs[0] += rot_x_pos_2*(rot_y_pos);
439 b_rhs[1] += rot_x_pos*(rot_y_pos);
440 }
441
442 lq_matrix[0][1] = lq_matrix[1][0];
443
444 // Thikonov regularization parameter
445 V alpha = 0.0;
446 for(size_t i = 0; i < DESCRIPTOR::d; ++i){
447 lq_matrix[i][i] += alpha;
448 }
449
450 // It is 2 because of the fitting parameters. Not dependent on the dimension
451 std::array<V,S> solved_fit = FreeSurface::solvePivotedLU<V,S>(lq_matrix, b_rhs, healthy_interfaces);
452
453 // signed curvature -> kappa = y'' / ( (1 + y'²)^(3/2) )
454 V denom = std::sqrt(1. + solved_fit[1]*solved_fit[1]);
455 denom = denom * denom * denom;
456 V curvature = 2.*solved_fit[0] / denom;
457 return util::max(-1., util::min(1., curvature));
458}
459
460template <typename CELL, typename V>
461V calculateSurfaceTensionCurvature3D(CELL& cell){
462 // This is b_z
463 auto normal = computeParkerYoungInterfaceNormal(cell);
464
465 using DESCRIPTOR = typename CELL::descriptor_t;
466 {
467 V norm = 0.;
468 for(size_t i = 0; i < DESCRIPTOR::d; ++i){
469 norm += normal[i] * normal[i];
470 }
471
473
474 if(norm < 1e-12){
475 return 0.;
476 }
477
478 for(size_t i = 0; i <DESCRIPTOR::d; ++i){
479 normal[i] /= norm;
480 }
481 }
482
483 std::array<V,3> r_vec{
484 0.56270900, 0.32704452, 0.75921047
485 };
486 /*
487 std::array<T,DESCRIPTOR::d> r_vec{
488 0.,0.,1.
489 };
490 */
491 std::array<std::array<V,3>,3> rotation{{
492 {{0., 0., 0.}},
493 //{{normal[1], -normal[0], 0.}},
494 {{normal[1] * r_vec[2] - normal[2] * r_vec[1], normal[2] * r_vec[0] - normal[0] * r_vec[2], normal[0] * r_vec[1] - normal[1] * r_vec[0]}},
495 {{normal[0], normal[1], normal[2]}}
496 }};
497
498 // Cross product with (0,0,1) x normal
499 // This is b_y
500
501 // (normal[0], normal[1], normal[2])
502
503 V cross_norm = 0.;
504 for(size_t i = 0; i < DESCRIPTOR::d; ++i){
505 cross_norm += rotation[1][i] * rotation[1][i];
506 }
507
508 // If too close too each other use the identity matrix
509 if(cross_norm > 1e-6){
510
511 cross_norm = util::sqrt(cross_norm);
512
513 for(size_t i = 0; i <DESCRIPTOR::d; ++i){
514 rotation[1][i] /= cross_norm;
515 }
516 }else {
517
518 rotation[1] = {{
519 -normal[2],
520 0.,
521 normal[0]
522 }};
523
524 cross_norm = 0.;
525 for(size_t i = 0; i < DESCRIPTOR::d; ++i){
526 cross_norm += rotation[1][i] * rotation[1][i];
527 }
528
529 cross_norm = util::sqrt(cross_norm);
530
531 for(size_t i = 0; i <DESCRIPTOR::d; ++i){
532 rotation[1][i] /= cross_norm;
533 }
534 }
535
536 // Cross product of ((0,0,1) x normal / | (0,0,1) x normal |) x normal
537 // This is b_x
538 rotation[0] = {{
539 rotation[1][1] * normal[2] - rotation[1][2] * normal[1],
540 rotation[1][2] * normal[0] - rotation[1][0] * normal[2],
541 rotation[1][0] * normal[1] - rotation[1][1] * normal[0]
542 }};
543
544 // These three form a matrix and are entered into each row
545 // ( b_x )
546 // ( b_y )
547 // ( b_z )
548
549 constexpr size_t S = 5;
550 std::array<std::array<V,S>, S> lq_matrix;
551 std::array<V,S> b_rhs;
552 for(size_t i = 0; i < S; ++i){
553 for(size_t j = 0; j < S; ++j){
554 lq_matrix[i][j] = 0.;
555 }
556 b_rhs[i] = 0.;
557 }
558 V origin_offset = 0.;
559 {
560 V fill_level = getClampedEpsilon(cell);
561 origin_offset = calculateCubeOffsetOpt<V,DESCRIPTOR>(fill_level, normal);
562 }
563
564 size_t healthy_interfaces = 0;
565 for(int iPop = 1; iPop < DESCRIPTOR::q; iPop++){
566 auto cellC = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
567
568 if(!isCellType(cellC, FreeSurface::Type::Interface) || !hasNeighbour(cellC, FreeSurface::Type::Gas)){
569 continue;
570 }
571
572 ++healthy_interfaces;
573
574 V fill_level = getClampedEpsilon(cellC);
575
576 V cube_offset = calculateCubeOffsetOpt<V, DESCRIPTOR>(fill_level, normal);
577
578 int i = descriptors::c<DESCRIPTOR>(iPop)[0];
579 int j = descriptors::c<DESCRIPTOR>(iPop)[1];
580 int k = descriptors::c<DESCRIPTOR>(iPop)[2];
581
582 std::array<V,3> pos{static_cast<V>(i),static_cast<V>(j),static_cast<V>(k)};
583 std::array<V,3> r_pos{0.,0.,cube_offset - origin_offset};
584
585 for(size_t a = 0; a < DESCRIPTOR::d; ++a){
586 for(size_t b = 0; b < DESCRIPTOR::d; ++b){
587 r_pos[a] += rotation[a][b] * pos[b];
588 }
589 }
590
591 V r_x_2 = r_pos[0] * r_pos[0];
592 V r_x_3 = r_x_2 * r_pos[0];
593 V r_x_4 = r_x_3 * r_pos[0];
594
595 V r_y_2 = r_pos[1] * r_pos[1];
596 V r_y_3 = r_y_2 * r_pos[1];
597 V r_y_4 = r_y_3 * r_pos[1];
598
599 V r_x_2_y_2 = r_x_2 * r_y_2;
600 V r_x_3_y = r_x_3 * r_pos[1];
601 V r_x_2_y = r_x_2 * r_pos[1];
602
603 V r_x_y_3 = r_pos[0] * r_y_3;
604 V r_x_y_2 = r_pos[0] * r_y_2;
605
606 V r_x_y = r_pos[0] * r_pos[1];
607
608 lq_matrix[0][0] += r_x_4;
609 lq_matrix[1][1] += r_y_4;
610 lq_matrix[2][2] += r_x_2_y_2;
611 lq_matrix[3][3] += r_x_2;
612 lq_matrix[4][4] += r_y_2;
613
614 // skip [1][0] copy later from [2][2]
615 lq_matrix[2][0] += r_x_3_y;
616 lq_matrix[3][0] += r_x_3;
617 lq_matrix[4][0] += r_x_2_y;
618
619 lq_matrix[2][1] += r_x_y_3;
620 lq_matrix[3][1] += r_x_y_2;
621 lq_matrix[4][1] += r_y_3;
622
623 // skip [3][2] copy from [4][0]
624 // skip [4][2] copy from [3][1]
625
626 lq_matrix[4][3] += r_x_y;
627
628 b_rhs[0] += r_x_2 * r_pos[2];
629 b_rhs[1] += r_y_2 * r_pos[2];
630 b_rhs[2] += r_x_y * r_pos[2];
631 b_rhs[3] += r_pos[0] * r_pos[2];
632 b_rhs[4] += r_pos[1] * r_pos[2];
633 }
634
635 lq_matrix[1][0] = lq_matrix[2][2];
636 lq_matrix[3][2] = lq_matrix[4][0];
637 lq_matrix[4][2] = lq_matrix[3][1];
638
639 for(size_t i = 0; i < S; ++i){
640 for(size_t j = i + 1; j < S; ++j){
641 lq_matrix[i][j] = lq_matrix[j][i];
642 }
643 }
644
645 // Consider using Thikonov regularization?
646 //T alpha = 1e-8;
647 V alpha = 0.0;
648 for(size_t i = 0; i < S; ++i){
649 lq_matrix[i][i] += alpha;
650 }
651
652 std::array<V,S> solved_fit = FreeSurface::solvePivotedLU<V,S>(lq_matrix, b_rhs, healthy_interfaces);
653
654 V denom = std::sqrt(1. + solved_fit[3]*solved_fit[3] + solved_fit[4]*solved_fit[4]);
655 denom = denom * denom * denom;
656 V curvature = ( (1.+solved_fit[4]*solved_fit[4]) * solved_fit[0] + (1. + solved_fit[3]*solved_fit[3] ) * solved_fit[1] - solved_fit[3] * solved_fit[4] * solved_fit[2] ) / denom;
657
658 return util::max(-1., util::min(1., curvature));
659}
660
661template<typename T, typename DESCRIPTOR>
662T plicInverse(T d_o, const Vector<T,DESCRIPTOR::d>& normal){
663
664 Vector<T,DESCRIPTOR::d> abs_normal;
665 for(int i = 0; i < DESCRIPTOR::d; i++){
666 abs_normal[i] = util::abs(normal[i]);
667 }
668
669 //const T n1 = std::min_element(abs_normal.begin(), abs_normal.end());
670 //const T n2 = std::max_element(abs_normal.begin(), abs_normal.end());
671
672 const T n1 = abs_normal[0];
673 const T n2 = abs_normal[1];
674
675 for(int i = 0; i < DESCRIPTOR::d; i++){
676 if(abs_normal[i] < n1){
677 n1 = abs_normal[i];
678 }
679
680 if(abs_normal[i] > n2){
681 n2 = abs_normal[i];
682 }
683 }
684
685 T abs_normal_acc = 0;
686 for(int i = 0; i < DESCRIPTOR::d; i++){
687 abs_normal_acc += abs_normal[i];
688 }
689 const T n3 = abs_normal_acc - n1 - n2;
690 const T d = 0.5 * (n1+n2+n3) - util::abs(d_o);
691 T vol;
692
693 if(DESCRIPTOR::d == 2){
694 if(d < n1){
695 vol = d * d / (2. * n1 * n2);
696 } else if(d >= n1){
697 vol = d / n2 - n1 / (2. * n2);
698 }
699 }
700
701 if(DESCRIPTOR::d == 3){
702 if(util::min(n1+n3,n2) <= d && d <= n2){
703 vol = (d-0.5 *(n1+n3))/n2;
704 } else if(d < n1){
705 vol = util::pow(d,3) / (6. * n1 * n2 * n3);
706 } else if(d <= n3){
707 vol = (3.0 * d * (d-n1) + std::pow(n1,2))/(6. * n2 * n3);
708 } else {
709 vol = (util::pow(d,3) - util::pow(d-n1,3) - util::pow(d-n3,3) - util::pow(util::max(0., d-n2),3)) / (6. * n1* n2 * n3);
710 }
711 }
712
713 return std::copysign(0.5 - vol, d_o) + 0.5;
714}
715
716template <typename CELL>
717NeighbourInfo getNeighbourInfo(CELL& cell) {
718 NeighbourInfo info{};
719 using DESCRIPTOR = typename CELL::descriptor_t;
720
721 for(int iPop = 1; iPop < DESCRIPTOR::q; ++iPop){
722 auto cellC = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
723
724 if(isCellType(cellC, FreeSurface::Type::Gas)){
725 info.has_gas_neighbours = true;
726 }
727 else if(isCellType(cellC, FreeSurface::Type::Fluid)){
728 info.has_fluid_neighbours = true;
729 }
730 else if(isCellType(cellC, FreeSurface::Type::Interface)){
731 ++info.interface_neighbours;
732 }
733 }
734 return info;
735}
736
737template <typename CELL, typename V>
738bool isHealthyInterface(CELL& cell) {
739 bool has_fluid_neighbours = false;
740 bool has_gas_neighbours = false;
741
742 if(!isCellType(cell, FreeSurface::Type::Interface)){
743 return false;
744 }
745
746 using DESCRIPTOR = typename CELL::descriptor_t;
747 for(int iPop = 1; iPop < DESCRIPTOR::q; ++iPop){
748 auto cellC = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
749 if(isCellType(cellC, FreeSurface::Type::Gas)){
750 has_gas_neighbours = true;
751 if(has_fluid_neighbours){
752 return true;
753 }
754 }
755 else if(isCellType(cellC, FreeSurface::Type::Fluid)){
756 has_fluid_neighbours = true;
757 if(has_gas_neighbours){
758 return true;
759 }
760 }
761 }
762 return false;
763}
764
765template <typename CELL, typename V>
766void setCellType(CELL& cell, const FreeSurface::Type& type) {
767 cell.template setField<FreeSurface::CELL_TYPE>(type);
768}
769
770template <typename CELL, typename V>
771void setCellFlags(CELL& cell, const FreeSurface::Flags& flags){
772 cell.template setField<FreeSurface::CELL_FLAGS>(flags);
773}
774
775} // namespace FreeSurface
776
777} // namespace olb
Super class maintaining block lattices for a cuboid decomposition.
void executePostProcessors(STAGE stage=STAGE())
Executes post processors for STAGE.
Plain old scalar vector.
Definition vector.h:47
std::enable_if_t< DESCRIPTOR::d==2, T > offsetHelper(T volume, const Vector< T, DESCRIPTOR::d > &sorted_normal) any_platform
void initialize(SuperLattice< T, DESCRIPTOR > &lattice)
T offsetHelperOpt(T vol, const Vector< T, DESCRIPTOR::d > &sn) any_platform
cpu::simd::Pack< T > sqrt(cpu::simd::Pack< T > value)
Definition pack.h:100
ADf< T, DIM > abs(const ADf< T, DIM > &a)
Definition aDiff.h:1019
cpu::simd::Pack< T > min(cpu::simd::Pack< T > rhs, cpu::simd::Pack< T > lhs)
Definition pack.h:124
cpu::simd::Pack< T > max(cpu::simd::Pack< T > rhs, cpu::simd::Pack< T > lhs)
Definition pack.h:130
ADf< T, DIM > sin(const ADf< T, DIM > &a)
Definition aDiff.h:569
ADf< T, DIM > asin(const ADf< T, DIM > &a)
Definition aDiff.h:596
cpu::simd::Pack< T > pow(cpu::simd::Pack< T > base, cpu::simd::Pack< T > exp)
Definition pack.h:112
ADf< T, DIM > atan(const ADf< T, DIM > &a)
Definition aDiff.h:614
Top level namespace for all of OpenLB.
std::enable_if_t< std::is_arithmetic< T >::type::value, T > abs(T x)
Definition util.h:396
constexpr T norm(const ScalarVector< T, D, IMPL > &a)
Euclidean vector norm.
#define any_platform
Define preprocessor macros for device-side functions, constant storage.
Definition platform.h:78