diff --git a/weight_and_balance_analysis/src/datapostprocessing.py b/weight_and_balance_analysis/src/datapostprocessing.py index 4f838785ae633af44a14e440e3305211bdfd93c1..5e6c652c1601b7b0cd4e7b6ef52879c39483c44c 100644 --- a/weight_and_balance_analysis/src/datapostprocessing.py +++ b/weight_and_balance_analysis/src/datapostprocessing.py @@ -1,6 +1,6 @@ # UNICADO - UNIversity Conceptual Aircraft Design and Optimization # -# Copyright (C) 2024 UNICADO consortium +# Copyright (C) 2025 UNICADO consortium # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/weight_and_balance_analysis/src/datapreprocessing.py b/weight_and_balance_analysis/src/datapreprocessing.py index d001a0d62ee21b66d72a8500ff6655630c2b6bb5..05da58a96ddbfa24f67904c06f2c43ccaf32eaa3 100644 --- a/weight_and_balance_analysis/src/datapreprocessing.py +++ b/weight_and_balance_analysis/src/datapreprocessing.py @@ -1,6 +1,6 @@ # UNICADO - UNIversity Conceptual Aircraft Design and Optimization # -# Copyright (C) 2024 UNICADO consortium +# Copyright (C) 2025 UNICADO consortium # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/weight_and_balance_analysis/src/readlayertext.py b/weight_and_balance_analysis/src/readlayertext.py index 64f43ddc6553b3fd2458320fcce6770329fb1270..df93bffe79b293c25f5fb68c2231da16627147ac 100644 --- a/weight_and_balance_analysis/src/readlayertext.py +++ b/weight_and_balance_analysis/src/readlayertext.py @@ -1,6 +1,6 @@ # UNICADO - UNIversity Conceptual Aircraft Design and Optimization # -# Copyright (C) 2024 UNICADO consortium +# Copyright (C) 2025 UNICADO consortium # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/weight_and_balance_analysis/src/tube_and_wing/standard/basic/acommodationIO.py b/weight_and_balance_analysis/src/tube_and_wing/standard/basic/acommodationIO.py index 32a45e596e6a8b29aa32c7abc3aab716b62fa675..a5013dbf09297c21cb3f6c976d36c4ae3da42cb0 100644 --- a/weight_and_balance_analysis/src/tube_and_wing/standard/basic/acommodationIO.py +++ b/weight_and_balance_analysis/src/tube_and_wing/standard/basic/acommodationIO.py @@ -1,6 +1,6 @@ # UNICADO - UNIversity Conceptual Aircraft Design and Optimization # -# Copyright (C) 2024 UNICADO consortium +# Copyright (C) 2025 UNICADO consortium # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/weight_and_balance_analysis/src/tube_and_wing/standard/basic/componentMassIO.py b/weight_and_balance_analysis/src/tube_and_wing/standard/basic/componentMassIO.py index b0c24713c48d7f4bb2acbcc3bc1f76ae9f0666d4..e5feffcc99fe7719e1004dc0e294964e9245c0a4 100644 --- a/weight_and_balance_analysis/src/tube_and_wing/standard/basic/componentMassIO.py +++ b/weight_and_balance_analysis/src/tube_and_wing/standard/basic/componentMassIO.py @@ -1,6 +1,6 @@ # UNICADO - UNIversity Conceptual Aircraft Design and Optimization # -# Copyright (C) 2024 UNICADO consortium +# Copyright (C) 2025 UNICADO consortium # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/weight_and_balance_analysis/src/tube_and_wing/standard/basic/massPropertiesIO.py b/weight_and_balance_analysis/src/tube_and_wing/standard/basic/massPropertiesIO.py index f0b878b036d2f2d040818909c52792c3a56ed84f..33730236b495f860047c6e432b3714f40d8af2dd 100644 --- a/weight_and_balance_analysis/src/tube_and_wing/standard/basic/massPropertiesIO.py +++ b/weight_and_balance_analysis/src/tube_and_wing/standard/basic/massPropertiesIO.py @@ -1,6 +1,6 @@ # UNICADO - UNIversity Conceptual Aircraft Design and Optimization # -# Copyright (C) 2024 UNICADO consortium +# Copyright (C) 2025 UNICADO consortium # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -118,12 +118,12 @@ class MassPropertiesIO: if name is not None: runtime_output.print(f"== {name} == ") - runtime_output.print(f"mass ... {self.mass}") + runtime_output.print(f"mass ... {self.mass:.4f}") for ax, value in self.center_of_gravity.items(): - runtime_output.print(f"{ax} ... {value}") + runtime_output.print(f"{ax} ... {value:.4f}") for component, value in self.inertia.items(): - runtime_output.print(f"{component} ... {value}") + runtime_output.print(f"{component} ... {value:.4f}") return self diff --git a/weight_and_balance_analysis/src/tube_and_wing/standard/basic/methodbasic.py b/weight_and_balance_analysis/src/tube_and_wing/standard/basic/methodbasic.py index edf7281d66a2277c1df130ca83268497b9e3595e..5639c900937cdcbf35976998e176a8415f3fc516 100644 --- a/weight_and_balance_analysis/src/tube_and_wing/standard/basic/methodbasic.py +++ b/weight_and_balance_analysis/src/tube_and_wing/standard/basic/methodbasic.py @@ -1,6 +1,6 @@ # UNICADO - UNIversity Conceptual Aircraft Design and Optimization # -# Copyright (C) 2024 UNICADO consortium +# Copyright (C) 2025 UNICADO consortium # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -63,7 +63,6 @@ def method_basic(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_confi # Data preparation and reading: ac_exchange_file = paths_and_names["root_of_aircraft_exchange_tree"] - aircraft_class = dict_mod_config["aircraft_class"] excel_file_path = paths_and_names['project_directory'] + \ '/' + "Seats_positions.xlsx" @@ -78,6 +77,10 @@ def method_basic(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_confi fuselage_length = geom2.measure.length(fuselage) wing_span = geom2.measure.span(wing) + nacelles, pylons, engines = read_propulsion_properties(ac_exchange_file) + aircraft_class = determine_ac_class( + runtime_output, dict_ac_exchange, len(engines)) + # Method selection area: # Select inertia method inertia_methods_available = { @@ -86,16 +89,7 @@ def method_basic(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_confi "mode_2": {"method": calculate_inertia_by_Raymer, "additional_parameters": [wing_span, fuselage_length, aircraft_class]}, } - # Select maximum landing mass method here - maximum_landing_mass_methods_available = { - "mode_0": {"method": calculate_maximum_landing_mass_by_default_method, - "additional_parameters": [ac_exchange_file]}, # noPep8 e501 - "mode_1": {"method": calculate_maximum_landing_mass_by_regression_rwth, - "additional_parameters": None} - } inertia_method = inertia_methods_available[dict_mod_config["mode_inertia_calculation"]] - maximum_landing_mass_method = maximum_landing_mass_methods_available[ - dict_mod_config["mode_maximum_landing_mass_calculation"]] # Select loading and active modes # ferry range or design mission refueling_mode = dict_mod_config["refueling_mode"] @@ -121,7 +115,6 @@ def method_basic(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_confi ac_exchange_file, dict_ac_exchange, runtime_output) tanks, sorted_tanks_defueling = read_tank_properties( ac_exchange_file, defueling_mode, runtime_output) - nacelles, pylons, engines = read_propulsion_properties(ac_exchange_file) maximum_takeoff_mass = read_maximum_takeoff_mass(ac_exchange_file) operator_items, fuselage_operator_items_mass, systems_operator_items_mass = read_operator_items( ac_exchange_file, fuselage_length) @@ -230,15 +223,9 @@ def method_basic(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_confi inertia_method).print("Design mass midflight") # Landing mass - if dict_mod_config["mode_maximum_landing_mass_calculation"] == "mode_0": - maximum_landing_mass = calculate_maximum_landing_mass_by_default_method( - mission_information, inertia_method, [ - operating_mass_empty, design_payload_mass, design_fuel_mass_minimum_landing], - maximum_landing_mass_method["additional_parameters"]).print("Maximum landing mass") - else: - runtime_output.print("Calculate MLM by RWTH regression ...") - maximum_landing_mass = calculate_maximum_landing_mass_by_regression_rwth( - maximum_takeoff_mass, inertia_method, main_components_mass_properties).print("Maximum landing mass") + runtime_output.print("Calculate MLM by RWTH regression ...") + maximum_landing_mass = calculate_maximum_landing_mass_by_regression_rwth( + maximum_takeoff_mass, inertia_method, main_components_mass_properties).print("Maximum landing mass") maximum_landing_mass.path_to_element = "./analysis/masses_cg_inertia/maximum_landing_mass" ################################################################################################################## @@ -412,9 +399,9 @@ def method_basic(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_confi most_aft_CG = most_aft_CG_over_mac * mac / 100 + x_leading_edge_mac runtime_output.print( - f"Most forward CG global position is {most_fwd_CG:.2f} m.") + f"Most forward CG global position is {most_fwd_CG:.4f} m.") runtime_output.print( - f"Most aft CG global position is {most_aft_CG:.2f} m.") + f"Most aft CG global position is {most_aft_CG:.4f} m.") # Determine corresponding mass for the most forward CG corresponding_mass_most_fwd_CG = None @@ -425,7 +412,7 @@ def method_basic(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_confi break runtime_output.print( - f"Corresponding mass most fwd CG is of {corresponding_mass_most_fwd_CG:.2f} kg.") + f"Corresponding mass most fwd CG is of {corresponding_mass_most_fwd_CG:.4f} kg.") # Determine corresponding mass for the most aft CG corresponding_mass_most_aft_CG = None @@ -436,7 +423,7 @@ def method_basic(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_confi break runtime_output.print( - f"Corresponding mass most aft CG is of {corresponding_mass_most_aft_CG:.2f} kg.") + f"Corresponding mass most aft CG is of {corresponding_mass_most_aft_CG:.4f} kg.") # Prepare the data to be written as nodes for acxml most_forward_mass = MassPropertiesIO( @@ -491,7 +478,7 @@ def method_basic(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_confi "Pylons": sum([pylon.mass for pylon in pylons if pylon is not None]) } - basic_output_dict = { + basic_output_dict_ini = { "structure_components": structure_masses, "operating_mass_empty": operating_mass_empty, "manufacturer_mass_empty": manufacturer_mass_empty, @@ -538,6 +525,8 @@ def method_basic(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_confi "Systems": systems } + basic_output_dict = format_floats(basic_output_dict_ini) + # 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 @@ -688,6 +677,9 @@ def calculate_inertia_by_Raymer(mass_properties=[], additional_parameters=[]): Returns: dict: inertia """ + import logging + runtime_output = logging.getLogger('module_logger') + mass_operation_empty = mass_properties[0] mass_payload_expected = mass_properties[1] mass_fuel_expected = mass_properties[2] @@ -701,16 +693,30 @@ def calculate_inertia_by_Raymer(mass_properties=[], additional_parameters=[]): # Dictionary of nondimensional radii of gyration for different aircraft classes radii_of_gyration = { - # "Single-engine prop": {"R_x": 0.25, "R_y": 0.38, "R_z": 0.39}, - # "Twin-engine prop": {"R_x": 0.30, "R_y": 0.40, "R_z": 0.44}, + # Single-engine prop + "prop_fuselage_1": {"R_x": 0.25, "R_y": 0.38, "R_z": 0.39}, + # Twin-engine prop + "prop_wing_2": {"R_x": 0.30, "R_y": 0.40, "R_z": 0.44}, # "Business jet twin": {"R_x": 0.30, "R_y": 0.34, "R_z": 0.39}, # "Twin turboprop transport": {"R_x": 0.22, "R_y": 0.33, "R_z": 0.38}, - "jet_fuselage_eng": {"R_x": 0.24, "R_y": 0.34, "R_z": 0.42}, - "jet_two_wing_eng": {"R_x": 0.23, "R_y": 0.33, "R_z": 0.45}, - "jet_four_wing_eng": {"R_x": 0.24, "R_y": 0.36, "R_z": 0.44}, - "blended_wing": {"R_x": 0.28, "R_y": 0.40, "R_z": 0.46}, + # jet_fuselage_eng + "fan_fuselage_2": {"R_x": 0.24, "R_y": 0.34, "R_z": 0.42}, + # jet_two_wing_eng + "fan_wing_2": {"R_x": 0.23, "R_y": 0.33, "R_z": 0.45}, + # jet_four_wing_eng + "fan_wing_4": {"R_x": 0.24, "R_y": 0.36, "R_z": 0.44}, + "blended_wing_body": {"R_x": 0.28, "R_y": 0.40, "R_z": 0.46}, } + if aircraft_class not in radii_of_gyration: + runtime_output.print( + f"Determined aircraft class for the selection of the Raymer's radii of gyration: {aircraft_class}.") + runtime_output.critical( + "The Raymer method is not working for this aircraft class. Please select another inertia calculation method!") + runtime_output.critical( + "No radiis of gyration implemented for this aircraft class. Values selected equal to jet_two_wing_eng") + aircraft_class = "fan_wing_2" + # Retrieve the nondimensional radii of gyration for the specified aircraft class radii = radii_of_gyration[aircraft_class] R_x = radii["R_x"] @@ -832,9 +838,9 @@ def calculate_operating_mass_empty(mass_properties, inertia_method, LE, mac): return operating_mass_empty -def calculate_maximum_landing_mass_by_default_method( +def calculate_landing_mass_by_default_method( mission_information=None, inertia_method=None, mass_properties=None, additional_parameters=[]): - """Calculate maximum landing mass by default method - OME - Design Payload - Reserve fuel + """Calculate landing mass by default method - OME - Design Payload - Reserve fuel Args: operating_mass_empty (float, optional): operating mass empty. Defaults to None. @@ -845,7 +851,7 @@ def calculate_maximum_landing_mass_by_default_method( float: maximum landing mass """ - maximum_landing_mass = MassPropertiesIO( + landing_mass = MassPropertiesIO( "./analysis/masses_cg_inertia/maximum_landing_mass/mass_properties") design_fuel_mass_trip = mission_information["trip_fuel_mass"] @@ -853,23 +859,23 @@ def calculate_maximum_landing_mass_by_default_method( # If mission information not existent - work with current landing mass if design_fuel_mass_trip is None or design_fuel_mass_mission is None: - maximum_landing_mass.read(additional_parameters[0]) - maximum_landing_mass.center_of_gravity = calculate_center_of_gravity( + landing_mass.read(additional_parameters[0]) + landing_mass.center_of_gravity = calculate_center_of_gravity( mass_properties) - maximum_landing_mass.inertia = inertia_method["method"]( - [maximum_landing_mass, MassPropertiesIO().initialize_zero(), + landing_mass.inertia = inertia_method["method"]( + [landing_mass, MassPropertiesIO().initialize_zero(), MassPropertiesIO().initialize_zero()], inertia_method["additional_parameters"]) - return maximum_landing_mass + return landing_mass if mission_information is not None: - maximum_landing_mass.mass = sum( + landing_mass.mass = sum( [element.mass for element in mass_properties]) - maximum_landing_mass.center_of_gravity = calculate_center_of_gravity( + landing_mass.center_of_gravity = calculate_center_of_gravity( mass_properties) - maximum_landing_mass.inertia = inertia_method["method"]([maximum_landing_mass, MassPropertiesIO( + landing_mass.inertia = inertia_method["method"]([landing_mass, MassPropertiesIO( ).initialize_zero(), MassPropertiesIO().initialize_zero()], inertia_method["additional_parameters"]) - return maximum_landing_mass + return landing_mass def calculate_maximum_landing_mass_by_regression_rwth(maximum_takeoff_mass, inertia_method, mass_properties): @@ -1006,39 +1012,6 @@ def calculate_cg_position_over_mac_defueling(initial_cg, initial_weight, fuel_ma return cg_positions, total_weight -def calculate_fuel_mass_properties_evenly_distributed(tanks, fuel_mass, runtime_output): - - # Divide fuel mass evenly on tanks - might be adaptable to inner and outer tanks - overall_fuel_mass_per_tank = fuel_mass / len(tanks) - fuel_mass_properties = MassPropertiesIO().initialize_zero() - fuel_mass_per_tank = [] - for empty_tank in tanks: - filled_tank_wo_structure = MassPropertiesIO().initialize_zero() - filled_tank_wo_structure.center_of_gravity = empty_tank.mass_properties.center_of_gravity - - if overall_fuel_mass_per_tank <= empty_tank.energy / empty_tank.gravimetric_density: - # if overall_fuel_mass_per_tank <= empty_tank.volume["value"] * empty_tank.fuel_density: - filled_tank_wo_structure.mass = overall_fuel_mass_per_tank - else: - runtime_output.critical( - "Fuel mass calculation failed ... tank to small") - raise ValueError("Fuel mass calculation failed ... tank to small") - # No adaption of Center of gravity or Inertia here - - filled_tank_wo_structure.moment_change = (filled_tank_wo_structure.mass * - filled_tank_wo_structure.center_of_gravity["x"]) - - fuel_mass_per_tank.append(filled_tank_wo_structure) - - fuel_mass_properties.mass = sum( - [filled_tank.mass for filled_tank in fuel_mass_per_tank]) - fuel_mass_properties.center_of_gravity = calculate_center_of_gravity( - fuel_mass_per_tank) - fuel_mass_properties.initialize_zero_inertia() - - return fuel_mass_properties, fuel_mass_per_tank - - def calculate_fuel_mass_properties(tanks, fuel_mass, routing_dict, dict_ac_exchange): """ Refuel tanks in the following priority order: @@ -1211,7 +1184,7 @@ def calculate_fuel_mass_properties(tanks, fuel_mass, routing_dict, dict_ac_excha # Check if there is still remaining fuel (should not happen unless there is a capacity error) if remaining_fuel > 0: if int(dict_ac_exchange['tool_level']) < int(routing_dict['tool_level']): - runtime_output.warning( + runtime_output.critical( "Fuel mass calculation failed ... Not enough capacity in tanks!") else: raise ValueError( @@ -1925,7 +1898,7 @@ def read_main_components(ac_exchange_file, dict_ac_exchange, runtime_output): f"./component_design/{component}/mass_properties") mass_properties[component].read(ac_exchange_file) runtime_output.print( - f"{component} - mass: {mass_properties[component].mass}") + f"{component} - mass: {mass_properties[component].mass:.4f}") return mass_properties @@ -2291,7 +2264,7 @@ def refueling(runtime_output, refueling_mode, **kwargs): for fuel in kwargs['maximum_fuel_mass_per_tank']: runtime_output.print( - f"Tank '{fuel.tank_location}' was filled with {fuel.mass:.2f} kg {fuel.energy_carrier}.") + f"Tank '{fuel.tank_location}' was filled with {fuel.mass:.4f} kg {fuel.energy_carrier}.") elif refueling_mode == "mode_0": runtime_output.print("REFUELING (design mission)...") cg_positions_over_mac_refueling, total_mass_refueling = \ @@ -2303,7 +2276,7 @@ def refueling(runtime_output, refueling_mode, **kwargs): for fuel in kwargs['design_fuel_mass_takeoff_per_tank']: runtime_output.print( - f"Tank '{fuel.tank_location}' was filled with {fuel.mass:.2f} kg {fuel.energy_carrier}.") + f"Tank '{fuel.tank_location}' was filled with {fuel.mass:.4f} kg {fuel.energy_carrier}.") else: runtime_output.critical("Invalid refueling mode specified") raise ValueError("Invalid refueling mode specified") @@ -2357,7 +2330,7 @@ def defueling(runtime_output, defueling_mode, **kwargs): for fuel in kwargs['defueling_mass_per_tank_ferry']: runtime_output.print( - f"From tank '{fuel.name}' was defueld {-fuel.mass:.2f} kg. Fuel left in tank: {fuel.fuel_left_in_tank:.2f} kg.") + f"From tank '{fuel.name}' was defueld {-fuel.mass:.4f} kg. Fuel left in tank: {fuel.fuel_left_in_tank:.4f} kg.") elif defueling_mode == "mode_0": cg_positions, total_mass = calculate_cg_position_over_mac_defueling( kwargs['starting_cg'], kwargs['starting_mass'], @@ -2365,8 +2338,39 @@ def defueling(runtime_output, defueling_mode, **kwargs): for fuel in kwargs['defueling_mass_per_tank_ferry']: runtime_output.print( - f"From tank '{fuel.name}' was defueld {-fuel.mass:.2f} kg. Fuel left in tank: {fuel.fuel_left_in_tank:.2f} kg.") + f"From tank '{fuel.name}' was defueld {-fuel.mass:.4f} kg. Fuel left in tank: {fuel.fuel_left_in_tank:.4f} kg.") else: runtime_output.critical("Invalid refueling mode specified") raise ValueError("Invalid refueling mode specified") return cg_positions, total_mass + + +def determine_ac_class(runtime_output, dict, nr): + + if dict["aircraft_type"] == "blended_wing_body": + ac_class = dict["aircraft_type"] + else: + # Select specific keys + selected_keys = ["propulsion_type", "propulsion_position"] + + # Concatenate only selected values + ac_class = "_".join( + dict[key] for key in selected_keys if key in dict) + "_" + str(nr) + + return ac_class + + +def format_floats(obj): + if isinstance(obj, dict): # If it's a dictionary, apply formatting recursively + return {key: format_floats(value) for key, value in obj.items()} + elif isinstance(obj, list): # If it's a list, format each item inside + return [format_floats(item) for item in obj] + elif isinstance(obj, float): # Format float values + return round(obj, 4) + elif hasattr(obj, "__dict__"): # If it's an object, format its attributes + for attr, value in obj.__dict__.items(): + # Recursively format attributes + setattr(obj, attr, format_floats(value)) + return obj # Return the modified object + else: # Keep other types (int, str, bool, etc.) unchanged + return obj diff --git a/weight_and_balance_analysis/src/tube_and_wing/standard/basic/propulsionIO.py b/weight_and_balance_analysis/src/tube_and_wing/standard/basic/propulsionIO.py index af2ed424d0300701ea366aeef58b9e4aa4663db4..dfe79b7c53bf0b0d8367ba69817710abcc87fb05 100644 --- a/weight_and_balance_analysis/src/tube_and_wing/standard/basic/propulsionIO.py +++ b/weight_and_balance_analysis/src/tube_and_wing/standard/basic/propulsionIO.py @@ -1,6 +1,6 @@ # UNICADO - UNIversity Conceptual Aircraft Design and Optimization # -# Copyright (C) 2024 UNICADO consortium +# Copyright (C) 2025 UNICADO consortium # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/weight_and_balance_analysis/src/tube_and_wing/standard/basic/tankIO.py b/weight_and_balance_analysis/src/tube_and_wing/standard/basic/tankIO.py index 6f421847599b5ebc649ecd4e8a45f16e64575802..a39b5251a80e90388d53a21c79e645589fd4d712 100644 --- a/weight_and_balance_analysis/src/tube_and_wing/standard/basic/tankIO.py +++ b/weight_and_balance_analysis/src/tube_and_wing/standard/basic/tankIO.py @@ -1,6 +1,6 @@ # UNICADO - UNIversity Conceptual Aircraft Design and Optimization # -# Copyright (C) 2024 UNICADO consortium +# Copyright (C) 2025 UNICADO consortium # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/weight_and_balance_analysis/src/tube_and_wing/standard/basic/transportTaskIO.py b/weight_and_balance_analysis/src/tube_and_wing/standard/basic/transportTaskIO.py index 4b5495795b91b5a03e4d568e402d1c6fcc29dc9d..f8a15fc99a32f3556cc2c867b34bb29c428a277d 100644 --- a/weight_and_balance_analysis/src/tube_and_wing/standard/basic/transportTaskIO.py +++ b/weight_and_balance_analysis/src/tube_and_wing/standard/basic/transportTaskIO.py @@ -1,6 +1,6 @@ # UNICADO - UNIversity Conceptual Aircraft Design and Optimization # -# Copyright (C) 2024 UNICADO consortium +# Copyright (C) 2025 UNICADO consortium # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/weight_and_balance_analysis/src/tube_and_wing/standard/general/methodhtmlreport.py b/weight_and_balance_analysis/src/tube_and_wing/standard/general/methodhtmlreport.py index a6765a90b9ead9e03a2120b2886d19ea39974bca..2fa87b68fcba81f6e98b2a9085f7688ce8be9702 100644 --- a/weight_and_balance_analysis/src/tube_and_wing/standard/general/methodhtmlreport.py +++ b/weight_and_balance_analysis/src/tube_and_wing/standard/general/methodhtmlreport.py @@ -1,6 +1,6 @@ # UNICADO - UNIversity Conceptual Aircraft Design and Optimization # -# Copyright (C) 2024 UNICADO consortium +# Copyright (C) 2025 UNICADO consortium # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -52,6 +52,8 @@ def method_html_report(paths_and_names, routing_dict, data_dict, method_specific data_dict["manufacturer_mass_empty"].mass), ("Operating Empty Mass", "OEM", data_dict["operating_mass_empty"].mass), + ("Maximum Ramp Mass", "MRM", + data_dict["design_mass"].mass), ("Maximum Takeoff Mass", "MTOM", data_dict["maximum_takeoff_mass"].mass), ("Maximum Landing Mass", "MLM", @@ -66,10 +68,6 @@ def method_html_report(paths_and_names, routing_dict, data_dict, method_specific ] aircraft_design_masses = [ - ("Design Mass (Mission)", "-", - data_dict["design_mass"].mass), - ("Design Mass (Takeoff)", "-", - data_dict["design_mass_takeoff"].mass), ("Design Payload Mass", "-", data_dict["design_payload_mass"].mass), ("Design Fuel Mass (Mission)", "-", data_dict["mission_fuel_mass"].mass), diff --git a/weight_and_balance_analysis/src/tube_and_wing/standard/general/methodplot.py b/weight_and_balance_analysis/src/tube_and_wing/standard/general/methodplot.py index 6616338cde7772e4456290be0bea4841b84a0564..cfbca0deed2cd21be5087f6410f3addd6a99c617 100644 --- a/weight_and_balance_analysis/src/tube_and_wing/standard/general/methodplot.py +++ b/weight_and_balance_analysis/src/tube_and_wing/standard/general/methodplot.py @@ -1,6 +1,6 @@ # UNICADO - UNIversity Conceptual Aircraft Design and Optimization # -# Copyright (C) 2024 UNICADO consortium +# Copyright (C) 2025 UNICADO consortium # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -69,7 +69,7 @@ def method_plot(paths_and_names, routing_dict, data_dict, method_specific_output plt.rcParams['ytick.color'] = '#ecf0f1' # Plotting - fig, ax = plt.subplots(figsize=(8, 5), facecolor='#494f4f') + fig, ax1 = plt.subplots(figsize=(8, 5), facecolor='#494f4f') # Bar positions ind = np.arange(len(components) + 1) # +1 for the total OEM bar @@ -77,42 +77,56 @@ def method_plot(paths_and_names, routing_dict, data_dict, method_specific_output # Create stacked bar for the total OEM bottom = 0 for i in range(len(components)): - ax.bar(0, component_values[i], bottom=bottom, - width=0.5, color=colors[i], label=components[i]) + ax1.bar(0, component_values[i], bottom=bottom, + width=0.5, color=colors[i], label=components[i]) bottom += component_values[i] # Create bars for each individual component (not stacked) for i in range(len(components)): - ax.bar(ind[i + 1], component_values[i], width=0.5, - color=colors[i], label=f'{components[i]}') + ax1.bar(ind[i + 1], component_values[i], width=0.5, + color=colors[i], label=f'{components[i]}') # Create stacked bar for the structure components bottom = 0 for i in range(len(structure_components)): - ax.bar(1, structure_component_values[i], bottom=bottom, - width=0.5, color=colors_structure[i], label=structure_components[i]) + ax1.bar(1, structure_component_values[i], bottom=bottom, + width=0.5, color=colors_structure[i], label=structure_components[i]) bottom += structure_component_values[i] # Configure the plot - ax.set_ylabel('Mass (kg)', color='#ecf0f1') - ax.set_title('Operating Empty Mass and its Components', color='#ecf0f1') - ax.set_xticks(ind) - ax.set_xticklabels(['OEM'] + components) + ax1.set_ylabel('Mass (kg)', color='#ecf0f1') + ax1.set_title('Operating Empty Mass and its Components', color='#ecf0f1') + ax1.set_xticks(ind) + ax1.set_xticklabels(['OEM'] + components) + + # Create a secondary y-axis for percentages + ax2 = ax1.twinx() + ax2.set_ylim(ax1.get_ylim()) # Match the y-axis limits + ax2.set_ylabel('Percentage of OEM (%)', color='#ecf0f1') + + # Generate percentage labels for the right y-axis + yticks_mass = ax1.get_yticks() # Get mass values from the left y-axis + yticks_percentage = [(ytick / ome) * 100 for ytick in yticks_mass] + ax2.set_yticklabels( + [f"{ytick:.0f}%" for ytick in yticks_percentage], color='#ecf0f1') # Manually add legend entries for structure components and others - handles, labels = ax.get_legend_handles_labels() + handles, labels = ax1.get_legend_handles_labels() legend_labels = components + structure_components handles = [plt.Rectangle((0, 0), 1, 1, color=color) for color in (colors + colors_structure)] - legend = ax.legend(handles, legend_labels, - loc='upper right', fontsize='small') + legend = ax1.legend(handles, legend_labels, + loc='upper right', fontsize='small') # Set the text in the legend to black for text in legend.get_texts(): text.set_color('black') # Adjusting the plot's edge color - for spine in ax.spines.values(): + for spine in ax1.spines.values(): + spine.set_edgecolor('#ecf0f1') + + for spine in ax2.spines.values(): spine.set_edgecolor('#ecf0f1') # Save the figure diff --git a/weight_and_balance_analysis/src/tube_and_wing/standard/general/methodtexoutput.py b/weight_and_balance_analysis/src/tube_and_wing/standard/general/methodtexoutput.py index a1646acb0a61238a9ad4a4ae9e51b18007537cb5..ec971952ae05d3f223ff280edd74948019e63a5e 100644 --- a/weight_and_balance_analysis/src/tube_and_wing/standard/general/methodtexoutput.py +++ b/weight_and_balance_analysis/src/tube_and_wing/standard/general/methodtexoutput.py @@ -1,6 +1,6 @@ # UNICADO - UNIversity Conceptual Aircraft Design and Optimization # -# Copyright (C) 2024 UNICADO consortium +# Copyright (C) 2025 UNICADO consortium # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/weight_and_balance_analysis/src/tube_and_wing/standard/general/methodxmlexport.py b/weight_and_balance_analysis/src/tube_and_wing/standard/general/methodxmlexport.py index 43b00c5458488c1196a4ba2d07fb61a03d808cf0..70f74837e4209ef9ef2fcfefbb0ba1a5aa8c2c1f 100644 --- a/weight_and_balance_analysis/src/tube_and_wing/standard/general/methodxmlexport.py +++ b/weight_and_balance_analysis/src/tube_and_wing/standard/general/methodxmlexport.py @@ -1,6 +1,6 @@ # UNICADO - UNIversity Conceptual Aircraft Design and Optimization # -# Copyright (C) 2024 UNICADO consortium +# Copyright (C) 2025 UNICADO consortium # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/weight_and_balance_analysis/src/tube_and_wing/standard/test_basic/test_IO.py b/weight_and_balance_analysis/src/tube_and_wing/standard/test_basic/test_IO.py index 847a837727d5b29e0565f889ea26cb59e954cf1c..454d7f2cfbb45f51c110c76468d8bd3ab0708609 100644 --- a/weight_and_balance_analysis/src/tube_and_wing/standard/test_basic/test_IO.py +++ b/weight_and_balance_analysis/src/tube_and_wing/standard/test_basic/test_IO.py @@ -1,6 +1,6 @@ # UNICADO - UNIversity Conceptual Aircraft Design and Optimization # -# Copyright (C) 2024 UNICADO consortium +# Copyright (C) 2025 UNICADO consortium # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/weight_and_balance_analysis/src/tube_and_wing/standard/test_basic/test_methodbasic.py b/weight_and_balance_analysis/src/tube_and_wing/standard/test_basic/test_methodbasic.py index 6053d2f31838d76f71dbac328db15b61beb2e557..3052b7cbbad617820e5a8f21967c2ac77633a2dc 100644 --- a/weight_and_balance_analysis/src/tube_and_wing/standard/test_basic/test_methodbasic.py +++ b/weight_and_balance_analysis/src/tube_and_wing/standard/test_basic/test_methodbasic.py @@ -1,6 +1,6 @@ # UNICADO - UNIversity Conceptual Aircraft Design and Optimization # -# Copyright (C) 2024 UNICADO consortium +# Copyright (C) 2025 UNICADO consortium # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/weight_and_balance_analysis/src/tube_and_wing/standard/usermethoddatapreparation.py b/weight_and_balance_analysis/src/tube_and_wing/standard/usermethoddatapreparation.py index 7babdb7762f6fef6ef17a70c22fa503efd25313c..cb7a568cd7082bb5680e851bc6123ea474e6554c 100644 --- a/weight_and_balance_analysis/src/tube_and_wing/standard/usermethoddatapreparation.py +++ b/weight_and_balance_analysis/src/tube_and_wing/standard/usermethoddatapreparation.py @@ -1,6 +1,6 @@ # UNICADO - UNIversity Conceptual Aircraft Design and Optimization # -# Copyright (C) 2024 UNICADO consortium +# Copyright (C) 2025 UNICADO consortium # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -62,6 +62,8 @@ def user_method_data_input_preparation(routing_dict): "x_wing_globlal_position": [component_design_path + 'wing/position/x', float], "x_aircraft_neutral_point": [aero_analysis_path + 'neutral_point/x', float], "aircraft_type": [design_specification_path + "configuration/configuration_type", str], + "propulsion_type": [design_specification_path + "propulsion/propulsor/type", str], + "propulsion_position": [design_specification_path + "propulsion/propulsor/position/parent_component", str], "loaded_mission_energy": [ mission_analysis_path + 'loaded_mission_energy/mission_energy[@ID="0"]/consumed_energy', float], "loaded_mission_energy_carrier": [ @@ -99,9 +101,6 @@ def user_method_data_input_preparation(routing_dict): module_settings_path = f"./program_settings/tube_and_wing/standard/{routing_dict['user_layer']}/" general_data_to_extract_from_module_configuration_dict = { "mode_inertia_calculation": [module_settings_path + "calculation_methods/inertia/method", str], - "aircraft_class": [module_settings_path + "calculation_methods/aircraft_type", str], - "mode_maximum_landing_mass_calculation": [module_settings_path + - "calculation_methods/maximum_landing_mass/method", str], "refueling_mode": [module_settings_path + "calculation_methods/refueling_mode/method", str], "defueling_mode": [module_settings_path + "calculation_methods/defueling_mode/method", str], "passengers_boarding_mode": [module_settings_path + "calculation_methods/passengers_boarding_mode/method", str], diff --git a/weight_and_balance_analysis/weight_and_balance_analysis.py b/weight_and_balance_analysis/weight_and_balance_analysis.py index 559a3d1abd540f8552d1fc379a75b35c7c8c8d5e..917b06438c4ffa0fca7e43ed2931ce4b141a8e63 100644 --- a/weight_and_balance_analysis/weight_and_balance_analysis.py +++ b/weight_and_balance_analysis/weight_and_balance_analysis.py @@ -1,6 +1,6 @@ # UNICADO - UNIversity Conceptual Aircraft Design and Optimization # -# Copyright (C) 2024 UNICADO consortium +# Copyright (C) 2025 UNICADO consortium # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/weight_and_balance_analysis/weight_and_balance_analysis_conf.xml b/weight_and_balance_analysis/weight_and_balance_analysis_conf.xml index bde2e2af4832162087304d02a6c1486ec92dd6aa..f07f1416a23e1935575b3fd8eb9f46ae894d3946 100644 --- a/weight_and_balance_analysis/weight_and_balance_analysis_conf.xml +++ b/weight_and_balance_analysis/weight_and_balance_analysis_conf.xml @@ -67,14 +67,6 @@ <value>mode_2</value> </method> </inertia> - <aircraft_type description="Aircraft configuration for determination of the nondimensional radii of gyration by Raymer. Selector: blended_wing / jet_fuselage_eng / jet_two_wing_eng / jet_four_wing_eng"> - <value>jet_two_wing_eng</value> - </aircraft_type> - <maximum_landing_mass description="Selector for the calculation method of the maximum landing mass. Selector: mode_0 (OME + Payload + Reserve_fuel) / mode_1 (by_regression_RWTH)"> - <method description="selected method"> - <value>mode_1</value> - </method> - </maximum_landing_mass> <refueling_mode description="Selector to specify if refueling should be done for the design or ferry range mission. Selector: mode_0 (design mission) / mode_1 (ferry range)"> <method description="selected method"> <value>mode_0</value>