1 // Copyright (c) 2017-2025, Lawrence Livermore National Security, LLC and other CEED contributors. 2 // All Rights Reserved. See the top-level LICENSE and NOTICE files for details. 3 // 4 // SPDX-License-Identifier: BSD-2-Clause 5 // 6 // This file is part of CEED: http://github.com/ceed 7 8 /// @file 9 /// Element anisotropy tensor, as defined in 'Invariant data-driven subgrid stress modeling in the strain-rate eigenframe for large eddy simulation' 10 /// Prakash et al. 2022 11 #include <ceed/types.h> 12 13 #include "utils.h" 14 #include "utils_eigensolver_jacobi.h" 15 16 // @brief Get Anisotropy tensor from xi_{i,j} 17 // @details A_ij = \Delta_{ij} / ||\Delta_ij||, \Delta_ij = (xi_{i,j})^(-1/2) 18 CEED_QFUNCTION_HELPER void AnisotropyTensor(const CeedScalar km_g_ij[6], CeedScalar A_ij[3][3], CeedScalar *delta, const CeedInt n_sweeps) { 19 CeedScalar evals[3], evecs[3][3], evals_evecs[3][3] = {{0.}}, g_ij[3][3]; 20 CeedInt work_vector[3]; 21 22 // Invert square root of metric tensor to get \Delta_ij 23 KMUnpack(km_g_ij, g_ij); 24 Diagonalize3(g_ij, evals, evecs, work_vector, SORT_DECREASING_EVALS, true, n_sweeps); 25 for (int i = 0; i < 3; i++) evals[i] = 1 / sqrt(evals[i]); 26 MatDiag3(evecs, evals, CEED_NOTRANSPOSE, evals_evecs); 27 MatMat3(evecs, evals_evecs, CEED_TRANSPOSE, CEED_NOTRANSPOSE, A_ij); // A_ij = E^T D E 28 29 // Scale by delta to get anisotropy tensor 30 *delta = sqrt(Dot3(evals, evals)); 31 ScaleN((CeedScalar *)A_ij, 1 / *delta, 9); 32 // NOTE Need 2 factor to get physical element size (rather than projected onto [-1,1]^dim) 33 // Should attempt to auto-determine this from the quadrature point coordinates in reference space 34 *delta *= 2; 35 } 36 37 // @brief RHS for L^2 projection of anisotropic tensor and it's Frobenius norm 38 CEED_QFUNCTION(AnisotropyTensorProjection)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 39 const CeedScalar(*q_data)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0]; 40 CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0]; 41 42 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 43 const CeedScalar wdetJ = q_data[0][i]; 44 const CeedScalar dXdx[3][3] = { 45 {q_data[1][i], q_data[2][i], q_data[3][i]}, 46 {q_data[4][i], q_data[5][i], q_data[6][i]}, 47 {q_data[7][i], q_data[8][i], q_data[9][i]} 48 }; 49 50 CeedScalar km_g_ij[6] = {0.}, A_ij[3][3] = {{0.}}, km_A_ij[6], delta; 51 KMMetricTensor(dXdx, km_g_ij); 52 AnisotropyTensor(km_g_ij, A_ij, &delta, 15); 53 KMPack(A_ij, km_A_ij); 54 55 for (CeedInt j = 0; j < 6; j++) v[j][i] = wdetJ * km_A_ij[j]; 56 v[6][i] = wdetJ * delta; 57 } 58 return 0; 59 } 60 61 // @brief Get anisotropic tensor and it's Frobenius norm at quadrature points 62 CEED_QFUNCTION(AnisotropyTensorCollocate)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 63 const CeedScalar(*q_data)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0]; 64 CeedScalar(*v)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0]; 65 66 CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 67 const CeedScalar dXdx[3][3] = { 68 {q_data[1][i], q_data[2][i], q_data[3][i]}, 69 {q_data[4][i], q_data[5][i], q_data[6][i]}, 70 {q_data[7][i], q_data[8][i], q_data[9][i]} 71 }; 72 73 CeedScalar km_g_ij[6] = {0.}, A_ij[3][3] = {{0.}}, km_A_ij[6], delta; 74 KMMetricTensor(dXdx, km_g_ij); 75 AnisotropyTensor(km_g_ij, A_ij, &delta, 15); 76 KMPack(A_ij, km_A_ij); 77 78 for (CeedInt j = 0; j < 6; j++) v[j][i] = km_A_ij[j]; 79 v[6][i] = delta; 80 } 81 return 0; 82 } 83