Skip to content

Commit

Permalink
A mission part can now be repeated.
Browse files Browse the repository at this point in the history
  • Loading branch information
christophe-david committed Dec 18, 2024
1 parent 8ee7323 commit f40b2a6
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@
from copy import deepcopy
from dataclasses import InitVar, dataclass, field, fields
from itertools import chain
from typing import List, Tuple
from typing import List, Optional, Tuple

import numpy as np

from .constants import NAME_TAG, SEGMENT_TYPE_TAG, TYPE_TAG
from .input_definition import InputDefinition
from ..schema import (
CLIMB_PARTS_TAG,
COUNT_TAG,
CRUISE_PART_TAG,
DESCENT_PARTS_TAG,
MISSION_DEFINITION_TAG,
Expand Down Expand Up @@ -66,6 +67,7 @@ class AbstractStructureBuilder(ABC):
name: str
parent_name: str = None
variable_prefix: str = ""
number: Optional[int] = None

_structure: dict = field(default=None, init=False)

Expand Down Expand Up @@ -133,7 +135,7 @@ def _build(self, definition: dict) -> dict:
.. Important::
Please use :meth:`_insert_builder` when another StructureBuilder object is needed in
Please use :meth:`process_builder` when another StructureBuilder object is needed in
the currently built structure.
:param definition: the dict that will be converted.
Expand All @@ -149,6 +151,8 @@ def qualified_name(self):
name = self.parent_name
if self.name:
name += f":{self.name}"
if self.number is not None:
name += f"_{self.number}"
return name

def _parse_inputs(
Expand Down Expand Up @@ -396,27 +400,29 @@ def _build(self, definition: dict) -> dict:

mission_parts = []
for part_definition in mission_definition[PARTS_TAG]:
if ROUTE_TAG in part_definition:
route_name = part_definition[ROUTE_TAG]
builder = RouteStructureBuilder(
definition, route_name, self.qualified_name, self.variable_prefix
)
elif PHASE_TAG in part_definition:
phase_name = part_definition[PHASE_TAG]
builder = PhaseStructureBuilder(
definition, phase_name, self.qualified_name, self.variable_prefix
)
elif SEGMENT_TAG in part_definition:
builder = SegmentStructureBuilder(
part_definition, "", self.qualified_name, self.variable_prefix
)
else:
builder = DefaultStructureBuilder(
part_definition, "", self.qualified_name, self.variable_prefix
)

part_structure = self.process_builder(builder)
mission_parts.append(part_structure)
numbers = range(part_definition[COUNT_TAG]) if COUNT_TAG in part_definition else [None]
for number in numbers:
if ROUTE_TAG in part_definition:
route_name = part_definition[ROUTE_TAG]
builder = RouteStructureBuilder(
definition, route_name, self.qualified_name, self.variable_prefix, number
)
elif PHASE_TAG in part_definition:
phase_name = part_definition[PHASE_TAG]
builder = PhaseStructureBuilder(
definition, phase_name, self.qualified_name, self.variable_prefix, number
)
elif SEGMENT_TAG in part_definition:
builder = SegmentStructureBuilder(
part_definition, "", self.qualified_name, self.variable_prefix, number
)
else:
builder = DefaultStructureBuilder(
part_definition, "", self.qualified_name, self.variable_prefix, number
)

part_structure = self.process_builder(builder)
mission_parts.append(part_structure)

mission_structure[PARTS_TAG] = mission_parts

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,17 +326,29 @@
"properties": {
"phase": {
"type": "string"
},
"count": {
"type": "integer"
}
}
},
"required": [
"phase"
]
},
"mission_route": {
"type": "object",
"additionalProperties": false,
"properties": {
"route": {
"type": "string"
},
"count": {
"type": "integer"
}
}
},
"required": [
"route"
]
},
"mission_reserve": {
"type": "object",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
PHASE_TAG = "phase"
RESERVE_TAG = "reserve"
PARTS_TAG = "parts"
COUNT_TAG = "count"
CLIMB_PARTS_TAG = "climb_parts"
CRUISE_PART_TAG = "cruise_part"
DESCENT_PARTS_TAG = "descent_parts"
Expand Down Expand Up @@ -114,20 +115,19 @@ def _validate(cls, content: dict):
for mission_definition in content[MISSION_DEFINITION_TAG].values():
reserve_count = 0
for part in mission_definition[PARTS_TAG]:
part_type, value = tuple(*part.items())
if part_type == PHASE_TAG:
Ensure(value).is_in(content[PHASE_DEFINITIONS_TAG])
elif part_type == ROUTE_TAG:
Ensure(value).is_in(content[ROUTE_DEFINITIONS_TAG])
elif part_type == RESERVE_TAG:
if PHASE_TAG in part:
Ensure(part[PHASE_TAG]).is_in(content[PHASE_DEFINITIONS_TAG])
elif ROUTE_TAG in part:
Ensure(part[ROUTE_TAG]).is_in(content[ROUTE_DEFINITIONS_TAG])
elif RESERVE_TAG in part:
reserve_count += 1
Ensure(value["ref"]).is_in(
Ensure(part[RESERVE_TAG]["ref"]).is_in(
list(content[ROUTE_DEFINITIONS_TAG]) + list(content[PHASE_DEFINITIONS_TAG])
)
Ensure(reserve_count).is_less_than_or_equal_to(1)
if reserve_count == 1:
# reserve definition should be the last part
Ensure(part_type).equals(RESERVE_TAG)
Ensure(RESERVE_TAG).is_in(part)

@classmethod
def _convert_none_values(cls, struct: Union[dict, list]):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ phases:
mass:
value: data:mission:operational:ramp_weight
unit: kg
taxi_out:
parts:
- segment: taxi
isa_offset: 0.0
thrust_rate: 0.3
true_airspeed: 0.0
target:
time: ~duration
takeoff:
parts:
- segment: transition
Expand Down Expand Up @@ -67,3 +75,8 @@ missions:
- phase: start
- phase: takeoff
- route: main_route
repetition:
parts:
- phase: start
- phase: taxi_out
count: 2
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,17 @@
<duration units="s">143.</duration>
</taxi_out>
</without_route>
<repetition>
<taxi_out>
<duration>100.0</duration>
</taxi_out>
<taxi_out_0>
<duration>100.0</duration>
</taxi_out_0>
<taxi_out_1>
<duration>200.0</duration>
</taxi_out_1>
</repetition>
</mission>
<weight>
<aircraft>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ def test_mission_group_breguet_without_fuel_adjustment(cleanup, with_dummy_plugi
out_file=RESULTS_FOLDER_PATH / "unlooped_breguet_mission_group.csv",
use_initializer_iteration=False,
mission_file_path=DATA_FOLDER_PATH / "test_breguet.yml",
mission_name="operational",
adjust_fuel=False,
reference_area_variable="data:geometry:aircraft:reference_area",
),
Expand Down Expand Up @@ -355,6 +356,7 @@ def test_mission_group_breguet_with_fuel_adjustment(cleanup, with_dummy_plugin_2
out_file=RESULTS_FOLDER_PATH / "looped_breguet_mission_group.csv",
use_initializer_iteration=True,
mission_file_path=DATA_FOLDER_PATH / "test_breguet.yml",
mission_name="operational",
use_inner_solvers=True,
reference_area_variable="data:geometry:aircraft:reference_area",
is_sizing=True,
Expand Down Expand Up @@ -520,3 +522,23 @@ def test_mission_group_without_route(cleanup, with_dummy_plugin_2):
assert_allclose(problem["data:mission:without_route:ZFW"], 55000.0, atol=1.0)
assert_allclose(problem["data:mission:without_route:needed_block_fuel"], 1136.8, atol=1.0)
assert_allclose(problem["data:mission:without_route:block_fuel"], 1136.8, atol=1.0)


def test_mission_with_repeated_phase(cleanup, with_dummy_plugin_2):
input_file_path = DATA_FOLDER_PATH / "test_mission.xml"
ivc = DataFile(input_file_path).to_ivc()

problem = run_system(
OMMission(
propulsion_id="test.wrapper.propulsion.dummy_engine",
out_file=RESULTS_FOLDER_PATH / "repetition.csv",
use_initializer_iteration=False,
mission_file_path=DATA_FOLDER_PATH / "test_breguet.yml",
mission_name="repetition",
),
ivc,
)
assert_allclose(problem["data:mission:repetition:taxi_out_0:duration"], 100.0, atol=1.0)
assert_allclose(problem["data:mission:repetition:taxi_out_1:duration"], 200.0, atol=1.0)
assert_allclose(problem["data:mission:repetition:taxi_out_0:fuel"], 70.2, atol=1.0)
assert_allclose(problem["data:mission:repetition:taxi_out_1:fuel"], 140.4, atol=1.0)
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ def test_with_custom_segment(cleanup, with_dummy_plugin_2):
propulsion_id="test.wrapper.propulsion.dummy_engine",
use_initializer_iteration=False,
mission_file_path=MissionWrapper(DATA_FOLDER_PATH / "test_with_custom_segment.yml"),
mission_name="test",
),
ivc,
)

0 comments on commit f40b2a6

Please sign in to comment.