OpenLB 1.8.1
Loading...
Searching...
No Matches
olb::FreeSurface Namespace Reference

Classes

struct  CELL_FLAGS
 
struct  CELL_TYPE
 
struct  DROP_ISOLATED_CELLS
 
struct  EPSILON
 
struct  HAS_SURFACE_TENSION
 
struct  LONELY_THRESHOLD
 
struct  MASS
 
struct  NeighbourInfo
 
struct  PREVIOUS_VELOCITY
 
struct  Stage0
 
struct  Stage1
 
struct  Stage2
 
struct  Stage3
 
struct  Stage4
 
struct  Stage5
 
struct  SURFACE_TENSION_PARAMETER
 
struct  TEMP_MASS_EXCHANGE
 
struct  TRANSITION
 

Enumerations

enum class  SolverType { rowPivotingLU , completePivotingLU , columnPivotingQR }
 
enum class  Type : std::uint8_t { Gas = 0 , Interface = 1 , Fluid = 2 , Solid = 4 }
 
enum class  Flags : std::uint8_t { None = 0 , ToGas = 1 , ToFluid = 2 , NewInterface = 4 }
 

Functions

template<typename T >
constexpr T zeroThreshold ()
 
template<typename T >
bool isNearZero (T a, T threshold)
 
template<typename T , typename DESCRIPTOR >
void initialize (SuperLattice< T, DESCRIPTOR > &sLattice)
 
template<typename CELL >
bool isCellType (CELL &cell, const FreeSurface::Type &type)
 
template<typename CELL >
void setCellType (CELL &cell, const FreeSurface::Type &type)
 
template<typename CELL >
bool hasNeighbour (CELL &cell, const FreeSurface::Type &type)
 
template<typename CELL >
bool hasCellFlags (CELL &cell, const FreeSurface::Flags &flags)
 
template<typename CELL >
void setCellFlags (CELL &cell, const FreeSurface::Flags &flags)
 
template<typename CELL >
bool hasNeighbourFlags (CELL &cell, const FreeSurface::Flags &flags)
 
template<typename CELL >
NeighbourInfo getNeighbourInfo (CELL &cell)
 
template<typename CELL , typename V >
bool isHealthyInterface (CELL &cell)
 
template<typename CELL , typename V >
void computeMassExcessWeights (CELL &cell, const Vector< V, CELL::descriptor_t::d > &normal, const bool &enableAllInterfaces, Vector< V, CELL::descriptor_t::q > &weights)
 
template<typename CELL , typename V >
getClampedEpsilon (const CELL &cell)
 
template<typename CELL , typename V >
getSmoothEpsilon (CELL &cell)
 
template<typename CELL , typename V >
getClampedSmoothEpsilon (const CELL &cell)
 
template<typename CELL , typename V >
Vector< V, CELL::descriptor_t::d > computeInterfaceNormal (CELL &cell)
 
template<typename T , int N>
void iterativeRefinement (const std::array< T, N *N > &A, const std::array< T, N *N > &M, std::array< T, N > &x, const std::array< T, N > &b, const int Nsol, int maxIter)
 
template<typename T , int N>
void rowPivotingLUSolver (std::array< T, N *N > &M, std::array< T, N > &x, std::array< T, N > &b, const int Nsol)
 
template<typename T , int N>
void completePivotingLUSolver (std::array< T, N *N > &M, std::array< T, N > &x, std::array< T, N > &b, const int Nsol)
 
template<typename T , int N>
void columnPivotingQRSolver (std::array< T, N *N > &M, std::array< T, N > &x, std::array< T, N > &b, const int Nsol)
 
template<typename T >
plicCubeReduced (const T &volume, const Vector< T, 3 > &n)
 
template<typename T >
plicCube (const T &volume, const Vector< T, 3 > &n)
 
template<typename T >
plicCubeInverse (const T &d0, const Vector< T, 3 > &n)
 
template<typename CELL , typename V >
computeCurvaturePLIC2D (CELL &cell)
 
template<typename CELL , typename V >
computeCurvaturePLIC3D (CELL &cell)
 
template<typename CELL , typename V >
computeCurvatureFDM3D (CELL &cell)
 
template<typename CELL , typename V >
computeCurvature (CELL &cell)
 

Variables

constexpr SolverType solver = SolverType::completePivotingLU
 

Enumeration Type Documentation

◆ Flags

enum class olb::FreeSurface::Flags : std::uint8_t
strong
Enumerator
None 
ToGas 
ToFluid 
NewInterface 

Definition at line 72 of file freeSurfaceHelpers.h.

72 : std::uint8_t {
73 None = 0,
74 ToGas = 1,
75 ToFluid = 2,
76 NewInterface = 4
77};

◆ SolverType

enum class olb::FreeSurface::SolverType
strong
Enumerator
rowPivotingLU 
completePivotingLU 
columnPivotingQR 

Definition at line 58 of file freeSurfaceHelpers.h.

◆ Type

enum class olb::FreeSurface::Type : std::uint8_t
strong
Enumerator
Gas 
Interface 
Fluid 
Solid 

Definition at line 65 of file freeSurfaceHelpers.h.

65 : std::uint8_t {
66 Gas = 0,
67 Interface = 1,
68 Fluid = 2,
69 Solid = 4
70};

Function Documentation

◆ columnPivotingQRSolver()

template<typename T , int N>
void olb::FreeSurface::columnPivotingQRSolver ( std::array< T, N *N > & M,
std::array< T, N > & x,
std::array< T, N > & b,
const int Nsol )

Definition at line 448 of file freeSurfaceHelpers.hh.

448 {
449 // Permutation array to record column swaps.
450 std::array<int, N> col_perm{};
451 for (int i = 0; i < N; ++i) { col_perm[i] = i; }
452
453 // Pre-computed column norm array, i.e., L2-normalization of each column.
454 std::array<T, N> col_norm{}, col_norm_tmp{};
455 for (int i = 0; i < Nsol; ++i) {
456 T norm = T(0);
457 for (int j = 0; j < Nsol; ++j) { norm += M[j * N + i] * M[j * N + i]; }
458 col_norm[i] = util::sqrt(norm);
459 col_norm_tmp[i] = col_norm[i];
460 }
461
462 // QR decomposition method with column pivoting.
463 for (int i = 0; i < Nsol; ++i) {
464 int pivotCol = i;
465 T maxValue = col_norm[i];
466
467 for (int j = i + 1; j < Nsol; ++j) {
468 if (col_norm[j] > maxValue) {
469 maxValue = col_norm[j];
470 pivotCol = j;
471 }
472 }
473
474 if (pivotCol != i) {
475 for (int k = 0; k < Nsol; ++k) { std::swap(M[k * N + i], M[k * N + pivotCol]); }
476 std::swap(col_perm[i], col_perm[pivotCol]);
477 std::swap(col_norm[i], col_norm[pivotCol]);
478 std::swap(col_norm_tmp[i], col_norm_tmp[pivotCol]);
479 }
480
481 // Compute the L2-norm of the i-th column, i.e., range is from row (i) to (Nsol - 1),
482 // and the reflection factor (r)
483 T r = T(0);
484 {
485 T norm = T(0);
486 for (int k = i; k < Nsol; ++k) { norm += M[k * N + i] * M[k * N + i]; }
487 norm = util::sqrt(norm);
488 r = M[i * N + i] >= T(0) ? -norm : norm;
489 }
490
491 // Prepare and normalize the Householder array for the i-th column.
492 std::array<T, N> v{};
493 {
494 v[0] = M[i * N + i] - r;
495 for (int k = i + 1; k < Nsol; ++k) { v[k - i] = M[k * N + i]; }
496
497 T norm = T(0);
498 for (int k = 0; k < Nsol - i; ++k) { norm += v[k] * v[k]; }
499 norm = util::sqrt(norm);
500
501 // Otherwise, the Householder array is already zero and no reflection is applied.
502 if (norm != T(0)) {
503 for (int k = 0; k < Nsol - i; ++k) { v[k] /= norm; }
504 }
505 }
506
507 // Apply the Householder reflection to the remaining columns.
508 for (int k = i; k < Nsol; ++k) {
509 T dot_product = T(0);
510 for (int j = 0; j < Nsol - i; ++j) { dot_product += v[j] * M[(j + i) * N + k]; }
511
512 dot_product *= T(2);
513 for (int j = 0; j < Nsol - i; ++j) { M[(j + i) * N + k] -= v[j] * dot_product; }
514 }
515
516 // Apply the Householder reflection to the right-hand side array.
517 {
518 T dot_product = T(0);
519 for (int k = 0; k < Nsol - i; ++k) { dot_product += v[k] * b[k + i]; }
520
521 dot_product *= T(2);
522 for (int k = 0; k < Nsol - i; ++k) { b[k + i] -= v[k] * dot_product; }
523 }
524
525 // Update the column norm array, i.e., adapted from LAPACK’s DGEQP3 routine.
526 for (int k = i + 1; k < Nsol; ++k) {
527 T tmp = col_norm[k] * col_norm[k] - M[i * N + k] * M[i * N + k];
528
529 // Recompute L2-norm from scratch, if tmp becomes negative or it drops below
530 // a specified threshold, e.g., T(0.1)
531 if (tmp <= T(0) || util::sqrt(tmp) < T(0.1) * col_norm_tmp[k]) {
532 T norm = T(0);
533 for (int j = i + 1; j < Nsol; ++j) { norm += M[j * N + k] * M[j * N + k]; }
534 col_norm[k] = util::sqrt(norm);
535 col_norm_tmp[k] = col_norm[k];
536 }
537 else {
538 col_norm[k] = util::sqrt(tmp);
539 }
540 }
541
542 // For the i-th column, replace diagonal elements with (r) and the non-diagonal elements with zero.
543 M[i * N + i] = r;
544 for (int k = i + 1; k < Nsol; ++k) { M[k * N + i] = T(0); }
545 }
546
547 // Backward substitution step, i.e., solve R*x = Qᵀ*b in place.
548 for (int i = Nsol - 1; i >= 0; --i) {
549 T sum = T(0);
550 for (int j = i + 1; j < Nsol; ++j) { sum += M[i * N + j] * x[j]; }
551 x[i] = (b[i] - sum) / M[i * N + i];
552 }
553
554 // Reconstruct x by inverting the permutation, i.e., for each
555 // index i, the original index is given by col_perm[i].
556 std::array<T, N> tmp = x;
557 for (int i = 0; i < Nsol; ++i) { x[col_perm[i]] = tmp[i]; }
558}
platform_constant Fraction M[Q][Q]
Definition mrt.h:57
constexpr T norm(const ScalarVector< T, D, IMPL > &a) any_platform
Euclidean vector norm.

References olb::norm(), and olb::util::sqrt().

+ Here is the call graph for this function:

◆ completePivotingLUSolver()

template<typename T , int N>
void olb::FreeSurface::completePivotingLUSolver ( std::array< T, N *N > & M,
std::array< T, N > & x,
std::array< T, N > & b,
const int Nsol )

Definition at line 383 of file freeSurfaceHelpers.hh.

383 {
384 // Add Tikhonov Regularization to the diagonal elements of M
385 for (int i = 0; i < Nsol; ++i) { M[i * N + i] += T(0); }
386
387 // Permutation vectors to record row and columns swaps.
388 std::array<int, N> row_perm{}, col_perm{};
389 for (int i = 0; i < N; ++i) {
390 row_perm[i] = i;
391 col_perm[i] = i;
392 }
393
394 // LU decomposition method with complete pivoting.
395 for (int i = 0; i < Nsol; ++i) {
396 int pivotRow = i, pivotCol = i;
397 T maxValue = std::abs(M[i * N + i]);
398
399 for (int r = i; r < Nsol; ++r) {
400 for (int c = i; c < Nsol; ++c) {
401 T value = std::abs(M[r * N + c]);
402 if (value > maxValue) {
403 maxValue = value;
404 pivotRow = r;
405 pivotCol = c;
406 }
407 }
408 }
409
410 if (pivotRow != i) {
411 for (int k = 0; k < Nsol; ++k) { std::swap(M[i * N + k], M[pivotRow * N + k]); }
412 std::swap(row_perm[i], row_perm[pivotRow]);
413 std::swap(b[i], b[pivotRow]);
414 }
415
416 if (pivotCol != i) {
417 for (int k = 0; k < Nsol; ++k) { std::swap(M[k * N + i], M[k * N + pivotCol]); }
418 std::swap(col_perm[i], col_perm[pivotCol]);
419 }
420
421 for (int j = i + 1; j < Nsol; ++j) {
422 M[j * N + i] /= M[i * N + i];
423 for (int k = i + 1; k < Nsol; ++k) { M[j * N + k] -= M[j * N + i] * M[i * N + k]; }
424 }
425 }
426
427 // Copy the right-hand side array into x array
428 for (int i = 0; i < Nsol; ++i) { x[i] = b[i]; }
429
430 // Forward substitution step, i.e., solve L * x = b in place.
431 for (int i = 0; i < Nsol; ++i) {
432 for (int j = 0; j < i; ++j) { x[i] -= M[i * N + j] * x[j]; }
433 }
434
435 // Backward substitution step, i.e., solve U * x = b in place.
436 for (int i = Nsol - 1; i >= 0; --i) {
437 for (int j = i + 1; j < Nsol; ++j) { x[i] -= M[i * N + j] * x[j]; }
438 x[i] /= M[i * N + i];
439 }
440
441 // Reconstruct original x by inverting the permutation, i.e.,
442 // for each index i, the original index is given by col_perm[i].
443 std::array<T, N> tmp = x;
444 for (int i = 0; i < Nsol; ++i) { x[col_perm[i]] = tmp[i]; }
445}
platform_constant int c[Q][D]
Definition functions.h:57
typename std::integral_constant< TYPE, VALUE >::type value
Identity type to wrap non-type template arguments.
Definition meta.h:96

◆ computeCurvature()

template<typename CELL , typename V >
V olb::FreeSurface::computeCurvature ( CELL & cell)

Definition at line 910 of file freeSurfaceHelpers.hh.

910 {
911 using DESCRIPTOR = typename CELL::descriptor_t;
912
913 if constexpr (DESCRIPTOR::d == 2) {
914 return computeCurvaturePLIC2D(cell);
915 } else if (DESCRIPTOR::d == 3) {
916 return computeCurvaturePLIC3D(cell);
917 }
918
919 return V(0);
920}

◆ computeCurvatureFDM3D()

template<typename CELL , typename V >
V olb::FreeSurface::computeCurvatureFDM3D ( CELL & cell)

Definition at line 867 of file freeSurfaceHelpers.hh.

867 {
868 using DESCRIPTOR = typename CELL::descriptor_t;
869
870 const auto normal = computeInterfaceNormal(cell);
871 if (norm_squared(normal) < zeroThreshold<V>()) { return V(0); }
872
873 const std::uint32_t gaussianMultipliers [DESCRIPTOR::q] =
874 {
875 std::uint32_t(8),
876 std::uint32_t(4), std::uint32_t(4), std::uint32_t(4),
877 std::uint32_t(2), std::uint32_t(2), std::uint32_t(2),
878 std::uint32_t(2), std::uint32_t(2), std::uint32_t(2),
879 std::uint32_t(1), std::uint32_t(1), std::uint32_t(1), std::uint32_t(1),
880 std::uint32_t(4), std::uint32_t(4), std::uint32_t(4),
881 std::uint32_t(2), std::uint32_t(2), std::uint32_t(2),
882 std::uint32_t(2), std::uint32_t(2), std::uint32_t(2),
883 std::uint32_t(1), std::uint32_t(1), std::uint32_t(1), std::uint32_t(1)
884 };
885
886 V curvature = V(0);
887 V weightSum = V(0);
888
889 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
890 auto direction = descriptors::c<DESCRIPTOR>(iPop);
891 auto nbrCell = cell.neighbor(direction);
892
893 // Check if the neighbour is an interface cell or has a gas neighbour
894 if (!isCellType(nbrCell, FreeSurface::Type::Interface) || !hasNeighbour(nbrCell, FreeSurface::Type::Gas)) {
895 continue;
896 }
897
898 const Vector<V, DESCRIPTOR::d> ei = {V(direction[0]), V(direction[1]), V(direction[2])};
899 const auto nbrNormal = util::normalize(computeInterfaceNormal(nbrCell));
900 const V weight = gaussianMultipliers[iPop];
901 weightSum += weight;
902 curvature += weight * util::dotProduct(ei, nbrNormal);
903 }
904
905 curvature /= weightSum;
906 return util::max(V(-1), util::min(V(1), curvature));
907}
constexpr T norm_squared(const ScalarVector< T, D, IMPL > &a) any_platform
Squared euclidean vector norm.

References olb::descriptors::c(), olb::util::dotProduct(), Gas, Interface, olb::util::max(), olb::util::min(), olb::norm_squared(), olb::util::normalize(), and zeroThreshold().

+ Here is the call graph for this function:

◆ computeCurvaturePLIC2D()

template<typename CELL , typename V >
V olb::FreeSurface::computeCurvaturePLIC2D ( CELL & cell)

Definition at line 644 of file freeSurfaceHelpers.hh.

644 {
645 using DESCRIPTOR = typename CELL::descriptor_t;
646
647 // Setup a new coordinate system where bz is perpendicular to the surface, while bx and by are
648 // tangent to the surface.
649 const auto normal = computeInterfaceNormal(cell);
650 Vector<V, 3> by = {normal[0], normal[1], V(0)};
651 if (norm_squared(by) < zeroThreshold<V>()) { return V(0); }
652 by = util::normalize(by);
653
654 const Vector<V, 3> rn{V(0), V(0), V(1)};
655 const Vector<V, 3> bx = crossProduct(by, rn);
656
657 // Number of healthy interface neighbours
658 std::uint8_t nbrInterfaces = std::uint8_t(0);
659
660 // Compute z-offset of center point using PLIC cube
661 const V centerOffset = plicCube<V>(getClampedEpsilon(cell), by);
662
663 // Number of neighbouring interface points, i.e., less than or equal to 8 (neighbours) - 1 (liquid) - 1 (gas).
664 std::array<Vector<V, DESCRIPTOR::d>, 6> points;
665
666 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
667 auto nbrCell = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
668
669 // Check if the neighbour is an interface cell or has a gas neighbour
670 if (!isCellType(nbrCell, FreeSurface::Type::Interface) || !hasNeighbour(nbrCell, FreeSurface::Type::Gas)) {
671 continue;
672 }
673
674 // Here it is assumed that neighbor normal vector is the same as center normal vector
675 const auto direction = descriptors::c<DESCRIPTOR>(iPop);
676 const Vector<V, 3> ei = {V(direction[0]), V(direction[1]), V(0)};
677 const V offset = plicCube<V>(getClampedEpsilon(nbrCell), by) - centerOffset;
678
679 // Transform coordinate system into (x, f(x)) and apply PLIC offset
680 points[nbrInterfaces++] = {util::dotProduct(ei, bx), util::dotProduct(ei, by) + offset};
681 }
682
683 // Prepare the linear equation system, to be initialized with zeros
684 std::array<V, 4> M{};
685 std::array<V, 2> solution{};
686 std::array<V, 2> b{};
687
688 for (std::uint8_t i = 0; i < nbrInterfaces; ++i) {
689 const V x = points[i][0];
690 const V y = points[i][1];
691 const V x2 = util::sqr(x);
692 const V x3 = util::sqr(x) * x;
693
694 M[0] += x2 * x2;
695 M[1] += x3;
696 M[3] += x2;
697
698 b[0] += x2 * y;
699 b[1] += x * y;
700 }
701
702 // Fill the lower triangle of the symmetric matrix
703 M[2] = M[1];
704
705 // Solve the linear system of equations, compile-time selection.
706 if constexpr (solver == SolverType::rowPivotingLU) {
707 if (nbrInterfaces >= 2) {
708 rowPivotingLUSolver<V, 2>(M, solution, b, 2);
709 } else {
710 rowPivotingLUSolver<V, 2>(M, solution, b, util::min(2, nbrInterfaces));
711 }
712 }
713 else if constexpr (solver == SolverType::completePivotingLU) {
714 if (nbrInterfaces >= 2) {
715 completePivotingLUSolver<V, 2>(M, solution, b, 2);
716 } else {
717 completePivotingLUSolver<V, 2>(M, solution, b, util::min(2, nbrInterfaces));
718 }
719 }
720 else if constexpr (solver == SolverType::columnPivotingQR) {
721 if (nbrInterfaces >= 2) {
722 columnPivotingQRSolver<V, 2>(M, solution, b, 2);
723 } else {
724 columnPivotingQRSolver<V, 2>(M, solution, b, util::min(2, nbrInterfaces));
725 }
726 }
727 else {
728 // Fall back to default solver
729 if (nbrInterfaces >= 2) {
730 completePivotingLUSolver<V, 2>(M, solution, b, 2);
731 } else {
732 completePivotingLUSolver<V, 2>(M, solution, b, util::min(2, nbrInterfaces));
733 }
734 }
735
736 const V A = solution[0], H = solution[1];
737 const V curvature = V(2) * A * util::cube(V(1) / util::sqrt(H * H + V(1)));
738 return util::max(V(-1), util::min(V(1), curvature));
739}
Plain old scalar vector.
constexpr SolverType solver
auto crossProduct(const ScalarVector< T, D, IMPL > &a, const ScalarVector< T, D, IMPL_ > &b) any_platform
Definition vector.h:274

References olb::descriptors::c(), columnPivotingQR, completePivotingLU, olb::crossProduct(), olb::util::cube(), olb::util::dotProduct(), Gas, Interface, olb::util::max(), olb::util::min(), olb::norm_squared(), olb::util::normalize(), rowPivotingLU, solver, olb::util::sqr(), olb::util::sqrt(), and zeroThreshold().

+ Here is the call graph for this function:

◆ computeCurvaturePLIC3D()

template<typename CELL , typename V >
V olb::FreeSurface::computeCurvaturePLIC3D ( CELL & cell)

Definition at line 743 of file freeSurfaceHelpers.hh.

743 {
744 using DESCRIPTOR = typename CELL::descriptor_t;
745
746 // Setup a new coordinate system where bz is perpendicular to the surface, while bx and by are
747 // tangent to the surface.
748 auto bz = computeInterfaceNormal(cell);
749 if (norm_squared(bz) < zeroThreshold<V>()) { return V(0); }
750 bz = util::normalize(bz);
751
752 // A random normalized vector that is just by random chance not collinear with bz
753 // const Vector<V, DESCRIPTOR::d> rn{V(0.56270900), V(0.32704452), V(0.75921047)};
754 const Vector<V, DESCRIPTOR::d> rn{V(0.5402151198529208), V(0.2765640977634561), V(0.7947829415070379)};
755
756 // Normalization is necessary here because bz and rn are not perpendicular
757 const Vector<V, DESCRIPTOR::d> by = util::normalize(crossProduct(bz, rn));
758 const Vector<V, DESCRIPTOR::d> bx = crossProduct(by, bz);
759
760 // Number of healthy interface neighbours
761 std::uint8_t nbrInterfaces = std::uint8_t(0);
762
763 // Compute z-offset of center point using PLIC cube
764 const V centerOffset = plicCube<V>(getClampedEpsilon(cell), bz);
765
766 // Number of neighbouring interface points, i.e., less than or equal to 26 (neighbours) - 1 (liquid) - 1 (gas).
767 std::array<Vector<V, DESCRIPTOR::d>, 24> points;
768
769 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
770 auto nbrCell = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
771
772 // Check if the neighbour is an interface cell or has a gas neighbour
773 if (!isCellType(nbrCell, FreeSurface::Type::Interface) || !hasNeighbour(nbrCell, FreeSurface::Type::Gas)) {
774 continue;
775 }
776
777 // Here it is assumed that neighbor normal vector is the same as center normal vector
778 const auto direction = descriptors::c<DESCRIPTOR>(iPop);
779 const Vector<V, DESCRIPTOR::d> ei = {V(direction[0]), V(direction[1]), V(direction[2])};
780 const V offset = plicCube<V>(getClampedEpsilon(nbrCell), bz) - centerOffset;
781
782 // Transform coordinate system into (x, y, f(x,y)) and apply PLIC offset
783 points[nbrInterfaces++] = {util::dotProduct(ei, bx), util::dotProduct(ei, by), util::dotProduct(ei, bz) + offset};
784 }
785
786 // Prepare the linear equation system, to be initialized with zeros
787 std::array<V, 25> M{};
788 std::array<V, 5> solution{};
789 std::array<V, 5> b{};
790
791 for (std::uint8_t i = 0; i < nbrInterfaces; ++i) {
792 const V x = points[i][0];
793 const V y = points[i][1];
794 const V z = points[i][2];
795 const V x2 = util::sqr(x);
796 const V y2 = util::sqr(y);
797 const V x3 = x2 * x;
798 const V y3 = y2 * y;
799
800 M[0] += x2 * x2;
801 M[1] += x2 * y2;
802 M[2] += x3 * y;
803 M[3] += x3;
804 M[4] += x2 * y;
805 M[6] += y2 * y2;
806 M[7] += x * y3;
807 M[8] += x * y2;
808 M[9] += y3;
809 M[12] += x2 * y2;
810 M[13] += x2 * y;
811 M[14] += x * y2;
812 M[18] += x2;
813 M[19] += x * y;
814 M[24] += y2;
815
816 b[0] += x2 * z;
817 b[1] += y2 * z;
818 b[2] += x * y * z;
819 b[3] += x * z;
820 b[4] += y * z;
821 }
822
823 // Fill the lower triangle of the symmetric matrix
824 for (std::uint8_t i = 1; i < 5; ++i) {
825 for (std::uint8_t j = 0; j < i; ++j) {
826 M[i * 5 + j] = M[j * 5 + i];
827 }
828 }
829
830 // Solve the linear system of equations, compile-time selection.
831 if constexpr (solver == SolverType::rowPivotingLU) {
832 if (nbrInterfaces >= 5) {
833 rowPivotingLUSolver<V, 5>(M, solution, b, 5);
834 } else {
835 rowPivotingLUSolver<V, 5>(M, solution, b, util::min(5, nbrInterfaces));
836 }
837 }
838 else if constexpr (solver == SolverType::completePivotingLU) {
839 if (nbrInterfaces >= 5) {
840 completePivotingLUSolver<V, 5>(M, solution, b, 5);
841 } else {
842 completePivotingLUSolver<V, 5>(M, solution, b, util::min(5, nbrInterfaces));
843 }
844 }
845 else if constexpr (solver == SolverType::columnPivotingQR) {
846 if (nbrInterfaces >= 5) {
847 columnPivotingQRSolver<V, 5>(M, solution, b, 5);
848 } else {
849 columnPivotingQRSolver<V, 5>(M, solution, b, util::min(5, nbrInterfaces));
850 }
851 }
852 else {
853 // Fall back to default solver
854 if (nbrInterfaces >= 5) {
855 completePivotingLUSolver<V, 5>(M, solution, b, 5);
856 } else {
857 completePivotingLUSolver<V, 5>(M, solution, b, util::min(5, nbrInterfaces));
858 }
859 }
860
861 const V A = solution[0], B = solution[1], C = solution[2], H = solution[3], I = solution[4];
862 const V curvature = (A * (I * I + V(1)) + B * (H * H + V(1)) - C * H * I) * util::cube(V(1) / util::sqrt(H * H + I * I + V(1)));
863 return util::max(V(-1), util::min(V(1), curvature));
864}

References olb::descriptors::c(), columnPivotingQR, completePivotingLU, olb::crossProduct(), olb::util::cube(), olb::util::dotProduct(), Gas, Interface, olb::util::max(), olb::util::min(), olb::norm_squared(), olb::util::normalize(), rowPivotingLU, solver, olb::util::sqr(), olb::util::sqrt(), and zeroThreshold().

+ Here is the call graph for this function:

◆ computeInterfaceNormal()

template<typename CELL , typename V >
Vector< V, CELL::descriptor_t::d > olb::FreeSurface::computeInterfaceNormal ( CELL & cell)

Definition at line 223 of file freeSurfaceHelpers.hh.

223 {
224
225 // Compute interface normal using Parker-Young approximation
227 if constexpr (CELL::descriptor_t::d == 3) {
228 auto eps_minusY = getClampedEpsilon(cell.neighbor({0, -1, 0}));
229 auto eps_plusY = getClampedEpsilon(cell.neighbor({0, 1, 0}));
230 auto eps_minusX = getClampedEpsilon(cell.neighbor({-1, 0, 0}));
231 auto eps_plusX = getClampedEpsilon(cell.neighbor({1, 0, 0}));
232 auto eps_minusYminusX = getClampedEpsilon(cell.neighbor({-1, -1, 0}));
233 auto eps_minusYplusX = getClampedEpsilon(cell.neighbor({1, -1, 0}));
234 auto eps_plusYminusX = getClampedEpsilon(cell.neighbor({-1, 1, 0}));
235 auto eps_plusYplusX = getClampedEpsilon(cell.neighbor({1, 1, 0}));
236 auto eps_minusZ = getClampedEpsilon(cell.neighbor({0, 0, -1}));
237 auto eps_plusZ = getClampedEpsilon(cell.neighbor({0, 0, 1}));
238 auto eps_minusZminusY = getClampedEpsilon(cell.neighbor({0, -1, -1}));
239 auto eps_minusZplusY = getClampedEpsilon(cell.neighbor({0, 1, -1}));
240 auto eps_minusZminusX = getClampedEpsilon(cell.neighbor({-1, 0, -1}));
241 auto eps_minusZplusX = getClampedEpsilon(cell.neighbor({1, 0, -1}));
242 auto eps_plusZminusY = getClampedEpsilon(cell.neighbor({0, -1, 1}));
243 auto eps_plusZplusY = getClampedEpsilon(cell.neighbor({0, 1, 1}));
244 auto eps_plusZminusX = getClampedEpsilon(cell.neighbor({-1, 0, 1}));
245 auto eps_plusZplusX = getClampedEpsilon(cell.neighbor({1, 0, 1}));
246 auto eps_minusZminusYminusX = getClampedEpsilon(cell.neighbor({-1, -1, -1}));
247 auto eps_minusZplusYminusX = getClampedEpsilon(cell.neighbor({-1, 1, -1}));
248 auto eps_minusZminusYplusX = getClampedEpsilon(cell.neighbor({1, -1, -1}));
249 auto eps_minusZplusYplusX = getClampedEpsilon(cell.neighbor({1, 1, -1}));
250 auto eps_plusZminusYminusX = getClampedEpsilon(cell.neighbor({-1, -1, 1}));
251 auto eps_plusZplusYminusX = getClampedEpsilon(cell.neighbor({-1, 1, 1}));
252 auto eps_plusZminusYplusX = getClampedEpsilon(cell.neighbor({1, -1, 1}));
253 auto eps_plusZplusYplusX = getClampedEpsilon(cell.neighbor({1, 1, 1}));
254
255 // Rearrange arithmetic operations to minimize the number of cancellation events, i.e., positive and negative terms.
256 normal[0] = V(4) * (eps_minusX - eps_plusX);
257 normal[0] += V(2) * ((eps_minusYminusX + eps_minusZminusX + eps_plusYminusX + eps_plusZminusX) - (eps_plusYplusX + eps_plusZplusX + eps_minusYplusX + eps_minusZplusX));
258 normal[0] += V(1) * ((eps_minusZminusYminusX + eps_plusZminusYminusX + eps_minusZplusYminusX + eps_plusZplusYminusX) - (eps_plusZplusYplusX + eps_minusZplusYplusX + eps_plusZminusYplusX + eps_minusZminusYplusX));
259
260 normal[1] = V(4) * (eps_minusY - eps_plusY);
261 normal[1] += V(2) * ((eps_minusYminusX + eps_minusZminusY + eps_minusYplusX + eps_plusZminusY) - (eps_plusYplusX + eps_plusZplusY + eps_plusYminusX + eps_minusZplusY));
262 normal[1] += V(1) * ((eps_minusZminusYminusX + eps_plusZminusYminusX + eps_plusZminusYplusX + eps_minusZminusYplusX) - (eps_plusZplusYplusX + eps_minusZplusYplusX + eps_minusZplusYminusX + eps_plusZplusYminusX));
263
264 normal[2] = V(4) * (eps_minusZ - eps_plusZ);
265 normal[2] += V(2) * ((eps_minusZminusX + eps_minusZminusY + eps_minusZplusX + eps_minusZplusY) - (eps_plusZplusX + eps_plusZplusY + eps_plusZminusX + eps_plusZminusY));
266 normal[2] += V(1) * ((eps_minusZminusYminusX + eps_minusZplusYplusX + eps_minusZplusYminusX + eps_minusZminusYplusX) - (eps_plusZplusYplusX + eps_plusZminusYminusX + eps_plusZminusYplusX + eps_plusZplusYminusX));
267
268 return normal;
269 }
270 else {
271 auto eps_minusY = getClampedEpsilon(cell.neighbor({0, -1}));
272 auto eps_plusY = getClampedEpsilon(cell.neighbor({0, 1}));
273 auto eps_minusX = getClampedEpsilon(cell.neighbor({-1, 0}));
274 auto eps_plusX = getClampedEpsilon(cell.neighbor({1, 0}));
275 auto eps_minusYminusX = getClampedEpsilon(cell.neighbor({-1, -1}));
276 auto eps_minusYplusX = getClampedEpsilon(cell.neighbor({1, -1}));
277 auto eps_plusYminusX = getClampedEpsilon(cell.neighbor({-1, 1}));
278 auto eps_plusYplusX = getClampedEpsilon(cell.neighbor({1, 1}));
279
280 // Rearrange arithmetic operations to minimize the number of cancellation events, i.e., positive and negative terms.
281 normal[0] = V(2) * (eps_minusX - eps_plusX);
282 normal[0] += V(1) * ((eps_minusYminusX + eps_plusYminusX) - (eps_plusYplusX + eps_minusYplusX));
283
284 normal[1] = V(2) * (eps_minusY - eps_plusY);
285 normal[1] += V(1) * ((eps_minusYminusX + eps_minusYplusX) - (eps_plusYplusX + eps_plusYminusX));
286
287 return normal;
288 }
289}

◆ computeMassExcessWeights()

template<typename CELL , typename V >
void olb::FreeSurface::computeMassExcessWeights ( CELL & cell,
const Vector< V, CELL::descriptor_t::d > & normal,
const bool & enableAllInterfaces,
Vector< V, CELL::descriptor_t::q > & weights )

Definition at line 131 of file freeSurfaceHelpers.hh.

135 {
136 using DESCRIPTOR = typename CELL::descriptor_t;
137
138 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
139 auto direction = descriptors::c<DESCRIPTOR>(iPop);
140 auto nbrCell = cell.neighbor(direction);
141
143 if constexpr (DESCRIPTOR::d == 3) {
144 ei = {V(direction[0]), V(direction[1]), V(direction[2])};
145 }
146 else { ei = {V(direction[0]), V(direction[1])}; }
147
148 if (isCellType(nbrCell, FreeSurface::Type::Interface) && hasCellFlags(nbrCell, FreeSurface::Flags::None)) {
149 const V nDotDirection = util::dotProduct(normal, ei);
150
151 if (hasCellFlags(cell, FreeSurface::Flags::ToFluid)) {
152 // This cell was converted from interface to fluid, so the normal vector is used as is
153 weights[iPop] = nDotDirection > V(0) ? nDotDirection : V(0);
154 }
155 else if (hasCellFlags(cell, FreeSurface::Flags::ToGas)) {
156 // This cell was converted from interface to gas, so the normal vector is inverted
157 weights[iPop] = nDotDirection < V(0) ? -nDotDirection : V(0);
158 }
159 }
160 else if (hasCellFlags(nbrCell, FreeSurface::Flags::NewInterface) && enableAllInterfaces) {
161 const V nDotDirection = util::dotProduct(normal, ei);
162
163 if (hasCellFlags(cell, FreeSurface::Flags::ToFluid)) {
164 // This cell was converted from interface to fluid, so the normal vector is used as is
165 weights[iPop] = nDotDirection > V(0) ? nDotDirection : V(0);
166 }
167 else if (hasCellFlags(cell, FreeSurface::Flags::ToGas)) {
168 // This cell was converted from interface to gas, so the normal vector is inverted
169 weights[iPop] = nDotDirection < V(0) ? -nDotDirection : V(0);
170 }
171 }
172 else {
173 // If the neighbour is not an interface cell, the weight is set to zero
174 weights[iPop] = V(0);
175 }
176 }
177}

References olb::descriptors::c(), olb::util::dotProduct(), Interface, NewInterface, None, ToFluid, and ToGas.

+ Here is the call graph for this function:

◆ getClampedEpsilon()

template<typename CELL , typename V >
V olb::FreeSurface::getClampedEpsilon ( const CELL & cell)

Definition at line 180 of file freeSurfaceHelpers.hh.

180 {
181
182 V epsilon = cell.template getField<FreeSurface::EPSILON>();
183 return util::max(V(0), util::min(V(1), epsilon));
184}

References olb::util::max(), and olb::util::min().

+ Here is the call graph for this function:

◆ getClampedSmoothEpsilon()

template<typename CELL , typename V >
V olb::FreeSurface::getClampedSmoothEpsilon ( const CELL & cell)

Definition at line 216 of file freeSurfaceHelpers.hh.

216 {
217
218 V epsilon = getSmoothEpsilon(cell);
219 return util::max(V(0), util::min(V(1), epsilon));
220}

References olb::util::max(), and olb::util::min().

+ Here is the call graph for this function:

◆ getNeighbourInfo()

template<typename CELL >
NeighbourInfo olb::FreeSurface::getNeighbourInfo ( CELL & cell)

Definition at line 86 of file freeSurfaceHelpers.hh.

86 {
87 NeighbourInfo info{};
88 using DESCRIPTOR = typename CELL::descriptor_t;
89
90 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
91 auto nbrCell = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
92
93 if (isCellType(nbrCell, FreeSurface::Type::Gas)) {
94 info.has_gas_neighbours = true;
95 }
96 else if (isCellType(nbrCell, FreeSurface::Type::Fluid)) {
97 info.has_fluid_neighbours = true;
98 }
99 else if (isCellType(nbrCell, FreeSurface::Type::Interface)) {
100 ++info.interface_neighbours;
101 }
102 }
103
104 return info;
105}

References olb::descriptors::c(), Fluid, Gas, and Interface.

+ Here is the call graph for this function:

◆ getSmoothEpsilon()

template<typename CELL , typename V >
V olb::FreeSurface::getSmoothEpsilon ( CELL & cell)

Definition at line 187 of file freeSurfaceHelpers.hh.

187 {
188 using DESCRIPTOR = typename CELL::descriptor_t;
189
190 // K8 kernel of Williams et al.
191 // Avoid uisng expensive std::pow to boost performance
192 auto kernel = [](auto radius, const auto& direction) {
193 auto norm = norm_squared(direction);
194 if (norm < radius * radius) {
195 auto tmp = decltype(radius)(1) - norm / (radius * radius);
196 return tmp * tmp * tmp * tmp;
197 } else { return decltype(radius)(0); }
198 };
199
200 // Kernel support radius is fixed at 2.0
201 V radius = V(2);
202 V denominator = V(0);
203 V tmp = V(0);
204
205 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
206 auto nbrCell = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
207 const auto direction = descriptors::c<DESCRIPTOR>(iPop);
208 tmp += kernel(radius, direction) * nbrCell.template getField<FreeSurface::EPSILON>();
209 if (norm(direction) < radius) { denominator += kernel(radius, direction); }
210 }
211
212 return tmp / denominator;
213}

References olb::descriptors::c(), olb::norm(), and olb::norm_squared().

+ Here is the call graph for this function:

◆ hasCellFlags()

template<typename CELL >
bool olb::FreeSurface::hasCellFlags ( CELL & cell,
const FreeSurface::Flags & flags )

Definition at line 64 of file freeSurfaceHelpers.hh.

64 {
65 return static_cast<bool>(cell.template getField<FreeSurface::CELL_FLAGS>() & flags);
66}

◆ hasNeighbour()

template<typename CELL >
bool olb::FreeSurface::hasNeighbour ( CELL & cell,
const FreeSurface::Type & type )

Definition at line 52 of file freeSurfaceHelpers.hh.

52 {
53 using DESCRIPTOR = typename CELL::descriptor_t;
54
55 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
56 auto nbrCell = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
57 if (isCellType(nbrCell, type)) { return true; }
58 }
59
60 return false;
61}

References olb::descriptors::c().

+ Here is the call graph for this function:

◆ hasNeighbourFlags()

template<typename CELL >
bool olb::FreeSurface::hasNeighbourFlags ( CELL & cell,
const FreeSurface::Flags & flags )

Definition at line 74 of file freeSurfaceHelpers.hh.

74 {
75 using DESCRIPTOR = typename CELL::descriptor_t;
76
77 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
78 auto nbrCell = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
79 if (hasCellFlags(nbrCell, flags)) { return true; }
80 }
81
82 return false;
83}

References olb::descriptors::c().

+ Here is the call graph for this function:

◆ initialize()

template<typename T , typename DESCRIPTOR >
void olb::FreeSurface::initialize ( SuperLattice< T, DESCRIPTOR > & sLattice)

◆ isCellType()

template<typename CELL >
bool olb::FreeSurface::isCellType ( CELL & cell,
const FreeSurface::Type & type )

Definition at line 42 of file freeSurfaceHelpers.hh.

42 {
43 return cell.template getField<FreeSurface::CELL_TYPE>() == type;
44}

◆ isHealthyInterface()

template<typename CELL , typename V >
bool olb::FreeSurface::isHealthyInterface ( CELL & cell)

Definition at line 108 of file freeSurfaceHelpers.hh.

108 {
109 using DESCRIPTOR = typename CELL::descriptor_t;
110 bool has_fluid_neighbours = false;
111 bool has_gas_neighbours = false;
112
113 if (!isCellType(cell, FreeSurface::Type::Interface)) { return false; }
114
115 for (int iPop = 1; iPop < DESCRIPTOR::q; ++iPop) {
116 auto nbrCell = cell.neighbor(descriptors::c<DESCRIPTOR>(iPop));
117 if (isCellType(nbrCell, FreeSurface::Type::Gas)) {
118 has_gas_neighbours = true;
119 if (has_fluid_neighbours) { return true; }
120 }
121 else if (isCellType(nbrCell, FreeSurface::Type::Fluid)) {
122 has_fluid_neighbours = true;
123 if (has_gas_neighbours) { return true; }
124 }
125 }
126
127 return false;
128}

References olb::descriptors::c(), Fluid, Gas, and Interface.

+ Here is the call graph for this function:

◆ isNearZero()

template<typename T >
bool olb::FreeSurface::isNearZero ( T a,
T threshold )
inline

Definition at line 54 of file freeSurfaceHelpers.h.

54 {
55 return util::abs(a) < threshold;
56}

References olb::util::abs().

+ Here is the call graph for this function:

◆ iterativeRefinement()

template<typename T , int N>
void olb::FreeSurface::iterativeRefinement ( const std::array< T, N *N > & A,
const std::array< T, N *N > & M,
std::array< T, N > & x,
const std::array< T, N > & b,
const int Nsol,
int maxIter )

Definition at line 292 of file freeSurfaceHelpers.hh.

296 {
297 std::array<T, N> residual{};
298 for (int iter = 0; iter < maxIter; ++iter) {
299 // Compute the residual, i.e., r = b - A * x
300 for (int i = 0; i < Nsol; ++i) {
301 T sum = T(0);
302 for (int j = 0; j < Nsol; ++j) { sum += A[i * N + j] * x[j]; }
303 residual[i] = b[i] - sum;
304 }
305
306 // Forward substitution
307 std::array<T, N> dx = residual;
308 for (int i = 0; i < Nsol; ++i) {
309 for (int k = 0; k < i; ++k) { dx[i] -= M[i * N + k] * dx[k]; }
310 }
311
312 // Backward substitution
313 for (int i = Nsol - 1; i >= 0; --i) {
314 for (int k = i + 1; k < Nsol; ++k) { dx[i] -= M[i * N + k] * dx[k]; }
315 dx[i] /= M[i * N + i];
316 }
317
318 // Update the solution, i.e., x = x + dx
319 for (int i = 0; i < Nsol; ++i) { x[i] += dx[i]; }
320 }
321}

◆ plicCube()

template<typename T >
T olb::FreeSurface::plicCube ( const T & volume,
const Vector< T, 3 > & n )

Definition at line 596 of file freeSurfaceHelpers.hh.

596 {
597 // Unit cube-plane intersection: using volume and normal vector to compute the offset
598 const T ax = util::abs(n[0]), ay = util::abs(n[1]), az = util::abs(n[2]);
599
600 // Eliminate symmetry cases, and normalize n using L1 norm
601 const T V = T(0.5) - util::abs(volume - T(0.5)), l = ax + ay + az;
602 const T n1 = util::min(util::min(ax, ay), az) / l;
603 const T n3 = util::max(util::max(ax, ay), az) / l;
604 const T n2 = std::fdim(T(1), n1 + n3); // ensure n2 >= 0
605 const T d = plicCubeReduced<T>(V, {n1, n2, n3});
606
607 return l * std::copysign(T(0.5) - d, volume - T(0.5));
608}

References olb::util::abs(), olb::util::max(), and olb::util::min().

+ Here is the call graph for this function:

◆ plicCubeInverse()

template<typename T >
T olb::FreeSurface::plicCubeInverse ( const T & d0,
const Vector< T, 3 > & n )

Definition at line 612 of file freeSurfaceHelpers.hh.

612 {
613 // Eliminate majority of cases due to symmetry
614 const T n1 = util::min(util::min(util::abs(n[0]), util::abs(n[1])), util::abs(n[2]));
615 const T n3 = util::max(util::max(util::abs(n[0]), util::abs(n[1])), util::abs(n[2]));
616 const T n2 = util::abs(n[0]) - n1 + util::abs(n[1]) + util::abs(n[2]) - n3;
617
618 // Compute PLIC using reduced symmetry, shifting origin from (0.0, 0.0, 0.0) to (0.5, 0.5, 0.5)
619 const T d = T(0.5) * (n1 + n2 + n3) - util::abs(d0);
620
621 T V = T(0);
622 if (util::min(n1 + n2, n3) <= d && d <= n3) {
623 // (I) Case 5
624 V = (d - T(0.5) * (n1 + n2)) / n3;
625 }
626 else if (d < n1) {
627 // (II) Case 1
628 V = util::cube(d) / (T(6) * n1 * n2 * n3);
629 }
630 else if (d <= n2) {
631 // (III) Case 2
632 V = (T(3) * d * (d - n1) + util::sqr(n1)) / (T(6) * n2 * n3);
633 }
634 else {
635 // (IV) Case 3 or 4
636 V = (util::cube(d) - util::cube(d - n1) - util::cube(d - n2) - util::cube(std::fdim(d, n3))) / (T(6) * n1 * n2 * n3);
637 }
638
639 return std::copysign(T(0.5) - V, d0) + V(0.5);
640}

References olb::util::abs(), olb::util::cube(), olb::util::max(), olb::util::min(), and olb::util::sqr().

+ Here is the call graph for this function:

◆ plicCubeReduced()

template<typename T >
T olb::FreeSurface::plicCubeReduced ( const T & volume,
const Vector< T, 3 > & n )

Definition at line 562 of file freeSurfaceHelpers.hh.

562 {
563 const T n1 = n[0], n2 = n[1], n3 = n[2];
564 const T n12 = n1 + n2, n3V = n3 * volume;
565
566 // (I) Case 5
567 if (n12 <= T(2) * n3V) { return n3V + T(0.5) * n12; }
568
569 // (II) After case 5, check n2 > 0 is true
570 const T sqn1 = util::sqr(n1), n26 = T(6) * n2, v1 = sqn1 / n26;
571
572 // (III) Case 2
573 if (v1 <= n3V && n3V < v1 + T(0.5) * (n2 - n1)) {
574 return T(0.5) * (n1 + util::sqrt(sqn1 + T(8) * n2 * (n3V - v1)));
575 }
576
577 // (IV) Case 1
578 const T V6 = n1 * n26 * n3V;
579 if (n3V < v1) { return std::cbrt(V6); }
580
581 // (V) After case 2, check n1 > 0 is true
582 const T v3 = n3 < n12 ? (util::sqr(n3) * (T(3) * n12 - n3) + sqn1 * (n1 - T(3) * n3) + util::sqr(n2) * (n2 - T(3) * n3)) / (n1 * n26) : T(0.5) * n12;
583 const T sqn12 = sqn1 + util::sqr(n2), V6cbn12 = V6 - util::cube(n1) - util::cube(n2);
584 const bool case34 = n3V < v3; // true: case (3), false: case (4)
585 const T a = case34 ? V6cbn12 : T(0.5) * (V6cbn12 - util::cube(n3));
586 const T b = case34 ? sqn12 : T(0.5) * (sqn12 + util::sqr(n3));
587 const T c = case34 ? n12 : T(0.5);
588 const T t = util::sqrt(util::sqr(c) - b);
589 static constexpr T oneThird = T(1) / T(3);
590
591 return c - T(2) * t * util::sin(oneThird * util::asin((util::cube(c) - T(0.5) * a - T(1.5) * b * c) / util::cube(t)));
592}
platform_constant Fraction t[Q]
Definition rtlbm.h:45

References olb::util::asin(), olb::util::cube(), olb::util::sin(), olb::util::sqr(), and olb::util::sqrt().

+ Here is the call graph for this function:

◆ rowPivotingLUSolver()

template<typename T , int N>
void olb::FreeSurface::rowPivotingLUSolver ( std::array< T, N *N > & M,
std::array< T, N > & x,
std::array< T, N > & b,
const int Nsol )

Definition at line 324 of file freeSurfaceHelpers.hh.

324 {
325 // Add Tikhonov Regularization to the diagonal elements of M
326 for (int i = 0; i < Nsol; ++i) { M[i * N + i] += T(0); }
327
328 // Make a copy of the original coefficient matrix.
329 std::array<T, N * N> Prev_M = M;
330
331 // Permutation vector to record row swaps.
332 std::array<int, N> row_perm{};
333 for (int i = 0; i < N; ++i) { row_perm[i] = i; }
334
335 // LU decomposition method with row pivoting.
336 for (int i = 0; i < Nsol; ++i) {
337 int pivotRow = i;
338 T maxValue = std::abs(M[i * N + i]);
339
340 for (int j = i + 1; j < Nsol; ++j) {
341 T value = std::abs(M[j * N + i]);
342 if (value > maxValue) {
343 maxValue = value;
344 pivotRow = j;
345 }
346 }
347
348 if (pivotRow != i) {
349 for (int k = 0; k < Nsol; ++k) { std::swap(M[i * N + k], M[pivotRow * N + k]); }
350 std::swap(row_perm[i], row_perm[pivotRow]);
351 std::swap(b[i], b[pivotRow]);
352 }
353
354 for (int j = i + 1; j < Nsol; ++j) {
355 M[j * N + i] /= M[i * N + i];
356 for (int k = i + 1; k < Nsol; ++k) { M[j * N + k] -= M[j * N + i] * M[i * N + k]; }
357 }
358 }
359
360 // Copy the right-hand side array into x array
361 for (int i = 0; i < Nsol; ++i) { x[i] = b[i]; }
362
363 // Forward substitution step, i.e., solve L * x = b in place.
364 for (int i = 0; i < Nsol; ++i) {
365 for (int j = 0; j < i; ++j) { x[i] -= M[i * N + j] * x[j]; }
366 }
367
368 // Backward substitution step, i.e., solve U * x = b in place.
369 for (int i = Nsol - 1; i >= 0; --i) {
370 for (int j = i + 1; j < Nsol; ++j) { x[i] -= M[i * N + j] * x[j]; }
371 x[i] /= M[i * N + i];
372 }
373
374 // Perform iterative refinement of the solution.
375 std::array<T, N * N> tmp = Prev_M;
376 for (int i = 0; i < Nsol; ++i) {
377 for (int j = 0; j < Nsol; ++j) { Prev_M[i * N + j] = tmp[row_perm[i] * N + j]; }
378 }
379 iterativeRefinement<T, N>(Prev_M, M, x, b, Nsol);
380}

◆ setCellFlags()

template<typename CELL >
void olb::FreeSurface::setCellFlags ( CELL & cell,
const FreeSurface::Flags & flags )

Definition at line 69 of file freeSurfaceHelpers.hh.

69 {
70 cell.template setField<FreeSurface::CELL_FLAGS>(flags);
71}

◆ setCellType()

template<typename CELL >
void olb::FreeSurface::setCellType ( CELL & cell,
const FreeSurface::Type & type )

Definition at line 47 of file freeSurfaceHelpers.hh.

47 {
48 cell.template setField<FreeSurface::CELL_TYPE>(type);
49}

◆ zeroThreshold()

template<typename T >
T olb::FreeSurface::zeroThreshold ( )
constexpr

Definition at line 45 of file freeSurfaceHelpers.h.

45 {
46 if constexpr (std::is_same<T, float>::value) {
47 return T{1e-6};
48 } else {
49 return T{1e-14};
50 }
51}
+ Here is the caller graph for this function:

Variable Documentation

◆ solver

SolverType olb::FreeSurface::solver = SolverType::completePivotingLU
constexpr

Definition at line 63 of file freeSurfaceHelpers.h.