Source code for fastoad_cs25.models.aerodynamics.components.compute_polar

"""Computation of CL and CD for whole aircraft."""
#  This file is part of FAST-OAD_CS25
#  Copyright (C) 2024 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 fastoad.api as oad
import numpy as np
import openmdao.api as om

from ..constants import SERVICE_POLAR, PolarType


[docs] @oad.RegisterSubmodel(SERVICE_POLAR, "fastoad.submodel.aerodynamics.polar.legacy") class ComputePolar(om.ExplicitComponent): """Computation of CL and CD for whole aircraft."""
[docs] def initialize(self): self.options.declare("polar_type", default=PolarType.HIGH_SPEED, types=PolarType)
[docs] def setup(self): self.add_input("tuning:aerodynamics:aircraft:high_speed:CD:k", val=np.nan, units="unitless") self.add_input( "tuning:aerodynamics:aircraft:high_speed:CD:offset", val=np.nan, units="unitless" ) self.add_input( "tuning:aerodynamics:aircraft:high_speed:CD:winglet_effect:k", val=np.nan, units="unitless", ) self.add_input( "tuning:aerodynamics:aircraft:high_speed:CD:winglet_effect:offset", val=np.nan, units="unitless", ) if self.options["polar_type"] != PolarType.HIGH_SPEED: self.add_input( "data:aerodynamics:aircraft:low_speed:CD:CD0", shape_by_conn=True, val=np.nan, units="unitless", ) self.add_input( "data:aerodynamics:aircraft:low_speed:CL", shape_by_conn=True, val=np.nan, units="unitless", ) self.add_input( "data:aerodynamics:aircraft:low_speed:CD:trim", shape_by_conn=True, val=np.nan, units="unitless", ) self.add_input( "data:aerodynamics:aircraft:low_speed:CD:induced:coefficient", val=np.nan, units="unitless", ) if self.options["polar_type"] == PolarType.TAKEOFF: self.add_input( "data:aerodynamics:high_lift_devices:takeoff:CL", val=np.nan, units="unitless" ) self.add_input( "data:aerodynamics:high_lift_devices:takeoff:CD", val=np.nan, units="unitless" ) self.add_output( "data:aerodynamics:aircraft:takeoff:CL", copy_shape="data:aerodynamics:aircraft:low_speed:CL", units="unitless", ) self.add_output( "data:aerodynamics:aircraft:takeoff:CD", copy_shape="data:aerodynamics:aircraft:low_speed:CL", units="unitless", ) self.add_output( "data:aerodynamics:aircraft:takeoff:CD:CD0", copy_shape="data:aerodynamics:aircraft:low_speed:CL", units="unitless", ) self.add_output( "data:aerodynamics:aircraft:takeoff:CD:induced", copy_shape="data:aerodynamics:aircraft:low_speed:CL", units="unitless", ) self.add_output( "data:aerodynamics:aircraft:takeoff:CD:wave", copy_shape="data:aerodynamics:aircraft:low_speed:CL", units="unitless", ) self.add_output( "data:aerodynamics:aircraft:takeoff:CD:trim", copy_shape="data:aerodynamics:aircraft:low_speed:CL", units="unitless", ) self.add_output( "data:aerodynamics:aircraft:takeoff:CD:offset", units="unitless", ) elif self.options["polar_type"] == PolarType.LANDING: self.add_input( "data:aerodynamics:high_lift_devices:landing:CL", val=np.nan, units="unitless" ) self.add_input( "data:aerodynamics:high_lift_devices:landing:CD", val=np.nan, units="unitless" ) self.add_output( "data:aerodynamics:aircraft:landing:CL", copy_shape="data:aerodynamics:aircraft:low_speed:CL", units="unitless", ) self.add_output( "data:aerodynamics:aircraft:landing:CD", copy_shape="data:aerodynamics:aircraft:low_speed:CL", units="unitless", ) self.add_output( "data:aerodynamics:aircraft:landing:CD:CD0", copy_shape="data:aerodynamics:aircraft:low_speed:CL", units="unitless", ) self.add_output( "data:aerodynamics:aircraft:landing:CD:induced", copy_shape="data:aerodynamics:aircraft:low_speed:CL", units="unitless", ) self.add_output( "data:aerodynamics:aircraft:landing:CD:wave", copy_shape="data:aerodynamics:aircraft:low_speed:CL", units="unitless", ) self.add_output( "data:aerodynamics:aircraft:landing:CD:trim", copy_shape="data:aerodynamics:aircraft:low_speed:CL", units="unitless", ) self.add_output( "data:aerodynamics:aircraft:landing:CD:offset", units="unitless", ) elif self.options["polar_type"] == PolarType.LOW_SPEED: self.add_output( "data:aerodynamics:aircraft:low_speed:CD", copy_shape="data:aerodynamics:aircraft:low_speed:CL", units="unitless", ) self.add_output( "data:aerodynamics:aircraft:low_speed:CD:induced", copy_shape="data:aerodynamics:aircraft:low_speed:CL", units="unitless", ) self.add_output( "data:aerodynamics:aircraft:low_speed:CD:wave", copy_shape="data:aerodynamics:aircraft:low_speed:CL", units="unitless", ) self.add_output("data:aerodynamics:aircraft:low_speed:CD:offset", units="unitless") else: raise AttributeError(f"Unknown polar type: {self.options['polar_type']}") elif self.options["polar_type"] == PolarType.HIGH_SPEED: self.add_input( "data:aerodynamics:aircraft:high_speed:CL", shape_by_conn=True, val=np.nan, units="unitless", ) self.add_input( "data:aerodynamics:aircraft:high_speed:CD:CD0", shape_by_conn=True, val=np.nan, units="unitless", ) self.add_input( "data:aerodynamics:aircraft:high_speed:CD:trim", shape_by_conn=True, val=np.nan, units="unitless", ) self.add_input( "data:aerodynamics:aircraft:high_speed:CD:wave", shape_by_conn=True, val=np.nan, units="unitless", ) self.add_input( "data:aerodynamics:aircraft:high_speed:CD:induced:coefficient", val=np.nan, units="unitless", ) self.add_output( "data:aerodynamics:aircraft:high_speed:CD", copy_shape="data:aerodynamics:aircraft:high_speed:CL", units="unitless", ) self.add_output( "data:aerodynamics:aircraft:high_speed:CD:induced", copy_shape="data:aerodynamics:aircraft:high_speed:CL", units="unitless", ) self.add_output("data:aerodynamics:aircraft:high_speed:CD:offset", units="unitless") self.add_output("data:aerodynamics:aircraft:high_speed:L_D_max", units="unitless") self.add_output("data:aerodynamics:aircraft:high_speed:optimal_CL", units="unitless") self.add_output("data:aerodynamics:aircraft:high_speed:optimal_CD", units="unitless") else: raise AttributeError(f"Unknown polar type: {self.options['polar_type']}")
[docs] def setup_partials(self): self.declare_partials("*", "*", method="fd")
[docs] def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): k_cd = inputs["tuning:aerodynamics:aircraft:high_speed:CD:k"] offset_cd = inputs["tuning:aerodynamics:aircraft:high_speed:CD:offset"] k_winglet_cd = inputs["tuning:aerodynamics:aircraft:high_speed:CD:winglet_effect:k"] offset_winglet_cd = inputs[ "tuning:aerodynamics:aircraft:high_speed:CD:winglet_effect:offset" ] if self.options["polar_type"] != PolarType.HIGH_SPEED: cl = inputs["data:aerodynamics:aircraft:low_speed:CL"] cd0 = inputs["data:aerodynamics:aircraft:low_speed:CD:CD0"] cd_trim = inputs["data:aerodynamics:aircraft:low_speed:CD:trim"] cd_c = 0.0 coef_k = inputs["data:aerodynamics:aircraft:low_speed:CD:induced:coefficient"] if self.options["polar_type"] == PolarType.TAKEOFF: delta_cl_hl = inputs["data:aerodynamics:high_lift_devices:takeoff:CL"] delta_cd_hl = inputs["data:aerodynamics:high_lift_devices:takeoff:CD"] elif self.options["polar_type"] == PolarType.LANDING: delta_cl_hl = inputs["data:aerodynamics:high_lift_devices:landing:CL"] delta_cd_hl = inputs["data:aerodynamics:high_lift_devices:landing:CD"] elif self.options["polar_type"] == PolarType.LOW_SPEED: delta_cl_hl = 0.0 delta_cd_hl = 0.0 else: raise AttributeError(f"Unknown polar type: {self.options['polar_type']}") elif self.options["polar_type"] == PolarType.HIGH_SPEED: cl = inputs["data:aerodynamics:aircraft:high_speed:CL"] cd0 = inputs["data:aerodynamics:aircraft:high_speed:CD:CD0"] cd_trim = inputs["data:aerodynamics:aircraft:high_speed:CD:trim"] cd_c = inputs["data:aerodynamics:aircraft:high_speed:CD:wave"] coef_k = inputs["data:aerodynamics:aircraft:high_speed:CD:induced:coefficient"] delta_cl_hl = 0.0 delta_cd_hl = 0.0 else: raise AttributeError(f"Unknown polar type: {self.options['polar_type']}") cl = cl + delta_cl_hl cd_cd0 = cd0 * k_cd cd_wave = cd_c * k_cd cd_trim_component = cd_trim * k_cd cd_induced = (coef_k * cl**2 * k_winglet_cd + offset_winglet_cd) * k_cd cd_offset = delta_cd_hl * k_cd + offset_cd cd = cd_cd0 + cd_wave + cd_trim_component + cd_induced + cd_offset if self.options["polar_type"] == PolarType.LOW_SPEED: outputs["data:aerodynamics:aircraft:low_speed:CD"] = cd outputs["data:aerodynamics:aircraft:low_speed:CD:induced"] = cd_induced outputs["data:aerodynamics:aircraft:low_speed:CD:wave"] = cd_wave outputs["data:aerodynamics:aircraft:low_speed:CD:offset"] = cd_offset elif self.options["polar_type"] == PolarType.TAKEOFF: outputs["data:aerodynamics:aircraft:takeoff:CL"] = cl outputs["data:aerodynamics:aircraft:takeoff:CD"] = cd outputs["data:aerodynamics:aircraft:takeoff:CD:CD0"] = cd_cd0 outputs["data:aerodynamics:aircraft:takeoff:CD:induced"] = cd_induced outputs["data:aerodynamics:aircraft:takeoff:CD:wave"] = cd_wave outputs["data:aerodynamics:aircraft:takeoff:CD:trim"] = cd_trim_component outputs["data:aerodynamics:aircraft:takeoff:CD:offset"] = cd_offset elif self.options["polar_type"] == PolarType.LANDING: outputs["data:aerodynamics:aircraft:landing:CL"] = cl outputs["data:aerodynamics:aircraft:landing:CD"] = cd outputs["data:aerodynamics:aircraft:landing:CD:CD0"] = cd_cd0 outputs["data:aerodynamics:aircraft:landing:CD:induced"] = cd_induced outputs["data:aerodynamics:aircraft:landing:CD:wave"] = cd_wave outputs["data:aerodynamics:aircraft:landing:CD:trim"] = cd_trim_component outputs["data:aerodynamics:aircraft:landing:CD:offset"] = cd_offset else: outputs["data:aerodynamics:aircraft:high_speed:CD"] = cd outputs["data:aerodynamics:aircraft:high_speed:CD:induced"] = cd_induced outputs["data:aerodynamics:aircraft:high_speed:CD:offset"] = cd_offset Cl_opt, Cd_opt = get_optimum_ClCd(np.array([cd, cl]))[0:2] outputs["data:aerodynamics:aircraft:high_speed:L_D_max"] = Cl_opt / Cd_opt outputs["data:aerodynamics:aircraft:high_speed:optimal_CL"] = Cl_opt outputs["data:aerodynamics:aircraft:high_speed:optimal_CD"] = Cd_opt
[docs] def get_optimum_ClCd(ClCd): lift_drag_ratio = ClCd[1, :] / ClCd[0, :] optimum_index = np.argmax(lift_drag_ratio) optimum_Cl = ClCd[1][optimum_index] optimum_Cd = ClCd[0][optimum_index] return optimum_Cl, optimum_Cd