2dBoundaryLayerExample/README.md
2026-04-24 13:14:31 +02:00

179 lines
5.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 with `modify_eps` (same inlet injection logic)
- `fix_omega` → replaced with `fix_eps` (enforces ε = 2ν k/y² at south wall each iteration)
- `modify_init` → initialises `eps2d = Cμ k²/(100ν)` instead of `om2d`; **return order** is `u2d, 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 `NuNet` model (tanh activations, dynamic layer list via `nn.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 = False` to fall back to standard analytic AKN expressions
### `run-python`
The original k-ω script used `cat` directly. The new script:
1. Renames `calceps``calceps_standard` in the solver (via `sed`)
2. Renames `fix_eps``fix_eps_standard` in the solver (via `sed`)
3. Injects `calceps_NN_SR.py` with the NN version before the solver
4. Uses `echo ""` between files to prevent concatenation syntax errors
```bash
#!/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)
```bash
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:
```bash
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
```bash
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
```bash
chmod +x run-python # only needed once
./run-python
```
Output is written to `out`. Monitor convergence in a second terminal:
```bash
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
```bash
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 |
---