Source code for pythermalcomfort.utils.scale_wind_speed_log

from __future__ import annotations

import numpy as np

from pythermalcomfort.classes_input import ScaleWindSpeedLogInputs
from pythermalcomfort.classes_return import ScaleWindSpeedLog


[docs] def scale_wind_speed_log( v_z1: float | list[float], z2: float | list[float], z1: float | list[float] = 10.0, z0: float | list[float] = 0.01, d: float | list[float] = 0.0, round_output: bool = True, ) -> ScaleWindSpeedLog: """Scale wind speed from the reference height to a user specified height using the logarithmic wind profile based on surface roughness length [Oke1987]_. The logarithmic wind profile is a semi-empirical relationship that describes how wind speed changes with height above the ground in the surface layer of the atmosphere. This equation assumes neutral atmospheric stability and is valid for heights typically ranging from a few meters to about 100 meters above the ground. It is commonly used in meteorology, wind engineering, and environmental studies to estimate wind speeds at different heights based on a known reference height. The formula is given by: .. math:: v(h) = v_{ref} \\times \\frac{\\ln((h - d)/z0)}{\\ln((h_{ref} - d)/z0)} where: - :math:`v(h)` is the wind speed at height :math:`h` - :math:`v_{ref}` is the wind speed at the reference height :math:`h_{ref}` (commonly 10 m) - :math:`h` is the height at which the wind speed is to be estimated - :math:`h_{ref}` is the reference height (10 m) - :math:`z0` is the surface roughness length, which characterizes the roughness of the terrain - :math:`d` is the zero-plane displacement height, which accounts for flow obstruction by vegetation or buildings .. note:: This function assumes neutral atmospheric stability conditions. For non-neutral conditions, more complex models such as the Monin-Obukhov similarity theory should be used. Moreover, this function does not account for obstacles or complex terrain effects which may require computational fluid dynamics (CFD) simulations or measurements. Parameters ---------- v_z1 : float or list of floats Wind speed at the reference height z1 (default 10 m), [m/s] z2 : float or list of floats Height at which wind speed needs to be scaled, [m] z1 : float or list of floats, optional Reference height, [m]. Default is 10.0 m. z0 : float or list of floats, optional Surface roughness length, [m]. Default is 0.01 (open terrain). This value accounts for the roughness of the terrain and varies based on land use source [Sharples2023]_: - 0.005 m: Inland waterbodies (lakes, dams) - 0.03 m: Irrigated Pasture (e.g., grassland, alfalfa) - 0.1 m: Irrigated cropping (e.g., wheat, soybeans) - 0.25 m: Irrigated Sugar (e.g., sugarcane, corn) - 0.4 m: Towns, villages, agricultural land with many or high hedges, forests and very rough and uneven terrain - 0.6 m: Large towns with tall buildings - 1.0 m: Urban areas - 1.6 m: Large cities with very tall buildings d : float or list of floats, optional Zero-plane displacement height, [m]. Default is 0.0 (no displacement). This value accounts for the height at which the wind speed effectively becomes zero due to obstacles like trees or buildings. Based on [Sharples2023]_: - 0.0 m: Inland waterbodies (lakes, dams) - 0.0 m: Irrigated Pasture (e.g., grassland, alfalfa) - 0.5 m: Irrigated cropping (e.g., wheat, soybeans) - 0.5 m: Irrigated Sugar (e.g., sugarcane, corn) - 0.5 m: Urban areas round_output : bool, optional If True, rounds output value. If False, it does not round it. Defaults to True. Returns ------- ScaleWindSpeedLog Dataclass with attribute ``v_z2`` containing the scaled wind speed(s) at the target height z2, [m/s]. Raises ------ TypeError If input parameters are not of valid numeric types ValueError If any parameter values violate physical constraints Examples -------- .. code-block:: python # Scale wind speed to 2m height (default open terrain z0=0.01) scale_wind_speed_log(v_z1=5.0, z2=2.0) # Scale wind speed to 2m height over rough terrain scale_wind_speed_log(v_z1=5.0, z2=2.0, z0=0.1) # Scale multiple wind speeds to different heights scale_wind_speed_log(v_z1=[3.0, 5.0], z2=[1.5, 2.5]) # Scale with different surface roughness for each measurement scale_wind_speed_log(v_z1=[3.0, 5.0], z2=[1.5, 2.5], z0=[0.02, 0.1]) """ ScaleWindSpeedLogInputs( v_z1=v_z1, z2=z2, z1=z1, z0=z0, d=d, ) v_z1 = np.asarray(v_z1, dtype=float) z2 = np.asarray(z2, dtype=float) z0 = np.asarray(z0, dtype=float) d = np.asarray(d, dtype=float) z1 = np.asarray(z1, dtype=float) # Use numpy.log to support array inputs/broadcasting with np.errstate(divide="raise", invalid="raise", over="raise", under="ignore"): v_z2 = v_z1 * np.log((z2 - d) / z0) / np.log((z1 - d) / z0) if round_output: v_z2 = np.around(v_z2, 2) # Return a dataclass instance for consistent API return ScaleWindSpeedLog(v_z2=v_z2)