Matching Custom Inlet¶
You have \(U(z)\) and \(I_u(z)\) profiles from a wind tunnel test and want to reproduce those exact conditions in AeroSim. The obstacle is that AeroSim’s inlet does not accept velocity and turbulence intensity directly - it requires a Reynolds stress tensor at each height. This guide walks through converting your measured profiles into the format AeroSim expects, configuring the ground roughness to match your tunnel floor, and confirming the profile is preserved at the model location.
Note
If you are working from a standard terrain category (e.g., Eurocode Category I–IV) without measured profiles, the ABL guided case covers that workflow end to end.
What the SEM Profile Requires¶
Before building the profile, establish the target format. The inlet CSV must contain the following columns:
z,ux,Rxx,Rxy,Rxz,Ryy,Ryz,Rzz
Column |
Physical quantity |
Units |
Notes |
|---|---|---|---|
|
Height above ground |
m |
Must start at or near the ground; values outside the CSV range are extrapolated as constants |
|
Mean streamwise velocity |
m/s |
Positive in the flow direction |
|
Reynolds stress \(\overline{u'u'}\) |
m^2/s^2 |
Streamwise normal stress; always positive |
|
Reynolds stress \(\overline{u'v'}\) |
m^2/s^2 |
Typically zero for neutral ABL with flow aligned to x |
|
Reynolds stress \(\overline{u'w'}\) |
m^2/s^2 |
Negative in a standard ABL |
|
Reynolds stress \(\overline{v'v'}\) |
m^2/s^2 |
Lateral normal stress; always positive |
|
Reynolds stress \(\overline{v'w'}\) |
m^2/s^2 |
Typically zero for neutral ABL |
|
Reynolds stress \(\overline{w'w'}\) |
m^2/s^2 |
Vertical normal stress; always positive |
The Reynolds stress tensor at each height must be positive semi-definite - otherwise the Cholesky decomposition used internally by the SEM will fail or produce unphysical turbulence.
Important
The necessary condition most commonly violated in practice is the determinant constraint along the xz plane:
Step 1: Prepare Your Input Data¶
Mean Velocity Profile U(z)¶
Your input is a set of height samples with mean streamwise velocity \(U(z)\) - typically from pitot tube or hot-wire probe traverses at discrete heights above the tunnel floor. Collect these into a two-column table (z in metres, U in m/s) before proceeding.
If measurements do not cover the full inlet height, you can extrapolate using a log-law fit \(U = a + b\ln(z)\) through the measured points, where \(b = u_*/\kappa\). This is optional: only do it if you need values at heights not covered by the traverse.
Turbulence Intensity Profile Iu(z)¶
Turbulence intensity is defined as:
Use the \(I_u(z)\) values directly from your wind tunnel calibration report. These are measured quantities - do not substitute a code formula unless measured data is unavailable.
Aerodynamic Roughness Length z0¶
The SEM inlet and the ground boundary condition both require a roughness length \(z_0\). Use the value that characterises your wind tunnel floor roughness, as specified in your tunnel calibration sheet.
If \(z_0\) is not explicitly stated, fit the log-law through your measured \(U(z)\) traverse:
Fitting \(U = a + b\ln(z)\) gives \(b = u_*/\kappa\) and \(z_0 = z \exp(-U(z)/b)\) at any measured point.
Step 2: Build the Profile CSV¶
The script below reads your measured U(z) and Iu(z) data, derives all Reynolds stress components, checks the positive semi-definiteness constraint, and writes the inlet CSV. The input file must have at least three columns: z (m), ux (m/s), and Iu (dimensionless). If you also have measured Iv and Iw, add them as additional columns.
For a full explanation of how each stress component is derived, see Reynolds Stress Derivation.
import numpy as np
import pandas as pd
# --- Load your measured wind tunnel profile ---
# Required columns: z (m), ux (m/s), Iu (dimensionless)
# Optional columns: Iv, Iw - use measured values when available
inp = pd.read_csv("my_profile.csv")
z = inp["z"].to_numpy()
ux = inp["ux"].to_numpy()
Iu = inp["Iu"].to_numpy()
# --- Rxx ---
sigma_u = Iu * ux
Rxx = sigma_u ** 2
# --- Ryy, Rzz ---
# Use measured Iv, Iw if available; otherwise apply default ratios
if "Iv" in inp.columns and "Iw" in inp.columns:
Ryy = (inp["Iv"].to_numpy() * ux) ** 2
Rzz = (inp["Iw"].to_numpy() * ux) ** 2
else:
Ryy = 0.5625 * Rxx # sigma_v / sigma_u ≈ 0.75
Rzz = 0.25 * Rxx # sigma_w / sigma_u ≈ 0.50
# --- Rxy, Ryz ---
Rxy = np.zeros_like(z)
Ryz = np.zeros_like(z)
# --- Rxz ---
# Default: simplified ratio - safe and sufficient for most wind tunnel profiles.
# Alternative (commented out): derive from log-law fit of the traverse.
Rxz = -0.3 * Rxx
# Alternative Rxz using log-law (requires z0 from tunnel floor calibration):
# kappa = 0.41
# z0 = 0.05 # aerodynamic roughness length (m) - must match ground BC in AeroSim
# delta = 500.0 # tunnel boundary layer depth (m, full-scale equivalent)
# u_star = np.median(kappa * ux / np.log(z / z0))
# taper = np.clip(1.0 - z / delta, 0.0, 1.0)
# Rxz = -(u_star ** 2) * taper
# --- Positive semi-definiteness check ---
check = Rxx * Rzz - Rxz ** 2
if np.any(check < 0):
failing = z[check < 0]
raise ValueError(
f"Positive semi-definiteness violated at z = {failing} m.\n"
"Use the simplified Rxz = -0.3 * Rxx or reduce |Rxz|."
)
# --- Write CSV ---
df = pd.DataFrame({
"z": z,
"ux": ux,
"Rxx": Rxx,
"Rxy": Rxy,
"Rxz": Rxz,
"Ryy": Ryy,
"Ryz": Ryz,
"Rzz": Rzz,
})
df.to_csv("inlet_profile.csv", index=False)
print("Written: inlet_profile.csv")
print(df.head())
Warning
The column header must contain z,ux,Rxx,Rxy,Rxz,Ryy,Ryz,Rzz - deviations will cause the interface to reject the file. Values outside the covered range of the profile are extrapolated as constants at the boundary values.
Step 3: Upload the Profile in AeroSim¶
In the AeroSim web interface, navigate to the Inlet Boundary Condition panel and:
Upload the CSV file produced in Step 3.
Set the ground boundary condition \(z_0\) to the same \(z_0\) identified from your wind tunnel floor (Step 1).
No additional backend parameters need to be configured. The \(z_0\) in the ground BC panel and the \(z_0\) that characterises your inlet profile must be consistent. A mismatch causes the profile to drift downstream as the solver equilibrates to the actual ground roughness.
Step 4: Configure Ground Roughness¶
The roughness fins must produce a floor roughness consistent with the \(z_0\) of your wind tunnel profile. Select the fin category whose \(z_0\) is closest to the value identified in Step 1:
Terrain Category |
\(z_0\) (m) |
Fin height |
|---|---|---|
0 (open sea) |
0.003 |
no fins |
I |
0.01 |
1 m |
II |
0.05 |
2 m |
III |
0.3 |
6 m |
IV |
1.0 |
10 m |
If your tunnel \(z_0\) falls between two categories, interpolate the fin height between the adjacent defaults.
Note
See the ABL guided case for fin geometry details, IBM wall model settings, and lateral and top boundary conditions.
Step 5: Run an Empty-Domain Validation¶
Before adding any geometry, run an empty-domain case - no buildings, same domain dimensions and roughness configuration - to confirm the profile at the model location matches your target. Export velocity probes at one or more streamwise stations at the expected position of the model. See the ABL guided case for how to configure the domain and request probe exports in the interface.
Important
Do not skip this step. A profile that looks correct at the inlet may drift over the domain fetch if the inlet \(z_0\) and the floor roughness are not well matched. Confirm that the profile at the model location reproduces your wind tunnel measurements before introducing geometry.
Step 6: Compare Against the Target¶
With probe data downloaded from AeroSim, compute time-averaged statistics at each height and compare against the target profile.
At each height, compute from the exported time series:
Mean velocity: \(\bar{U}_{sim} = \text{mean}(u_x)\)
RMS: \(\sigma_u = \text{std}(u_x)\)
Turbulence intensity: \(I_{u,sim} = \sigma_u / \bar{U}_{sim}\)
Troubleshooting¶
Symptom |
Likely cause |
Fix |
|---|---|---|
Simulation crashes / NaN values |
Cholesky failure - tensor not positive semi-definite |
Check \(R_{xx} \cdot R_{zz} \geq R_{xz}^2\) at every height; verify \(R_{xz}\) is negative |
\(I_u\) in simulation much lower than target |
\(I_u\) underestimated in CSV |
Recalculate \(I_u\); verify stress component ratios against source data |
\(I_u\) in simulation much higher than target |
Stress components overestimated |
Recalculate \(\sigma_u = I_u \cdot U\); check \(R_{yy}\), \(R_{zz}\) ratios |
Profile drifts significantly downstream |
Roughness \(z_0\) inconsistent with inlet \(z_0\) |
Verify that the fin category matches your tunnel floor \(z_0\); interpolate fin height between categories if needed |
Profile height mismatch in domain |
Datum offset not accounted for |
Shift the |
File not accepted / interface error |
Column header mismatch |
Verify header is exactly |
Next Steps¶
With a validated empty-domain profile, the inlet CSV and roughness configuration can be reused in the full engineering simulation. See:
ABL guided case - standard terrain category setup, domain guidelines, and refinement recommendations.
Pressure on Buildings (CAARC) - applying the validated profile to a reference high-rise.
Pedestrian Comfort - extending the setup to evaluate wind conditions at ground level.
Topography Factor (Bolund Hill) - accounting for terrain effects on the wind profile.