diff --git a/lib/xmet/skew_t.py b/lib/xmet/skew_t.py index 92807c0..9045e4e 100644 --- a/lib/xmet/skew_t.py +++ b/lib/xmet/skew_t.py @@ -180,92 +180,6 @@ class SkewTGraph(): cr.stroke() 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, cr: cairo.Context, x: float, diff --git a/lib/xmet/sounding.py b/lib/xmet/sounding.py index fc120db..44fdb7f 100644 --- a/lib/xmet/sounding.py +++ b/lib/xmet/sounding.py @@ -1,8 +1,13 @@ import datetime import shapely -from xmet.db import Database, DatabaseTable, DatabaseOrder -from xmet.coord import COORD_SYSTEM +from xmet.db import Database, DatabaseTable, DatabaseOrder +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_MOIST = 4.0 @@ -42,6 +47,9 @@ class SoundingSample(DatabaseTable): def is_saturated(self) -> bool: return self.humidity >= 100.0 +class SoundingParameters(): + pass + class Sounding(DatabaseTable): __slots__ = ( 'id', 'station', 'timestamp_observed', 'timestamp_released', @@ -168,3 +176,40 @@ class Sounding(DatabaseTable): ]]).fetchall()) 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