Compare commits
3 commits
b3eeeab4d7
...
53ea99803e
Author | SHA1 | Date | |
---|---|---|---|
53ea99803e | |||
50cdf4052d | |||
9be71c7e44 |
3 changed files with 87 additions and 91 deletions
|
@ -180,92 +180,6 @@ class SkewTGraph():
|
||||||
cr.stroke()
|
cr.stroke()
|
||||||
cr.restore()
|
cr.restore()
|
||||||
|
|
||||||
def follow_dry_adiabat(self, temp: float, pressure: float) -> dict:
|
|
||||||
levels = dict()
|
|
||||||
|
|
||||||
for level in loft_parcel(temp, pressure, lambda t, p: LAPSE_RATE_DRY):
|
|
||||||
t2, p2 = level
|
|
||||||
|
|
||||||
levels[p2] = t2
|
|
||||||
|
|
||||||
return levels
|
|
||||||
|
|
||||||
def follow_moist_adiabat(self, temp: float, pressure: float) -> dict:
|
|
||||||
levels = dict()
|
|
||||||
|
|
||||||
for level in loft_parcel(temp, pressure, moist_lapse_rate):
|
|
||||||
t2, p2 = level
|
|
||||||
|
|
||||||
levels[p2] = t2
|
|
||||||
|
|
||||||
return levels
|
|
||||||
|
|
||||||
def follow_saturated_mixing_ratio(self, temp: float, pressure: float) -> dict:
|
|
||||||
levels = dict()
|
|
||||||
|
|
||||||
ratio = saturated_mixing_ratio(temp, pressure)
|
|
||||||
|
|
||||||
p2 = pressure
|
|
||||||
|
|
||||||
while p2 >= PRESSURE_MIN:
|
|
||||||
levels[p2] = mixing_ratio_temp(ratio, p2)
|
|
||||||
|
|
||||||
p2 -= 10
|
|
||||||
|
|
||||||
return levels
|
|
||||||
|
|
||||||
def follow_sounding(self, sounding: Sounding) -> dict:
|
|
||||||
levels = dict()
|
|
||||||
|
|
||||||
for sample in sounding.samples:
|
|
||||||
levels[sample.pressure] = sample.temp
|
|
||||||
|
|
||||||
return levels
|
|
||||||
|
|
||||||
def intersect(self, series1: dict, series2: dict):
|
|
||||||
pairs = nearest(sorted(series1.keys(), reverse=True),
|
|
||||||
sorted(series2.keys(), reverse=True))
|
|
||||||
|
|
||||||
sign_last = None
|
|
||||||
|
|
||||||
for pair in pairs:
|
|
||||||
v1, v2 = series1[pair[0]], series2[pair[1]]
|
|
||||||
|
|
||||||
sign = cmp(v1, v2)
|
|
||||||
|
|
||||||
if sign == 0 or (sign_last is not None and sign_last != sign):
|
|
||||||
return pair[0]
|
|
||||||
|
|
||||||
sign_last = sign
|
|
||||||
|
|
||||||
def find_lfc(self, temp: float, pressure: float, sounding: Sounding) -> float:
|
|
||||||
moist_adiabat = self.follow_moist_adiabat(temp, pressure)
|
|
||||||
temp_line = self.follow_sounding(sounding)
|
|
||||||
|
|
||||||
pairs = nearest(sorted(moist_adiabat.keys(), reverse=True),
|
|
||||||
sorted(temp_line.keys(), reverse=True))
|
|
||||||
|
|
||||||
for pair in pairs:
|
|
||||||
v1, v2 = moist_adiabat[pair[0]], temp_line[pair[1]]
|
|
||||||
|
|
||||||
if v1 > v2:
|
|
||||||
return pair[0]
|
|
||||||
|
|
||||||
def derive_parameters(self, sounding: Sounding):
|
|
||||||
dry_adiabat = self.follow_dry_adiabat(sounding.samples[0].temp,
|
|
||||||
sounding.samples[0].pressure)
|
|
||||||
|
|
||||||
saturated_mr_line = self.follow_saturated_mixing_ratio(sounding.samples[0].dewpoint,
|
|
||||||
sounding.samples[0].pressure)
|
|
||||||
|
|
||||||
lcl = self.intersect(dry_adiabat, saturated_mr_line)
|
|
||||||
lfc = self.find_lfc(dry_adiabat[lcl], lcl, sounding)
|
|
||||||
|
|
||||||
return {
|
|
||||||
'lcl': lcl,
|
|
||||||
'lfc': lfc
|
|
||||||
}
|
|
||||||
|
|
||||||
def draw(self,
|
def draw(self,
|
||||||
cr: cairo.Context,
|
cr: cairo.Context,
|
||||||
x: float,
|
x: float,
|
||||||
|
|
|
@ -3,6 +3,11 @@ import shapely
|
||||||
|
|
||||||
from xmet.db import Database, DatabaseTable, DatabaseOrder
|
from xmet.db import Database, DatabaseTable, DatabaseOrder
|
||||||
from xmet.coord import COORD_SYSTEM
|
from xmet.coord import COORD_SYSTEM
|
||||||
|
from xmet.list import nearest
|
||||||
|
from xmet.series import Series
|
||||||
|
from xmet.thermo import follow_dry_adiabat, \
|
||||||
|
follow_moist_adiabat, \
|
||||||
|
follow_saturated_mixing_ratio
|
||||||
|
|
||||||
LAPSE_RATE_DRY = 9.8 # degrees C per 1000m
|
LAPSE_RATE_DRY = 9.8 # degrees C per 1000m
|
||||||
LAPSE_RATE_MOIST = 4.0
|
LAPSE_RATE_MOIST = 4.0
|
||||||
|
@ -42,6 +47,9 @@ class SoundingSample(DatabaseTable):
|
||||||
def is_saturated(self) -> bool:
|
def is_saturated(self) -> bool:
|
||||||
return self.humidity >= 100.0
|
return self.humidity >= 100.0
|
||||||
|
|
||||||
|
class SoundingParameters():
|
||||||
|
pass
|
||||||
|
|
||||||
class Sounding(DatabaseTable):
|
class Sounding(DatabaseTable):
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'id', 'station', 'timestamp_observed', 'timestamp_released',
|
'id', 'station', 'timestamp_observed', 'timestamp_released',
|
||||||
|
@ -168,3 +176,40 @@ class Sounding(DatabaseTable):
|
||||||
]]).fetchall())
|
]]).fetchall())
|
||||||
|
|
||||||
return sounding
|
return sounding
|
||||||
|
|
||||||
|
def follow_temp(self) -> Series:
|
||||||
|
series = Series()
|
||||||
|
|
||||||
|
for sample in self.samples:
|
||||||
|
series[sample.pressure] = sample.temp
|
||||||
|
|
||||||
|
return series
|
||||||
|
|
||||||
|
def find_lfc(self, temp: float, pressure: float) -> float:
|
||||||
|
moist_adiabat = follow_moist_adiabat(temp, pressure)
|
||||||
|
temp_line = self.follow_temp()
|
||||||
|
|
||||||
|
pairs = nearest(sorted(moist_adiabat.keys(), reverse=True),
|
||||||
|
sorted(temp_line.keys(), reverse=True))
|
||||||
|
|
||||||
|
for pair in pairs:
|
||||||
|
v1, v2 = moist_adiabat[pair[0]], temp_line[pair[1]]
|
||||||
|
|
||||||
|
if v1 > v2:
|
||||||
|
return pair[0]
|
||||||
|
|
||||||
|
def derive_parameters(self) -> SoundingParameters:
|
||||||
|
dry_adiabat = follow_dry_adiabat(self.samples[0].temp,
|
||||||
|
self.samples[0].pressure)
|
||||||
|
|
||||||
|
saturated_mr_line = follow_saturated_mixing_ratio(self.samples[0].dewpoint,
|
||||||
|
self.samples[0].pressure)
|
||||||
|
|
||||||
|
lcl = dry_adiabat.intersect(saturated_mr_line)
|
||||||
|
lfc = self.find_lfc(dry_adiabat[lcl], lcl)
|
||||||
|
|
||||||
|
params = SoundingParameters()
|
||||||
|
params.lcl = lcl
|
||||||
|
params.lfc = lfc
|
||||||
|
|
||||||
|
return params
|
||||||
|
|
|
@ -2,6 +2,8 @@ import math
|
||||||
|
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
|
from xmet.series import Series
|
||||||
|
|
||||||
LAPSE_RATE_DRY = 9.8 / 1000 # degrees C per km
|
LAPSE_RATE_DRY = 9.8 / 1000 # degrees C per km
|
||||||
LAPSE_RATE_MOIST = 4.0 / 1000
|
LAPSE_RATE_MOIST = 4.0 / 1000
|
||||||
|
|
||||||
|
@ -101,7 +103,8 @@ def moist_lapse_rate(temp: float, pressure: float) -> float:
|
||||||
|
|
||||||
def loft_parcel(start_temp: float,
|
def loft_parcel(start_temp: float,
|
||||||
start_pressure: float,
|
start_pressure: float,
|
||||||
lapse_rate: Callable):
|
lapse_rate: Callable,
|
||||||
|
step: float=10.0):
|
||||||
"""
|
"""
|
||||||
Loft a parcel of air from a given pressure, at a given temperature,
|
Loft a parcel of air from a given pressure, at a given temperature,
|
||||||
yielding a Tuple containing the temperature and pressure of that parcel
|
yielding a Tuple containing the temperature and pressure of that parcel
|
||||||
|
@ -136,7 +139,41 @@ def loft_parcel(start_temp: float,
|
||||||
|
|
||||||
if pressure == PRESSURE_MIN:
|
if pressure == PRESSURE_MIN:
|
||||||
break
|
break
|
||||||
elif pressure - 10.0 < PRESSURE_MIN:
|
elif pressure - step < PRESSURE_MIN:
|
||||||
pressure = PRESSURE_MIN
|
pressure = PRESSURE_MIN
|
||||||
else:
|
else:
|
||||||
pressure -= 10.0
|
pressure -= step
|
||||||
|
|
||||||
|
def follow_dry_adiabat(temp: float, pressure: float) -> Series:
|
||||||
|
series = Series()
|
||||||
|
|
||||||
|
for level in loft_parcel(temp, pressure, lambda t, p: LAPSE_RATE_DRY):
|
||||||
|
t2, p2 = level
|
||||||
|
|
||||||
|
series[p2] = t2
|
||||||
|
|
||||||
|
return series
|
||||||
|
|
||||||
|
def follow_moist_adiabat(temp: float, pressure: float) -> Series:
|
||||||
|
series = Series()
|
||||||
|
|
||||||
|
for level in loft_parcel(temp, pressure, moist_lapse_rate):
|
||||||
|
t2, p2 = level
|
||||||
|
|
||||||
|
series[p2] = t2
|
||||||
|
|
||||||
|
return series
|
||||||
|
|
||||||
|
def follow_saturated_mixing_ratio(temp: float, pressure: float) -> Series:
|
||||||
|
series = dict()
|
||||||
|
|
||||||
|
ratio = saturated_mixing_ratio(temp, pressure)
|
||||||
|
|
||||||
|
p2 = pressure
|
||||||
|
|
||||||
|
while p2 >= PRESSURE_MIN:
|
||||||
|
series[p2] = mixing_ratio_temp(ratio, p2)
|
||||||
|
|
||||||
|
p2 -= 10
|
||||||
|
|
||||||
|
return series
|
||||||
|
|
Loading…
Add table
Reference in a new issue