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

z

Height above ground

m

Must start at or near the ground; values outside the CSV range are extrapolated as constants

ux

Mean streamwise velocity

m/s

Positive in the flow direction

Rxx

Reynolds stress \(\overline{u'u'}\)

m^2/s^2

Streamwise normal stress; always positive

Rxy

Reynolds stress \(\overline{u'v'}\)

m^2/s^2

Typically zero for neutral ABL with flow aligned to x

Rxz

Reynolds stress \(\overline{u'w'}\)

m^2/s^2

Negative in a standard ABL

Ryy

Reynolds stress \(\overline{v'v'}\)

m^2/s^2

Lateral normal stress; always positive

Ryz

Reynolds stress \(\overline{v'w'}\)

m^2/s^2

Typically zero for neutral ABL

Rzz

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:

\[ R_{xx} \cdot R_{zz} \geq R_{xz}^2 \]

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:

\[ I_u(z) = \frac{\sigma_u}{U(z)} \]

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:

\[ U(z) = \frac{u_*}{\kappa} \ln\left(\frac{z}{z_0}\right) \]

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:

  1. Upload the CSV file produced in Step 3.

  2. 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 z column in the CSV by the offset between the domain floor and the measurement datum

File not accepted / interface error

Column header mismatch

Verify header is exactly z,ux,Rxx,Rxy,Rxz,Ryy,Ryz,Rzz (case-sensitive)


Next Steps

With a validated empty-domain profile, the inlet CSV and roughness configuration can be reused in the full engineering simulation. See: