@@ -37,6 +37,7 @@ export AbstractAtmosphericDrivers,
37
37
make_update_drivers,
38
38
prescribed_lai_era5,
39
39
prescribed_forcing_era5,
40
+ prescribed_perturbed_forcing_era5,
40
41
prescribed_analytic_forcing,
41
42
default_zenith_angle
42
43
@@ -1031,16 +1032,53 @@ function specific_humidity_from_dewpoint(
1031
1032
sim_FT (T_dew_air) - _T_freeze,
1032
1033
sim_FT (T_air) - _T_freeze,
1033
1034
)
1034
-
1035
- # In the future, we will use Thermodynamics functions for all of the below. Note that the type of T_air must match the type of the parameters in thermo_params.
1036
- esat:: sim_FT = Thermodynamics. saturation_vapor_pressure (
1035
+ q = Thermodynamics. q_vap_from_RH_liquid (
1037
1036
thermo_params,
1037
+ sim_FT (P_air),
1038
1038
sim_FT (T_air),
1039
- Thermodynamics . Liquid () ,
1039
+ rh ,
1040
1040
)
1041
+ return q
1042
+ end
1043
+
1044
+
1045
+ """
1046
+ perturbed_specific_humidity_from_dewpoint(dewpoint_temperature, temperature, air_pressure, earth_param_set, ΔT)
1041
1047
1042
- e = rh * esat
1043
- q = data_FT (0.622 * e / (P_air - 0.378 * e))
1048
+ Estimates the perturbed specific humidity given the true dewpoint temperature, true temperature of the air
1049
+ in Kelvin, and true air pressure in Pa, along with a perturbation to the temperature ΔT, and the ClimaLand
1050
+ `earth_param_set`. We first compute relative humidity, and then using RH and the perturbed air temperature,
1051
+ equal to true temperature+ΔT, compute a perturbed `q`.
1052
+
1053
+ Relative humidity is computed using the Magnus formula.
1054
+
1055
+ For more information on the Magnus formula, see e.g.
1056
+ Lawrence, Mark G. (1 February 2005).
1057
+ "The Relationship between Relative Humidity and the Dewpoint Temperature in Moist Air:
1058
+ A Simple Conversion and Applications".
1059
+ Bulletin of the American Meteorological Society. 86 (2): 225–234.
1060
+ """
1061
+ function perturbed_specific_humidity_from_dewpoint (
1062
+ T_dew_air:: data_FT ,
1063
+ T_air:: data_FT ,
1064
+ P_air:: data_FT ,
1065
+ earth_param_set,
1066
+ ΔT,
1067
+ ) where {data_FT <: Real }
1068
+ thermo_params = LP. thermodynamic_parameters (earth_param_set)
1069
+ _T_freeze = LP. T_freeze (earth_param_set)
1070
+ sim_FT = typeof (_T_freeze)
1071
+ # Obtain the relative humidity. This function requires temperatures in Celsius
1072
+ rh:: sim_FT = rh_from_dewpoint (
1073
+ sim_FT (T_dew_air) - _T_freeze,
1074
+ sim_FT (T_air) - _T_freeze,
1075
+ )
1076
+ q = Thermodynamics. q_vap_from_RH_liquid (
1077
+ thermo_params,
1078
+ sim_FT (P_air),
1079
+ sim_FT (T_air + ΔT),
1080
+ rh,
1081
+ )
1044
1082
return q
1045
1083
end
1046
1084
@@ -1049,6 +1087,8 @@ end
1049
1087
1050
1088
Returns the relative humidity given the dewpoint temperature in Celsius and the
1051
1089
air temperature in Celsius, using the Magnus formula.
1090
+ Since this formula is not restricted to the [0,1] range,
1091
+ we enforce it.
1052
1092
1053
1093
For more information on the Magnus formula, see e.g.
1054
1094
Lawrence, Mark G. (1 February 2005).
@@ -1061,7 +1101,7 @@ function rh_from_dewpoint(Td_C::FT, T_C::FT) where {FT <: Real}
1061
1101
b = FT (17.625 ) # unitless
1062
1102
γ = Td_C * b / (c + Td_C)
1063
1103
rh = exp (γ - b * T_C / (c + T_C))
1064
- return rh
1104
+ return max ( FT ( 0 ), min (rh, FT ( 1.0 )))
1065
1105
end
1066
1106
1067
1107
"""
@@ -1499,27 +1539,28 @@ function prescribed_forcing_era5(
1499
1539
# Pass a list of files in all cases
1500
1540
era5_ncdata_path isa String && (era5_ncdata_path = [era5_ncdata_path])
1501
1541
1502
- # Precip is provide as a mass flux; convert to volume flux of liquid water with ρ =1000 kg/m^3
1542
+ _ρ_liq = LP. ρ_cloud_liq (earth_param_set)
1543
+ # Precip is provided as a mass flux; convert to volume flux of liquid water with ρ = 1000 kg/m^3
1503
1544
precip = TimeVaryingInput (
1504
1545
[era5_ncdata_path, era5_ncdata_path],
1505
1546
[" mtpr" , " msr" ],
1506
1547
surface_space;
1507
1548
start_date,
1508
1549
regridder_type,
1509
1550
regridder_kwargs = (; interpolation_method),
1510
- file_reader_kwargs = (; preprocess_func = (data) -> - data / 1000 ,),
1551
+ file_reader_kwargs = (; preprocess_func = (data) -> - data / _ρ_liq ,),
1511
1552
method = time_interpolation_method,
1512
1553
compose_function = (mtpr, msr) -> min .(mtpr .- msr, Float32 (0 )),
1513
1554
)
1514
- # Precip is provide as a mass flux; convert to volume flux of liquid water with ρ =1000 kg/m^3
1555
+ # Precip is provided as a mass flux; convert to volume flux of liquid water with ρ = 1000 kg/m^3
1515
1556
snow_precip = TimeVaryingInput (
1516
1557
era5_ncdata_path,
1517
1558
" msr" ,
1518
1559
surface_space;
1519
1560
start_date,
1520
1561
regridder_type,
1521
1562
regridder_kwargs = (; interpolation_method),
1522
- file_reader_kwargs = (; preprocess_func = (data) -> - data / 1000 ,),
1563
+ file_reader_kwargs = (; preprocess_func = (data) -> - data / _ρ_liq ,),
1523
1564
method = time_interpolation_method,
1524
1565
)
1525
1566
if max_wind_speed isa Nothing
@@ -1642,6 +1683,194 @@ function prescribed_forcing_era5(
1642
1683
return (; atmos, radiation)
1643
1684
end
1644
1685
1686
+ """
1687
+ prescribed_perturbed_forcing_era5(era5_ncdata_path,
1688
+ surface_space,
1689
+ start_date,
1690
+ earth_param_set,
1691
+ ΔT,
1692
+ FT;
1693
+ gustiness=1,
1694
+ max_wind_speed = nothing,
1695
+ c_co2 = TimeVaryingInput((t) -> 4.2e-4),
1696
+ time_interpolation_method = LinearInterpolation(PeriodicCalendar()),
1697
+ regridder_type = :InterpolationsRegridder,
1698
+ interpolation_method = Interpolations.Constant(),)
1699
+
1700
+ A helper function which constructs the `PrescribedAtmosphere` and `PrescribedRadiativeFluxes`
1701
+ from a file path pointing to the ERA5 data in a netcdf file, the surface_space, the start date,
1702
+ and the earth_param_set, applying a change in the instantaneous temperature at each point
1703
+ of ΔT, while keeping the relative humidity fixed.
1704
+
1705
+ Please see the documentation for `prescribed_forcing_era5` for more information.
1706
+ """
1707
+ function prescribed_perturbed_forcing_era5 (
1708
+ era5_ncdata_path,
1709
+ surface_space,
1710
+ start_date,
1711
+ earth_param_set,
1712
+ ΔT,
1713
+ FT;
1714
+ gustiness = 1 ,
1715
+ max_wind_speed = nothing ,
1716
+ c_co2 = TimeVaryingInput ((t) -> 4.2e-4 ),
1717
+ time_interpolation_method = LinearInterpolation (PeriodicCalendar ()),
1718
+ regridder_type = :InterpolationsRegridder ,
1719
+ interpolation_method = Interpolations. Constant (),
1720
+ )
1721
+ # Pass a list of files in all cases
1722
+ era5_ncdata_path isa String && (era5_ncdata_path = [era5_ncdata_path])
1723
+ _ρ_liq = LP. ρ_cloud_liq (earth_param_set)
1724
+ # Precip is provided as a mass flux; convert to volume flux of liquid water with ρ = 1000 kg/m^3
1725
+ precip = TimeVaryingInput (
1726
+ [era5_ncdata_path, era5_ncdata_path],
1727
+ [" mtpr" , " msr" ],
1728
+ surface_space;
1729
+ start_date,
1730
+ regridder_type,
1731
+ regridder_kwargs = (; interpolation_method),
1732
+ file_reader_kwargs = (; preprocess_func = (data) -> - data / _ρ_liq,),
1733
+ method = time_interpolation_method,
1734
+ compose_function = (mtpr, msr) -> min .(mtpr .- msr, Float32 (0 )),
1735
+ )
1736
+ # Precip is provided as a mass flux; convert to volume flux of liquid water with ρ = 1000 kg/m^3
1737
+ snow_precip = TimeVaryingInput (
1738
+ era5_ncdata_path,
1739
+ " msr" ,
1740
+ surface_space;
1741
+ start_date,
1742
+ regridder_type,
1743
+ regridder_kwargs = (; interpolation_method),
1744
+ file_reader_kwargs = (; preprocess_func = (data) -> - data / _ρ_liq,),
1745
+ method = time_interpolation_method,
1746
+ )
1747
+ if max_wind_speed isa Nothing
1748
+ wind_function = (u, v) -> sqrt .(u .^ 2 .+ v .^ 2 )
1749
+ else
1750
+ wind_function = (u, v) -> min .(sqrt .(u .^ 2 .+ v .^ 2 ), max_wind_speed)
1751
+ end
1752
+
1753
+ u_atmos = TimeVaryingInput (
1754
+ [era5_ncdata_path, era5_ncdata_path],
1755
+ [" u10" , " v10" ],
1756
+ surface_space;
1757
+ start_date,
1758
+ regridder_type,
1759
+ regridder_kwargs = (; interpolation_method),
1760
+ compose_function = wind_function,
1761
+ method = time_interpolation_method,
1762
+ )
1763
+ specific_humidity (Td, T, P; params = earth_param_set, ΔT = ΔT) =
1764
+ ClimaLand. perturbed_specific_humidity_from_dewpoint .(
1765
+ Td,
1766
+ T,
1767
+ P,
1768
+ params,
1769
+ ΔT,
1770
+ )
1771
+ q_atmos = TimeVaryingInput (
1772
+ [era5_ncdata_path, era5_ncdata_path, era5_ncdata_path],
1773
+ [" d2m" , " t2m" , " sp" ],
1774
+ surface_space;
1775
+ start_date,
1776
+ regridder_type,
1777
+ regridder_kwargs = (; interpolation_method),
1778
+ compose_function = specific_humidity,
1779
+ method = time_interpolation_method,
1780
+ )
1781
+ P_atmos = TimeVaryingInput (
1782
+ era5_ncdata_path,
1783
+ " sp" ,
1784
+ surface_space;
1785
+ start_date,
1786
+ regridder_type,
1787
+ regridder_kwargs = (; interpolation_method),
1788
+ method = time_interpolation_method,
1789
+ )
1790
+ perturb_temp (T; ΔT = ΔT) = T .+ ΔT
1791
+ T_atmos = TimeVaryingInput (
1792
+ era5_ncdata_path,
1793
+ " t2m" ,
1794
+ surface_space;
1795
+ start_date,
1796
+ regridder_type,
1797
+ regridder_kwargs = (; interpolation_method),
1798
+ file_reader_kwargs = (; preprocess_func = perturb_temp),
1799
+ method = time_interpolation_method,
1800
+ )
1801
+ h_atmos = FT (10 )
1802
+
1803
+ atmos = PrescribedAtmosphere (
1804
+ precip,
1805
+ snow_precip,
1806
+ T_atmos,
1807
+ u_atmos,
1808
+ q_atmos,
1809
+ P_atmos,
1810
+ start_date,
1811
+ h_atmos,
1812
+ earth_param_set;
1813
+ gustiness = FT (gustiness),
1814
+ c_co2 = c_co2,
1815
+ )
1816
+
1817
+ SW_d = TimeVaryingInput (
1818
+ era5_ncdata_path,
1819
+ " msdwswrf" ,
1820
+ surface_space;
1821
+ start_date,
1822
+ regridder_type,
1823
+ regridder_kwargs = (; interpolation_method),
1824
+ method = time_interpolation_method,
1825
+ )
1826
+ function compute_diffuse_fraction (total, direct)
1827
+ diff = max (total - direct, Float32 (0 ))
1828
+ return min (diff / (total + eps (Float32)), Float32 (1 ))
1829
+ end
1830
+ function compute_diffuse_fraction_broadcasted (total, direct)
1831
+ return @. compute_diffuse_fraction (total, direct)
1832
+ end
1833
+
1834
+ frac_diff = TimeVaryingInput (
1835
+ [era5_ncdata_path, era5_ncdata_path],
1836
+ [" msdwswrf" , " msdrswrf" ],
1837
+ surface_space;
1838
+ start_date,
1839
+ regridder_type,
1840
+ regridder_kwargs = (; interpolation_method),
1841
+ method = time_interpolation_method,
1842
+ compose_function = compute_diffuse_fraction_broadcasted,
1843
+ )
1844
+ LW_d = TimeVaryingInput (
1845
+ era5_ncdata_path,
1846
+ " msdwlwrf" ,
1847
+ surface_space;
1848
+ start_date,
1849
+ regridder_type,
1850
+ regridder_kwargs = (; interpolation_method),
1851
+ method = time_interpolation_method,
1852
+ )
1853
+ zenith_angle =
1854
+ (t, s) -> default_zenith_angle (
1855
+ t,
1856
+ s;
1857
+ latitude = ClimaCore. Fields. coordinate_field (surface_space). lat,
1858
+ longitude = ClimaCore. Fields. coordinate_field (surface_space). long,
1859
+ insol_params = earth_param_set. insol_params,
1860
+ )
1861
+
1862
+ radiation = PrescribedRadiativeFluxes (
1863
+ FT,
1864
+ SW_d,
1865
+ LW_d,
1866
+ start_date;
1867
+ θs = zenith_angle,
1868
+ earth_param_set = earth_param_set,
1869
+ frac_diff = frac_diff,
1870
+ )
1871
+ return (; atmos, radiation)
1872
+ end
1873
+
1645
1874
1646
1875
"""
1647
1876
prescribed_lai_era5(era5_lai_ncdata_path,
0 commit comments