Skip to content

Commit 25db917

Browse files
committed
much improved accuracy / fewer iterations by switching atmosphere lookup to a log interpolation
1 parent c521c0a commit 25db917

File tree

2 files changed

+49
-23
lines changed

2 files changed

+49
-23
lines changed

pyNastran/utils/atmosphere.py

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838

3939

4040
def get_alt_for_density(density: float, density_units: str='slug/ft^3',
41-
alt_units: str='ft', nmax: int=20, tol: float=5.) -> float:
41+
alt_units: str='ft', nmax: int=20, tol: float=0.1) -> float:
4242
"""
4343
Gets the altitude associated with a given air density.
4444
@@ -52,7 +52,7 @@ def get_alt_for_density(density: float, density_units: str='slug/ft^3',
5252
sets the units for the output altitude; ft, m, kft
5353
nmax : int; default=20
5454
max number of iterations for convergence
55-
tol : float; default=5.
55+
tol : float; default=0.1
5656
tolerance in alt_units
5757
5858
Returns
@@ -71,15 +71,20 @@ def get_alt_for_density(density: float, density_units: str='slug/ft^3',
7171

7272
# Newton's method
7373
alt1 = alt_old
74+
log_density = np.log(density)
7475
while abs(alt_final - alt_old) > tol and n < nmax:
7576
alt_old = alt_final
7677
alt1 = alt_old
7778
alt2 = alt_old + dalt
7879
rho1: float = atm_density(alt1, density_units=density_units)
7980
rho2: float = atm_density(alt2, density_units=density_units)
80-
m = dalt / (rho2 - rho1)
81-
alt_final = m * (density - rho1) + alt1
81+
# natural log
82+
log_rho1 = np.log(rho1)
83+
log_rho2 = np.log(rho2)
84+
m = dalt / (log_rho2 - log_rho1)
85+
alt_final = m * (log_density - log_rho1) + alt1
8286
n += 1
87+
8388
if abs(alt_final - alt_old) > tol:
8489
raise RuntimeError(f'Did not converge; Check your units; n=nmax={nmax}\n'
8590
f'target alt={alt_final} alt_current={alt1}')
@@ -91,7 +96,7 @@ def get_alt_for_eas_with_constant_mach(equivalent_airspeed: float,
9196
mach: float,
9297
velocity_units: str='ft/s',
9398
alt_units: str='ft',
94-
nmax: int=20, tol: float=5.) -> float:
99+
nmax: int=20, tol: float=0.1) -> float:
95100
"""
96101
Gets the altitude associated with a equivalent airspeed.
97102
@@ -131,6 +136,7 @@ def get_alt_for_eas_with_constant_mach(equivalent_airspeed: float,
131136
k = np.sqrt(T0 / p0)
132137
#eas = a * mach * sqrt((p * T0) / (T * p0)) = a * mach * sqrt(p / T) * k
133138

139+
log_eas = np.log(equivalent_airspeed)
134140
# Newton's method
135141
while abs(alt_final - alt_old) > tol and n < nmax:
136142
alt_old = alt_final
@@ -144,8 +150,10 @@ def get_alt_for_eas_with_constant_mach(equivalent_airspeed: float,
144150
sos2 = np.sqrt(1.4 * R * T2)
145151
eas1 = sos1 * mach * np.sqrt(press1 / T1) * k
146152
eas2 = sos2 * mach * np.sqrt(press2 / T2) * k
147-
m = dalt / (eas2 - eas1)
148-
alt_final = m * (equivalent_airspeed - eas1) + alt1
153+
log_eas1 = np.log(eas1)
154+
log_eas2 = np.log(eas2)
155+
m = dalt / (log_eas2 - log_eas1)
156+
alt_final = m * (log_eas - log_eas1) + alt1
149157
n += 1
150158

151159
if n > nmax - 1:
@@ -156,7 +164,7 @@ def get_alt_for_eas_with_constant_mach(equivalent_airspeed: float,
156164

157165
def get_alt_for_q_with_constant_mach(q: float, mach: float,
158166
pressure_units: str='psf', alt_units: str='ft',
159-
nmax: int=20, tol: float=5.) -> float:
167+
nmax: int=20, tol: float=0.1) -> float:
160168
"""
161169
Gets the altitude associated with a dynamic pressure.
162170
@@ -172,7 +180,7 @@ def get_alt_for_q_with_constant_mach(q: float, mach: float,
172180
the altitude units; ft, kft, m
173181
nmax : int; default=20
174182
max number of iterations for convergence
175-
tol : float; default=5.
183+
tol : float; default=0.1
176184
tolerance in alt_units
177185
178186
Returns
@@ -189,7 +197,7 @@ def get_alt_for_q_with_constant_mach(q: float, mach: float,
189197

190198
def get_alt_for_pressure(pressure: float,
191199
pressure_units: str='psf', alt_units: str='ft',
192-
nmax: int=20, tol: float=5.) -> float:
200+
nmax: int=20, tol: float=0.1) -> float:
193201
"""
194202
Gets the altitude associated with a pressure.
195203
@@ -203,7 +211,7 @@ def get_alt_for_pressure(pressure: float,
203211
the altitude units; ft, kft, m
204212
nmax : int; default=20
205213
max number of iterations for convergence
206-
tol : float; default=5.
214+
tol : float; default=0.1
207215
altitude tolerance in alt_units
208216
209217
Returns
@@ -220,14 +228,17 @@ def get_alt_for_pressure(pressure: float,
220228
n = 0
221229

222230
# Newton's method
231+
log_pressure = np.log(pressure)
223232
while abs(alt_final - alt_old) > tol and n < nmax:
224233
alt_old = alt_final
225234
alt1 = alt_old
226235
alt2 = alt_old + dalt
227236
press1 = atm_pressure(alt1)
228237
press2 = atm_pressure(alt2)
229-
m = dalt / (press2 - press1)
230-
alt_final = m * (pressure - press1) + alt1
238+
log_press1 = np.log(press1)
239+
log_press2 = np.log(press2)
240+
m = dalt / (log_press2 - log_press1)
241+
alt_final = m * (log_pressure - log_press1) + alt1
231242
n += 1
232243

233244
if n > nmax - 1:
@@ -243,7 +254,7 @@ def get_alt_for_pressure(pressure: float,
243254
def get_alt_for_mach_eas(mach: float,
244255
eas: float, eas_units: str='knots',
245256
alt_units: str='ft',
246-
nmax: int=20, tol: float=5.) -> float:
257+
nmax: int=20, tol: float=0.1) -> float:
247258
"""
248259
Gets the altitude associated with an equivalent airspeed.
249260
@@ -259,7 +270,7 @@ def get_alt_for_mach_eas(mach: float,
259270
the altitude units; ft, kft, m
260271
nmax : int; default=20
261272
max number of iterations for convergence
262-
tol : float; default=5.
273+
tol : float; default=0.1
263274
altitude tolerance in alt_units
264275
265276
Returns
@@ -276,17 +287,20 @@ def get_alt_for_mach_eas(mach: float,
276287
n = 0
277288

278289
# Newton's method
290+
log_eas = np.log(eas)
279291
while abs(alt_final - alt_old) > tol and n < nmax:
280292
alt_old = alt_final
281293
alt1 = alt_old
282294
alt2 = alt_old + dalt
283295
eas1 = atm_equivalent_airspeed(alt1, mach, alt_units='ft', eas_units='ft/s')
284296
eas2 = atm_equivalent_airspeed(alt2, mach, alt_units='ft', eas_units='ft/s')
297+
log_eas1 = np.log(eas1)
298+
log_eas2 = np.log(eas2)
285299

286300
# y = (y2-y1)/(x2-x1)*(x-x1) + y1
287301
# y = m * (x-x1) + y1
288-
m = dalt / (eas2 - eas1)
289-
alt_final = m * (eas - eas1) + alt1
302+
m = dalt / (log_eas2 - log_eas1)
303+
alt_final = m * (log_eas - log_eas1) + alt1
290304
n += 1
291305

292306
if n > nmax - 1:
@@ -1236,6 +1250,7 @@ def make_flfacts_eas_sweep_constant_mach(mach: float,
12361250
#
12371251
# then pressure in a sane unit (e.g., psi/psf/Pa)
12381252
# without a wacky conversion
1253+
assert eas.min() > 0, eas
12391254
eas_fts = convert_velocity(eas, eas_units, 'ft/s')
12401255
rho0_english: float = atm_density(
12411256
0., R=1716., alt_units=alt_units,
@@ -1250,6 +1265,7 @@ def make_flfacts_eas_sweep_constant_mach(mach: float,
12501265
alt_fti = get_alt_for_pressure(pressure_psfi, pressure_units='psf',
12511266
alt_units='ft', nmax=30, tol=1.)
12521267
rhoi = atm_density(alt_fti, R=1716., alt_units='ft', density_units=density_units)
1268+
assert np.isfinite(alt_ft[i]), alt_fti
12531269
rho[i] = rhoi
12541270
alt_ft[i] = alt_fti
12551271
alt = convert_altitude(alt_ft, 'ft', alt_units)

pyNastran/utils/test/test_atmosphere.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -515,23 +515,26 @@ def test_sutherland_viscoscity(self):
515515

516516
def test_get_alt_for_density(self):
517517
"""tests ``get_alt_for_density``"""
518-
alt_targets = [0., 10., 20., 30., 40., 50.]
518+
alt_targets = [
519+
0., 10., 20., 30., 40., 50.,
520+
300., 350., 400.,
521+
]
519522
for alt_target in alt_targets:
520523
rho1 = atm_density(alt_target * 1000.)
521524
alt1 = get_alt_for_density(rho1)
522525
#self.assertAlmostEqual(alt, alt_target)
523526
assert np.allclose(alt1, alt_target*1000, atol=1.), 'alt1=%s alt_target=%s' % (alt1, alt_target*1000)
524527

525528
rho2 = atm_density(alt_target, alt_units='kft', density_units='kg/m^3')
526-
tol = 0.005 # 5 feet
527-
alt2 = get_alt_for_density(rho2, density_units='kg/m^3', alt_units='kft', tol=tol)
529+
tol = 0.005 # 5 feet
530+
alt2 = get_alt_for_density(rho2, density_units='kg/m^3', alt_units='kft', tol=tol, nmax=50)
528531
#self.assertAlmostEqual(alt, alt_target)
529532
assert np.allclose(alt2, alt_target, atol=1e-3), 'alt2=%s alt_target=%s' % (alt2, alt_target)
530533

531534
def test_get_alt_for_mach_eas(self):
532535
"""tests ``get_alt_for_mach_eas``"""
533536
mach = 0.8
534-
eas_target = 500. # knots
537+
eas_target = 500. # knots
535538
alt_expected = 3067.215571329515 # ft
536539
alt = get_alt_for_mach_eas(mach, eas_target, alt_units='ft', eas_units='knots', tol=1e-12)
537540
assert np.allclose(alt, alt_expected)
@@ -542,7 +545,10 @@ def test_get_alt_for_mach_eas(self):
542545

543546
def test_get_alt_for_pressure(self):
544547
"""tests ``get_alt_for_pressure``"""
545-
alt_targets = [0., 10., 20., 30., 40., 50.]
548+
alt_targets = [
549+
0., 10., 20., 30., 40., 50.,
550+
300., 350., #400.,
551+
]
546552
for alt_target in alt_targets:
547553
pressure1 = atm_pressure(alt_target*1000.)
548554
#alt1 = get_alt_for_pressure(pressure1, tol=5., SI=False, nmax=20)
@@ -595,7 +601,10 @@ def test_get_alt_for_q_with_constant_mach2(self):
595601
def test_get_alt_for_eas_with_constant_mach(self):
596602
"""tests get_alt_for_q_with_constant_mach"""
597603
mach = 0.8
598-
alt_targets = [0., 10., 20., 30., 40., 50.]
604+
alt_targets = [
605+
0., 10., 20., 30., 40., 50.,
606+
300., 350., 400.,
607+
]
599608
for alt_target in alt_targets:
600609
veq1 = atm_equivalent_airspeed(
601610
alt_target*1000., mach, alt_units='ft', eas_units='ft/s')
@@ -644,6 +653,7 @@ def test_atm_unit_reynolds_number(self):
644653

645654
def test_sweep_eas_mach(self):
646655
eass = np.linspace(0., 1000., num=101, dtype='float64')
656+
eass[0] = 1e-5
647657
#neas = len(eass)
648658

649659
machi = 0.5

0 commit comments

Comments
 (0)