Turbulent Boundary Layer Environments
This module provides zero-pressure-gradient turbulent boundary layer (ZPG TBL) CFD environments for reinforcement learning.
Two actuation strategies share the ZPGTBLBase class:
ZPGTBLJet
Jet-based actuation. Actions lie in [−MAX_CONTROL, +MAX_CONTROL]
and are passed directly to the CFD solver.
ZPGTBLSurfaceWave
Actuated traveling surface wave parameterized by
[amplitude, speed, wavelength]. All three parameters are strictly
positive, which requires a per-action asymmetric action space and a
non-zero reset; see the class docstring for config details.
Both environments receive the same solver force output
[F_x, F_y, F_z, C_P, area] (nDim + 2 elements), where C_P is
the power coefficient (0.0 for the jet case) and area is the wetted
wall area used for normalisation. Forces and power are pre-normalised by
the solver by dynamic pressure (q_∞ = ρ_∞ · ½ · U_∞²) and
q_∞ · U_∞ respectively, so dividing by area yields the final
dimensionless coefficients. Observation sizing and normalization for this
extended force vector are handled in ZPGTBLBase.
ZPGTBLBase Objects
class ZPGTBLBase(MaiaFlowEnv)
Base class for zero-pressure-gradient turbulent boundary layer environments.
Uses the structured-grid m-AIA solver (MAIA_STRCTRD).
Both actuation variants (jet and surface wave) receive the solver force
output [F_x, F_y, F_z, C_P, area] (nDim + 2 elements).
Forces F_x … F_z and C_P are pre-normalised by the solver using
the dynamic pressure q_∞ = ρ_∞ · ½ · U_∞² (forces) and
q_∞ · U_∞ (power), so dividing by the returned area gives the
final dimensionless drag and power coefficients.
This class provides:
configure_observations()– sizes the force observation slot tonDim + 2per boundary.setup_normalization()– handles theC_Pandareaelements in the'U_inf'normalization strategy.get_reward()– unified reward for both actuation variants:
.. math::
R = -C_D - \omega \cdot C_P
where C_D = F_x / area and C_P = forces[3] / area.
For the jet case C_P = 0, so the reward reduces to -C_D.
configure_observations
def configure_observations() -> None
Configure the number of observations.
Overrides the base class to account for the extended solver force
output [F_x, F_y, F_z, C_P, area] (nDim + 2 elements per
boundary instead of nDim).
setup_normalization
def setup_normalization() -> None
Set up observation normalization.
Overrides the base class to handle the extended force vector
[F_x, F_y, F_z, C_P, area] (nDim + 2 elements per boundary).
For the 'U_inf' strategy, free-stream values computed from the
isentropic relations in __init__ are used:
- velocities scaled by
U_T(total free-stream speed), - density by
rho_inf, - pressure by the dynamic pressure
q_inf = ½ · rho_inf · U_T².
Force-slot entries use loc = 0, scale = 1 since the solver
already normalises them by q_inf (forces) / q_inf·U_T (power).
get_reward
def get_reward() -> Tuple[float, Dict]
Compute the unified reward: :math:R = -C_D - \omega \cdot C_P.
The solver returns [F_x, F_y, F_z, C_P, area]. Forces and
power are pre-normalised by q_∞ and q_∞ · U_∞ respectively;
dividing by area yields the final dimensionless coefficients::
C_D = forces[0] / area C_P = forces[3] / area
For the jet environment C_P = 0 and the reward reduces to
-C_D.
Returns:
Tuple of (reward, info_dict). info_dict contains
'forces', 'C_D', and 'C_P'.
ZPGTBLJet Objects
class ZPGTBLJet(ZPGTBLBase)
ZPG turbulent boundary layer with jet-based flow control.
Uses the same actuation setup as the :class:~hydrogym.maia.envs.Cube
environment: actions lie in [−MAX_CONTROL, +MAX_CONTROL] (scaled by
the base-class step()) and are passed directly to the CFD solver.
The number of jet actuators is read from lbNoJets in the property file.
The solver returns [F_x, F_y, F_z, C_P, area] with C_P = 0.0,
so the reward reduces to -C_D. Observation sizing, normalization,
and reward computation are all provided by :class:``3.
Attributes:
- ``4 int - Number of jet boundary conditions.
convert_action
def convert_action(action: np.ndarray) -> np.ndarray
Pass the (already MAX_CONTROL-scaled) jet actuation to the solver.
Arguments:
action- Action array scaled byMAX_CONTROL.
Returns:
Action array for the CFD solver.
ZPGTBLSurfaceWave Objects
class ZPGTBLSurfaceWave(ZPGTBLBase)
ZPG turbulent boundary layer with actuated traveling surface wave.
The surface is driven by a traveling wave with three strictly positive control parameters:
============= ============================================= Parameter Description ============= ============================================= amplitude Wave amplitude speed Wave propagation speed wavelength Spatial period of the wave ============= =============================================
Differences from :class:ZPGTBLJet:
- Action space – Per-action
[lower_bound, upper_bound]read frommaia.action_lower_bounds/maia.action_upper_boundsin the config. TheMAX_CONTROLscaling instep()is not applied. - Reset – Uses
[amplitude_init, speed_init, wavelength_init]from config (zeros would crash the CFD solver). - Reward – :math:
3 (shared with :class:`ZPGTBLJet` via :class:5; hereC_P > 0because the surface wave performs work on the fluid).
Required additional entries in the maia section of the YAML config:
.. code-block:: yaml
maia: amplitude_init: 0.05 speed_init: 1.0 wavelength_init: 2.0 action_lower_bounds: [0.01, 0.1, 0.5] action_upper_bounds: [0.2, 3.0, 10.0] omega: 0.1
set_observation_action_spaces
def set_observation_action_spaces() -> None
Override to use a normalized [0, 1] action space per parameter.
The RL agent always operates in [0, 1]; physical values are
recovered inside :meth:convert_action via the per-parameter affine
mapping lower + action * (upper - lower).
convert_action
def convert_action(action: np.ndarray) -> List[float]
Scale a normalized [0, 1] action to physical wave parameters.
Applies the per-parameter affine map::
physical[i] = lower[i] + action[i] * (upper[i] - lower[i])
Arguments:
action- Normalized action array with values in[0, 1], corresponding to[amplitude, speed, wavelength].
Returns:
Physical actuation values for the CFD solver.
step
def step(
action: Optional[np.ndarray] = None
) -> Tuple[np.ndarray, float, bool, bool, Dict]
Advance the environment by one step.
Overrides the base class to skip MAX_CONTROL scaling. The
incoming action is in [0, 1] and is converted to physical
units by :meth:convert_action.
Arguments:
action- Normalized action in[0, 1]for[amplitude, speed, wavelength].
Returns:
Tuple of (observation, reward, terminated, truncated, info).
reset
def reset(seed: Optional[int] = None,
options: Optional[Dict] = None) -> Tuple[np.ndarray, Dict]
Reset the environment with physically valid initial wave parameters.
Overrides the base-class reset to avoid setting zero control actions,
which would crash the CFD solver for the surface-wave boundary
condition. init_action is passed in physical units directly,
bypassing the [0, 1] → physical scaling used during normal steps.
Arguments:
seed- Optional random seed.options- Optional reset options (unused).
Returns:
Tuple of (initial_observation, info).