Skip to content

Gridded wind

GriddedWind samples the les_fvm C-grid wind (U at x + Δx/2, V at y + Δy/2, W at T-points) at each particle: trilinear in space, piecewise-linear in time, edge value held beyond the walls. Build it from a MetField (gridded_wind_from_met) or a PrescribedWindField (gridded_wind_from_prescribed) and pass it as wind to integrate_particles, simulate_lagrangian or compute_footprint. Tier II.

wind_interp

C-grid-aware gridded wind for particles (implementation in plumax.lagrangian._src.wind_interp).

GriddedWind

Bases: Module

Mean wind on the les_fvm C-grid, trilinear in space, linear in time.

u(p, t) = Σ_corners w_corner u_corner over the 16 samples of u that bracket (t, z, y, x), and likewise for v and w on their own points.

Attributes:

Name Type Description
x Float[Array, ' x']

T-point x-coordinates x_i [m], uniformly spaced.

y Float[Array, ' y']

T-point y-coordinates y_j [m], uniformly spaced.

z Float[Array, ' z']

T-point z-coordinates z_k [m], uniformly spaced.

times Float[Array, ' time']

Sample times [s], strictly increasing.

u Float[Array, 'time z y x']

Eastward wind [m/s] at U-points (x_i + Δx/2).

v Float[Array, 'time z y x']

Northward wind [m/s] at V-points (y_j + Δy/2).

w Float[Array, 'time z y x']

Vertical wind [m/s] at T-points.

Examples:

A uniform wind is reproduced exactly, anywhere and at any time:

>>> import jax.numpy as jnp
>>> axis = jnp.arange(4.0) * 10.0 + 5.0
>>> ones = jnp.ones((2, 4, 4, 4))
>>> wind = GriddedWind(
...     axis, axis, axis, jnp.array([0.0, 60.0]), 3.0 * ones, -ones, 0 * ones
... )
>>> wind(jnp.array([[12.0, 7.0, 31.0], [-50.0, 80.0, 0.0]]), 30.0).tolist()
[[3.0, -1.0, 0.0], [3.0, -1.0, 0.0]]

u is read on U-points, half a cell east of the T-points, so a field linear in x comes back as x itself at the T-points:

>>> u = jnp.broadcast_to(axis + 5.0, (2, 4, 4, 4))
>>> sheared = GriddedWind(axis, axis, axis, jnp.array([0.0, 60.0]), u, ones, ones)
>>> sheared(jnp.array([[15.0, 15.0, 15.0]]), 0.0)[:, 0].tolist()
[15.0]

Piecewise-linear in time between the stored times:

>>> ramp = jnp.stack([ones[0], 3.0 * ones[0]])
>>> gusty = GriddedWind(axis, axis, axis, jnp.array([0.0, 60.0]), ramp, ones, ones)
>>> gusty(jnp.array([[15.0, 15.0, 15.0]]), 15.0)[:, 0].tolist()
[1.5]

from_grid classmethod

from_grid(plume_grid: PlumeGrid3D, times: Float[Array, ' time'], u: Float[Array, 'time z y x'], v: Float[Array, 'time z y x'], w: Float[Array, 'time z y x']) -> GriddedWind

Wind on a les_fvm analysis grid, interior arrays on the C-grid.

Parameters:

Name Type Description Default
plume_grid PlumeGrid3D

The analysis grid; its T-point axes place the samples.

required
times Float[Array, ' time']

Sample times [s], strictly increasing.

required
u Float[Array, 'time z y x']

Eastward wind [m/s] at U-points.

required
v Float[Array, 'time z y x']

Northward wind [m/s] at V-points.

required
w Float[Array, 'time z y x']

Vertical wind [m/s] at T-points.

required

Returns:

Type Description
GriddedWind

The gridded wind.

Source code in src/plumax/lagrangian/_src/wind_interp.py
@classmethod
def from_grid(
    cls,
    plume_grid: PlumeGrid3D,
    times: Float[Array, " time"],
    u: Float[Array, "time z y x"],
    v: Float[Array, "time z y x"],
    w: Float[Array, "time z y x"],
) -> GriddedWind:
    """Wind on a ``les_fvm`` analysis grid, interior arrays on the C-grid.

    Args:
        plume_grid: The analysis grid; its T-point axes place the samples.
        times: Sample times [s], strictly increasing.
        u: Eastward wind [m/s] at U-points.
        v: Northward wind [m/s] at V-points.
        w: Vertical wind [m/s] at T-points.

    Returns:
        The gridded wind.
    """
    return cls(plume_grid.x, plume_grid.y, plume_grid.z, times, u, v, w)

gridded_wind_from_met

gridded_wind_from_met(field: MetField) -> GriddedWind

The wind of a :class:~plumax.met.MetField, for particles.

MetField already stores u, v, w on the C-grid with a time axis, so this is a relabelling; no value changes.

Parameters:

Name Type Description Default
field MetField

Meteorology on an analysis grid.

required

Returns:

Type Description
GriddedWind

The gridded wind on field.plume_grid at field.times.

Source code in src/plumax/lagrangian/_src/wind_interp.py
def gridded_wind_from_met(field: MetField) -> GriddedWind:
    """The wind of a :class:`~plumax.met.MetField`, for particles.

    ``MetField`` already stores ``u``, ``v``, ``w`` on the C-grid with a time
    axis, so this is a relabelling; no value changes.

    Args:
        field: Meteorology on an analysis grid.

    Returns:
        The gridded wind on ``field.plume_grid`` at ``field.times``.
    """
    return GriddedWind.from_grid(
        field.plume_grid, field.times, field.u, field.v, field.w
    )

gridded_wind_from_prescribed

gridded_wind_from_prescribed(wind_field: PrescribedWindField, times: Float[Array, ' time']) -> GriddedWind

Sample a les_fvm :class:~plumax.les_fvm.wind.PrescribedWindField.

The field is evaluated at each of times; between them the particles see the piecewise-linear interpolant, so times should resolve the field's own time variation.

Parameters:

Name Type Description Default
wind_field PrescribedWindField

The Eulerian solver's wind.

required
times Float[Array, ' time']

Times [s] to sample it at, strictly increasing.

required

Returns:

Type Description
GriddedWind

The gridded wind on wind_field.plume_grid.

Source code in src/plumax/lagrangian/_src/wind_interp.py
def gridded_wind_from_prescribed(
    wind_field: PrescribedWindField, times: Float[Array, " time"]
) -> GriddedWind:
    """Sample a ``les_fvm`` :class:`~plumax.les_fvm.wind.PrescribedWindField`.

    The field is evaluated at each of ``times``; between them the particles see
    the piecewise-linear interpolant, so ``times`` should resolve the field's
    own time variation.

    Args:
        wind_field: The Eulerian solver's wind.
        times: Times [s] to sample it at, strictly increasing.

    Returns:
        The gridded wind on ``wind_field.plume_grid``.
    """
    times = jnp.asarray(times)
    samples = [wind_field(t) for t in times]
    # interior points: drop the one-cell ghost ring the solver pads with
    u, v, w = (
        jnp.stack([sample[c][1:-1, 1:-1, 1:-1] for sample in samples]) for c in range(3)
    )
    return GriddedWind.from_grid(wind_field.plume_grid, times, u, v, w)