pycontrails 0.50.2__cp312-cp312-win_amd64.whl → 0.51.1__cp312-cp312-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pycontrails might be problematic. Click here for more details.
- pycontrails/_version.py +2 -2
- pycontrails/core/datalib.py +22 -0
- pycontrails/core/flight.py +87 -7
- pycontrails/core/met.py +33 -5
- pycontrails/core/polygon.py +10 -3
- pycontrails/core/rgi_cython.cp312-win_amd64.pyd +0 -0
- pycontrails/datalib/ecmwf/__init__.py +6 -0
- pycontrails/datalib/ecmwf/arco_era5.py +2 -53
- pycontrails/datalib/ecmwf/common.py +4 -0
- pycontrails/datalib/ecmwf/era5.py +2 -6
- pycontrails/datalib/ecmwf/era5_model_level.py +481 -0
- pycontrails/datalib/ecmwf/hres_model_level.py +494 -0
- pycontrails/datalib/ecmwf/model_levels.py +79 -0
- pycontrails/datalib/ecmwf/static/model_level_dataframe_v20240418.csv +139 -0
- pycontrails/datalib/ecmwf/variables.py +12 -0
- pycontrails/models/humidity_scaling/humidity_scaling.py +55 -8
- pycontrails/models/humidity_scaling/quantiles/era5-model-level-quantiles.pq +0 -0
- pycontrails/models/ps_model/ps_aircraft_params.py +19 -3
- pycontrails/models/ps_model/ps_grid.py +21 -21
- pycontrails/models/ps_model/ps_model.py +41 -6
- pycontrails/models/ps_model/ps_operational_limits.py +15 -6
- pycontrails/models/ps_model/static/{ps-aircraft-params-20240417.csv → ps-aircraft-params-20240524.csv} +16 -12
- pycontrails/models/ps_model/static/ps-synonym-list-20240524.csv +103 -0
- pycontrails/physics/thermo.py +1 -1
- pycontrails/utils/types.py +3 -2
- {pycontrails-0.50.2.dist-info → pycontrails-0.51.1.dist-info}/METADATA +4 -4
- {pycontrails-0.50.2.dist-info → pycontrails-0.51.1.dist-info}/RECORD +32 -26
- /pycontrails/models/humidity_scaling/quantiles/{era5-quantiles.pq → era5-pressure-level-quantiles.pq} +0 -0
- {pycontrails-0.50.2.dist-info → pycontrails-0.51.1.dist-info}/LICENSE +0 -0
- {pycontrails-0.50.2.dist-info → pycontrails-0.51.1.dist-info}/NOTICE +0 -0
- {pycontrails-0.50.2.dist-info → pycontrails-0.51.1.dist-info}/WHEEL +0 -0
- {pycontrails-0.50.2.dist-info → pycontrails-0.51.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
n,a [Pa],b,ph [hPa],pf [hPa],Geopotential Altitude [m],Geometric Altitude [m],Temperature [K],Density [kg/m^3]
|
|
2
|
+
0,0.0,0.0,0.0,,,,,
|
|
3
|
+
1,2.000365,0.0,0.02,0.01,79301.79,80301.65,198.05,1.8e-05
|
|
4
|
+
2,3.102241,0.0,0.031,0.0255,73721.58,74584.91,209.21,4.2e-05
|
|
5
|
+
3,4.666084,0.0,0.0467,0.0388,71115.75,71918.79,214.42,6.3e-05
|
|
6
|
+
4,6.827977,0.0,0.0683,0.0575,68618.43,69365.77,221.32,9e-05
|
|
7
|
+
5,9.746966,0.0,0.0975,0.0829,66210.99,66906.53,228.06,0.000127
|
|
8
|
+
6,13.605424,0.0,0.1361,0.1168,63890.03,64537.43,234.56,0.000173
|
|
9
|
+
7,18.608931,0.0,0.1861,0.1611,61651.77,62254.39,240.83,0.000233
|
|
10
|
+
8,24.985718,0.0,0.2499,0.218,59492.5,60053.46,246.87,0.000308
|
|
11
|
+
9,32.98571,0.0,0.3299,0.2899,57408.61,57930.78,252.71,0.0004
|
|
12
|
+
10,42.879242,0.0,0.4288,0.3793,55396.62,55882.68,258.34,0.000512
|
|
13
|
+
11,54.955463,0.0,0.5496,0.4892,53453.2,53905.62,263.78,0.000646
|
|
14
|
+
12,69.520576,0.0,0.6952,0.6224,51575.15,51996.21,269.04,0.000806
|
|
15
|
+
13,86.895882,0.0,0.869,0.7821,49767.41,50159.36,270.65,0.001007
|
|
16
|
+
14,107.415741,0.0,1.0742,0.9716,48048.7,48413.94,270.65,0.001251
|
|
17
|
+
15,131.425507,0.0,1.3143,1.1942,46416.22,46756.98,269.02,0.001546
|
|
18
|
+
16,159.279404,0.0,1.5928,1.4535,44881.17,45199.69,264.72,0.001913
|
|
19
|
+
17,191.338562,0.0,1.9134,1.7531,43440.23,43738.55,260.68,0.002343
|
|
20
|
+
18,227.968948,0.0,2.2797,2.0965,42085.0,42364.93,256.89,0.002843
|
|
21
|
+
19,269.539581,0.0,2.6954,2.4875,40808.05,41071.2,253.31,0.003421
|
|
22
|
+
20,316.420746,0.0,3.1642,2.9298,39602.76,39850.56,249.94,0.004084
|
|
23
|
+
21,368.982361,0.0,3.6898,3.427,38463.25,38696.94,246.75,0.004838
|
|
24
|
+
22,427.592499,0.0,4.2759,3.9829,37384.22,37604.95,243.73,0.005693
|
|
25
|
+
23,492.616028,0.0,4.9262,4.601,36360.94,36569.72,240.86,0.006655
|
|
26
|
+
24,564.413452,0.0,5.6441,5.2851,35389.15,35586.89,238.14,0.007731
|
|
27
|
+
25,643.339905,0.0,6.4334,6.0388,34465.0,34652.52,235.55,0.008931
|
|
28
|
+
26,729.744141,0.0,7.2974,6.8654,33585.02,33763.05,233.09,0.010261
|
|
29
|
+
27,823.967834,0.0,8.2397,7.7686,32746.04,32915.27,230.74,0.011729
|
|
30
|
+
28,926.34491,0.0,9.2634,8.7516,31945.53,32106.57,228.6,0.013337
|
|
31
|
+
29,1037.201172,0.0,10.372,9.8177,31177.59,31330.96,227.83,0.015012
|
|
32
|
+
30,1156.853638,0.0,11.5685,10.9703,30438.54,30584.71,227.09,0.016829
|
|
33
|
+
31,1285.610352,0.0,12.8561,12.2123,29726.69,29866.09,226.38,0.018793
|
|
34
|
+
32,1423.770142,0.0,14.2377,13.5469,29040.48,29173.5,225.69,0.02091
|
|
35
|
+
33,1571.622925,0.0,15.7162,14.977,28378.46,28505.47,225.03,0.023186
|
|
36
|
+
34,1729.448975,0.0,17.2945,16.5054,27739.29,27860.64,224.39,0.025624
|
|
37
|
+
35,1897.519287,0.0,18.9752,18.1348,27121.74,27237.73,223.77,0.028232
|
|
38
|
+
36,2076.095947,0.0,20.761,19.8681,26524.63,26635.56,223.17,0.031013
|
|
39
|
+
37,2265.431641,0.0,22.6543,21.7076,25946.9,26053.04,222.6,0.033972
|
|
40
|
+
38,2465.770508,0.0,24.6577,23.656,25387.55,25489.15,222.04,0.037115
|
|
41
|
+
39,2677.348145,0.0,26.7735,25.7156,24845.63,24942.93,221.5,0.040445
|
|
42
|
+
40,2900.391357,0.0,29.0039,27.8887,24320.28,24413.5,220.97,0.043967
|
|
43
|
+
41,3135.119385,0.0,31.3512,30.1776,23810.67,23900.02,220.46,0.047685
|
|
44
|
+
42,3381.743652,0.0,33.8174,32.5843,23316.04,23401.71,219.97,0.051604
|
|
45
|
+
43,3640.468262,0.0,36.4047,35.1111,22835.68,22917.85,219.49,0.055727
|
|
46
|
+
44,3911.490479,0.0,39.1149,37.7598,22368.91,22447.75,219.02,0.060059
|
|
47
|
+
45,4194.930664,0.0,41.9493,40.5321,21915.16,21990.82,218.57,0.064602
|
|
48
|
+
46,4490.817383,0.0,44.9082,43.4287,21473.98,21546.62,218.12,0.069359
|
|
49
|
+
47,4799.149414,0.0,47.9915,46.4498,21045.0,21114.77,217.7,0.07433
|
|
50
|
+
48,5119.89502,0.0,51.199,49.5952,20627.87,20694.9,217.28,0.079516
|
|
51
|
+
49,5452.990723,0.0,54.5299,52.8644,20222.24,20286.66,216.87,0.084916
|
|
52
|
+
50,5798.344727,0.0,57.9834,56.2567,19827.95,19889.88,216.65,0.090458
|
|
53
|
+
51,6156.074219,0.0,61.5607,59.7721,19443.55,19503.09,216.65,0.09611
|
|
54
|
+
52,6526.946777,0.0,65.2695,63.4151,19068.35,19125.61,216.65,0.101968
|
|
55
|
+
53,6911.870605,0.0,69.1187,67.1941,18701.27,18756.34,216.65,0.108045
|
|
56
|
+
54,7311.869141,0.0,73.1187,71.1187,18341.27,18394.25,216.65,0.114355
|
|
57
|
+
55,7727.412109,7e-06,77.281,75.1999,17987.41,18038.35,216.65,0.120917
|
|
58
|
+
56,8159.354004,2.4e-05,81.6182,79.4496,17638.78,17687.77,216.65,0.127751
|
|
59
|
+
57,8608.525391,5.9e-05,86.145,83.8816,17294.53,17341.62,216.65,0.134877
|
|
60
|
+
58,9076.400391,0.000112,90.8774,88.5112,16953.83,16999.08,216.65,0.142321
|
|
61
|
+
59,9562.682617,0.000199,95.828,93.3527,16616.09,16659.55,216.65,0.150106
|
|
62
|
+
60,10065.978516,0.00034,101.0047,98.4164,16281.1,16322.83,216.65,0.158248
|
|
63
|
+
61,10584.631836,0.000562,106.4153,103.71,15948.85,15988.88,216.65,0.16676
|
|
64
|
+
62,11116.662109,0.00089,112.0681,109.2417,15619.3,15657.7,216.65,0.175655
|
|
65
|
+
63,11660.067383,0.001353,117.9714,115.0198,15292.44,15329.24,216.65,0.184946
|
|
66
|
+
64,12211.547852,0.001992,124.1337,121.0526,14968.24,15003.5,216.65,0.194646
|
|
67
|
+
65,12766.873047,0.002857,130.5637,127.3487,14646.68,14680.44,216.65,0.20477
|
|
68
|
+
66,13324.668945,0.003971,137.2703,133.917,14327.75,14360.05,216.65,0.215331
|
|
69
|
+
67,13881.331055,0.005378,144.2624,140.7663,14011.41,14042.3,216.65,0.226345
|
|
70
|
+
68,14432.139648,0.007133,151.5493,147.9058,13697.65,13727.18,216.65,0.237825
|
|
71
|
+
69,14975.615234,0.009261,159.1403,155.3448,13386.45,13414.65,216.65,0.249786
|
|
72
|
+
70,15508.256836,0.011806,167.045,163.0927,13077.79,13104.7,216.65,0.262244
|
|
73
|
+
71,16026.115234,0.014816,175.2731,171.1591,12771.64,12797.3,216.65,0.275215
|
|
74
|
+
72,16527.322266,0.018318,183.8344,179.5537,12467.99,12492.44,216.65,0.288713
|
|
75
|
+
73,17008.789063,0.022355,192.7389,188.2867,12166.81,12190.1,216.65,0.302755
|
|
76
|
+
74,17467.613281,0.026964,201.9969,197.3679,11868.08,11890.24,216.65,0.317357
|
|
77
|
+
75,17901.621094,0.032176,211.6186,206.8078,11571.79,11592.86,216.65,0.332536
|
|
78
|
+
76,18308.433594,0.038026,221.6146,216.6166,11277.92,11297.93,216.65,0.348308
|
|
79
|
+
77,18685.71875,0.044548,231.9954,226.805,10986.7,11005.69,216.74,0.364545
|
|
80
|
+
78,19031.289063,0.051773,242.7719,237.3837,10696.22,10714.22,218.62,0.378253
|
|
81
|
+
79,19343.511719,0.059728,253.9549,248.3634,10405.61,10422.64,220.51,0.392358
|
|
82
|
+
80,19620.042969,0.068448,265.5556,259.7553,10114.89,10130.98,222.4,0.406868
|
|
83
|
+
81,19859.390625,0.077958,277.5852,271.5704,9824.08,9839.26,224.29,0.42179
|
|
84
|
+
82,20059.931641,0.088286,290.0548,283.82,9533.2,9547.49,226.18,0.43713
|
|
85
|
+
83,20219.664063,0.099462,302.9762,296.5155,9242.26,9255.7,228.08,0.452897
|
|
86
|
+
84,20337.863281,0.111505,316.3607,309.6684,8951.3,8963.9,229.97,0.469097
|
|
87
|
+
85,20412.308594,0.124448,330.2202,323.2904,8660.32,8672.11,231.86,0.485737
|
|
88
|
+
86,20442.078125,0.138313,344.5663,337.3932,8369.35,8380.36,233.75,0.502825
|
|
89
|
+
87,20425.71875,0.153125,359.4111,351.9887,8078.41,8088.67,235.64,0.520367
|
|
90
|
+
88,20361.816406,0.16891,374.7666,367.0889,7787.51,7797.04,237.53,0.53837
|
|
91
|
+
89,20249.511719,0.185689,390.645,382.7058,7496.68,7505.51,239.42,0.556842
|
|
92
|
+
90,20087.085938,0.203491,407.0583,398.8516,7205.93,7214.09,241.31,0.57579
|
|
93
|
+
91,19874.025391,0.222333,424.019,415.5387,6915.29,6922.8,243.2,0.595219
|
|
94
|
+
92,19608.572266,0.242244,441.5395,432.7792,6624.76,6631.66,245.09,0.615138
|
|
95
|
+
93,19290.226563,0.263242,459.6321,450.5858,6334.38,6340.68,246.98,0.635553
|
|
96
|
+
94,18917.460938,0.285354,478.3096,468.9708,6044.15,6049.89,248.86,0.656471
|
|
97
|
+
95,18489.707031,0.308598,497.5845,487.947,5754.1,5759.3,250.75,0.677899
|
|
98
|
+
96,18006.925781,0.332939,517.4198,507.5021,5464.6,5469.3,252.63,0.699815
|
|
99
|
+
97,17471.839844,0.358254,537.7195,527.5696,5176.77,5180.98,254.5,0.722139
|
|
100
|
+
98,16888.6875,0.384363,558.343,548.0312,4892.26,4896.02,256.35,0.744735
|
|
101
|
+
99,16262.046875,0.411125,579.1926,568.7678,4612.58,4615.92,258.17,0.767472
|
|
102
|
+
100,15596.695313,0.438391,600.1668,589.6797,4338.77,4341.73,259.95,0.790242
|
|
103
|
+
101,14898.453125,0.466003,621.1624,610.6646,4071.8,4074.41,261.68,0.812937
|
|
104
|
+
102,14173.324219,0.4938,642.0764,631.6194,3812.53,3814.82,263.37,0.835453
|
|
105
|
+
103,13427.769531,0.521619,662.8084,652.4424,3561.7,3563.69,265.0,0.857686
|
|
106
|
+
104,12668.257813,0.549301,683.262,673.0352,3319.94,3321.67,266.57,0.879541
|
|
107
|
+
105,11901.339844,0.576692,703.3467,693.3043,3087.75,3089.25,268.08,0.900929
|
|
108
|
+
106,11133.304688,0.603648,722.9795,713.1631,2865.54,2866.83,269.52,0.921768
|
|
109
|
+
107,10370.175781,0.630036,742.0855,732.5325,2653.58,2654.69,270.9,0.941988
|
|
110
|
+
108,9617.515625,0.655736,760.5996,751.3426,2452.04,2452.99,272.21,0.961527
|
|
111
|
+
109,8880.453125,0.680643,778.4661,769.5329,2260.99,2261.8,273.45,0.980334
|
|
112
|
+
110,8163.375,0.704669,795.6396,787.0528,2080.41,2081.09,274.63,0.998368
|
|
113
|
+
111,7470.34375,0.727739,812.0847,803.8622,1910.19,1910.76,275.73,1.015598
|
|
114
|
+
112,6804.421875,0.749797,827.7756,819.9302,1750.14,1750.63,276.77,1.032005
|
|
115
|
+
113,6168.53125,0.770798,842.6959,835.2358,1600.04,1600.44,277.75,1.047576
|
|
116
|
+
114,5564.382813,0.790717,856.8376,849.7668,1459.58,1459.91,278.66,1.06231
|
|
117
|
+
115,4993.796875,0.809536,870.2004,863.519,1328.43,1328.7,279.52,1.076209
|
|
118
|
+
116,4457.375,0.827256,882.791,876.4957,1206.21,1206.44,280.31,1.089286
|
|
119
|
+
117,3955.960938,0.843881,894.6222,888.7066,1092.54,1092.73,281.05,1.101558
|
|
120
|
+
118,3489.234375,0.859432,905.7116,900.1669,987.0,987.15,281.73,1.113047
|
|
121
|
+
119,3057.265625,0.873929,916.0815,910.8965,889.17,889.29,282.37,1.123777
|
|
122
|
+
120,2659.140625,0.887408,925.7571,920.9193,798.62,798.72,282.96,1.133779
|
|
123
|
+
121,2294.242188,0.8999,934.7666,930.2618,714.94,715.02,283.5,1.143084
|
|
124
|
+
122,1961.5,0.911448,943.1399,938.9532,637.7,637.76,284.0,1.151724
|
|
125
|
+
123,1659.476563,0.922096,950.9082,947.024,566.49,566.54,284.47,1.159733
|
|
126
|
+
124,1387.546875,0.931881,958.1037,954.5059,500.91,500.95,284.89,1.167147
|
|
127
|
+
125,1143.25,0.94086,964.7584,961.4311,440.58,440.61,285.29,1.173999
|
|
128
|
+
126,926.507813,0.949064,970.9046,967.8315,385.14,385.16,285.65,1.180323
|
|
129
|
+
127,734.992188,0.95655,976.5737,973.7392,334.22,334.24,285.98,1.186154
|
|
130
|
+
128,568.0625,0.963352,981.7968,979.1852,287.51,287.52,286.28,1.191523
|
|
131
|
+
129,424.414063,0.969513,986.6036,984.2002,244.68,244.69,286.56,1.196462
|
|
132
|
+
130,302.476563,0.975078,991.023,988.8133,205.44,205.44,286.81,1.201001
|
|
133
|
+
131,202.484375,0.980072,995.0824,993.0527,169.5,169.51,287.05,1.205168
|
|
134
|
+
132,122.101563,0.984542,998.8081,996.9452,136.62,136.62,287.26,1.208992
|
|
135
|
+
133,62.78125,0.9885,1002.225,1000.5165,106.54,106.54,287.46,1.212498
|
|
136
|
+
134,22.835938,0.991984,1005.3562,1003.7906,79.04,79.04,287.64,1.21571
|
|
137
|
+
135,3.757813,0.995003,1008.2239,1006.79,53.92,53.92,287.8,1.21865
|
|
138
|
+
136,0.0,0.99763,1010.8487,1009.5363,30.96,30.96,287.95,1.221341
|
|
139
|
+
137,0.0,1.0,1013.25,1012.0494,10.0,10.0,288.09,1.223803
|
|
@@ -240,5 +240,17 @@ SURFACE_VARIABLES = [
|
|
|
240
240
|
CloudAreaFraction,
|
|
241
241
|
SurfaceSolarDownwardRadiation,
|
|
242
242
|
]
|
|
243
|
+
MODEL_LEVEL_VARIABLES = [
|
|
244
|
+
met_var.AirTemperature,
|
|
245
|
+
met_var.SpecificHumidity,
|
|
246
|
+
met_var.VerticalVelocity,
|
|
247
|
+
met_var.EastwardWind,
|
|
248
|
+
met_var.NorthwardWind,
|
|
249
|
+
RelativeVorticity,
|
|
250
|
+
Divergence,
|
|
251
|
+
CloudAreaFractionInLayer,
|
|
252
|
+
SpecificCloudIceWaterContent,
|
|
253
|
+
SpecificCloudLiquidWaterContent,
|
|
254
|
+
]
|
|
243
255
|
|
|
244
256
|
ECMWF_VARIABLES = PRESSURE_LEVEL_VARIABLES + SURFACE_VARIABLES
|
|
@@ -591,9 +591,15 @@ class HumidityScalingByLevel(HumidityScaling):
|
|
|
591
591
|
|
|
592
592
|
|
|
593
593
|
@functools.cache
|
|
594
|
-
def _load_quantiles() -> pd.DataFrame:
|
|
594
|
+
def _load_quantiles(level_type: str) -> pd.DataFrame:
|
|
595
595
|
"""Load precomputed ERA5 and IAGOS quantiles.
|
|
596
596
|
|
|
597
|
+
Parameters
|
|
598
|
+
----------
|
|
599
|
+
level_type : {"pressure", "model"}
|
|
600
|
+
Select whether to load precomputed quantiles from pressure- vs
|
|
601
|
+
model-level ERA5 data.
|
|
602
|
+
|
|
597
603
|
Returns
|
|
598
604
|
-------
|
|
599
605
|
pd.DataFrame
|
|
@@ -602,17 +608,20 @@ def _load_quantiles() -> pd.DataFrame:
|
|
|
602
608
|
and the interpolation methodology. The IAOGS quantiles are in the
|
|
603
609
|
``("iagos", "iagos")`` column.
|
|
604
610
|
"""
|
|
605
|
-
path = pathlib.Path(__file__).parent / "quantiles" / "era5-quantiles.pq"
|
|
606
|
-
|
|
611
|
+
path = pathlib.Path(__file__).parent / "quantiles" / f"era5-{level_type}-level-quantiles.pq"
|
|
612
|
+
df = pd.read_parquet(path)
|
|
613
|
+
df.attrs["path"] = str(path)
|
|
614
|
+
return df
|
|
607
615
|
|
|
608
616
|
|
|
609
617
|
def histogram_matching(
|
|
610
618
|
era5_rhi: ArrayLike,
|
|
611
619
|
product_type: str,
|
|
620
|
+
level_type: str,
|
|
612
621
|
member: int | None,
|
|
613
622
|
q_method: str | None,
|
|
614
623
|
) -> npt.NDArray[np.float64]:
|
|
615
|
-
"""Map ERA5-derived RHi to
|
|
624
|
+
"""Map ERA5-derived RHi to its corresponding IAGOS quantile via histogram matching.
|
|
616
625
|
|
|
617
626
|
This matching is performed on a **single** ERA5 ensemble member.
|
|
618
627
|
|
|
@@ -622,6 +631,11 @@ def histogram_matching(
|
|
|
622
631
|
ERA5-derived RHi values for the given ensemble member.
|
|
623
632
|
product_type : {"reanalysis", "ensemble_members"}
|
|
624
633
|
The ERA5 product type.
|
|
634
|
+
level_type : {"pressure", "model"}
|
|
635
|
+
Select whether to perform quantile mapping based on quantiles from
|
|
636
|
+
pressure- or model-level ERA5 data. Selecting ``level_type == "model"``
|
|
637
|
+
when ``product_type == "ensemble_members"`` will produce a warning
|
|
638
|
+
and change ``product_type`` to ``"reanalysis"``.
|
|
625
639
|
member : int | None
|
|
626
640
|
The ERA5 ensemble member to use. Must be in the range ``[0, 10)``.
|
|
627
641
|
Only used if ``product_type == "ensemble_members"``.
|
|
@@ -634,18 +648,30 @@ def histogram_matching(
|
|
|
634
648
|
The IAGOS quantiles corresponding to the ERA5-derived RHi values. Returned
|
|
635
649
|
as a numpy array with the same shape and dtype as ``era5_rhi``.
|
|
636
650
|
"""
|
|
637
|
-
|
|
651
|
+
if level_type not in ["pressure", "model"]:
|
|
652
|
+
msg = f"Invalid 'level_type' value '{level_type}'. " "Must be one of ['pressure', 'model']."
|
|
653
|
+
raise ValueError(msg)
|
|
654
|
+
df = _load_quantiles(level_type)
|
|
638
655
|
iagos_quantiles = df[("iagos", "iagos")]
|
|
639
656
|
|
|
657
|
+
if product_type == "ensemble_members" and level_type == "model":
|
|
658
|
+
msg = (
|
|
659
|
+
"No quantiles available for model-level ensemble data. "
|
|
660
|
+
"Switching to product_type = 'reanalysis'."
|
|
661
|
+
)
|
|
662
|
+
warnings.warn(msg)
|
|
663
|
+
product_type = "reanalysis"
|
|
664
|
+
|
|
640
665
|
if product_type == "ensemble_members":
|
|
641
666
|
col = f"ensemble{member}", q_method or "linear-q"
|
|
642
667
|
elif product_type == "reanalysis":
|
|
643
668
|
col = "reanalysis", q_method or "linear-q"
|
|
644
669
|
else:
|
|
645
|
-
|
|
670
|
+
msg = (
|
|
646
671
|
f"Invalid 'product_type' value '{product_type}'. "
|
|
647
672
|
"Must be one of ['reanalysis', 'ensemble_members']."
|
|
648
673
|
)
|
|
674
|
+
raise ValueError(msg)
|
|
649
675
|
|
|
650
676
|
try:
|
|
651
677
|
era5_quantiles = df[col]
|
|
@@ -691,7 +717,7 @@ def histogram_matching_all_members(
|
|
|
691
717
|
|
|
692
718
|
# Perform histogram matching on the given ensemble member
|
|
693
719
|
ensemble_member_rhi = histogram_matching(
|
|
694
|
-
era5_rhi_all_members[:, member], "ensemble_members", member, q_method
|
|
720
|
+
era5_rhi_all_members[:, member], "ensemble_members", "pressure", member, q_method
|
|
695
721
|
)
|
|
696
722
|
|
|
697
723
|
# Perform histogram matching on all other ensemble members
|
|
@@ -702,7 +728,7 @@ def histogram_matching_all_members(
|
|
|
702
728
|
ensemble_mean_rhi += ensemble_member_rhi
|
|
703
729
|
else:
|
|
704
730
|
ensemble_mean_rhi += histogram_matching(
|
|
705
|
-
era5_rhi_all_members[:, r], "ensemble_members", r, q_method
|
|
731
|
+
era5_rhi_all_members[:, r], "ensemble_members", "pressure", r, q_method
|
|
706
732
|
)
|
|
707
733
|
|
|
708
734
|
# Divide by the number of ensemble members to get the mean
|
|
@@ -766,6 +792,9 @@ class HistogramMatchingParams(models.ModelParams):
|
|
|
766
792
|
#: The ERA5 product. Must be one of ``"reanalysis"`` or ``"ensemble_members"``.
|
|
767
793
|
product_type: str = "reanalysis"
|
|
768
794
|
|
|
795
|
+
#: The ERA5 vertical level type. Must be one of ``"pressure"`` or ``"model"``.
|
|
796
|
+
level_type: str = "pressure"
|
|
797
|
+
|
|
769
798
|
#: The ERA5 ensemble member to use. Must be in the range ``[0, 10)``.
|
|
770
799
|
#: Only used if ``product_type`` is ``"ensemble_members"``.
|
|
771
800
|
member: int | None = None
|
|
@@ -779,6 +808,23 @@ class HistogramMatching(HumidityScaling):
|
|
|
779
808
|
formula = "era5_quantiles -> iagos_quantiles"
|
|
780
809
|
default_params = HistogramMatchingParams
|
|
781
810
|
|
|
811
|
+
def __init__(
|
|
812
|
+
self,
|
|
813
|
+
met: MetDataset | None = None,
|
|
814
|
+
params: dict[str, Any] | None = None,
|
|
815
|
+
**params_kwargs: Any,
|
|
816
|
+
):
|
|
817
|
+
if (params is None or "level_type" not in params) and (
|
|
818
|
+
params_kwargs is None or "level_type" not in params_kwargs
|
|
819
|
+
):
|
|
820
|
+
msg = (
|
|
821
|
+
"The default level_type will change from 'pressure' to 'model' "
|
|
822
|
+
"in a future release. To silence this warning, "
|
|
823
|
+
"provide a 'level_type' value when instantiating HistogramMatching."
|
|
824
|
+
)
|
|
825
|
+
warnings.warn(msg, DeprecationWarning)
|
|
826
|
+
super().__init__(met, params, **params_kwargs)
|
|
827
|
+
|
|
782
828
|
@overrides
|
|
783
829
|
def scale(
|
|
784
830
|
self,
|
|
@@ -793,6 +839,7 @@ class HistogramMatching(HumidityScaling):
|
|
|
793
839
|
rhi_1 = histogram_matching(
|
|
794
840
|
rhi,
|
|
795
841
|
self.params["product_type"],
|
|
842
|
+
self.params["level_type"],
|
|
796
843
|
self.params["member"],
|
|
797
844
|
self.params["interpolation_q_method"],
|
|
798
845
|
)
|
|
@@ -14,7 +14,7 @@ import pandas as pd
|
|
|
14
14
|
from pycontrails.physics import constants as c
|
|
15
15
|
|
|
16
16
|
#: Path to the Poll-Schumann aircraft parameters CSV file.
|
|
17
|
-
PS_FILE_PATH = pathlib.Path(__file__).parent / "static" / "ps-aircraft-params-
|
|
17
|
+
PS_FILE_PATH = pathlib.Path(__file__).parent / "static" / "ps-aircraft-params-20240524.csv"
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
@dataclasses.dataclass(frozen=True)
|
|
@@ -192,8 +192,23 @@ def _row_to_aircraft_engine_params(tup: Any) -> tuple[str, PSAircraftEngineParam
|
|
|
192
192
|
|
|
193
193
|
|
|
194
194
|
@functools.cache
|
|
195
|
-
def load_aircraft_engine_params(
|
|
196
|
-
|
|
195
|
+
def load_aircraft_engine_params(
|
|
196
|
+
engine_deterioration_factor: float = 0.025,
|
|
197
|
+
) -> Mapping[str, PSAircraftEngineParams]:
|
|
198
|
+
"""
|
|
199
|
+
Extract aircraft-engine parameters for each aircraft type supported by the PS model.
|
|
200
|
+
|
|
201
|
+
Parameters
|
|
202
|
+
----------
|
|
203
|
+
engine_deterioration_factor: float
|
|
204
|
+
Account for "in-service" engine deterioration between maintenance cycles.
|
|
205
|
+
Default value reduces `eta_1` by 2.5%, which increases the fuel flow estimates by 2.5%.
|
|
206
|
+
|
|
207
|
+
Returns
|
|
208
|
+
-------
|
|
209
|
+
Mapping[str, PSAircraftEngineParams]
|
|
210
|
+
Aircraft-engine parameters for each aircraft type supported by the PS model.
|
|
211
|
+
"""
|
|
197
212
|
dtypes = {
|
|
198
213
|
"ICAO": object,
|
|
199
214
|
"Manufacturer": object,
|
|
@@ -239,6 +254,7 @@ def load_aircraft_engine_params() -> Mapping[str, PSAircraftEngineParams]:
|
|
|
239
254
|
}
|
|
240
255
|
|
|
241
256
|
df = pd.read_csv(PS_FILE_PATH, dtype=dtypes)
|
|
257
|
+
df["eta_1"] = df["eta_1"] * (1.0 - engine_deterioration_factor)
|
|
242
258
|
|
|
243
259
|
return dict(_row_to_aircraft_engine_params(tup) for tup in df.itertuples(index=False))
|
|
244
260
|
|
|
@@ -83,7 +83,7 @@ class PSGrid(AircraftPerformanceGrid):
|
|
|
83
83
|
from the :attr:`met` attribute of the :class:`PSGrid` instance.
|
|
84
84
|
The aircraft type is taken from ``source.attrs["aircraft_type"]``. If this field
|
|
85
85
|
is not present, ``params["aircraft_type"]`` is used instead. See the
|
|
86
|
-
static CSV file :file:`ps-aircraft-params-
|
|
86
|
+
static CSV file :file:`ps-aircraft-params-20240524.csv` for a list of supported
|
|
87
87
|
aircraft types.
|
|
88
88
|
**params : Any
|
|
89
89
|
Override the default parameters of the :class:`PSGrid` instance.
|
|
@@ -389,32 +389,32 @@ def ps_nominal_grid(
|
|
|
389
389
|
>>> perf.to_dataframe()
|
|
390
390
|
aircraft_mass engine_efficiency fuel_flow
|
|
391
391
|
level
|
|
392
|
-
200.0 58416.
|
|
393
|
-
210.0 61617.
|
|
394
|
-
220.0 64829.702583 0.
|
|
395
|
-
230.0 68026.
|
|
396
|
-
240.0 71187.
|
|
397
|
-
250.0
|
|
398
|
-
260.0
|
|
399
|
-
270.0
|
|
400
|
-
280.0
|
|
401
|
-
290.0
|
|
392
|
+
200.0 58416.230843 0.300958 0.575635
|
|
393
|
+
210.0 61617.676624 0.300958 0.604417
|
|
394
|
+
220.0 64829.702583 0.300958 0.633199
|
|
395
|
+
230.0 68026.415695 0.300958 0.662998
|
|
396
|
+
240.0 71187.897060 0.300958 0.694631
|
|
397
|
+
250.0 71775.399825 0.300824 0.703349
|
|
398
|
+
260.0 71765.716737 0.300363 0.708259
|
|
399
|
+
270.0 71752.405400 0.299671 0.714514
|
|
400
|
+
280.0 71736.129079 0.298823 0.721878
|
|
401
|
+
290.0 71717.392170 0.297875 0.730169
|
|
402
402
|
|
|
403
403
|
>>> # Now compute it for a higher Mach number
|
|
404
404
|
>>> perf = ps_nominal_grid("A320", level=level, mach_number=0.78)
|
|
405
405
|
>>> perf.to_dataframe()
|
|
406
406
|
aircraft_mass engine_efficiency fuel_flow
|
|
407
407
|
level
|
|
408
|
-
200.0
|
|
409
|
-
210.0 60626.062062 0.
|
|
410
|
-
220.0 63818.
|
|
411
|
-
230.0 66993.
|
|
412
|
-
240.0 70129.930503 0.
|
|
413
|
-
250.0
|
|
414
|
-
260.0
|
|
415
|
-
270.0
|
|
416
|
-
280.0
|
|
417
|
-
290.0
|
|
408
|
+
200.0 57941.825236 0.306598 0.596100
|
|
409
|
+
210.0 60626.062062 0.306605 0.621331
|
|
410
|
+
220.0 63818.498306 0.306605 0.650918
|
|
411
|
+
230.0 66993.691517 0.306605 0.681551
|
|
412
|
+
240.0 70129.930503 0.306605 0.714069
|
|
413
|
+
250.0 71703.009059 0.306560 0.732944
|
|
414
|
+
260.0 71690.188652 0.306239 0.739276
|
|
415
|
+
270.0 71673.392089 0.305694 0.747052
|
|
416
|
+
280.0 71653.431321 0.304997 0.755990
|
|
417
|
+
290.0 71630.901315 0.304201 0.765883
|
|
418
418
|
"""
|
|
419
419
|
coords: dict[str, Any] | xrcc.DataArrayCoordinates
|
|
420
420
|
if isinstance(air_temperature, xr.DataArray):
|
|
@@ -3,11 +3,14 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import dataclasses
|
|
6
|
+
import functools
|
|
7
|
+
import pathlib
|
|
6
8
|
from collections.abc import Mapping
|
|
7
9
|
from typing import Any, NoReturn, overload
|
|
8
10
|
|
|
9
11
|
import numpy as np
|
|
10
12
|
import numpy.typing as npt
|
|
13
|
+
import pandas as pd
|
|
11
14
|
from overrides import overrides
|
|
12
15
|
|
|
13
16
|
from pycontrails.core import flight
|
|
@@ -30,6 +33,9 @@ from pycontrails.utils.types import ArrayOrFloat
|
|
|
30
33
|
|
|
31
34
|
# mypy: disable-error-code = "type-var, arg-type"
|
|
32
35
|
|
|
36
|
+
#: Path to the Poll-Schumann aircraft parameters CSV file.
|
|
37
|
+
PS_SYNONYM_FILE_PATH = pathlib.Path(__file__).parent / "static" / "ps-synonym-list-20240524.csv"
|
|
38
|
+
|
|
33
39
|
|
|
34
40
|
@dataclasses.dataclass
|
|
35
41
|
class PSFlightParams(AircraftPerformanceParams):
|
|
@@ -39,6 +45,13 @@ class PSFlightParams(AircraftPerformanceParams):
|
|
|
39
45
|
#: efficiency to always exceed this value.
|
|
40
46
|
eta_over_eta_b_min: float | None = 0.5
|
|
41
47
|
|
|
48
|
+
#: Account for "in-service" engine deterioration between maintenance cycles.
|
|
49
|
+
#: Default value is set to +2.5% increase in fuel consumption.
|
|
50
|
+
# Reference:
|
|
51
|
+
# Gurrola Arrieta, M.D.J., Botez, R.M. and Lasne, A., 2024. An Engine Deterioration Model for
|
|
52
|
+
# Predicting Fuel Consumption Impact in a Regional Aircraft. Aerospace, 11(6), p.426.
|
|
53
|
+
engine_deterioration_factor: float = 0.025
|
|
54
|
+
|
|
42
55
|
|
|
43
56
|
class PSFlight(AircraftPerformance):
|
|
44
57
|
"""Simulate aircraft performance using Poll-Schumann (PS) model.
|
|
@@ -68,7 +81,10 @@ class PSFlight(AircraftPerformance):
|
|
|
68
81
|
**params_kwargs: Any,
|
|
69
82
|
) -> None:
|
|
70
83
|
super().__init__(met=met, params=params, **params_kwargs)
|
|
71
|
-
self.aircraft_engine_params = load_aircraft_engine_params(
|
|
84
|
+
self.aircraft_engine_params = load_aircraft_engine_params(
|
|
85
|
+
self.params["engine_deterioration_factor"]
|
|
86
|
+
)
|
|
87
|
+
self.synonym_dict = get_aircraft_synonym_dict_ps()
|
|
72
88
|
|
|
73
89
|
def check_aircraft_type_availability(
|
|
74
90
|
self, aircraft_type: str, raise_error: bool = True
|
|
@@ -92,7 +108,7 @@ class PSFlight(AircraftPerformance):
|
|
|
92
108
|
KeyError
|
|
93
109
|
raises KeyError if the aircraft type is not covered by database
|
|
94
110
|
"""
|
|
95
|
-
if aircraft_type in self.aircraft_engine_params:
|
|
111
|
+
if aircraft_type in self.aircraft_engine_params or aircraft_type in self.synonym_dict:
|
|
96
112
|
return True
|
|
97
113
|
if raise_error:
|
|
98
114
|
msg = f"Aircraft type {aircraft_type} not covered by the PS model."
|
|
@@ -125,13 +141,15 @@ class PSFlight(AircraftPerformance):
|
|
|
125
141
|
raise KeyError(msg) from exc
|
|
126
142
|
|
|
127
143
|
try:
|
|
128
|
-
|
|
144
|
+
atyp_ps = self.synonym_dict.get(aircraft_type) or aircraft_type
|
|
145
|
+
aircraft_params = self.aircraft_engine_params[atyp_ps]
|
|
129
146
|
except KeyError as exc:
|
|
130
147
|
msg = f"Aircraft type {aircraft_type} not covered by the PS model."
|
|
131
148
|
raise KeyError(msg) from exc
|
|
132
149
|
|
|
133
150
|
# Set flight attributes based on engine, if they aren't already defined
|
|
134
151
|
self.source.attrs.setdefault("aircraft_performance_model", self.name)
|
|
152
|
+
self.source.attrs.setdefault("aircraft_type_ps", atyp_ps)
|
|
135
153
|
self.source.attrs.setdefault("n_engine", aircraft_params.n_engine)
|
|
136
154
|
|
|
137
155
|
self.source.attrs.setdefault("wingspan", aircraft_params.wing_span)
|
|
@@ -148,7 +166,7 @@ class PSFlight(AircraftPerformance):
|
|
|
148
166
|
|
|
149
167
|
# Run the simulation
|
|
150
168
|
aircraft_performance = self.simulate_fuel_and_performance(
|
|
151
|
-
aircraft_type=
|
|
169
|
+
aircraft_type=atyp_ps,
|
|
152
170
|
altitude_ft=self.source.altitude_ft,
|
|
153
171
|
time=self.source["time"],
|
|
154
172
|
true_airspeed=true_airspeed,
|
|
@@ -242,7 +260,7 @@ class PSFlight(AircraftPerformance):
|
|
|
242
260
|
|
|
243
261
|
elif isinstance(time, np.ndarray):
|
|
244
262
|
dt_sec = flight.segment_duration(time, dtype=altitude_ft.dtype)
|
|
245
|
-
rocd = flight.segment_rocd(dt_sec, altitude_ft)
|
|
263
|
+
rocd = flight.segment_rocd(dt_sec, altitude_ft, air_temperature)
|
|
246
264
|
dv_dt = jet.acceleration(true_airspeed, dt_sec)
|
|
247
265
|
theta = jet.climb_descent_angle(true_airspeed, rocd)
|
|
248
266
|
|
|
@@ -299,7 +317,7 @@ class PSFlight(AircraftPerformance):
|
|
|
299
317
|
|
|
300
318
|
# Flight phase
|
|
301
319
|
segment_duration = flight.segment_duration(time, dtype=altitude_ft.dtype)
|
|
302
|
-
rocd = flight.segment_rocd(segment_duration, altitude_ft)
|
|
320
|
+
rocd = flight.segment_rocd(segment_duration, altitude_ft, air_temperature)
|
|
303
321
|
|
|
304
322
|
if correct_fuel_flow:
|
|
305
323
|
flight_phase = flight.segment_phase(rocd, altitude_ft)
|
|
@@ -980,3 +998,20 @@ def fuel_flow_correction(
|
|
|
980
998
|
descent = flight_phase == flight.FlightPhase.DESCENT
|
|
981
999
|
ff_max[descent] = 0.3 * fuel_flow_max_sls
|
|
982
1000
|
return np.clip(fuel_flow, ff_min, ff_max)
|
|
1001
|
+
|
|
1002
|
+
|
|
1003
|
+
@functools.cache
|
|
1004
|
+
def get_aircraft_synonym_dict_ps() -> dict[str, str]:
|
|
1005
|
+
"""Read `ps-synonym-list-20240524.csv` from the static directory.
|
|
1006
|
+
|
|
1007
|
+
Returns
|
|
1008
|
+
-------
|
|
1009
|
+
dict[str, str]
|
|
1010
|
+
Dictionary of the form ``{"icao_aircraft_type": "ps_aircraft_type"}``.
|
|
1011
|
+
"""
|
|
1012
|
+
# get path to static PS synonym list
|
|
1013
|
+
synonym_path = pathlib.Path(__file__).parent / "static" / "ps-synonym-list-20240524.csv"
|
|
1014
|
+
df_atyp_icao_to_ps = pd.read_csv(
|
|
1015
|
+
synonym_path, usecols=["ICAO Aircraft Code", "PS ATYP"], index_col=0
|
|
1016
|
+
)
|
|
1017
|
+
return df_atyp_icao_to_ps.squeeze("columns").to_dict()
|
|
@@ -118,7 +118,7 @@ def max_available_thrust_coefficient(
|
|
|
118
118
|
c_t_eta_b: ArrayOrFloat,
|
|
119
119
|
atyp_param: PSAircraftEngineParams,
|
|
120
120
|
*,
|
|
121
|
-
buffer: float = 0.
|
|
121
|
+
buffer: float = 0.20,
|
|
122
122
|
) -> ArrayOrFloat:
|
|
123
123
|
"""
|
|
124
124
|
Calculate maximum available thrust coefficient.
|
|
@@ -134,8 +134,8 @@ def max_available_thrust_coefficient(
|
|
|
134
134
|
atyp_param : PSAircraftEngineParams
|
|
135
135
|
Extracted aircraft and engine parameters.
|
|
136
136
|
buffer : float, optional
|
|
137
|
-
Additional buffer for maximum
|
|
138
|
-
which increases the maximum
|
|
137
|
+
Additional buffer for maximum throttle parameter `tr_max`. The default value recommended by
|
|
138
|
+
Ian Poll is 0.2, which increases the maximum throttle parameter by 20%.
|
|
139
139
|
|
|
140
140
|
Returns
|
|
141
141
|
-------
|
|
@@ -148,9 +148,10 @@ def max_available_thrust_coefficient(
|
|
|
148
148
|
atyp_param.tet_mcc,
|
|
149
149
|
atyp_param.tr_ec,
|
|
150
150
|
atyp_param.m_ec,
|
|
151
|
+
buffer=buffer,
|
|
151
152
|
)
|
|
152
153
|
c_t_max_over_c_t_eta_b = 1.0 + 2.5 * (tr_max - 1.0)
|
|
153
|
-
return c_t_max_over_c_t_eta_b * c_t_eta_b
|
|
154
|
+
return c_t_max_over_c_t_eta_b * c_t_eta_b
|
|
154
155
|
|
|
155
156
|
|
|
156
157
|
def get_excess_thrust_available(
|
|
@@ -234,6 +235,8 @@ def _normalised_max_throttle_parameter(
|
|
|
234
235
|
tet_mcc: float,
|
|
235
236
|
tr_ec: float,
|
|
236
237
|
m_ec: float,
|
|
238
|
+
*,
|
|
239
|
+
buffer: float = 0.20,
|
|
237
240
|
) -> ArrayOrFloat:
|
|
238
241
|
"""
|
|
239
242
|
Calculate normalised maximum throttle parameter.
|
|
@@ -251,6 +254,10 @@ def _normalised_max_throttle_parameter(
|
|
|
251
254
|
temperature for maximum overall efficiency.
|
|
252
255
|
m_ec : float
|
|
253
256
|
Engine characteristic Mach number associated with `tr_ec`.
|
|
257
|
+
buffer : float, optional
|
|
258
|
+
Additional buffer for maximum throttle parameter. The default value recommended by Ian Poll
|
|
259
|
+
is 0.2, which increases the maximum throttle parameter by 20%. This affects the maximum
|
|
260
|
+
available thrust coefficient calculated downstream.
|
|
254
261
|
|
|
255
262
|
Returns
|
|
256
263
|
-------
|
|
@@ -263,8 +270,10 @@ def _normalised_max_throttle_parameter(
|
|
|
263
270
|
entry to the freestream total temperature, normalised with its value for maximum engine
|
|
264
271
|
overall efficiency at the same freestream Mach number.
|
|
265
272
|
"""
|
|
266
|
-
return (
|
|
267
|
-
|
|
273
|
+
return (
|
|
274
|
+
(tet_mcc / air_temperature)
|
|
275
|
+
/ (tr_ec * (1.0 - 0.53 * (mach_number - m_ec) ** 2) * (1.0 + 0.2 * mach_number**2))
|
|
276
|
+
* (1.0 + buffer)
|
|
268
277
|
)
|
|
269
278
|
|
|
270
279
|
|