/// MoonMet - MoonBit 气象计算库
/// 基于 MetPy 实现的主要气象计算功能
/// 提供热力学、动力学和基础气象计算函数
// ============================================================================
// 1. 湿空气属性函数
// ============================================================================
///|
/// 计算湿空气的比气体常数 R_m
/// 公式: R_m = (1 - q) R_d + q R_v
/// q: 比湿 (kg/kg)
pub fn moist_air_gas_constant(specific_humidity : Double) -> Double {
dry_air_gas_constant +
specific_humidity * (water_gas_constant - dry_air_gas_constant)
}
///|
/// 计算湿空气的定压比热 C_pm
/// 公式: C_pm = (1 - q) C_pd + q C_pv
/// q: 比湿 (kg/kg)
pub fn moist_air_specific_heat_pressure(specific_humidity : Double) -> Double {
dry_air_spec_heat_press +
specific_humidity * (wv_specific_heat_press - dry_air_spec_heat_press)
}
///|
/// 计算湿空气的 Poisson 指数 κ_m
/// 公式: κ_m = R_m / C_pm
pub fn moist_air_poisson_exponent(specific_humidity : Double) -> Double {
moist_air_gas_constant(specific_humidity) /
moist_air_specific_heat_pressure(specific_humidity)
}
///|
/// 计算水的汽化潜热
/// 公式: L = L_0 - (C_pl - C_pv) * (T - T_0)
/// T: 温度 (K)
pub fn latent_heat_vaporization(temperature : Double) -> Double {
water_heat_vaporization -
(water_specific_heat - wv_specific_heat_press) *
(temperature - water_triple_point_temperature)
}
///|
/// 计算水的升华潜热
/// 公式: L_s = L_v + L_f
pub fn latent_heat_sublimation(temperature : Double) -> Double {
latent_heat_vaporization(temperature) + water_heat_fusion
}
///|
/// 计算水的融化潜热
pub fn latent_heat_melting(_temperature : Double) -> Double {
water_heat_fusion
}
// ============================================================================
// 2. 饱和水汽压函数
// ============================================================================
///|
/// 计算液态水的饱和水汽压(内部函数)
/// 公式: e = e_{s0} * (T_0/T)^((C_pl - C_pv)/R_v) * exp((L_0/(R_v*T_0) - L/(R_v*T)))
fn _saturation_vapor_pressure_liquid(temperature : Double) -> Double {
let latent_heat = latent_heat_vaporization(temperature)
let heat_power = (water_specific_heat - wv_specific_heat_press) /
water_gas_constant
let exp_term = water_heat_vaporization /
(water_gas_constant * water_triple_point_temperature) -
latent_heat / (water_gas_constant * temperature)
sat_pressure_0c *
Double::pow(water_triple_point_temperature / temperature, heat_power) *
@math.exp(exp_term)
}
///|
/// 计算冰的饱和水汽压(内部函数)
/// 公式: e_i = e_{i0} * (T_0/T)^((C_pi - C_pv)/R_v) * exp((L_s0/(R_v*T_0) - L_s/(R_v*T)))
fn _saturation_vapor_pressure_solid(temperature : Double) -> Double {
let latent_heat = latent_heat_sublimation(temperature)
let ls0 = water_heat_vaporization + water_heat_fusion
let heat_power = (ice_specific_heat - wv_specific_heat_press) /
water_gas_constant
let exp_term = ls0 / (water_gas_constant * water_triple_point_temperature) -
latent_heat / (water_gas_constant * temperature)
sat_pressure_0c *
Double::pow(water_triple_point_temperature / temperature, heat_power) *
@math.exp(exp_term)
}
///|
/// 计算饱和水汽压
/// temperature: 温度 (K)
/// phase: "liquid" (默认), "solid", 或 "auto"
pub fn saturation_vapor_pressure(
temperature : Double,
phase : String,
) -> Double {
match phase {
"liquid" => _saturation_vapor_pressure_liquid(temperature)
"solid" => _saturation_vapor_pressure_solid(temperature)
"auto" =>
if temperature > water_triple_point_temperature {
_saturation_vapor_pressure_liquid(temperature)
} else {
_saturation_vapor_pressure_solid(temperature)
}
_ => _saturation_vapor_pressure_liquid(temperature) // 默认使用液态
}
}
// ============================================================================
// 3. Exner 函数和位温
// ============================================================================
///|
/// 计算 Exner 函数
/// 公式: Π = (p/p_0)^κ
/// pressure: 气压 (Pa)
/// reference_pressure: 参考气压 (Pa),默认 100000 Pa
pub fn exner_function(
pressure : Double,
reference_pressure? : Double,
) -> Double {
let ref_p = match reference_pressure {
Some(p) => p
None => pot_temp_ref_press
}
Double::pow(pressure / ref_p, poisson_exponent)
}
///|
/// 计算位温
/// 公式: θ = T * (p_0/p)^κ
/// pressure: 气压 (Pa)
/// temperature: 温度 (K)
pub fn potential_temperature(pressure : Double, temperature : Double) -> Double {
let ref_p : Double = pot_temp_ref_press
temperature / exner_function(pressure, reference_pressure=ref_p)
}
///|
/// 从位温计算温度
/// 公式: T = θ * (p/p_0)^κ
pub fn temperature_from_potential_temperature(
pressure : Double,
potential_temperature : Double,
) -> Double {
let ref_p : Double = pot_temp_ref_press
potential_temperature * exner_function(pressure, reference_pressure=ref_p)
}
// ============================================================================
// 4. 混合比和比湿
// ============================================================================
///|
/// 计算混合比
/// 公式: r = ε * e / (p - e)
/// partial_press: 水汽分压 (Pa)
/// total_press: 总气压 (Pa)
/// molecular_weight_ratio: 分子量比,默认使用 ε (水汽/干空气)
pub fn mixing_ratio(
partial_press : Double,
total_press : Double,
eps_param? : Double,
) -> Double {
let eps = match eps_param {
Some(e) => e
None => molecular_weight_ratio
}
eps * partial_press / (total_press - partial_press)
}
///|
/// 计算饱和混合比
/// 公式: r_s = ε * e_s / (p - e_s)
pub fn saturation_mixing_ratio(
total_press : Double,
temperature : Double,
phase : String,
) -> Double {
let es = saturation_vapor_pressure(temperature, phase)
let eps_val : Double = molecular_weight_ratio
mixing_ratio(es, total_press, eps_param=eps_val)
}
///|
/// 从比湿计算混合比
/// 公式: w = q / (1 - q)
pub fn mixing_ratio_from_specific_humidity(
specific_humidity : Double,
) -> Double {
specific_humidity / (1.0 - specific_humidity)
}
///|
/// 从混合比计算比湿
/// 公式: q = w / (1 + w)
pub fn specific_humidity_from_mixing_ratio(mixing_ratio : Double) -> Double {
mixing_ratio / (1.0 + mixing_ratio)
}
///|
/// 从露点和气压计算比湿
/// pressure: 气压 (Pa)
/// dewpoint: 露点温度 (K)
/// phase: "liquid" (默认), "solid", 或 "auto"
pub fn specific_humidity_from_dewpoint(
pressure : Double,
dewpoint : Double,
phase : String,
) -> Double {
let mixing_ratio_val = saturation_mixing_ratio(pressure, dewpoint, phase)
specific_humidity_from_mixing_ratio(mixing_ratio_val)
}
///|
/// 计算水汽压
/// 公式: e = p * r / (r + ε)
pub fn vapor_pressure(pressure : Double, mixing_ratio_val : Double) -> Double {
pressure * mixing_ratio_val / (molecular_weight_ratio + mixing_ratio_val)
}
// ============================================================================
// 5. 露点温度
// ============================================================================
///|
/// 从水汽压计算露点温度
/// 使用 Bolton (1980) 公式的逆函数
/// vapor_pressure: 水汽压 (Pa)
/// 返回: 露点温度 (K)
pub fn dewpoint(vapor_pressure : Double) -> Double {
// Bolton 公式返回摄氏度,需要加上 273.15 转换为开尔文
// 公式: T(°C) = 243.5 * ln(e / 6.112) / (17.67 - ln(e / 6.112))
// 其中 e 是水汽压(毫巴),6.112 是 0°C 的饱和水汽压(毫巴)
let e_mbar = vapor_pressure / 100.0 // 转换为毫巴
let e0_mbar = sat_pressure_0c / 100.0 // 0°C 的饱和水汽压(毫巴)
let val = @math.ln(e_mbar / e0_mbar)
let td_celsius = 243.5 * val / (17.67 - val)
273.15 + td_celsius // 转换为开尔文
}
///|
/// 从相对湿度计算露点温度
pub fn dewpoint_from_relative_humidity(
temperature : Double,
relative_humidity : Double,
_phase : String,
) -> Double {
let es = saturation_vapor_pressure(temperature, "liquid")
let e = es * relative_humidity
dewpoint(e)
}
///|
/// 从比湿和气压计算露点温度
pub fn dewpoint_from_specific_humidity(
pressure : Double,
specific_humidity : Double,
) -> Double {
let mixing_ratio_val = mixing_ratio_from_specific_humidity(specific_humidity)
let e = vapor_pressure(pressure, mixing_ratio_val)
dewpoint(e)
}
// ============================================================================
// 6. 相对湿度
// ============================================================================
///|
/// 从混合比计算相对湿度
pub fn relative_humidity_from_mixing_ratio(
pressure : Double,
temperature : Double,
mixing_ratio_val : Double,
phase : String,
) -> Double {
let e = vapor_pressure(pressure, mixing_ratio_val)
let es = saturation_vapor_pressure(temperature, phase)
e / es
}
///|
/// 从比湿计算相对湿度
pub fn relative_humidity_from_specific_humidity(
pressure : Double,
temperature : Double,
specific_humidity : Double,
phase : String,
) -> Double {
let mixing_ratio_val = mixing_ratio_from_specific_humidity(specific_humidity)
relative_humidity_from_mixing_ratio(
pressure, temperature, mixing_ratio_val, phase,
)
}
///|
/// 从露点计算相对湿度
pub fn relative_humidity_from_dewpoint(
temperature : Double,
dewpoint : Double,
phase : String,
) -> Double {
let e = saturation_vapor_pressure(dewpoint, phase)
let es = saturation_vapor_pressure(temperature, phase)
e / es
}
///|
/// 从相对湿度计算混合比
pub fn mixing_ratio_from_relative_humidity(
pressure : Double,
temperature : Double,
relative_humidity : Double,
phase : String,
) -> Double {
let es = saturation_vapor_pressure(temperature, phase)
let e = es * relative_humidity
let eps_val : Double = molecular_weight_ratio
mixing_ratio(e, pressure, eps_param=eps_val)
}
// ============================================================================
// 7. 虚温和密度
// ============================================================================
///|
/// 计算虚温
/// 公式: T_v = T * (1 + r/ε) / (1 + r)
/// temperature: 温度 (K)
/// mixing_ratio_val: 混合比
pub fn virtual_temperature(
temperature : Double,
mixing_ratio_val : Double,
) -> Double {
temperature *
(1.0 + mixing_ratio_val / molecular_weight_ratio) /
(1.0 + mixing_ratio_val)
}
///|
/// 从露点计算虚温
pub fn virtual_temperature_from_dewpoint(
pressure : Double,
temperature : Double,
dewpoint : Double,
) -> Double {
let mixing_ratio_val = saturation_mixing_ratio(pressure, dewpoint, "liquid")
virtual_temperature(temperature, mixing_ratio_val)
}
///|
/// 计算虚位温
pub fn virtual_potential_temperature(
pressure : Double,
temperature : Double,
mixing_ratio_val : Double,
) -> Double {
let tv = virtual_temperature(temperature, mixing_ratio_val)
potential_temperature(pressure, tv)
}
///|
/// 计算空气密度
/// 公式: ρ = p / (R_d * T_v)
/// pressure: 气压 (Pa)
/// temperature: 温度 (K)
/// mixing_ratio_val: 混合比,默认 0(干空气)
pub fn density(
pressure : Double,
temperature : Double,
mixing_ratio_val? : Double,
) -> Double {
let mr = match mixing_ratio_val {
Some(m) => m
None => 0.0
}
let tv = virtual_temperature(temperature, mr)
pressure / (dry_air_gas_constant * tv)
}
// ============================================================================
// 8. 相当位温和湿球温度
// ============================================================================
///|
/// 计算相当位温
/// 使用 Bolton (1980) 简化公式
/// pressure: 气压 (Pa)
/// temperature: 温度 (K)
/// dewpoint: 露点温度 (K)
pub fn equivalent_potential_temperature(
pressure : Double,
temperature : Double,
dewpoint : Double,
) -> Double {
let theta = potential_temperature(pressure, temperature)
let mixing_ratio_val = saturation_mixing_ratio(pressure, dewpoint, "liquid")
// Bolton (1980) 公式
let tl = temperature - 55.0 // 简化处理
let theta_e = theta *
@math.exp(
(3036.0 / tl - 1.78) * mixing_ratio_val * (1.0 + 0.448 * mixing_ratio_val),
)
theta_e
}
///|
/// 计算饱和相当位温
pub fn saturation_equivalent_potential_temperature(
pressure : Double,
temperature : Double,
) -> Double {
equivalent_potential_temperature(pressure, temperature, temperature)
}
///|
/// 计算湿球温度(简化迭代方法)
/// pressure_pa: 气压 (Pa)
/// temperature_k: 温度 (K)
/// dewpoint_k: 露点温度 (K)
pub fn wet_bulb_temperature(
pressure_pa : Double,
temperature_k : Double,
dewpoint_k : Double,
) -> Double {
// 边界检查:如果温度低于露点,返回露点
if temperature_k <= dewpoint_k {
return dewpoint_k
}
// 初始猜测:湿球温度介于干球温度和露点温度之间
let mut tw = (temperature_k + dewpoint_k) / 2.0
// 确保初始值在合理范围内
if tw < dewpoint_k {
tw = dewpoint_k + 0.1
}
if tw > temperature_k {
tw = temperature_k - 0.1
}
// 计算当前比湿(在循环外计算一次即可)
let q = specific_humidity_from_dewpoint(pressure_pa, dewpoint_k, "liquid")
// 迭代求解(最多 100 次)
for _iter = 0; _iter < 100; _iter = _iter + 1 {
// 计算湿球温度对应的比湿(应该是饱和的)
let qs_w = specific_humidity_from_dewpoint(pressure_pa, tw, "liquid")
// 检查是否出现 NaN 或无效值
if qs_w != qs_w || qs_w <= 0.0 {
// 如果计算失败,返回保守估计
return (temperature_k + dewpoint_k) / 2.0
}
// 使用简化的湿球温度公式(基于能量平衡)
// Tw = T - (Lv / cp) * (q - qs(Tw)) / (1 + q)
let lv = latent_heat_vaporization(tw) // J/kg
let cp = moist_air_specific_heat_pressure(q) // J/(K*kg)
// 检查 cp 是否为零或无效
if cp <= 0.0 || cp != cp {
return (temperature_k + dewpoint_k) / 2.0
}
// 计算新的湿球温度
let q_diff = q - qs_w
let denominator = 1.0 + q
if denominator <= 0.0 {
return (temperature_k + dewpoint_k) / 2.0
}
let new_tw = temperature_k - lv / cp * q_diff / denominator
// 检查新值是否有效
if new_tw != new_tw ||
new_tw < dewpoint_k - 10.0 ||
new_tw > temperature_k + 10.0 {
// 如果新值无效,使用阻尼回退
tw = (tw + dewpoint_k) / 2.0
continue
}
// 限制在合理范围内
let clamped_tw = if new_tw < dewpoint_k {
dewpoint_k + 0.01
} else if new_tw > temperature_k {
temperature_k - 0.01
} else {
new_tw
}
// 检查收敛(使用较小的容差)
if Double::abs(clamped_tw - tw) < 0.001 {
return clamped_tw
}
// 使用阻尼迭代(混合新旧值)以避免振荡
tw = 0.7 * tw + 0.3 * clamped_tw
}
// 如果未收敛,返回最后一次迭代的结果(确保在合理范围内)
if tw < dewpoint_k {
dewpoint_k + 0.1
} else if tw > temperature_k {
temperature_k - 0.1
} else {
tw
}
}
// ============================================================================
// 9. 基础气象函数(风速、风向等)
// ============================================================================
///|
/// 计算风速
/// u: 东西方向风速分量 (m/s)
/// v: 南北方向风速分量 (m/s)
pub fn wind_speed(u : Double, v : Double) -> Double {
Double::sqrt(u * u + v * v)
}
///|
/// 计算风向
/// u: 东西方向风速分量 (m/s)
/// v: 南北方向风速分量 (m/s)
/// convention: "from" (默认,气象学约定) 或 "to" (海洋学约定)
/// 返回: 风向角度 (度),0-360,0度为北
pub fn wind_direction(u : Double, v : Double, convention? : String) -> Double {
let conv = match convention {
Some(c) => c
None => "from"
}
// 计算反正切,得到 -180 到 180 度的角度
let angle_rad = @math.atan2(-v, -u)
let angle_deg = angle_rad * 180.0 / 3.141592653589793
// 转换为 0-360 度范围
let mut wdir = 90.0 - angle_deg
// 处理海洋学约定
if conv == "to" {
wdir = wdir - 180.0
}
// 规范化到 0-360 度
if wdir <= 0.0 {
wdir = wdir + 360.0
}
// 处理静风情况
if Double::abs(u) < 0.001 && Double::abs(v) < 0.001 {
0.0
} else {
wdir
}
}
///|
/// 从风速和风向计算 u, v 分量
/// speed: 风速 (m/s)
/// wind_direction: 风向 (度),0度为北,顺时针增加
pub fn wind_components(
speed : Double,
wind_direction_deg : Double,
) -> (Double, Double) {
let angle_rad = (270.0 - wind_direction_deg) * 3.141592653589793 / 180.0
let u = speed * @math.cos(angle_rad)
let v = speed * @math.sin(angle_rad)
(u, v)
}
///|
/// 计算科里奥利参数
/// latitude: 纬度 (度)
/// 返回: 科里奥利参数 (1/s)
pub fn coriolis_parameter(latitude : Double) -> Double {
2.0 * earth_avg_angular_vel * @math.sin(latitude * 3.141592653589793 / 180.0)
}
// ============================================================================
// 10. 稳定度分析函数
// ============================================================================
///|
/// 计算 Brunt-Väisälä 频率的平方
/// height: 高度数组 (m)
/// potential_temperature: 位温数组 (K)
/// 返回: N² 数组 (1/s²)
pub fn brunt_vaisala_frequency_squared(
height : Array[Double],
potential_temperature : Array[Double],
) -> Array[Double]? {
if height.length() < 2 || potential_temperature.length() != height.length() {
None
} else {
let n = height.length()
let g = earth_gravity
let n2 = Array::make(n, 0.0)
// 第一个点:使用前向差分
if n > 1 {
let dz = height[1] - height[0]
let dtheta = potential_temperature[1] - potential_temperature[0]
let n2_val = if dz > 0.0 && potential_temperature[0] > 0.0 {
g / potential_temperature[0] * (dtheta / dz)
} else {
0.0
}
n2[0] = n2_val
}
// 中间点:使用中心差分
for i = 1; i < n - 1; i = i + 1 {
let dz = height[i + 1] - height[i - 1]
let dtheta = potential_temperature[i + 1] - potential_temperature[i - 1]
let n2_val = if dz > 0.0 && potential_temperature[i] > 0.0 {
g / potential_temperature[i] * (dtheta / dz)
} else {
0.0
}
n2[i] = n2_val
}
// 最后一个点:使用后向差分
if n > 1 {
let dz = height[n - 1] - height[n - 2]
let dtheta = potential_temperature[n - 1] - potential_temperature[n - 2]
let n2_val = if dz > 0.0 && potential_temperature[n - 1] > 0.0 {
g / potential_temperature[n - 1] * (dtheta / dz)
} else {
0.0
}
n2[n - 1] = n2_val
}
Some(n2)
}
}
///|
/// 计算 Brunt-Väisälä 频率
pub fn brunt_vaisala_frequency(
height : Array[Double],
potential_temperature : Array[Double],
) -> Array[Double]? {
match brunt_vaisala_frequency_squared(height, potential_temperature) {
Some(n2) => {
let n = Array::make(n2.length(), 0.0)
for i = 0; i < n2.length(); i = i + 1 {
n[i] = if n2[i] > 0.0 { Double::sqrt(n2[i]) } else { 0.0 }
}
Some(n)
}
None => None
}
}
///|
/// 计算 Brunt-Väisälä 周期
pub fn brunt_vaisala_period(
height : Array[Double],
potential_temperature : Array[Double],
) -> Array[Double]? {
match brunt_vaisala_frequency(height, potential_temperature) {
Some(n) => {
let period = Array::make(n.length(), 0.0)
for i = 0; i < n.length(); i = i + 1 {
period[i] = if n[i] > 0.0 {
2.0 * 3.141592653589793 / n[i]
} else {
0.0
}
}
Some(period)
}
None => None
}
}
///|
/// 计算静力稳定度 (Static Stability)
/// 公式: σ = - (1/θ) * (∂θ/∂p)
/// 返回静力稳定度 (1/Pa)
/// 正值表示稳定,负值表示不稳定
pub fn static_stability(
pressure : Array[Double],
temperature : Array[Double],
) -> Array[Double]? {
if pressure.length() != temperature.length() || pressure.length() < 2 {
None
} else {
let n = pressure.length()
let stability = Array::make(n, 0.0)
// 计算位温
let theta = Array::make(n, 0.0)
for i = 0; i < n; i = i + 1 {
theta[i] = potential_temperature(pressure[i], temperature[i])
}
// 第一个点:使用前向差分
if n > 1 {
let dp = pressure[0] - pressure[1] // 注意:压力向上递减
let dtheta = theta[1] - theta[0]
stability[0] = if dp != 0.0 && theta[0] > 0.0 {
-1.0 / theta[0] * (dtheta / dp)
} else {
0.0
}
}
// 中间点:使用中心差分
for i = 1; i < n - 1; i = i + 1 {
let dp = pressure[i - 1] - pressure[i + 1]
let dtheta = theta[i + 1] - theta[i - 1]
stability[i] = if dp != 0.0 && theta[i] > 0.0 {
-1.0 / theta[i] * (dtheta / dp)
} else {
0.0
}
}
// 最后一个点:使用后向差分
if n > 1 {
let dp = pressure[n - 2] - pressure[n - 1]
let dtheta = theta[n - 1] - theta[n - 2]
stability[n - 1] = if dp != 0.0 && theta[n - 1] > 0.0 {
-1.0 / theta[n - 1] * (dtheta / dp)
} else {
0.0
}
}
Some(stability)
}
}
// ============================================================================
// 11. 厚度计算
// ============================================================================
///|
/// 计算静力学厚度
/// pressure_bottom: 底层气压 (Pa)
/// pressure_top: 顶层气压 (Pa)
/// temperature: 平均温度 (K)
/// mixing_ratio_val: 混合比(可选)
pub fn thickness_hydrostatic(
pressure_bottom : Double,
pressure_top : Double,
temperature : Double,
mixing_ratio_val? : Double,
) -> Double {
let mr = match mixing_ratio_val {
Some(m) => m
None => 0.0
}
let tv = virtual_temperature(temperature, mr)
let r = dry_air_gas_constant
let g = earth_gravity
// 使用虚温的静力学方程
// h = (R_d * T_v / g) * ln(p_bottom / p_top)
r * tv / g * @math.ln(pressure_bottom / pressure_top)
}
// ============================================================================
// 12. 静态能量
// ============================================================================
///|
/// 计算干静力能
/// height: 高度 (m)
/// temperature: 温度 (K)
pub fn dry_static_energy(height : Double, temperature : Double) -> Double {
let cp = dry_air_spec_heat_press
let g = earth_gravity
cp * temperature + g * height
}
///|
/// 计算湿静力能
/// height: 高度 (m)
/// temperature: 温度 (K)
/// specific_humidity: 比湿 (kg/kg)
pub fn moist_static_energy(
height : Double,
temperature : Double,
specific_humidity : Double,
) -> Double {
let dse = dry_static_energy(height, temperature)
let lv = latent_heat_vaporization(temperature)
dse + lv * specific_humidity
}
// ============================================================================
// 13. 湿绝热过程和对流分析
// ============================================================================
///|
/// 计算湿绝热过程中的温度
/// 使用迭代方法计算气块沿湿绝热线移动时的温度
/// pressure: 目标气压 (Pa)
/// temperature: 起始温度 (K)
/// reference_pressure: 参考气压 (Pa),默认使用起始气压
/// max_iters: 最大迭代次数,默认 50
/// eps: 收敛容差,默认 1e-6
/// 返回: 目标气压下的温度 (K)
pub fn moist_lapse(
pressure : Double,
temperature : Double,
reference_pressure? : Double,
max_iters? : Int,
eps? : Double,
) -> Double {
let ref_p = match reference_pressure {
Some(p) => p
None => pressure
}
let _max_iter = match max_iters {
Some(n) => n
None => 50
}
let tolerance = match eps {
Some(e) => e
None => 0.000001
}
// 如果压力相同,直接返回温度
if (pressure - ref_p).abs() < tolerance {
temperature
} else {
// 计算初始混合比(假设饱和)
let initial_mr = saturation_mixing_ratio(ref_p, temperature, "liquid")
// 使用迭代方法计算湿绝热温度
// 基于能量守恒:C_p * dT = R * T * dp/p - L * dr
let mut current_temp = temperature
let mut current_p = ref_p
let mut current_mr = initial_mr
// 使用数值积分方法,将压力变化分成小步
let dp = pressure - ref_p
let n_steps = 20 // 分成20步以提高精度
let dp_step = dp / Double::from_int(n_steps)
for _step = 0; _step < n_steps; _step = _step + 1 {
current_p = current_p + dp_step
// 计算当前温度下的饱和混合比
let mr_sat = saturation_mixing_ratio(current_p, current_temp, "liquid")
// 如果当前混合比超过饱和,则按湿绝热过程
if current_mr >= mr_sat {
// 湿绝热过程:潜热释放
let lv = latent_heat_vaporization(current_temp)
let cp = moist_air_specific_heat_pressure(
specific_humidity_from_mixing_ratio(current_mr),
)
let r = moist_air_gas_constant(
specific_humidity_from_mixing_ratio(current_mr),
)
// 计算温度变化
// dT = (R*T/p - L*dr_sat/dT) * dp / (C_p + L*dr_sat/dT)
// 简化:使用近似公式
let gamma_m = earth_gravity /
cp *
(1.0 + lv * mr_sat / (r * current_temp)) /
(1.0 + lv * lv * mr_sat / (cp * r * current_temp * current_temp))
let dt = -gamma_m * dp_step / (earth_gravity * current_temp / r)
current_temp = current_temp + dt
// 更新混合比为饱和值
current_mr = mr_sat
} else {
// 干绝热过程
let cp = dry_air_spec_heat_press
let r = dry_air_gas_constant
let gamma_d = earth_gravity / cp
let dt = -gamma_d * dp_step / (earth_gravity * current_temp / r)
current_temp = current_temp + dt
}
// 防止温度过低或过高
if current_temp < 150.0 {
current_temp = 150.0
}
if current_temp > 400.0 {
current_temp = 400.0
}
}
current_temp
}
}
///|
/// 计算抬升凝结高度 (LCL - Lifting Condensation Level)
/// 气块从起始位置干绝热抬升至饱和的高度
/// pressure: 起始气压 (Pa)
/// temperature: 起始温度 (K)
/// dewpoint: 起始露点温度 (K)
/// max_iters: 最大迭代次数,默认 50
/// eps: 收敛容差,默认 1e-6
/// 返回: (LCL 气压 (Pa), LCL 温度 (K))
pub fn lcl(
pressure : Double,
temperature : Double,
dewpoint : Double,
max_iters? : Int,
eps? : Double,
) -> (Double, Double) {
let max_iter = match max_iters {
Some(n) => n
None => 50
}
let tolerance = match eps {
Some(e) => e
None => 0.000001
}
// 如果温度已经低于等于露点,说明已经饱和
if temperature <= dewpoint {
(pressure, temperature)
} else {
// 计算起始混合比(从露点)
let initial_mr = saturation_mixing_ratio(pressure, dewpoint, "liquid")
// 使用迭代方法找到 LCL
// LCL 是气块干绝热抬升至饱和时的位置
// 使用 Bolton (1980) 公式的改进版本
let mut current_p = pressure
let mut current_t = temperature
let mut iter = 0
while iter < max_iter {
// 计算当前温度下的饱和混合比
let mr_sat = saturation_mixing_ratio(current_p, current_t, "liquid")
// 检查是否达到饱和
if (mr_sat - initial_mr).abs() < tolerance * initial_mr {
break
}
// 如果还没饱和,继续干绝热抬升
// 使用二分法或牛顿法找到 LCL 气压
// 简化:使用 Bolton 公式的近似
let t_ratio = current_t / dewpoint
let p_lcl = pressure * Double::pow(t_ratio, -3.5)
// 计算在 LCL 处的温度(干绝热过程)
let t_lcl = dewpoint * Double::pow(p_lcl / pressure, poisson_exponent)
// 更新
let dp = (p_lcl - current_p).abs()
if dp < tolerance * pressure {
current_p = p_lcl
current_t = t_lcl
break
}
current_p = p_lcl
current_t = t_lcl
iter = iter + 1
}
(current_p, current_t)
}
}
///|
/// 计算气块剖面(温度随压力的变化)
/// 从起始位置干绝热抬升到 LCL,然后湿绝热抬升
/// pressure: 起始气压 (Pa)
/// temperature: 起始温度 (K)
/// dewpoint: 起始露点温度 (K)
/// 返回: 温度剖面数组(对应压力数组)
pub fn parcel_profile(
pressure : Array[Double],
temperature : Double,
dewpoint : Double,
) -> Array[Double]? {
if pressure.length() == 0 {
None
} else {
let start_pressure = pressure[0]
let start_temp = temperature
let start_dewpoint = dewpoint
// 计算 LCL
let (lcl_p, lcl_t) = lcl(start_pressure, start_temp, start_dewpoint)
let profile = Array::make(pressure.length(), 0.0)
for i = 0; i < pressure.length(); i = i + 1 {
let p = pressure[i]
if p >= lcl_p {
// LCL 以下:干绝热过程
let theta = potential_temperature(start_pressure, start_temp)
profile[i] = temperature_from_potential_temperature(p, theta)
} else {
// LCL 以上:湿绝热过程
profile[i] = moist_lapse(p, lcl_t, reference_pressure=lcl_p)
}
}
Some(profile)
}
}
///|
/// 计算自由对流高度 (LFC - Level of Free Convection)
/// 气块温度首次高于环境温度的高度
/// pressure: 气压数组 (Pa),从地面向上递减
/// temperature: 环境温度数组 (K)
/// parcel_profile: 气块温度剖面 (K)
/// 返回: (LFC 气压 (Pa), LFC 温度 (K)),如果不存在则返回 None
pub fn lfc(
pressure : Array[Double],
temperature : Array[Double],
parcel_profile : Array[Double],
) -> (Double, Double)? {
if pressure.length() != temperature.length() ||
pressure.length() != parcel_profile.length() ||
pressure.length() < 2 {
None
} else {
// 找到气块温度首次高于环境温度的位置
let mut found = false
let mut lfc_p = 0.0
let mut lfc_t = 0.0
// 从下往上查找(数组索引从大到小,因为压力向上递减)
for i = 0; i < pressure.length() - 1; i = i + 1 {
let p_parcel = parcel_profile[i]
let t_env = temperature[i]
// 检查气块温度是否高于环境温度
if p_parcel > t_env && !found {
// 找到 LFC
found = true
lfc_p = pressure[i]
lfc_t = p_parcel
break
}
}
if found {
Some((lfc_p, lfc_t))
} else {
None
}
}
}
///|
/// 计算平衡高度 (EL - Equilibrium Level)
/// 气块温度再次等于环境温度的高度(LFC 之上)
/// pressure: 气压数组 (Pa),从地面向上递减
/// temperature: 环境温度数组 (K)
/// parcel_profile: 气块温度剖面 (K)
/// which: "top" 或 "bottom",返回哪个 EL(默认 "top")
/// 返回: (EL 气压 (Pa), EL 温度 (K)),如果不存在则返回 None
pub fn el(
pressure : Array[Double],
temperature : Array[Double],
parcel_profile : Array[Double],
which? : String,
) -> (Double, Double)? {
let which_el = match which {
Some(w) => w
None => "top"
}
if pressure.length() != temperature.length() ||
pressure.length() != parcel_profile.length() ||
pressure.length() < 2 {
None
} else {
// 先找到 LFC
let lfc_result = lfc(pressure, temperature, parcel_profile)
match lfc_result {
Some((lfc_p, _lfc_t)) => {
// 找到 LFC 后,在 LFC 之上查找 EL
let mut found = false
let mut el_p = 0.0
let mut el_t = 0.0
let mut lfc_index = 0
// 找到 LFC 的索引
for i = 0; i < pressure.length(); i = i + 1 {
if (pressure[i] - lfc_p).abs() < 1.0 {
lfc_index = i
break
}
}
// 从 LFC 向上查找 EL
if which_el == "top" {
// 找最高的 EL
for i = lfc_index + 1; i < pressure.length(); i = i + 1 {
let p_parcel = parcel_profile[i]
let t_env = temperature[i]
// 如果气块温度等于或低于环境温度,找到 EL
if p_parcel <= t_env {
found = true
el_p = pressure[i]
el_t = p_parcel
// 继续找最高的
}
}
} else {
// 找最低的 EL(第一个)
for i = lfc_index + 1; i < pressure.length(); i = i + 1 {
let p_parcel = parcel_profile[i]
let t_env = temperature[i]
if p_parcel <= t_env {
found = true
el_p = pressure[i]
el_t = p_parcel
break
}
}
}
if found {
Some((el_p, el_t))
} else {
None
}
}
None => None
}
}
}
///|
/// 计算对流可用位能 (CAPE) 和对流抑制能量 (CIN)
/// pressure: 气压数组 (Pa),从地面向上递减
/// temperature: 环境温度数组 (K)
/// dewpoint: 环境露点数组 (K)
/// parcel_profile: 气块温度剖面 (K)
/// 返回: (CAPE (J/kg), CIN (J/kg))
pub fn cape_cin(
pressure : Array[Double],
temperature : Array[Double],
dewpoint : Array[Double],
parcel_profile : Array[Double],
) -> (Double, Double) {
if pressure.length() != temperature.length() ||
pressure.length() != dewpoint.length() ||
pressure.length() != parcel_profile.length() ||
pressure.length() < 2 {
(0.0, 0.0)
} else {
let r = dry_air_gas_constant
let mut cape = 0.0
let mut cin = 0.0
// 找到 LFC
let lfc_result = lfc(pressure, temperature, parcel_profile)
let mut lfc_index = 0
let mut el_index = pressure.length() - 1
match lfc_result {
Some((lfc_p, _lfc_t)) => {
// 找到 LFC 的索引
for i = 0; i < pressure.length(); i = i + 1 {
if (pressure[i] - lfc_p).abs() < 1.0 {
lfc_index = i
break
}
}
// 找到 EL
let el_result = el(pressure, temperature, parcel_profile, which="top")
match el_result {
Some((el_p, _el_t)) =>
for i = 0; i < pressure.length(); i = i + 1 {
if (pressure[i] - el_p).abs() < 1.0 {
el_index = i
break
}
}
None => ignore(0)
}
}
None =>
// 没有 LFC,返回 0
return (0.0, 0.0)
}
// 计算 CIN(从地面到 LFC)
for i = 0; i < lfc_index; i = i + 1 {
let t_parcel = parcel_profile[i]
let t_env = temperature[i]
let p = pressure[i]
if t_parcel < t_env {
// 负浮力区域,计算 CIN
let tv_parcel = virtual_temperature(t_parcel, 0.0)
let tv_env = virtual_temperature(t_env, 0.0)
let buoyancy = r * (tv_parcel - tv_env) / p
cin = cin + buoyancy * (pressure[i] - pressure[i + 1])
}
}
// 计算 CAPE(从 LFC 到 EL)
for i = lfc_index; i < el_index; i = i + 1 {
let t_parcel = parcel_profile[i]
let t_env = temperature[i]
let p = pressure[i]
if t_parcel > t_env {
// 正浮力区域,计算 CAPE
let tv_parcel = virtual_temperature(t_parcel, 0.0)
let tv_env = virtual_temperature(t_env, 0.0)
let buoyancy = r * (tv_parcel - tv_env) / p
cape = cape + buoyancy * (pressure[i] - pressure[i + 1])
}
}
(cape, cin)
}
}
// ============================================================================
// 14. 运动学函数
// ============================================================================
///|
/// 计算垂直涡度
/// 公式: ζ = ∂v/∂x - ∂u/∂y
/// u: 东西方向风速 (m/s),二维数组
/// v: 南北方向风速 (m/s),二维数组
/// dx: x 方向网格间距 (m)
/// dy: y 方向网格间距 (m)
/// 返回: 垂直涡度数组 (1/s)
pub fn vorticity(
u : Array[Array[Double]],
v : Array[Array[Double]],
dx : Double,
dy : Double,
) -> Array[Array[Double]]? {
if u.length() != v.length() || u.length() == 0 {
None
} else {
let ny = u.length()
let nx = if u[0].length() > 0 { u[0].length() } else { 0 }
if nx == 0 || v.length() == 0 || v[0].length() != nx {
None
} else {
let vort = Array::make(ny, Array::make(nx, 0.0))
// 使用中心差分计算涡度
for j = 0; j < ny; j = j + 1 {
for i = 0; i < nx; i = i + 1 {
// ∂v/∂x
let dv_dx = if i > 0 && i < nx - 1 {
(v[j][i + 1] - v[j][i - 1]) / (2.0 * dx)
} else if i == 0 {
(v[j][i + 1] - v[j][i]) / dx
} else {
(v[j][i] - v[j][i - 1]) / dx
}
// ∂u/∂y
let du_dy = if j > 0 && j < ny - 1 {
(u[j + 1][i] - u[j - 1][i]) / (2.0 * dy)
} else if j == 0 {
(u[j + 1][i] - u[j][i]) / dy
} else {
(u[j][i] - u[j - 1][i]) / dy
}
// 涡度 = ∂v/∂x - ∂u/∂y
vort[j][i] = dv_dx - du_dy
}
}
Some(vort)
}
}
}
///|
/// 计算水平散度
/// 公式: D = ∂u/∂x + ∂v/∂y
/// u: 东西方向风速 (m/s),二维数组
/// v: 南北方向风速 (m/s),二维数组
/// dx: x 方向网格间距 (m)
/// dy: y 方向网格间距 (m)
/// 返回: 散度数组 (1/s)
pub fn divergence(
u : Array[Array[Double]],
v : Array[Array[Double]],
dx : Double,
dy : Double,
) -> Array[Array[Double]]? {
if u.length() != v.length() || u.length() == 0 {
None
} else {
let ny = u.length()
let nx = if u[0].length() > 0 { u[0].length() } else { 0 }
if nx == 0 || v.length() == 0 || v[0].length() != nx {
None
} else {
let div = Array::make(ny, Array::make(nx, 0.0))
// 使用中心差分计算散度
for j = 0; j < ny; j = j + 1 {
for i = 0; i < nx; i = i + 1 {
// ∂u/∂x
let du_dx = if i > 0 && i < nx - 1 {
(u[j][i + 1] - u[j][i - 1]) / (2.0 * dx)
} else if i == 0 {
(u[j][i + 1] - u[j][i]) / dx
} else {
(u[j][i] - u[j][i - 1]) / dx
}
// ∂v/∂y
let dv_dy = if j > 0 && j < ny - 1 {
(v[j + 1][i] - v[j - 1][i]) / (2.0 * dy)
} else if j == 0 {
(v[j + 1][i] - v[j][i]) / dy
} else {
(v[j][i] - v[j - 1][i]) / dy
}
// 散度 = ∂u/∂x + ∂v/∂y
div[j][i] = du_dx + dv_dy
}
}
Some(div)
}
}
}
///|
/// 计算地转风
/// 从位势高度计算地转风速分量
/// height: 位势高度 (m),二维数组
/// dx: x 方向网格间距 (m)
/// dy: y 方向网格间距 (m)
/// latitude_deg: 纬度 (度)
/// 返回: (u_g (m/s), v_g (m/s)),u_g 为东西分量,v_g 为南北分量
pub fn geostrophic_wind(
height : Array[Array[Double]],
dx : Double,
dy : Double,
latitude_deg : Double,
) -> (Array[Array[Double]], Array[Array[Double]])? {
if height.length() == 0 {
None
} else {
let ny = height.length()
let nx = if height[0].length() > 0 { height[0].length() } else { 0 }
if nx == 0 {
None
} else {
// 计算科里奥利参数
let f = coriolis_parameter(latitude_deg)
let u_g = Array::make(ny, Array::make(nx, 0.0))
let v_g = Array::make(ny, Array::make(nx, 0.0))
// 地转风公式:
// u_g = - (g/f) * (∂h/∂y)
// v_g = (g/f) * (∂h/∂x)
let g = earth_gravity
let coeff = g / f
for j = 0; j < ny; j = j + 1 {
for i = 0; i < nx; i = i + 1 {
// ∂h/∂x (用于计算 v_g)
let dh_dx = if i > 0 && i < nx - 1 {
(height[j][i + 1] - height[j][i - 1]) / (2.0 * dx)
} else if i == 0 {
(height[j][i + 1] - height[j][i]) / dx
} else {
(height[j][i] - height[j][i - 1]) / dx
}
// ∂h/∂y (用于计算 u_g)
let dh_dy = if j > 0 && j < ny - 1 {
(height[j + 1][i] - height[j - 1][i]) / (2.0 * dy)
} else if j == 0 {
(height[j + 1][i] - height[j][i]) / dy
} else {
(height[j][i] - height[j - 1][i]) / dy
}
// u_g = - (g/f) * (∂h/∂y)
u_g[j][i] = -coeff * dh_dy
// v_g = (g/f) * (∂h/∂x)
v_g[j][i] = coeff * dh_dx
}
}
Some((u_g, v_g))
}
}
}
///|
/// 计算位涡(正压)
/// 公式: PV = (ζ + f) / H
/// u: 东西方向风速 (m/s),二维数组
/// v: 南北方向风速 (m/s),二维数组
/// dx: x 方向网格间距 (m)
/// dy: y 方向网格间距 (m)
/// latitude_deg: 纬度 (度)
/// height: 位势高度或厚度 (m),二维数组
/// 返回: 位涡数组 (PVU, 1e-6 m²·K·s⁻¹·kg⁻¹)
pub fn potential_vorticity_barotropic(
u : Array[Array[Double]],
v : Array[Array[Double]],
dx : Double,
dy : Double,
latitude_deg : Double,
height : Array[Array[Double]],
) -> Array[Array[Double]]? {
// 计算相对涡度
let vort_result = vorticity(u, v, dx, dy)
match vort_result {
Some(vort) =>
if height.length() != vort.length() || height.length() == 0 {
None
} else {
let ny = vort.length()
let nx = if vort[0].length() > 0 { vort[0].length() } else { 0 }
if nx == 0 || height[0].length() != nx {
None
} else {
let f = coriolis_parameter(latitude_deg)
let pv = Array::make(ny, Array::make(nx, 0.0))
for j = 0; j < ny; j = j + 1 {
for i = 0; i < nx; i = i + 1 {
// 绝对涡度 = 相对涡度 + 科里奥利参数
let abs_vort = vort[j][i] + f
// 位涡 = 绝对涡度 / 高度
if height[j][i] > 0.0 {
pv[j][i] = abs_vort / height[j][i]
} else {
pv[j][i] = 0.0
}
}
}
Some(pv)
}
}
None => None
}
}
///|
/// 计算位涡(斜压)
/// 公式: PV = -g * (ζ + f) * (∂θ/∂p)
/// u: 东西方向风速 (m/s),二维数组
/// v: 南北方向风速 (m/s),二维数组
/// temperature: 温度 (K),二维数组
/// pressure: 气压 (Pa),一维数组(垂直方向)
/// dx: x 方向网格间距 (m)
/// dy: y 方向网格间距 (m)
/// latitude_deg: 纬度 (度)
/// 返回: 位涡数组 (PVU, 1e-6 m²·K·s⁻¹·kg⁻¹)
pub fn potential_vorticity_baroclinic(
u : Array[Array[Double]],
v : Array[Array[Double]],
temperature : Array[Array[Double]],
pressure : Array[Double],
dx : Double,
dy : Double,
latitude_deg : Double,
) -> Array[Array[Double]]? {
// 计算相对涡度
let vort_result = vorticity(u, v, dx, dy)
match vort_result {
Some(vort) =>
if temperature.length() != vort.length() || temperature.length() == 0 {
None
} else {
let ny = vort.length()
let nx = if vort[0].length() > 0 { vort[0].length() } else { 0 }
if nx == 0 || temperature[0].length() != nx || pressure.length() < 2 {
None
} else {
let f = coriolis_parameter(latitude_deg)
let g = earth_gravity
let pv = Array::make(ny, Array::make(nx, 0.0))
// 计算位温梯度
for j = 0; j < ny; j = j + 1 {
for i = 0; i < nx; i = i + 1 {
// 计算位温(使用平均气压)
let avg_p = if pressure.length() > 0 {
let mut sum = 0.0
for k = 0; k < pressure.length(); k = k + 1 {
sum = sum + pressure[k]
}
sum / Double::from_int(pressure.length())
} else {
100000.0
}
let theta = potential_temperature(avg_p, temperature[j][i])
// 计算 ∂θ/∂p(使用中心差分)
let dtheta_dp = if pressure.length() > 1 {
// 简化为使用压力差的倒数
let dp = pressure[0] - pressure[pressure.length() - 1]
if dp > 0.0 {
// 近似:使用平均温度梯度
-theta / dp
} else {
0.0
}
} else {
0.0
}
// 绝对涡度
let abs_vort = vort[j][i] + f
// 位涡 = -g * (ζ + f) * (∂θ/∂p)
pv[j][i] = -g * abs_vort * dtheta_dp
}
}
Some(pv)
}
}
None => None
}
}
// ============================================================================
// 11. 天气指数函数
// ============================================================================
///|
/// 在指定气压层进行线性插值
/// pressure: 气压数组 (Pa),从高到低排列
/// values: 对应的值数组
/// target_pressure: 目标气压 (Pa)
/// 返回: 插值得到的值,如果无法插值则返回 None
fn interpolate_to_pressure(
pressure : Array[Double],
values : Array[Double],
target_pressure : Double,
) -> Double? {
if pressure.length() != values.length() || pressure.length() < 2 {
None
} else {
// 找到包含目标气压的区间
let mut found = false
let mut idx = 0
// 如果目标气压在数组范围内
if target_pressure <= pressure[0] &&
target_pressure >= pressure[pressure.length() - 1] {
// 找到目标气压所在的区间
for i = 0; i < pressure.length() - 1; i = i + 1 {
if (
target_pressure <= pressure[i] && target_pressure >= pressure[i + 1]
) ||
(target_pressure >= pressure[i] && target_pressure <= pressure[i + 1]) {
idx = i
found = true
break
}
}
}
if !found {
// 如果目标气压在数组外,使用最近的点
if target_pressure > pressure[0] {
Some(values[0])
} else if target_pressure < pressure[pressure.length() - 1] {
Some(values[pressure.length() - 1])
} else {
None
}
} else {
// 线性插值
let p1 = pressure[idx]
let p2 = pressure[idx + 1]
let v1 = values[idx]
let v2 = values[idx + 1]
if Double::abs(p2 - p1) < 1.0 {
// 如果压力差太小,直接返回平均值
Some((v1 + v2) / 2.0)
} else {
let ratio = (target_pressure - p1) / (p2 - p1)
Some(v1 + ratio * (v2 - v1))
}
}
}
}
///|
/// 计算抬升指数 (Lifted Index)
/// LI = T500 - Tp500
/// 其中 T500 是 500 hPa 的观测温度,Tp500 是气块在 500 hPa 的温度
/// pressure: 气压数组 (Pa),从高到低排列
/// temperature: 环境温度数组 (K)
/// parcel_profile: 气块温度剖面 (K)
/// 返回: 抬升指数 (K),负值表示不稳定
pub fn lifted_index(
pressure : Array[Double],
temperature : Array[Double],
parcel_profile : Array[Double],
) -> Double? {
if pressure.length() != temperature.length() ||
pressure.length() != parcel_profile.length() {
None
} else {
// 500 hPa = 50000 Pa
let p500 = 50000.0
// 插值得到 500 hPa 的观测温度
let t500_result = interpolate_to_pressure(pressure, temperature, p500)
// 插值得到 500 hPa 的气块温度
let tp500_result = interpolate_to_pressure(pressure, parcel_profile, p500)
match (t500_result, tp500_result) {
(Some(t500), Some(tp500)) => Some(t500 - tp500)
_ => None
}
}
}
///|
/// 计算 K 指数
/// K = (T850 - T500) + Td850 - (T700 - Td700)
/// pressure: 气压数组 (Pa),从高到低排列
/// temperature: 温度数组 (K)
/// dewpoint: 露点温度数组 (K)
/// 返回: K 指数 (K),正值越大表示对流潜力越大
pub fn k_index(
pressure : Array[Double],
temperature : Array[Double],
dewpoint : Array[Double],
) -> Double? {
if pressure.length() != temperature.length() ||
pressure.length() != dewpoint.length() {
None
} else {
// 标准气压层:850, 700, 500 hPa
let p850 = 85000.0
let p700 = 70000.0
let p500 = 50000.0
// 插值得到各层的温度和露点
let t850_result = interpolate_to_pressure(pressure, temperature, p850)
let t700_result = interpolate_to_pressure(pressure, temperature, p700)
let t500_result = interpolate_to_pressure(pressure, temperature, p500)
let td850_result = interpolate_to_pressure(pressure, dewpoint, p850)
let td700_result = interpolate_to_pressure(pressure, dewpoint, p700)
match (t850_result, t700_result, t500_result, td850_result, td700_result) {
(Some(t850), Some(t700), Some(t500), Some(td850), Some(td700)) =>
// K = (T850 - T500) + Td850 - (T700 - Td700)
Some(t850 - t500 + td850 - (t700 - td700))
_ => None
}
}
}
///|
/// 计算可降水量 (Precipitable Water)
/// 通过对比湿进行气压积分得到
/// pressure: 气压数组 (Pa),从高到低排列
/// dewpoint: 露点温度数组 (K)
/// bottom: 底层气压 (Pa),可选,默认使用数组最小值
/// top: 顶层气压 (Pa),可选,默认使用数组最大值
/// 返回: 可降水量 (mm)
pub fn precipitable_water(
pressure : Array[Double],
dewpoint : Array[Double],
bottom? : Double,
top? : Double,
) -> Double? {
if pressure.length() != dewpoint.length() || pressure.length() < 2 {
None
} else {
let p_bottom = match bottom {
Some(b) => b
None => pressure[pressure.length() - 1] // 最低气压(数组末尾)
}
let p_top = match top {
Some(t) => t
None => pressure[0] // 最高气压(数组开头)
}
// 找到积分区间
let mut start_idx = 0
let mut end_idx = pressure.length() - 1
// 找到顶部和底部对应的索引
for i = 0; i < pressure.length(); i = i + 1 {
if pressure[i] <= p_top && start_idx == 0 {
start_idx = i
}
if pressure[i] >= p_bottom {
end_idx = i
}
}
if start_idx >= end_idx {
None
} else {
// 计算可降水量
// PW = (1/g) * ∫ q dp
// 其中 q 是比湿,g 是重力加速度
let g = earth_gravity
let mut pw = 0.0
// 使用梯形法则进行积分
for i = start_idx; i < end_idx; i = i + 1 {
let p1 = pressure[i]
let p2 = pressure[i + 1]
let td1 = dewpoint[i]
let td2 = dewpoint[i + 1]
// 计算比湿
let q1 = specific_humidity_from_dewpoint(p1, td1, "liquid")
let q2 = specific_humidity_from_dewpoint(p2, td2, "liquid")
// 梯形积分
let dp = Double::abs(p2 - p1)
let q_avg = (q1 + q2) / 2.0
// 积分:∫ q dp / g
// 转换为 mm: (kg/kg * Pa) / (m/s²) * (1000 mm/m) = mm
pw = pw + q_avg * dp / g * 1000.0
}
Some(pw)
}
}
}
// ============================================================================
// 12. 运动学扩展函数
// ============================================================================
///|
/// 计算风暴相对螺旋度 (Storm Relative Helicity)
/// 公式: H = ∫ (v - vs) · (∇ × v) dz
/// height: 高度数组 (m),从低到高排列
/// u: 东西方向风速分量数组 (m/s)
/// v: 南北方向风速分量数组 (m/s)
/// depth: 积分深度 (m),默认 3000 m
/// bottom: 底层高度 (m),可选,默认使用数组最小值
/// storm_u: 风暴移动速度的 u 分量 (m/s),可选,默认 0
/// storm_v: 风暴移动速度的 v 分量 (m/s),可选,默认 0
/// 返回: 风暴相对螺旋度 (m²/s²)
pub fn storm_relative_helicity(
height : Array[Double],
u : Array[Double],
v : Array[Double],
depth? : Double,
bottom? : Double,
storm_u? : Double,
storm_v? : Double,
) -> Double? {
if height.length() != u.length() ||
height.length() != v.length() ||
height.length() < 2 {
None
} else {
let depth_val = match depth {
Some(d) => d
None => 3000.0 // 默认 3000 m
}
let bottom_val = match bottom {
Some(b) => b
None => height[0] // 最低高度(数组开头)
}
let top_val = bottom_val + depth_val
let storm_u_val = match storm_u {
Some(u) => u
None => 0.0
}
let storm_v_val = match storm_v {
Some(v) => v
None => 0.0
}
// 找到积分区间
let mut start_idx = 0
let mut end_idx = height.length() - 1
for i = 0; i < height.length(); i = i + 1 {
if height[i] >= bottom_val && start_idx == 0 {
start_idx = i
}
if height[i] <= top_val {
end_idx = i
}
}
if start_idx >= end_idx {
None
} else {
// 计算相对风速
let rel_u = Array::make(end_idx - start_idx + 1, 0.0)
let rel_v = Array::make(end_idx - start_idx + 1, 0.0)
let h_arr = Array::make(end_idx - start_idx + 1, 0.0)
for i = start_idx; i <= end_idx; i = i + 1 {
let idx = i - start_idx
rel_u[idx] = u[i] - storm_u_val
rel_v[idx] = v[i] - storm_v_val
h_arr[idx] = height[i]
}
// 计算螺旋度
// H = ∫ (v_rel · (∇ × v_rel)) dz
// 对于一维垂直剖面,简化计算
// H ≈ Σ (v_rel[i] · (u_rel[i+1] - u_rel[i]) - u_rel[i] · (v_rel[i+1] - v_rel[i])) * Δz
let mut helicity = 0.0
for i = 0; i < rel_u.length() - 1; i = i + 1 {
let dz = h_arr[i + 1] - h_arr[i]
if dz > 0.0 {
// 计算相对涡度项
let du_dz = (rel_u[i + 1] - rel_u[i]) / dz
let dv_dz = (rel_v[i + 1] - rel_v[i]) / dz
// 螺旋度贡献: v · (du/dz) - u · (dv/dz)
let contrib = rel_v[i] * du_dz - rel_u[i] * dv_dz
helicity = helicity + contrib * dz
}
}
Some(helicity)
}
}
}
// ============================================================================
// 13. 体感温度函数
// ============================================================================
///|
/// 计算风寒指数 (Wind Chill)
/// 基于 NWS 风寒指数公式(2001年版本)
/// temperature: 环境温度 (K)
/// speed: 风速 (m/s)
/// face_level_winds: 是否使用面部高度风速(默认 false,使用 10 m 高度)
/// mask_undefined: 是否对超出有效范围的值返回 None(默认 true)
/// 返回: 风寒指数 (K)
/// 注意: 仅当温度 < 10°C (283.15 K) 且风速 > 4.8 km/h (1.33 m/s) 时有效
pub fn windchill(
temperature : Double,
speed : Double,
_face_level_winds? : Bool,
mask_undefined? : Bool,
) -> Double? {
let mask = match mask_undefined {
Some(m) => m
None => true
}
// 转换为摄氏度用于计算
let temp_c = temperature - 273.15
// 风速转换为 km/h
let speed_kmh = speed * 3.6
// 有效范围检查
let is_valid = temp_c < 10.0 && speed_kmh > 4.8
if !is_valid && mask {
None
} else {
// NWS 风寒指数公式(2001年)
// WC = 13.12 + 0.6215*T - 11.37*V^0.16 + 0.3965*T*V^0.16
// 其中 T 是温度(°C),V 是风速(km/h)
let v_pow = Double::pow(speed_kmh, 0.16)
let wc_c = 13.12 + 0.6215 * temp_c - 11.37 * v_pow + 0.3965 * temp_c * v_pow
// 转换回开尔文
Some(wc_c + 273.15)
}
}
///|
/// 计算热指数 (Heat Index)
/// 基于 Rothfusz 公式(NWS 使用)
/// temperature: 环境温度 (K)
/// relative_humidity: 相对湿度 (0-1 或 0-100)
/// mask_undefined: 是否对超出有效范围的值返回 None(默认 true)
/// 返回: 热指数 (K)
/// 注意: 仅当温度 > 27°C (300.15 K) 且相对湿度 > 40% 时有效
pub fn heat_index(
temperature : Double,
relative_humidity : Double,
mask_undefined? : Bool,
) -> Double? {
let mask = match mask_undefined {
Some(m) => m
None => true
}
// 转换为摄氏度用于计算
let temp_c = temperature - 273.15
// 处理相对湿度:如果 > 1,假设是百分比形式
let rh = if relative_humidity > 1.0 {
relative_humidity / 100.0
} else {
relative_humidity
}
// 有效范围检查
let is_valid = temp_c > 27.0 && rh > 0.4
if !is_valid && mask {
None
} else {
// Rothfusz 热指数公式
// HI = c1 + c2*T + c3*RH + c4*T*RH + c5*T² + c6*RH² + c7*T²*RH + c8*T*RH² + c9*T²*RH²
let c1 : Double = -8.78469475556
let c2 : Double = 1.61139411
let c3 : Double = 2.33854883889
let c4 : Double = -0.14611605
let c5 : Double = -0.012308094
let c6 : Double = -0.0164248277778
let c7 : Double = 0.002211732
let c8 : Double = 0.00072546
let c9 : Double = -0.000003582
let t2 = temp_c * temp_c
let rh2 = rh * rh
let hi_c = c1 +
c2 * temp_c +
c3 * rh +
c4 * temp_c * rh +
c5 * t2 +
c6 * rh2 +
c7 * t2 * rh +
c8 * temp_c * rh2 +
c9 * t2 * rh2
// 转换回开尔文
Some(hi_c + 273.15)
}
}
// ============================================================================
// 14. 新增功能函数
// ============================================================================
///|
/// 计算表观温度 (Apparent Temperature)
/// 根据条件自动选择风寒指数或热指数
/// temperature: 环境温度 (K)
/// relative_humidity: 相对湿度 (0-1 或 0-100)
/// speed: 风速 (m/s)
/// face_level_winds: 是否使用面部高度风速(默认 false)
/// mask_undefined: 是否对超出有效范围的值返回 None(默认 true)
pub fn apparent_temperature(
temperature : Double,
relative_humidity : Double,
speed : Double,
face_level_winds? : Bool,
mask_undefined? : Bool,
) -> Double? {
let mask = match mask_undefined {
Some(m) => m
None => true
}
// 计算风寒指数和热指数
let face_level_winds_val = match face_level_winds {
Some(flw) => flw
None => false
}
let wc_result = windchill(
temperature,
speed,
_face_level_winds=face_level_winds_val,
mask_undefined=mask,
)
let hi_result = heat_index(
temperature,
relative_humidity,
mask_undefined=mask,
)
// 如果风寒指数有效,使用风寒指数;否则使用热指数
match (wc_result, hi_result) {
(Some(wc), _) => Some(wc)
(None, Some(hi)) => Some(hi)
(None, None) =>
if mask {
None
} else {
Some(temperature) // 如果都无效且不mask,返回原温度
}
}
}
///|
/// 计算干绝热过程中的温度
/// 气块干绝热抬升时的温度变化
/// pressure: 目标气压 (Pa)
/// temperature: 起始温度 (K)
/// reference_pressure: 参考气压 (Pa),默认使用 pressure
/// 返回: 目标气压下的温度 (K)
pub fn dry_lapse(
pressure : Double,
temperature : Double,
reference_pressure? : Double,
) -> Double {
let ref_p = match reference_pressure {
Some(p) => p
None => pressure
}
// 干绝热过程:T = T_ref * (p / p_ref)^κ
temperature * Double::pow(pressure / ref_p, poisson_exponent)
}
///|
/// 计算对流凝结高度 (CCL - Convective Condensation Level)
/// 使用地面混合比线与环境温度廓线的交点
/// pressure: 气压数组 (Pa),从高到低排列
/// temperature: 环境温度数组 (K)
/// dewpoint: 露点温度数组 (K)
/// which: "top" (默认) 或 "bottom",返回哪个 CCL
/// 返回: (CCL 气压 (Pa), CCL 温度 (K), 对流温度 (K))
pub fn ccl(
pressure : Array[Double],
temperature : Array[Double],
dewpoint_arr : Array[Double],
which? : String,
) -> (Double, Double, Double)? {
if pressure.length() != temperature.length() ||
pressure.length() != dewpoint_arr.length() ||
pressure.length() < 2 {
None
} else {
let which_val = match which {
Some(w) => w
None => "top"
}
// 使用地面露点计算起始混合比
let p_start = pressure[0]
let dewpoint_start = dewpoint_arr[0]
let es_start = saturation_vapor_pressure(dewpoint_start, "liquid")
let r_start = mixing_ratio(es_start, p_start)
// 计算沿等混合比线的温度(即露点温度)
fn calc_rt(i : Int) -> Double {
let e = vapor_pressure(pressure[i], r_start)
dewpoint(e)
}
let rt_profile = Array::makei(pressure.length(), calc_rt)
// 找到等混合比线与环境温度线的交点
// 简化实现:找到第一个交点
let mut found_intersection = false
let mut p_ccl = 0.0
let mut t_ccl = 0.0
for i = 0; i < pressure.length() - 1; i = i + 1 {
if !found_intersection {
let rt1 = rt_profile[i]
let rt2 = rt_profile[i + 1]
let t1 = temperature[i]
let t2 = temperature[i + 1]
// 如果等混合比线从一侧穿过环境温度线到另一侧
if (rt1 > t1 && rt2 <= t2) || (rt1 <= t1 && rt2 > t2) {
// 线性插值找到交点
// 解方程: rt1 + ratio * (rt2 - rt1) = t1 + ratio * (t2 - t1)
// ratio = (t1 - rt1) / ((rt2 - rt1) - (t2 - t1))
let p1 = pressure[i]
let p2 = pressure[i + 1]
let denominator = rt2 - rt1 - (t2 - t1)
let ratio = if denominator != 0.0 {
(t1 - rt1) / denominator
} else {
0.5
}
// 限制 ratio 在 [0, 1] 范围内
let ratio_clamped = if ratio < 0.0 {
0.0
} else if ratio > 1.0 {
1.0
} else {
ratio
}
p_ccl = p1 + ratio_clamped * (p2 - p1)
t_ccl = t1 + ratio_clamped * (t2 - t1)
found_intersection = true
// 如果 which 是 "top",继续查找最后一个交点
if which_val != "top" {
break
}
}
} else if which_val == "top" {
// 继续查找最后一个交点
let rt1 = rt_profile[i]
let rt2 = rt_profile[i + 1]
let t1 = temperature[i]
let t2 = temperature[i + 1]
if (rt1 > t1 && rt2 <= t2) || (rt1 <= t1 && rt2 > t2) {
let p1 = pressure[i]
let p2 = pressure[i + 1]
let denominator = rt2 - rt1 - (t2 - t1)
let ratio = if denominator != 0.0 {
(t1 - rt1) / denominator
} else {
0.5
}
let ratio_clamped = if ratio < 0.0 {
0.0
} else if ratio > 1.0 {
1.0
} else {
ratio
}
p_ccl = p1 + ratio_clamped * (p2 - p1)
t_ccl = t1 + ratio_clamped * (t2 - t1)
}
}
}
if !found_intersection {
None
} else {
// 计算对流温度(从地面干绝热抬升到 CCL 的温度)
let t_convective = dry_lapse(p_start, t_ccl, reference_pressure=p_ccl)
Some((p_ccl, t_ccl, t_convective))
}
}
}
///|
/// 标准大气压转高度
/// 使用 U.S. 标准大气公式
/// pressure: 气压 (Pa)
/// 返回: 高度 (m)
pub fn pressure_to_height_std(pressure : Double) -> Double {
// 标准大气常数
let t0 : Double = 288.0 // K
let p0 : Double = 101325.0 // Pa (1013.25 hPa)
let gamma : Double = 0.0065 // K/m (6.5 K/km)
// 公式: Z = (T0 / γ) * [1 - (p/p0)^(R*γ/g)]
let r = dry_air_gas_constant
let g = earth_gravity
let exponent = r * gamma / g
t0 / gamma * (1.0 - Double::pow(pressure / p0, exponent))
}
///|
/// 高度转标准大气压
/// 使用 U.S. 标准大气公式的逆函数
/// height: 高度 (m)
/// 返回: 气压 (Pa)
pub fn height_to_pressure_std(height : Double) -> Double {
// 标准大气常数
let t0 : Double = 288.0 // K
let p0 : Double = 101325.0 // Pa
let gamma : Double = 0.0065 // K/m
// 公式: p = p0 * [1 - (γ*Z/T0)]^(g/(R*γ))
let r = dry_air_gas_constant
let g = earth_gravity
let exponent = g / (r * gamma)
p0 * Double::pow(1.0 - gamma * height / t0, exponent)
}
///|
/// 计算体积切变 (Bulk Shear)
/// 计算指定层内的风切变
/// pressure: 气压数组 (Pa),从高到低排列
/// u: u 分量数组 (m/s)
/// v: v 分量数组 (m/s)
/// depth: 层厚度 (Pa),默认 10000 Pa (100 hPa)
/// bottom: 底层气压 (Pa),默认使用数组最大值
/// 返回: (u 切变 (m/s), v 切变 (m/s))
pub fn bulk_shear(
pressure : Array[Double],
u : Array[Double],
v : Array[Double],
depth? : Double,
bottom? : Double,
) -> (Double, Double)? {
if pressure.length() != u.length() ||
pressure.length() != v.length() ||
pressure.length() < 2 {
None
} else {
let depth_val = match depth {
Some(d) => d
None => 10000.0 // 100 hPa
}
let bottom_val = match bottom {
Some(b) => b
None => {
// 找到最大气压(数组末尾)
let mut max_p = pressure[0]
for i = 0; i < pressure.length(); i = i + 1 {
if pressure[i] > max_p {
max_p = pressure[i]
}
}
max_p
}
}
let top_val = bottom_val - depth_val
// 找到底层和顶层的索引
let mut bottom_idx = 0
let mut top_idx = pressure.length() - 1
for i = 0; i < pressure.length(); i = i + 1 {
if Double::abs(pressure[i] - bottom_val) <
Double::abs(pressure[bottom_idx] - bottom_val) {
bottom_idx = i
}
if Double::abs(pressure[i] - top_val) <
Double::abs(pressure[top_idx] - top_val) {
top_idx = i
}
}
// 计算切变 = 顶层风 - 底层风
let u_shear = u[top_idx] - u[bottom_idx]
let v_shear = v[top_idx] - v[bottom_idx]
Some((u_shear, v_shear))
}
}
///|
/// 计算 Bunkers 风暴移动向量
/// 计算右移和左移超级单体风暴的移动方向
/// pressure: 气压数组 (Pa)
/// u: u 分量数组 (m/s)
/// v: v 分量数组 (m/s)
/// height: 高度数组 (m)
/// 返回: ((右移 u, 右移 v), (左移 u, 左移 v), (平均 u, 平均 v))
pub fn bunkers_storm_motion(
pressure : Array[Double],
u : Array[Double],
v : Array[Double],
height : Array[Double],
) -> ((Double, Double), (Double, Double), (Double, Double))? {
if pressure.length() != u.length() ||
pressure.length() != v.length() ||
pressure.length() != height.length() ||
pressure.length() < 2 {
None
} else {
// 找到 0-6km 层的平均风
let mut u_sum = 0.0
let mut v_sum = 0.0
let mut count = 0
let h6km = 6000.0
for i = 0; i < height.length(); i = i + 1 {
if height[i] <= h6km {
u_sum = u_sum + u[i]
v_sum = v_sum + v[i]
count = count + 1
}
}
if count == 0 {
None
} else {
let u_mean = u_sum / Double::from_int(count)
let v_mean = v_sum / Double::from_int(count)
// 找到 0-0.5km 层的平均风
let mut u_500m = 0.0
let mut v_500m = 0.0
let mut count_500m = 0
let h500m = 500.0
for i = 0; i < height.length(); i = i + 1 {
if height[i] <= h500m {
u_500m = u_500m + u[i]
v_500m = v_500m + v[i]
count_500m = count_500m + 1
}
}
if count_500m == 0 {
None
} else {
u_500m = u_500m / Double::from_int(count_500m)
v_500m = v_500m / Double::from_int(count_500m)
// 找到 5.5-6km 层的平均风
let mut u_5500m = 0.0
let mut v_5500m = 0.0
let mut count_5500m = 0
let h5500m = 5500.0
for i = 0; i < height.length(); i = i + 1 {
if height[i] >= h5500m && height[i] <= h6km {
u_5500m = u_5500m + u[i]
v_5500m = v_5500m + v[i]
count_5500m = count_5500m + 1
}
}
if count_5500m == 0 {
None
} else {
u_5500m = u_5500m / Double::from_int(count_5500m)
v_5500m = v_5500m / Double::from_int(count_5500m)
// 计算切变向量
let shear_u = u_5500m - u_500m
let shear_v = v_5500m - v_500m
let shear_mag = Double::sqrt(shear_u * shear_u + shear_v * shear_v)
if shear_mag < 0.001 {
None
} else {
// 计算偏差向量(垂直于切变,幅度 7.5 m/s)
let dev_mag = 7.5
let rdev_u = shear_v * (dev_mag / shear_mag)
let rdev_v = -shear_u * (dev_mag / shear_mag)
// 右移:平均风 + 偏差
let rm_u = u_mean + rdev_u
let rm_v = v_mean + rdev_v
// 左移:平均风 - 偏差
let lm_u = u_mean - rdev_u
let lm_v = v_mean - rdev_v
Some(((rm_u, rm_v), (lm_u, lm_v), (u_mean, v_mean)))
}
}
}
}
}
}
///|
/// 计算超级单体复合参数
/// mucape: 最不稳定 CAPE (J/kg)
/// effective_storm_helicity: 有效层风暴相对螺旋度 (m²/s²)
/// effective_shear: 有效切变 (m/s)
/// 返回: 超级单体复合参数(无量纲)
pub fn supercell_composite(
mucape : Double,
effective_storm_helicity : Double,
effective_shear : Double,
) -> Double {
// 公式: SCP = (MUCAPE/1000) * (ESRH/50) * (EShear/20)
// 其中 EShear 在 10 m/s 以下设为 0,20 m/s 以上设为 1
let eshear_term = if effective_shear < 10.0 {
0.0
} else if effective_shear > 20.0 {
1.0
} else {
effective_shear / 20.0
}
mucape / 1000.0 * (effective_storm_helicity / 50.0) * eshear_term
}
///|
/// 计算显著龙卷风参数
/// sbcape: 地面基础 CAPE (J/kg)
/// surface_based_lcl_height: 地面基础 LCL 高度 (m)
/// storm_helicity_1km: 0-1km 风暴相对螺旋度 (m²/s²)
/// shear_6km: 0-6km 切变 (m/s)
/// 返回: 显著龙卷风参数(无量纲)
pub fn significant_tornado(
sbcape : Double,
surface_based_lcl_height : Double,
storm_helicity_1km : Double,
shear_6km : Double,
) -> Double {
// 公式: SIGTOR = (SBCAPE/1500) * ((2000-LCL)/1000) * (SRH1km/150) * (Shear6km/20)
// LCL 高度项:>2000m 设为 0,<1000m 设为 1
let lcl_term = if surface_based_lcl_height > 2000.0 {
0.0
} else if surface_based_lcl_height < 1000.0 {
1.0
} else {
(2000.0 - surface_based_lcl_height) / 1000.0
}
// 切变项:<12.5 m/s 设为 0,>30 m/s 设为 1.5
let shear_term = if shear_6km < 12.5 {
0.0
} else if shear_6km > 30.0 {
1.5
} else {
shear_6km / 20.0
}
sbcape / 1500.0 * lcl_term * (storm_helicity_1km / 150.0) * shear_term
}
///|
/// 计算切变涡度
/// 公式: ζ_shear = (∂v/∂x - ∂u/∂y) 的切变部分
/// u: 东西方向风速 (m/s),二维数组
/// v: 南北方向风速 (m/s),二维数组
/// dx: x 方向网格间距 (m)
/// dy: y 方向网格间距 (m)
/// 返回: 切变涡度数组 (1/s)
pub fn shear_vorticity(
u : Array[Array[Double]],
v : Array[Array[Double]],
dx : Double,
dy : Double,
) -> Array[Array[Double]]? {
// 切变涡度与总涡度相同,因为这是二维水平涡度
vorticity(u, v, dx, dy)
}
///|
/// 计算曲率涡度
/// 公式: ζ_curvature = (v/r) - (u/r) * (∂u/∂x + ∂v/∂y)
/// 其中 r 是曲率半径(简化计算)
/// u: 东西方向风速 (m/s),二维数组
/// v: 南北方向风速 (m/s),二维数组
/// dx: x 方向网格间距 (m)
/// dy: y 方向网格间距 (m)
/// 返回: 曲率涡度数组 (1/s)
pub fn curvature_vorticity(
u : Array[Array[Double]],
v : Array[Array[Double]],
dx : Double,
dy : Double,
) -> Array[Array[Double]]? {
// 简化实现:曲率涡度通常需要流线曲率信息
// 这里使用近似方法
if u.length() != v.length() || u.length() == 0 {
None
} else {
let ny = u.length()
let nx = if u[0].length() > 0 { u[0].length() } else { 0 }
if nx == 0 || v.length() == 0 || v[0].length() != nx {
None
} else {
let curv_vort = Array::make(ny, Array::make(nx, 0.0))
// 计算风速
for j = 0; j < ny; j = j + 1 {
for i = 0; i < nx; i = i + 1 {
let speed = Double::sqrt(u[j][i] * u[j][i] + v[j][i] * v[j][i])
// 计算风向变化率作为曲率涡度的近似
// 这是一个简化实现
let du_dx = if i > 0 && i < nx - 1 {
(u[j][i + 1] - u[j][i - 1]) / (2.0 * dx)
} else if i == 0 {
(u[j][i + 1] - u[j][i]) / dx
} else {
(u[j][i] - u[j][i - 1]) / dx
}
let dv_dy = if j > 0 && j < ny - 1 {
(v[j + 1][i] - v[j - 1][i]) / (2.0 * dy)
} else if j == 0 {
(v[j + 1][i] - v[j][i]) / dy
} else {
(v[j][i] - v[j - 1][i]) / dy
}
// 曲率涡度的简化计算(基于速度梯度的组合)
if speed > 0.001 {
curv_vort[j][i] = (v[j][i] / dx - u[j][i] / dy) *
(du_dx + dv_dy) /
speed
} else {
curv_vort[j][i] = 0.0
}
}
}
Some(curv_vort)
}
}
}
///|
/// 计算平流
/// 计算标量场沿着风场的平流
/// field: 标量场(二维数组)
/// u: 东西方向风速 (m/s),二维数组
/// v: 南北方向风速 (m/s),二维数组
/// dx: x 方向网格间距 (m)
/// dy: y 方向网格间距 (m)
/// 返回: 平流数组(与 field 相同单位/s)
pub fn advection(
field : Array[Array[Double]],
u : Array[Array[Double]],
v : Array[Array[Double]],
dx : Double,
dy : Double,
) -> Array[Array[Double]]? {
if field.length() != u.length() ||
field.length() != v.length() ||
field.length() == 0 {
None
} else {
let ny = field.length()
let nx = if field[0].length() > 0 { field[0].length() } else { 0 }
if nx == 0 || u[0].length() != nx || v[0].length() != nx {
None
} else {
let adv = Array::make(ny, Array::make(nx, 0.0))
// 平流公式: -u * (∂field/∂x) - v * (∂field/∂y)
for j = 0; j < ny; j = j + 1 {
for i = 0; i < nx; i = i + 1 {
// ∂field/∂x
let df_dx = if i > 0 && i < nx - 1 {
(field[j][i + 1] - field[j][i - 1]) / (2.0 * dx)
} else if i == 0 {
(field[j][i + 1] - field[j][i]) / dx
} else {
(field[j][i] - field[j][i - 1]) / dx
}
// ∂field/∂y
let df_dy = if j > 0 && j < ny - 1 {
(field[j + 1][i] - field[j - 1][i]) / (2.0 * dy)
} else if j == 0 {
(field[j + 1][i] - field[j][i]) / dy
} else {
(field[j][i] - field[j - 1][i]) / dy
}
// 平流 = -u * df/dx - v * df/dy
adv[j][i] = -u[j][i] * df_dx - v[j][i] * df_dy
}
}
Some(adv)
}
}
}
///|
/// 计算锋生函数
/// 计算温度梯度的时间变化率
/// temperature: 温度场 (K),二维数组
/// u: 东西方向风速 (m/s),二维数组
/// v: 南北方向风速 (m/s),二维数组
/// dx: x 方向网格间距 (m)
/// dy: y 方向网格间距 (m)
/// 返回: 锋生函数数组 (K/(m·s))
pub fn frontogenesis(
temperature : Array[Array[Double]],
u : Array[Array[Double]],
v : Array[Array[Double]],
dx : Double,
dy : Double,
) -> Array[Array[Double]]? {
if temperature.length() != u.length() ||
temperature.length() != v.length() ||
temperature.length() == 0 {
None
} else {
let ny = temperature.length()
let nx = if temperature[0].length() > 0 {
temperature[0].length()
} else {
0
}
if nx == 0 || u[0].length() != nx || v[0].length() != nx {
None
} else {
let fronto = Array::make(ny, Array::make(nx, 0.0))
// 锋生函数: F = -0.5 * |∇T|^(-1) * [∇T · ∇(dV/dt)]
// 简化实现:计算温度梯度的平流
for j = 0; j < ny; j = j + 1 {
for i = 0; i < nx; i = i + 1 {
// 计算温度梯度
let dT_dx = if i > 0 && i < nx - 1 {
(temperature[j][i + 1] - temperature[j][i - 1]) / (2.0 * dx)
} else if i == 0 {
(temperature[j][i + 1] - temperature[j][i]) / dx
} else {
(temperature[j][i] - temperature[j][i - 1]) / dx
}
let dT_dy = if j > 0 && j < ny - 1 {
(temperature[j + 1][i] - temperature[j - 1][i]) / (2.0 * dy)
} else if j == 0 {
(temperature[j + 1][i] - temperature[j][i]) / dy
} else {
(temperature[j][i] - temperature[j - 1][i]) / dy
}
let grad_T_mag = Double::sqrt(dT_dx * dT_dx + dT_dy * dT_dy)
if grad_T_mag > 0.001 {
// 计算速度梯度
let du_dx = if i > 0 && i < nx - 1 {
(u[j][i + 1] - u[j][i - 1]) / (2.0 * dx)
} else if i == 0 {
(u[j][i + 1] - u[j][i]) / dx
} else {
(u[j][i] - u[j][i - 1]) / dx
}
let du_dy = if j > 0 && j < ny - 1 {
(u[j + 1][i] - u[j - 1][i]) / (2.0 * dy)
} else if j == 0 {
(u[j + 1][i] - u[j][i]) / dy
} else {
(u[j][i] - u[j - 1][i]) / dy
}
let dv_dx = if i > 0 && i < nx - 1 {
(v[j][i + 1] - v[j][i - 1]) / (2.0 * dx)
} else if i == 0 {
(v[j][i + 1] - v[j][i]) / dx
} else {
(v[j][i] - v[j][i - 1]) / dx
}
let dv_dy = if j > 0 && j < ny - 1 {
(v[j + 1][i] - v[j - 1][i]) / (2.0 * dy)
} else if j == 0 {
(v[j + 1][i] - v[j][i]) / dy
} else {
(v[j][i] - v[j - 1][i]) / dy
}
// 锋生函数: F = -0.5/|∇T| * [dT/dx * (du/dx * dT/dx + dv/dx * dT/dy) +
// dT/dy * (du/dy * dT/dx + dv/dy * dT/dy)]
let term1 = dT_dx * (du_dx * dT_dx + dv_dx * dT_dy)
let term2 = dT_dy * (du_dy * dT_dx + dv_dy * dT_dy)
fronto[j][i] = -0.5 / grad_T_mag * (term1 + term2)
} else {
fronto[j][i] = 0.0
}
}
}
Some(fronto)
}
}
}