Skip to content

Turbulence Model

Turbulence Model module.

The class for the Turbulence models used in pyELQ.

TurbulenceModel dataclass

Bases: ABC

Abstract class to define specific turbulence model behaviour.

Source code in src/pyelq/dispersion_model/turbulence_model.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@dataclass
class TurbulenceModel(ABC):
    """Abstract class to define specific turbulence model behaviour."""

    @abstractmethod
    def calculate(
        self, turbulence_vector: np.ndarray, source_z: np.ndarray, wind_speed: np.ndarray, distance_x: np.ndarray
    ) -> np.ndarray:
        """Abstract method to define behaviour for turbulence calculation.

        All inputs are expected to have the same shape.

        Args:
            turbulence_vector (np.ndarray): wind turbulence parameter [unit depends on model].
            source_z (np.ndarray): height of source relative to ground [m].
            wind_speed (np.ndarray): wind speed at sensor location [m/s].
            distance_x (np.ndarray): distance along plume axis from source to sensor location [m].

        Returns:
            np.ndarray: stability of the plume according to the specific model in use.

        """

calculate(turbulence_vector, source_z, wind_speed, distance_x) abstractmethod

Abstract method to define behaviour for turbulence calculation.

All inputs are expected to have the same shape.

Parameters:

Name Type Description Default
turbulence_vector ndarray

wind turbulence parameter [unit depends on model].

required
source_z ndarray

height of source relative to ground [m].

required
wind_speed ndarray

wind speed at sensor location [m/s].

required
distance_x ndarray

distance along plume axis from source to sensor location [m].

required

Returns:

Type Description
ndarray

np.ndarray: stability of the plume according to the specific model in use.

Source code in src/pyelq/dispersion_model/turbulence_model.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@abstractmethod
def calculate(
    self, turbulence_vector: np.ndarray, source_z: np.ndarray, wind_speed: np.ndarray, distance_x: np.ndarray
) -> np.ndarray:
    """Abstract method to define behaviour for turbulence calculation.

    All inputs are expected to have the same shape.

    Args:
        turbulence_vector (np.ndarray): wind turbulence parameter [unit depends on model].
        source_z (np.ndarray): height of source relative to ground [m].
        wind_speed (np.ndarray): wind speed at sensor location [m/s].
        distance_x (np.ndarray): distance along plume axis from source to sensor location [m].

    Returns:
        np.ndarray: stability of the plume according to the specific model in use.

    """

AngularModel dataclass

Bases: TurbulenceModel

Basic turbulence model.

Source code in src/pyelq/dispersion_model/turbulence_model.py
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
@dataclass
class AngularModel(TurbulenceModel):
    """Basic turbulence model."""

    def calculate(
        self, turbulence_vector: np.ndarray, source_z: np.ndarray, wind_speed: np.ndarray, distance_x: np.ndarray
    ):
        """Calculate the effective wind component perpendicular to the wind direction at the source location.

        Expects that 'turbulence_vector' is in degrees.

        Args:
            turbulence_vector (np.ndarray): wind turbulence parameter [degrees].
            source_z (np.ndarray): height of source relative to ground [m].
            wind_speed (np.ndarray): wind speed at sensor location [m/s].
            distance_x (np.ndarray): distance along plume axis from source to sensor location [m].

        Returns:
            np.ndarray: calculated plume spread values.

        """
        return np.tan(turbulence_vector * (np.pi / 180)) * np.abs(distance_x)

calculate(turbulence_vector, source_z, wind_speed, distance_x)

Calculate the effective wind component perpendicular to the wind direction at the source location.

Expects that 'turbulence_vector' is in degrees.

Parameters:

Name Type Description Default
turbulence_vector ndarray

wind turbulence parameter [degrees].

required
source_z ndarray

height of source relative to ground [m].

required
wind_speed ndarray

wind speed at sensor location [m/s].

required
distance_x ndarray

distance along plume axis from source to sensor location [m].

required

Returns:

Type Description

np.ndarray: calculated plume spread values.

Source code in src/pyelq/dispersion_model/turbulence_model.py
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
def calculate(
    self, turbulence_vector: np.ndarray, source_z: np.ndarray, wind_speed: np.ndarray, distance_x: np.ndarray
):
    """Calculate the effective wind component perpendicular to the wind direction at the source location.

    Expects that 'turbulence_vector' is in degrees.

    Args:
        turbulence_vector (np.ndarray): wind turbulence parameter [degrees].
        source_z (np.ndarray): height of source relative to ground [m].
        wind_speed (np.ndarray): wind speed at sensor location [m/s].
        distance_x (np.ndarray): distance along plume axis from source to sensor location [m].

    Returns:
        np.ndarray: calculated plume spread values.

    """
    return np.tan(turbulence_vector * (np.pi / 180)) * np.abs(distance_x)

DraxlerModel dataclass

Bases: TurbulenceModel

Draxler Turbulence Model.

This turbulence model attempts to characterise the dispersion of the plume based on the travel time (T) of the gas from source location to sensor.

The calculation and default parameters used in this class are derived from 'Determination of Atmospheric Diffusion Parameters, R. R. Draxler, DOI: 10.1016/0004-6981(76)90226-2'.

The calculation relies on the use of universal functions that are used to characterise the turbulence in the horizontal and vertical directions under particular conditions of atmospheric stability and source heights. The functions take the form:

f = 1 / (1 + scale * (T/t_i) ** exp)

Taking T as input, and as parameters: scale: an increase in scale reduces the effective dispersion along the entire plume, constraining plume growth with distance. exp: an increase in this power creates a more abrupt transition point from greater to lesser dispersion. t_i: the time scale at which the transition from greater to lower dispersion is expected to occur.

This class differentiates between the stability parameters for ground and elevated sources, and provides defaults for both of these in both the horizontal and vertical planes. The scale and exp values are drawn from Equations 2.7 and 2.9 in the Draxler paper and the t_i values from Table 3.

Attributes:

Name Type Description
scale_ground float

the scale parameter as described above for a ground source.

exp_ground float

the exp parameter as described above for a ground source.

t_i_ground float

the t_i parameter as described above for a ground source.

scale_elevated float

the scale parameter as described above for an elevated source.

exp_elevated float

the exp parameter as described above for an elevated source.

t_i_elevated float

the t_i parameter as described above for an elevated source.

ground_threshold float

maximum height above ground at which source can be considered 'ground'.

Source code in src/pyelq/dispersion_model/turbulence_model.py
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
@dataclass
class DraxlerModel(TurbulenceModel):
    """Draxler Turbulence Model.

    This turbulence model attempts to characterise the dispersion of the plume based on the travel
    time (T) of the gas from source location to sensor.

    The calculation and default parameters used in this class are derived from 'Determination of Atmospheric
    Diffusion Parameters, R. R. Draxler, DOI: 10.1016/0004-6981(76)90226-2'.

    The calculation relies on the use of universal functions that are used to characterise the turbulence
    in the horizontal and vertical directions under particular conditions of atmospheric stability and source
    heights. The functions take the form:

        f = 1 / (1 + scale * (T/t_i) ** exp)

    Taking T as input, and as parameters:
        scale: an increase in scale reduces the effective dispersion along the entire plume, constraining
            plume growth with distance.
        exp: an increase in this power creates a more abrupt transition point from greater to lesser dispersion.
        t_i: the time scale at which the transition from greater to lower dispersion is expected to occur.

    This class differentiates between the stability parameters for ground and elevated sources, and provides
    defaults for both of these in both the horizontal and vertical planes. The scale and exp values are drawn
    from Equations 2.7 and 2.9 in the Draxler paper and the t_i values from Table 3.

    Attributes:
        scale_ground (float): the scale parameter as described above for a ground source.
        exp_ground (float): the exp parameter as described above for a ground source.
        t_i_ground (float): the t_i parameter as described above for a ground source.
        scale_elevated (float): the scale parameter as described above for an elevated source.
        exp_elevated (float): the exp parameter as described above for an elevated source.
        t_i_elevated (float): the t_i parameter as described above for an elevated source.
        ground_threshold (float): maximum height above ground at which source can be considered 'ground'.

    """

    scale_ground: float
    scale_elevated: float
    exp_ground: float
    exp_elevated: float
    t_i_ground: float
    t_i_elevated: float
    ground_threshold: float = 0.5

    def calculate(
        self, turbulence_vector: np.ndarray, source_z: np.ndarray, wind_speed: np.ndarray, distance_x: np.ndarray
    ) -> np.ndarray:
        """Draxler calculation for atmospheric diffusion. See DOI above for details.

        Expects that 'turbulence_vector' is in m/s.

        Args:
            turbulence_vector (np.ndarray): wind turbulence parameter [m/s].
            source_z (np.ndarray): height of source relative to ground [m].
            wind_speed (np.ndarray): wind speed at sensor location [m/s].
            distance_x (np.ndarray): distance along plume axis from source to sensor location [m].

        Returns:
            np.ndarray: calculated plume spread values.

        """
        positive_part_x = np.maximum(distance_x, 0.0)
        diffusion_time = positive_part_x / wind_speed

        is_ground_source: np.ndarray[bool] = source_z <= self.ground_threshold
        scale = np.where(is_ground_source, self.scale_ground, self.scale_elevated)
        power = np.where(is_ground_source, self.exp_ground, self.exp_elevated)
        t_i = np.where(is_ground_source, self.t_i_ground, self.t_i_elevated)

        f = 1.0 / (1.0 + scale * ((diffusion_time / t_i) ** power))

        return (turbulence_vector / wind_speed) * positive_part_x * f

    @staticmethod
    def default_horizontal(**kwargs):
        """Construct a DraxlerModel object for horizontal turbulence, overriding defaults where provided."""
        DEFAULT_DRAXLER_HORIZONTAL = {
            "scale_ground": 0.9,
            "scale_elevated": 0.9,
            "exp_ground": 0.5,
            "exp_elevated": 0.5,
            "t_i_ground": 300.0,
            "t_i_elevated": 1000.0,
        }
        params = DEFAULT_DRAXLER_HORIZONTAL | kwargs
        return DraxlerModel(**params)

    @staticmethod
    def default_vertical(**kwargs):
        """Construct a DraxlerModel object for vertical turbulence, overriding defaults where provided."""
        DEFAULT_DRAXLER_VERTICAL = {
            "scale_ground": 0.9,
            "scale_elevated": 0.945,
            "exp_ground": 0.5,
            "exp_elevated": 0.806,
            "t_i_ground": 50.0,
            "t_i_elevated": 100.0,
        }
        params = DEFAULT_DRAXLER_VERTICAL | kwargs
        return DraxlerModel(**params)

calculate(turbulence_vector, source_z, wind_speed, distance_x)

Draxler calculation for atmospheric diffusion. See DOI above for details.

Expects that 'turbulence_vector' is in m/s.

Parameters:

Name Type Description Default
turbulence_vector ndarray

wind turbulence parameter [m/s].

required
source_z ndarray

height of source relative to ground [m].

required
wind_speed ndarray

wind speed at sensor location [m/s].

required
distance_x ndarray

distance along plume axis from source to sensor location [m].

required

Returns:

Type Description
ndarray

np.ndarray: calculated plume spread values.

Source code in src/pyelq/dispersion_model/turbulence_model.py
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
def calculate(
    self, turbulence_vector: np.ndarray, source_z: np.ndarray, wind_speed: np.ndarray, distance_x: np.ndarray
) -> np.ndarray:
    """Draxler calculation for atmospheric diffusion. See DOI above for details.

    Expects that 'turbulence_vector' is in m/s.

    Args:
        turbulence_vector (np.ndarray): wind turbulence parameter [m/s].
        source_z (np.ndarray): height of source relative to ground [m].
        wind_speed (np.ndarray): wind speed at sensor location [m/s].
        distance_x (np.ndarray): distance along plume axis from source to sensor location [m].

    Returns:
        np.ndarray: calculated plume spread values.

    """
    positive_part_x = np.maximum(distance_x, 0.0)
    diffusion_time = positive_part_x / wind_speed

    is_ground_source: np.ndarray[bool] = source_z <= self.ground_threshold
    scale = np.where(is_ground_source, self.scale_ground, self.scale_elevated)
    power = np.where(is_ground_source, self.exp_ground, self.exp_elevated)
    t_i = np.where(is_ground_source, self.t_i_ground, self.t_i_elevated)

    f = 1.0 / (1.0 + scale * ((diffusion_time / t_i) ** power))

    return (turbulence_vector / wind_speed) * positive_part_x * f

default_horizontal(**kwargs) staticmethod

Construct a DraxlerModel object for horizontal turbulence, overriding defaults where provided.

Source code in src/pyelq/dispersion_model/turbulence_model.py
139
140
141
142
143
144
145
146
147
148
149
150
151
@staticmethod
def default_horizontal(**kwargs):
    """Construct a DraxlerModel object for horizontal turbulence, overriding defaults where provided."""
    DEFAULT_DRAXLER_HORIZONTAL = {
        "scale_ground": 0.9,
        "scale_elevated": 0.9,
        "exp_ground": 0.5,
        "exp_elevated": 0.5,
        "t_i_ground": 300.0,
        "t_i_elevated": 1000.0,
    }
    params = DEFAULT_DRAXLER_HORIZONTAL | kwargs
    return DraxlerModel(**params)

default_vertical(**kwargs) staticmethod

Construct a DraxlerModel object for vertical turbulence, overriding defaults where provided.

Source code in src/pyelq/dispersion_model/turbulence_model.py
153
154
155
156
157
158
159
160
161
162
163
164
165
@staticmethod
def default_vertical(**kwargs):
    """Construct a DraxlerModel object for vertical turbulence, overriding defaults where provided."""
    DEFAULT_DRAXLER_VERTICAL = {
        "scale_ground": 0.9,
        "scale_elevated": 0.945,
        "exp_ground": 0.5,
        "exp_elevated": 0.806,
        "t_i_ground": 50.0,
        "t_i_elevated": 100.0,
    }
    params = DEFAULT_DRAXLER_VERTICAL | kwargs
    return DraxlerModel(**params)