Skip to content

Commit 51cac38

Browse files
committed
2 parents 741ac57 + 25db917 commit 51cac38

File tree

5 files changed

+90
-39
lines changed

5 files changed

+90
-39
lines changed

pyNastran/f06/dev/flutter/utils.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
def load_f06_op2(f06_filename: str, log: SimpleLogger,
1616
in_units: str,
1717
out_units: str,
18-
use_rhoref: bool) -> tuple[OP2, dict[int, FlutterResponse]]:
18+
use_rhoref: bool,
19+
make_alt: bool=False) -> tuple[OP2, dict[int, FlutterResponse]]:
1920
"""
2021
load a Vg-Vf plot from:
2122
- OP2 / F06
@@ -43,6 +44,7 @@ def load_f06_op2(f06_filename: str, log: SimpleLogger,
4344
f06_units=in_units_dict,
4445
out_units=out_units_dict,
4546
use_rhoref=use_rhoref,
47+
make_alt=make_alt,
4648
log=log)
4749
except Exception as e:
4850
log.error(str(e))

pyNastran/f06/flutter_response.py

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -575,10 +575,6 @@ def _set_pknl_results(self,
575575

576576
vel_units_in = in_units['velocity']
577577
q_units_in = in_units['dynamic_pressure']
578-
if _is_q_units_consistent(density_units_in, vel_units_in, q_units_in):
579-
q = 0.5 * rho * vel**2
580-
else: # pragma: no cover
581-
raise NotImplementedError((density_units_in, vel_units_in, q_units_in))
582578

583579
# eas = (2 * q / rho_ref)**0.5
584580
# eas = V * sqrt(rho / rhoSL)
@@ -590,27 +586,35 @@ def _set_pknl_results(self,
590586
altitude_units = in_units['altitude']
591587

592588
# print('density_units_in=%r density_units2=%r' % (density_units_in, density_units2))
593-
kdensityi = convert_density(1., density_units_in, 'slug/ft^3')
594-
595589
resultsi = results[:, :, :9]
596590
assert resultsi.shape[2] == 9, resultsi.shape
597591

598-
rho_in_slug_ft3 = rho * kdensityi
592+
rho_in_slug_ft3 = rho_ref * rho
599593
ft_to_alt_unit = convert_altitude(1., 'ft', altitude_units)
600594
if self.make_alt:
595+
nrho = len(rho)
601596
alt_ft = []
602597
for idensity, densityi in enumerate(rho_in_slug_ft3.ravel()):
603598
try:
604599
alt_fti = get_alt_for_density(densityi, density_units='slug/ft^3',
605-
alt_units='ft', nmax=20)
600+
alt_units='ft', nmax=50)
606601
except Exception:
607-
raise RuntimeError(f'failed to find altitude for density[{idensity}]='
608-
f'{rho.ravel()[idensity]:g}; density_units_in={density_units_in!r}')
602+
raise RuntimeError(f'Case {idensity+1}/{nrho}: failed to find altitude for density='
603+
f'{rho.ravel()[idensity]:g}; density_units_in={density_units_in!r} ({densityi:g} slug/ft^3)\n'
604+
' output_rho_range = rho_ref * input_rho_range\n'
605+
f' rho_ref: {rho_ref:g} {density_units_in}\n'
606+
f' input_rho_range: [{rho.min():g}, {rho.max()}]\n'
607+
f' => output_rho_range: [{rho_in_slug_ft3.min():g}, {rho_in_slug_ft3.max()}] slug/ft^3')
609608
alt_ft.append(alt_fti)
610609
alt = np.array(alt_ft, dtype=rho.dtype).reshape(vel.shape) * ft_to_alt_unit
611610
else:
612611
alt = np.full(vel.shape, np.nan, dtype=vel.dtype)
613612

613+
# get dynamic pressure
614+
if _is_q_units_consistent(density_units_in, vel_units_in, q_units_in):
615+
q = 0.5 * rho * vel**2
616+
else: # pragma: no cover
617+
raise NotImplementedError((density_units_in, vel_units_in, q_units_in))
614618
results2 = np.dstack([resultsi, eas, q, alt])
615619
# results2[:, :, self.idensity] = rho
616620
return results2
@@ -809,6 +813,9 @@ def get_flutter_crossings(self,
809813
easi, dampi, freqi = remove_eas_range(
810814
easi, dampi, freqi, eas_range)
811815

816+
if len(freqi) == 0:
817+
continue
818+
812819
for damping_targeti, damping_requiredi in damping_crossings_dict.items():
813820
if dampi.max() < damping_requiredi:
814821
continue
@@ -1796,6 +1803,8 @@ def plot_vg_vf(self, fig=None, damp_axes=None, freq_axes=None,
17961803
vel_calc, damping_calc, freq_calc = remove_eas_range(
17971804
vel, damping, freq, eas_range)
17981805

1806+
if len(freq) == 0:
1807+
continue
17991808
jcolor, color, linestyle2, symbol2, texti, is_removedi = _increment_jcolor(
18001809
mode, jcolor, color, linestyle, symbol,
18011810
freq, damping, freq_tol=freq_tol, freq_tol_remove=freq_tol_remove,
@@ -3653,14 +3662,22 @@ def _get_divergence(self,
36533662
dampi = self.results[imode, :, self.idamping].flatten()
36543663
easi = self.results[imode, :, self.ieas].flatten()
36553664
freqi = self.results[imode, :, self.ifreq].flatten()
3656-
36573665
easi, dampi, freqi = remove_excluded_points(
36583666
easi, dampi, freqi, point_removal)
36593667
easi, dampi, freqi = remove_eas_range(
36603668
easi, dampi, freqi, eas_range)
36613669

3670+
if len(freqi) == 0:
3671+
continue
3672+
36623673
for freq_targeti, freq_requiredi in freq_crossings_dict.items():
3663-
if freqi.min() > freq_requiredi:
3674+
try:
3675+
freqi_min = freqi.min()
3676+
except:
3677+
warnings.warn(f'mode={mode}; freqi={freqi}')
3678+
raise
3679+
3680+
if freqi_min > freq_requiredi:
36643681
continue
36653682

36663683
# dfreq sign is flipped because get_zero_crossings

pyNastran/f06/parse_flutter.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def make_flutter_response(f06_filename: PathLike,
3939
f06_units=None, out_units=None,
4040
use_rhoref: bool=False,
4141
read_flutter: bool=True,
42+
make_alt: bool=True,
4243
log: Optional[SimpleLogger]=None) -> tuple[dict[int, FlutterResponse],
4344
dict[str, Any]]:
4445
"""
@@ -57,7 +58,10 @@ def make_flutter_response(f06_filename: PathLike,
5758
False: assume the density in the table is absolute density
5859
True: assume the density should be defined by sea level density,
5960
so density is a density ratio
60-
61+
read_flutter : bool; default=True
62+
don't parse the flutter to extract eigenvalues/mass
63+
make_alt : bool; default=True
64+
make altitude an output variable
6165
6266
Returns
6367
-------
@@ -225,7 +229,8 @@ def make_flutter_response(f06_filename: PathLike,
225229
in_units=f06_units,
226230
use_rhoref=use_rhoref,
227231
eigenvector=eigenvectors_array,
228-
eigr_eigi_velocity=eigr_eigi_velocity)
232+
eigr_eigi_velocity=eigr_eigi_velocity,
233+
make_alt=make_alt)
229234
response.set_out_units(out_units)
230235
#_remove_neutrinos(response, log)
231236
flutters[subcase] = response
@@ -426,7 +431,8 @@ def make_flutter_response(f06_filename: PathLike,
426431
in_units=f06_units,
427432
use_rhoref=use_rhoref,
428433
eigenvector=eigenvectors_array,
429-
eigr_eigi_velocity=eigr_eigi_velocity)
434+
eigr_eigi_velocity=eigr_eigi_velocity,
435+
make_alt=make_alt)
430436
response.set_out_units(out_units)
431437
flutters[subcase] = response
432438

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)

0 commit comments

Comments
 (0)