"""
Python module for fuselage weight calculation, part of the airframe mass computation.
"""
# This file is part of FAST-OAD_CS23 : A framework for rapid Overall Aircraft Design
# Copyright (C) 2025 ONERA & ISAE-SUPAERO
# FAST is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import logging
import fastoad.api as oad
import numpy as np
import openmdao.api as om
from stdatm import AtmosphereWithPartials
from .constants import (
SERVICE_FUSELAGE_MASS,
SUBMODEL_FUSELAGE_MASS_LEGACY,
SUBMODEL_FUSELAGE_MASS_RAYMER,
SUBMODEL_FUSELAGE_MASS_ROSKAM,
)
_LOGGER = logging.getLogger(__name__)
oad.RegisterSubmodel.active_models[SERVICE_FUSELAGE_MASS] = SUBMODEL_FUSELAGE_MASS_LEGACY
[docs]@oad.RegisterSubmodel(SERVICE_FUSELAGE_MASS, SUBMODEL_FUSELAGE_MASS_LEGACY)
class ComputeFuselageWeight(om.ExplicitComponent):
"""
Fuselage weight estimation
Based on a statistical analysis. See :cite:`nicolai:2010` but can also be found in
:cite:`gudmundsson:2013`.
"""
# pylint: disable=missing-function-docstring
# Overriding OpenMDAO setup
[docs] def setup(self):
self.add_input("data:mission:sizing:cs23:sizing_factor:ultimate_aircraft", val=np.nan)
self.add_input("data:weight:aircraft:MTOW", val=np.nan, units="lb")
self.add_input("data:weight:airframe:fuselage:k_factor", val=1.0)
self.add_input("data:geometry:fuselage:maximum_width", val=np.nan, units="m")
self.add_input("data:geometry:fuselage:maximum_height", val=np.nan, units="m")
self.add_input("data:geometry:fuselage:length", val=np.nan, units="m")
self.add_input("data:TLAR:v_max_sl", val=np.nan, units="kn")
self.add_output("data:weight:airframe:fuselage:mass", units="lb")
self.declare_partials("*", "*", method="exact")
# pylint: disable=missing-function-docstring, unused-argument
# Overriding OpenMDAO compute, not all arguments are used
[docs] def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None):
sizing_factor_ultimate = inputs["data:mission:sizing:cs23:sizing_factor:ultimate_aircraft"]
mtow = inputs["data:weight:aircraft:MTOW"]
maximum_width = inputs["data:geometry:fuselage:maximum_width"]
maximum_height = inputs["data:geometry:fuselage:maximum_height"]
fus_length = inputs["data:geometry:fuselage:length"]
v_max_sl = inputs["data:TLAR:v_max_sl"]
a2 = (
200.0
* (
(mtow * sizing_factor_ultimate / (10.0**5.0)) ** 0.286
* (fus_length * 3.28084 / 10.0) ** 0.857
* (maximum_width + maximum_height)
* 0.328084
* (v_max_sl / 100.0) ** 0.338
)
** 1.1
) # mass formula in lb
outputs["data:weight:airframe:fuselage:mass"] = (
a2 * inputs["data:weight:airframe:fuselage:k_factor"]
)
# pylint: disable=missing-function-docstring, unused-argument
# Overriding OpenMDAO compute_partials, not all arguments are used
[docs] def compute_partials(self, inputs, partials, discrete_inputs=None):
sizing_factor_ultimate = inputs["data:mission:sizing:cs23:sizing_factor:ultimate_aircraft"]
mtow = inputs["data:weight:aircraft:MTOW"]
maximum_width = inputs["data:geometry:fuselage:maximum_width"]
maximum_height = inputs["data:geometry:fuselage:maximum_height"]
fus_length = inputs["data:geometry:fuselage:length"]
v_max_sl = inputs["data:TLAR:v_max_sl"]
k_factor = inputs["data:weight:airframe:fuselage:k_factor"]
partials[
"data:weight:airframe:fuselage:mass",
"data:mission:sizing:cs23:sizing_factor:ultimate_aircraft",
] = k_factor * (
200.0
* 0.3146
* (
(mtow / (10.0**5.0)) ** 0.286
* (fus_length * 3.28084 / 10.0) ** 0.857
* (maximum_width + maximum_height)
* 0.328084
* (v_max_sl / 100.0) ** 0.338
)
** 1.1
* sizing_factor_ultimate**-0.6854
)
partials["data:weight:airframe:fuselage:mass", "data:weight:aircraft:MTOW"] = k_factor * (
200.0
* 0.3146
* (
(sizing_factor_ultimate / (10.0**5.0)) ** 0.286
* (fus_length * 3.28084 / 10.0) ** 0.857
* (maximum_width + maximum_height)
* 0.328084
* (v_max_sl / 100.0) ** 0.338
)
** 1.1
* mtow**-0.6854
)
partials["data:weight:airframe:fuselage:mass", "data:geometry:fuselage:maximum_width"] = (
k_factor
* (
200.0
* 1.1
* (
(mtow * sizing_factor_ultimate / (10.0**5.0)) ** 0.286
* (fus_length * 3.28084 / 10.0) ** 0.857
* 0.328084
* (v_max_sl / 100.0) ** 0.338
)
** 1.1
* (maximum_width + maximum_height) ** 0.1
)
)
partials["data:weight:airframe:fuselage:mass", "data:geometry:fuselage:maximum_height"] = (
k_factor
* (
200.0
* 1.1
* (
(mtow * sizing_factor_ultimate / (10.0**5.0)) ** 0.286
* (fus_length * 3.28084 / 10.0) ** 0.857
* 0.328084
* (v_max_sl / 100.0) ** 0.338
)
** 1.1
* (maximum_width + maximum_height) ** 0.1
)
)
partials["data:weight:airframe:fuselage:mass", "data:geometry:fuselage:length"] = (
k_factor
* 200.0
* 0.9427
* (
(mtow * sizing_factor_ultimate / (10.0**5.0)) ** 0.286
* 0.328084**0.857
* (maximum_width + maximum_height)
* 0.328084
* (v_max_sl / 100.0) ** 0.338
)
** 1.1
* fus_length ** (-0.0573)
)
partials["data:weight:airframe:fuselage:mass", "data:TLAR:v_max_sl"] = k_factor * (
200.0
* 0.3718
* (
(mtow * sizing_factor_ultimate / (10.0**5.0)) ** 0.286
* (fus_length * 3.28084 / 10.0) ** 0.857
* (maximum_width + maximum_height)
* 0.328084
* 0.01**0.338
)
** 1.1
* v_max_sl**-0.6282
)
partials["data:weight:airframe:fuselage:mass", "data:weight:airframe:fuselage:k_factor"] = (
200.0
* (
(mtow * sizing_factor_ultimate / (10.0**5.0)) ** 0.286
* (fus_length * 3.28084 / 10.0) ** 0.857
* (maximum_width + maximum_height)
* 0.328084
* (v_max_sl / 100.0) ** 0.338
)
** 1.1
)
[docs]@oad.RegisterSubmodel(SERVICE_FUSELAGE_MASS, SUBMODEL_FUSELAGE_MASS_RAYMER)
class ComputeFuselageWeightRaymer(om.ExplicitComponent):
"""
Fuselage weight estimation
Based on : Raymer, Daniel. Aircraft design: a conceptual approach. American Institute of
Aeronautics and Astronautics, Inc., 2012.
Can also be found in : Gudmundsson, Snorri. General aviation aircraft design: Applied Methods
and Procedures. Butterworth-Heinemann, 2013. Equation (6-25).
"""
# pylint: disable=missing-function-docstring
# Overriding OpenMDAO setup
[docs] def setup(self):
self.add_input("data:geometry:fuselage:length", val=np.nan, units="ft")
self.add_input("data:geometry:fuselage:front_length", val=np.nan, units="ft")
self.add_input("data:geometry:fuselage:rear_length", val=np.nan, units="ft")
self.add_input("data:geometry:fuselage:maximum_width", val=np.nan, units="ft")
self.add_input("data:geometry:fuselage:maximum_height", val=np.nan, units="ft")
self.add_input("data:geometry:fuselage:wet_area", val=np.nan, units="ft**2")
self.add_input("data:mission:sizing:cs23:sizing_factor:ultimate_aircraft", val=np.nan)
self.add_input("data:weight:aircraft:MTOW", val=np.nan, units="lb")
self.add_input("data:weight:airframe:fuselage:k_factor", val=1.0)
self.add_input(
"data:geometry:horizontal_tail:MAC:at25percent:x:from_wingMAC25", val=np.nan, units="ft"
)
self.add_input("data:mission:sizing:main_route:cruise:altitude", val=np.nan, units="ft")
self.add_input("data:TLAR:v_cruise", val=np.nan, units="m/s")
self.add_output("data:weight:airframe:fuselage:mass", units="lb")
self.declare_partials(
of="*",
wrt="*",
method="exact",
)
# pylint: disable=missing-function-docstring, unused-argument
# Overriding OpenMDAO compute, not all arguments are used
[docs] def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None):
fus_length = inputs["data:geometry:fuselage:length"]
lav = inputs["data:geometry:fuselage:front_length"]
lar = inputs["data:geometry:fuselage:rear_length"]
maximum_width = inputs["data:geometry:fuselage:maximum_width"]
maximum_height = inputs["data:geometry:fuselage:maximum_height"]
wet_area_fus = inputs["data:geometry:fuselage:wet_area"]
sizing_factor_ultimate = inputs["data:mission:sizing:cs23:sizing_factor:ultimate_aircraft"]
mtow = inputs["data:weight:aircraft:MTOW"]
lp_ht = inputs["data:geometry:horizontal_tail:MAC:at25percent:x:from_wingMAC25"]
cruise_alt = inputs["data:mission:sizing:main_route:cruise:altitude"]
v_cruise = inputs["data:TLAR:v_cruise"]
atm_cruise = AtmosphereWithPartials(cruise_alt)
rho_cruise = atm_cruise.density
pressure_cruise = atm_cruise.pressure
atm_sl = AtmosphereWithPartials(0.0)
pressure_sl = atm_sl.pressure
dynamic_pressure = 1.0 / 2.0 * rho_cruise * v_cruise**2.0 * 0.020885434273039
if cruise_alt > 10000.0:
is_pressurized = 1.0
else:
is_pressurized = 0.0
# is_pressurized is an option that affects the fuselage sizing.
# It describes whether the fuselage is pressurized or not depending on the cruise altitude.
fus_dia = (maximum_height + maximum_width) / 2.0
v_press = (fus_length - lar - lav) * np.pi * (fus_dia / 2.0) ** 2.0
delta_p = (pressure_sl - pressure_cruise) * 0.000145038
a2 = 0.052 * (
wet_area_fus**1.086
* (sizing_factor_ultimate * mtow) ** 0.177
* lp_ht ** (-0.051)
* ((fus_length - lar - lav) / maximum_height) ** (-0.072)
* dynamic_pressure**0.241
+ 11.9 * (v_press * delta_p) ** 0.271 * is_pressurized
)
outputs["data:weight:airframe:fuselage:mass"] = (
a2 * inputs["data:weight:airframe:fuselage:k_factor"]
)
# pylint: disable=missing-function-docstring, unused-argument
# Overriding OpenMDAO compute_partials, not all arguments are used
[docs] def compute_partials(self, inputs, partials, discrete_inputs=None):
fus_length = inputs["data:geometry:fuselage:length"]
lav = inputs["data:geometry:fuselage:front_length"]
lar = inputs["data:geometry:fuselage:rear_length"]
maximum_width = inputs["data:geometry:fuselage:maximum_width"]
maximum_height = inputs["data:geometry:fuselage:maximum_height"]
wet_area_fus = inputs["data:geometry:fuselage:wet_area"]
sizing_factor_ultimate = inputs["data:mission:sizing:cs23:sizing_factor:ultimate_aircraft"]
mtow = inputs["data:weight:aircraft:MTOW"]
lp_ht = inputs["data:geometry:horizontal_tail:MAC:at25percent:x:from_wingMAC25"]
cruise_alt = inputs["data:mission:sizing:main_route:cruise:altitude"]
v_cruise = inputs["data:TLAR:v_cruise"]
k_factor = inputs["data:weight:airframe:fuselage:k_factor"]
atm_cruise = AtmosphereWithPartials(cruise_alt)
rho_cruise = atm_cruise.density
pressure_cruise = atm_cruise.pressure
atm_sl = AtmosphereWithPartials(0.0)
pressure_sl = atm_sl.pressure
dynamic_pressure = 1.0 / 2.0 * rho_cruise * v_cruise**2.0 * 0.020885434273039
fus_dia = (maximum_height + maximum_width) / 2.0
v_press = (fus_length - lar - lav) * np.pi * (fus_dia / 2.0) ** 2.0
delta_p = (pressure_sl - pressure_cruise) * 0.000145038
if cruise_alt > 10000.0:
is_pressurized = 1.0
else:
is_pressurized = 0.0
# is_pressurized is an option that affects the fuselage sizing.
# It describes whether the fuselage is pressurized or not depending on the cruise altitude.
partials["data:weight:airframe:fuselage:mass", "data:weight:airframe:fuselage:k_factor"] = (
0.052
* (
wet_area_fus**1.086
* (sizing_factor_ultimate * mtow) ** 0.177
* lp_ht ** (-0.051)
* ((fus_length - lar - lav) / maximum_height) ** (-0.072)
* dynamic_pressure**0.241
+ 11.9 * (v_press * delta_p) ** 0.271 * is_pressurized
)
)
partials["data:weight:airframe:fuselage:mass", "data:geometry:fuselage:length"] = (
k_factor
* (
0.052
* (
wet_area_fus**1.086
* (sizing_factor_ultimate * mtow) ** 0.177
* lp_ht ** (-0.051)
* -0.072
* (fus_length - lar - lav) ** (-1.072)
* maximum_height**0.072
* dynamic_pressure**0.241
+ 11.9
* is_pressurized
* 0.271
* (fus_length - lar - lav) ** -0.729
* (delta_p * np.pi * (fus_dia / 2.0) ** 2.0) ** 0.271
)
)
)
partials["data:weight:airframe:fuselage:mass", "data:geometry:fuselage:front_length"] = (
k_factor
* (
0.052
* (
wet_area_fus**1.086
* (sizing_factor_ultimate * mtow) ** 0.177
* lp_ht ** (-0.051)
* maximum_height**0.072
* 0.072
* (fus_length - lar - lav) ** -1.072
* dynamic_pressure**0.241
- 11.9
* is_pressurized
* 0.271
* (fus_length - lar - lav) ** (-0.729)
* (delta_p * np.pi * (fus_dia / 2.0) ** 2.0) ** 0.271
)
)
)
partials["data:weight:airframe:fuselage:mass", "data:geometry:fuselage:rear_length"] = (
k_factor
* (
0.052
* (
wet_area_fus**1.086
* (sizing_factor_ultimate * mtow) ** 0.177
* lp_ht ** (-0.051)
* maximum_height**0.072
* 0.072
* (fus_length - lar - lav) ** -1.072
* dynamic_pressure**0.241
- 11.9
* is_pressurized
* 0.271
* (fus_length - lar - lav) ** (-0.729)
* (delta_p * np.pi * (fus_dia / 2.0) ** 2.0) ** 0.271
)
)
)
partials["data:weight:airframe:fuselage:mass", "data:geometry:fuselage:maximum_width"] = (
k_factor
* 0.052
* is_pressurized
* 11.9
* 0.271
* (delta_p * np.pi * (fus_length - lar - lav)) ** 0.271
* (maximum_height + maximum_width) ** (-0.458)
* 2 ** (-0.084)
)
partials["data:weight:airframe:fuselage:mass", "data:geometry:fuselage:maximum_height"] = (
k_factor
* (
0.052
* (
wet_area_fus**1.086
* (sizing_factor_ultimate * mtow) ** 0.177
* lp_ht ** (-0.051)
* (fus_length - lar - lav) ** (-0.072)
* dynamic_pressure**0.241
* 0.072
* maximum_height**-0.928
+ is_pressurized
* 11.9
* 0.271
* (delta_p * np.pi * (fus_length - lar - lav)) ** 0.271
* (maximum_height + maximum_width) ** (-0.458)
* 2 ** (-0.084)
)
)
)
partials["data:weight:airframe:fuselage:mass", "data:geometry:fuselage:wet_area"] = (
k_factor
* 0.052
* (
1.086
* wet_area_fus**0.086
* (sizing_factor_ultimate * mtow) ** 0.177
* lp_ht ** (-0.051)
* ((fus_length - lar - lav) / maximum_height) ** (-0.072)
* dynamic_pressure**0.241
)
)
partials[
"data:weight:airframe:fuselage:mass",
"data:mission:sizing:cs23:sizing_factor:ultimate_aircraft",
] = k_factor * (
0.052
* (
wet_area_fus**1.086
* 0.177
* (sizing_factor_ultimate * mtow) ** -0.823
* mtow
* lp_ht ** (-0.051)
* ((fus_length - lar - lav) / maximum_height) ** (-0.072)
* dynamic_pressure**0.241
)
)
partials["data:weight:airframe:fuselage:mass", "data:weight:aircraft:MTOW"] = k_factor * (
0.052
* (
wet_area_fus**1.086
* 0.177
* (sizing_factor_ultimate * mtow) ** -0.823
* sizing_factor_ultimate
* lp_ht ** (-0.051)
* ((fus_length - lar - lav) / maximum_height) ** (-0.072)
* dynamic_pressure**0.241
)
)
partials[
"data:weight:airframe:fuselage:mass",
"data:geometry:horizontal_tail:MAC:at25percent:x:from_wingMAC25",
] = k_factor * (
-0.052
* (
wet_area_fus**1.086
* (sizing_factor_ultimate * mtow) ** 0.177
* 0.051
* lp_ht ** (-1.051)
* ((fus_length - lar - lav) / maximum_height) ** (-0.072)
* dynamic_pressure**0.241
)
)
partials["data:weight:airframe:fuselage:mass", "data:TLAR:v_cruise"] = (
k_factor
* 0.052
* wet_area_fus**1.086
* (sizing_factor_ultimate * mtow) ** 0.177
* lp_ht ** (-0.051)
* ((fus_length - lar - lav) / maximum_height) ** (-0.072)
* 2
* 0.241
* (0.5 * rho_cruise * 0.020885434273039) ** 0.241
* v_cruise ** (-0.518)
)
partials[
"data:weight:airframe:fuselage:mass", "data:mission:sizing:main_route:cruise:altitude"
] = k_factor * (
0.052
* (
wet_area_fus**1.086
* (sizing_factor_ultimate * mtow) ** 0.177
* lp_ht ** (-0.051)
* ((fus_length - lar - lav) / maximum_height) ** (-0.072)
* 0.241
* (0.5 * v_cruise**2.0 * 0.020885434273039) ** 0.241
* atm_cruise.partial_density_altitude
* rho_cruise ** (-0.759)
- 11.9
* is_pressurized
* 0.271
* (v_press * 0.000145038) ** 0.271
* atm_cruise.partial_pressure_altitude
* (pressure_sl - pressure_cruise) ** (-0.729)
)
)
[docs]@oad.RegisterSubmodel(SERVICE_FUSELAGE_MASS, SUBMODEL_FUSELAGE_MASS_ROSKAM)
class ComputeFuselageWeightRoskam(om.ExplicitComponent):
"""
Fuselage weight estimation, includes the computation of the fuselage weight for a high wing
aircraft.
Based on : Roskam. Airplane design - Part 5: component weight estimation
"""
# pylint: disable=missing-function-docstring
# Overriding OpenMDAO setup
[docs] def setup(self):
self.add_input("data:geometry:fuselage:length", val=np.nan, units="ft")
self.add_input("data:geometry:fuselage:front_length", val=np.nan, units="ft")
self.add_input("data:weight:aircraft:MTOW", val=np.nan, units="lb")
self.add_input("data:geometry:cabin:seats:passenger:NPAX_max", val=np.nan)
self.add_input("data:geometry:fuselage:maximum_width", val=np.nan, units="ft")
self.add_input("data:geometry:fuselage:maximum_height", val=np.nan, units="ft")
self.add_input("data:geometry:wing_configuration", val=np.nan)
self.add_output("data:weight:airframe:fuselage:mass", units="lb")
self.declare_partials(
of="*",
wrt=[
"data:geometry:fuselage:length",
"data:geometry:fuselage:front_length",
"data:weight:aircraft:MTOW",
"data:geometry:cabin:seats:passenger:NPAX_max",
"data:geometry:fuselage:maximum_width",
"data:geometry:fuselage:maximum_width",
"data:geometry:fuselage:maximum_height",
],
method="exact",
)
# pylint: disable=missing-function-docstring, unused-argument
# Overriding OpenMDAO compute, not all arguments are used
[docs] def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None):
fus_length = inputs["data:geometry:fuselage:length"]
lav = inputs["data:geometry:fuselage:front_length"]
maximum_width = inputs["data:geometry:fuselage:maximum_width"]
maximum_height = inputs["data:geometry:fuselage:maximum_height"]
mtow = inputs["data:weight:aircraft:MTOW"]
npax_max = (
inputs["data:geometry:cabin:seats:passenger:NPAX_max"] + 2.0
) # addition of 2 pilots
wing_config = inputs["data:geometry:wing_configuration"]
fus_dia = (maximum_height + maximum_width) / 2.0
p_max = 2 * np.pi * (fus_dia / 2) # maximum perimeter of the fuselage
if wing_config == 1.0:
# The formula found in Roskam originally contains a division by 100, but it leads to
# results way too low. It will be omitted here. It does not seem to cause an issue
# for the high wing configuration however, so we will simply issue a warning with a
# recommendation to switch method for low wing aircraft
a2 = 0.04682 * (mtow**0.692 * npax_max**0.374 * (fus_length - lav) ** 0.590)
_LOGGER.warning(
"This submodel is not trusted for the computation of the fuselage weight of low "
"wing aircraft as it gives very small results. Consider switching submodel"
)
elif wing_config == 3.0:
a2 = 14.86 * (
mtow**0.144
* ((fus_length - lav) / p_max) ** 0.778
* (fus_length - lav) ** 0.383
* npax_max**0.455
)
else:
_LOGGER.info(
"No formula available for the weight of the fuselage with a mid-wing "
"configuration, taking high wing instead"
)
a2 = 14.86 * (
mtow**0.144
* ((fus_length - lav) / p_max) ** 0.778
* (fus_length - lav) ** 0.383
* npax_max**0.455
)
outputs["data:weight:airframe:fuselage:mass"] = a2
# pylint: disable=missing-function-docstring, unused-argument
# Overriding OpenMDAO compute_partials, not all arguments are used
[docs] def compute_partials(self, inputs, partials, discrete_inputs=None):
wing_config = inputs["data:geometry:wing_configuration"]
if wing_config == 1.0:
fus_length = inputs["data:geometry:fuselage:length"]
lav = inputs["data:geometry:fuselage:front_length"]
mtow = inputs["data:weight:aircraft:MTOW"]
npax_max = (
inputs["data:geometry:cabin:seats:passenger:NPAX_max"] + 2.0
) # addition of 2 pilots
partials["data:weight:airframe:fuselage:mass", "data:weight:aircraft:MTOW"] = (
0.04682 * (0.692 * mtow**-0.308 * npax_max**0.374 * (fus_length - lav) ** 0.590)
)
partials[
"data:weight:airframe:fuselage:mass", "data:geometry:cabin:seats:passenger:NPAX_max"
] = 0.04682 * (mtow**0.692 * 0.374 * npax_max**-0.626 * (fus_length - lav) ** 0.590)
partials["data:weight:airframe:fuselage:mass", "data:geometry:fuselage:length"] = (
0.04682 * (mtow**0.692 * npax_max**0.374 * 0.590 * (fus_length - lav) ** -0.41)
)
partials[
"data:weight:airframe:fuselage:mass", "data:geometry:fuselage:front_length"
] = -(0.04682 * (mtow**0.692 * npax_max**0.374 * 0.590 * (fus_length - lav) ** -0.41))
partials[
"data:weight:airframe:fuselage:mass", "data:geometry:fuselage:maximum_width"
] = 0.0
partials[
"data:weight:airframe:fuselage:mass", "data:geometry:fuselage:maximum_height"
] = 0.0
else:
fus_length = inputs["data:geometry:fuselage:length"]
lav = inputs["data:geometry:fuselage:front_length"]
mtow = inputs["data:weight:aircraft:MTOW"]
npax_max = (
inputs["data:geometry:cabin:seats:passenger:NPAX_max"] + 2.0
) # addition of 2 pilots
maximum_width = inputs["data:geometry:fuselage:maximum_width"]
maximum_height = inputs["data:geometry:fuselage:maximum_height"]
p_max = (
np.pi * (maximum_height + maximum_width) / 2
) # maximum perimeter of the fuselage
partials["data:weight:airframe:fuselage:mass", "data:weight:aircraft:MTOW"] = 14.86 * (
0.144
* mtow**-0.856
* ((fus_length - lav) / p_max) ** 0.778
* (fus_length - lav) ** 0.383
* npax_max**0.455
)
partials[
"data:weight:airframe:fuselage:mass", "data:geometry:cabin:seats:passenger:NPAX_max"
] = 14.86 * (
mtow**0.144
* ((fus_length - lav) / p_max) ** 0.778
* (fus_length - lav) ** 0.383
* 0.455
* npax_max**-0.545
)
partials["data:weight:airframe:fuselage:mass", "data:geometry:fuselage:length"] = (
14.86
* (
mtow**0.144
* p_max**-0.778
* 1.161
* (fus_length - lav) ** 0.161
* npax_max**0.455
)
)
partials[
"data:weight:airframe:fuselage:mass", "data:geometry:fuselage:front_length"
] = -14.86 * (
mtow**0.144 * p_max**-0.778 * 1.161 * (fus_length - lav) ** 0.161 * npax_max**0.455
)
partials[
"data:weight:airframe:fuselage:mass", "data:geometry:fuselage:maximum_width"
] = (
14.86
* (
mtow**0.144
* -0.778
* p_max**-1.778
* (fus_length - lav) ** 1.161
* npax_max**0.455
)
* np.pi
/ 2.0
)
partials[
"data:weight:airframe:fuselage:mass", "data:geometry:fuselage:maximum_height"
] = (
14.86
* (
mtow**0.144
* -0.778
* p_max**-1.778
* (fus_length - lav) ** 1.161
* npax_max**0.455
)
* np.pi
/ 2.0
)