Skip to content
Snippets Groups Projects
Commit 3a3aa698 authored by Timeea's avatar Timeea Committed by Florian Schültke
Browse files

Allow overshoot tanks during the first iterations

parent 5f9cd96a
No related branches found
No related tags found
5 merge requests!263Apply 1 suggestion(s) to 1 file(s),!261Reintruduced automatic flight condition selection,!245Apply 1 suggestion(s) to 1 file(s),!233Initial open source version,!31Allow overshoot tanks during the first iterations
......@@ -95,7 +95,10 @@ def data_postprocessing(paths_and_names, routing_dict, data_dict, runtime_output
key_output_dict, method_specific_output_dict = routing_dict['func_user_method_data_output_preparation'](
data_dict)
# Extract tool level from routing dictionary.
tool_level = routing_dict['tool_level']
if data_dict['current_tool_level'] is None:
tool_level = routing_dict['tool_level']
else:
tool_level = str(data_dict['current_tool_level'])
"""Write data to aircraft exchange file."""
# Extract root and path to aircraft exchange file.
......
......@@ -45,7 +45,7 @@ def method_basic(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_confi
excel_file_path = paths_and_names['project_directory'] + \
'/' + "Seats_positions.xlsx"
airplane_seatings = pd.read_excel(excel_file_path)
DEBUG(msg="ToDo: Update this when acommodation and neutral point position are available.")
DEBUG(msg="ToDo: Update this when acommodation positions are available.")
# Read fuselage and wing
wing = read_wing(paths_and_names)
......@@ -130,27 +130,29 @@ def method_basic(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_confi
# Maximum fuel mass + maximum fuel mass per tank
maximum_fuel_mass, maximum_fuel_mass_per_tank, ferry_range_mass = calculate_maximum_fuel_mass(
tanks, operating_mass_empty, x_leading_edge_mac, mac)
tanks, operating_mass_empty, x_leading_edge_mac, mac, maximum_takeoff_mass, runtime_output)
maximum_fuel_mass.print("Maximum fuel mass")
ferry_range_mass.print("Ferry range mass")
# Maximum PL mass
maximum_payload_mass = calculate_maximum_payload_mass(
dict_ac_exchange["maximum_structural_payload_mass"],
main_components_mass_properties["fuselage"]).print("Maximimum payload mass")
# Design payload mass
design_payload_mass = calculate_design_payload_mass(
transport_task_data, main_components_mass_properties["fuselage"]).print("Design payload mass")
transport_task_data, main_components_mass_properties["fuselage"], maximum_payload_mass, runtime_output).print(
"Design payload mass")
# Design fuel mass
design_fuel_mass_takeoff, design_fuel_mass_midflight, design_fuel_mass_minimum_landing, \
design_fuel_mass_takeoff_per_tank, consumed_fuel, design_fuel_mass = \
design_fuel_mass_takeoff_per_tank, consumed_fuel, design_fuel_mass, design_fuel_mass_per_tank = \
calculate_design_fuel_mass(
mission_information, maximum_takeoff_mass, operating_mass_empty, design_payload_mass, tanks)
mission_information, maximum_takeoff_mass, operating_mass_empty, design_payload_mass, tanks,
routing_dict, dict_ac_exchange)
design_fuel_mass.path_to_element = "./analysis/masses_cg_inertia/design_fuel_mass"
design_fuel_mass.print("Design fuel mass")
# Maximum PL mass
maximum_payload_mass = calculate_maximum_payload_mass(
dict_ac_exchange["maximum_structural_payload_mass"],
main_components_mass_properties["fuselage"]).print("Maximimum payload mass")
# Maximum zero fuel mass
maximum_zero_fuel_mass = calculate_maximum_zero_fuel_mass(
[operating_mass_empty, maximum_payload_mass], inertia_method).print("Maximum zero fuel mass")
......@@ -158,15 +160,20 @@ def method_basic(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_confi
maximum_zero_fuel_mass.center_of_gravity['x'] - x_leading_edge_mac) / mac * 100
# Design mass
design_mass_takeoff = calculate_design_mass(
design_mass = calculate_design_mass(
[operating_mass_empty, design_payload_mass,
design_fuel_mass_takeoff], inertia_method
).print("Design mass takeoff")
design_mass = copy.copy(design_mass_takeoff)
design_fuel_mass], inertia_method
).print("Design mass")
design_mass.path_to_element = "./analysis/masses_cg_inertia/design_mass"
design_mass.cg_mac = (
design_mass.center_of_gravity['x'] - x_leading_edge_mac) / mac * 100
# Design mass takeoff
design_mass_takeoff = calculate_design_mass(
[operating_mass_empty, design_payload_mass,
design_fuel_mass_takeoff], inertia_method
).print("Design mass takeoff")
# Maximum takeoff mass
maximum_takeoff_mass = copy.copy(design_mass)
maximum_takeoff_mass.path_to_element = "./analysis/masses_cg_inertia/maximum_takeoff_mass"
......@@ -295,7 +302,7 @@ def method_basic(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_confi
x_coord_back_front_cargo = x_coord_front_back_cargo[::-1]
runtime_output.print("CARGO...")
DEBUG(msg="cargo = luggage mass per passenger, modify/extend later with palets / add additional cargo")
DEBUG(msg="The palets positions unknown. The cargo is loaded at the same locations as the seats positions.")
cg_positions_over_mac_cargo_front_back, total_mass_cargo_front_back = \
cg_change_cargo_loading_front_back(
......@@ -344,9 +351,9 @@ def method_basic(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_confi
# Determine the CG curtailment:
most_fwd_CG_over_mac = min([min(cg_positions_over_mac_refueling), min(cg_positions_over_mac_passengers_front_back),
min(cg_positions_over_mac_cargo_front_back)])
min(cg_positions_over_mac_cargo_front_back), min(cg_positions_over_mac_defueling)])
most_aft_CG_over_mac = max([max(cg_positions_over_mac_refueling), max(cg_positions_over_mac_passengers_back_front),
max(cg_positions_over_mac_cargo_back_front)])
max(cg_positions_over_mac_cargo_back_front), max(cg_positions_over_mac_defueling)])
most_fwd_CG = most_fwd_CG_over_mac * mac / 100 + x_leading_edge_mac
most_aft_CG = most_aft_CG_over_mac * mac / 100 + x_leading_edge_mac
......@@ -365,10 +372,14 @@ def method_basic(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_confi
corresponding_index = cg_positions_over_mac_passengers_front_back.index(
most_fwd_CG_over_mac)
corresponding_mass_most_fwd_CG = total_mass_passengers_front_back[corresponding_index]
else:
elif most_fwd_CG_over_mac in cg_positions_over_mac_cargo_front_back:
corresponding_index = cg_positions_over_mac_cargo_front_back.index(
most_fwd_CG_over_mac)
corresponding_mass_most_fwd_CG = total_mass_cargo_front_back[corresponding_index]
else:
corresponding_index = cg_positions_over_mac_defueling.index(
most_fwd_CG_over_mac)
corresponding_mass_most_fwd_CG = total_mass_defueling[corresponding_index]
runtime_output.print(
f"Corresponding mass most fwd CG is of {corresponding_mass_most_fwd_CG:.2f} kg.")
......@@ -382,10 +393,14 @@ def method_basic(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_confi
corresponding_index = cg_positions_over_mac_passengers_back_front.index(
most_aft_CG_over_mac)
corresponding_mass_most_aft_CG = total_mass_passengers_back_front[corresponding_index]
else:
elif most_aft_CG_over_mac in cg_positions_over_mac_cargo_back_front:
corresponding_index = cg_positions_over_mac_cargo_back_front.index(
most_aft_CG_over_mac)
corresponding_mass_most_aft_CG = total_mass_cargo_back_front[corresponding_index]
else:
corresponding_index = cg_positions_over_mac_defueling.index(
most_fwd_CG_over_mac)
corresponding_mass_most_fwd_CG = total_mass_defueling[corresponding_index]
runtime_output.print(
f"Corresponding mass most aft CG is of {corresponding_mass_most_aft_CG:.2f} kg.")
......@@ -488,6 +503,18 @@ def method_basic(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_confi
"Systems": systems
}
# Check if the given tool_level from aircrat exchange file is None -> if true: -> initialize with 0.
if dict_ac_exchange['tool_level'] is None:
basic_output_dict['current_tool_level'] = 0
# Else if condition: Check if given tool_level from aircrat exchange is lower than the given own tool level
# from module configuration file -> if true: Add 1 to current tool level from airaft exchange file.
elif dict_ac_exchange['tool_level'] < int(routing_dict['tool_level']):
basic_output_dict['current_tool_level'] = dict_ac_exchange['tool_level'] + 1
# Else condition: The given tool_level from aircrat exchange is equal to the given own tool level
else:
basic_output_dict['current_tool_level'] = int(
routing_dict['tool_level'])
return basic_output_dict
......@@ -833,7 +860,7 @@ def calculate_maximum_landing_mass_by_regression_rwth(maximum_takeoff_mass, iner
return maximum_landing_mass
def calculate_maximum_fuel_mass(tanks, ome, LE, mac):
def calculate_maximum_fuel_mass(tanks, ome, LE, mac, mtom, runtime_output):
"""Calculation of maximum fuel mass
Args:
......@@ -883,6 +910,10 @@ def calculate_maximum_fuel_mass(tanks, ome, LE, mac):
ferry_range_mass.cg_mac = (
max_fuel_mass.center_of_gravity['x'] - LE) / mac * 100
if ferry_range_mass.mass >= mtom.mass:
runtime_output.warning(
"Attention: OEW + max_fuel_mass is higher than MTOW.")
return max_fuel_mass, max_fuel_mass_per_tank, ferry_range_mass
......@@ -973,7 +1004,7 @@ def calculate_fuel_mass_properties_evenly_distributed(tanks, fuel_mass, runtime_
return fuel_mass_properties, fuel_mass_per_tank
def calculate_fuel_mass_properties(tanks, fuel_mass):
def calculate_fuel_mass_properties(tanks, fuel_mass, routing_dict, dict_ac_exchange):
"""
Refuel tanks in the following priority order:
1. Left and Right wing tanks (equally). These are also sorted based on the order defined before according
......@@ -991,6 +1022,8 @@ def calculate_fuel_mass_properties(tanks, fuel_mass):
Raises:
ValueError: If the fuel mass exceeds the total tank capacity.
"""
import logging
runtime_output = logging.getLogger('module_logger')
# Helper function to categorize tanks based on location string
def categorize_tank_location(tank):
......@@ -1019,7 +1052,7 @@ def calculate_fuel_mass_properties(tanks, fuel_mass):
tank.energy / tank.gravimetric_density for tank in tanks)
if fuel_mass > total_capacity:
raise ValueError(
runtime_output.critical(
"Fuel mass to be filled up exceeds the total capacity of all tanks!")
# Distribute fuel across left and right wing tanks first
......@@ -1142,8 +1175,12 @@ def calculate_fuel_mass_properties(tanks, fuel_mass):
# Check if there is still remaining fuel (should not happen unless there is a capacity error)
if remaining_fuel > 0:
raise ValueError(
"Fuel mass calculation failed ... Not enough capacity in tanks!")
if int(dict_ac_exchange['tool_level']) < int(routing_dict['tool_level']):
runtime_output.warning(
"Fuel mass calculation failed ... Not enough capacity in tanks!")
else:
raise ValueError(
"Fuel mass calculation failed ... Not enough capacity in tanks!")
fuel_mass_properties = MassPropertiesIO().initialize_zero()
fuel_mass_properties.mass = sum(
......@@ -1344,7 +1381,8 @@ def calculate_fuel_properties_defueling(tanks, fuel_to_remove, fuel_mass_per_tan
def calculate_design_fuel_mass(mission_information=None, maximum_takeoff_mass=None,
operating_mass_empty=None, design_payload_mass=None, tanks=None):
operating_mass_empty=None, design_payload_mass=None, tanks=None,
routing_dict=None, dict_ac_exchange=None):
"""Calculation of design fuel mass
Args:
......@@ -1377,14 +1415,14 @@ def calculate_design_fuel_mass(mission_information=None, maximum_takeoff_mass=No
# distribute the fuel along the tanks and save the informations per tank
# the refueling order is already saved in the sorted tanks
# = (preparation for the calculation of the refueling-cg-shift)
design_fuel_mass, design_fuel_mass_takeoff_per_tank = calculate_fuel_mass_properties(
tanks, design_fuel_mass)
design_fuel_mass, design_fuel_mass_per_tank = calculate_fuel_mass_properties(
tanks, design_fuel_mass, routing_dict, dict_ac_exchange)
design_fuel_mass_takeoff, design_fuel_mass_takeoff_per_tank = calculate_fuel_mass_properties(
tanks, design_fuel_mass_at_takeoff)
tanks, design_fuel_mass_at_takeoff, routing_dict, dict_ac_exchange)
design_fuel_mass_midflight, _ = calculate_fuel_mass_properties(
tanks, design_fuel_mass_midflight)
tanks, design_fuel_mass_midflight, routing_dict, dict_ac_exchange)
design_fuel_mass_landing_minimum, _ = calculate_fuel_mass_properties(
tanks, design_fuel_mass_landing_minimum)
tanks, design_fuel_mass_landing_minimum, routing_dict, dict_ac_exchange)
else:
contingency_fuel_factor = 0.05
alternate_fuel_factor = 0.1
......@@ -1404,16 +1442,17 @@ def calculate_design_fuel_mass(mission_information=None, maximum_takeoff_mass=No
design_fuel_mass_takeoff, design_fuel_mass_takeoff_per_tank = calculate_fuel_mass_properties(
tanks, design_fuel_mass_takeoff)
design_fuel_mass_midflight, _ = calculate_fuel_mass_properties(
tanks, design_fuel_mass_midflight)
tanks, design_fuel_mass_midflight, routing_dict, dict_ac_exchange)
design_fuel_mass_landing_minimum, _ = calculate_fuel_mass_properties(
tanks, design_fuel_mass_landing_minimum)
tanks, design_fuel_mass_landing_minimum, routing_dict, dict_ac_exchange)
design_fuel_mass = design_fuel_mass_at_takeoff
design_fuel_mass_per_tank = design_fuel_mass_takeoff_per_tank
return (design_fuel_mass_takeoff, design_fuel_mass_midflight, design_fuel_mass_landing_minimum,
design_fuel_mass_takeoff_per_tank, consumed_fuel_during_flight, design_fuel_mass)
design_fuel_mass_takeoff_per_tank, consumed_fuel_during_flight, design_fuel_mass, design_fuel_mass_per_tank)
def calculate_design_payload_mass(transport_task_data, fuselage_mass_properties):
def calculate_design_payload_mass(transport_task_data, fuselage_mass_properties, max_payload, runtime_output):
"""Design payload mass
Args:
......@@ -1427,9 +1466,17 @@ def calculate_design_payload_mass(transport_task_data, fuselage_mass_properties)
"./analysis/masses_cg_inertia/design_payload_mass")
design_payload_mass.mass = transport_task_data.combined_passenger_and_luggage_mass + \
transport_task_data.additional_cargo_mass
if design_payload_mass.mass > max_payload.mass:
runtime_output.critical(
"Design payload mass exceeds the maximum allowed payload mass!")
raise ValueError(
"Design payload mass exceeds the maximum allowed payload mass!")
# cargodeck + passenger_deck + additional cargo
design_payload_mass.center_of_gravity = fuselage_mass_properties.center_of_gravity
design_payload_mass.initialize_zero_inertia()
DEBUG(msg="The CG of the design payload is set at the fuselage CG.")
return design_payload_mass
......@@ -1473,8 +1520,8 @@ def cg_change_cargo_loading_front_back(initial_cg, initial_weight, x_coordinate_
cg_positions.append(initial_mac_position)
for row in range(len(x_coordinate_row)):
added_mass = transport_task_data.luggage_mass_per_passenger * \
transport_task_data.number_of_passengers / len(x_coordinate_row)
added_mass = (transport_task_data.luggage_mass_per_passenger * transport_task_data.number_of_passengers +
transport_task_data.additional_cargo_mass) / len(x_coordinate_row)
total_weight_new += added_mass
total_moment_change += added_mass * (x_coordinate_row[row])
......@@ -1574,6 +1621,7 @@ def calculate_maximum_payload_mass(maximum_structural_payload_mass, fuselage_acc
# Use accomodation center_of_gravity
maximum_payload_mass.center_of_gravity = fuselage_accomodation_mass_properties.center_of_gravity
maximum_payload_mass.initialize_zero_inertia()
DEBUG(msg="The CG of the maximum payload is set at the fuselage CG.")
return maximum_payload_mass
......@@ -1626,7 +1674,7 @@ def calculate_mtow_max_payload(ome, maximum_payload_mass, wing, mtom, LE, mac):
mtow_max_payload.center_of_gravity = (
ome.mass * ome.center_of_gravity['x'] + fuel * wing.center_of_gravity['x'] +
maximum_payload_mass.mass * maximum_payload_mass.center_of_gravity['x']) / mtow_max_payload.mass
# DEBUG(msg="Estimated CG (fuel CG set in wing CG)")
DEBUG(msg="Estimated CG of MTOM with max payload. (fuel CG set in wing CG, payload CG set in fuselage CG.)")
mtow_max_payload.cg_mac = (
mtow_max_payload.center_of_gravity - LE) / mac * 100.0
......@@ -1643,7 +1691,7 @@ def calculate_mtow_max_fuel(ome, fuel_mass, wing, fuselage, mtom, LE, mac):
mtow_max_fuel.mass = ome.mass + max_fuel + payload_mass
mtow_max_fuel.center_of_gravity = (ome.mass * ome.center_of_gravity['x'] + max_fuel * wing.center_of_gravity['x'] +
payload_mass * fuselage.center_of_gravity['x']) / mtow_max_fuel.mass
# DEBUG(msg="Estimated CG (fuel CG set in wing CG)")
DEBUG(msg="Estimated CG of MTOM with max fuel. (fuel CG set in wing CG, payload CG set in fuselage CG.)")
mtow_max_fuel.cg_mac = (mtow_max_fuel.center_of_gravity - LE) / mac * 100.0
return mtow_max_fuel
......@@ -1865,7 +1913,7 @@ def read_operator_items(ac_exchange_file, fuselage_length):
total_mass += operator_items[i].mass
fuselage_operator_items_mass = total_mass
i = i + 1
# DEBUG(msg="CGs of fuselage operator items missing in axml -> estimated here at 45% fuselage length")
DEBUG(msg="CGs of fuselage operator items estimated here at 45% fuselage length")
systems_operator_nodes = ac_exchange_file.findall(
"./component_design/systems/operator_items/operator_item[@ID]"
......
......@@ -28,9 +28,9 @@ def method_html_report(paths_and_names, routing_dict, data_dict, method_specific
# general aircraft masses
aircraft_general_masses = [
("Manufacturer Mass Empty", "MME",
("Manufacturer Empty Mass", "MEM",
data_dict["manufacturer_mass_empty"].mass),
("Operating Mass Empty", "OME",
("Operating Empty Mass", "OEM",
data_dict["operating_mass_empty"].mass),
("Maximum Takeoff Mass", "MTOM",
data_dict["maximum_takeoff_mass"].mass),
......@@ -49,6 +49,7 @@ def method_html_report(paths_and_names, routing_dict, data_dict, method_specific
("Maximum Landing Mass", "MLM",
data_dict["maximum_landing_mass"].mass),
("Design Payload Mass", "-", data_dict["design_payload_mass"].mass),
("Design Fuel Mass", "-", data_dict["design_fuel_mass"].mass),
("Design Fuel Mass Takeoff", "-",
data_dict["design_fuel_mass_takeoff"].mass),
("Design Fuel Mass Midflight", "-",
......
......@@ -75,9 +75,9 @@ def method_plot(paths_and_names, routing_dict, data_dict, method_specific_output
# Configure the plot
ax.set_ylabel('Mass (kg)', color='#ecf0f1')
ax.set_title('Operating Mass Empty and its Components', color='#ecf0f1')
ax.set_title('Operating Empty Mass and its Components', color='#ecf0f1')
ax.set_xticks(ind)
ax.set_xticklabels(['OME'] + components)
ax.set_xticklabels(['OEM'] + components)
# Manually add legend entries for structure components and others
handles, labels = ax.get_legend_handles_labels()
......@@ -119,11 +119,11 @@ def method_plot(paths_and_names, routing_dict, data_dict, method_specific_output
plt.plot(data_dict["Defueling CG change"],
data_dict["Defueling mass change"], 'm') # Defueling
legend_labels = ['Refueling', 'Pax from front', 'Pax from back', 'Cargo from front', 'Cargo from back',
'Defueling', 'OME', 'MTOM', 'Neutral point Ma=0,83',
'Defueling', 'OEM', 'MTOM', 'Neutral point Ma=0,83',
'Most fwd CG', 'Most aft CG', 'NP-5%']
else:
legend_labels = ['Refueling', 'Pax from front', 'Pax from back', 'Cargo from front', 'Cargo from back',
'OME', 'MTOM', 'Neutral point Ma=0,83',
'OEM', 'MTOM', 'Neutral point Ma=0,83',
'Most fwd CG', 'Most aft CG', 'NP-5%']
N = np.arange(data_dict["Most forward CG mac position"] - 5, max(
......@@ -184,7 +184,7 @@ def method_plot(paths_and_names, routing_dict, data_dict, method_specific_output
data_dict["design_mass"].mass, 's')
# Define legend labels and colors
legend_labels2 = ['OME', 'MZFM (max Payload)', 'max Payload + Fuel', 'max Fuel + Payload', 'max Fuel',
legend_labels2 = ['OEM', 'MZFM (max Payload)', 'max Payload + Fuel', 'max Fuel + Payload', 'max Fuel',
'Design Fuel + Design Payload']
legend = plt.legend(legend_labels2, loc='lower right', fontsize='small')
for text in legend.get_texts():
......
......@@ -73,6 +73,7 @@ def user_method_data_input_preparation(routing_dict):
"energy_carrier_density": [design_specification_path + 'energy_carriers/energy_carrier[@ID="0"]/density',
float],
"energy_carrier_type": [design_specification_path + 'energy_carriers/energy_carrier[@ID="0"]/type', str],
'tool_level': ['./analysis/masses_cg_inertia', 'tool_level'],
}
"""Module configuration file."""
......
......@@ -8,7 +8,7 @@
<value>../projects/</value>
</aircraft_exchange_file_directory>
<own_tool_level description="Specify the tool level of this tool">
<value>2</value>
<value>3</value>
</own_tool_level>
<console_output description="Selector to specify the console output. Selector: mode_0 (Off) / mode_1 (only out/err/warn) / mode_2 (1 + info) / mode_3 (2 + debug)">
<value>mode_1</value>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment