|
1 | 1 | """ |
2 | 2 | U.S. Standard Atmosphere 1976 thermophysical model. |
3 | 3 |
|
4 | | -U.S. Standard Atmosphere, 1976 thermophysical model according to |
5 | | -:cite:`NASA1976USStandardAtmosphere`. |
| 4 | +The U.S. Standard Atmosphere 1976 model :cite:`NASA1976USStandardAtmosphere` |
| 5 | +divides the atmosphere into two altitude regions: |
| 6 | +
|
| 7 | +1. the low-altitude region, from 0 to 86 kilometers |
| 8 | +2. the high-altitude region, from 86 to 1000 kilometers. |
| 9 | +
|
| 10 | +A number of computational functions hereafter are specialised for one or |
| 11 | +the other altitude region and is valid only in that altitude region, not in |
| 12 | +the other. |
| 13 | +Their name include a ``low_altitude`` or a ``high_altitude`` part to reflect |
| 14 | +that they are valid only in the low altitude region and high altitude region, |
| 15 | +respectively. |
6 | 16 | """ |
7 | 17 | import datetime |
8 | 18 | import typing as t |
9 | 19 |
|
10 | 20 | import numpy as np |
11 | 21 | import numpy.ma as ma |
12 | 22 | import numpy.typing as npt |
13 | | -import pint |
14 | 23 | import xarray as xr |
15 | 24 | from scipy.integrate import cumulative_trapezoid |
16 | 25 | from scipy.interpolate import interp1d |
|
62 | 71 | from .constants import Z7 |
63 | 72 | from .constants import Z8 |
64 | 73 | from .constants import Z9 |
65 | | -from .units import ureg |
66 | | - |
67 | | -# ------------------------------------------------------------------------------ |
68 | | -# |
69 | | -# Atmospheric vertical profile data set generator |
70 | | -# |
71 | | -# ------------------------------------------------------------------------------ |
72 | | - |
73 | | -_DEFAULT_LEVELS = np.linspace(0.0, 100.0, 51) * ureg.km |
74 | | - |
75 | | - |
76 | | -def make( |
77 | | - levels: pint.Quantity = _DEFAULT_LEVELS, # type: ignore |
78 | | -) -> xr.Dataset: |
79 | | - """Make U.S. Standard Atmosphere 1976. |
80 | | -
|
81 | | - Parameters |
82 | | - ---------- |
83 | | - levels: quantity, optional |
84 | | - Level altitudes. |
85 | | - The values must be sorted by increasing order. |
86 | | - The default levels are 51 linearly spaced values between 0 and 100 km. |
87 | | -
|
88 | | - Valid range: 0 to 1000 km. |
89 | | -
|
90 | | - Returns |
91 | | - ------- |
92 | | - Dataset |
93 | | - Data set holding the values of the pressure, temperature, |
94 | | - total number density and number densities of the individual |
95 | | - gas species in each layer. |
96 | | -
|
97 | | - Raises |
98 | | - ------ |
99 | | - ValueError |
100 | | - When levels are out of range. |
101 | | -
|
102 | | - Notes |
103 | | - ----- |
104 | | - The pressure, temperature and number densities given in each layer of |
105 | | - the altitude mesh are computed at the altitude of the layers centers. |
106 | | - In other words, the layer's middle is taken as the altitude |
107 | | - representative of the whole layer. For example, in a layer with lower |
108 | | - and upper altitudes of 1000 and 2000 m, the thermophysical variables |
109 | | - are computed at the altitude of 1500 m. |
110 | | - """ |
111 | | - if np.any(levels > ureg.Quantity(1e6, "m")) or np.any(levels < 0.0): |
112 | | - raise ValueError("Levels altitudes must be in [0, 1e6] m.") |
113 | | - |
114 | | - z_layer = (levels[:-1] + levels[1:]) / 2 |
115 | | - |
116 | | - # create the data set |
117 | | - ds = compute( |
118 | | - z=z_layer.m_as("m"), |
119 | | - variables=["p", "t", "n", "n_tot"], |
120 | | - ) |
121 | | - |
122 | | - # derive atmospheric thermophysical properties profile data set |
123 | | - thermoprops_ds = ( |
124 | | - xr.Dataset( |
125 | | - data_vars={ |
126 | | - "p": ds.p, |
127 | | - "t": ds.t, |
128 | | - "n": ds.n_tot, |
129 | | - "mr": ds.n / ds.n_tot, |
130 | | - } |
131 | | - ) |
132 | | - .rename_dims({"z": "z_layer"}) |
133 | | - .reset_coords("z", drop=True) |
134 | | - ) |
135 | | - thermoprops_ds.coords["z_layer"] = ( |
136 | | - "z_layer", |
137 | | - z_layer.magnitude, |
138 | | - dict( |
139 | | - standard_name="layer_altitude", |
140 | | - long_name="layer altitude", |
141 | | - units=str(z_layer.units), |
142 | | - ), |
143 | | - ) |
144 | | - thermoprops_ds.coords["z_level"] = ( |
145 | | - "z_level", |
146 | | - levels.magnitude, |
147 | | - dict( |
148 | | - standard_name="level_altitude", |
149 | | - long_name="level altitude", |
150 | | - units=str(levels.units), |
151 | | - ), |
152 | | - ) |
153 | | - thermoprops_ds.attrs = dict( |
154 | | - convention="CF-1.8", |
155 | | - title="U.S. Standard Atmosphere 1976", |
156 | | - history=f"{datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')} - " |
157 | | - f"data creation - ussa1976, version {__version__}", |
158 | | - source=f"ussa1976, version {__version__}", |
159 | | - references="U.S. Standard Atmosphere, 1976, NASA-TM-X-74335, " |
160 | | - "NOAA-S/T-76-1562", |
161 | | - ) |
162 | | - |
163 | | - return thermoprops_ds |
164 | 74 |
|
165 | 75 |
|
166 | 76 | # List of all gas species |
167 | | -SPECIES = ["N2", "O2", "Ar", "CO2", "Ne", "He", "Kr", "Xe", "CH4", "H2", "O", "H"] |
| 77 | +SPECIES = [ |
| 78 | + "N2", |
| 79 | + "O2", |
| 80 | + "Ar", |
| 81 | + "CO2", |
| 82 | + "Ne", |
| 83 | + "He", |
| 84 | + "Kr", |
| 85 | + "Xe", |
| 86 | + "CH4", |
| 87 | + "H2", |
| 88 | + "O", |
| 89 | + "H", |
| 90 | +] |
168 | 91 |
|
169 | 92 | # List of variables computed by the model |
170 | 93 | VARIABLES = [ |
@@ -245,20 +168,6 @@ def make( |
245 | 168 | } |
246 | 169 |
|
247 | 170 |
|
248 | | -# ------------------------------------------------------------------------------ |
249 | | -# |
250 | | -# Computational functions. |
251 | | -# The U.S. Standard Atmosphere 1976 model divides the atmosphere into two |
252 | | -# altitude regions: |
253 | | -# 1. the low-altitude region, from 0 to 86 kilometers |
254 | | -# 2. the high-altitude region, from 86 to 1000 kilometers. |
255 | | -# The majority of computational functions hereafter are specialised for one or |
256 | | -# the other altitude region and is valid only in that altitude region, not in |
257 | | -# the other. |
258 | | -# |
259 | | -# ------------------------------------------------------------------------------ |
260 | | - |
261 | | - |
262 | 171 | def compute( |
263 | 172 | z: npt.NDArray[np.float64], |
264 | 173 | variables: t.Optional[t.List[str]] = None, |
|
0 commit comments