aboutsummaryrefslogtreecommitdiff
path: root/skewt.py
blob: e674d09ae69e53189c724b806f5cd4b1acb8e740 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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
import matplotlib.gridspec as gridspec
import matplotlib.pyplot as plt

import numpy as np

import metpy.calc as mpcalc
from metpy.cbook import get_test_data
from metpy.plots import add_metpy_logo, Hodograph, SkewT
from metpy.units import units

class Skewt:
    def __init__(self, p, T, Td, max_barbs=20, title=None):
        self._p = p
        self._T = T
        self._Td = Td

        self._info_lines = []

        self.barb_div = int(max(len(p)/max_barbs,1))


        # Create a new figure. The dimensions here give a good aspect ratio
        self._fig = plt.figure(figsize=(9, 9))
        plt.rcParams["font.family"] = "monospace"
        #self._fig = plt.figure()

        if title is not None:
            plt.suptitle(title, x=0, y=0, va='bottom', ha='left')

        # Grid for plots
        self._gs = gridspec.GridSpec(3, 3)
        self._skew = SkewT(self._fig, rotation=45, subplot=self._gs[:, :2])

        # Plot the data using normal plotting functions, in this case using
        # log scaling in Y, as dictated by the typical meteorological plot
        self._skew.plot(p, T, 'r')
        self._skew.plot(p, Td, 'b')

        plt.xlabel('$T$ $[^o C]$')
        plt.ylabel('$p$ $[hPa]$')

    def addWindUV(self, u, v):
        self._u = u
        self._v = v

        ax = self._fig.add_subplot(self._gs[0, -1])
        h = Hodograph(ax, component_range=max(u + v).magnitude)
        h.add_grid(increment=20)
        h.plot_colormapped(u, v, self._p)
        plt.tight_layout()
        plt.xlabel('$m/s$')
        plt.ylabel('$m/s$')

        self._skew.plot_barbs(self._p[::self.barb_div], u[::self.barb_div], v[::self.barb_div])

    def addInfo(self, line):
        self._info_lines.append(line)

    def addAnalysis(self, analysis='ccl', shade=False):
        f = {'ccl': self._cclAnalysis, 'lcl': self._lclAnalysis}

        lvl, parcel = f[analysis]()

        self._skew.plot(self._p, parcel, 'y')
        self._skew.plot(lvl[0], lvl[1], 'o', markerfacecolor='red', linewidth=1)

        # TODO why exception on cape_cin()?
        # ValueError: zero-size array to reduction operation minimum which has no identity
        # https://github.com/Unidata/MetPy/pull/3132
        try:
            cape, cin = mpcalc.cape_cin(self._p, self._T, self._Td, parcel, which_el='top')
            self.addInfo(f'CAPE {int(cape.magnitude)} $J/kg$ CIN {int(cin.magnitude)} $J/kg$')
        except ValueError:
            print('CAPE/CIN Failed with ValueError')
            self.addInfo('CAPE #### CIN ####')

        if shade:
            self._skew.shade_cape(self._p,self._T,parcel)
            self._skew.shade_cin(self._p,self._T,parcel)

    def _cclAnalysis(self):
        #p = np.arange(max(self._p).magnitude, min(self._p).magnitude, -50) * units.hPa

        ccl = mpcalc.ccl(self._p,self._T,self._Td)
        ccl_ground=mpcalc.dry_lapse(self._p[:1], ccl[1], ccl[0])
        ccl_ground_parcel= mpcalc.parcel_profile(self._p, ccl_ground[0], self._Td[0])

        return (ccl, ccl_ground_parcel)

    def _lclAnalysis(self):
        ground_parcel= mpcalc.parcel_profile(self._p, self._T[0], self._Td[0])
        lcl = mpcalc.lcl(self._p[0],self._T[0],self._Td[0])

        return (lcl, ground_parcel)

    def _buildInfoBox(self):
        ax = self._fig.add_subplot(self._gs[1,-1])
        ax.text(0, 0, '\n'.join(self._info_lines), ha='left', va='center',
                size=10, fontfamily='monospace')
        ax.axis("off")

    def plot(self, filename=None):
        self._buildInfoBox()

        # Add the relevant special lines
        #self._skew.ax.set_ylim(max(self._p), min(self._p))
        self._skew.ax.set_ylim(1000, 100)
        self._skew.plot_dry_adiabats(linewidth=1)
        self._skew.plot_moist_adiabats(linewidth=1)
        self._skew.plot_mixing_lines(linewidth=1)

        # Good bounds for aspect ratio
        self._skew.ax.set_xlim(-30, 40)

        if filename is not None:
            plt.savefig(filename)
        else:
            plt.show()