Module ocean_science_utilities.wavephysics.balance.jb23_wind_input
Expand source code
import numba # type: ignore
import numpy as np
from typing import Optional, TypedDict
from ocean_science_utilities.wavephysics.fluidproperties import (
GRAVITATIONAL_ACCELERATION,
AIR,
WATER,
)
from ocean_science_utilities.wavephysics.balance.jb23_tail_stress import (
tail_stress_parametrization_jb23,
)
from ocean_science_utilities.wavephysics.balance.st4_wind_input import ST4WindInput
from ocean_science_utilities.wavetheory.lineardispersion import (
inverse_intrinsic_dispersion_relation,
)
class JB23WaveGenerationParameters(TypedDict):
gravitational_acceleration: float
charnock_maximum_roughness: float
charnock_constant: float
air_density: float
water_density: float
vonkarman_constant: float
wave_age_tuning_parameter: float
growth_parameter_betamax: float
elevation: float
air_viscosity: float
viscous_stress_parameter: float
surface_tension: float
width_factor: float
non_linear_effect_strength: float
class JB23WindInput(ST4WindInput):
name = "JB23 generation"
def __init__(self, parameters: Optional[JB23WaveGenerationParameters] = None):
super(JB23WindInput, self).__init__(parameters)
# The obly difference with ST4 as implemented is the calculation
# of the tail contributions.
self._wind_source_term_function = _jb23_wind_generation_point
self._tail_stress_parametrization_function = tail_stress_parametrization_jb23
@staticmethod
def default_parameters() -> JB23WaveGenerationParameters:
return JB23WaveGenerationParameters(
wave_age_tuning_parameter=0.008,
growth_parameter_betamax=2,
gravitational_acceleration=GRAVITATIONAL_ACCELERATION,
charnock_maximum_roughness=np.inf,
charnock_constant=0.005,
air_density=AIR.density,
water_density=WATER.density,
vonkarman_constant=AIR.vonkarman_constant,
elevation=10,
air_viscosity=AIR.kinematic_viscosity,
viscous_stress_parameter=1.0 / 25.0,
surface_tension=WATER.kinematic_surface_tension,
width_factor=0.6,
non_linear_effect_strength=1.0,
)
# ----------------------------------------------------------------------------------------------------------------------
# ST4 Point wise implementation functions (apply to a single spatial point)
# ----------------------------------------------------------------------------------------------------------------------
@numba.njit(cache=True, fastmath=True)
def _jb23_wind_generation_point(
variance_density,
wind,
depth,
roughness_length,
spectral_grid,
parameters,
wind_source=None,
):
"""
Implememtation of the st4 wind growth rate term
:param variance_density:
:param wind:
:param depth:
:param roughness_length:
:param spectral_grid:
:param parameters:
:return:
"""
# Get the spectral size
number_of_frequencies, number_of_directions = variance_density.shape
# Allocate the output variable
if wind_source is None:
wind_source = np.empty((number_of_frequencies, number_of_directions))
# Unpack variables passed in dictionaries/tuples for ease of reference and
# some slight speed benifits (avoid dictionary lookup in loops).
vonkarman_constant = parameters["vonkarman_constant"]
growth_parameter_betamax = parameters["growth_parameter_betamax"]
air_density = parameters["air_density"]
water_density = parameters["water_density"]
gravitational_acceleration = parameters["gravitational_acceleration"]
radian_frequency = spectral_grid["radian_frequency"]
radian_direction = spectral_grid["radian_direction"]
direction_step = spectral_grid["direction_step"]
elevation = parameters["elevation"]
non_linear_effect_strength = parameters["non_linear_effect_strength"]
wind_forcing, wind_direction_degrees, wind_forcing_type = wind
# Preprocess winds to the correct conventions (U10->friction velocity if need be,
# wind direction degrees->radians)
if wind_forcing_type == "u10":
friction_velocity = (
wind_forcing * vonkarman_constant / np.log(elevation / roughness_length)
)
elif wind_forcing_type in ["ustar", "friction_velocity"]:
friction_velocity = wind_forcing
else:
raise ValueError("Unknown wind input type")
wind_direction_radian = wind_direction_degrees * np.pi / 180
# precalculate the constant multiplication.
constant_factor = (
growth_parameter_betamax / vonkarman_constant**2 * air_density / water_density
)
if depth == np.inf:
wavenumber = radian_frequency**2 / gravitational_acceleration
else:
wavenumber = inverse_intrinsic_dispersion_relation(radian_frequency, depth)
mutual_angle = (radian_direction - wind_direction_radian + np.pi) % (
2 * np.pi
) - np.pi
cosine = np.cos(mutual_angle)
sine2 = np.sin(mutual_angle) ** 2
# Loop over all frequencies/directions
epsilon = air_density / water_density
cosine_wave_age_tuning = parameters["wave_age_tuning_parameter"] * cosine
for frequency_index in range(number_of_frequencies):
# Since wavenumber and wavespeed only depend on frequency we calculate those
# outside of the direction loop.
wavespeed = radian_frequency[frequency_index] / wavenumber[frequency_index]
group_speed = wavespeed / 2
relative_speed = friction_velocity / wavespeed
N1 = 0.0
N2 = 0.0
for direction_index in range(number_of_directions):
# If the wave direction has an along wind component we continue.
if cosine[direction_index] > 0:
# Calculate the directional factor in the wind input formulation
W = relative_speed * cosine[direction_index]
effective_wave_age = np.log(
wavenumber[frequency_index] * roughness_length
) + vonkarman_constant / (W + cosine_wave_age_tuning[direction_index])
if effective_wave_age > 0:
effective_wave_age = 0
growth_rate = (
constant_factor
* np.exp(effective_wave_age)
* effective_wave_age**4
* W**2
* radian_frequency[frequency_index]
)
# Note that Peter does not include the k factor into the defenition of
# his F(k,\theta). So we need an additional division by k (in addition
# to the jacobian cg) to make this work- hence the wavenumber **2 !!
common_factor = (
non_linear_effect_strength
* variance_density[frequency_index, direction_index]
* group_speed
* direction_step[direction_index]
* wavenumber[frequency_index] ** 2
* growth_rate
/ vonkarman_constant
/ friction_velocity
/ epsilon
/ 2
/ np.pi
)
N1 += common_factor * sine2[direction_index]
N2 += common_factor
wind_source[frequency_index, direction_index] = (
growth_rate * variance_density[frequency_index, direction_index]
)
else:
wind_source[frequency_index, direction_index] = 0.0
#
for direction_index in range(number_of_directions):
wind_source[frequency_index, direction_index] = (
wind_source[frequency_index, direction_index] * (1 + N1) / (1 + N2)
)
return wind_source
Classes
class JB23WaveGenerationParameters (*args, **kwargs)
-
dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)
Expand source code
class JB23WaveGenerationParameters(TypedDict): gravitational_acceleration: float charnock_maximum_roughness: float charnock_constant: float air_density: float water_density: float vonkarman_constant: float wave_age_tuning_parameter: float growth_parameter_betamax: float elevation: float air_viscosity: float viscous_stress_parameter: float surface_tension: float width_factor: float non_linear_effect_strength: float
Ancestors
- builtins.dict
Class variables
var air_density : float
var air_viscosity : float
var charnock_constant : float
var charnock_maximum_roughness : float
var elevation : float
var gravitational_acceleration : float
var growth_parameter_betamax : float
var non_linear_effect_strength : float
var surface_tension : float
var viscous_stress_parameter : float
var vonkarman_constant : float
var water_density : float
var wave_age_tuning_parameter : float
var width_factor : float
class JB23WindInput (parameters: Optional[JB23WaveGenerationParameters] = None)
-
Helper class that provides a standard way to create an ABC using inheritance.
Expand source code
class JB23WindInput(ST4WindInput): name = "JB23 generation" def __init__(self, parameters: Optional[JB23WaveGenerationParameters] = None): super(JB23WindInput, self).__init__(parameters) # The obly difference with ST4 as implemented is the calculation # of the tail contributions. self._wind_source_term_function = _jb23_wind_generation_point self._tail_stress_parametrization_function = tail_stress_parametrization_jb23 @staticmethod def default_parameters() -> JB23WaveGenerationParameters: return JB23WaveGenerationParameters( wave_age_tuning_parameter=0.008, growth_parameter_betamax=2, gravitational_acceleration=GRAVITATIONAL_ACCELERATION, charnock_maximum_roughness=np.inf, charnock_constant=0.005, air_density=AIR.density, water_density=WATER.density, vonkarman_constant=AIR.vonkarman_constant, elevation=10, air_viscosity=AIR.kinematic_viscosity, viscous_stress_parameter=1.0 / 25.0, surface_tension=WATER.kinematic_surface_tension, width_factor=0.6, non_linear_effect_strength=1.0, )
Ancestors
- ST4WindInput
- WindGeneration
- SourceTerm
- abc.ABC
Class variables
var name
Static methods
def default_parameters() ‑> JB23WaveGenerationParameters
-
Expand source code
@staticmethod def default_parameters() -> JB23WaveGenerationParameters: return JB23WaveGenerationParameters( wave_age_tuning_parameter=0.008, growth_parameter_betamax=2, gravitational_acceleration=GRAVITATIONAL_ACCELERATION, charnock_maximum_roughness=np.inf, charnock_constant=0.005, air_density=AIR.density, water_density=WATER.density, vonkarman_constant=AIR.vonkarman_constant, elevation=10, air_viscosity=AIR.kinematic_viscosity, viscous_stress_parameter=1.0 / 25.0, surface_tension=WATER.kinematic_surface_tension, width_factor=0.6, non_linear_effect_strength=1.0, )