Skip to content

Commit

Permalink
Test for the CL limitation during climb and cruise
Browse files Browse the repository at this point in the history
  • Loading branch information
Eric Nguyen Van committed Jan 25, 2024
1 parent 3f9a10b commit 7e35d28
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 7 deletions.
10 changes: 8 additions & 2 deletions docs/documentation/mission_module/mission_file/segments.rst
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@ Mach 0.8 while keeping equivalent_airspeed constant).
mach: constant # level that provides maximum lift/drag
altitude: # at current mass.
value: optimal_flight_level
maximum_CL: 0.6 # Limitation on lift coefficient.
# The altitude will be limited to the closest
# flight level within the CL limitation.
.. seealso::

Expand Down Expand Up @@ -274,9 +277,11 @@ prepending climb, if any).
An :code:`optimal_cruise` segment simulates a cruise climb, i.e. a cruise where the aircraft
climbs gradually to keep being at altitude of maximum lift/drag ratio.

It assumed the segment actually starts at altitude of maximum lift/drag ratio, which can be
The :code:`optimal_cruise` segment can be realised at a constant lift coefficient using the parameter :code:`maximum_CL`.

It assumed the segment actually starts at altitude of maximum lift/drag ratio or the altitude given by :code:`maximum_CL`, which can be
achieved with an :ref:`segment-altitude_change` segment with :code:`optimal_altitude` as target
altitude.
altitude and :code:`maximum_CL` as parameter.

*The common way to optimize the fuel consumption for commercial aircraft is a step climb cruise.
Such segment will be implemented in the future.*
Expand All @@ -288,6 +293,7 @@ Such segment will be implemented in the future.*
segment: optimal_cruise
polar: data:aerodynamics:aircraft:cruise # High speed aerodynamic polar
engine_setting: cruise
maximum_CL: 0.6
target:
ground_distance: # Cruise for 2000 nautical miles
value: 2000
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@
<fuel units="kg">
95.0<!--mass of consumed fuel during takeoff phase in sizing mission--></fuel>
</takeoff>
<main_route>
<climb>
<max_CL>0.65</max_CL>
</climb>
<cruise>
<max_CL>0.65</max_CL>
</cruise>
</main_route>
<taxi_out>
<thrust_rate>0.3</thrust_rate>
<duration units="s">143.</duration>
Expand All @@ -89,6 +97,14 @@
<fuel units="kg">
95.0<!--mass of consumed fuel during takeoff phase in sizing mission--></fuel>
</takeoff>
<main_route>
<climb>
<max_CL>0.65</max_CL>
</climb>
<cruise>
<max_CL>0.65</max_CL>
</cruise>
</main_route>
<taxi_out>
<thrust_rate>0.3</thrust_rate>
<duration units="s">143.</duration>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,34 @@ phases:
mach: constant
altitude:
value: optimal_flight_level
maximum_CL: ~max_CL
climb_optimal:
engine_setting: climb
polar: data:aerodynamics:aircraft:cruise
thrust_rate: 0.9
time_step: 5.0
parts:
- segment: altitude_change
target:
altitude:
value: 10000.
unit: ft
equivalent_airspeed: constant
- segment: speed_change
target:
equivalent_airspeed:
value: 300.
unit: kn
- segment: altitude_change
target:
equivalent_airspeed: constant
mach: 0.78
- segment: altitude_change
target:
mach: constant
altitude:
value: optimal_altitude
maximum_CL: ~max_CL
descent:
engine_setting: idle
polar: data:aerodynamics:aircraft:cruise
Expand Down Expand Up @@ -155,6 +183,28 @@ routes:
segment: cruise
engine_setting: cruise
polar: data:aerodynamics:aircraft:cruise
maximum_CL: ~max_CL
descent_parts:
- phase: descent
main_route_optimal:
distance_accuracy:
value: ~
default: 0.5
unit: km
range:
value: 2000.
unit: NM
climb_parts:
- phase: initial_climb
- phase: climb_optimal
cruise_part:
segment: optimal_cruise
engine_setting: cruise
polar: data:aerodynamics:aircraft:cruise
target:
altitude:
value: optimal_altitude
maximum_CL: ~max_CL
descent_parts:
- phase: descent
diversion:
Expand Down Expand Up @@ -198,3 +248,9 @@ missions:
ref: main_route
multiplier: 0.03
use_all_block_fuel: True
operational_optimal:
parts:
- route: main_route_optimal
- reserve:
ref: main_route_optimal
multiplier: 0.03
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@
<fuel units="kg">
95.0<!--mass of consumed fuel during takeoff phase in sizing mission--></fuel>
</takeoff>
<main_route>
<climb>
<max_CL>0.65</max_CL>
</climb>
<cruise>
<max_CL>0.65</max_CL>
</cruise>
</main_route>
<taxi_out>
<thrust_rate>0.3</thrust_rate>
<duration units="s">143.</duration>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from scipy.constants import foot, knot, nautical_mile

from fastoad._utils.testing import run_system
from fastoad.io import DataFile
from fastoad.io import DataFile, VariableIO
from ..mission import OMMission
from ..mission_run import AdvancedMissionComp
from ..mission_wrapper import MissionWrapper
Expand Down Expand Up @@ -395,3 +395,83 @@ def test_mission_group_with_fuel_objective(cleanup, with_dummy_plugin_2):

assert_allclose(problem["data:mission:fuel_as_objective:needed_block_fuel"], 10000.0, atol=1.0)
assert_allclose(problem["data:mission:fuel_as_objective:reserve:fuel"], 260.0, atol=1.0)


def test_mission_group_with_CL_limitation(cleanup, with_dummy_plugin_2):

input_file_path = pth.join(DATA_FOLDER_PATH, "test_mission.xml")
vars = VariableIO(input_file_path).read(
ignore=[
"data:mission:operational:main_route:climb:max_CL",
"data:mission:operational:main_route:cruise:max_CL",
]
)
ivc = vars.to_ivc()

# Activate CL limitation during cruise and climb
ivc.add_output("data:mission:operational:main_route:climb:max_CL", val=0.45)
ivc.add_output("data:mission:operational:main_route:cruise:max_CL", val=0.45)

problem = run_system(
AdvancedMissionComp(
propulsion_id="test.wrapper.propulsion.dummy_engine",
out_file=pth.join(RESULTS_FOLDER_PATH, "CL_limitation.csv"),
use_initializer_iteration=False,
mission_file_path=MissionWrapper(
pth.join(DATA_FOLDER_PATH, "test_mission.yml"), mission_name="operational"
),
reference_area_variable="data:geometry:aircraft:reference_area",
),
ivc,
)

flight_points = problem.model.component.flight_points
climb_points = flight_points.loc[flight_points["name"] == "operational:main_route:climb"]
CL_end_climb = climb_points.CL.iloc[-1]
altitude_end_climb = climb_points.altitude.iloc[-1]

# check CL and flight level, CL is lower than 0.45 because the climb phase stops at the closest flight level.
assert_allclose(CL_end_climb, 0.445, atol=1e-3)
assert_allclose(altitude_end_climb, 9753.6, atol=1e-1)

# Now check climbing cruise with contant CL
ivc.add_output(
"data:mission:operational_optimal:main_route_optimal:climb_optimal:max_CL", val=0.45
)
ivc.add_output("data:mission:operational_optimal:main_route_optimal:cruise:max_CL", val=0.45)
ivc.add_output("data:mission:operational_optimal:taxi_out:thrust_rate", val=0.3)
ivc.add_output("data:mission:operational_optimal:taxi_out:duration", val=300, units="s")
ivc.add_output("data:mission:operational_optimal:takeoff:fuel", val=100, units="kg")
ivc.add_output("data:mission:operational_optimal:takeoff:V2", val=70, units="m/s")
ivc.add_output("data:mission:operational_optimal:TOW", val=70000, units="kg")

problem = run_system(
AdvancedMissionComp(
propulsion_id="test.wrapper.propulsion.dummy_engine",
out_file=pth.join(RESULTS_FOLDER_PATH, "CL_limitation.csv"),
use_initializer_iteration=False,
mission_file_path=MissionWrapper(
pth.join(DATA_FOLDER_PATH, "test_mission.yml"), mission_name="operational_optimal"
),
reference_area_variable="data:geometry:aircraft:reference_area",
),
ivc,
)

flight_points = problem.model.component.flight_points
climb_points = flight_points.loc[
flight_points["name"] == "operational_optimal:main_route_optimal:climb_optimal"
]
cruise_points = flight_points.loc[
flight_points["name"] == "operational_optimal:main_route_optimal:cruise"
]
CL_end_climb = climb_points.CL.iloc[-1]
altitude_end_climb = climb_points.altitude.iloc[-1]
altitude_end_cruise = cruise_points.altitude.iloc[-1]
CL_end_cruise = cruise_points.CL.iloc[-1]

# Check CL and altitude for a climbing cruise at constant CL.
assert_allclose(CL_end_climb, 0.45, atol=1e-3)
assert_allclose(CL_end_cruise, 0.45, atol=1e-3)
assert_allclose(altitude_end_climb, 9821.85, atol=1e-1)
assert_allclose(altitude_end_cruise, 10343.68, atol=1e-1)
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ class AbstractTimeStepFlightSegment(
#: the flight path).
time_step: float = DEFAULT_TIME_STEP

# The lift coefficient at which the cruise phase will be realised
CL_cruise: float = None
# The maximum lift coefficient for optimal climb and cruise segments
maximum_CL: float = None

Check notice on line 73 in src/fastoad/models/performances/mission/segments/time_step_base.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/fastoad/models/performances/mission/segments/time_step_base.py#L73

Attribute name "maximum_CL" doesn't conform to snake_case naming style

#: Minimum and maximum authorized altitude values. If computed altitude gets beyond these
#: limits, computation will be interrupted and a warning message will be issued in logger.
Expand Down Expand Up @@ -318,8 +318,8 @@ def _get_optimal_altitude(
def distance_to_optimum(altitude):
atm = self._get_atmosphere_point(altitude)
true_airspeed = mach * atm.speed_of_sound
if self.CL_cruise is not None:
CL_optimal = min(self.polar.optimal_cl, self.CL_cruise)
if self.maximum_CL is not None:
CL_optimal = min(self.polar.optimal_cl, self.maximum_CL)

Check notice on line 322 in src/fastoad/models/performances/mission/segments/time_step_base.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/fastoad/models/performances/mission/segments/time_step_base.py#L322

Variable name "CL_optimal" doesn't conform to snake_case naming style
else:
CL_optimal = self.polar.optimal_cl

Check notice on line 324 in src/fastoad/models/performances/mission/segments/time_step_base.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/fastoad/models/performances/mission/segments/time_step_base.py#L324

Variable name "CL_optimal" doesn't conform to snake_case naming style
optimal_air_density = (
Expand Down

0 comments on commit 7e35d28

Please sign in to comment.