Add TRT-LES diagnostics and plotting functionality
- Introduced new functions for computing and plotting TRT-LES fields. - Enhanced case diagnostics to include mass drift and inlet variance metrics. - Updated configuration to support inlet profile selection and TRT magic parameter. - Modified existing functions to accommodate new diagnostic calculations. - Improved case tagging to include inlet profile and TRT parameters. - Added checks for fluid dynamics and diagnostics in the run_case function.
This commit is contained in:
@@ -193,12 +193,17 @@ class FlowField:
|
||||
# max_id = max(self.objects.keys())
|
||||
else:
|
||||
raise ValueError(f"Unsupported data type {self.DATA_TYPE}.")
|
||||
|
||||
# Ensure host-side DDF mirrors current device state before local edits.
|
||||
cuda.memcpy_dtoh(self.ddf, self.ddf_gpu)
|
||||
|
||||
for x in range(int(x_c - radius) - 1, int(x_c + radius) + 1):
|
||||
for y in range(int(y_c - radius) - 1, int(y_c + radius) + 1):
|
||||
if (x - x_c) ** 2 + (y - y_c) ** 2 < radius**2:
|
||||
k = x + y * self.FIELD_SHAPE[0]
|
||||
self.flag[k] = SOLID
|
||||
for i in range(self.LATTICE):
|
||||
self.ddf[k + i * self.FIELD_SIZE] = self.WW[i]
|
||||
delta_temp = np.zeros(11, dtype=self.DATA_TYPE)
|
||||
delta_temp[0] = id_object.view(self.DATA_TYPE)
|
||||
for i in range(self.LATTICE):
|
||||
@@ -245,6 +250,8 @@ class FlowField:
|
||||
cuda.memcpy_htod(self.delta_gpu, self.delta_curve)
|
||||
cuda.memcpy_htod(self.flag_gpu, self.flag)
|
||||
cuda.memcpy_htod(self.indx_gpu, self.indx)
|
||||
cuda.memcpy_htod(self.ddf_gpu, self.ddf)
|
||||
cuda.memcpy_htod(self.temp_gpu, self.ddf)
|
||||
|
||||
self._rebuild_kernel()
|
||||
|
||||
|
||||
@@ -43,6 +43,25 @@ __device__ inline void apply_bounce_back(float* __restrict__ f,
|
||||
// For nodes at y=1 (adjacent to y=0 wall) or y=NY-2 (adjacent to y=NY-1 wall)
|
||||
// ---------------------------------------------------------------------------
|
||||
#if NQ == 9
|
||||
__device__ inline void apply_wall_bb_d2q9_y_pull(unsigned int y,
|
||||
float* __restrict__ f,
|
||||
const fpxx* __restrict__ fi_in,
|
||||
unsigned long k)
|
||||
{
|
||||
if (y == 1) {
|
||||
// Directions sourced from y=0 wall in pull step: +y, +x+y, -x+y.
|
||||
f[3] = load_ddf(fi_in, index_f(k, 4u));
|
||||
f[5] = load_ddf(fi_in, index_f(k, 6u));
|
||||
f[8] = load_ddf(fi_in, index_f(k, 7u));
|
||||
}
|
||||
else if (y == (unsigned int)(NY - 2)) {
|
||||
// Directions sourced from y=NY-1 wall in pull step: -y, -x-y, +x-y.
|
||||
f[4] = load_ddf(fi_in, index_f(k, 3u));
|
||||
f[6] = load_ddf(fi_in, index_f(k, 5u));
|
||||
f[7] = load_ddf(fi_in, index_f(k, 8u));
|
||||
}
|
||||
}
|
||||
|
||||
__device__ inline void apply_wall_bb_d2q9(unsigned int y,
|
||||
float* __restrict__ f)
|
||||
{
|
||||
|
||||
@@ -31,6 +31,14 @@
|
||||
#define OUTLET_BLEND_ALPHA 0.70f
|
||||
#endif
|
||||
|
||||
#ifndef OUTLET_SRT_NEQ_DAMP
|
||||
#define OUTLET_SRT_NEQ_DAMP 0.50f
|
||||
#endif
|
||||
|
||||
#ifndef INLET_TRT_NEQ_DAMP
|
||||
#define INLET_TRT_NEQ_DAMP 0.50f
|
||||
#endif
|
||||
|
||||
__device__ __forceinline__ float inlet_target_u(float y_coord) {
|
||||
#if INLET_PROFILE == 0
|
||||
return U0;
|
||||
@@ -69,10 +77,22 @@ __device__ inline void apply_parabolic_inlet(float* __restrict__ f,
|
||||
compute_feq(rho_neb, u_target, v_target, feq_tar);
|
||||
compute_feq(rho_neb, u_neb, v_neb, feq_neb);
|
||||
|
||||
// Non-equilibrium extrapolation
|
||||
f[1] = f_neb[1] - feq_neb[1] + feq_tar[1];
|
||||
f[5] = f_neb[5] - feq_neb[5] + feq_tar[5];
|
||||
f[7] = f_neb[7] - feq_neb[7] + feq_tar[7];
|
||||
#if COLLISION_MODEL == 1
|
||||
// TRT path: reconstruct full population set at inlet using damped donor
|
||||
// non-equilibrium transport. This follows the high-Re stable family that
|
||||
// replaces all boundary-node populations, reducing odd-mode contamination.
|
||||
const float beta = INLET_TRT_NEQ_DAMP;
|
||||
#pragma unroll
|
||||
for (int i = 0; i < 9; i++) {
|
||||
const float fneq = f_neb[i] - feq_neb[i];
|
||||
f[i] = feq_tar[i] + beta * fneq;
|
||||
}
|
||||
#else
|
||||
const float beta = 1.0f;
|
||||
f[1] = feq_tar[1] + beta * (f_neb[1] - feq_neb[1]);
|
||||
f[5] = feq_tar[5] + beta * (f_neb[5] - feq_neb[5]);
|
||||
f[7] = feq_tar[7] + beta * (f_neb[7] - feq_neb[7]);
|
||||
#endif
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -94,20 +114,30 @@ __device__ inline void apply_pressure_outlet(float* __restrict__ f,
|
||||
f[6] = f_neb[6];
|
||||
#else
|
||||
|
||||
// Convective non-reflecting style: extrapolate outlet density from neighbor
|
||||
// and keep neighbor velocity.
|
||||
// Prescribed-pressure outlet: keep neighbor velocity and impose reference
|
||||
// outlet density for NEQ reconstruction.
|
||||
float rho_neb, u_neb, v_neb;
|
||||
compute_rho_u(f_neb, rho_neb, u_neb, v_neb);
|
||||
#if OUTLET_BACKFLOW_CLAMP
|
||||
u_neb = fmaxf(u_neb, 0.0f);
|
||||
#endif
|
||||
float rho_out = rho_neb;
|
||||
float rho_out = RHO;
|
||||
|
||||
float feq_tar[9], feq_neb[9];
|
||||
compute_feq(rho_out, u_neb, v_neb, feq_tar);
|
||||
compute_feq(rho_neb, u_neb, v_neb, feq_neb);
|
||||
|
||||
#if OUTLET_MODE == 2
|
||||
#if COLLISION_MODEL == 0
|
||||
// SRT path: use full-population damped NEQ reconstruction at outlet to
|
||||
// suppress checkerboard/grid noise from high-frequency donor content.
|
||||
// Reference: high-Re outlet regularization rationale and NEQ decomposition.
|
||||
const float beta = OUTLET_SRT_NEQ_DAMP;
|
||||
#pragma unroll
|
||||
for (int i = 0; i < 9; i++) {
|
||||
const float fneq = f_neb[i] - feq_neb[i];
|
||||
f[i] = feq_tar[i] + beta * fneq;
|
||||
}
|
||||
#elif OUTLET_MODE == 2
|
||||
const float a = OUTLET_BLEND_ALPHA;
|
||||
f[2] = a * (f_neb[2] - feq_neb[2] + feq_tar[2]) + (1.0f - a) * f_neb[2];
|
||||
f[8] = a * (f_neb[8] - feq_neb[8] + feq_tar[8]) + (1.0f - a) * f_neb[8];
|
||||
@@ -178,15 +208,22 @@ __device__ inline void apply_pressure_outlet_3d(float* __restrict__ f,
|
||||
un = fmaxf(un, 0.0f);
|
||||
#endif
|
||||
|
||||
// Convective non-reflecting style: extrapolate outlet density from neighbor
|
||||
// and keep neighbor velocity.
|
||||
float rho_out = rho_neb;
|
||||
// Prescribed-pressure outlet: keep neighbor velocity and impose reference
|
||||
// outlet density for NEQ reconstruction.
|
||||
float rho_out = RHO;
|
||||
float feq_tar[19], feq_neb[19];
|
||||
compute_feq(rho_out, un, vn, wn, feq_tar);
|
||||
compute_feq(rho_neb, un, vn, wn, feq_neb);
|
||||
|
||||
// Reconstruct cx<0 directions: i = 2, 8, 10, 14, 16
|
||||
#if OUTLET_MODE == 2
|
||||
#if COLLISION_MODEL == 0
|
||||
const float beta = OUTLET_SRT_NEQ_DAMP;
|
||||
#pragma unroll
|
||||
for (int i = 0; i < 19; i++) {
|
||||
const float fneq = f_neb[i] - feq_neb[i];
|
||||
f[i] = feq_tar[i] + beta * fneq;
|
||||
}
|
||||
#elif OUTLET_MODE == 2
|
||||
const float a = OUTLET_BLEND_ALPHA;
|
||||
f[2] = a * (f_neb[2] - feq_neb[2] + feq_tar[2]) + (1.0f - a) * f_neb[2];
|
||||
f[8] = a * (f_neb[8] - feq_neb[8] + feq_tar[8]) + (1.0f - a) * f_neb[8];
|
||||
|
||||
@@ -91,9 +91,8 @@ __global__ void InitTubeFlow_v2(uint8_t* flag, fpxx* fi)
|
||||
index_from_thread(x, y, k);
|
||||
if (x >= (unsigned int)NX || y >= (unsigned int)NY) return;
|
||||
|
||||
float u_init = U0 * 1.5f * (1.0f - 4.0f * ((float)y - 0.5f * (NY - 1))
|
||||
* ((float)y - 0.5f * (NY - 1))
|
||||
/ ((float)(NY - 2) * (float)(NY - 2)));
|
||||
// Keep startup field consistent with runtime inlet configuration.
|
||||
float u_init = inlet_target_u((float)y);
|
||||
|
||||
if (y == 0 || y == NY - 1 || x == 0 || x == NX - 1) {
|
||||
flag[k] = LEGACY_SOLID;
|
||||
@@ -116,9 +115,8 @@ __global__ void InitTubeFlow_v2(uint8_t* flag, fpxx* fi)
|
||||
index_from_thread(x, y, z, k);
|
||||
if (x >= (unsigned int)NX || y >= (unsigned int)NY || z >= (unsigned int)NZ) return;
|
||||
|
||||
float u_init = U0 * 1.5f * (1.0f - 4.0f * ((float)y - 0.5f * (NY - 1))
|
||||
* ((float)y - 0.5f * (NY - 1))
|
||||
/ ((float)(NY - 2) * (float)(NY - 2)));
|
||||
// Keep startup field consistent with runtime inlet configuration.
|
||||
float u_init = inlet_target_u((float)y);
|
||||
|
||||
if (y == 0 || y == NY - 1 || x == 0 || x == NX - 1) {
|
||||
flag[k] = LEGACY_SOLID;
|
||||
@@ -217,9 +215,14 @@ __global__ void OneStep(
|
||||
}
|
||||
}
|
||||
|
||||
if ((fl & LEGACY_FLUID) && (y == 1u || y == (unsigned int)(NY - 2))) {
|
||||
apply_wall_bb_d2q9_y_pull(y, f, fi_in, k);
|
||||
}
|
||||
|
||||
// Collision
|
||||
if (fl & LEGACY_FLUID) {
|
||||
float feq[NQ], Fin[NQ];
|
||||
compute_rho_u(f, rho_n, ux, uy);
|
||||
compute_feq(rho_n, ux, uy, feq);
|
||||
zero_forcing(Fin);
|
||||
float omega_col = d_params.omega;
|
||||
@@ -298,9 +301,14 @@ __global__ void OneStep(
|
||||
}
|
||||
}
|
||||
|
||||
if ((fl & LEGACY_FLUID) && (y == 1 || y == NY - 2)) {
|
||||
apply_wall_bb_d3q19_y_pull(y, f, fi_in, k);
|
||||
}
|
||||
|
||||
// ---- Collision (fluid only) ----
|
||||
if (fl & LEGACY_FLUID) {
|
||||
float feq[NQ], Fin[NQ];
|
||||
compute_rho_u(f, rho_n, ux, uy, uz);
|
||||
compute_feq(rho_n, ux, uy, uz, feq);
|
||||
zero_forcing(Fin);
|
||||
float omega_col = d_params.omega;
|
||||
|
||||
@@ -100,4 +100,9 @@
|
||||
|
||||
#ifndef OMEGA_COLLISION_MAX
|
||||
#define OMEGA_COLLISION_MAX 1.999f
|
||||
#endif
|
||||
|
||||
// TRT magic parameter Lambda used to map omega+ -> omega-
|
||||
#ifndef TRT_MAGIC_PARAM
|
||||
#define TRT_MAGIC_PARAM 0.1875f
|
||||
#endif
|
||||
@@ -13,8 +13,11 @@
|
||||
#ifndef CELERIS_OPERATORS_COLLISION_TRT_CUH
|
||||
#define CELERIS_OPERATORS_COLLISION_TRT_CUH
|
||||
|
||||
// Magic parameter Λ = 3/16 (optimal for porous-media / bounce-back wall location)
|
||||
// Magic parameter Λ for TRT wall/collision coupling.
|
||||
// Default keeps previous behavior; can be overridden in macros.h / tests.
|
||||
#ifndef TRT_MAGIC_PARAM
|
||||
#define TRT_MAGIC_PARAM (0.1875f)
|
||||
#endif
|
||||
|
||||
__device__ __forceinline__ float compute_omega_minus(float omega_plus) {
|
||||
return 1.0f / (TRT_MAGIC_PARAM / (1.0f / omega_plus - 0.5f) + 0.5f);
|
||||
|
||||
@@ -19,8 +19,21 @@
|
||||
#define LES_CS 0.16f
|
||||
#endif
|
||||
|
||||
#ifndef LES_FP_ITERS
|
||||
#define LES_FP_ITERS 3
|
||||
#endif
|
||||
|
||||
#ifndef LES_FP_RELAX
|
||||
#define LES_FP_RELAX 0.70f
|
||||
#endif
|
||||
|
||||
#ifndef LES_NUT_MAX_RATIO
|
||||
#define LES_NUT_MAX_RATIO 20.0f
|
||||
#endif
|
||||
|
||||
|
||||
__device__ __forceinline__ float clamp_omega(float w) {
|
||||
return fminf(1.999f, fmaxf(0.01f, w));
|
||||
return fminf(OMEGA_COLLISION_MAX, fmaxf(OMEGA_COLLISION_MIN, w));
|
||||
}
|
||||
|
||||
#if NQ == 9
|
||||
@@ -32,6 +45,7 @@ __device__ __forceinline__ float compute_omega_smag(const float* __restrict__ f,
|
||||
{
|
||||
const float rho_safe = fmaxf(rho, 1.0e-12f);
|
||||
const float tau0 = fmaxf(1.0f / fmaxf(omega0, 1.0e-6f), 0.500001f);
|
||||
const float nu0 = (tau0 - 0.5f) * (1.0f / 3.0f);
|
||||
|
||||
// Πneq = Σ (ci ci)(fi-feqi)
|
||||
float pixx = 0.0f, piyy = 0.0f, pixy = 0.0f;
|
||||
@@ -46,18 +60,36 @@ __device__ __forceinline__ float compute_omega_smag(const float* __restrict__ f,
|
||||
pixy += fneq * cx * cy;
|
||||
}
|
||||
|
||||
const float denom = 2.0f * rho_safe * CS2 * tau0;
|
||||
const float sxx = -pixx / denom;
|
||||
const float syy = -piyy / denom;
|
||||
const float sxy = -pixy / denom;
|
||||
// Self-consistent tau iteration for LES/TRT coupling.
|
||||
// S is estimated from Pi_neq with current tau_eff, then nu_t updates tau_eff.
|
||||
float tau_eff = tau0;
|
||||
const float tau_max = 1.0f / fmaxf(OMEGA_COLLISION_MIN, 1.0e-6f);
|
||||
|
||||
// |S| = sqrt(2 Sij Sij)
|
||||
const float s_mag = sqrtf(2.0f * (sxx*sxx + syy*syy + 2.0f*sxy*sxy));
|
||||
#pragma unroll
|
||||
for (int it = 0; it < LES_FP_ITERS; ++it) {
|
||||
const float denom = 2.0f * rho_safe * CS2 * fmaxf(tau_eff, 0.500001f);
|
||||
const float sxx = -pixx / denom;
|
||||
const float syy = -piyy / denom;
|
||||
const float sxy = -pixy / denom;
|
||||
|
||||
const float nu0 = (tau0 - 0.5f) * (1.0f / 3.0f);
|
||||
const float nut = (LES_CS * LES_CS) * s_mag;
|
||||
const float nue = nu0 + nut;
|
||||
return clamp_omega(1.0f / (3.0f * nue + 0.5f));
|
||||
// Use deviatoric strain to reduce SGS sensitivity to bulk/pressure-wave content.
|
||||
const float tr = 0.5f * (sxx + syy);
|
||||
const float sxx_dev = sxx - tr;
|
||||
const float syy_dev = syy - tr;
|
||||
const float sxy_dev = sxy;
|
||||
|
||||
// |S_dev| = sqrt(2 S_dev,ij S_dev,ij)
|
||||
const float s_mag = sqrtf(2.0f * (sxx_dev*sxx_dev + syy_dev*syy_dev + 2.0f*sxy_dev*sxy_dev));
|
||||
|
||||
float nut = (LES_CS * LES_CS) * s_mag;
|
||||
const float nut_cap = fmaxf(0.0f, LES_NUT_MAX_RATIO * nu0);
|
||||
nut = fminf(fmaxf(nut, 0.0f), nut_cap);
|
||||
|
||||
const float tau_new = fminf(tau_max, fmaxf(0.500001f, 0.5f + 3.0f * (nu0 + nut)));
|
||||
tau_eff = LES_FP_RELAX * tau_new + (1.0f - LES_FP_RELAX) * tau_eff;
|
||||
}
|
||||
|
||||
return clamp_omega(1.0f / tau_eff);
|
||||
}
|
||||
|
||||
#elif NQ == 19
|
||||
@@ -69,6 +101,7 @@ __device__ __forceinline__ float compute_omega_smag(const float* __restrict__ f,
|
||||
{
|
||||
const float rho_safe = fmaxf(rho, 1.0e-12f);
|
||||
const float tau0 = fmaxf(1.0f / fmaxf(omega0, 1.0e-6f), 0.500001f);
|
||||
const float nu0 = (tau0 - 0.5f) * (1.0f / 3.0f);
|
||||
|
||||
// Πneq = Σ (ci ci)(fi-feqi)
|
||||
float pixx = 0.0f, piyy = 0.0f, pizz = 0.0f;
|
||||
@@ -88,22 +121,42 @@ __device__ __forceinline__ float compute_omega_smag(const float* __restrict__ f,
|
||||
piyz += fneq * cy * cz;
|
||||
}
|
||||
|
||||
const float denom = 2.0f * rho_safe * CS2 * tau0;
|
||||
const float sxx = -pixx / denom;
|
||||
const float syy = -piyy / denom;
|
||||
const float szz = -pizz / denom;
|
||||
const float sxy = -pixy / denom;
|
||||
const float sxz = -pixz / denom;
|
||||
const float syz = -piyz / denom;
|
||||
// Self-consistent tau iteration for LES/TRT coupling.
|
||||
float tau_eff = tau0;
|
||||
const float tau_max = 1.0f / fmaxf(OMEGA_COLLISION_MIN, 1.0e-6f);
|
||||
|
||||
// |S| = sqrt(2 Sij Sij)
|
||||
const float s_mag = sqrtf(2.0f * (sxx*sxx + syy*syy + szz*szz
|
||||
+ 2.0f*(sxy*sxy + sxz*sxz + syz*syz)));
|
||||
#pragma unroll
|
||||
for (int it = 0; it < LES_FP_ITERS; ++it) {
|
||||
const float denom = 2.0f * rho_safe * CS2 * fmaxf(tau_eff, 0.500001f);
|
||||
const float sxx = -pixx / denom;
|
||||
const float syy = -piyy / denom;
|
||||
const float szz = -pizz / denom;
|
||||
const float sxy = -pixy / denom;
|
||||
const float sxz = -pixz / denom;
|
||||
const float syz = -piyz / denom;
|
||||
|
||||
const float nu0 = (tau0 - 0.5f) * (1.0f / 3.0f);
|
||||
const float nut = (LES_CS * LES_CS) * s_mag;
|
||||
const float nue = nu0 + nut;
|
||||
return clamp_omega(1.0f / (3.0f * nue + 0.5f));
|
||||
// Use deviatoric strain to reduce SGS sensitivity to bulk/pressure-wave content.
|
||||
const float tr = (sxx + syy + szz) / 3.0f;
|
||||
const float sxx_dev = sxx - tr;
|
||||
const float syy_dev = syy - tr;
|
||||
const float szz_dev = szz - tr;
|
||||
const float sxy_dev = sxy;
|
||||
const float sxz_dev = sxz;
|
||||
const float syz_dev = syz;
|
||||
|
||||
// |S_dev| = sqrt(2 S_dev,ij S_dev,ij)
|
||||
const float s_mag = sqrtf(2.0f * (sxx_dev*sxx_dev + syy_dev*syy_dev + szz_dev*szz_dev
|
||||
+ 2.0f*(sxy_dev*sxy_dev + sxz_dev*sxz_dev + syz_dev*syz_dev)));
|
||||
|
||||
float nut = (LES_CS * LES_CS) * s_mag;
|
||||
const float nut_cap = fmaxf(0.0f, LES_NUT_MAX_RATIO * nu0);
|
||||
nut = fminf(fmaxf(nut, 0.0f), nut_cap);
|
||||
|
||||
const float tau_new = fminf(tau_max, fmaxf(0.500001f, 0.5f + 3.0f * (nu0 + nut)));
|
||||
tau_eff = LES_FP_RELAX * tau_new + (1.0f - LES_FP_RELAX) * tau_eff;
|
||||
}
|
||||
|
||||
return clamp_omega(1.0f / tau_eff);
|
||||
}
|
||||
|
||||
#endif // NQ
|
||||
|
||||
@@ -128,12 +128,12 @@ __global__ void StreamCollideDouble(
|
||||
|
||||
// ----- Wall bounce-back (top/bottom walls) -----
|
||||
#if NQ == 9
|
||||
if (y == 1 || y == NY - 2) {
|
||||
apply_wall_bb_d2q9(y, f);
|
||||
if ((fl & LEGACY_FLUID) && (y == 1u || y == (unsigned int)(NY - 2))) {
|
||||
apply_wall_bb_d2q9_y_pull(y, f, fi_in, k);
|
||||
}
|
||||
#elif NQ == 19
|
||||
if (y == 1 || y == NY - 2) {
|
||||
apply_wall_bb_d3q19_y(y, f);
|
||||
if ((fl & LEGACY_FLUID) && (y == 1u || y == (unsigned int)(NY - 2))) {
|
||||
apply_wall_bb_d3q19_y_pull(y, f, fi_in, k);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -143,6 +143,7 @@ __global__ void StreamCollideDouble(
|
||||
float Fin[NQ];
|
||||
|
||||
#if NQ == 9
|
||||
compute_rho_u(f, rho_n, ux, uy);
|
||||
compute_feq(rho_n, ux, uy, feq);
|
||||
zero_forcing(Fin);
|
||||
float omega_col = d_params.omega;
|
||||
@@ -161,6 +162,7 @@ __global__ void StreamCollideDouble(
|
||||
#endif
|
||||
|
||||
#elif NQ == 19
|
||||
compute_rho_u(f, rho_n, ux, uy, uz);
|
||||
compute_feq(rho_n, ux, uy, uz, feq);
|
||||
zero_forcing(Fin);
|
||||
float omega_col = d_params.omega;
|
||||
|
||||
Reference in New Issue
Block a user