PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
Filter.c
Go to the documentation of this file.
1/**
2 * @file Filter.c
3 * @brief Implements numerical filtering schemes for Large Eddy Simulation (LES).
4 *
5 * This file contains the functions necessary to apply a "test filter" to the resolved
6 * velocity field, which is a core component of the dynamic Smagorinsky turbulence model.
7 * The choice of filter (e.g., a general 3D box filter or a specialized 2D
8 * homogeneous filter) is determined by the simulation's configuration.
9 */
10
11#include "Filter.h"
12#include <math.h> // Required for fabs() in the safe division check.
13
14/*================================================================================*
15 * INTERNAL (STATIC) FUNCTIONS *
16 *================================================================================*/
17
18/**
19 * @brief (Internal) Applies a 2D homogeneous test filter in the i-k plane using Simpson's rule.
20 *
21 * This is a specialized, high-accuracy filter designed for flow cases that are statistically
22 * homogeneous in two directions (e.g., the streamwise and spanwise directions of a channel flow).
23 * It uses a fixed-weight stencil based on Simpson's rule for numerical integration, which
24 * offers better spectral properties than a simple box filter. The weights are constant and
25 * do not depend on cell volume, assuming a uniform grid in the homogeneous plane.
26 *
27 * @ref Morinishi, Kera, & Vasilyev, "Skew-symmetric formulation for large eddy simulation
28 * of compressible turbulent flows," Journal of Computational Physics, 2004.
29 *
30 * @param values The 3x3x3 array of scalar values from the local stencil. For this 2D filter,
31 * only the central j-plane (j-index = 1) is utilized.
32 * @return The filtered scalar value.
33 */
34static double ApplySimpsonRuleHomogeneousFilter(double values[3][3][3])
35{
36 // The stencil only uses the central j-plane (j-index = 1, corresponding to the y-direction).
37 // The formula is a weighted sum of the 9 points in that plane.
38 const double corners = values[0][1][0] + values[2][1][0] + values[0][1][2] + values[2][1][2]; // 4 corner points
39 const double edges = values[0][1][1] + values[1][1][0] + values[2][1][1] + values[1][1][2]; // 4 edge-center points
40 const double center = values[1][1][1]; // 1 center point
41
42 // The weights (1, 4, 16) and normalization factor (36) come from the 2D Simpson's rule.
43 return (corners + 4.0 * edges + 16.0 * center) / 36.0;
44}
45
46
47/**
48 * @brief (Internal) Applies a volume-weighted 3D box filter to a 3x3x3 stencil.
49 *
50 * This is the general-purpose filter for non-uniform, curvilinear grids. To correctly
51 * average a quantity over a space with varying cell sizes, the contribution of each
52 * point must be weighted by its associated volume (or, equivalently, 1/Jacobian).
53 *
54 * The logic calculates a weighted average over eight 2x2x2 sub-cubes (or "octants")
55 * that surround the central node of the 3x3x3 stencil. This produces a filtered value
56 * representative of the larger volume covered by the test filter.
57 *
58 * @param values The 3x3x3 array of scalar values at the stencil points.
59 * @param weights The 3x3x3 array of weights, where weight = 1.0 / cell_volume (i.e., the Jacobian).
60 * @return The volume-weighted filtered scalar value.
61 */
62static double ApplyVolumeWeightedBoxFilter(double values[3][3][3], double weights[3][3][3])
63{
64 // v1...v8 store the sum of (value * weight) for each of the 8 sub-cubes.
65 // w1...w8 store the sum of (weight) for each of the 8 sub-cubes.
66 double v1, v2, v3, v4, v5, v6, v7, v8;
67 double w1, w2, w3, w4, w5, w6, w7, w8;
68
69 // --- Calculations for the 4 sub-cubes on the bottom layer (k-indices 0 and 1) ---
70
71 // Bottom-Back-Left sub-cube (i-indices: 0,1; j-indices: 0,1; k-indices: 0,1)
72 v1 = ( values[0][0][0]*weights[0][0][0] + values[1][0][0]*weights[1][0][0] + values[0][1][0]*weights[0][1][0] + values[1][1][0]*weights[1][1][0] +
73 values[0][0][1]*weights[0][0][1] + values[1][0][1]*weights[1][0][1] + values[0][1][1]*weights[0][1][1] + values[1][1][1]*weights[1][1][1] );
74 w1 = ( weights[0][0][0] + weights[1][0][0] + weights[0][1][0] + weights[1][1][0] +
75 weights[0][0][1] + weights[1][0][1] + weights[0][1][1] + weights[1][1][1] );
76
77 // Bottom-Back-Right sub-cube (i-indices: 1,2; j-indices: 0,1; k-indices: 0,1)
78 v2 = ( values[1][0][0]*weights[1][0][0] + values[2][0][0]*weights[2][0][0] + values[1][1][0]*weights[1][1][0] + values[2][1][0]*weights[2][1][0] +
79 values[1][0][1]*weights[1][0][1] + values[2][0][1]*weights[2][0][1] + values[1][1][1]*weights[1][1][1] + values[2][1][1]*weights[2][1][1] );
80 w2 = ( weights[1][0][0] + weights[2][0][0] + weights[1][1][0] + weights[2][1][0] +
81 weights[1][0][1] + weights[2][0][1] + weights[1][1][1] + weights[2][1][1] );
82
83 // Bottom-Front-Left sub-cube (i-indices: 0,1; j-indices: 1,2; k-indices: 0,1)
84 v3 = ( values[0][1][0]*weights[0][1][0] + values[1][1][0]*weights[1][1][0] + values[0][2][0]*weights[0][2][0] + values[1][2][0]*weights[1][2][0] +
85 values[0][1][1]*weights[0][1][1] + values[1][1][1]*weights[1][1][1] + values[0][2][1]*weights[0][2][1] + values[1][2][1]*weights[1][2][1] );
86 w3 = ( weights[0][1][0] + weights[1][1][0] + weights[0][2][0] + weights[1][2][0] +
87 weights[0][1][1] + weights[1][1][1] + weights[0][2][1] + weights[1][2][1] );
88
89 // Bottom-Front-Right sub-cube (i-indices: 1,2; j-indices: 1,2; k-indices: 0,1)
90 v4 = ( values[1][1][0]*weights[1][1][0] + values[2][1][0]*weights[2][1][0] + values[1][2][0]*weights[1][2][0] + values[2][2][0]*weights[2][2][0] +
91 values[1][1][1]*weights[1][1][1] + values[2][1][1]*weights[2][1][1] + values[1][2][1]*weights[1][2][1] + values[2][2][1]*weights[2][2][1] );
92 w4 = ( weights[1][1][0] + weights[2][1][0] + weights[1][2][0] + weights[2][2][0] +
93 weights[1][1][1] + weights[2][1][1] + weights[1][2][1] + weights[2][2][1] );
94
95
96 // --- Calculations for the 4 sub-cubes on the top layer (k-indices 1 and 2) ---
97
98 // Top-Back-Left sub-cube (i-indices: 0,1; j-indices: 0,1; k-indices: 1,2)
99 v5 = ( values[0][0][1]*weights[0][0][1] + values[1][0][1]*weights[1][0][1] + values[0][1][1]*weights[0][1][1] + values[1][1][1]*weights[1][1][1] +
100 values[0][0][2]*weights[0][0][2] + values[1][0][2]*weights[1][0][2] + values[0][1][2]*weights[0][1][2] + values[1][1][2]*weights[1][1][2] );
101 w5 = ( weights[0][0][1] + weights[1][0][1] + weights[0][1][1] + weights[1][1][1] +
102 weights[0][0][2] + weights[1][0][2] + weights[0][1][2] + weights[1][1][2] );
103
104 // Top-Back-Right sub-cube (i-indices: 1,2; j-indices: 0,1; k-indices: 1,2)
105 v6 = ( values[1][0][1]*weights[1][0][1] + values[2][0][1]*weights[2][0][1] + values[1][1][1]*weights[1][1][1] + values[2][1][1]*weights[2][1][1] +
106 values[1][0][2]*weights[1][0][2] + values[2][0][2]*weights[2][0][2] + values[1][1][2]*weights[1][1][2] + values[2][1][2]*weights[2][1][2] );
107 w6 = ( weights[1][0][1] + weights[2][0][1] + weights[1][1][1] + weights[2][1][1] +
108 weights[1][0][2] + weights[2][0][2] + weights[1][1][2] + weights[2][1][2] );
109
110 // Top-Front-Left sub-cube (i-indices: 0,1; j-indices: 1,2; k-indices: 1,2)
111 v7 = ( values[0][1][1]*weights[0][1][1] + values[1][1][1]*weights[1][1][1] + values[0][2][1]*weights[0][2][1] + values[1][2][1]*weights[1][2][1] +
112 values[0][1][2]*weights[0][1][2] + values[1][1][2]*weights[1][1][2] + values[0][2][2]*weights[0][2][2] + values[1][2][2]*weights[1][2][2] );
113 w7 = ( weights[0][1][1] + weights[1][1][1] + weights[0][2][1] + weights[1][2][1] +
114 weights[0][1][2] + weights[1][1][2] + weights[0][2][2] + weights[1][2][2] );
115
116 // Top-Front-Right sub-cube (i-indices: 1,2; j-indices: 1,2; k-indices: 1,2)
117 v8 = ( values[1][1][1]*weights[1][1][1] + values[2][1][1]*weights[2][1][1] + values[1][2][1]*weights[1][2][1] + values[2][2][1]*weights[2][2][1] +
118 values[1][1][2]*weights[1][1][2] + values[2][1][2]*weights[2][1][2] + values[1][2][2]*weights[1][2][2] + values[2][2][2]*weights[2][2][2] );
119 w8 = ( weights[1][1][1] + weights[2][1][1] + weights[1][2][1] + weights[2][2][1] +
120 weights[1][1][2] + weights[2][1][2] + weights[1][2][2] + weights[2][2][2] );
121
122 // Sum the contributions from all 8 octants.
123 double total_weighted_value = v1+v2+v3+v4+v5+v6+v7+v8;
124 double total_weight = w1+w2+w3+w4+w5+w6+w7+w8;
125
126 // Production safety check: avoid division by zero if all weights are zero
127 // (e.g., if the stencil is entirely inside a solid body).
128 if (fabs(total_weight) < 1.0e-12) {
129 return 0.0;
130 }
131
132 return total_weighted_value / total_weight;
133}
134
135
136/*================================================================================*
137 * PUBLIC FUNCTIONS *
138 *================================================================================*/
139
140#undef __FUNCT__
141#define __FUNCT__ "ApplyLESTestFilter"
142/**
143 * @brief Public interface for applying the LES test filter.
144 */
145double ApplyLESTestFilter(const SimCtx *simCtx, double values[3][3][3], double weights[3][3][3])
146{
147 // This function acts as a dispatcher. It reads the simulation configuration
148 // and calls the appropriate internal filtering routine.
149 if (simCtx->testfilter_ik) {
150 // If the "-testfilter_ik" flag is set, it indicates that the simulation
151 // is homogeneous in the i and k directions, so we use the specialized,
152 // more accurate Simpson's rule filter. The `weights` are ignored in this case.
154 } else {
155 // This is the default case for general, non-uniform, curvilinear grids.
156 // The volume-weighted box filter is used to ensure correct averaging.
157 return ApplyVolumeWeightedBoxFilter(values, weights);
158 }
159}
double ApplyLESTestFilter(const SimCtx *simCtx, double values[3][3][3], double weights[3][3][3])
Public interface for applying the LES test filter.
Definition Filter.c:145
static double ApplyVolumeWeightedBoxFilter(double values[3][3][3], double weights[3][3][3])
(Internal) Applies a volume-weighted 3D box filter to a 3x3x3 stencil.
Definition Filter.c:62
static double ApplySimpsonRuleHomogeneousFilter(double values[3][3][3])
(Internal) Applies a 2D homogeneous test filter in the i-k plane using Simpson's rule.
Definition Filter.c:34
PetscInt testfilter_ik
Definition variables.h:612
The master context for the entire simulation.
Definition variables.h:538