Source code for fastga_he.models.performances.energy_specific_air_range

#  This file is part of FAST-OAD_CS23-HE : A framework for rapid Overall Aircraft Design of Hybrid
#  Electric Aircraft.
#  Copyright (C) 2025 ISAE-SUPAERO

import fastoad.api as oad
import numpy as np
import openmdao.api as om
from fastoad.module_management.constants import ModelDomain

from fastga_he.models.environmental_impacts.simple_energy_impact import (
    ENERGY_CONTENT_JET_FUEL,
    KWH_TO_MJ,
)

ENERGY_CONTENT_AVGAS = 43.7  # In M/kg


[docs] @oad.RegisterOpenMDAOSystem( "fastga_he.performances.energy_specific_air_range", domain=ModelDomain.PERFORMANCE ) class EnergySpecificAirRange(om.ExplicitComponent): """ Component that computes the Energy Specific Air Range (ESAR) which is the distance that can be travelled with one unit of energy. """ def __init__(self, **kwargs): super().__init__(**kwargs) self.energy_content_fuel = None
[docs] def initialize(self): mission_possible_option = ["design", "operational", "both"] self.options.declare( name="mission", default="design", values=["design", "operational", "both"], desc="Option to give the type of mission whose impact we must study, possible option include" + ", ".join(mission_possible_option), allow_none=False, ) fuel_type_option = ["jet_fuel", "avgas"] self.options.declare( name="fuel_type", default="jet_fuel", values=fuel_type_option, desc="Option to give the type of fuel used, possible option include" + ", ".join(fuel_type_option), )
[docs] def setup(self): mission_option = self.options["mission"] if mission_option == "design" or mission_option == "both": self.add_input("data:mission:sizing:fuel", units="kg", val=np.nan) self.add_input("data:mission:sizing:energy", units="kW*h", val=np.nan) self.add_input("data:TLAR:range", np.nan, units="NM") self.add_output("data:mission:sizing:energy_specific_air_range", units="NM/kW/h") if mission_option == "operational" or mission_option == "both": self.add_input("data:mission:operational:fuel", units="kg", val=np.nan) self.add_input("data:mission:operational:energy", units="kW*h", val=np.nan) self.add_input("data:mission:operational:range", np.nan, units="NM") self.add_output("data:mission:operational:energy_specific_air_range", units="NM/kW/h") if self.options["fuel_type"] == "avgas": self.energy_content_fuel = ENERGY_CONTENT_AVGAS else: self.energy_content_fuel = ENERGY_CONTENT_JET_FUEL
[docs] def setup_partials(self): mission_option = self.options["mission"] if mission_option == "design" or mission_option == "both": self.declare_partials( of="data:mission:sizing:energy_specific_air_range", wrt=["data:mission:sizing:fuel", "data:mission:sizing:energy", "data:TLAR:range"], method="exact", ) if mission_option == "operational" or mission_option == "both": self.declare_partials( of="data:mission:operational:energy_specific_air_range", wrt=[ "data:mission:operational:fuel", "data:mission:operational:energy", "data:mission:operational:range", ], method="exact", )
[docs] def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): mission_option = self.options["mission"] if mission_option == "design" or mission_option == "both": fuel_consumed = inputs["data:mission:sizing:fuel"] electricity_consumed = inputs["data:mission:sizing:energy"] design_range = inputs["data:TLAR:range"] total_energy = ( fuel_consumed * self.energy_content_fuel / KWH_TO_MJ + electricity_consumed ) outputs["data:mission:sizing:energy_specific_air_range"] = design_range / total_energy if mission_option == "operational" or mission_option == "both": fuel_consumed = inputs["data:mission:operational:fuel"] electricity_consumed = inputs["data:mission:operational:energy"] op_range = inputs["data:mission:operational:range"] total_energy = ( fuel_consumed * self.energy_content_fuel / KWH_TO_MJ + electricity_consumed ) outputs["data:mission:operational:energy_specific_air_range"] = op_range / total_energy
[docs] def compute_partials(self, inputs, partials, discrete_inputs=None): mission_option = self.options["mission"] if mission_option == "design" or mission_option == "both": fuel_consumed = inputs["data:mission:sizing:fuel"] electricity_consumed = inputs["data:mission:sizing:energy"] design_range = inputs["data:TLAR:range"] total_energy = ( fuel_consumed * self.energy_content_fuel / KWH_TO_MJ + electricity_consumed ) partials[ "data:mission:sizing:energy_specific_air_range", "data:mission:sizing:fuel" ] = -design_range / total_energy**2.0 * self.energy_content_fuel / KWH_TO_MJ partials[ "data:mission:sizing:energy_specific_air_range", "data:mission:sizing:energy" ] = -design_range / total_energy**2.0 partials["data:mission:sizing:energy_specific_air_range", "data:TLAR:range"] = ( 1.0 / total_energy ) if mission_option == "operational" or mission_option == "both": fuel_consumed = inputs["data:mission:operational:fuel"] electricity_consumed = inputs["data:mission:operational:energy"] op_range = inputs["data:mission:operational:range"] total_energy = ( fuel_consumed * self.energy_content_fuel / KWH_TO_MJ + electricity_consumed ) partials[ "data:mission:operational:energy_specific_air_range", "data:mission:operational:fuel", ] = -op_range / total_energy**2.0 * self.energy_content_fuel / KWH_TO_MJ partials[ "data:mission:operational:energy_specific_air_range", "data:mission:operational:energy", ] = -op_range / total_energy**2.0 partials[ "data:mission:operational:energy_specific_air_range", "data:mission:operational:range", ] = 1.0 / total_energy