diff --git a/aviary/docs/theory_guide/gasp_based_bwb.ipynb b/aviary/docs/theory_guide/gasp_based_bwb.ipynb index edc3b2294..cb0a284d9 100644 --- a/aviary/docs/theory_guide/gasp_based_bwb.ipynb +++ b/aviary/docs/theory_guide/gasp_based_bwb.ipynb @@ -319,18 +319,20 @@ " AeroGeom,\n", " BWBAeroSetup,\n", " BWBBodyLiftCurveSlope,\n", - " BWBFormFactorAndSIWB,\n", + " BWBSIWB,\n", " BWBLiftCoeff,\n", " BWBLiftCoeffClean,\n", " CruiseAero,\n", " LowSpeedAero,\n", " Xlifts,\n", ")\n", + "from aviary.subsystems.aerodynamics.gasp_based.gasp_aero_coeffs import BWBFormFactor\n", "from aviary.subsystems.aerodynamics.gasp_based.table_based import TabularCruiseAero\n", "from aviary.utils.doctape import get_all_non_aviary_names, get_variable_name, glue_variable\n", "\n", "glue_variable(get_variable_name(BWBBodyLiftCurveSlope), md_code=True)\n", - "glue_variable(get_variable_name(BWBFormFactorAndSIWB), md_code=True)\n", + "glue_variable(get_variable_name(BWBFormFactor), md_code=True)\n", + "glue_variable(get_variable_name(BWBSIWB), md_code=True)\n", "glue_variable(get_variable_name(BWBAeroSetup), md_code=True)\n", "glue_variable(get_variable_name(BWBLiftCoeff), md_code=True)\n", "glue_variable(get_variable_name(BWBLiftCoeffClean), md_code=True)\n", @@ -366,7 +368,8 @@ "This feature implements GASP aerodynamics subsystems for BWB aircraft. Five new components are added:\n", "\n", "- {glue:md}`BWBBodyLiftCurveSlope`\n", - "- {glue:md}`BWBFormFactorAndSIWB`\n", + "- {glue:md}`BWBFormFactor`\n", + "- {glue:md}`BWBSIWB`\n", "- {glue:md}`BWBAeroSetup`\n", "- {glue:md}`BWBLiftCoeff`\n", "- {glue:md}`BWBLiftCoeffClean`\n", diff --git a/aviary/docs/user_guide_unreviewed/subsystems/blended_wing_body_GASP.ipynb b/aviary/docs/user_guide_unreviewed/subsystems/blended_wing_body_GASP.ipynb index 5142a209f..81a141a2e 100644 --- a/aviary/docs/user_guide_unreviewed/subsystems/blended_wing_body_GASP.ipynb +++ b/aviary/docs/user_guide_unreviewed/subsystems/blended_wing_body_GASP.ipynb @@ -223,7 +223,7 @@ " BWBBodyLiftCurveSlope,\n", " AeroGeom,\n", " AeroSetup,\n", - " FormFactorAndSIWB,\n", + " SIWB,\n", " UFac,\n", " BWBBodyLiftCurveSlope,\n", ")\n", @@ -240,7 +240,7 @@ "[cl, cd] = get_all_non_aviary_names(AeroForces, include_in_out='in')\n", "glue_variable('CL', cl, md_code=True)\n", "glue_variable('CD', cd, md_code=True)\n", - "siwb = get_all_non_aviary_names(FormFactorAndSIWB)[0]\n", + "siwb = get_all_non_aviary_names(SIWB)[0]\n", "glue_variable('siwb', siwb, md_code=True)\n", "ufac = get_all_non_aviary_names(UFac, include_in_out='out')[0]\n", "glue_variable('ufac', ufac, md_code=True)\n", diff --git a/aviary/mission/two_dof/ode/params.py b/aviary/mission/two_dof/ode/params.py index 4fdd61ebb..cebf9eb0b 100644 --- a/aviary/mission/two_dof/ode/params.py +++ b/aviary/mission/two_dof/ode/params.py @@ -124,7 +124,7 @@ def promote_params(sys, trajs=None, phases=None): Aircraft.Wing.VERTICAL_MOUNT_LOCATION: dict(units='unitless', val=0), Aircraft.Design.STATIC_MARGIN: dict(units='unitless', val=0.03), Aircraft.Design.CG_DELTA: dict(units='unitless', val=0.25), - Aircraft.Fuselage.FORM_FACTOR: dict(units='unitless', val=1.25), + Aircraft.Fuselage.FORM_FACTOR: dict(units='unitless', val=1.05557953), Aircraft.Nacelle.FORM_FACTOR: dict(units='unitless', val=1.5), Aircraft.Wing.FUSELAGE_INTERFERENCE_FACTOR: dict(units='unitless', val=1.1), Aircraft.Design.DRAG_COEFFICIENT_INCREMENT: dict(units='unitless', val=0.00175), diff --git a/aviary/mission/two_dof/ode/test/test_breguet_cruise_ode.py b/aviary/mission/two_dof/ode/test/test_breguet_cruise_ode.py index f8009a974..047e535a5 100644 --- a/aviary/mission/two_dof/ode/test/test_breguet_cruise_ode.py +++ b/aviary/mission/two_dof/ode/test/test_breguet_cruise_ode.py @@ -48,6 +48,7 @@ def test_cruise(self): self.prob.set_val(Aircraft.Wing.FORM_FACTOR, 1.25) self.prob.set_val(Aircraft.VerticalTail.FORM_FACTOR, 1.25) self.prob.set_val(Aircraft.HorizontalTail.FORM_FACTOR, 1.25) + self.prob.set_val(Aircraft.Fuselage.FORM_FACTOR, 1.05557953) set_params_for_unit_tests(self.prob) diff --git a/aviary/mission/two_dof/ode/test/test_flight_ode.py b/aviary/mission/two_dof/ode/test/test_flight_ode.py index a5d76aa23..791b5c976 100644 --- a/aviary/mission/two_dof/ode/test/test_flight_ode.py +++ b/aviary/mission/two_dof/ode/test/test_flight_ode.py @@ -61,6 +61,7 @@ def test_start_of_climb(self): self.prob.set_val(Aircraft.HorizontalTail.FORM_FACTOR, 1.25) set_params_for_unit_tests(self.prob) + self.prob.set_val(Aircraft.Fuselage.FORM_FACTOR, 1.05557953) self.prob.run_model() @@ -107,6 +108,7 @@ def test_end_of_climb(self): self.prob.set_val(Aircraft.HorizontalTail.FORM_FACTOR, 1.25) set_params_for_unit_tests(self.prob) + self.prob.set_val(Aircraft.Fuselage.FORM_FACTOR, 1.05557953) self.prob.run_model() @@ -217,6 +219,7 @@ def test_low_alt(self): self.prob.set_val(Aircraft.HorizontalTail.FORM_FACTOR, 1.25) set_params_for_unit_tests(self.prob) + self.prob.set_val(Aircraft.Fuselage.FORM_FACTOR, 1.05557953) self.prob.run_model() diff --git a/aviary/mission/two_dof/ode/test/test_flight_path_ode.py b/aviary/mission/two_dof/ode/test/test_flight_path_ode.py index 53b702125..fddc91009 100644 --- a/aviary/mission/two_dof/ode/test/test_flight_path_ode.py +++ b/aviary/mission/two_dof/ode/test/test_flight_path_ode.py @@ -49,6 +49,7 @@ def test_case1(self): self.prob.set_val(Aircraft.Wing.FORM_FACTOR, 1.25) self.prob.set_val(Aircraft.VerticalTail.FORM_FACTOR, 1.25) self.prob.set_val(Aircraft.HorizontalTail.FORM_FACTOR, 1.25) + self.prob.set_val(Aircraft.Fuselage.FORM_FACTOR, 1.05557953) self.prob.run_model() testvals = { diff --git a/aviary/mission/two_dof/ode/test/test_takeoff_ode.py b/aviary/mission/two_dof/ode/test/test_takeoff_ode.py index f54c4e9d7..c108b9e4e 100644 --- a/aviary/mission/two_dof/ode/test/test_takeoff_ode.py +++ b/aviary/mission/two_dof/ode/test/test_takeoff_ode.py @@ -49,6 +49,7 @@ def test_groundroll_partials(self): self.prob.set_val(Aircraft.Wing.FORM_FACTOR, 1.25) self.prob.set_val(Aircraft.VerticalTail.FORM_FACTOR, 1.25) self.prob.set_val(Aircraft.HorizontalTail.FORM_FACTOR, 1.25) + self.prob.set_val(Aircraft.Fuselage.FORM_FACTOR, 1.05557953) self.prob.set_val(Dynamic.Mission.VELOCITY, [75, 150], units='kn') self.prob.set_val(Dynamic.Vehicle.MASS, [100000, 100000], units='lbm') @@ -105,6 +106,7 @@ def test_rotation_partials(self): self.prob.set_val(Aircraft.Wing.FORM_FACTOR, 1.25) self.prob.set_val(Aircraft.VerticalTail.FORM_FACTOR, 1.25) self.prob.set_val(Aircraft.HorizontalTail.FORM_FACTOR, 1.25) + self.prob.set_val(Aircraft.Fuselage.FORM_FACTOR, 1.05557953) set_params_for_unit_tests(self.prob) diff --git a/aviary/models/aircraft/test_aircraft/aircraft_for_bench_FwGm.csv b/aviary/models/aircraft/test_aircraft/aircraft_for_bench_FwGm.csv index 5f92590f8..b7be6c2c1 100644 --- a/aviary/models/aircraft/test_aircraft/aircraft_for_bench_FwGm.csv +++ b/aviary/models/aircraft/test_aircraft/aircraft_for_bench_FwGm.csv @@ -52,7 +52,7 @@ aircraft:fuel:wing_fuel_fraction,0.6,unitless aircraft:fuselage:aisle_width,24,inch aircraft:fuselage:delta_diameter,4.5,ft aircraft:fuselage:flat_plate_area_increment,0.25,ft**2 -aircraft:fuselage:form_factor,1.25,unitless +aircraft:fuselage:form_factor,1.05557953,unitless aircraft:fuselage:mass_coefficient,128,unitless aircraft:fuselage:nose_fineness,1,unitless aircraft:fuselage:num_aisles,1,unitless diff --git a/aviary/models/aircraft/test_aircraft/aircraft_for_bench_GwGm.csv b/aviary/models/aircraft/test_aircraft/aircraft_for_bench_GwGm.csv index 1514e3c9f..ab2684460 100644 --- a/aviary/models/aircraft/test_aircraft/aircraft_for_bench_GwGm.csv +++ b/aviary/models/aircraft/test_aircraft/aircraft_for_bench_GwGm.csv @@ -54,7 +54,6 @@ aircraft:furnishings:mass,11192,lbm aircraft:fuselage:aisle_width,24,inch aircraft:fuselage:delta_diameter,4.5,ft aircraft:fuselage:flat_plate_area_increment,0.25,ft**2 -aircraft:fuselage:form_factor,1.25,unitless aircraft:fuselage:mass_coefficient,128,unitless aircraft:fuselage:nose_fineness,1,unitless aircraft:fuselage:num_aisles,1,unitless diff --git a/aviary/subsystems/aerodynamics/aerodynamics_builder.py b/aviary/subsystems/aerodynamics/aerodynamics_builder.py index 7177dcdcb..be20b5d07 100644 --- a/aviary/subsystems/aerodynamics/aerodynamics_builder.py +++ b/aviary/subsystems/aerodynamics/aerodynamics_builder.py @@ -786,6 +786,7 @@ def report(self, prob, reports_folder, **kwargs): Aircraft.Fuselage.FLAT_PLATE_AREA_INCREMENT, Aircraft.Fuselage.LENGTH, Aircraft.Fuselage.WETTED_AREA, + Aircraft.Fuselage.FORM_FACTOR, Aircraft.HorizontalTail.AREA, Aircraft.HorizontalTail.AVERAGE_CHORD, Aircraft.HorizontalTail.FORM_FACTOR, diff --git a/aviary/subsystems/aerodynamics/gasp_based/gasp_aero_coeffs.py b/aviary/subsystems/aerodynamics/gasp_based/gasp_aero_coeffs.py index ecfc55445..7d6532afb 100644 --- a/aviary/subsystems/aerodynamics/gasp_based/gasp_aero_coeffs.py +++ b/aviary/subsystems/aerodynamics/gasp_based/gasp_aero_coeffs.py @@ -1,8 +1,10 @@ +import warnings import numpy as np import openmdao.api as om -from aviary.variable_info.functions import add_aviary_input, add_aviary_output -from aviary.variable_info.variables import Aircraft, Mission +from aviary.variable_info.enums import Verbosity +from aviary.variable_info.functions import add_aviary_input, add_aviary_option, add_aviary_output +from aviary.variable_info.variables import Aircraft, Settings SWETFCT = 1.02 @@ -181,3 +183,121 @@ def compute_partials(self, inputs, J): J[Aircraft.Nacelle.FORM_FACTOR, Aircraft.Nacelle.AVG_DIAMETER] = 1.5 * 0.35 / xln J[Aircraft.Nacelle.FORM_FACTOR, Aircraft.Nacelle.AVG_LENGTH] = -1.5 * 0.35 * dbarn / xln**2 + + +class FormFactor(om.ExplicitComponent): + """ + Compute body form factor + Incompressible form factor for streamlined bodies. From Hoerner's "Fluid Dynamic Drag", p. 6-17. + """ + + def initialize(self): + add_aviary_option(self, Settings.VERBOSITY) + + def setup(self): + add_aviary_input(self, Aircraft.Fuselage.AVG_DIAMETER, units='ft', desc='SWF') + add_aviary_input(self, Aircraft.Fuselage.LENGTH, units='ft', desc='ELF') + + add_aviary_output( + self, + Aircraft.Fuselage.FORM_FACTOR, + units='unitless', + desc='FFFUS: fuselage form factor', + ) + + def setup_partials(self): + self.declare_partials( + Aircraft.Fuselage.FORM_FACTOR, + [ + Aircraft.Fuselage.AVG_DIAMETER, + Aircraft.Fuselage.LENGTH, + ], + ) + + def compute(self, inputs, outputs): + verbosity = self.options[Settings.VERBOSITY] + + fus_len = inputs[Aircraft.Fuselage.LENGTH] + cabin_width = inputs[Aircraft.Fuselage.AVG_DIAMETER] + + if fus_len == 0.0: + if verbosity > Verbosity.BRIEF: + warnings.warn('Aircraft.Fuselage.LENGTH should not be 0.0 in FormFactor.') + + # fuselage form drag factor + fffus = 1 + 1.5 * (cabin_width / fus_len) ** 1.5 + 7 * (cabin_width / fus_len) ** 3 + outputs[Aircraft.Fuselage.FORM_FACTOR] = fffus + + def compute_partials(self, inputs, J): + fus_len = inputs[Aircraft.Fuselage.LENGTH] + cabin_width = inputs[Aircraft.Fuselage.AVG_DIAMETER] + + dfffus_dcabin_width = ( + 2.25 * (cabin_width / fus_len) ** 0.5 / fus_len + + 21 * (cabin_width / fus_len) ** 2.0 / fus_len + ) + dfffus_dfus_len = ( + -2.25 * (cabin_width / fus_len) ** 0.5 * cabin_width / fus_len**2.0 + - 21.0 * (cabin_width / fus_len) ** 2.0 * cabin_width / fus_len**2.0 + ) + + J[Aircraft.Fuselage.FORM_FACTOR, Aircraft.Fuselage.AVG_DIAMETER] = dfffus_dcabin_width + J[Aircraft.Fuselage.FORM_FACTOR, Aircraft.Fuselage.LENGTH] = dfffus_dfus_len + + +class BWBFormFactor(om.ExplicitComponent): + """ + Compute body form factor and SIWB for BWB aircraft + Incompressible form factor for streamlined bodies. From Hoerner's "Fluid Dynamic Drag", p. 6-17. + """ + + def initialize(self): + add_aviary_option(self, Settings.VERBOSITY) + + def setup(self): + add_aviary_input(self, Aircraft.Fuselage.HYDRAULIC_DIAMETER, units='ft', desc='DHYDRAL') + add_aviary_input(self, Aircraft.Fuselage.LENGTH, units='ft', desc='ELF') + + self.add_output( + Aircraft.Fuselage.FORM_FACTOR, + units='unitless', + desc='FFFUS: fuselage form factor', + ) + + def setup_partials(self): + self.declare_partials( + Aircraft.Fuselage.FORM_FACTOR, + [ + Aircraft.Fuselage.HYDRAULIC_DIAMETER, + Aircraft.Fuselage.LENGTH, + ], + ) + + def compute(self, inputs, outputs): + verbosity = self.options[Settings.VERBOSITY] + + fus_len = inputs[Aircraft.Fuselage.LENGTH] + diam = inputs[Aircraft.Fuselage.HYDRAULIC_DIAMETER] + + if fus_len == 0.0: + if verbosity > Verbosity.BRIEF: + warnings.warn('Aircraft.Fuselage.LENGTH should not be 0.0 in BWBFormFactor.') + + # fuselage form drag factor + fffus = 1 + 1.5 * (diam / fus_len) ** 1.5 + 7 * (diam / fus_len) ** 3 + outputs[Aircraft.Fuselage.FORM_FACTOR] = fffus + + def compute_partials(self, inputs, J): + fus_len = inputs[Aircraft.Fuselage.LENGTH] + diam = inputs[Aircraft.Fuselage.HYDRAULIC_DIAMETER] + + dfffus_ddiam = ( + 2.25 * (diam / fus_len) ** 0.5 / fus_len + 21 * (diam / fus_len) ** 2.0 / fus_len + ) + dfffus_dfus_len = ( + -2.25 * (diam / fus_len) ** 0.5 * diam / fus_len**2.0 + - 21.0 * (diam / fus_len) ** 2.0 * diam / fus_len**2.0 + ) + + J[Aircraft.Fuselage.FORM_FACTOR, Aircraft.Fuselage.HYDRAULIC_DIAMETER] = dfffus_ddiam + J[Aircraft.Fuselage.FORM_FACTOR, Aircraft.Fuselage.LENGTH] = dfffus_dfus_len diff --git a/aviary/subsystems/aerodynamics/gasp_based/gaspaero.py b/aviary/subsystems/aerodynamics/gasp_based/gaspaero.py index 5c03c381a..4c707d335 100644 --- a/aviary/subsystems/aerodynamics/gasp_based/gaspaero.py +++ b/aviary/subsystems/aerodynamics/gasp_based/gaspaero.py @@ -8,7 +8,7 @@ from aviary.utils.math import sigmoidX, smooth_min, d_smooth_min from aviary.variable_info.enums import AircraftTypes, Verbosity from aviary.variable_info.functions import add_aviary_input, add_aviary_option, add_aviary_output -from aviary.variable_info.variables import Aircraft, Dynamic, Mission, Settings +from aviary.variable_info.variables import Aircraft, Dynamic, Settings # # data from EAERO @@ -410,10 +410,9 @@ def compute(self, inputs, outputs): outputs['lift_ratio'] = lift_ratio -class FormFactorAndSIWB(om.ExplicitComponent): +class SIWB(om.ExplicitComponent): """ - Compute body form factor and SIWB for tube+wing aircraft - Incompressible form factor for streamlined bodies. From Hoerner's "Fluid Dynamic Drag", p. 6-17. + Compute SIWB for tube+wing aircraft """ def initialize(self): @@ -421,15 +420,8 @@ def initialize(self): def setup(self): add_aviary_input(self, Aircraft.Fuselage.AVG_DIAMETER, units='ft', desc='SWF') - add_aviary_input(self, Aircraft.Fuselage.LENGTH, units='ft', desc='ELF') add_aviary_input(self, Aircraft.Wing.SPAN, units='ft', desc='B') - add_aviary_output( - self, - Aircraft.Fuselage.FORM_FACTOR, - units='unitless', - desc='FFFUS: fuselage form factor', - ) self.add_output( 'siwb', units='unitless', @@ -437,13 +429,6 @@ def setup(self): ) def setup_partials(self): - self.declare_partials( - Aircraft.Fuselage.FORM_FACTOR, - [ - Aircraft.Fuselage.AVG_DIAMETER, - Aircraft.Fuselage.LENGTH, - ], - ) self.declare_partials( 'siwb', [ @@ -455,20 +440,12 @@ def setup_partials(self): def compute(self, inputs, outputs): verbosity = self.options[Settings.VERBOSITY] - fus_len = inputs[Aircraft.Fuselage.LENGTH] cabin_width = inputs[Aircraft.Fuselage.AVG_DIAMETER] wingspan = inputs[Aircraft.Wing.SPAN] - if fus_len == 0.0: - if verbosity > Verbosity.BRIEF: - warnings.warn('Aircraft.Fuselage.LENGTH should not be 0.0 in FormFactorAndSIWB.') if wingspan == 0.0: if verbosity > Verbosity.BRIEF: - warnings.warn('Aircraft.Wing.SPAN should not be 0.0 in FormFactorAndSIWB.') - - # fuselage form drag factor - fffus = 1 + 1.5 * (cabin_width / fus_len) ** 1.5 + 7 * (cabin_width / fus_len) ** 3 - outputs[Aircraft.Fuselage.FORM_FACTOR] = fffus + warnings.warn('Aircraft.Wing.SPAN should not be 0.0 in SIWB.') # fuselage width over wing span wfob = cabin_width / wingspan @@ -476,19 +453,9 @@ def compute(self, inputs, outputs): outputs['siwb'] = siwb def compute_partials(self, inputs, J): - fus_len = inputs[Aircraft.Fuselage.LENGTH] cabin_width = inputs[Aircraft.Fuselage.AVG_DIAMETER] wingspan = inputs[Aircraft.Wing.SPAN] - dfffus_dcabin_width = ( - 2.25 * (cabin_width / fus_len) ** 0.5 / fus_len - + 21 * (cabin_width / fus_len) ** 2.0 / fus_len - ) - dfffus_dfus_len = ( - -2.25 * (cabin_width / fus_len) ** 0.5 * cabin_width / fus_len**2.0 - - 21.0 * (cabin_width / fus_len) ** 2.0 * cabin_width / fus_len**2.0 - ) - wfob = cabin_width / wingspan # siwb = 1 - 0.0088 * wfob - 1.7364 * wfob**2 - 2.303 * wfob**3 + 6.0606 * wfob**4 @@ -506,13 +473,11 @@ def compute_partials(self, inputs, J): - 4 * 6.0606 * wfob**3 * cabin_width / wingspan**2 ) - J[Aircraft.Fuselage.FORM_FACTOR, Aircraft.Fuselage.AVG_DIAMETER] = dfffus_dcabin_width - J[Aircraft.Fuselage.FORM_FACTOR, Aircraft.Fuselage.LENGTH] = dfffus_dfus_len J['siwb', Aircraft.Fuselage.AVG_DIAMETER] = dsiwb_dcabin_width J['siwb', Aircraft.Wing.SPAN] = dsiwb_dwingspan -class BWBFormFactorAndSIWB(om.ExplicitComponent): +class BWBSIWB(om.ExplicitComponent): """ Compute body form factor and SIWB for BWB aircraft Incompressible form factor for streamlined bodies. From Hoerner's "Fluid Dynamic Drag", p. 6-17. @@ -523,14 +488,8 @@ def initialize(self): def setup(self): add_aviary_input(self, Aircraft.Fuselage.HYDRAULIC_DIAMETER, units='ft', desc='DHYDRAL') - add_aviary_input(self, Aircraft.Fuselage.LENGTH, units='ft', desc='ELF') add_aviary_input(self, Aircraft.Wing.SPAN, units='ft', desc='B') - self.add_output( - Aircraft.Fuselage.FORM_FACTOR, - units='unitless', - desc='FFFUS: fuselage form factor', - ) self.add_output( 'siwb', units='unitless', @@ -538,13 +497,6 @@ def setup(self): ) def setup_partials(self): - self.declare_partials( - Aircraft.Fuselage.FORM_FACTOR, - [ - Aircraft.Fuselage.HYDRAULIC_DIAMETER, - Aircraft.Fuselage.LENGTH, - ], - ) self.declare_partials( 'siwb', [ @@ -556,20 +508,12 @@ def setup_partials(self): def compute(self, inputs, outputs): verbosity = self.options[Settings.VERBOSITY] - fus_len = inputs[Aircraft.Fuselage.LENGTH] diam = inputs[Aircraft.Fuselage.HYDRAULIC_DIAMETER] wingspan = inputs[Aircraft.Wing.SPAN] - if fus_len == 0.0: - if verbosity > Verbosity.BRIEF: - warnings.warn('Aircraft.Fuselage.LENGTH should not be 0.0 in FormFactorAndSIWB.') if wingspan == 0.0: if verbosity > Verbosity.BRIEF: - warnings.warn('Aircraft.Wing.SPAN should not be 0.0 in FormFactorAndSIWB.') - - # fuselage form drag factor - fffus = 1 + 1.5 * (diam / fus_len) ** 1.5 + 7 * (diam / fus_len) ** 3 - outputs[Aircraft.Fuselage.FORM_FACTOR] = fffus + warnings.warn('Aircraft.Wing.SPAN should not be 0.0 in BWBSIWB.') # hydraulic diameter over wing span wfob = diam / wingspan @@ -577,18 +521,9 @@ def compute(self, inputs, outputs): outputs['siwb'] = siwb def compute_partials(self, inputs, J): - fus_len = inputs[Aircraft.Fuselage.LENGTH] diam = inputs[Aircraft.Fuselage.HYDRAULIC_DIAMETER] wingspan = inputs[Aircraft.Wing.SPAN] - dfffus_ddiam = ( - 2.25 * (diam / fus_len) ** 0.5 / fus_len + 21 * (diam / fus_len) ** 2.0 / fus_len - ) - dfffus_dfus_len = ( - -2.25 * (diam / fus_len) ** 0.5 * diam / fus_len**2.0 - - 21.0 * (diam / fus_len) ** 2.0 * diam / fus_len**2.0 - ) - wfob = diam / wingspan # siwb = 1 - 0.0088 * wfob - 1.7364 * wfob**2 - 2.303 * wfob**3 + 6.0606 * wfob**4 @@ -606,8 +541,6 @@ def compute_partials(self, inputs, J): - 4 * 6.0606 * wfob**3 * diam / wingspan**2 ) - J[Aircraft.Fuselage.FORM_FACTOR, Aircraft.Fuselage.HYDRAULIC_DIAMETER] = dfffus_ddiam - J[Aircraft.Fuselage.FORM_FACTOR, Aircraft.Fuselage.LENGTH] = dfffus_dfus_len J['siwb', Aircraft.Fuselage.HYDRAULIC_DIAMETER] = dsiwb_ddiam J['siwb', Aircraft.Wing.SPAN] = dsiwb_dwingspan @@ -1162,7 +1095,8 @@ def setup(self): ], ) - self.add_subsystem('form_factor', FormFactorAndSIWB(), promotes=['*']) + # self.add_subsystem('form_factor', FormFactor(), promotes=['*']) + self.add_subsystem('siwbcomp', SIWB(), promotes=['*']) self.add_subsystem('geom', AeroGeom(num_nodes=nn), promotes=['*']) @@ -1221,7 +1155,8 @@ def setup(self): ], ) - self.add_subsystem('form_factor', BWBFormFactorAndSIWB(), promotes=['*']) + # self.add_subsystem('form_factor', BWBFormFactor(), promotes=['*']) + self.add_subsystem('siwbcomp', BWBSIWB(), promotes=['*']) self.add_subsystem('geom', AeroGeom(num_nodes=nn), promotes=['*']) diff --git a/aviary/subsystems/aerodynamics/gasp_based/premission_aero.py b/aviary/subsystems/aerodynamics/gasp_based/premission_aero.py index 2023e6407..bb5ca45f0 100644 --- a/aviary/subsystems/aerodynamics/gasp_based/premission_aero.py +++ b/aviary/subsystems/aerodynamics/gasp_based/premission_aero.py @@ -10,12 +10,17 @@ from aviary.subsystems.aerodynamics.gasp_based.flaps_model.basic_calculations import ( BasicFlapsGeometry, ) -from aviary.subsystems.aerodynamics.gasp_based.gasp_aero_coeffs import AeroFormfactors +from aviary.subsystems.aerodynamics.gasp_based.gasp_aero_coeffs import ( + AeroFormfactors, + FormFactor, + BWBFormFactor, +) from aviary.subsystems.aerodynamics.gasp_based.interference import ( WingFuselageInterferencePremission, ) from aviary.subsystems.atmosphere.atmosphere import Atmosphere -from aviary.variable_info.enums import SpeedType +from aviary.variable_info.enums import AircraftTypes, SpeedType +from aviary.variable_info.functions import add_aviary_option from aviary.variable_info.variables import Aircraft, Dynamic, Mission # TODO: add subsystems to compute CLMXFU, CLMXTO, CLMXLD using dynamic aero components @@ -25,6 +30,9 @@ class PreMissionAero(om.Group): """Takeoff and landing flaps modeling.""" + def initialize(self): + add_aviary_option(self, Aircraft.Design.TYPE) + def setup(self): self.add_subsystem( 'wing_fus_interference_premission', @@ -43,6 +51,22 @@ def setup(self): promotes_outputs=['*'], ) + design_type = self.options[Aircraft.Design.TYPE] + if design_type is AircraftTypes.TRANSPORT: + self.add_subsystem( + 'aero_form_factors2', + FormFactor(), + promotes_inputs=['*'], + promotes_outputs=['*'], + ) + else: + self.add_subsystem( + 'aero_form_factors2', + BWBFormFactor(), + promotes_inputs=['*'], + promotes_outputs=['*'], + ) + # speeds weren't originally computed here, speedtype of Mach is intended # to avoid multiple sources for computed Mach (gets calculated somewhere upstream) self.add_subsystem( diff --git a/aviary/subsystems/aerodynamics/gasp_based/test/test_gasp_aero_coeffs.py b/aviary/subsystems/aerodynamics/gasp_based/test/test_gasp_aero_coeffs.py index 3df99be87..84e7bb5b9 100644 --- a/aviary/subsystems/aerodynamics/gasp_based/test/test_gasp_aero_coeffs.py +++ b/aviary/subsystems/aerodynamics/gasp_based/test/test_gasp_aero_coeffs.py @@ -3,8 +3,12 @@ import openmdao.api as om from openmdao.utils.assert_utils import assert_check_partials, assert_near_equal -from aviary.subsystems.aerodynamics.gasp_based.gasp_aero_coeffs import AeroFormfactors -from aviary.variable_info.variables import Aircraft, Mission +from aviary.subsystems.aerodynamics.gasp_based.gasp_aero_coeffs import ( + AeroFormfactors, + FormFactor, + BWBFormFactor, +) +from aviary.variable_info.variables import Aircraft tol = 1e-8 @@ -40,5 +44,55 @@ def test_aero_coeffs(self): assert_check_partials(partial_data, atol=1e-12, rtol=1e-14) +class FormFactorTest(unittest.TestCase): + """Test fuselage form factor computation and SIWB computation""" + + def test_case1(self): + prob = om.Problem() + + prob.model.add_subsystem( + 'formfactor', + FormFactor(), + promotes=['*'], + ) + + prob.model.set_input_defaults(Aircraft.Fuselage.AVG_DIAMETER, val=19.365, units='ft') + prob.model.set_input_defaults(Aircraft.Fuselage.LENGTH, val=71.5245514, units='ft') + + prob.setup(check=False, force_alloc_complex=True) + prob.run_model() + + tol = 1e-5 + assert_near_equal(prob[Aircraft.Fuselage.FORM_FACTOR], 1.35024726, tol) + + partial_data = prob.check_partials(out_stream=None, method='cs') + assert_check_partials(partial_data, atol=1e-11, rtol=1e-11) + + +class BWBFormFactorTest(unittest.TestCase): + """Test fuselage form factor computation and SIWB computation""" + + def test_case1(self): + prob = om.Problem() + + prob.model.add_subsystem( + 'formfactor', + BWBFormFactor(), + promotes=['*'], + ) + + prob.model.set_input_defaults(Aircraft.Fuselage.HYDRAULIC_DIAMETER, val=19.365, units='ft') + prob.model.set_input_defaults(Aircraft.Fuselage.LENGTH, val=71.5245514, units='ft') + + prob.setup(check=False, force_alloc_complex=True) + prob.run_model() + + tol = 1e-5 + assert_near_equal(prob[Aircraft.Fuselage.FORM_FACTOR], 1.35024726, tol) + + partial_data = prob.check_partials(out_stream=None, method='cs') + assert_check_partials(partial_data, atol=1e-11, rtol=1e-11) + + if __name__ == '__main__': unittest.main() diff --git a/aviary/subsystems/aerodynamics/gasp_based/test/test_gaspaero.py b/aviary/subsystems/aerodynamics/gasp_based/test/test_gaspaero.py index 5efdad72a..57d0ce649 100644 --- a/aviary/subsystems/aerodynamics/gasp_based/test/test_gaspaero.py +++ b/aviary/subsystems/aerodynamics/gasp_based/test/test_gaspaero.py @@ -6,26 +6,28 @@ import openmdao.api as om import pandas as pd from openmdao.utils.assert_utils import assert_check_partials, assert_near_equal +from openmdao.utils.testing_utils import use_tempdirs from aviary.subsystems.aerodynamics.gasp_based.gaspaero import ( AeroGeom, CruiseAero, DragCoef, DragCoefClean, - FormFactorAndSIWB, GroundEffect, LiftCoeff, LiftCoeffClean, LowSpeedAero, + SIWB, UFac, Xlifts, WingTailRatios, BWBBodyLiftCurveSlope, - BWBFormFactorAndSIWB, BWBLiftCoeff, BWBLiftCoeffClean, BWBAeroSetup, + BWBSIWB, ) +from aviary.subsystems.aerodynamics.gasp_based.gasp_aero_coeffs import FormFactor, BWBFormFactor from aviary.utils.aviary_values import AviaryValues from aviary.variable_info.functions import setup_model_options from aviary.variable_info.options import get_option_defaults @@ -38,6 +40,7 @@ setup_data = json.load(file) +@use_tempdirs class GASPAeroTest(unittest.TestCase): """ Test overall pre-mission and mission aero systems in cruise and near-ground flight. @@ -66,6 +69,8 @@ def test_cruise(self): # extra params needed for cruise aero prob.set_val(Aircraft.Design.LIFT_COEFFICIENT_MAX_FLAPS_UP, setup_data['clmwfu']) prob.set_val(Aircraft.Design.DRAG_DIVERGENCE_SHIFT, setup_data['scfac']) + # FormFactor + prob.set_val(Aircraft.Fuselage.FORM_FACTOR, 1.05557953) for i, row in cruise_data.iterrows(): alt = row['alt'] @@ -111,6 +116,8 @@ def test_ground(self): prob.set_val('airport_alt', 0.0) # not defined in standalone aero prob.set_val(Aircraft.Wing.FLAP_CHORD_RATIO, setup_data['cfoc']) prob.set_val(Aircraft.Design.GROSS_MASS, setup_data['wgto']) + # FormFactor + prob.set_val(Aircraft.Fuselage.FORM_FACTOR, 1.05557953) for i, row in ground_data.iterrows(): ilift = row['ilift'] # 2: takeoff, 3: landing @@ -186,6 +193,8 @@ def test_ground_alpha_out(self): setup_model_options(prob, AviaryValues({Aircraft.Engine.NUM_ENGINES: ([2], 'unitless')})) prob.setup(check=False, force_alloc_complex=True) + # FormFactor + prob.set_val(Aircraft.Fuselage.FORM_FACTOR, 1.05557953) _init_geom(prob) @@ -537,54 +546,50 @@ def test_case3(self): assert_check_partials(partial_data, atol=1e-11, rtol=1e-11) -class FormFactorAndSIWBTest(unittest.TestCase): +class SIWBTest(unittest.TestCase): """Test fuselage form factor computation and SIWB computation""" def test_case1(self): prob = om.Problem() prob.model.add_subsystem( - 'form_factor', - FormFactorAndSIWB(), + 'siwb_comp', + SIWB(), promotes=['*'], ) prob.model.set_input_defaults(Aircraft.Fuselage.AVG_DIAMETER, val=19.365, units='ft') - prob.model.set_input_defaults(Aircraft.Fuselage.LENGTH, val=71.5245514, units='ft') prob.model.set_input_defaults(Aircraft.Wing.SPAN, val=146.38501, units='ft') prob.setup(check=False, force_alloc_complex=True) prob.run_model() tol = 1e-5 - assert_near_equal(prob[Aircraft.Fuselage.FORM_FACTOR], 1.35024726, tol) assert_near_equal(prob['siwb'], 0.964972794, tol) partial_data = prob.check_partials(out_stream=None, method='cs') assert_check_partials(partial_data, atol=1e-11, rtol=1e-11) -class BWBFormFactorAndSIWBTest(unittest.TestCase): +class BWBSIWBTest(unittest.TestCase): """Test fuselage form factor computation and SIWB computation""" def test_case1(self): prob = om.Problem() prob.model.add_subsystem( - 'form_factor', - BWBFormFactorAndSIWB(), + 'siwb_comp', + BWBSIWB(), promotes=['*'], ) prob.model.set_input_defaults(Aircraft.Fuselage.HYDRAULIC_DIAMETER, val=19.365, units='ft') - prob.model.set_input_defaults(Aircraft.Fuselage.LENGTH, val=71.5245514, units='ft') prob.model.set_input_defaults(Aircraft.Wing.SPAN, val=146.38501, units='ft') prob.setup(check=False, force_alloc_complex=True) prob.run_model() tol = 1e-5 - assert_near_equal(prob[Aircraft.Fuselage.FORM_FACTOR], 1.35024726, tol) assert_near_equal(prob['siwb'], 0.964972794, tol) partial_data = prob.check_partials(out_stream=None, method='cs') @@ -707,6 +712,7 @@ def test_case1(self): assert_near_equal(prob['SA7'], [0.03978045, 0.03978045], tol) +@use_tempdirs class BWBAeroSetupTest(unittest.TestCase): def test_case1(self): options = get_option_defaults() @@ -750,7 +756,7 @@ def test_case1(self): Aircraft.HorizontalTail.MOMENT_RATIO, 0.5463, units='unitless' ) - # BWBFormFactorAndSIWB + # BWBFormFactor prob.model.set_input_defaults(Aircraft.Fuselage.HYDRAULIC_DIAMETER, 19.3650932, units='ft') prob.model.set_input_defaults(Aircraft.Fuselage.LENGTH, 71.5245514, units='ft') @@ -761,6 +767,9 @@ def test_case1(self): prob.model.set_input_defaults( Dynamic.Atmosphere.KINEMATIC_VISCOSITY, [0.00034882, 0.00034882], units='ft**2/s' ) + # FormFactor + prob.model.set_input_defaults(Aircraft.Fuselage.FORM_FACTOR, 1.35024721, units='unitless') + prob.model.set_input_defaults(Aircraft.Wing.FORM_FACTOR, 2.563, units='unitless') prob.model.set_input_defaults(Aircraft.Nacelle.FORM_FACTOR, 1.2, units='unitless') prob.model.set_input_defaults(Aircraft.VerticalTail.FORM_FACTOR, 2.361, units='unitless') @@ -817,7 +826,6 @@ def test_case1(self): assert_near_equal(prob['SA6'], [2.09276756, 2.09276756], tol) assert_near_equal(prob['SA7'], [0.03978045, 0.03978045], tol) - assert_near_equal(prob[Aircraft.Fuselage.FORM_FACTOR], 1.35024721, tol) assert_near_equal(prob['siwb'], 0.96497277, tol) @@ -1204,6 +1212,7 @@ def test_case2(self): assert_near_equal(prob['CD'], [0.01465816, 0.0156808], tol) +@use_tempdirs class BWBCruiseAeroTest(unittest.TestCase): def setUp(self): self.options = options = get_option_defaults() @@ -1242,7 +1251,7 @@ def setUp(self): Aircraft.HorizontalTail.MOMENT_RATIO, 0.5463, units='unitless' ) - # BWBAeroSetup/BWBFormFactorAndSIWB + # BWBAeroSetup/BWBFormFactor prob.model.set_input_defaults(Aircraft.Fuselage.HYDRAULIC_DIAMETER, 19.3650932, units='ft') prob.model.set_input_defaults(Aircraft.Fuselage.LENGTH, 71.5245514, units='ft') @@ -1336,6 +1345,8 @@ def test_case1(self): prob.model.set_input_defaults( Dynamic.Vehicle.ANGLE_OF_ATTACK, [3.611767, 3.611767], units='deg' ) + # BWBFormFactor + prob.model.set_input_defaults(Aircraft.Fuselage.FORM_FACTOR, 1.35024721, units='unitless') setup_model_options(prob, options) @@ -1370,6 +1381,8 @@ def test_case2(self): # CLFromLift prob.model.set_input_defaults('lift_req', [817.74, 817.74], units='lbf') + # BWBFormFactor + prob.model.set_input_defaults(Aircraft.Fuselage.FORM_FACTOR, 1.35024721, units='unitless') setup_model_options(prob, options) @@ -1389,6 +1402,7 @@ def test_case2(self): assert_near_equal(prob[Dynamic.Vehicle.DRAG], [41.8535328, 41.8535328], tol) +@use_tempdirs class BWBLowSpeedAeroTest1(unittest.TestCase): def setUp(self): self.options = options = get_option_defaults() @@ -1427,7 +1441,7 @@ def setUp(self): Aircraft.HorizontalTail.MOMENT_RATIO, 0.5463, units='unitless' ) - # BWBAeroSetup/BWBFormFactorAndSIWB + # BWBAeroSetup/BWBFormFactor prob.model.set_input_defaults(Aircraft.Fuselage.HYDRAULIC_DIAMETER, 19.3650932, units='ft') prob.model.set_input_defaults(Aircraft.Fuselage.LENGTH, 71.5245514, units='ft') @@ -1505,6 +1519,9 @@ def test_case1(self): promotes=['*'], ) + # BWBFormFactor + prob.model.set_input_defaults(Aircraft.Fuselage.FORM_FACTOR, 1.35024721, units='unitless') + setup_model_options(prob, options) prob.setup(check=False, force_alloc_complex=True) @@ -1528,6 +1545,7 @@ def test_case1(self): assert_near_equal(prob[Dynamic.Vehicle.DRAG], [37.73329763, 37.73329763], tol) +@use_tempdirs class BWBLowSpeedAeroTest2(unittest.TestCase): def setUp(self): self.options = options = get_option_defaults() @@ -1567,7 +1585,7 @@ def setUp(self): Aircraft.HorizontalTail.MOMENT_RATIO, 0.5463, units='unitless' ) - # BWBAeroSetup/BWBFormFactorAndSIWB + # BWBAeroSetup/BWBFormFactor prob.model.set_input_defaults(Aircraft.Fuselage.HYDRAULIC_DIAMETER, 19.3650932, units='ft') prob.model.set_input_defaults(Aircraft.Fuselage.LENGTH, 71.5245514, units='ft') @@ -1631,6 +1649,8 @@ def setUp(self): # AeroForces prob.model.set_input_defaults(Dynamic.Atmosphere.DYNAMIC_PRESSURE, [1.0, 1.0], units='psf') + # BWBFormFactor + prob.model.set_input_defaults(Aircraft.Fuselage.FORM_FACTOR, 1.35024721, units='unitless') options = self.options setup_model_options(prob, options) @@ -1677,6 +1697,7 @@ def test_case1(self): assert_near_equal(CL_over_CD, [CL_Over_CDs[i], CL_Over_CDs[i]], tol) +@use_tempdirs class BWBLowSpeedAeroTest3(unittest.TestCase): def setUp(self): self.options = options = get_option_defaults() @@ -1716,7 +1737,7 @@ def setUp(self): Aircraft.HorizontalTail.MOMENT_RATIO, 0.5463, units='unitless' ) - # BWBAeroSetup/BWBFormFactorAndSIWB + # BWBAeroSetup/BWBFormFactor prob.model.set_input_defaults(Aircraft.Fuselage.HYDRAULIC_DIAMETER, 19.3650932, units='ft') prob.model.set_input_defaults(Aircraft.Fuselage.LENGTH, 71.5245514, units='ft') @@ -1780,6 +1801,8 @@ def setUp(self): # AeroForces prob.model.set_input_defaults(Dynamic.Atmosphere.DYNAMIC_PRESSURE, [1.0, 1.0], units='psf') + # bwbFormFactor + prob.model.set_input_defaults(Aircraft.Fuselage.FORM_FACTOR, 1.35024721, units='unitless') options = self.options setup_model_options(prob, options) diff --git a/aviary/validation_cases/benchmark_tests/test_problem_types_GwGm.py b/aviary/validation_cases/benchmark_tests/test_problem_types_GwGm.py index 00df256fa..ee791fbd2 100644 --- a/aviary/validation_cases/benchmark_tests/test_problem_types_GwGm.py +++ b/aviary/validation_cases/benchmark_tests/test_problem_types_GwGm.py @@ -39,6 +39,9 @@ def test_off_design_IPOPT(self): 'aircraft:design:gross_mass', self.sized_mass, units='lbm' ) prob_fallout.aviary_inputs.set_val('mission:gross_mass', self.sized_mass, units='lbm') + prob_fallout.aviary_inputs.set_val( + 'aircraft:fuselage:form_factor', 1.05557953, units='unitless' + ) prob_fallout.check_and_preprocess_inputs()