Skip to content

Commit 50817c8

Browse files
authored
Pre-V1 simulation fixes (#139)
* Fix PTF reader for 2-digit CAS values * Add non-strict mode to EDB access * Allow missing performance models in selector * PTF files do not contain maximum payload * Change to split performance table in legacy performance models * Generate performance models in phase split format * ROCD check for climb phase * Fix small problems for OAG simulation * Make output store contain only enabled species * Argument checking in HCCO emissions calculation * Logging for HCCO invalid parameters * Handle performance models with only two masses
1 parent c7b650b commit 50817c8

18 files changed

Lines changed: 1079 additions & 1828 deletions

File tree

src/AEIC/commands/make_performance_model.py

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ def _set_with_inline(tbl, key: str, value: Any) -> None:
5858
tbl[key].comment(_INLINE_COMMENTS[key])
5959

6060

61-
def _format_flight_performance(cols: list[str], data: list[list[float]]) -> str:
61+
def _format_flight_performance(
62+
phase: str, cols: list[str], data: list[list[float]]
63+
) -> str:
6264
"""Render the [flight_performance] section as a string with the
6365
right-aligned numeric column layout used by the sample performance
6466
model file."""
@@ -80,7 +82,7 @@ def _format_flight_performance(cols: list[str], data: list[list[float]]) -> str:
8082
data_lines.append(f' [ {padded}],')
8183
data_block = 'data = [\n' + '\n'.join(data_lines) + '\n]'
8284

83-
return '[flight_performance]\n' + cols_block + '\n\n' + data_block + '\n'
85+
return f'[{phase}_flight_performance]\n' + cols_block + '\n\n' + data_block + '\n'
8486

8587

8688
def _fix_empty_comments(text: str) -> str:
@@ -101,7 +103,9 @@ def write_legacy_performance_toml(
101103
apu_name: str | None,
102104
lto_dump: dict[str, Any],
103105
speeds_dump: dict[str, Any],
104-
flight_performance: dict[str, Any],
106+
climb_flight_performance: dict[str, Any],
107+
cruise_flight_performance: dict[str, Any],
108+
descent_flight_performance: dict[str, Any],
105109
) -> None:
106110
doc = document()
107111
doc.add(comment('Performance model type (one of: legacy, bada, tasopt, piano).'))
@@ -174,14 +178,26 @@ def write_legacy_performance_toml(
174178
trailer_doc.add(nl())
175179
trailer = _fix_empty_comments(tomlkit.dumps(trailer_doc))
176180

177-
fp_section = _format_flight_performance(
178-
flight_performance['cols'], flight_performance['data']
181+
climb_fp_section = _format_flight_performance(
182+
'climb', climb_flight_performance['cols'], climb_flight_performance['data']
183+
)
184+
cruise_fp_section = _format_flight_performance(
185+
'cruise', cruise_flight_performance['cols'], cruise_flight_performance['data']
186+
)
187+
descent_fp_section = _format_flight_performance(
188+
'descent',
189+
descent_flight_performance['cols'],
190+
descent_flight_performance['data'],
179191
)
180192

181193
with open(path, 'w', encoding='utf-8') as fp:
182194
fp.write(body)
183195
fp.write(trailer)
184-
fp.write(fp_section)
196+
fp.write(climb_fp_section)
197+
fp.write('\n')
198+
fp.write(cruise_fp_section)
199+
fp.write('\n')
200+
fp.write(descent_fp_section)
185201

186202

187203
def lto_from_edb(engine_file, engine_uid, thrust_fractions) -> LTOPerformance:
@@ -208,19 +224,34 @@ def lto_from_toml(lto_file) -> LTOPerformance:
208224
return lto_input.convert()
209225

210226

211-
def build_performance_table(ptf: PTFData) -> dict[str, Any]:
227+
def build_performance_table(ptf: PTFData, phase: str) -> dict[str, Any]:
212228
cols = ['fl', 'mass', 'tas', 'rocd', 'fuel_flow']
229+
include_high = True
230+
if ptf.high_mass == ptf.nominal_mass:
231+
include_high = False
213232
data = []
214-
for r in ptf.climb:
215-
data.append([r.fl, ptf.low_mass, r.tas, r.rocd_low, r.fuel_flow_nom])
216-
data.append([r.fl, ptf.nominal_mass, r.tas, r.rocd_nom, r.fuel_flow_nom])
217-
data.append([r.fl, ptf.high_mass, r.tas, r.rocd_high, r.fuel_flow_nom])
218-
for r in ptf.cruise:
219-
data.append([r.fl, ptf.low_mass, r.tas, 0.0, r.fuel_flow_low])
220-
data.append([r.fl, ptf.nominal_mass, r.tas, 0.0, r.fuel_flow_nom])
221-
data.append([r.fl, ptf.high_mass, r.tas, 0.0, r.fuel_flow_high])
222-
for r in ptf.descent:
223-
data.append([r.fl, ptf.nominal_mass, r.tas, r.rocd_nom, r.fuel_flow_nom])
233+
match phase:
234+
case 'climb':
235+
for r in ptf.climb:
236+
data.append([r.fl, ptf.low_mass, r.tas, r.rocd_low, r.fuel_flow_nom])
237+
data.append(
238+
[r.fl, ptf.nominal_mass, r.tas, r.rocd_nom, r.fuel_flow_nom]
239+
)
240+
if include_high:
241+
data.append(
242+
[r.fl, ptf.high_mass, r.tas, r.rocd_high, r.fuel_flow_nom]
243+
)
244+
case 'cruise':
245+
for r in ptf.cruise:
246+
data.append([r.fl, ptf.low_mass, r.tas, 0.0, r.fuel_flow_low])
247+
data.append([r.fl, ptf.nominal_mass, r.tas, 0.0, r.fuel_flow_nom])
248+
if include_high:
249+
data.append([r.fl, ptf.high_mass, r.tas, 0.0, r.fuel_flow_high])
250+
case 'descent':
251+
for r in ptf.descent:
252+
data.append(
253+
[r.fl, ptf.nominal_mass, r.tas, r.rocd_nom, r.fuel_flow_nom]
254+
)
224255
return dict(cols=cols, data=sorted(data, key=lambda x: (x[1], x[0], -x[3])))
225256

226257

@@ -290,6 +321,12 @@ def make_performance_model(ctx, output_file):
290321
required=True,
291322
help='Number of engines on the aircraft (1-8).',
292323
)
324+
@click.option(
325+
'--maximum-payload',
326+
type=int,
327+
required=True,
328+
help='Maximum payload in kg.',
329+
)
293330
@click.option(
294331
'--apu-name',
295332
required=False,
@@ -307,6 +344,7 @@ def legacy(
307344
ptf_file,
308345
aircraft_class,
309346
number_of_engines,
347+
maximum_payload,
310348
apu_name,
311349
):
312350
Config.load()
@@ -335,12 +373,14 @@ def legacy(
335373
aircraft_class=aircraft_class,
336374
isa_offset=ptf_data.isa_offset,
337375
maximum_altitude_ft=ptf_data.maximum_altitude_ft,
338-
maximum_payload_kg=ptf_data.maximum_payload,
376+
maximum_payload_kg=maximum_payload,
339377
number_of_engines=number_of_engines,
340378
apu_name=apu_name,
341379
lto_dump=lto_dump,
342380
speeds_dump=ptf_data.speeds.model_dump(),
343-
flight_performance=build_performance_table(ptf_data),
381+
climb_flight_performance=build_performance_table(ptf_data, 'climb'),
382+
cruise_flight_performance=build_performance_table(ptf_data, 'cruise'),
383+
descent_flight_performance=build_performance_table(ptf_data, 'descent'),
344384
)
345385

346386

src/AEIC/commands/run_simulations.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ def simulate_slice(
5959
pm = performance_model
6060
if isinstance(performance_model, PerformanceModelSelector):
6161
pm = performance_model(mission)
62+
if pm is None:
63+
logger.warning(
64+
f'no performance model for mission '
65+
f'{mission.origin} -> {mission.destination} '
66+
f'({mission.aircraft_type}, '
67+
f'{mission.gc_distance / 1000:0.2f} km)'
68+
)
69+
continue
6270
assert isinstance(pm, BasePerformanceModel)
6371
traj = builder.fly(pm, mission)
6472
traj.add_fields(compute_emissions(pm, fuel, traj))
@@ -67,7 +75,8 @@ def simulate_slice(
6775
logger.exception(
6876
'Error simulating mission '
6977
f'{mission.origin} -> {mission.destination} '
70-
f'({mission.gc_distance / 1000:0.2f} km):'
78+
f'({mission.aircraft_type}, '
79+
f'{mission.gc_distance / 1000:0.2f} km):'
7180
)
7281
nfailed += 1
7382
continue

0 commit comments

Comments
 (0)