aero-agent-roles 1.0.0
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.
- package/NOTICE +18 -0
- package/README.md +49 -0
- package/bin/aero-agent-roles.js +127 -0
- package/lib/catalog.js +97 -0
- package/lib/install.js +48 -0
- package/lib/mcp.js +199 -0
- package/manifest.json +516 -0
- package/package.json +44 -0
- package/roles/aerodynamics-engineer/ROLE.md +153 -0
- package/roles/aerodynamics-engineer/SOURCES.md +12 -0
- package/roles/aerodynamics-engineer/cli.py +143 -0
- package/roles/aerodynamics-engineer/core/aerodynamics_core.py +1218 -0
- package/roles/aerodynamics-engineer/templates/aero-design-report-template.md +72 -0
- package/roles/aerodynamics-engineer/tests/test_aerodynamics_core.py +227 -0
- package/roles/aerodynamics-engineer/tests/test_role_aerodynamics.py +94 -0
- package/roles/aircraft-design-engineer/ROLE.md +138 -0
- package/roles/aircraft-design-engineer/SOURCES.md +12 -0
- package/roles/aircraft-design-engineer/cli.py +152 -0
- package/roles/aircraft-design-engineer/core/aircraft_design_core.py +1278 -0
- package/roles/aircraft-design-engineer/templates/concept-design-template.md +99 -0
- package/roles/aircraft-design-engineer/tests/test_aircraft_design_core.py +269 -0
- package/roles/aircraft-design-engineer/tests/test_role_aircraft_design_engineer.py +106 -0
- package/roles/airworthiness-compliance-engineer/ROLE.md +122 -0
- package/roles/airworthiness-compliance-engineer/SOURCES.md +16 -0
- package/roles/airworthiness-compliance-engineer/cli.py +146 -0
- package/roles/airworthiness-compliance-engineer/core/airworthiness_core.py +894 -0
- package/roles/airworthiness-compliance-engineer/templates/compliance-matrix-template.md +46 -0
- package/roles/airworthiness-compliance-engineer/tests/test_airworthiness_core.py +224 -0
- package/roles/airworthiness-compliance-engineer/tests/test_role_airworthiness.py +85 -0
- package/roles/as9100-quality-auditor/ROLE.md +128 -0
- package/roles/as9100-quality-auditor/SOURCES.md +16 -0
- package/roles/as9100-quality-auditor/cli.py +136 -0
- package/roles/as9100-quality-auditor/core/as9100_core.py +857 -0
- package/roles/as9100-quality-auditor/templates/findings-report-template.md +119 -0
- package/roles/as9100-quality-auditor/tests/test_as9100_cli_profile.py +105 -0
- package/roles/as9100-quality-auditor/tests/test_as9100_core.py +204 -0
- package/roles/as9100-quality-auditor/tests/test_role_as9100.py +90 -0
- package/roles/data-bus-avionics-engineer/ROLE.md +146 -0
- package/roles/data-bus-avionics-engineer/SOURCES.md +23 -0
- package/roles/data-bus-avionics-engineer/cli.py +311 -0
- package/roles/data-bus-avionics-engineer/core/data_bus_avionics_core.py +1121 -0
- package/roles/data-bus-avionics-engineer/templates/bus-assessment-template.md +134 -0
- package/roles/data-bus-avionics-engineer/tests/test_bundle_protocol.py +137 -0
- package/roles/data-bus-avionics-engineer/tests/test_data_bus_avionics_core.py +261 -0
- package/roles/data-bus-avionics-engineer/tests/test_role_data_bus_avionics_engineer.py +121 -0
- package/roles/do160-environmental-engineer/ROLE.md +136 -0
- package/roles/do160-environmental-engineer/SOURCES.md +30 -0
- package/roles/do160-environmental-engineer/cli.py +147 -0
- package/roles/do160-environmental-engineer/core/do160_environmental_core.py +1246 -0
- package/roles/do160-environmental-engineer/templates/environmental-qualification-plan-template.md +114 -0
- package/roles/do160-environmental-engineer/tests/test_do160_environmental_core.py +312 -0
- package/roles/do160-environmental-engineer/tests/test_role_do160_environmental_engineer.py +281 -0
- package/roles/do178c-cert-engineer/ROLE.md +120 -0
- package/roles/do178c-cert-engineer/SOURCES.md +16 -0
- package/roles/do178c-cert-engineer/cli.py +129 -0
- package/roles/do178c-cert-engineer/core/do178c_core.py +411 -0
- package/roles/do178c-cert-engineer/templates/psac-template.md +74 -0
- package/roles/do178c-cert-engineer/tests/test_do178c_cert_engineer_core.py +91 -0
- package/roles/do178c-cert-engineer/tests/test_role_do178c.py +109 -0
- package/roles/do254-hardware-engineer/ROLE.md +127 -0
- package/roles/do254-hardware-engineer/SOURCES.md +19 -0
- package/roles/do254-hardware-engineer/cli.py +308 -0
- package/roles/do254-hardware-engineer/core/do254_hardware_core.py +761 -0
- package/roles/do254-hardware-engineer/templates/phac-template.md +87 -0
- package/roles/do254-hardware-engineer/tests/test_do254_bundle_profile.py +152 -0
- package/roles/do254-hardware-engineer/tests/test_do254_hardware_core.py +200 -0
- package/roles/do254-hardware-engineer/tests/test_role_do254_hardware.py +108 -0
- package/roles/engineering-analysis-engineer/ROLE.md +154 -0
- package/roles/engineering-analysis-engineer/SOURCES.md +15 -0
- package/roles/engineering-analysis-engineer/cli.py +151 -0
- package/roles/engineering-analysis-engineer/core/engineering_analysis_engineer_core.py +866 -0
- package/roles/engineering-analysis-engineer/templates/analysis-memo-template.md +73 -0
- package/roles/engineering-analysis-engineer/tests/test_engineering_analysis_engineer_core.py +367 -0
- package/roles/engineering-analysis-engineer/tests/test_role_engineering_analysis_engineer.py +113 -0
- package/roles/flight-management-engineer/ROLE.md +160 -0
- package/roles/flight-management-engineer/SOURCES.md +30 -0
- package/roles/flight-management-engineer/cli.py +258 -0
- package/roles/flight-management-engineer/core/flight_management_core.py +1473 -0
- package/roles/flight-management-engineer/templates/route-assessment-template.md +94 -0
- package/roles/flight-management-engineer/tests/test_flight_management_core.py +437 -0
- package/roles/flight-management-engineer/tests/test_role_flight_management_engineer.py +123 -0
- package/roles/flight-mechanics-engineer/ROLE.md +162 -0
- package/roles/flight-mechanics-engineer/SOURCES.md +51 -0
- package/roles/flight-mechanics-engineer/cli.py +149 -0
- package/roles/flight-mechanics-engineer/core/flight_mechanics_core.py +1507 -0
- package/roles/flight-mechanics-engineer/templates/perf-sc-report-template.md +82 -0
- package/roles/flight-mechanics-engineer/tests/test_flight_mechanics_core.py +277 -0
- package/roles/flight-mechanics-engineer/tests/test_role_flight_mechanics_engineer.py +141 -0
- package/roles/flight-test-engineer/ROLE.md +137 -0
- package/roles/flight-test-engineer/SOURCES.md +24 -0
- package/roles/flight-test-engineer/cli.py +135 -0
- package/roles/flight-test-engineer/core/flight_test_core.py +931 -0
- package/roles/flight-test-engineer/templates/flight-test-template.md +143 -0
- package/roles/flight-test-engineer/tests/test_flight_test_engineer_core.py +227 -0
- package/roles/flight-test-engineer/tests/test_role_flight_test_engineer.py +150 -0
- package/roles/flight-test-performance-engineer/ROLE.md +165 -0
- package/roles/flight-test-performance-engineer/SOURCES.md +20 -0
- package/roles/flight-test-performance-engineer/cli.py +319 -0
- package/roles/flight-test-performance-engineer/core/flight_test_performance_core.py +1398 -0
- package/roles/flight-test-performance-engineer/templates/performance-report-template.md +105 -0
- package/roles/flight-test-performance-engineer/tests/test_flight_test_performance_engineer_core.py +308 -0
- package/roles/flight-test-performance-engineer/tests/test_role_flight_test_performance_engineer.py +165 -0
- package/roles/gnc-engineer/ROLE.md +141 -0
- package/roles/gnc-engineer/SOURCES.md +35 -0
- package/roles/gnc-engineer/cli.py +141 -0
- package/roles/gnc-engineer/core/gnc_core.py +1074 -0
- package/roles/gnc-engineer/templates/gnc-report-template.md +105 -0
- package/roles/gnc-engineer/tests/test_gnc_engineer_core.py +238 -0
- package/roles/gnc-engineer/tests/test_role_gnc_engineer.py +142 -0
- package/roles/guidance-engineer/ROLE.md +156 -0
- package/roles/guidance-engineer/SOURCES.md +43 -0
- package/roles/guidance-engineer/cli.py +280 -0
- package/roles/guidance-engineer/core/guidance_core.py +820 -0
- package/roles/guidance-engineer/templates/guidance-report-template.md +70 -0
- package/roles/guidance-engineer/tests/test_guidance_engineer_core.py +269 -0
- package/roles/guidance-engineer/tests/test_role_guidance_engineer.py +146 -0
- package/roles/high-speed-aerodynamics-engineer/ROLE.md +161 -0
- package/roles/high-speed-aerodynamics-engineer/SOURCES.md +18 -0
- package/roles/high-speed-aerodynamics-engineer/cli.py +282 -0
- package/roles/high-speed-aerodynamics-engineer/core/high_speed_aero_core.py +1566 -0
- package/roles/high-speed-aerodynamics-engineer/templates/high-speed-memo-template.md +75 -0
- package/roles/high-speed-aerodynamics-engineer/tests/test_bundle_protocol.py +103 -0
- package/roles/high-speed-aerodynamics-engineer/tests/test_high_speed_aero_core.py +280 -0
- package/roles/high-speed-aerodynamics-engineer/tests/test_role_high_speed_aerodynamics.py +125 -0
- package/roles/ndt-engineer/ROLE.md +157 -0
- package/roles/ndt-engineer/SOURCES.md +18 -0
- package/roles/ndt-engineer/cli.py +316 -0
- package/roles/ndt-engineer/core/ndt_core.py +1590 -0
- package/roles/ndt-engineer/templates/ndt-plan-template.md +109 -0
- package/roles/ndt-engineer/tests/test_ndt_cli_bundle.py +183 -0
- package/roles/ndt-engineer/tests/test_ndt_core.py +437 -0
- package/roles/ndt-engineer/tests/test_role_ndt_engineer.py +148 -0
- package/roles/numerical-analysis-engineer/ROLE.md +158 -0
- package/roles/numerical-analysis-engineer/SOURCES.md +19 -0
- package/roles/numerical-analysis-engineer/cli.py +414 -0
- package/roles/numerical-analysis-engineer/core/numerical_analysis_core.py +1220 -0
- package/roles/numerical-analysis-engineer/templates/verification-memo-template.md +120 -0
- package/roles/numerical-analysis-engineer/tests/test_bundle_protocol.py +157 -0
- package/roles/numerical-analysis-engineer/tests/test_numerical_analysis_core.py +337 -0
- package/roles/numerical-analysis-engineer/tests/test_role_numerical_analysis_engineer.py +117 -0
- package/roles/propulsion-engineer/ROLE.md +163 -0
- package/roles/propulsion-engineer/SOURCES.md +15 -0
- package/roles/propulsion-engineer/cli.py +168 -0
- package/roles/propulsion-engineer/core/propulsion_core.py +1291 -0
- package/roles/propulsion-engineer/templates/propulsion-report-template.md +92 -0
- package/roles/propulsion-engineer/tests/test_propulsion_engineer_core.py +294 -0
- package/roles/propulsion-engineer/tests/test_role_propulsion_engineer.py +106 -0
- package/roles/space-systems-engineer/ROLE.md +161 -0
- package/roles/space-systems-engineer/SOURCES.md +49 -0
- package/roles/space-systems-engineer/cli.py +173 -0
- package/roles/space-systems-engineer/core/space_systems_core.py +980 -0
- package/roles/space-systems-engineer/templates/space-systems-template.md +88 -0
- package/roles/space-systems-engineer/tests/test_role_space_systems_engineer.py +117 -0
- package/roles/space-systems-engineer/tests/test_space_systems_engineer_core.py +247 -0
- package/roles/state-estimation-engineer/ROLE.md +154 -0
- package/roles/state-estimation-engineer/SOURCES.md +27 -0
- package/roles/state-estimation-engineer/cli.py +326 -0
- package/roles/state-estimation-engineer/core/state_estimation_core.py +958 -0
- package/roles/state-estimation-engineer/templates/nav-state-estimator-design-template.md +76 -0
- package/roles/state-estimation-engineer/tests/test_bundle_protocol.py +110 -0
- package/roles/state-estimation-engineer/tests/test_role_state_estimation_engineer.py +127 -0
- package/roles/state-estimation-engineer/tests/test_state_estimation_engineer_core.py +231 -0
- package/roles/structures-loads-engineer/ROLE.md +171 -0
- package/roles/structures-loads-engineer/SOURCES.md +44 -0
- package/roles/structures-loads-engineer/cli.py +219 -0
- package/roles/structures-loads-engineer/core/structures_loads_core.py +1112 -0
- package/roles/structures-loads-engineer/templates/loads-strength-report-template.md +96 -0
- package/roles/structures-loads-engineer/tests/test_bundle_protocol.py +101 -0
- package/roles/structures-loads-engineer/tests/test_role_structures_loads_engineer.py +147 -0
- package/roles/structures-loads-engineer/tests/test_structures_loads_core.py +261 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""cli.py - run the High-Speed Aerodynamics Engineer role.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python3 cli.py build [--out file.md] [--mach M] [--altitude m]
|
|
6
|
+
[--bundle] [--no-dispatch] [--profile p.json]
|
|
7
|
+
# build the High-Speed Aerodynamic Analysis Memo
|
|
8
|
+
# --bundle also emit evidence/{model,gates,provenance}.json
|
|
9
|
+
# --profile apply a customer/program profile (basis, regs, formats)
|
|
10
|
+
python3 cli.py check --file file.md # gate-check an existing memo
|
|
11
|
+
|
|
12
|
+
The role ENGINE (core/high_speed_aero_core.py) does the work standalone:
|
|
13
|
+
isentropic relations, Mach-area, Prandtl-Glauert / Karman-Tsien
|
|
14
|
+
corrections, normal/oblique shock relations, Prandtl-Meyer expansion,
|
|
15
|
+
shock-expansion airfoil, Sears-Haack wave drag, boundary-layer
|
|
16
|
+
thickness/transition, stagnation-line layer, rough-wall skin friction,
|
|
17
|
+
Sutton-Graves heating screen, bow-shock standoff, and the evidence
|
|
18
|
+
gates. When AeroSkills is present (AEROSKILLS_DEV or ~/AeroSkills), the
|
|
19
|
+
CLI dispatches the bound leaf logic (isentropic ratios, normal shock,
|
|
20
|
+
oblique shock, boundary layer) and cross-checks each against the
|
|
21
|
+
core's - two independent implementations agreeing is recorded in
|
|
22
|
+
provenance.json.
|
|
23
|
+
"""
|
|
24
|
+
import argparse
|
|
25
|
+
import importlib.util
|
|
26
|
+
import json
|
|
27
|
+
import os
|
|
28
|
+
import sys
|
|
29
|
+
|
|
30
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
31
|
+
"core"))
|
|
32
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
33
|
+
"..", "..", "scripts"))
|
|
34
|
+
import high_speed_aero_core as core # noqa: E402
|
|
35
|
+
import evidence # noqa: E402
|
|
36
|
+
|
|
37
|
+
AEROSKILLS = os.environ.get("AEROSKILLS_DEV", os.path.expanduser("~/AeroSkills"))
|
|
38
|
+
ROLE_SLUG = "high-speed-aerodynamics-engineer"
|
|
39
|
+
SKILLS_RELEASE = "v1.3.0+"
|
|
40
|
+
|
|
41
|
+
# Dispatch rows: each cross-checks one core quantity against the logic of
|
|
42
|
+
# a bound AeroSkills leaf (same inputs, same formulas - two independent
|
|
43
|
+
# implementations agreeing is the strongest offline evidence available).
|
|
44
|
+
DISPATCH = [
|
|
45
|
+
{"leaf": "aerodynamics/high-speed/isentropic-flow-relations",
|
|
46
|
+
"logic": "isentropic_flow_relations_logic.py",
|
|
47
|
+
"fn": "total_static_ratios", "key": "p0_over_p",
|
|
48
|
+
"args": {"mach": 2.0}, "tol": 1e-9,
|
|
49
|
+
"point": "isentropic p0/p at M 2.0"},
|
|
50
|
+
{"leaf": "aerodynamics/high-speed/normal-shock",
|
|
51
|
+
"logic": "normal_shock_logic.py",
|
|
52
|
+
"fn": "shock_properties", "key": "p02_p01",
|
|
53
|
+
"args": {"M1": 2.0}, "tol": 1e-9,
|
|
54
|
+
"point": "normal-shock total-pressure recovery at M1 = 2.0"},
|
|
55
|
+
{"leaf": "aerodynamics/high-speed/oblique-shock",
|
|
56
|
+
"logic": "oblique_shock_logic.py",
|
|
57
|
+
"fn": "shock_properties", "key": "beta_deg",
|
|
58
|
+
"args": {"M1": 2.0, "theta_deg": 10.0}, "tol": 1e-6,
|
|
59
|
+
"point": "weak oblique-shock wave angle at M1 = 2.0, theta = 10 deg"},
|
|
60
|
+
{"leaf": "aerodynamics/boundary-layer/boundary-layer-theory",
|
|
61
|
+
"logic": "boundary_layer_theory_logic.py",
|
|
62
|
+
"fn": "flat_plate_thicknesses", "key": "delta",
|
|
63
|
+
"args": {"x": 6.0, "re_x": 2.866e7}, "tol": 1e-9,
|
|
64
|
+
"point": "flat-plate BL thickness at x = 6 m, Re_x = 2.866e7"},
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _core_value(row):
|
|
69
|
+
"""Recompute the core value for a dispatch row with the same args."""
|
|
70
|
+
if row["leaf"].endswith("isentropic-flow-relations"):
|
|
71
|
+
return core.total_static_ratios(row["args"]["mach"])[row["key"]]
|
|
72
|
+
if row["leaf"].endswith("normal-shock"):
|
|
73
|
+
return core.normal_shock(row["args"]["M1"])[row["key"]]
|
|
74
|
+
if row["leaf"].endswith("oblique-shock"):
|
|
75
|
+
return core.oblique_shock(row["args"]["M1"],
|
|
76
|
+
row["args"]["theta_deg"])[row["key"]]
|
|
77
|
+
if row["leaf"].endswith("boundary-layer-theory"):
|
|
78
|
+
return core.flat_plate_thicknesses(row["args"]["x"],
|
|
79
|
+
row["args"]["re_x"])[row["key"]]
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _dispatch_crosschecks(item, model):
|
|
84
|
+
"""Run the bound AeroSkills logic files and compare with the core.
|
|
85
|
+
|
|
86
|
+
Returns a list of provenance 'skills' rows (empty when AeroSkills is
|
|
87
|
+
absent or a leaf file is missing).
|
|
88
|
+
"""
|
|
89
|
+
rows = []
|
|
90
|
+
for row in DISPATCH:
|
|
91
|
+
path = os.path.join(AEROSKILLS, "skills", row["leaf"],
|
|
92
|
+
"scripts", row["logic"])
|
|
93
|
+
if not os.path.exists(path):
|
|
94
|
+
continue
|
|
95
|
+
spec = importlib.util.spec_from_file_location("hs_dispatch", path)
|
|
96
|
+
mod = importlib.util.module_from_spec(spec)
|
|
97
|
+
try:
|
|
98
|
+
spec.loader.exec_module(mod)
|
|
99
|
+
except Exception:
|
|
100
|
+
continue
|
|
101
|
+
fn = getattr(mod, row["fn"], None)
|
|
102
|
+
if fn is None:
|
|
103
|
+
continue
|
|
104
|
+
try:
|
|
105
|
+
skill_out = fn(**row["args"])
|
|
106
|
+
skill_value = skill_out[row["key"]] if isinstance(skill_out, dict) \
|
|
107
|
+
else skill_out
|
|
108
|
+
core_value = _core_value(row)
|
|
109
|
+
if not isinstance(skill_value, (int, float)) or \
|
|
110
|
+
not isinstance(core_value, (int, float)):
|
|
111
|
+
continue
|
|
112
|
+
delta = abs(float(core_value) - float(skill_value))
|
|
113
|
+
rows.append({
|
|
114
|
+
"leaf": row["leaf"],
|
|
115
|
+
"skill_md": "SKILL.md",
|
|
116
|
+
"logic_file": "scripts/" + row["logic"],
|
|
117
|
+
"function": row["fn"],
|
|
118
|
+
"dispatched": True,
|
|
119
|
+
"cross_check_point": row["point"],
|
|
120
|
+
"core_value": round(float(core_value), 6),
|
|
121
|
+
"skill_value": round(float(skill_value), 6),
|
|
122
|
+
"delta": round(delta, 9),
|
|
123
|
+
"agrees": delta <= row["tol"],
|
|
124
|
+
"tolerance": row["tol"],
|
|
125
|
+
"skills_release": SKILLS_RELEASE,
|
|
126
|
+
})
|
|
127
|
+
except Exception:
|
|
128
|
+
continue
|
|
129
|
+
return rows
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _provenance(item, model, dispatch_rows):
|
|
133
|
+
return {
|
|
134
|
+
"role": ROLE_SLUG,
|
|
135
|
+
"core": {
|
|
136
|
+
"file": "core/high_speed_aero_core.py",
|
|
137
|
+
"functions": ["total_static_ratios", "normal_shock",
|
|
138
|
+
"oblique_shock", "shock_expansion_airfoil",
|
|
139
|
+
"flat_plate_thicknesses", "flat_plate_transition",
|
|
140
|
+
"bow_shock_standoff_distance",
|
|
141
|
+
"stagnation_heat_flux"],
|
|
142
|
+
"version": "0.1.0",
|
|
143
|
+
},
|
|
144
|
+
"skills": dispatch_rows,
|
|
145
|
+
"cross_checked": bool(dispatch_rows and all(
|
|
146
|
+
r.get("agrees") for r in dispatch_rows)),
|
|
147
|
+
"disclaimer": "DRAFT for human review. Not an approval document.",
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def cmd_build(args):
|
|
152
|
+
item = core.example_item()
|
|
153
|
+
if args.mach is not None:
|
|
154
|
+
if not (1.0 < args.mach < 4.0):
|
|
155
|
+
print("error: --mach must be in (1.0, 4.0) for this memo")
|
|
156
|
+
return 1
|
|
157
|
+
item.cruise_mach = args.mach
|
|
158
|
+
if args.altitude is not None:
|
|
159
|
+
item.cruise_altitude_m = args.altitude
|
|
160
|
+
if args.climb is not None:
|
|
161
|
+
item.climb_mach = args.climb
|
|
162
|
+
try:
|
|
163
|
+
model = core.build_memo(item)
|
|
164
|
+
except ValueError as e:
|
|
165
|
+
print("error: %s" % e)
|
|
166
|
+
print("note: the reference planform keeps a subsonic leading edge "
|
|
167
|
+
"(M cos(Lambda) < 1) and its supercritical section stays below "
|
|
168
|
+
"drag divergence M_DD at the effective Mach; the default "
|
|
169
|
+
"condition is M 2.0")
|
|
170
|
+
return 1
|
|
171
|
+
md = core.render_memo_markdown(model)
|
|
172
|
+
|
|
173
|
+
# program profile (customer tailoring): load + apply context header
|
|
174
|
+
profile = None
|
|
175
|
+
try:
|
|
176
|
+
profile = evidence.load_profile(args.profile)
|
|
177
|
+
except ValueError as e:
|
|
178
|
+
print("error: bad profile: %s" % e)
|
|
179
|
+
return 1
|
|
180
|
+
if profile:
|
|
181
|
+
model["profile"] = {
|
|
182
|
+
"customer": profile.get("customer"),
|
|
183
|
+
"program": profile.get("program"),
|
|
184
|
+
"basis": profile.get("basis"),
|
|
185
|
+
"authority": profile.get("authority"),
|
|
186
|
+
"der": profile.get("der"),
|
|
187
|
+
"document_prefix": profile.get("document_prefix"),
|
|
188
|
+
"revision": profile.get("revision"),
|
|
189
|
+
}
|
|
190
|
+
md = evidence.profile_header_block(profile) + md
|
|
191
|
+
|
|
192
|
+
gates = core.check_memo(model)
|
|
193
|
+
|
|
194
|
+
dispatch_rows = []
|
|
195
|
+
if not args.no_dispatch:
|
|
196
|
+
dispatch_rows = _dispatch_crosschecks(item, model)
|
|
197
|
+
|
|
198
|
+
if args.out:
|
|
199
|
+
with open(args.out, "w") as f:
|
|
200
|
+
f.write(md if md.endswith("\n") else md + "\n")
|
|
201
|
+
print("built high-speed aerodynamic analysis memo for %s -> %s"
|
|
202
|
+
% (model["vehicle"], args.out))
|
|
203
|
+
if profile:
|
|
204
|
+
print("profile: %s / %s" % (profile.get("customer"),
|
|
205
|
+
profile.get("program")))
|
|
206
|
+
print("M=%.2f p0/p=%.3f A/A*=%.4f recovery2=%.4f "
|
|
207
|
+
"M_DD=%.3f T0=%.1f K"
|
|
208
|
+
% (model["cruise_mach"], model["total_ratios"]["p0_over_p"],
|
|
209
|
+
model["area_ratio"], model["shocks"]["recovery_total"],
|
|
210
|
+
model["corrections"]["m_dd"],
|
|
211
|
+
model["total_conditions"]["t0"]))
|
|
212
|
+
print("gates: all_pass=%s %s" % (gates["all_pass"], gates))
|
|
213
|
+
if args.bundle:
|
|
214
|
+
prov = _provenance(item, model, dispatch_rows)
|
|
215
|
+
paths = evidence.write_bundle(
|
|
216
|
+
args.out, ROLE_SLUG,
|
|
217
|
+
"high-speed aerodynamic analysis memo", model, gates, prov)
|
|
218
|
+
print("bundle: model=%s" % paths["model"])
|
|
219
|
+
print(" gates=%s" % paths["gates"])
|
|
220
|
+
print(" provenance=%s" % paths["provenance"])
|
|
221
|
+
if dispatch_rows:
|
|
222
|
+
print("cross-check: %d leaf logic file(s) dispatched"
|
|
223
|
+
% len(dispatch_rows))
|
|
224
|
+
for r in dispatch_rows:
|
|
225
|
+
print(" %s core=%.6f skill=%.6f delta=%.9f agrees=%s"
|
|
226
|
+
% (r["leaf"].split("/")[-1], r["core_value"],
|
|
227
|
+
r["skill_value"], r["delta"], r["agrees"]))
|
|
228
|
+
else:
|
|
229
|
+
print("cross-check: not dispatched (no AeroSkills logic)")
|
|
230
|
+
else:
|
|
231
|
+
print(md)
|
|
232
|
+
return 0 if gates["all_pass"] else 1
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def cmd_check(args):
|
|
236
|
+
if not os.path.exists(args.file):
|
|
237
|
+
print("file not found: %s" % args.file)
|
|
238
|
+
return 1
|
|
239
|
+
md = open(args.file).read()
|
|
240
|
+
gates = core.check_memo_markdown(md)
|
|
241
|
+
for name, ok in gates.items():
|
|
242
|
+
if name != "all_pass":
|
|
243
|
+
print("%s %s" % ("PASS" if ok else "FAIL", name))
|
|
244
|
+
print("RESULT: %s" % ("PASS" if gates["all_pass"] else "FAIL"))
|
|
245
|
+
return 0 if gates["all_pass"] else 1
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def main():
|
|
249
|
+
p = argparse.ArgumentParser(
|
|
250
|
+
description="High-Speed Aerodynamics Engineer role")
|
|
251
|
+
sub = p.add_subparsers(dest="cmd", required=True)
|
|
252
|
+
|
|
253
|
+
b = sub.add_parser("build")
|
|
254
|
+
b.add_argument("--out", default="", help="output file (default: stdout)")
|
|
255
|
+
b.add_argument("--mach", type=float, default=None,
|
|
256
|
+
help="cruise Mach override (default 2.0; up to ~2.2 keeps "
|
|
257
|
+
"the section below drag divergence M_DD 0.908 at the "
|
|
258
|
+
"effective Mach)")
|
|
259
|
+
b.add_argument("--altitude", type=float, default=None,
|
|
260
|
+
help="cruise altitude override in metres (default 18288)")
|
|
261
|
+
b.add_argument("--climb", type=float, default=None,
|
|
262
|
+
help="transonic climb check Mach override (default 0.85)")
|
|
263
|
+
b.add_argument("--bundle", action="store_true",
|
|
264
|
+
help="emit evidence/{model,gates,provenance}.json")
|
|
265
|
+
b.add_argument("--no-dispatch", action="store_true",
|
|
266
|
+
help="skip dispatching bound AeroSkills logic")
|
|
267
|
+
b.add_argument("--profile", default="",
|
|
268
|
+
help="program profile JSON (customer tailoring, "
|
|
269
|
+
"docs/PROFILE-SCHEMA.md)")
|
|
270
|
+
b.set_defaults(fn=cmd_build)
|
|
271
|
+
|
|
272
|
+
c = sub.add_parser("check")
|
|
273
|
+
c.add_argument("--file", required=True,
|
|
274
|
+
help="high-speed analysis memo markdown to gate-check")
|
|
275
|
+
c.set_defaults(fn=cmd_check)
|
|
276
|
+
|
|
277
|
+
args = p.parse_args()
|
|
278
|
+
sys.exit(args.fn(args))
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
if __name__ == "__main__":
|
|
282
|
+
main()
|