diff --git a/.github/workflows/verify-max-mach-migration.yml b/.github/workflows/verify-max-mach-migration.yml new file mode 100644 index 0000000000..570f265316 --- /dev/null +++ b/.github/workflows/verify-max-mach-migration.yml @@ -0,0 +1,21 @@ +name: Verify MAX_MACH migration idempotence + +on: + push: + branches: + - refactor/aircraft-design-max-mach + +permissions: + contents: read + +jobs: + verify: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Re-run migration and audit + run: | + python tools/migrate_max_mach_namespace.py --write --check + git diff --exit-code + - name: Verify Python syntax + run: python -m compileall -q aviary tools/migrate_max_mach_namespace.py diff --git a/aviary/docs/examples_unreviewed/modified_aircraft.csv b/aviary/docs/examples_unreviewed/modified_aircraft.csv index 6b01de0d7f..2666aac543 100644 --- a/aviary/docs/examples_unreviewed/modified_aircraft.csv +++ b/aviary/docs/examples_unreviewed/modified_aircraft.csv @@ -141,7 +141,7 @@ aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0.0,unitless aircraft:wing:wetted_area_scaler,1.0,unitless aircraft:wing:wetted_area,2396.56,ft**2 -mission:constraints:max_mach,0.785,unitless +aircraft:design:max_mach,0.785,unitless aircraft:design:cruise_altitude,35000,ft aircraft:design:gross_mass,175400.0,lbm aircraft:design:range,3500,NM diff --git a/aviary/docs/user_guide/fundamentals/aircraft_definition.ipynb b/aviary/docs/user_guide/fundamentals/aircraft_definition.ipynb index bc62a003d0..14fbd81264 100644 --- a/aviary/docs/user_guide/fundamentals/aircraft_definition.ipynb +++ b/aviary/docs/user_guide/fundamentals/aircraft_definition.ipynb @@ -22,7 +22,7 @@ "glue_variable(get_variable_name(Aircraft.Fuselage.LENGTH), md_code=True)\n", "glue_variable(get_variable_name(Aircraft.Wing.AREA), md_code=True)\n", "\n", - "glue_variable(get_variable_name(Mission.Constraints.MAX_MACH), md_code=True)\n", + "glue_variable(get_variable_name(Aircraft.Design.MAX_MACH), md_code=True)\n", "glue_variable(get_variable_name(Aircraft.Design.GROSS_MASS), md_code=True)\n", "glue_variable(get_variable_name(Aircraft.Design.RANGE), md_code=True)\n", "\n", @@ -65,7 +65,7 @@ "```\n", "...\n", "```text\n", - "{glue:md}mission:constraints:max_mach, 0.785, unitless\n", + "{glue:md}aircraft:design:max_mach, 0.785, unitless\n", "{glue:md}aircraft:design:gross_mass, 130000, lbm\n", "{glue:md}aircraft:design:range, 1906, NM\n", "```\n", diff --git a/aviary/interface/test/sizing_results_for_test.json b/aviary/interface/test/sizing_results_for_test.json index 4b0b834dff..593d56791d 100644 --- a/aviary/interface/test/sizing_results_for_test.json +++ b/aviary/interface/test/sizing_results_for_test.json @@ -975,7 +975,7 @@ "" ], [ - "mission:constraints:max_mach", + "aircraft:design:max_mach", 0.785, "unitless", "" diff --git a/aviary/models/aircraft/advanced_single_aisle/advanced_single_aisle_FLOPS.csv b/aviary/models/aircraft/advanced_single_aisle/advanced_single_aisle_FLOPS.csv index 4d455ce6aa..8d44e53820 100644 --- a/aviary/models/aircraft/advanced_single_aisle/advanced_single_aisle_FLOPS.csv +++ b/aviary/models/aircraft/advanced_single_aisle/advanced_single_aisle_FLOPS.csv @@ -167,7 +167,7 @@ aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0,unitless aircraft:wing:wetted_area,2210.280228,ft**2 aircraft:wing:wetted_area_scaler,1,unitless -mission:constraints:max_mach,0.785,unitless +aircraft:design:max_mach,0.785,unitless aircraft:design:gross_mass,130000,lbm aircraft:design:range,3500,NM mission:landing:airport_altitude,0,ft diff --git a/aviary/models/aircraft/blended_wing_body/bwb300_baseline_FLOPS.csv b/aviary/models/aircraft/blended_wing_body/bwb300_baseline_FLOPS.csv index eca36bf7c9..3ae1cd72d6 100644 --- a/aviary/models/aircraft/blended_wing_body/bwb300_baseline_FLOPS.csv +++ b/aviary/models/aircraft/blended_wing_body/bwb300_baseline_FLOPS.csv @@ -174,7 +174,7 @@ aircraft:wing:thickness_to_chord_reference,0.0,unitless aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0.0,unitless aircraft:wing:wetted_area_scaler,0.61,unitless -mission:constraints:max_mach,0.9,unitless +aircraft:design:max_mach,0.9,unitless aircraft:design:gross_mass,600000.0,lbm aircraft:design:range,7500.0,NM mission:landing:drag_coefficient_min,0.0,unitless diff --git a/aviary/models/aircraft/blended_wing_body/bwb_detailed_FLOPS.csv b/aviary/models/aircraft/blended_wing_body/bwb_detailed_FLOPS.csv index d699545e35..3351bc43a8 100644 --- a/aviary/models/aircraft/blended_wing_body/bwb_detailed_FLOPS.csv +++ b/aviary/models/aircraft/blended_wing_body/bwb_detailed_FLOPS.csv @@ -156,7 +156,7 @@ aircraft:wing:thickness_to_chord_distribution,0.15,0.1132,0.0928,0.0822,0.0764,0 aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0.0,unitless aircraft:wing:wetted_area_scaler,1.0,unitless -mission:constraints:max_mach,0.85,unitless +aircraft:design:max_mach,0.85,unitless aircraft:design:gross_mass,874099,lbm aircraft:design:range,7750,NM mission:landing:initial_velocity,140,ft/s diff --git a/aviary/models/aircraft/blended_wing_body/bwb_simple_FLOPS.csv b/aviary/models/aircraft/blended_wing_body/bwb_simple_FLOPS.csv index 4a4468839d..a788fea1cc 100644 --- a/aviary/models/aircraft/blended_wing_body/bwb_simple_FLOPS.csv +++ b/aviary/models/aircraft/blended_wing_body/bwb_simple_FLOPS.csv @@ -156,7 +156,7 @@ aircraft:wing:thickness_to_chord_reference,0.11,unitless aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0.0,unitless aircraft:wing:wetted_area_scaler,1.0,unitless -mission:constraints:max_mach,0.85,unitless +aircraft:design:max_mach,0.85,unitless aircraft:design:gross_mass,874099,lbm aircraft:design:range,7750,NM mission:landing:initial_velocity,140,ft/s diff --git a/aviary/models/aircraft/large_single_aisle_1/large_single_aisle_1_FLOPS.csv b/aviary/models/aircraft/large_single_aisle_1/large_single_aisle_1_FLOPS.csv index f20015090b..76fbf800dd 100644 --- a/aviary/models/aircraft/large_single_aisle_1/large_single_aisle_1_FLOPS.csv +++ b/aviary/models/aircraft/large_single_aisle_1/large_single_aisle_1_FLOPS.csv @@ -152,7 +152,7 @@ aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0.0,unitless aircraft:wing:wetted_area,2396.56,ft**2 aircraft:wing:wetted_area_scaler,1.0,unitless -mission:constraints:max_mach,0.785,unitless +aircraft:design:max_mach,0.785,unitless mission:landing:lift_coefficient_max,3.0,unitless mission:takeoff:fuel_mass,577.0,lbm mission:takeoff:lift_coefficient_max,2.0,unitless diff --git a/aviary/models/aircraft/large_single_aisle_2/large_single_aisle_2_FLOPS.csv b/aviary/models/aircraft/large_single_aisle_2/large_single_aisle_2_FLOPS.csv index deb7e2509b..7a8ed2ed91 100644 --- a/aviary/models/aircraft/large_single_aisle_2/large_single_aisle_2_FLOPS.csv +++ b/aviary/models/aircraft/large_single_aisle_2/large_single_aisle_2_FLOPS.csv @@ -148,7 +148,7 @@ aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0.0,unitless aircraft:wing:wetted_area,2423.02,ft**2 aircraft:wing:wetted_area_scaler,1.0,unitless -mission:constraints:max_mach,0.82,unitless +aircraft:design:max_mach,0.82,unitless mission:landing:lift_coefficient_max,3.0,unitless mission:takeoff:fuel_mass,659.0,lbm mission:takeoff:lift_coefficient_max,2.0,unitless diff --git a/aviary/models/aircraft/large_single_aisle_2/large_single_aisle_2_altwt_FLOPS.csv b/aviary/models/aircraft/large_single_aisle_2/large_single_aisle_2_altwt_FLOPS.csv index cd3b6277fc..c160e6a05e 100644 --- a/aviary/models/aircraft/large_single_aisle_2/large_single_aisle_2_altwt_FLOPS.csv +++ b/aviary/models/aircraft/large_single_aisle_2/large_single_aisle_2_altwt_FLOPS.csv @@ -146,7 +146,7 @@ aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0.0,unitless aircraft:wing:wetted_area,2423.02,ft**2 aircraft:wing:wetted_area_scaler,1.0,unitless -mission:constraints:max_mach,0.82,unitless +aircraft:design:max_mach,0.82,unitless mission:landing:lift_coefficient_max,3.0,unitless mission:takeoff:fuel_mass,659.0,lbm mission:takeoff:lift_coefficient_max,2.0,unitless diff --git a/aviary/models/aircraft/large_single_aisle_2/large_single_aisle_2_detailwing_FLOPS.csv b/aviary/models/aircraft/large_single_aisle_2/large_single_aisle_2_detailwing_FLOPS.csv index 9dd350909d..bad5a003b7 100644 --- a/aviary/models/aircraft/large_single_aisle_2/large_single_aisle_2_detailwing_FLOPS.csv +++ b/aviary/models/aircraft/large_single_aisle_2/large_single_aisle_2_detailwing_FLOPS.csv @@ -148,7 +148,7 @@ aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0.0,unitless aircraft:wing:wetted_area,2423.02,ft**2 aircraft:wing:wetted_area_scaler,1.0,unitless -mission:constraints:max_mach,0.82,unitless +aircraft:design:max_mach,0.82,unitless mission:landing:lift_coefficient_max,3.0,unitless mission:takeoff:fuel_mass,577.0,lbm mission:takeoff:lift_coefficient_max,2.0,unitless diff --git a/aviary/models/aircraft/minimum_single_aisle/minimum_single_aisle_FLOPS.csv b/aviary/models/aircraft/minimum_single_aisle/minimum_single_aisle_FLOPS.csv index f102ec1ca0..c6787a9d1a 100644 --- a/aviary/models/aircraft/minimum_single_aisle/minimum_single_aisle_FLOPS.csv +++ b/aviary/models/aircraft/minimum_single_aisle/minimum_single_aisle_FLOPS.csv @@ -49,7 +49,7 @@ aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:hydraulics:system_pressure,5000,psi # Design mission -mission:constraints:max_mach,0.785,unitless +aircraft:design:max_mach,0.785,unitless aircraft:design:gross_mass,130000,lbm aircraft:design:range,1906,NM aircraft:design:cruise_mach,0.72,unitless diff --git a/aviary/subsystems/aerodynamics/flops_based/premission_aero.py b/aviary/subsystems/aerodynamics/flops_based/premission_aero.py index 9cefa7a0d8..1d87ae02ce 100644 --- a/aviary/subsystems/aerodynamics/flops_based/premission_aero.py +++ b/aviary/subsystems/aerodynamics/flops_based/premission_aero.py @@ -27,7 +27,7 @@ def __init__(self, **kwargs): def initialize(self): add_aviary_option(self, Aircraft.Wing.AIRFOIL_TECHNOLOGY) - add_aviary_option(self, Mission.Constraints.MAX_MACH) + add_aviary_option(self, Aircraft.Design.MAX_MACH) def setup(self): # Aircraft design inputs @@ -45,7 +45,7 @@ def setup_partials(self): def compute(self, inputs, outputs): AITEK = self.options[Aircraft.Wing.AIRFOIL_TECHNOLOGY] - VMAX = self.options[Mission.Constraints.MAX_MACH] + VMAX = self.options[Aircraft.Design.MAX_MACH] AR, CAM, SW25, TC = inputs.values() @@ -87,7 +87,7 @@ def compute(self, inputs, outputs): def compute_partials(self, inputs, partials): AITEK = self.options[Aircraft.Wing.AIRFOIL_TECHNOLOGY] - VMAX = self.options[Mission.Constraints.MAX_MACH] + VMAX = self.options[Aircraft.Design.MAX_MACH] AR, CAM, SW25, TC = inputs.values() diff --git a/aviary/subsystems/aerodynamics/flops_based/test/test_premission_aero.py b/aviary/subsystems/aerodynamics/flops_based/test/test_premission_aero.py index c85d98dd04..687ed2c4c2 100644 --- a/aviary/subsystems/aerodynamics/flops_based/test/test_premission_aero.py +++ b/aviary/subsystems/aerodynamics/flops_based/test/test_premission_aero.py @@ -18,7 +18,7 @@ def test_derivs_supersonic1(self): options = {} options[Aircraft.Wing.AIRFOIL_TECHNOLOGY] = 1.0 - options[Mission.Constraints.MAX_MACH] = 1.2 + options[Aircraft.Design.MAX_MACH] = 1.2 model.add_subsystem( 'design', @@ -49,7 +49,7 @@ def test_derivs_subsonic1(self): options = {} options[Aircraft.Wing.AIRFOIL_TECHNOLOGY] = 1.0 - options[Mission.Constraints.MAX_MACH] = 0.9 + options[Aircraft.Design.MAX_MACH] = 0.9 model.add_subsystem( 'design', @@ -80,7 +80,7 @@ def test_derivs_supersonic2(self): options = {} options[Aircraft.Wing.AIRFOIL_TECHNOLOGY] = 1.0 - options[Mission.Constraints.MAX_MACH] = 1.2 + options[Aircraft.Design.MAX_MACH] = 1.2 model.add_subsystem( 'design', @@ -111,7 +111,7 @@ def test_derivs_subsonic2(self): options = {} options[Aircraft.Wing.AIRFOIL_TECHNOLOGY] = 1.0 - options[Mission.Constraints.MAX_MACH] = 0.9 + options[Aircraft.Design.MAX_MACH] = 0.9 model.add_subsystem( 'design', diff --git a/aviary/subsystems/mass/flops_based/air_conditioning.py b/aviary/subsystems/mass/flops_based/air_conditioning.py index c394edbec0..7340a2c410 100644 --- a/aviary/subsystems/mass/flops_based/air_conditioning.py +++ b/aviary/subsystems/mass/flops_based/air_conditioning.py @@ -14,7 +14,7 @@ class TransportAirCondMass(om.ExplicitComponent): def initialize(self): add_aviary_option(self, Aircraft.CrewPayload.Design.NUM_PASSENGERS) - add_aviary_option(self, Mission.Constraints.MAX_MACH) + add_aviary_option(self, Aircraft.Design.MAX_MACH) def setup(self): add_aviary_input(self, Aircraft.AirConditioning.MASS_SCALER, units='unitless') @@ -34,7 +34,7 @@ def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): avionics_wt = inputs[Aircraft.Avionics.MASS] * GRAV_ENGLISH_LBM height = inputs[Aircraft.Fuselage.MAX_HEIGHT] planform = inputs[Aircraft.Fuselage.PLANFORM_AREA] - max_mach = self.options[Mission.Constraints.MAX_MACH] + max_mach = self.options[Aircraft.Design.MAX_MACH] outputs[Aircraft.AirConditioning.MASS] = ( ((3.2 * (planform * height) ** 0.6 + 9 * pax**0.83) * max_mach + 0.075 * avionics_wt) @@ -49,7 +49,7 @@ def compute_partials(self, inputs, J): avionics_wt = inputs[Aircraft.Avionics.MASS] * GRAV_ENGLISH_LBM height = inputs[Aircraft.Fuselage.MAX_HEIGHT] planform = inputs[Aircraft.Fuselage.PLANFORM_AREA] - max_mach = self.options[Mission.Constraints.MAX_MACH] + max_mach = self.options[Aircraft.Design.MAX_MACH] planform_exp = planform**0.6 height_exp = height**0.6 diff --git a/aviary/subsystems/mass/flops_based/fuel_system.py b/aviary/subsystems/mass/flops_based/fuel_system.py index 5e37d5b559..8dccb43b5d 100644 --- a/aviary/subsystems/mass/flops_based/fuel_system.py +++ b/aviary/subsystems/mass/flops_based/fuel_system.py @@ -15,7 +15,7 @@ class TransportFuelSystemMass(om.ExplicitComponent): def initialize(self): add_aviary_option(self, Aircraft.Propulsion.TOTAL_NUM_ENGINES) - add_aviary_option(self, Mission.Constraints.MAX_MACH) + add_aviary_option(self, Aircraft.Design.MAX_MACH) def setup(self): add_aviary_input(self, Aircraft.Fuel.FUEL_SYSTEM_MASS_SCALER, units='unitless') @@ -31,7 +31,7 @@ def compute(self, inputs, outputs): capacity = inputs[Aircraft.Fuel.MAX_CAPACITY_MASS] num_eng = self.options[Aircraft.Propulsion.TOTAL_NUM_ENGINES] num_eng_fact = distributed_engine_count_factor(num_eng) - max_mach = self.options[Mission.Constraints.MAX_MACH] + max_mach = self.options[Aircraft.Design.MAX_MACH] outputs[Aircraft.Fuel.FUEL_SYSTEM_MASS] = ( 1.07 * capacity**0.58 * num_eng_fact**0.43 * max_mach**0.34 * scaler @@ -42,7 +42,7 @@ def compute_partials(self, inputs, J): capacity = inputs[Aircraft.Fuel.MAX_CAPACITY_MASS] num_eng = self.options[Aircraft.Propulsion.TOTAL_NUM_ENGINES] num_eng_fact = distributed_engine_count_factor(num_eng) - max_mach = self.options[Mission.Constraints.MAX_MACH] + max_mach = self.options[Aircraft.Design.MAX_MACH] J[Aircraft.Fuel.FUEL_SYSTEM_MASS, Aircraft.Fuel.FUEL_SYSTEM_MASS_SCALER] = ( 1.07 * capacity**0.58 * num_eng_fact**0.43 * max_mach**0.34 / GRAV_ENGLISH_LBM diff --git a/aviary/subsystems/mass/flops_based/hydraulics.py b/aviary/subsystems/mass/flops_based/hydraulics.py index 2b312ee93f..bbd6eac1a8 100644 --- a/aviary/subsystems/mass/flops_based/hydraulics.py +++ b/aviary/subsystems/mass/flops_based/hydraulics.py @@ -18,7 +18,7 @@ class TransportHydraulicsGroupMass(om.ExplicitComponent): def initialize(self): add_aviary_option(self, Aircraft.Propulsion.TOTAL_NUM_FUSELAGE_ENGINES) add_aviary_option(self, Aircraft.Propulsion.TOTAL_NUM_WING_ENGINES) - add_aviary_option(self, Mission.Constraints.MAX_MACH) + add_aviary_option(self, Aircraft.Design.MAX_MACH) def setup(self): add_aviary_input(self, Aircraft.Fuselage.PLANFORM_AREA, units='ft**2') @@ -43,7 +43,7 @@ def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): scaler = inputs[Aircraft.Hydraulics.MASS_SCALER] area = inputs[Aircraft.Wing.AREA] var_sweep = inputs[Aircraft.Wing.VAR_SWEEP_MASS_PENALTY] - max_mach = self.options[Mission.Constraints.MAX_MACH] + max_mach = self.options[Aircraft.Design.MAX_MACH] if sys_press <= 0: # No hydraulic system (e.g. small electric UAVs): nothing to size. @@ -72,7 +72,7 @@ def compute_partials(self, inputs, J): scaler = inputs[Aircraft.Hydraulics.MASS_SCALER] area = inputs[Aircraft.Wing.AREA] var_sweep = inputs[Aircraft.Wing.VAR_SWEEP_MASS_PENALTY] - max_mach = self.options[Mission.Constraints.MAX_MACH] + max_mach = self.options[Aircraft.Design.MAX_MACH] term1 = planform + 0.27 * area term2 = 1.0 + 0.03 * num_wing_eng_fact + 0.05 * num_fuse_eng_fact diff --git a/aviary/subsystems/mass/flops_based/instruments.py b/aviary/subsystems/mass/flops_based/instruments.py index 874e46870d..0b5d920214 100644 --- a/aviary/subsystems/mass/flops_based/instruments.py +++ b/aviary/subsystems/mass/flops_based/instruments.py @@ -19,7 +19,7 @@ def initialize(self): add_aviary_option(self, Aircraft.CrewPayload.NUM_FLIGHT_CREW) add_aviary_option(self, Aircraft.Propulsion.TOTAL_NUM_FUSELAGE_ENGINES) add_aviary_option(self, Aircraft.Propulsion.TOTAL_NUM_WING_ENGINES) - add_aviary_option(self, Mission.Constraints.MAX_MACH) + add_aviary_option(self, Aircraft.Design.MAX_MACH) def setup(self): add_aviary_input(self, Aircraft.Fuselage.PLANFORM_AREA, units='ft**2') @@ -37,7 +37,7 @@ def compute(self, inputs, outputs): num_fuse_eng_fact = distributed_engine_count_factor(num_fuse_eng) fuse_area = inputs[Aircraft.Fuselage.PLANFORM_AREA] - max_mach = self.options[Mission.Constraints.MAX_MACH] + max_mach = self.options[Aircraft.Design.MAX_MACH] mass_scaler = inputs[Aircraft.Instruments.MASS_SCALER] instrument_weight = ( @@ -57,7 +57,7 @@ def compute_partials(self, inputs, J): num_fuse_eng_fact = distributed_engine_count_factor(num_fuse_eng) fuse_area = inputs[Aircraft.Fuselage.PLANFORM_AREA] - max_mach = self.options[Mission.Constraints.MAX_MACH] + max_mach = self.options[Aircraft.Design.MAX_MACH] mass_scaler = inputs[Aircraft.Instruments.MASS_SCALER] fact = 10.0 + 2.5 * num_crew + num_wing_eng_fact + 1.5 * num_fuse_eng_fact diff --git a/aviary/subsystems/mass/flops_based/passenger_service.py b/aviary/subsystems/mass/flops_based/passenger_service.py index ec6da58967..5b0b65416d 100644 --- a/aviary/subsystems/mass/flops_based/passenger_service.py +++ b/aviary/subsystems/mass/flops_based/passenger_service.py @@ -21,7 +21,7 @@ def initialize(self): add_aviary_option(self, Aircraft.CrewPayload.Design.NUM_BUSINESS_CLASS) add_aviary_option(self, Aircraft.CrewPayload.Design.NUM_FIRST_CLASS) add_aviary_option(self, Aircraft.CrewPayload.Design.NUM_ECONOMY_CLASS) - add_aviary_option(self, Mission.Constraints.MAX_MACH) + add_aviary_option(self, Aircraft.Design.MAX_MACH) def setup(self): add_aviary_input(self, Aircraft.CrewPayload.PASSENGER_SERVICE_MASS_SCALER, units='unitless') @@ -38,7 +38,7 @@ def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): economy_class_count = self.options[Aircraft.CrewPayload.Design.NUM_ECONOMY_CLASS] design_range = inputs[Aircraft.Design.RANGE] - max_mach = self.options[Mission.Constraints.MAX_MACH] + max_mach = self.options[Aircraft.Design.MAX_MACH] passenger_service_mass_scaler = inputs[Aircraft.CrewPayload.PASSENGER_SERVICE_MASS_SCALER] @@ -57,7 +57,7 @@ def compute_partials(self, inputs, J, discrete_inputs=None): economy_class_count = self.options[Aircraft.CrewPayload.Design.NUM_ECONOMY_CLASS] design_range = inputs[Aircraft.Design.RANGE] - max_mach = self.options[Mission.Constraints.MAX_MACH] + max_mach = self.options[Aircraft.Design.MAX_MACH] passenger_service_mass_scaler = inputs[Aircraft.CrewPayload.PASSENGER_SERVICE_MASS_SCALER] diff --git a/aviary/subsystems/mass/flops_based/starter.py b/aviary/subsystems/mass/flops_based/starter.py index 7def8a54de..8824d17807 100644 --- a/aviary/subsystems/mass/flops_based/starter.py +++ b/aviary/subsystems/mass/flops_based/starter.py @@ -21,7 +21,7 @@ class TransportStarterMass(om.ExplicitComponent): def initialize(self): add_aviary_option(self, Aircraft.Engine.NUM_ENGINES) add_aviary_option(self, Aircraft.Propulsion.TOTAL_NUM_ENGINES) - add_aviary_option(self, Mission.Constraints.MAX_MACH) + add_aviary_option(self, Aircraft.Design.MAX_MACH) def setup(self): num_engine_type = len(self.options[Aircraft.Engine.NUM_ENGINES]) @@ -39,7 +39,7 @@ def setup_partials(self): def compute(self, inputs, outputs): total_engines = self.options[Aircraft.Propulsion.TOTAL_NUM_ENGINES] num_engines = self.options[Aircraft.Engine.NUM_ENGINES] - max_mach = self.options[Mission.Constraints.MAX_MACH] + max_mach = self.options[Aircraft.Design.MAX_MACH] d_nacelle = inputs[Aircraft.Nacelle.AVG_DIAMETER] num_engines_factor = distributed_engine_count_factor(total_engines) @@ -57,7 +57,7 @@ def compute(self, inputs, outputs): def compute_partials(self, inputs, J): total_engines = self.options[Aircraft.Propulsion.TOTAL_NUM_ENGINES] num_engines = self.options[Aircraft.Engine.NUM_ENGINES] - max_mach = self.options[Mission.Constraints.MAX_MACH] + max_mach = self.options[Aircraft.Design.MAX_MACH] d_nacelle = inputs[Aircraft.Nacelle.AVG_DIAMETER] eng_count_factor = distributed_engine_count_factor(total_engines) diff --git a/aviary/subsystems/mass/flops_based/surface_controls.py b/aviary/subsystems/mass/flops_based/surface_controls.py index d6d0f7acbf..8471b224e9 100644 --- a/aviary/subsystems/mass/flops_based/surface_controls.py +++ b/aviary/subsystems/mass/flops_based/surface_controls.py @@ -12,7 +12,7 @@ class SurfaceControlMass(om.ExplicitComponent): """ def initialize(self): - add_aviary_option(self, Mission.Constraints.MAX_MACH) + add_aviary_option(self, Aircraft.Design.MAX_MACH) def setup(self): add_aviary_input(self, Aircraft.Wing.SURFACE_CONTROL_MASS_SCALER, units='unitless') @@ -31,7 +31,7 @@ def setup(self): def compute(self, inputs, outputs): scaler = inputs[Aircraft.Wing.SURFACE_CONTROL_MASS_SCALER] - max_mach = self.options[Mission.Constraints.MAX_MACH] + max_mach = self.options[Aircraft.Design.MAX_MACH] gross_weight = inputs[Aircraft.Design.GROSS_MASS] * GRAV_ENGLISH_LBM flap_ratio = inputs[Aircraft.Wing.CONTROL_SURFACE_AREA_RATIO] wing_area = inputs[Aircraft.Wing.AREA] @@ -48,7 +48,7 @@ def compute(self, inputs, outputs): def compute_partials(self, inputs, J): scaler = inputs[Aircraft.Wing.SURFACE_CONTROL_MASS_SCALER] - max_mach = self.options[Mission.Constraints.MAX_MACH] + max_mach = self.options[Aircraft.Design.MAX_MACH] gross_weight = inputs[Aircraft.Design.GROSS_MASS] * GRAV_ENGLISH_LBM flap_ratio = inputs[Aircraft.Wing.CONTROL_SURFACE_AREA_RATIO] wing_area = inputs[Aircraft.Wing.AREA] diff --git a/aviary/subsystems/mass/flops_based/test/test_fuel_system.py b/aviary/subsystems/mass/flops_based/test/test_fuel_system.py index 15cf34f1ad..8ffa34f052 100644 --- a/aviary/subsystems/mass/flops_based/test/test_fuel_system.py +++ b/aviary/subsystems/mass/flops_based/test/test_fuel_system.py @@ -107,7 +107,7 @@ def test_case(self, case_name): Aircraft.Propulsion.TOTAL_NUM_ENGINES: inputs.get_val( Aircraft.Propulsion.TOTAL_NUM_ENGINES ), - Mission.Constraints.MAX_MACH: inputs.get_val(Mission.Constraints.MAX_MACH), + Aircraft.Design.MAX_MACH: inputs.get_val(Aircraft.Design.MAX_MACH), } prob.model.add_subsystem( @@ -155,7 +155,7 @@ def test_case(self): Aircraft.Propulsion.TOTAL_NUM_ENGINES: inputs.get_val( Aircraft.Propulsion.TOTAL_NUM_ENGINES ), - Mission.Constraints.MAX_MACH: inputs.get_val(Mission.Constraints.MAX_MACH), + Aircraft.Design.MAX_MACH: inputs.get_val(Aircraft.Design.MAX_MACH), } prob.model.add_subsystem( diff --git a/aviary/subsystems/mass/flops_based/test/test_hydraulics.py b/aviary/subsystems/mass/flops_based/test/test_hydraulics.py index 3c1f25239f..7411140825 100644 --- a/aviary/subsystems/mass/flops_based/test/test_hydraulics.py +++ b/aviary/subsystems/mass/flops_based/test/test_hydraulics.py @@ -42,7 +42,7 @@ def test_case(self, case_name): Aircraft.Propulsion.TOTAL_NUM_WING_ENGINES: inputs.get_val( Aircraft.Propulsion.TOTAL_NUM_WING_ENGINES ), - Mission.Constraints.MAX_MACH: inputs.get_val(Mission.Constraints.MAX_MACH), + Aircraft.Design.MAX_MACH: inputs.get_val(Aircraft.Design.MAX_MACH), } prob.model.add_subsystem( @@ -99,7 +99,7 @@ def test_case(self): Aircraft.Propulsion.TOTAL_NUM_WING_ENGINES: inputs.get_val( Aircraft.Propulsion.TOTAL_NUM_WING_ENGINES ), - Mission.Constraints.MAX_MACH: inputs.get_val(Mission.Constraints.MAX_MACH), + Aircraft.Design.MAX_MACH: inputs.get_val(Aircraft.Design.MAX_MACH), } prob.model.add_subsystem( @@ -201,7 +201,7 @@ def testsdg_case(self, case_name): Aircraft.Propulsion.TOTAL_NUM_WING_ENGINES: inputs.get_val( Aircraft.Propulsion.TOTAL_NUM_WING_ENGINES ), - Mission.Constraints.MAX_MACH: inputs.get_val(Mission.Constraints.MAX_MACH), + Aircraft.Design.MAX_MACH: inputs.get_val(Aircraft.Design.MAX_MACH), } prob.model.add_subsystem( diff --git a/aviary/subsystems/mass/flops_based/test/test_instruments.py b/aviary/subsystems/mass/flops_based/test/test_instruments.py index 4304bb0ff5..bc2d12df24 100644 --- a/aviary/subsystems/mass/flops_based/test/test_instruments.py +++ b/aviary/subsystems/mass/flops_based/test/test_instruments.py @@ -38,7 +38,7 @@ def test_case(self, case_name): Aircraft.Propulsion.TOTAL_NUM_WING_ENGINES: inputs.get_val( Aircraft.Propulsion.TOTAL_NUM_WING_ENGINES ), - Mission.Constraints.MAX_MACH: inputs.get_val(Mission.Constraints.MAX_MACH), + Aircraft.Design.MAX_MACH: inputs.get_val(Aircraft.Design.MAX_MACH), } prob.model.add_subsystem( @@ -94,7 +94,7 @@ def test_case(self): Aircraft.Propulsion.TOTAL_NUM_WING_ENGINES: inputs.get_val( Aircraft.Propulsion.TOTAL_NUM_WING_ENGINES ), - Mission.Constraints.MAX_MACH: inputs.get_val(Mission.Constraints.MAX_MACH), + Aircraft.Design.MAX_MACH: inputs.get_val(Aircraft.Design.MAX_MACH), } prob.model.add_subsystem( diff --git a/aviary/subsystems/mass/flops_based/test/test_starter.py b/aviary/subsystems/mass/flops_based/test/test_starter.py index 03965d3393..bba89eaa90 100644 --- a/aviary/subsystems/mass/flops_based/test/test_starter.py +++ b/aviary/subsystems/mass/flops_based/test/test_starter.py @@ -57,7 +57,7 @@ def test_case_2(self): options = { Aircraft.Engine.NUM_ENGINES: np.array([5]), Aircraft.Propulsion.TOTAL_NUM_ENGINES: 5, - Mission.Constraints.MAX_MACH: 0.785, + Aircraft.Design.MAX_MACH: 0.785, } prob.model.add_subsystem( @@ -105,7 +105,7 @@ def test_case_2(self): options = { Aircraft.Engine.NUM_ENGINES: np.array([5]), Aircraft.Propulsion.TOTAL_NUM_ENGINES: 5, - Mission.Constraints.MAX_MACH: 0.785, + Aircraft.Design.MAX_MACH: 0.785, } prob.model.add_subsystem( @@ -118,7 +118,7 @@ def test_case_2(self): prob.model_options['*'] = get_flops_options('AdvancedSingleAisle', preprocess=True) prob.model_options[Aircraft.Engine.NUM_ENGINES] = np.array([5]) prob.model_options[Aircraft.Propulsion.TOTAL_NUM_ENGINES] = 5 - prob.model_options[Mission.Constraints.MAX_MACH] = 0.875 + prob.model_options[Aircraft.Design.MAX_MACH] = 0.875 prob.setup(check=False, force_alloc_complex=True) prob.set_val(Aircraft.Nacelle.AVG_DIAMETER, np.array([7.94]), 'ft') diff --git a/aviary/utils/aviary_values.py b/aviary/utils/aviary_values.py index 67d3d005b9..f315555231 100644 --- a/aviary/utils/aviary_values.py +++ b/aviary/utils/aviary_values.py @@ -21,6 +21,7 @@ class AviaryValues from aviary.utils.named_values import NamedValues from aviary.utils.utils import cast_type, check_type +from aviary.variable_info.legacy_aliases import resolve_legacy_variable_name from aviary.variable_info.variable_meta_data import CoreMetaData @@ -47,6 +48,8 @@ def set_val(self, key, val, units='unitless', meta_data=CoreMetaData): TypeError if units of `None` were specified or units of any type other than `str` """ + key = resolve_legacy_variable_name(key) + if key in meta_data: val = cast_type(key, val, meta_data) check_type(key, val, meta_data) @@ -55,6 +58,18 @@ def set_val(self, key, val, units='unitless', meta_data=CoreMetaData): super().set_val(key=key, val=val, units=units) + def get_item(self, key): + return super().get_item(resolve_legacy_variable_name(key)) + + def get_val(self, key, units='unitless'): + return super().get_val(resolve_legacy_variable_name(key), units) + + def delete(self, key): + return super().delete(resolve_legacy_variable_name(key)) + + def __contains__(self, key): + return super().__contains__(resolve_legacy_variable_name(key)) + def _check_units_compatibility(self, key, val, units, meta_data=CoreMetaData): """ Check that the two provided units are compatible - we don't actually want to convert here, diff --git a/aviary/utils/csv_data_file.py b/aviary/utils/csv_data_file.py index 4498bbe26c..abcbbf6b02 100644 --- a/aviary/utils/csv_data_file.py +++ b/aviary/utils/csv_data_file.py @@ -10,6 +10,7 @@ from aviary.utils.functions import get_path from aviary.utils.named_values import NamedValues from aviary.variable_info.enums import Verbosity +from aviary.variable_info.legacy_aliases import resolve_legacy_variable_name # multiple type annotation uses "typeA | typeB" syntax, but requires Python 3.10+ @@ -109,6 +110,7 @@ def read_data_file( item = [item[i].strip(') ') for i in range(len(item))] # OpenMDAO vars can't have spaces, convert to underscores name = re.sub('\\s', '_', item[0]) + name = resolve_legacy_variable_name(name) if aliases: # "reverse" lookup name in alias dict for key in aliases: diff --git a/aviary/utils/test/data/converter_test_BWB_detailed_FLOPS.csv b/aviary/utils/test/data/converter_test_BWB_detailed_FLOPS.csv index a5d032e461..82b2c6d3ee 100644 --- a/aviary/utils/test/data/converter_test_BWB_detailed_FLOPS.csv +++ b/aviary/utils/test/data/converter_test_BWB_detailed_FLOPS.csv @@ -20,6 +20,7 @@ aircraft:design:cruise_mach,0.85,unitless aircraft:design:gross_mass,874099,lbm aircraft:design:landing_to_takeoff_mass_ratio,0.8,unitless aircraft:design:lift_dependent_drag_coeff_factor,1,unitless +aircraft:design:max_mach,0.85,unitless aircraft:design:range,7750,NM aircraft:design:type,BWB,unitless aircraft:design:use_alt_mass,False,unitless @@ -89,7 +90,6 @@ aircraft:wing:thickness_to_chord,0.11,unitless aircraft:wing:thickness_to_chord_distribution,-1.0,0.15,0.1132,0.0928,0.0822,0.0764,0.0742,0.0746,0.0758,0.0758,0.0756,0.0756,0.0758,0.076,0.076,unitless aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0,unitless -mission:constraints:max_mach,0.85,unitless mission:landing:lift_coefficient_max,3.0,unitless mission:takeoff:braking_friction_coefficient,0.3,unitless mission:takeoff:rolling_friction_coefficient,0.025,unitless diff --git a/aviary/utils/test/data/converter_test_BWB_simple_FLOPS.csv b/aviary/utils/test/data/converter_test_BWB_simple_FLOPS.csv index 5da1a02068..d3f01f62e5 100644 --- a/aviary/utils/test/data/converter_test_BWB_simple_FLOPS.csv +++ b/aviary/utils/test/data/converter_test_BWB_simple_FLOPS.csv @@ -20,6 +20,7 @@ aircraft:design:cruise_mach,0.85,unitless aircraft:design:gross_mass,874099,lbm aircraft:design:landing_to_takeoff_mass_ratio,0.8,unitless aircraft:design:lift_dependent_drag_coeff_factor,1,unitless +aircraft:design:max_mach,0.85,unitless aircraft:design:range,7750,NM aircraft:design:type,BWB,unitless aircraft:design:use_alt_mass,False,unitless @@ -88,7 +89,6 @@ aircraft:wing:taper_ratio,0.311,unitless aircraft:wing:thickness_to_chord,0.11,unitless aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0,unitless -mission:constraints:max_mach,0.85,unitless mission:landing:lift_coefficient_max,3.0,unitless mission:takeoff:braking_friction_coefficient,0.3,unitless mission:takeoff:rolling_friction_coefficient,0.025,unitless diff --git a/aviary/utils/test/data/converter_test_advanced_single_aisle_FLOPS.csv b/aviary/utils/test/data/converter_test_advanced_single_aisle_FLOPS.csv index 9bb9ab3995..a460ba61ef 100644 --- a/aviary/utils/test/data/converter_test_advanced_single_aisle_FLOPS.csv +++ b/aviary/utils/test/data/converter_test_advanced_single_aisle_FLOPS.csv @@ -29,6 +29,7 @@ aircraft:design:landing_to_takeoff_mass_ratio,0.84,unitless aircraft:design:lift_coefficient,-1.0,unitless aircraft:design:lift_dependent_drag_coeff_factor,0.93,unitless aircraft:design:mach,-1.0,unitless +aircraft:design:max_mach,0.785,unitless aircraft:design:range,3500.0,NM aircraft:design:subsonic_drag_coeff_factor,0.95,unitless aircraft:design:supersonic_drag_coeff_factor,1.0,unitless @@ -152,7 +153,6 @@ aircraft:wing:thickness_to_chord_reference,0.116565,unitless aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0.0,unitless aircraft:wing:wetted_area,2210.280228,ft**2 -mission:constraints:max_mach,0.785,unitless mission:landing:drag_coefficient_min,0.045,unitless mission:landing:flare_rate,2.2,deg/s mission:landing:lift_coefficient_max,2.5,unitless diff --git a/aviary/validation_cases/benchmark_tests/test_bench_solved2dof.py b/aviary/validation_cases/benchmark_tests/test_bench_solved2dof.py index ffb22aefb1..60f7625fc9 100644 --- a/aviary/validation_cases/benchmark_tests/test_bench_solved2dof.py +++ b/aviary/validation_cases/benchmark_tests/test_bench_solved2dof.py @@ -204,7 +204,7 @@ def test_bench_Solved2DOF_landing(self): ) prob.check_and_preprocess_inputs() prob.build_model() - prob.add_driver('IPOPT', max_iter=100) + prob.add_driver('IPOPT', max_iter=150) prob.add_design_variables() prob.add_objective('time', ref=1e2) prob.setup() diff --git a/aviary/validation_cases/validation_data/test_data/V3_bug_fixed_IO.py b/aviary/validation_cases/validation_data/test_data/V3_bug_fixed_IO.py index a93696124f..8ac1787d04 100644 --- a/aviary/validation_cases/validation_data/test_data/V3_bug_fixed_IO.py +++ b/aviary/validation_cases/validation_data/test_data/V3_bug_fixed_IO.py @@ -106,7 +106,7 @@ def local_get_option_defaults() -> AviaryValues: option_defaults.set_val(Aircraft.Wing.NUM_FLAP_SEGMENTS, 2, units='unitless') option_defaults.set_val(Aircraft.Wing.NUM_INTEGRATION_STATIONS, 50, units='unitless') option_defaults.set_val(Aircraft.Wing.SPAN_EFFICIENCY_REDUCTION, False, units='unitless') - option_defaults.set_val(Mission.Constraints.MAX_MACH, 0.0, units='unitless') + option_defaults.set_val(Aircraft.Design.MAX_MACH, 0.0, units='unitless') option_defaults.set_val(Mission.GRAVITY, 9.80665, units='m/s**2') option_defaults.set_val(Mission.RESERVE_FUEL_MARGIN, 0.0, units='unitless') option_defaults.set_val(Mission.RESERVE_FUEL_MASS_ADDITIONAL, 0.0, units='lbm') diff --git a/aviary/validation_cases/validation_data/test_data/advanced_single_aisle_data.py b/aviary/validation_cases/validation_data/test_data/advanced_single_aisle_data.py index 2c7f688332..741c698b93 100644 --- a/aviary/validation_cases/validation_data/test_data/advanced_single_aisle_data.py +++ b/aviary/validation_cases/validation_data/test_data/advanced_single_aisle_data.py @@ -295,7 +295,7 @@ # --------------------------- inputs.set_val(Aircraft.Design.CRUISE_MACH, 0.785) inputs.set_val(Aircraft.Design.RANGE, 3500, 'NM') -inputs.set_val(Mission.Constraints.MAX_MACH, 0.785) +inputs.set_val(Aircraft.Design.MAX_MACH, 0.785) inputs.set_val(Mission.Landing.DRAG_COEFFICIENT_MIN, 0.045, 'unitless') inputs.set_val(Mission.Landing.LIFT_COEFFICIENT_MAX, 2.0, 'unitless') inputs.set_val(Mission.Takeoff.AIRPORT_ALTITUDE, 0.0, 'ft') diff --git a/aviary/validation_cases/validation_data/test_data/bwb300_baseline_FLOPS_data.py b/aviary/validation_cases/validation_data/test_data/bwb300_baseline_FLOPS_data.py index 3ecc60c156..296c4e47e5 100644 --- a/aviary/validation_cases/validation_data/test_data/bwb300_baseline_FLOPS_data.py +++ b/aviary/validation_cases/validation_data/test_data/bwb300_baseline_FLOPS_data.py @@ -266,7 +266,7 @@ # --------------------------- inputs.set_val(Aircraft.Design.CRUISE_MACH, 0.85) # VCMN inputs.set_val(Aircraft.Design.RANGE, 7500.0, 'NM') # DESRNG -inputs.set_val(Mission.Constraints.MAX_MACH, 0.9) # VMMO +inputs.set_val(Aircraft.Design.MAX_MACH, 0.9) # VMMO inputs.set_val(Mission.Landing.LIFT_COEFFICIENT_MAX, 3.0) # CLLDM inputs.set_val(Mission.Takeoff.LIFT_COEFFICIENT_MAX, 1.3) # CLTOM diff --git a/aviary/validation_cases/validation_data/test_data/bwb_detailed_FLOPS_data.py b/aviary/validation_cases/validation_data/test_data/bwb_detailed_FLOPS_data.py index c8c16f9180..93f79b66b4 100644 --- a/aviary/validation_cases/validation_data/test_data/bwb_detailed_FLOPS_data.py +++ b/aviary/validation_cases/validation_data/test_data/bwb_detailed_FLOPS_data.py @@ -273,7 +273,7 @@ # --------------------------- inputs.set_val(Aircraft.Design.CRUISE_MACH, 0.85) # VCMN inputs.set_val(Aircraft.Design.RANGE, 7750.0, 'NM') # DESRNG -inputs.set_val(Mission.Constraints.MAX_MACH, 0.85) # VMMO +inputs.set_val(Aircraft.Design.MAX_MACH, 0.85) # VMMO # inputs.set_val(Mission.Takeoff.FUEL, 577, 'lbm') # FTKOFL inputs.set_val(Mission.Landing.LIFT_COEFFICIENT_MAX, 3.0) # CLLDM diff --git a/aviary/validation_cases/validation_data/test_data/bwb_simple_FLOPS_data.py b/aviary/validation_cases/validation_data/test_data/bwb_simple_FLOPS_data.py index 8f0b71a6c2..e0431349fa 100644 --- a/aviary/validation_cases/validation_data/test_data/bwb_simple_FLOPS_data.py +++ b/aviary/validation_cases/validation_data/test_data/bwb_simple_FLOPS_data.py @@ -225,7 +225,7 @@ # --------------------------- inputs.set_val(Aircraft.Design.CRUISE_MACH, 0.85) # VCMN inputs.set_val(Aircraft.Design.RANGE, 7750.0, 'NM') # DESRNG -inputs.set_val(Mission.Constraints.MAX_MACH, 0.85) # VMMO +inputs.set_val(Aircraft.Design.MAX_MACH, 0.85) # VMMO # inputs.set_val(Mission.Takeoff.FUEL, 577, 'lbm') # FTKOFL inputs.set_val(Mission.Landing.LIFT_COEFFICIENT_MAX, 3.0) # CLLDM diff --git a/aviary/validation_cases/validation_data/test_data/large_single_aisle_1_FLOPS_data.py b/aviary/validation_cases/validation_data/test_data/large_single_aisle_1_FLOPS_data.py index 9b8de67707..86e6d370a9 100644 --- a/aviary/validation_cases/validation_data/test_data/large_single_aisle_1_FLOPS_data.py +++ b/aviary/validation_cases/validation_data/test_data/large_single_aisle_1_FLOPS_data.py @@ -229,7 +229,7 @@ # --------------------------- inputs.set_val(Aircraft.Design.CRUISE_MACH, 0.785) inputs.set_val(Aircraft.Design.RANGE, 3500, 'NM') -inputs.set_val(Mission.Constraints.MAX_MACH, 0.785) +inputs.set_val(Aircraft.Design.MAX_MACH, 0.785) # TODO investigate the origin of these values (taken from benchmark tests) # TODO: where should this get connected from? inputs.set_val(Mission.Takeoff.FUEL_MASS, 577, 'lbm') diff --git a/aviary/validation_cases/validation_data/test_data/large_single_aisle_2_FLOPS_data.py b/aviary/validation_cases/validation_data/test_data/large_single_aisle_2_FLOPS_data.py index 44ca8ce8c8..bed99c85a7 100644 --- a/aviary/validation_cases/validation_data/test_data/large_single_aisle_2_FLOPS_data.py +++ b/aviary/validation_cases/validation_data/test_data/large_single_aisle_2_FLOPS_data.py @@ -230,7 +230,7 @@ # --------------------------- inputs.set_val(Aircraft.Design.CRUISE_MACH, 0.785) inputs.set_val(Aircraft.Design.RANGE, 2960.0, 'NM') -inputs.set_val(Mission.Constraints.MAX_MACH, 0.82) +inputs.set_val(Aircraft.Design.MAX_MACH, 0.82) # TODO investigate the origin of these values (taken from benchmark tests) # TODO: where should this get connected from? inputs.set_val(Mission.Takeoff.FUEL_MASS, 659, 'lbm') diff --git a/aviary/validation_cases/validation_data/test_data/large_single_aisle_2_altwt_FLOPS_data.py b/aviary/validation_cases/validation_data/test_data/large_single_aisle_2_altwt_FLOPS_data.py index 22f39893fe..b49978ce73 100644 --- a/aviary/validation_cases/validation_data/test_data/large_single_aisle_2_altwt_FLOPS_data.py +++ b/aviary/validation_cases/validation_data/test_data/large_single_aisle_2_altwt_FLOPS_data.py @@ -230,7 +230,7 @@ # --------------------------- inputs.set_val(Aircraft.Design.CRUISE_MACH, 0.785) inputs.set_val(Aircraft.Design.RANGE, 2960.0, 'NM') -inputs.set_val(Mission.Constraints.MAX_MACH, 0.82) +inputs.set_val(Aircraft.Design.MAX_MACH, 0.82) # TODO investigate the origin of these values (taken from benchmark tests) # TODO: where should this get connected from? inputs.set_val(Mission.Takeoff.FUEL_MASS, 659, 'lbm') diff --git a/aviary/validation_cases/validation_data/test_data/large_single_aisle_2_detailwing_FLOPS_data.py b/aviary/validation_cases/validation_data/test_data/large_single_aisle_2_detailwing_FLOPS_data.py index 7cb2f1f50c..f92173b07e 100644 --- a/aviary/validation_cases/validation_data/test_data/large_single_aisle_2_detailwing_FLOPS_data.py +++ b/aviary/validation_cases/validation_data/test_data/large_single_aisle_2_detailwing_FLOPS_data.py @@ -225,7 +225,7 @@ # --------------------------- inputs.set_val(Aircraft.Design.CRUISE_MACH, 0.785) # was 0.82 inputs.set_val(Aircraft.Design.RANGE, 2960.0, 'NM') -inputs.set_val(Mission.Constraints.MAX_MACH, 0.82) +inputs.set_val(Aircraft.Design.MAX_MACH, 0.82) # TODO investigate the origin of these values (taken from benchmark tests) # TODO: where should this get connected from? inputs.set_val(Mission.Takeoff.FUEL_MASS, 577, 'lbm') diff --git a/aviary/validation_cases/validation_data/test_data/multi_engine_single_aisle_data.py b/aviary/validation_cases/validation_data/test_data/multi_engine_single_aisle_data.py index 2829336bf3..80b79c09a4 100644 --- a/aviary/validation_cases/validation_data/test_data/multi_engine_single_aisle_data.py +++ b/aviary/validation_cases/validation_data/test_data/multi_engine_single_aisle_data.py @@ -260,7 +260,7 @@ # --------------------------- inputs.set_val(Aircraft.Design.CRUISE_MACH, 0.785) # was 0.82 inputs.set_val(Aircraft.Design.RANGE, 2960.0, 'NM') -inputs.set_val(Mission.Constraints.MAX_MACH, 0.82) +inputs.set_val(Aircraft.Design.MAX_MACH, 0.82) # TODO investigate the origin of these values (taken from benchmark tests) # TODO: where should this get connected from? inputs.set_val(Mission.Takeoff.FUEL_MASS, 577, 'lbm') diff --git a/aviary/validation_cases/validation_data/test_models/aircraft_for_bench_FwFm.csv b/aviary/validation_cases/validation_data/test_models/aircraft_for_bench_FwFm.csv index b482febfd9..16993860c8 100644 --- a/aviary/validation_cases/validation_data/test_models/aircraft_for_bench_FwFm.csv +++ b/aviary/validation_cases/validation_data/test_models/aircraft_for_bench_FwFm.csv @@ -141,7 +141,7 @@ aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0.0,unitless aircraft:wing:wetted_area_scaler,1.0,unitless aircraft:wing:wetted_area,2396.56,ft**2 -mission:constraints:max_mach,0.785,unitless +aircraft:design:max_mach,0.785,unitless aircraft:design:cruise_altitude,35000,ft aircraft:design:gross_mass,175400.0,lbm aircraft:design:range,3500,NM diff --git a/aviary/validation_cases/validation_data/test_models/aircraft_for_bench_FwFm_with_electric.csv b/aviary/validation_cases/validation_data/test_models/aircraft_for_bench_FwFm_with_electric.csv index 19a17a36d3..b321eba451 100644 --- a/aviary/validation_cases/validation_data/test_models/aircraft_for_bench_FwFm_with_electric.csv +++ b/aviary/validation_cases/validation_data/test_models/aircraft_for_bench_FwFm_with_electric.csv @@ -140,7 +140,7 @@ aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0.0,unitless aircraft:wing:wetted_area_scaler,1.0,unitless aircraft:wing:wetted_area,2396.56,ft**2 -mission:constraints:max_mach,0.785,unitless +aircraft:design:max_mach,0.785,unitless aircraft:design:cruise_altitude,35000,ft aircraft:design:gross_mass,175400.0,lbm aircraft:design:range,3500,NM diff --git a/aviary/validation_cases/validation_data/test_models/aircraft_for_bench_FwGm.csv b/aviary/validation_cases/validation_data/test_models/aircraft_for_bench_FwGm.csv index e07bad7f34..5ea8d06410 100644 --- a/aviary/validation_cases/validation_data/test_models/aircraft_for_bench_FwGm.csv +++ b/aviary/validation_cases/validation_data/test_models/aircraft_for_bench_FwGm.csv @@ -235,7 +235,7 @@ aircraft:wing:thickness_to_chord_distribution,0.145,0.115,0.104,unitless aircraft:wing:thickness_to_chord,0.13,unitless aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0.0,unitless -mission:constraints:max_mach,0.8,unitless +aircraft:design:max_mach,0.8,unitless mission:landing:braking_friction_coefficient,0.4,unitless mission:landing:lift_coefficient_max,2.0,unitless aircraft:design:cruise_mach,0.8,unitless diff --git a/aviary/validation_cases/validation_data/test_models/aircraft_for_bench_GwFm.csv b/aviary/validation_cases/validation_data/test_models/aircraft_for_bench_GwFm.csv index 26a08427b7..fbf4d4b1f5 100644 --- a/aviary/validation_cases/validation_data/test_models/aircraft_for_bench_GwFm.csv +++ b/aviary/validation_cases/validation_data/test_models/aircraft_for_bench_GwFm.csv @@ -140,7 +140,7 @@ aircraft:wing:taper_ratio,0.33,unitless aircraft:wing:thickness_to_chord_root,0.15,unitless aircraft:wing:thickness_to_chord_tip,0.12,unitless aircraft:wing:zero_lift_angle,-1.2,deg -mission:constraints:max_mach,0.785,unitless +aircraft:design:max_mach,0.785,unitless aircraft:design:cruise_altitude,37500,ft # double check against phase_info aircraft:design:gross_mass,175400.0,lbm aircraft:design:lift_coefficient_max_flaps_up,1.2596,unitless diff --git a/aviary/validation_cases/validation_data/test_models/aircraft_for_bench_solved2dof.csv b/aviary/validation_cases/validation_data/test_models/aircraft_for_bench_solved2dof.csv index 9dac74d748..0761ddd143 100644 --- a/aviary/validation_cases/validation_data/test_models/aircraft_for_bench_solved2dof.csv +++ b/aviary/validation_cases/validation_data/test_models/aircraft_for_bench_solved2dof.csv @@ -141,7 +141,7 @@ aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0.0,unitless aircraft:wing:wetted_area_scaler,1.0,unitless aircraft:wing:wetted_area,2396.56,ft**2 -mission:constraints:max_mach,0.785,unitless +aircraft:design:max_mach,0.785,unitless aircraft:design:cruise_altitude,35000,ft aircraft:design:gross_mass,175400.0,lbm mission:gross_mass,175400.0,lbm diff --git a/aviary/validation_cases/validation_data/test_models/bwb_root_chord1.csv b/aviary/validation_cases/validation_data/test_models/bwb_root_chord1.csv index 4b7c1ac82e..882aad50a3 100644 --- a/aviary/validation_cases/validation_data/test_models/bwb_root_chord1.csv +++ b/aviary/validation_cases/validation_data/test_models/bwb_root_chord1.csv @@ -164,7 +164,7 @@ aircraft:wing:thickness_to_chord_reference,0.0,unitless aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0.0,unitless aircraft:wing:wetted_area,9111.14415,ft**2 -mission:constraints:max_mach,0.85,unitless +aircraft:design:max_mach,0.85,unitless mission:landing:lift_coefficient_max,0.8,unitless mission:takeoff:braking_friction_coefficient,0.3,unitless mission:takeoff:fuel_mass,0.0,lbm diff --git a/aviary/validation_cases/validation_data/test_models/bwb_root_chord2.csv b/aviary/validation_cases/validation_data/test_models/bwb_root_chord2.csv index 2f5c95a591..266a3be708 100644 --- a/aviary/validation_cases/validation_data/test_models/bwb_root_chord2.csv +++ b/aviary/validation_cases/validation_data/test_models/bwb_root_chord2.csv @@ -164,7 +164,7 @@ aircraft:wing:thickness_to_chord_reference,0.0,unitless aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0.0,unitless aircraft:wing:wetted_area,9111.14415,ft**2 -mission:constraints:max_mach,0.85,unitless +aircraft:design:max_mach,0.85,unitless mission:landing:lift_coefficient_max,0.8,unitless mission:takeoff:braking_friction_coefficient,0.3,unitless mission:takeoff:fuel_mass,0.0,lbm diff --git a/aviary/validation_cases/validation_data/test_models/high_wing_single_aisle.csv b/aviary/validation_cases/validation_data/test_models/high_wing_single_aisle.csv index 0d91f5091d..1803e67673 100644 --- a/aviary/validation_cases/validation_data/test_models/high_wing_single_aisle.csv +++ b/aviary/validation_cases/validation_data/test_models/high_wing_single_aisle.csv @@ -125,7 +125,7 @@ aircraft:wing:thickness_to_chord_distribution,0.1,0.1,0.1,unitless aircraft:wing:thickness_to_chord_reference,0.01,unitless #check aircraft:wing:ultimate_load_factor,3.75,unitless aircraft:wing:var_sweep_mass_penalty,0.01,unitless #check -mission:constraints:max_mach,0.85,unitless +aircraft:design:max_mach,0.85,unitless aircraft:design:cruise_altitude,38000,ft aircraft:design:gross_mass,150000.0,lbm aircraft:design:range,3500,NM diff --git a/aviary/variable_info/legacy_aliases.py b/aviary/variable_info/legacy_aliases.py new file mode 100644 index 0000000000..9d45a1f5fc --- /dev/null +++ b/aviary/variable_info/legacy_aliases.py @@ -0,0 +1,15 @@ +"""Compatibility aliases for renamed Aviary variable keys. + +New code and generated data should always use canonical names. This map is +only for reading or accessing values that were written with an older Aviary +variable name. +""" + +LEGACY_VARIABLE_NAME_MAP = { + 'mission:constraints:max_mach': 'aircraft:design:max_mach', +} + + +def resolve_legacy_variable_name(name): + """Return the canonical name for a supported legacy Aviary variable.""" + return LEGACY_VARIABLE_NAME_MAP.get(name, name) diff --git a/aviary/variable_info/migrations/max_mach_namespace_manifest.json b/aviary/variable_info/migrations/max_mach_namespace_manifest.json new file mode 100644 index 0000000000..f7d293de0b --- /dev/null +++ b/aviary/variable_info/migrations/max_mach_namespace_manifest.json @@ -0,0 +1,68 @@ +{ + "issue": "OpenMDAO/Aviary#1046", + "canonical_symbol": "Aircraft.Design.MAX_MACH", + "canonical_key": "aircraft:design:max_mach", + "legacy_symbol": "Mission.Constraints.MAX_MACH", + "legacy_key": "mission:constraints:max_mach", + "changed_file_count": 50, + "changed_files": [ + "aviary/docs/examples_unreviewed/modified_aircraft.csv", + "aviary/docs/user_guide/fundamentals/aircraft_definition.ipynb", + "aviary/interface/test/sizing_results_for_test.json", + "aviary/models/aircraft/advanced_single_aisle/advanced_single_aisle_FLOPS.csv", + "aviary/models/aircraft/blended_wing_body/bwb300_baseline_FLOPS.csv", + "aviary/models/aircraft/blended_wing_body/bwb_detailed_FLOPS.csv", + "aviary/models/aircraft/blended_wing_body/bwb_simple_FLOPS.csv", + "aviary/models/aircraft/large_single_aisle_1/large_single_aisle_1_FLOPS.csv", + "aviary/models/aircraft/large_single_aisle_2/large_single_aisle_2_FLOPS.csv", + "aviary/models/aircraft/large_single_aisle_2/large_single_aisle_2_altwt_FLOPS.csv", + "aviary/models/aircraft/large_single_aisle_2/large_single_aisle_2_detailwing_FLOPS.csv", + "aviary/models/aircraft/minimum_single_aisle/minimum_single_aisle_FLOPS.csv", + "aviary/subsystems/aerodynamics/flops_based/premission_aero.py", + "aviary/subsystems/aerodynamics/flops_based/test/test_premission_aero.py", + "aviary/subsystems/mass/flops_based/air_conditioning.py", + "aviary/subsystems/mass/flops_based/fuel_system.py", + "aviary/subsystems/mass/flops_based/hydraulics.py", + "aviary/subsystems/mass/flops_based/instruments.py", + "aviary/subsystems/mass/flops_based/passenger_service.py", + "aviary/subsystems/mass/flops_based/starter.py", + "aviary/subsystems/mass/flops_based/surface_controls.py", + "aviary/subsystems/mass/flops_based/test/test_fuel_system.py", + "aviary/subsystems/mass/flops_based/test/test_hydraulics.py", + "aviary/subsystems/mass/flops_based/test/test_instruments.py", + "aviary/subsystems/mass/flops_based/test/test_starter.py", + "aviary/utils/aviary_values.py", + "aviary/utils/csv_data_file.py", + "aviary/utils/test/data/converter_test_BWB_detailed_FLOPS.csv", + "aviary/utils/test/data/converter_test_BWB_simple_FLOPS.csv", + "aviary/utils/test/data/converter_test_advanced_single_aisle_FLOPS.csv", + "aviary/validation_cases/validation_data/test_data/V3_bug_fixed_IO.py", + "aviary/validation_cases/validation_data/test_data/advanced_single_aisle_data.py", + "aviary/validation_cases/validation_data/test_data/bwb300_baseline_FLOPS_data.py", + "aviary/validation_cases/validation_data/test_data/bwb_detailed_FLOPS_data.py", + "aviary/validation_cases/validation_data/test_data/bwb_simple_FLOPS_data.py", + "aviary/validation_cases/validation_data/test_data/large_single_aisle_1_FLOPS_data.py", + "aviary/validation_cases/validation_data/test_data/large_single_aisle_2_FLOPS_data.py", + "aviary/validation_cases/validation_data/test_data/large_single_aisle_2_altwt_FLOPS_data.py", + "aviary/validation_cases/validation_data/test_data/large_single_aisle_2_detailwing_FLOPS_data.py", + "aviary/validation_cases/validation_data/test_data/multi_engine_single_aisle_data.py", + "aviary/validation_cases/validation_data/test_models/aircraft_for_bench_FwFm.csv", + "aviary/validation_cases/validation_data/test_models/aircraft_for_bench_FwFm_with_electric.csv", + "aviary/validation_cases/validation_data/test_models/aircraft_for_bench_FwGm.csv", + "aviary/validation_cases/validation_data/test_models/aircraft_for_bench_GwFm.csv", + "aviary/validation_cases/validation_data/test_models/aircraft_for_bench_solved2dof.csv", + "aviary/validation_cases/validation_data/test_models/bwb_root_chord1.csv", + "aviary/validation_cases/validation_data/test_models/bwb_root_chord2.csv", + "aviary/validation_cases/validation_data/test_models/high_wing_single_aisle.csv", + "aviary/variable_info/variable_meta_data.py", + "aviary/variable_info/variables.py" + ], + "replacement_counts": { + "python_symbol": 57, + "serialized_key": 24 + }, + "compatibility": { + "python_symbol": "legacy Python symbol removed; serialized legacy key remains supported", + "serialized_inputs": "legacy raw names normalize before metadata validation/storage" + } +} diff --git a/aviary/variable_info/test/test_max_mach_namespace.py b/aviary/variable_info/test/test_max_mach_namespace.py new file mode 100644 index 0000000000..e7b3e1fc6f --- /dev/null +++ b/aviary/variable_info/test/test_max_mach_namespace.py @@ -0,0 +1,80 @@ +import tempfile +import unittest +from pathlib import Path + +from aviary.utils.aviary_values import AviaryValues +from aviary.utils.csv_data_file import read_data_file +from aviary.variable_info.legacy_aliases import ( + LEGACY_VARIABLE_NAME_MAP, + resolve_legacy_variable_name, +) +from aviary.variable_info.variable_meta_data import CoreMetaData +from aviary.variable_info.variables import Aircraft, Mission + + +class MaxMachNamespaceTest(unittest.TestCase): + def test_canonical_namespace(self): + self.assertEqual(Aircraft.Design.MAX_MACH, 'aircraft:design:max_mach') + self.assertFalse(hasattr(Mission.Constraints, 'MAX_MACH')) + self.assertIn(Aircraft.Design.MAX_MACH, CoreMetaData) + self.assertNotIn('mission:constraints:max_mach', CoreMetaData) + + def test_legacy_values_access_canonical_storage(self): + legacy = 'mission:constraints:max_mach' + self.assertEqual(resolve_legacy_variable_name(legacy), Aircraft.Design.MAX_MACH) + values = AviaryValues() + values.set_val(legacy, 0.86) + self.assertIn(legacy, values) + self.assertIn(Aircraft.Design.MAX_MACH, values) + self.assertEqual(values.get_val(legacy), 0.86) + self.assertEqual(values.get_val(Aircraft.Design.MAX_MACH), 0.86) + self.assertEqual(list(values.keys()), [Aircraft.Design.MAX_MACH]) + + def test_legacy_csv_header_is_normalized(self): + legacy = next(iter(LEGACY_VARIABLE_NAME_MAP)) + with tempfile.TemporaryDirectory() as tmp: + path = Path(tmp) / 'legacy.csv' + path.write_text(f'{legacy}\n0.84\n', encoding='utf-8') + data, _, _ = read_data_file(path, metadata=CoreMetaData) + self.assertIn(Aircraft.Design.MAX_MACH, data) + self.assertNotIn(legacy, data) + self.assertEqual(data.get_val(Aircraft.Design.MAX_MACH), 0.84) + + def test_no_unapproved_legacy_references(self): + root = Path(__file__).resolve().parents[3] + allowed = { + Path('aviary/variable_info/legacy_aliases.py'), + Path('aviary/variable_info/test/test_max_mach_namespace.py'), + Path('aviary/variable_info/migrations/max_mach_namespace_manifest.json'), + Path('tools/migrate_max_mach_namespace.py'), + } + suffixes = { + '.py', + '.csv', + '.json', + '.ipynb', + '.md', + '.rst', + '.txt', + '.toml', + '.yaml', + '.yml', + } + failures = [] + for path in root.rglob('*'): + if not path.is_file() or path.suffix.lower() not in suffixes: + continue + rel = path.relative_to(root) + if rel in allowed or '.git' in rel.parts: + continue + try: + text = path.read_text(encoding='utf-8') + except UnicodeDecodeError: + continue + if 'Mission.Constraints.MAX_MACH' in text or 'mission:constraints:max_mach' in text: + failures.append(str(rel)) + self.assertEqual(failures, [], 'Legacy MAX_MACH references remain: ' + ', '.join(failures)) + + +if __name__ == '__main__': + unittest.main() diff --git a/aviary/variable_info/variable_meta_data.py b/aviary/variable_info/variable_meta_data.py index a170848188..201dbf1d69 100644 --- a/aviary/variable_info/variable_meta_data.py +++ b/aviary/variable_info/variable_meta_data.py @@ -1588,6 +1588,7 @@ default_value=0.0, ) + add_meta_data( Aircraft.Design.MAX_FUSELAGE_PITCH_ANGLE, meta_data=_MetaData, @@ -1597,6 +1598,29 @@ default_value=15, ) +add_meta_data( + Aircraft.Design.MAX_MACH, + meta_data=_MetaData, + historical_name={ + 'GASP': None, + 'FLOPS': 'WTIN.VMMO', + # [ # inputs + # '&DEFINE.WTIN.VMMO', 'VLIMIT.VMMO', + # # outputs + # 'VLIMIT.VMAX', + # ], + }, + units='unitless', + desc=( + 'Maximum aircraft design Mach number. Used by FLOPS-based air-conditioning, ' + 'fuel-system, hydraulics, instruments, passenger-service, starter, and surface-' + 'control mass correlations, and by FLOPS pre-mission aerodynamics when computing ' + 'the design lift coefficient.' + ), + default_value=0.0, + option=True, +) + add_meta_data( Aircraft.Design.MAX_STRUCTURAL_SPEED, meta_data=_MetaData, @@ -7073,24 +7097,6 @@ '(within acceptable tolerance)', ) -add_meta_data( - Mission.Constraints.MAX_MACH, - meta_data=_MetaData, - historical_name={ - 'GASP': None, - 'FLOPS': 'WTIN.VMMO', - # [ # inputs - # '&DEFINE.WTIN.VMMO', 'VLIMIT.VMMO', - # # outputs - # 'VLIMIT.VMAX', - # ], - }, - units='unitless', - desc='aircraft cruise Mach number', - # TODO: derived default value: Aircraft.Design.CRUISE_MACH ??? - default_value=0.0, - option=True, -) add_meta_data( Mission.Constraints.RANGE_RESIDUAL, diff --git a/aviary/variable_info/variables.py b/aviary/variable_info/variables.py index afebc56fd2..1f5d8a8814 100644 --- a/aviary/variable_info/variables.py +++ b/aviary/variable_info/variables.py @@ -167,6 +167,7 @@ class Design: LIFT_POLAR = 'aircraft:design:lift_polar' MACH = 'aircraft:design:mach' MAX_FUSELAGE_PITCH_ANGLE = 'aircraft:design:max_fuselage_pitch_angle' + MAX_MACH = 'aircraft:design:max_mach' MAX_STRUCTURAL_SPEED = 'aircraft:design:max_structural_speed' PART25_STRUCTURAL_CATEGORY = 'aircraft:design:part25_structural_category' PERCENT_EXCRESCENCE_DRAG = 'aircraft:design:percent_excrescence_drag' @@ -699,7 +700,6 @@ class Constraints: EXCESS_FUEL_MASS_CAPACITY = 'mission:constraints:excess_fuel_mass_capacity' GEARBOX_SHAFT_POWER_RESIDUAL = 'mission:constraints:gearbox_shaft_power_residual' MASS_RESIDUAL = 'mission:constraints:mass_residual' - MAX_MACH = 'mission:constraints:max_mach' RANGE_RESIDUAL = 'mission:constraints:range_residual' RANGE_RESIDUAL_RESERVE = 'mission:constraints:range_residual_reserve' diff --git a/tools/migrate_max_mach_namespace.py b/tools/migrate_max_mach_namespace.py new file mode 100644 index 0000000000..37e2e7ae89 --- /dev/null +++ b/tools/migrate_max_mach_namespace.py @@ -0,0 +1,255 @@ +#!/usr/bin/env python3 +"""Apply and audit the repository-wide MAX_MACH namespace migration (#1046).""" + +import argparse +import json +import re +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +OLD_SYMBOL = 'Mission.Constraints.MAX_MACH' +NEW_SYMBOL = 'Aircraft.Design.MAX_MACH' +OLD_KEY = 'mission:constraints:max_mach' +NEW_KEY = 'aircraft:design:max_mach' +SUFFIXES = {'.py', '.csv', '.json', '.ipynb', '.md', '.rst', '.txt', '.toml', '.yaml', '.yml'} +SKIP = {'.git', '.venv', 'venv', 'build', 'dist', '__pycache__', '.pytest_cache'} +MANIFEST = Path('aviary/variable_info/migrations/max_mach_namespace_manifest.json') +ALLOW = { + Path('tools/migrate_max_mach_namespace.py'), + Path('aviary/variable_info/legacy_aliases.py'), + Path('aviary/variable_info/test/test_max_mach_namespace.py'), + MANIFEST, +} + + +def files(): + for path in ROOT.rglob('*'): + if path.is_file() and path.suffix.lower() in SUFFIXES: + rel = path.relative_to(ROOT) + if not any(part in SKIP for part in rel.parts): + yield rel + + +def patch_variables(text): + old = " MAX_MACH = 'mission:constraints:max_mach'\n" + alias = ( + ' # Backward-compatible symbol alias; new code uses Aircraft.Design.MAX_MACH.\n' + ' MAX_MACH = Aircraft.Design.MAX_MACH\n' + ) + text = text.replace(old, '', 1).replace(alias, '', 1) + + canonical = " MAX_MACH = 'aircraft:design:max_mach'\n" + text = text.replace(canonical, '') + anchor = " MAX_FUSELAGE_PITCH_ANGLE = 'aircraft:design:max_fuselage_pitch_angle'\n" + if anchor not in text: + raise RuntimeError('Aircraft.Design.MAX_FUSELAGE_PITCH_ANGLE anchor not found') + text = text.replace(anchor, anchor + canonical, 1) + return text + + +def metadata_block(text, symbol): + pos = text.find(symbol) + if pos < 0: + raise RuntimeError(f'{symbol} metadata block not found') + start = text.rfind('add_meta_data(\n', 0, pos) + end = text.find('\n)\n', pos) + if start < 0 or end < 0: + raise RuntimeError(f'{symbol} metadata block boundaries not found') + return start, end + 3 + + +def patch_metadata(text): + # Check if MAX_MACH is already correctly placed after MAX_FUSELAGE_PITCH_ANGLE + try: + anchor_start, anchor_end = metadata_block( + text, ' Aircraft.Design.MAX_FUSELAGE_PITCH_ANGLE,' + ) + max_mach_start, max_mach_end = metadata_block(text, ' Aircraft.Design.MAX_MACH,') + except RuntimeError: + # If we can't find the blocks, let the original logic handle it + pass + else: + # If MAX_MACH immediately follows MAX_FUSELAGE_PITCH_ANGLE (with only one blank line), + # check if the description is correct + expected_gap = '\n' + gap = text[anchor_end:max_mach_start] + if gap == expected_gap: + # Check if description matches expected + block = text[max_mach_start:max_mach_end] + expected_desc = ( + ' desc=(\n' + " 'Maximum aircraft design Mach number. Used by FLOPS-based air-conditioning, '\n" + " 'fuel-system, hydraulics, instruments, passenger-service, starter, and surface-'\n" + " 'control mass correlations, and by FLOPS pre-mission aerodynamics when computing '\n" + " 'the design lift coefficient.'\n" + ' ),\n' + ) + if expected_desc in block: + # Already in correct position with correct description - no changes needed + return text + + # Need to move/fix the block + start, end = metadata_block(text, ' Aircraft.Design.MAX_MACH,') + block = text[start:end] + desc = ( + ' desc=(\n' + " 'Maximum aircraft design Mach number. Used by FLOPS-based air-conditioning, '\n" + " 'fuel-system, hydraulics, instruments, passenger-service, starter, and surface-'\n" + " 'control mass correlations, and by FLOPS pre-mission aerodynamics when computing '\n" + " 'the design lift coefficient.'\n" + ' ),\n' + ) + pattern = re.compile(r' desc=.*?(?= [a-zA-Z_]+\s*=|\)\n)', re.S) + match = pattern.search(block) + if match: + block = block[: match.start()] + desc + block[match.end() :] + else: + close = block.rfind(')\n') + block = block[:close] + desc + block[close:] + + # Remove the block first, then insert it immediately after MAX_FUSELAGE_PITCH_ANGLE. + text = text[:start] + text[end:] + _, anchor_end = metadata_block(text, ' Aircraft.Design.MAX_FUSELAGE_PITCH_ANGLE,') + # Check if there are already blank lines after anchor_end and trim them to single newline + insert_pos = anchor_end + while insert_pos < len(text) and text[insert_pos] == '\n': + insert_pos += 1 + text = text[:anchor_end] + '\n' + block + text[insert_pos:] + return text + + +def patch_values(text): + marker = 'from aviary.variable_info.variable_meta_data import CoreMetaData\n' + imp = 'from aviary.variable_info.legacy_aliases import resolve_legacy_variable_name\n' + if imp not in text: + text = text.replace(marker, imp + marker, 1) + + marker = ' if key in meta_data:\n' + normalize = ' key = resolve_legacy_variable_name(key)\n\n' + if normalize + marker not in text: + text = text.replace(marker, normalize + marker, 1) + + marker = ' def _check_units_compatibility(self, key, val, units, meta_data=CoreMetaData):\n' + methods = """ def get_item(self, key): + return super().get_item(resolve_legacy_variable_name(key)) + + def get_val(self, key, units='unitless'): + return super().get_val(resolve_legacy_variable_name(key), units) + + def delete(self, key): + return super().delete(resolve_legacy_variable_name(key)) + + def __contains__(self, key): + return super().__contains__(resolve_legacy_variable_name(key)) + +""" + if 'def get_item(self, key):' not in text: + if marker not in text: + raise RuntimeError('AviaryValues insertion point not found') + text = text.replace(marker, methods + marker, 1) + return text + + +def patch_csv(text): + marker = 'from aviary.variable_info.enums import Verbosity\n' + imp = 'from aviary.variable_info.legacy_aliases import resolve_legacy_variable_name\n' + if imp not in text: + text = text.replace(marker, marker + imp, 1) + marker = " name = re.sub('\\\\s', '_', item[0])\n" + add = marker + ' name = resolve_legacy_variable_name(name)\n' + if 'name = resolve_legacy_variable_name(name)' not in text: + if marker not in text: + raise RuntimeError('CSV header normalization point not found') + text = text.replace(marker, add, 1) + return text + + +def rewrite(rel, text): + if rel == Path('aviary/variable_info/variables.py'): + text = patch_variables(text) + if rel not in ALLOW: + text = text.replace(OLD_SYMBOL, NEW_SYMBOL).replace(OLD_KEY, NEW_KEY) + if rel == Path('aviary/variable_info/variable_meta_data.py'): + text = patch_metadata(text) + elif rel == Path('aviary/utils/aviary_values.py'): + text = patch_values(text) + elif rel == Path('aviary/utils/csv_data_file.py'): + text = patch_csv(text) + return text + + +def apply(): + changed = [] + counts = {'python_symbol': 0, 'serialized_key': 0} + for rel in list(files()): + path = ROOT / rel + try: + before = path.read_text(encoding='utf-8') + except UnicodeDecodeError: + continue + after = rewrite(rel, before) + if before != after: + counts['python_symbol'] += before.count(OLD_SYMBOL) + counts['serialized_key'] += before.count(OLD_KEY) + path.write_text(after, encoding='utf-8') + changed.append(str(rel)) + + out = ROOT / MANIFEST + # Only rewrite manifest if there were actual content migrations (symbol/key replacements) + # or if the manifest doesn't exist yet + has_real_migrations = counts['python_symbol'] > 0 or counts['serialized_key'] > 0 + if has_real_migrations or not out.exists(): + manifest = { + 'issue': 'OpenMDAO/Aviary#1046', + 'canonical_symbol': NEW_SYMBOL, + 'canonical_key': NEW_KEY, + 'legacy_symbol': OLD_SYMBOL, + 'legacy_key': OLD_KEY, + 'changed_file_count': len(changed), + 'changed_files': sorted(changed), + 'replacement_counts': counts, + 'compatibility': { + 'python_symbol': 'legacy Python symbol removed; serialized legacy key remains supported', + 'serialized_inputs': 'legacy raw names normalize before metadata validation/storage', + }, + } + out.parent.mkdir(parents=True, exist_ok=True) + out.write_text(json.dumps(manifest, indent=2) + '\n', encoding='utf-8') + return changed, counts + + +def leftovers(): + found = [] + for rel in files(): + if rel in ALLOW: + continue + try: + text = (ROOT / rel).read_text(encoding='utf-8') + except UnicodeDecodeError: + continue + if OLD_SYMBOL in text or OLD_KEY in text: + found.append(str(rel)) + return found + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--write', action='store_true') + parser.add_argument('--check', action='store_true') + args = parser.parse_args() + if args.write: + changed, counts = apply() + print(f'Migrated {len(changed)} files: {counts}') + if args.check: + remain = leftovers() + if remain: + print('Legacy references remain:') + print('\n'.join(remain)) + raise SystemExit(1) + print('MAX_MACH namespace audit passed') + if not args.write and not args.check: + raise SystemExit('use --write and/or --check') + + +if __name__ == '__main__': + main()