-
Notifications
You must be signed in to change notification settings - Fork 12
/
saturation.F90
832 lines (618 loc) · 28.1 KB
/
saturation.F90
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
!-------------------------------------------------------------------------
!$Id$
!===============================================================================
module saturation
! Description:
! Contains functions that compute saturation with respect
! to liquid or ice.
!-----------------------------------------------------------------------
#ifdef GFDL
use model_flags, only: & ! h1g, 2010-06-18
I_sat_sphum
#endif
use clubb_precision, only: &
core_rknd ! Variable(s)
implicit none
private ! Change default so all items private
public :: sat_mixrat_liq, sat_mixrat_liq_lookup, sat_mixrat_ice, rcm_sat_adj, &
sat_vapor_press_liq
private :: sat_vapor_press_liq_flatau, sat_vapor_press_liq_bolton
private :: sat_vapor_press_ice_flatau, sat_vapor_press_ice_bolton
! Lookup table of values for saturation
real( kind = core_rknd ), private, dimension(188:343) :: &
svp_liq_lookup_table
data svp_liq_lookup_table(188:343) / &
0.049560547_core_rknd, 0.059753418_core_rknd, 0.070129395_core_rknd, 0.083618164_core_rknd, &
0.09814453_core_rknd, 0.11444092_core_rknd, 0.13446045_core_rknd, 0.15686035_core_rknd, &
0.18218994_core_rknd, 0.21240234_core_rknd, 0.24725342_core_rknd, 0.28668213_core_rknd, &
0.33184814_core_rknd, 0.3826294_core_rknd, 0.4416504_core_rknd, 0.50775146_core_rknd, &
0.58343506_core_rknd, 0.6694946_core_rknd, 0.7668457_core_rknd, 0.87750244_core_rknd, &
1.0023804_core_rknd, 1.1434937_core_rknd, 1.3028564_core_rknd, 1.482544_core_rknd, &
1.6847534_core_rknd, 1.9118042_core_rknd, 2.1671143_core_rknd, 2.4535522_core_rknd, &
2.774231_core_rknd, 3.1330566_core_rknd, 3.5343628_core_rknd, 3.9819336_core_rknd, &
4.480713_core_rknd, 5.036072_core_rknd, 5.6540527_core_rknd, 6.340088_core_rknd, &
7.1015015_core_rknd, 7.9450684_core_rknd, 8.8793335_core_rknd, 9.91217_core_rknd, &
11.053528_core_rknd, 12.313049_core_rknd, 13.70166_core_rknd, 15.231018_core_rknd, &
16.91394_core_rknd, 18.764038_core_rknd, 20.795898_core_rknd, 23.025574_core_rknd, &
25.470093_core_rknd, 28.147766_core_rknd, 31.078003_core_rknd, 34.282043_core_rknd, &
37.782593_core_rknd, 41.60382_core_rknd, 45.771606_core_rknd, 50.31366_core_rknd, &
55.259644_core_rknd, 60.641174_core_rknd, 66.492004_core_rknd, 72.84802_core_rknd, &
79.74756_core_rknd, 87.23126_core_rknd, 95.34259_core_rknd, 104.12747_core_rknd, &
113.634796_core_rknd, 123.91641_core_rknd, 135.02725_core_rknd, 147.02563_core_rknd, &
159.97308_core_rknd, 173.93488_core_rknd, 188.97995_core_rknd, 205.18109_core_rknd, &
222.61517_core_rknd, 241.36334_core_rknd, 261.51108_core_rknd, 283.14853_core_rknd, &
306.37054_core_rknd, 331.27698_core_rknd, 357.97278_core_rknd, 386.56842_core_rknd, &
417.17978_core_rknd, 449.9286_core_rknd, 484.94254_core_rknd, 522.3556_core_rknd, &
562.30804_core_rknd, 604.947_core_rknd, 650.42645_core_rknd, 698.9074_core_rknd, &
750.55835_core_rknd, 805.55554_core_rknd, 864.0828_core_rknd, 926.3325_core_rknd, &
992.5052_core_rknd, 1062.8102_core_rknd, 1137.4657_core_rknd, 1216.6995_core_rknd, &
1300.7483_core_rknd, 1389.8594_core_rknd, 1484.2896_core_rknd, 1584.3064_core_rknd, &
1690.1881_core_rknd, 1802.224_core_rknd, 1920.7146_core_rknd, 2045.9724_core_rknd, &
2178.3218_core_rknd, 2318.099_core_rknd, 2465.654_core_rknd, 2621.3489_core_rknd, &
2785.5596_core_rknd, 2958.6758_core_rknd, 3141.101_core_rknd, 3333.2534_core_rknd, &
3535.5657_core_rknd, 3748.4863_core_rknd, 3972.4792_core_rknd, 4208.024_core_rknd, &
4455.616_core_rknd, 4715.7686_core_rknd, 4989.0127_core_rknd, 5275.8945_core_rknd, &
5576.9795_core_rknd, 5892.8535_core_rknd, 6224.116_core_rknd, 6571.3926_core_rknd, &
6935.3213_core_rknd, 7316.5674_core_rknd, 7715.8105_core_rknd, 8133.755_core_rknd, &
8571.125_core_rknd, 9028.667_core_rknd, 9507.15_core_rknd, 10007.367_core_rknd, &
10530.132_core_rknd, 11076.282_core_rknd, 11646.683_core_rknd, 12242.221_core_rknd, &
12863.808_core_rknd, 13512.384_core_rknd, 14188.913_core_rknd, 14894.385_core_rknd, &
15629.823_core_rknd, 16396.268_core_rknd, 17194.799_core_rknd, 18026.516_core_rknd, &
18892.55_core_rknd, 19794.07_core_rknd, 20732.262_core_rknd, 21708.352_core_rknd, &
22723.592_core_rknd, 23779.273_core_rknd, 24876.709_core_rknd, 26017.258_core_rknd, &
27202.3_core_rknd, 28433.256_core_rknd, 29711.578_core_rknd, 31038.766_core_rknd /
!$omp threadprivate( svp_liq_lookup_table )
contains
!-------------------------------------------------------------------------
elemental real( kind = core_rknd ) function sat_mixrat_liq( p_in_Pa, T_in_K )
! Description:
! Used to compute the saturation mixing ratio of liquid water.
! References:
! Formula from Emanuel 1994, 4.4.14
!-------------------------------------------------------------------------
use constants_clubb, only: &
ep ! Variable
use clubb_precision, only: &
core_rknd ! Variable(s)
implicit none
! Input Variables
real( kind = core_rknd ), intent(in) :: &
p_in_Pa, & ! Pressure [Pa]
T_in_K ! Temperature [K]
! Local Variables
real( kind = core_rknd ) :: esatv
! --- Begin Code ---
! Calculate the SVP for water vapor.
esatv = sat_vapor_press_liq( T_in_K )
! If esatv exceeds the air pressure, then assume esatv~=0.5*pressure
! and set rsat = ep = 0.622
if ( p_in_Pa-esatv < 1.0_core_rknd ) then
sat_mixrat_liq = ep
else
#ifdef GFDL
! GFDL uses specific humidity
! Formula for Saturation Specific Humidity
if ( I_sat_sphum ) then ! h1g, 2010-06-18 begin mod
sat_mixrat_liq = ep * ( esatv / ( p_in_Pa - (1.0_core_rknd-ep) * esatv ) )
else
sat_mixrat_liq = ep * ( esatv / ( p_in_Pa - esatv ) )
endif ! h1g, 2010-06-18 end mod
#else
! Formula for Saturation Mixing Ratio:
!
! rs = (epsilon) * [ esat / ( p - esat ) ];
! where epsilon = R_d / R_v
sat_mixrat_liq = ep * esatv / ( p_in_Pa - esatv )
#endif
end if
return
end function sat_mixrat_liq
!-------------------------------------------------------------------------
elemental real( kind = core_rknd ) function sat_mixrat_liq_lookup( p_in_Pa, T_in_K )
! Description:
! Used to compute the saturation mixing ratio of liquid water.
! This function utilizes sat_vapor_press_liq_lookup; the SVP is found
! using a lookup table rather than calculating it using various
! approximations.
! References:
! Formula from Emanuel 1994, 4.4.14
!-------------------------------------------------------------------------
use constants_clubb, only: &
ep ! Variable
use clubb_precision, only: &
core_rknd ! Variable(s)
implicit none
! Input Variables
real( kind = core_rknd ), intent(in) :: &
p_in_Pa, & ! Pressure [Pa]
T_in_K ! Temperature [K]
! Local Variables
real( kind = core_rknd ) :: esatv
! --- Begin Code ---
! Calculate the SVP for water vapor using a lookup table.
esatv = sat_vapor_press_liq_lookup( T_in_K )
! If esatv exceeds the air pressure, then assume esatv~=0.5*pressure
! and set rsat = ep = 0.622
if ( p_in_Pa-esatv < 1.0_core_rknd ) then
sat_mixrat_liq_lookup = ep
else
#ifdef GFDL
! GFDL uses specific humidity
! Formula for Saturation Specific Humidity
if( I_sat_sphum ) then ! h1g, 2010-06-18 begin mod
sat_mixrat_liq_lookup = ep * ( esatv / ( p_in_Pa - (1.0_core_rknd-ep) * esatv ) )
else
sat_mixrat_liq_lookup = ep * ( esatv / ( p_in_Pa - esatv ) )
endif ! h1g, 2010-06-18 end mod
#else
! Formula for Saturation Mixing Ratio:
!
! rs = (epsilon) * [ esat / ( p - esat ) ];
! where epsilon = R_d / R_v
sat_mixrat_liq_lookup = ep * ( esatv / ( p_in_Pa - esatv ) )
#endif
end if
return
end function sat_mixrat_liq_lookup
!-----------------------------------------------------------------
elemental function sat_vapor_press_liq( T_in_K ) result ( esat )
! Description:
! Computes SVP for water vapor. Calls one of the other functions
! that calculate an approximation to SVP.
! References:
! None
use model_flags, only: &
saturation_formula, & ! Variable
saturation_bolton, &
saturation_gfdl, &
saturation_flatau
use clubb_precision, only: &
core_rknd ! Variable(s)
implicit none
! Input Variables
real( kind = core_rknd ), intent(in) :: T_in_K ! Temperature [K]
! Output Variables
real( kind = core_rknd ) :: esat ! Saturation Vapor Pressure over Water [Pa]
! Undefined approximation
esat = -99999.999_core_rknd
! Saturation Vapor Pressure, esat, can be found to be approximated
! in many different ways.
select case ( saturation_formula )
case ( saturation_bolton )
! Using the Bolton 1980 approximations for SVP over vapor
esat = sat_vapor_press_liq_bolton( T_in_K )
case ( saturation_flatau )
! Using the Flatau, et al. polynomial approximation for SVP over vapor
esat = sat_vapor_press_liq_flatau( T_in_K )
! ---> h1g
case ( saturation_gfdl )
! Using GFDL polynomial approximation for SVP with respect to liquid
esat = sat_vapor_press_liq_gfdl( T_in_K )
! <--- h1g
! Add new cases after this
end select
return
end function sat_vapor_press_liq
!------------------------------------------------------------------------
elemental function sat_vapor_press_liq_lookup( T_in_K ) result ( esat )
! Description:
! Computes SVP for water vapor, using a lookup table.
!
! The lookup table was constructed using the Flatau approximation.
! References:
! ``Polynomial Fits to Saturation Vapor Pressure'' Falatau, Walko,
! and Cotton. (1992) Journal of Applied Meteorology, Vol. 31,
! pp. 1507--1513
!------------------------------------------------------------------------
implicit none
! External
intrinsic :: max, min, int, anint
! Input Variables
real( kind = core_rknd ), intent(in) :: T_in_K ! Temperature [K]
! Output Variables
real( kind = core_rknd ) :: esat ! Saturation vapor pressure over water [Pa]
! Local Variables
integer :: T_in_K_int
! ---- Begin Code ----
T_in_K_int = int( anint( T_in_K ) )
! Since this approximation is only good out to -85 degrees Celsius we
! truncate the result here
T_in_K_int = min( max( T_in_K_int, 188 ), 343 )
! Use the lookup table to determine the saturation vapor pressure.
esat = svp_liq_lookup_table( T_in_K_int )
return
end function sat_vapor_press_liq_lookup
!------------------------------------------------------------------------
elemental function sat_vapor_press_liq_flatau( T_in_K ) result ( esat )
! Description:
! Computes SVP for water vapor.
! References:
! ``Polynomial Fits to Saturation Vapor Pressure'' Falatau, Walko,
! and Cotton. (1992) Journal of Applied Meteorology, Vol. 31,
! pp. 1507--1513
!------------------------------------------------------------------------
use constants_clubb, only: T_freeze_K
use clubb_precision, only: &
core_rknd ! Variable(s)
implicit none
! Constant parameters
! Relative error norm expansion (-50 to 50 deg_C) from
! Table 3 of pp. 1510 of Flatau et al. 1992 (Water Vapor)
! (The 100 coefficient converts from mb to Pa)
! real, dimension(7), parameter :: a = &
! 100.* (/ 6.11176750, 0.443986062, 0.143053301E-01, &
! 0.265027242E-03, 0.302246994E-05, 0.203886313E-07, &
! 0.638780966E-10 /)
! Relative error norm expansion (-85 to 70 deg_C) from
! Table 4 of pp. 1511 of Flatau et al.
!real( kind = core_rknd ), dimension(9), parameter :: a = &
!100._core_rknd * &
! Commented out because the form has been redone, causing these number to no longer be needed,
! leaving them in for now for reference.
! (/ 6.11583699_core_rknd, 0.444606896_core_rknd, 0.143177157E-01_core_rknd, &
! 0.264224321E-03_core_rknd, 0.299291081E-05_core_rknd, 0.203154182E-07_core_rknd, &
! 0.702620698E-10_core_rknd, 0.379534310E-13_core_rknd,-0.321582393E-15_core_rknd /)
real( kind = core_rknd ), parameter :: min_T_in_C = -85._core_rknd ! [deg_C]
! Input Variables
real( kind = core_rknd ), intent(in) :: T_in_K ! Temperature [K]
! Output Variables
real( kind = core_rknd ) :: esat ! Saturation vapor pressure over water [Pa]
! Local Variables
real( kind = core_rknd ) :: T_in_C, T_in_C_sqd
! integer :: i ! Loop index
! ---- Begin Code ----
! Determine deg K - 273.15
T_in_C = T_in_K - T_freeze_K
! Since this approximation is only good out to -85 degrees Celsius we
! truncate the result here (Flatau, et al. 1992)
T_in_C = max( T_in_C, min_T_in_C )
! Polynomial approx. (Flatau, et al. 1992)
! This is the generalized formula but is not computationally efficient.
! Based on Wexler's expressions(2.1)-(2.4) (See Flatau et al. p 1508)
! e_{sat} = a_1 + a_2 ( T - T_0 ) + ... + a_{n+1} ( T - T_0 )^n
! esat = a(1)
! do i = 2, size( a ) , 1
! esat = esat + a(i) * ( T_in_C )**(i-1)
! end do
! The 8th order polynomial fit. When running deep
! convective cases I noticed that absolute temperature often dips below
! -50 deg_C at higher altitudes, where the 6th order approximation is
! not accurate. -dschanen 20 Nov 2008
!esat = a(1) + T_in_C*( a(2) + T_in_C*( a(3) + T_in_C*( a(4) + T_in_C &
!*( a(5) + T_in_C*( a(6) + T_in_C*( a(7) + T_in_C*( a(8) + T_in_C*( a(9) ) ) ) ) ) ) ) )
! Factoring the polynomial above and changing it into this form allows the cpu
! to complete the calculations out of order. This is because modern cpus can complete
! multiple instructions at once if they do not depend on eachother, in the above case
! each instruction relies on the result of the last. In this version however, the terms
! in the parentheses could potentially be calculated in parallel by different execution
! units in the cpu, then only when those terms are being multiplied together do the
! instructions need to be done one at a time. See clubb issue 834 for more info.
! - Gunther Huebler, Aug 2018
T_in_C_sqd = T_in_C**2
esat = -3.21582393e-14_core_rknd * ( T_in_C - 646.5835252598777_core_rknd ) &
* ( T_in_C + 90.72381630364440_core_rknd ) &
* ( T_in_C_sqd + 111.0976961559954_core_rknd * T_in_C + 6459.629194243118_core_rknd ) &
* ( T_in_C_sqd + 152.3131930092453_core_rknd * T_in_C + 6499.774954705265_core_rknd ) &
* ( T_in_C_sqd + 174.4279584934021_core_rknd * T_in_C + 7721.679732114084_core_rknd )
return
end function sat_vapor_press_liq_flatau
!------------------------------------------------------------------------
elemental function sat_vapor_press_liq_bolton( T_in_K ) result ( esat )
! Description:
! Computes SVP for water vapor.
! References:
! Bolton 1980
!------------------------------------------------------------------------
use constants_clubb, only: T_freeze_K
use clubb_precision, only: &
core_rknd ! Variable(s)
implicit none
! External
intrinsic :: exp
! Input Variables
real( kind = core_rknd ), intent(in) :: T_in_K ! Temperature [K]
! Output Variables
real( kind = core_rknd ) :: esat ! Saturation vapor pressure over water [Pa]
! (Bolton 1980) approx.
! Generally this more computationally expensive than the Flatau polnomial expansion
esat = 611.2_core_rknd * exp( (17.67_core_rknd*(T_in_K-T_freeze_K)) / &
(T_in_K-29.65_core_rknd) ) ! Known magic number
return
end function sat_vapor_press_liq_bolton
! ---> h1g, 2010-06-16
!------------------------------------------------------------------------
elemental function sat_vapor_press_liq_gfdl( T_in_K ) result ( esat )
! Description:
! copy from "GFDL polysvp.F90"
! Compute saturation vapor pressure with respect to liquid by using
! function from Goff and Gratch (1946)
! Polysvp returned in units of pa.
! T_in_K is input in units of K.
!------------------------------------------------------------------------
use clubb_precision, only: &
core_rknd ! Variable(s)
implicit none
! Input Variables
real( kind = core_rknd ), intent(in) :: T_in_K ! Absolute temperature [K]
! Output Variables
real( kind = core_rknd ) :: esat ! Saturation vapor pressure over water [Pa]
! Local Variables
real( kind = core_rknd ), parameter :: &
min_T_in_K = 203.15_core_rknd ! Lowest temperature at which Goff-Gratch is valid [K]
real( kind = core_rknd ) :: &
T_in_K_clipped ! Absolute temperature with minimum threshold applied [K]
! Since the Goff-Gratch approximation is valid only down to -70 degrees Celsius,
! we threshold the temperature. This will yield a minimal saturation at
! cold temperatures.
T_in_K_clipped = max( min_T_in_K, T_in_K )
! Goff Gratch equation, uncertain below -70 C
esat = 10._core_rknd**(-7.90298_core_rknd*(373.16_core_rknd/T_in_K_clipped-1._core_rknd)+ &
5.02808_core_rknd*log10(373.16_core_rknd/T_in_K_clipped)- &
1.3816e-7_core_rknd*(10._core_rknd**(11.344_core_rknd &
*(1._core_rknd-T_in_K_clipped/373.16_core_rknd))-1._core_rknd)+ &
8.1328e-3_core_rknd*(10._core_rknd**(-3.49149_core_rknd &
*(373.16_core_rknd/T_in_K_clipped-1._core_rknd))-1._core_rknd)+ &
log10(1013.246_core_rknd))*100._core_rknd ! Known magic number
return
end function sat_vapor_press_liq_gfdl
! <--- h1g, 2010-06-16
!------------------------------------------------------------------------
elemental real( kind = core_rknd ) function sat_mixrat_ice( p_in_Pa, T_in_K )
! Description:
! Used to compute the saturation mixing ratio of ice.
! References:
! Formula from Emanuel 1994, 4.4.15
!-------------------------------------------------------------------------
use constants_clubb, only: &
ep ! Variable(s)
use clubb_precision, only: &
core_rknd ! Variable(s)
implicit none
! External
intrinsic :: trim
! Input Variables
real( kind = core_rknd ), intent(in) :: &
p_in_Pa, & ! Pressure [Pa]
T_in_K ! Temperature [K]
! Local Variables
real( kind = core_rknd ) :: esat_ice
! --- Begin Code ---
! Determine the SVP for the given temperature
esat_ice = sat_vapor_press_ice( T_in_K )
! If esat_ice exceeds the air pressure, then assume esat_ice~=0.5*pressure
! and set rsat = ep = 0.622
if ( p_in_Pa-esat_ice < 1.0_core_rknd ) then
sat_mixrat_ice = ep
else
#ifdef GFDL
! GFDL uses specific humidity
! Formula for Saturation Specific Humidity
if( I_sat_sphum ) then ! h1g, 2010-06-18 begin mod
sat_mixrat_ice = ep * ( esat_ice / ( p_in_Pa - (1.0_core_rknd-ep) * esat_ice ) )
else
sat_mixrat_ice = ep * ( esat_ice / ( p_in_Pa - esat_ice ) )
endif ! h1g, 2010-06-18 end mod
#else
! Formula for Saturation Mixing Ratio:
!
! rs = (epsilon) * [ esat / ( p - esat ) ];
! where epsilon = R_d / R_v
sat_mixrat_ice = ep * ( esat_ice / ( p_in_Pa - esat_ice ) )
#endif
end if
return
end function sat_mixrat_ice
!------------------------------------------------------------------------
elemental function sat_vapor_press_ice( T_in_K ) result ( esat_ice )
!
! Description:
! Computes SVP for ice, using one of the various approximations.
!
! References:
! None
!------------------------------------------------------------------------
use model_flags, only: &
saturation_formula, & ! Variable(s)
saturation_bolton, &
saturation_gfdl, &
saturation_flatau
use clubb_precision, only: &
core_rknd ! Variable(s)
implicit none
! Input Variable
real( kind = core_rknd ), intent(in) :: &
T_in_K ! Temperature [K]
! Output Variable
real( kind = core_rknd ) :: esat_ice ! Saturation Vapor Pressure over Ice [Pa]
! Undefined approximation
esat_ice = -99999.999_core_rknd
select case ( saturation_formula )
case ( saturation_bolton )
! Using the Bolton 1980 approximations for SVP over ice
esat_ice = sat_vapor_press_ice_bolton( T_in_K )
case ( saturation_flatau )
! Using the Flatau, et al. polynomial approximation for SVP over ice
esat_ice = sat_vapor_press_ice_flatau( T_in_K )
! ---> h1g, 2010-06-16
case ( saturation_gfdl )
! Using GFDL polynomial approximation for SVP with respect to ice
esat_ice = sat_vapor_press_ice_gfdl( T_in_K )
! <--- h1g, 2010-06-16
! Add new cases after this
end select
return
end function sat_vapor_press_ice
!------------------------------------------------------------------------
elemental function sat_vapor_press_ice_flatau( T_in_K ) result ( esati )
!
! Description:
! Computes SVP for ice.
!
! References:
! ``Polynomial Fits to Saturation Vapor Pressure'' Falatau, Walko,
! and Cotton. (1992) Journal of Applied Meteorology, Vol. 31,
! pp. 1507--1513
!------------------------------------------------------------------------
use constants_clubb, only: T_freeze_K
use clubb_precision, only: &
core_rknd ! Variable(s)
implicit none
! External
intrinsic :: max
! Relative error norm expansion (-90 to 0 deg_C) from
! Table 4 of pp. 1511 of Flatau et al. 1992 (Ice)
real( kind = core_rknd ), dimension(9), parameter :: a = &
100._core_rknd * (/ 6.09868993_core_rknd, 0.499320233_core_rknd, 0.184672631E-01_core_rknd, &
0.402737184E-03_core_rknd, 0.565392987E-05_core_rknd, 0.521693933E-07_core_rknd, &
0.307839583E-09_core_rknd, 0.105785160E-11_core_rknd, 0.161444444E-14_core_rknd /)
real( kind = core_rknd ), parameter :: min_T_in_C = -90._core_rknd ! [deg_C]
! Input Variables
real( kind = core_rknd ), intent(in) :: T_in_K ! Temperature [deg_K]
! Output Variables
real( kind = core_rknd ) :: esati ! Saturation vapor pressure over ice [Pa]
! Local Variables
real( kind = core_rknd ) :: T_in_C ! Temperature [deg_C]
! integer :: i
! ---- Begin Code ----
! Determine deg K - 273.15
T_in_C = T_in_K - T_freeze_K
! Since this approximation is only good out to -90 degrees Celsius we
! truncate the result here (Flatau, et al. 1992)
T_in_C = max( T_in_C, min_T_in_C )
! Polynomial approx. (Flatau, et al. 1992)
! esati = a(1)
! do i = 2, size( a ), 1
! esati = esati + a(i) * ( T_in_C )**(i-1)
! end do
esati = a(1) + T_in_C*( a(2) + T_in_C*( a(3) + T_in_C*( a(4) + T_in_C &
*( a(5) + T_in_C*( a(6) + T_in_C*( a(7) + T_in_C*( a(8) + T_in_C*( a(9) ) ) ) ) ) ) ) )
return
end function sat_vapor_press_ice_flatau
!------------------------------------------------------------------------
elemental function sat_vapor_press_ice_bolton( T_in_K ) result ( esati )
!
! Description:
! Computes SVP for ice.
!
! References:
! Bolton 1980
!------------------------------------------------------------------------
use clubb_precision, only: &
core_rknd ! Variable(s)
implicit none
! External
intrinsic :: exp, log
! Input Variables
real( kind = core_rknd ), intent(in) :: T_in_K ! Temperature [K]
! Output Variables
real( kind = core_rknd ) :: esati ! Saturation vapor pressure over ice [Pa]
! Exponential approx.
esati = 100.0_core_rknd * exp( 23.33086_core_rknd - &
(6111.72784_core_rknd/T_in_K) + (0.15215_core_rknd*log( T_in_K )) )
return
end function sat_vapor_press_ice_bolton
! ---> h1g, 2010-06-16
!------------------------------------------------------------------------
elemental function sat_vapor_press_ice_gfdl( T_in_K ) result ( esati )
! Description:
! copy from "GFDL polysvp.F90"
! Compute saturation vapor pressure with respect to liquid by using
! function from Goff and Gratch (1946)
!
! Polysvp returned in units of pa.
! T_in_K is input in units of K.
!------------------------------------------------------------------------
use clubb_precision, only: &
core_rknd ! Variable(s)
implicit none
! Input Variables
real( kind = core_rknd ), intent(in) :: T_in_K ! Absolute temperature [K]
! Output Variables
real( kind = core_rknd ) :: esati ! Saturation vapor pressure over ice [Pa]
! Local Variables
real( kind = core_rknd ), parameter :: &
min_T_in_K = 173.15_core_rknd ! Lowest temperature at which Goff-Gratch is valid [K]
real( kind = core_rknd ) :: &
T_in_K_clipped ! Absolute temperature with minimum threshold applied [K]
! Since the Goff-Gratch ice approximation is valid only down to -100 degrees Celsius,
! we threshold the temperature. This will yield a minimal saturation at
! cold temperatures.
T_in_K_clipped = max( min_T_in_K, T_in_K )
! Goff Gratch equation (good down to -100 C)
esati = 10._core_rknd**(-9.09718_core_rknd* &
(273.16_core_rknd/T_in_K_clipped-1._core_rknd)-3.56654_core_rknd* &
log10(273.16_core_rknd/T_in_K_clipped)+0.876793_core_rknd* &
(1._core_rknd-T_in_K_clipped/273.16_core_rknd)+ &
log10(6.1071_core_rknd))*100._core_rknd ! Known magic number
return
end function sat_vapor_press_ice_gfdl
! <--- h1g, 2010-06-16
!-------------------------------------------------------------------------
function rcm_sat_adj( thlm, rtm, p_in_Pa, exner ) result ( rcm )
! Description:
!
! This function uses an iterative method to find the value of rcm
! from an initial profile that has saturation at some point.
!
! References:
! None
!-------------------------------------------------------------------------
use clubb_precision, only: &
core_rknd ! Variable(s)
use constants_clubb, only: &
Cp, & ! Variable(s)
Lv, &
zero_threshold
implicit none
! Local Constant(s)
real( kind = core_rknd ), parameter :: &
tolerance = 0.001_core_rknd ! Tolerance on theta calculation [K]
integer, parameter :: &
itermax = 1000000 ! Maximum interations
! External
intrinsic :: max, abs
! Input Variable(s)
real( kind = core_rknd ), intent(in) :: &
thlm, & ! Liquid Water Potential Temperature [K]
rtm, & ! Total Water Mixing Ratio [kg/kg]
p_in_Pa, & ! Pressure [Pa]
exner ! Exner function [-]
! Output Variable(s)
real( kind = core_rknd ) :: rcm ! Cloud water mixing ratio [kg/kg]
! Local Variable(s)
real( kind = core_rknd ) :: &
theta, answer, too_low, too_high ! [K]
integer :: iteration
! ----- Begin Code -----
! Default initialization
theta = thlm
too_high = 0.0_core_rknd
too_low = 0.0_core_rknd
do iteration = 1, itermax, 1
answer = &
theta - (Lv/(Cp*exner)) &
*(MAX( rtm - sat_mixrat_liq(p_in_Pa,theta*exner), zero_threshold ))
if ( ABS(answer - thlm) <= tolerance ) then
exit
else if ( answer - thlm > tolerance ) then
too_high = theta
else if ( thlm - answer > tolerance ) THEN
too_low = theta
end if
! For the first timestep, be sure to set a "too_high"
! that is "way too high."
if ( iteration == 1 ) then
too_high = theta + 20.0_core_rknd
end if
theta = (too_low + too_high)/2.0_core_rknd
end do ! 1..itermax
if ( iteration == itermax ) then
! Magic Eric Raut added to remove compiler warning (clearly this value is not used)
rcm = 0.0_core_rknd
stop "Error in rcm_sat_adj: could not determine rcm"
else
rcm = MAX( rtm - sat_mixrat_liq( p_in_Pa, theta*exner), zero_threshold )
return
end if
end function rcm_sat_adj
end module saturation