test of integrating neural network in pyCALC-RANS solver k epsilon model
| boundary-layer-RANS-keps-NN | ||
| .gitignore | ||
| calc_earsm.py | ||
| pyCALC-RANS.py | ||
| pyCALC-readme | ||
| README.md | ||
| README~ | ||
2D Flat-Plate Boundary Layer — RANS k-ε with Neural Network damping functions
Overview
The NN is trained on DNS channel flow data (Re_τ = 10 000) using NN_SR.py
and then loaded into the CFD solver via calceps_NN_SR.py.
Directory structure
2d-example-boundary-layer/
├── pyCALC-RANS.py # main solver (do not edit)
├── global # global variable declarations (do not edit)
├── calc_earsm.py # empty EARSM placeholder (not used here)
│
└── boundary-layer-RANS-keps-NN/
├── run-python # bash script to assemble and run the solver
├── setup_case.py # case settings (turbulence model, BCs, solvers)
├── modify_case.py # hooks: modify_init, fix_eps, modify_eps, etc.
├── calceps_NN_SR.py # NN-augmented calceps (replaces calceps in solver)
├── generate-bound-layer-grid.py # grid generation script
├── plot_inlet_bound.py # post-processing and plotting
├── create-inlet-rans-profiles.py # saves inlet profiles for restarts
├── vel_2540_dns.prof # DNS reference data (Re_θ ≈ 2540)
├── x2d.dat, y2d.dat # grid files (generated by grid script)
├── z.dat # spanwise extent
└── nn/ # trained NN model files
├── model-f_2-f_mu-Re10000.pth
├── scaler-input0-f_2-f_mu-Re10000.bin
├── scaler-input1-f_2-f_mu-Re10000.bin
└── min-max-f_2-f_mu-Re10000.txt
Changes from the original k-ω boundary layer case
setup_case.py
| Parameter | Original (k-ω) | New (k-ε NN) |
|---|---|---|
kom |
True |
False |
keps |
not set | True |
c_omega_1/2 |
5/9, 3/40 |
kept as dummies (required by solver init) |
prand_omega |
2.0 |
kept as dummy |
c_eps_1 |
— | 1.5 |
c_eps_2 |
— | 1.9 |
prand_eps |
— | 1.4 |
prand_k |
2.0 |
1.4 |
urf_omega |
0.5 |
kept as dummy |
urf_eps |
— | 0.5 |
convergence_limit_om |
-1e-6 |
kept as dummy |
convergence_limit_eps |
— | -1e-6 |
restart |
True |
False (no eps restart file from k-ω run) |
| omega BCs | full block | replaced with eps BCs |
Epsilon wall BC (eps_bc_south) is set as Dirichlet but the value is
overridden every iteration by fix_eps() using ε = 2ν k/y².
modify_case.py
modify_om→ replaced withmodify_eps(same inlet injection logic)fix_omega→ replaced withfix_eps(enforces ε = 2ν k/y² at south wall each iteration)modify_init→ initialiseseps2d = Cμ k²/(100ν)instead ofom2d; return order isu2d, v2d, k2d, om2d, eps2d, vis2d, dist
calceps_NN_SR.py (new file, injected by run-python)
Replaces calceps in pyCALC-RANS.py. Key differences from the standard solver:
- Loads
NuNetmodel (tanh activations, dynamic layer list viann.ModuleList) - Predicts both f₂ and f_μ simultaneously (2-output network)
- Inputs: y⁺ (scaled with local u_τ from south wall) and y* (from ε)
- Outputs clipped to training min/max range
- Set
NN_bool = Falseto fall back to standard analytic AKN expressions
run-python
The original k-ω script used cat directly. The new script:
- Renames
calceps→calceps_standardin the solver (viased) - Renames
fix_eps→fix_eps_standardin the solver (viased) - Injects
calceps_NN_SR.pywith the NN version before the solver - Uses
echo ""between files to prevent concatenation syntax errors
#!/bin/bash
sed '/setup_case()/d' setup_case.py > temp_file
sed 's/def calceps/def calceps_standard/' ../pyCALC-RANS.py | \
sed 's/def fix_eps/def fix_eps_standard/' > temp_file1
{
cat ../global
echo ""
cat temp_file
echo ""
cat modify_case.py
echo ""
cat calceps_NN_SR.py
echo ""
cat temp_file1
} > exec-pyCALC-RANS.py
/chalmers/groups/lada_8/anaconda3/bin/python -u exec-pyCALC-RANS.py > out
How to run
Step 1 — Generate the grid (first time only)
cd boundary-layer-RANS-keps-NN/
python generate-bound-layer-grid.py
This produces x2d.dat and y2d.dat.
Step 2 — Train the NN (first time only, or if re-training)
The NN files are already in nn/. If you need to retrain:
cd ../ # go to 2d-example-boundary-layer/
python NN_SR.py
This writes the model, scalers and min-max file into nn/.
Step 3 — Link the nn/ folder into the case directory
cd boundary-layer-RANS-keps-NN/
ln -s ../nn nn # only needed if nn/ symlink does not already exist
Step 4 — Run the CFD solver
chmod +x run-python # only needed once
./run-python
Output is written to out. Monitor convergence in a second terminal:
tail -f out | grep 'max res'
The run is converged when max res drops below sormax = 5e-5 (set in setup_case.py).
Step 5 — Plot results
python plot_inlet_bound.py
Produces the following figures:
| File | Contents |
|---|---|
ustar-vs-x.png |
Friction velocity u_τ along the plate |
u_log_python.png |
U⁺ vs y⁺ (log), compared to DNS at Re_θ ≈ 2540 |
vis_python.png |
ν_t/ν vs y⁺ at three x stations |
vis_vs_y_python.png |
ν_t/ν vs y at three x stations |
eps_vs_y_python.png |
ε vs y at three x stations |
k_model_python.png |
k/u_τ² vs y⁺ at three x stations |
k_vs_y_python.png |
k/u_τ² vs y at three x stations |
cf_vs_re_mom.png |
Skin friction C_f vs Re_θ, compared to empirical correlation |