Completed
Pull Request — master (#439)
by Ryan
01:21
created

_lcl_iter()   A

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
1
# Copyright (c) 2008-2015 MetPy Developers.
2
# Distributed under the terms of the BSD 3-Clause License.
3
# SPDX-License-Identifier: BSD-3-Clause
4
"""Contains a collection of thermodynamic calculations."""
5
6
from __future__ import division
7
8
import numpy as np
9
import scipy.integrate as si
10
import scipy.optimize as so
11
12
from .tools import find_intersections
13
from ..constants import Cp_d, epsilon, kappa, Lv, P0, Rd
14
from ..package_tools import Exporter
15
from ..units import atleast_1d, check_units, concatenate, units
16
17
exporter = Exporter(globals())
18
19
sat_pressure_0c = 6.112 * units.millibar
20
21
22
@exporter.export
23
@check_units('[pressure]', '[temperature]')
24
def potential_temperature(pressure, temperature):
25
    r"""Calculate the potential temperature.
26
27
    Uses the Poisson equation to calculation the potential temperature
28
    given `pressure` and `temperature`.
29
30
    Parameters
31
    ----------
32
    pressure : `pint.Quantity`
33
        The total atmospheric pressure
34
    temperature : `pint.Quantity`
35
        The temperature
36
37
    Returns
38
    -------
39
    `pint.Quantity`
40
        The potential temperature corresponding to the the temperature and
41
        pressure.
42
43
    See Also
44
    --------
45
    dry_lapse
46
47
    Notes
48
    -----
49
    Formula:
50
51
    .. math:: \Theta = T (P_0 / P)^\kappa
52
53
    Examples
54
    --------
55
    >>> from metpy.units import units
56
    >>> metpy.calc.potential_temperature(800. * units.mbar, 273. * units.kelvin)
57
    <Quantity(290.96653180346203, 'kelvin')>
58
59
    """
60
    return temperature * (P0 / pressure).to('dimensionless')**kappa
61
62
63
@exporter.export
64
@check_units('[pressure]', '[temperature]')
65
def dry_lapse(pressure, temperature):
66
    r"""Calculate the temperature at a level assuming only dry processes.
67
68
    This function lifts a parcel starting at `temperature`, conserving
69
    potential temperature. The starting pressure should be the first item in
70
    the `pressure` array.
71
72
    Parameters
73
    ----------
74
    pressure : `pint.Quantity`
75
        The atmospheric pressure level(s) of interest
76
    temperature : `pint.Quantity`
77
        The starting temperature
78
79
    Returns
80
    -------
81
    `pint.Quantity`
82
       The resulting parcel temperature at levels given by `pressure`
83
84
    See Also
85
    --------
86
    moist_lapse : Calculate parcel temperature assuming liquid saturation
87
                  processes
88
    parcel_profile : Calculate complete parcel profile
89
    potential_temperature
90
91
    """
92
    return temperature * (pressure / pressure[0])**kappa
93
94
95
@exporter.export
96
@check_units('[pressure]', '[temperature]')
97
def moist_lapse(pressure, temperature):
98
    r"""Calculate the temperature at a level assuming liquid saturation processes.
99
100
    This function lifts a parcel starting at `temperature`. The starting
101
    pressure should be the first item in the `pressure` array. Essentially,
102
    this function is calculating moist pseudo-adiabats.
103
104
    Parameters
105
    ----------
106
    pressure : `pint.Quantity`
107
        The atmospheric pressure level(s) of interest
108
    temperature : `pint.Quantity`
109
        The starting temperature
110
111
    Returns
112
    -------
113
    `pint.Quantity`
114
       The temperature corresponding to the the starting temperature and
115
       pressure levels.
116
117
    See Also
118
    --------
119
    dry_lapse : Calculate parcel temperature assuming dry adiabatic processes
120
    parcel_profile : Calculate complete parcel profile
121
122
    Notes
123
    -----
124
    This function is implemented by integrating the following differential
125
    equation:
126
127
    .. math:: \frac{dT}{dP} = \frac{1}{P} \frac{R_d T + L_v r_s}
128
                                {C_{pd} + \frac{L_v^2 r_s \epsilon}{R_d T^2}}
129
130
    This equation comes from [Bakhshaii2013]_.
131
132
    """
133
    def dt(t, p):
134
        t = units.Quantity(t, temperature.units)
135
        p = units.Quantity(p, pressure.units)
136
        rs = saturation_mixing_ratio(p, t)
137
        frac = ((Rd * t + Lv * rs) /
138
                (Cp_d + (Lv * Lv * rs * epsilon / (Rd * t * t)))).to('kelvin')
139
        return frac / p
140
    return units.Quantity(si.odeint(dt, atleast_1d(temperature).squeeze(),
141
                                    pressure.squeeze()).T.squeeze(), temperature.units)
142
143
144
@exporter.export
145
@check_units('[pressure]', '[temperature]', '[temperature]')
146
def lcl(pressure, temperature, dewpt, max_iters=50, eps=1e-5):
147
    r"""Calculate the lifted condensation level (LCL) using from the starting point.
148
149
    The starting state for the parcel is defined by `temperature`, `dewpt`,
150
    and `pressure`.
151
152
    Parameters
153
    ----------
154
    pressure : `pint.Quantity`
155
        The starting atmospheric pressure
156
    temperature : `pint.Quantity`
157
        The starting temperature
158
    dewpt : `pint.Quantity`
159
        The starting dew point
160
161
    Returns
162
    -------
163
    `(pint.Quantity, pint.Quantity)`
164
        The LCL pressure and temperature
165
166
    Other Parameters
167
    ----------------
168
    max_iters : int, optional
169
        The maximum number of iterations to use in calculation, defaults to 50.
170
    eps : float, optional
171
        The desired relative error in the calculated value, defaults to 1e-5.
172
173
    See Also
174
    --------
175
    parcel_profile
176
177
    Notes
178
    -----
179
    This function is implemented using an iterative approach to solve for the
180
    LCL. The basic algorithm is:
181
182
    1. Find the dew point from the LCL pressure and starting mixing ratio
183
    2. Find the LCL pressure from the starting temperature and dewpoint
184
    3. Iterate until convergence
185
186
    The function is guaranteed to finish by virtue of the `max_iters` counter.
187
188
    """
189
    def _lcl_iter(p, p0, w, t):
190
        td = dewpoint(vapor_pressure(units.Quantity(p, pressure.units), w))
191
        return (p0 * (td / t) ** (1. / kappa)).m
192
193
    w = mixing_ratio(saturation_vapor_pressure(dewpt), pressure)
194
    fp = so.fixed_point(_lcl_iter, pressure.m, args=(pressure.m, w, temperature),
195
                        xtol=eps, maxiter=max_iters, method='del2')
196
    lcl_p = units.Quantity(fp, pressure.units)
197
    return lcl_p, dewpoint(vapor_pressure(lcl_p, w))
198
199
200
@exporter.export
201
@check_units('[pressure]', '[temperature]', '[temperature]')
202 View Code Duplication
def lfc(pressure, temperature, dewpt):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
203
    r"""Calculate the level of free convection (LFC).
204
205
    This works by finding the first intersection of the ideal parcel path and
206
    the measured parcel temperature.
207
208
    Parameters
209
    ----------
210
    pressure : `pint.Quantity`
211
        The atmospheric pressure
212
    temperature : `pint.Quantity`
213
        The temperature at the levels given by `pressure`
214
    dewpt : `pint.Quantity`
215
        The dew point at the levels given by `pressure`
216
217
    Returns
218
    -------
219
    `pint.Quantity`
220
        The LFC pressure and temperature
221
222
    See Also
223
    --------
224
    parcel_profile
225
226
    """
227
    ideal_profile = parcel_profile(pressure, temperature[0], dewpt[0]).to('degC')
228
229
    # The parcel profile and data have the same first data point, so we ignore
230
    # that point to get the real first intersection for the LFC calculation.
231
    x, y = find_intersections(pressure[1:], ideal_profile[1:], temperature[1:],
232
                              direction='increasing')
233
    if len(x) == 0:
234
        return np.nan * pressure.units, np.nan * temperature.units
235
    else:
236
        return x[0], y[0]
237
238
239
@exporter.export
240 View Code Duplication
@check_units('[pressure]', '[temperature]', '[temperature]')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
241
def el(pressure, temperature, dewpt):
242
    r"""Calculate the equilibrium level.
243
244
    This works by finding the last intersection of the ideal parcel path and
245
    the measured environmental temperature. If there is one or fewer intersections, there is
246
    no equilibrium level.
247
248
    Parameters
249
    ----------
250
    pressure : `pint.Quantity`
251
        The atmospheric pressure
252
    temperature : `pint.Quantity`
253
        The temperature at the levels given by `pressure`
254
    dewpt : `pint.Quantity`
255
        The dew point at the levels given by `pressure`
256
257
    Returns
258
    -------
259
    `pint.Quantity, pint.Quantity`
260
        The EL pressure and temperature
261
262
    See Also
263
    --------
264
    parcel_profile
265
266
    """
267
    ideal_profile = parcel_profile(pressure, temperature[0], dewpt[0]).to('degC')
268
    x, y = find_intersections(pressure[1:], ideal_profile[1:], temperature[1:])
269
270
    # If there is only one intersection, it's the LFC and we return None.
271
    if len(x) <= 1:
272
        return np.nan * pressure.units, np.nan * temperature.units
273
    else:
274
        return x[-1], y[-1]
275
276
277
@exporter.export
278
@check_units('[pressure]', '[temperature]', '[temperature]')
279
def parcel_profile(pressure, temperature, dewpt):
280
    r"""Calculate the profile a parcel takes through the atmosphere.
281
282
    The parcel starts at `temperature`, and `dewpt`, lifted up
283
    dry adiabatically to the LCL, and then moist adiabatically from there.
284
    `pressure` specifies the pressure levels for the profile.
285
286
    Parameters
287
    ----------
288
    pressure : `pint.Quantity`
289
        The atmospheric pressure level(s) of interest. The first entry should be the starting
290
        point pressure.
291
    temperature : `pint.Quantity`
292
        The starting temperature
293
    dewpt : `pint.Quantity`
294
        The starting dew point
295
296
    Returns
297
    -------
298
    `pint.Quantity`
299
        The parcel temperatures at the specified pressure levels.
300
301
    See Also
302
    --------
303
    lcl, moist_lapse, dry_lapse
304
305
    """
306
    # Find the LCL
307
    l = lcl(pressure[0], temperature, dewpt)[0].to(pressure.units)
308
309
    # Find the dry adiabatic profile, *including* the LCL. We need >= the LCL in case the
310
    # LCL is included in the levels. It's slightly redundant in that case, but simplifies
311
    # the logic for removing it later.
312
    press_lower = concatenate((pressure[pressure >= l], l))
313
    t1 = dry_lapse(press_lower, temperature)
314
315
    # Find moist pseudo-adiabatic profile starting at the LCL
316
    press_upper = concatenate((l, pressure[pressure < l]))
317
    t2 = moist_lapse(press_upper, t1[-1]).to(t1.units)
318
319
    # Return LCL *without* the LCL point
320
    return concatenate((t1[:-1], t2[1:]))
321
322
323
@exporter.export
324
@check_units('[pressure]', '[dimensionless]')
325
def vapor_pressure(pressure, mixing):
326
    r"""Calculate water vapor (partial) pressure.
327
328
    Given total `pressure` and water vapor `mixing` ratio, calculates the
329
    partial pressure of water vapor.
330
331
    Parameters
332
    ----------
333
    pressure : `pint.Quantity`
334
        total atmospheric pressure
335
    mixing : `pint.Quantity`
336
        dimensionless mass mixing ratio
337
338
    Returns
339
    -------
340
    `pint.Quantity`
341
        The ambient water vapor (partial) pressure in the same units as
342
        `pressure`.
343
344
    Notes
345
    -----
346
    This function is a straightforward implementation of the equation given in many places,
347
    such as [Hobbs1977]_ pg.71:
348
349
    .. math:: e = p \frac{r}{r + \epsilon}
350
351
    See Also
352
    --------
353
    saturation_vapor_pressure, dewpoint
354
355
    """
356
    return pressure * mixing / (epsilon + mixing)
357
358
359
@exporter.export
360
@check_units('[temperature]')
361
def saturation_vapor_pressure(temperature):
362
    r"""Calculate the saturation water vapor (partial) pressure.
363
364
    Parameters
365
    ----------
366
    temperature : `pint.Quantity`
367
        The temperature
368
369
    Returns
370
    -------
371
    `pint.Quantity`
372
        The saturation water vapor (partial) pressure
373
374
    See Also
375
    --------
376
    vapor_pressure, dewpoint
377
378
    Notes
379
    -----
380
    Instead of temperature, dewpoint may be used in order to calculate
381
    the actual (ambient) water vapor (partial) pressure.
382
383
    The formula used is that from [Bolton1980]_ for T in degrees Celsius:
384
385
    .. math:: 6.112 e^\frac{17.67T}{T + 243.5}
386
387
    """
388
    # Converted from original in terms of C to use kelvin. Using raw absolute values of C in
389
    # a formula plays havoc with units support.
390
    return sat_pressure_0c * np.exp(17.67 * (temperature - 273.15 * units.kelvin) /
391
                                    (temperature - 29.65 * units.kelvin))
392
393
394
@exporter.export
395
@check_units('[temperature]', '[dimensionless]')
396
def dewpoint_rh(temperature, rh):
397
    r"""Calculate the ambient dewpoint given air temperature and relative humidity.
398
399
    Parameters
400
    ----------
401
    temperature : `pint.Quantity`
402
        Air temperature
403
    rh : `pint.Quantity`
404
        Relative humidity expressed as a ratio in the range [0, 1]
405
406
    Returns
407
    -------
408
    `pint.Quantity`
409
        The dew point temperature
410
411
    See Also
412
    --------
413
    dewpoint, saturation_vapor_pressure
414
415
    """
416
    return dewpoint(rh * saturation_vapor_pressure(temperature))
417
418
419
@exporter.export
420
@check_units('[pressure]')
421
def dewpoint(e):
422
    r"""Calculate the ambient dewpoint given the vapor pressure.
423
424
    Parameters
425
    ----------
426
    e : `pint.Quantity`
427
        Water vapor partial pressure
428
429
    Returns
430
    -------
431
    `pint.Quantity`
432
        Dew point temperature
433
434
    See Also
435
    --------
436
    dewpoint_rh, saturation_vapor_pressure, vapor_pressure
437
438
    Notes
439
    -----
440
    This function inverts the [Bolton1980]_ formula for saturation vapor
441
    pressure to instead calculate the temperature. This yield the following
442
    formula for dewpoint in degrees Celsius:
443
444
    .. math:: T = \frac{243.5 log(e / 6.112)}{17.67 - log(e / 6.112)}
445
446
    """
447
    val = np.log(e / sat_pressure_0c)
448
    return 0. * units.degC + 243.5 * units.delta_degC * val / (17.67 - val)
449
450
451
@exporter.export
452
@check_units('[pressure]', '[pressure]', '[dimensionless]')
453
def mixing_ratio(part_press, tot_press, molecular_weight_ratio=epsilon):
454
    r"""Calculate the mixing ratio of a gas.
455
456
    This calculates mixing ratio given its partial pressure and the total pressure of
457
    the air. There are no required units for the input arrays, other than that
458
    they have the same units.
459
460
    Parameters
461
    ----------
462
    part_press : `pint.Quantity`
463
        Partial pressure of the constituent gas
464
    tot_press : `pint.Quantity`
465
        Total air pressure
466
    molecular_weight_ratio : `pint.Quantity` or float, optional
467
        The ratio of the molecular weight of the constituent gas to that assumed
468
        for air. Defaults to the ratio for water vapor to dry air
469
        (:math:`\epsilon\approx0.622`).
470
471
    Returns
472
    -------
473
    `pint.Quantity`
474
        The (mass) mixing ratio, dimensionless (e.g. Kg/Kg or g/g)
475
476
    Notes
477
    -----
478
    This function is a straightforward implementation of the equation given in many places,
479
    such as [Hobbs1977]_ pg.73:
480
481
    .. math:: r = \epsilon \frac{e}{p - e}
482
483
    See Also
484
    --------
485
    saturation_mixing_ratio, vapor_pressure
486
487
    """
488
    return molecular_weight_ratio * part_press / (tot_press - part_press)
489
490
491
@exporter.export
492
@check_units('[pressure]', '[temperature]')
493
def saturation_mixing_ratio(tot_press, temperature):
494
    r"""Calculate the saturation mixing ratio of water vapor.
495
496
    This calculation is given total pressure and the temperature. The implementation
497
    uses the formula outlined in [Hobbs1977]_ pg.73.
498
499
    Parameters
500
    ----------
501
    tot_press: `pint.Quantity`
502
        Total atmospheric pressure
503
    temperature: `pint.Quantity`
504
        The temperature
505
506
    Returns
507
    -------
508
    `pint.Quantity`
509
        The saturation mixing ratio, dimensionless
510
511
    """
512
    return mixing_ratio(saturation_vapor_pressure(temperature), tot_press)
513
514
515
@exporter.export
516
@check_units('[pressure]', '[temperature]')
517
def equivalent_potential_temperature(pressure, temperature):
518
    r"""Calculate equivalent potential temperature.
519
520
    This calculation must be given an air parcel's pressure and temperature.
521
    The implementation uses the formula outlined in [Hobbs1977]_ pg.78-79.
522
523
    Parameters
524
    ----------
525
    pressure: `pint.Quantity`
526
        Total atmospheric pressure
527
    temperature: `pint.Quantity`
528
        The temperature
529
530
    Returns
531
    -------
532
    `pint.Quantity`
533
        The corresponding equivalent potential temperature of the parcel
534
535
    Notes
536
    -----
537
    .. math:: \Theta_e = \Theta e^\frac{L_v r_s}{C_{pd} T}
538
539
    """
540
    pottemp = potential_temperature(pressure, temperature)
541
    smixr = saturation_mixing_ratio(pressure, temperature)
542
    return pottemp * np.exp(Lv * smixr / (Cp_d * temperature))
543
544
545
@exporter.export
546
@check_units('[temperature]', '[dimensionless]', '[dimensionless]')
547
def virtual_temperature(temperature, mixing, molecular_weight_ratio=epsilon):
548
    r"""Calculate virtual temperature.
549
550
    This calculation must be given an air parcel's temperature and mixing ratio.
551
    The implementation uses the formula outlined in [Hobbs2006]_ pg.80.
552
553
    Parameters
554
    ----------
555
    temperature: `pint.Quantity`
556
        The temperature
557
    mixing : `pint.Quantity`
558
        dimensionless mass mixing ratio
559
    molecular_weight_ratio : `pint.Quantity` or float, optional
560
        The ratio of the molecular weight of the constituent gas to that assumed
561
        for air. Defaults to the ratio for water vapor to dry air
562
        (:math:`\epsilon\approx0.622`).
563
564
    Returns
565
    -------
566
    `pint.Quantity`
567
        The corresponding virtual temperature of the parcel
568
569
    Notes
570
    -----
571
    .. math:: T_v = T \frac{\text{w} + \epsilon}{\epsilon\,(1 + \text{w})}
572
573
    """
574
    return temperature * ((mixing + molecular_weight_ratio) /
575
                          (molecular_weight_ratio * (1 + mixing)))
576
577
578
@exporter.export
579
@check_units('[pressure]', '[temperature]', '[dimensionless]', '[dimensionless]')
580
def virtual_potential_temperature(pressure, temperature, mixing,
581
                                  molecular_weight_ratio=epsilon):
582
    r"""Calculate virtual potential temperature.
583
584
    This calculation must be given an air parcel's pressure, temperature, and mixing ratio.
585
    The implementation uses the formula outlined in [Markowski2010]_ pg.13.
586
587
    Parameters
588
    ----------
589
    pressure: `pint.Quantity`
590
        Total atmospheric pressure
591
    temperature: `pint.Quantity`
592
        The temperature
593
    mixing : `pint.Quantity`
594
        dimensionless mass mixing ratio
595
    molecular_weight_ratio : `pint.Quantity` or float, optional
596
        The ratio of the molecular weight of the constituent gas to that assumed
597
        for air. Defaults to the ratio for water vapor to dry air
598
        (:math:`\epsilon\approx0.622`).
599
600
    Returns
601
    -------
602
    `pint.Quantity`
603
        The corresponding virtual potential temperature of the parcel
604
605
    Notes
606
    -----
607
    .. math:: \Theta_v = \Theta \frac{\text{w} + \epsilon}{\epsilon\,(1 + \text{w})}
608
609
    """
610
    pottemp = potential_temperature(pressure, temperature)
611
    return virtual_temperature(pottemp, mixing, molecular_weight_ratio)
612
613
614
@exporter.export
615
@check_units('[pressure]', '[temperature]', '[dimensionless]', '[dimensionless]')
616
def density(pressure, temperature, mixing, molecular_weight_ratio=epsilon):
617
    r"""Calculate density.
618
619
    This calculation must be given an air parcel's pressure, temperature, and mixing ratio.
620
    The implementation uses the formula outlined in [Hobbs2006]_ pg.67.
621
622
    Parameters
623
    ----------
624
    temperature: `pint.Quantity`
625
        The temperature
626
    pressure: `pint.Quantity`
627
        Total atmospheric pressure
628
    mixing : `pint.Quantity`
629
        dimensionless mass mixing ratio
630
    molecular_weight_ratio : `pint.Quantity` or float, optional
631
        The ratio of the molecular weight of the constituent gas to that assumed
632
        for air. Defaults to the ratio for water vapor to dry air
633
        (:math:`\epsilon\approx0.622`).
634
635
    Returns
636
    -------
637
    `pint.Quantity`
638
        The corresponding density of the parcel
639
640
    Notes
641
    -----
642
    .. math:: \rho = \frac{p}{R_dT_v}
643
644
    """
645
    virttemp = virtual_temperature(temperature, mixing, molecular_weight_ratio)
646
    return (pressure / (Rd * virttemp)).to(units.kilogram / units.meter ** 3)
647
648
649
@exporter.export
650
@check_units('[temperature]', '[temperature]', '[pressure]')
651
def relative_humidity_wet_psychrometric(dry_bulb_temperature, web_bulb_temperature,
652
                                        pressure, **kwargs):
653
    r"""Calculate the relative humidity with wet bulb and dry bulb temperatures.
654
655
    This uses a psychrometric relationship as outlined in [WMO8-2008]_, with
656
    coefficients from [Fan1987]_.
657
658
    Parameters
659
    ----------
660
    dry_bulb_temperature: `pint.Quantity`
661
        Dry bulb temperature
662
    web_bulb_temperature: `pint.Quantity`
663
        Wet bulb temperature
664
    pressure: `pint.Quantity`
665
        Total atmospheric pressure
666
667
    Returns
668
    -------
669
    `pint.Quantity`
670
        Relative humidity
671
672
    Notes
673
    -----
674
    .. math:: RH = 100 \frac{e}{e_s}
675
676
    * :math:`RH` is relative humidity
677
    * :math:`e` is vapor pressure from the wet psychrometric calculation
678
    * :math:`e_s` is the saturation vapor pressure
679
680
    See Also
681
    --------
682
    psychrometric_vapor_pressure_wet, saturation_vapor_pressure
683
684
    """
685
    return (100 * units.percent * psychrometric_vapor_pressure_wet(dry_bulb_temperature,
686
            web_bulb_temperature, pressure, **kwargs) /
687
            saturation_vapor_pressure(dry_bulb_temperature))
688
689
690
@exporter.export
691
@check_units('[temperature]', '[temperature]', '[pressure]')
692
def psychrometric_vapor_pressure_wet(dry_bulb_temperature, wet_bulb_temperature, pressure,
693
                                     psychrometer_coefficient=6.21e-4 / units.kelvin):
694
    r"""Calculate the vapor pressure with wet bulb and dry bulb temperatures.
695
696
    This uses a psychrometric relationship as outlined in [WMO8-2008]_, with
697
    coefficients from [Fan1987]_.
698
699
    Parameters
700
    ----------
701
    dry_bulb_temperature: `pint.Quantity`
702
        Dry bulb temperature
703
    wet_bulb_temperature: `pint.Quantity`
704
        Wet bulb temperature
705
    pressure: `pint.Quantity`
706
        Total atmospheric pressure
707
    psychrometer_coefficient: `pint.Quantity`
708
        Psychrometer coefficient
709
710
    Returns
711
    -------
712
    `pint.Quantity`
713
        Vapor pressure
714
715
    Notes
716
    -----
717
    .. math:: e' = e'_w(T_w) - A p (T - T_w)
718
719
    * :math:`e'` is vapor pressure
720
    * :math:`e'_w(T_w)` is the saturation vapor pressure with respect to water at temperature
721
      :math:`T_w`
722
    * :math:`p` is the pressure of the wet bulb
723
    * :math:`T` is the temperature of the dry bulb
724
    * :math:`T_w` is the temperature of the wet bulb
725
    * :math:`A` is the psychrometer coefficient
726
727
    Psychrometer coefficient depends on the specific instrument being used and the ventilation
728
    of the instrument.
729
730
    See Also
731
    --------
732
    saturation_vapor_pressure
733
734
    """
735
    return (saturation_vapor_pressure(wet_bulb_temperature) - psychrometer_coefficient *
736
            pressure * (dry_bulb_temperature - wet_bulb_temperature).to('kelvin'))
737