diff --git a/tank_design/CMakeLists.txt b/tank_design/CMakeLists.txt index e4918e7444ef4c4d1a92ed2c7765eccd2d34360b..de33827efac3873b97ac30b0c9dced5a970c2c03 100644 --- a/tank_design/CMakeLists.txt +++ b/tank_design/CMakeLists.txt @@ -43,6 +43,9 @@ list(APPEND PYINSTALLER_OPTIONS "--add-data=${CMAKE_CURRENT_LIST_DIR}/src${DATA_ list(APPEND PYINSTALLER_OPTIONS "--collect-all=matplotlib") list(APPEND PYINSTALLER_OPTIONS "--collect-all=pycoordinatesystemconversion") +list(APPEND PYINSTALLER_OPTIONS "--collect-all=pyunitconversion") +list(APPEND PYINSTALLER_OPTIONS "--collect-all=pyaircraftgeometry2") +list(APPEND PYINSTALLER_OPTIONS "--collect-all=pyaixml") # Add the target which compiles the executable using pyinstaller add_custom_target(${MODULE_NAME} ALL diff --git a/tank_design/src/datapostprocessing.py b/tank_design/src/datapostprocessing.py index 597da574ba1202144f41b515c637d980148683cc..a60425f3307990b5492f6c33af8fdfa0dbe3613a 100644 --- a/tank_design/src/datapostprocessing.py +++ b/tank_design/src/datapostprocessing.py @@ -1,525 +1,529 @@ -"""Module providing functions for data postprocessing.""" -# Import standard modules. - -# Import own modules. -from datapostprocessingmodule import method_data_postprocessing -from datapostprocessingmodule import write_key_data_to_aircraft_exchange_file -from datapostprocessingmodule import prepare_element_tree_for_module_key_parameter - - -def data_postprocessing(paths_and_names, routing_dict, data_dict, runtime_output): - """Perform data postprocessing and write data to an aircraft exchange file. - - This function is responsible for data postprocessing that involves the following steps: - (1) Data preparation: The module manager prepares a list containing all paths to the key parameters that must - be written to the aircraft exchange file. - (2) Write data to the aircraft exchange file: The results of the module execution are passed on to the function - responsible for properly writing the data to the aircraft exchange file. - (3) Method-specific data postprocessing: User-defined method-specific postprocessing is conducted as needed. - - :param dict paths_and_names: Dictionary containing system paths and ElementTrees - :param dict routing_dict: Dictionary containing routing parameters - :param dict data_dict: Dictionary containing the result of the module execution (direct operating cost estimation) - :param logging.Logger runtime_output: Logging object used for capturing log messages in the module - :return: None - """ - - """Data preparation.""" - # Changes to this list are the sole responsibility of the module manager! - paths_to_key_parameters_list = [ - './component_design/tank/position/x', - './component_design/tank/position/y', - './component_design/tank/position/z', - './component_design/tank/mass_properties/mass', - './component_design/tank/mass_properties/inertia/j_xx', - './component_design/tank/mass_properties/inertia/j_yy', - './component_design/tank/mass_properties/inertia/j_zz', - './component_design/tank/mass_properties/inertia/j_xy', - './component_design/tank/mass_properties/inertia/j_xz', - './component_design/tank/mass_properties/inertia/j_yx', - './component_design/tank/mass_properties/inertia/j_yz', - './component_design/tank/mass_properties/inertia/j_zx', - './component_design/tank/mass_properties/inertia/j_zy', - './component_design/tank/mass_properties/center_of_gravity/x', - './component_design/tank/mass_properties/center_of_gravity/y', - './component_design/tank/mass_properties/center_of_gravity/z', - './component_design/tank/specific/additional_fuselage_length', - './component_design/tank/specific/tank[@ID="0"]/name', - './component_design/tank/specific/tank[@ID="0"]/designator', - './component_design/tank/specific/tank[@ID="0"]/position/x', - './component_design/tank/specific/tank[@ID="0"]/position/y', - './component_design/tank/specific/tank[@ID="0"]/position/z', - './component_design/tank/specific/tank[@ID="0"]/direction/x', - './component_design/tank/specific/tank[@ID="0"]/direction/y', - './component_design/tank/specific/tank[@ID="0"]/direction/z', - './component_design/tank/specific/tank[@ID="0"]/mass_properties/mass', - './component_design/tank/specific/tank[@ID="0"]/mass_properties/inertia/j_xx', - './component_design/tank/specific/tank[@ID="0"]/mass_properties/inertia/j_yy', - './component_design/tank/specific/tank[@ID="0"]/mass_properties/inertia/j_zz', - './component_design/tank/specific/tank[@ID="0"]/mass_properties/inertia/j_xy', - './component_design/tank/specific/tank[@ID="0"]/mass_properties/inertia/j_xz', - './component_design/tank/specific/tank[@ID="0"]/mass_properties/inertia/j_yx', - './component_design/tank/specific/tank[@ID="0"]/mass_properties/inertia/j_yz', - './component_design/tank/specific/tank[@ID="0"]/mass_properties/inertia/j_zx', - './component_design/tank/specific/tank[@ID="0"]/mass_properties/inertia/j_zy', - './component_design/tank/specific/tank[@ID="0"]/mass_properties/center_of_gravity/x', - './component_design/tank/specific/tank[@ID="0"]/mass_properties/center_of_gravity/y', - './component_design/tank/specific/tank[@ID="0"]/mass_properties/center_of_gravity/z', - './component_design/tank/specific/tank[@ID="0"]/maximum_energy_capacity', - './component_design/tank/specific/tank[@ID="0"]/mass_properties/centroid/x', - './component_design/tank/specific/tank[@ID="0"]/mass_properties/centroid/y', - './component_design/tank/specific/tank[@ID="0"]/mass_properties/centroid/z', - './component_design/tank/specific/tank[@ID="0"]/geometry/cross_section[@ID="0"]/name', - './component_design/tank/specific/tank[@ID="0"]/geometry/cross_section[@ID="0"]/position/x', - './component_design/tank/specific/tank[@ID="0"]/geometry/cross_section[@ID="0"]/position/y', - './component_design/tank/specific/tank[@ID="0"]/geometry/cross_section[@ID="0"]/position/z', - './component_design/tank/specific/tank[@ID="0"]/geometry/cross_section[@ID="0"]/shape', - './component_design/tank/specific/tank[@ID="0"]/geometry/cross_section[@ID="0"]/height', - './component_design/tank/specific/tank[@ID="0"]/geometry/cross_section[@ID="0"]/width', - './component_design/tank/specific/tank[@ID="0"]/geometry/cross_section[@ID="0"]/length' - ] - module_key_parameters_dict = { - 'component_design': { - 'tank': { - 'attributes': { - 'description': 'Description of aircraft tanks.', - 'tool_level': '0'}, - 'position': { - 'attributes': {'description': - 'Tank reference point, position of the tanks with regard to the global reference ' - 'point.'}, - 'x': { - 'attributes': {'description': 'Distance between the foremost tank end and the global ' - 'reference point in x-direction.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '-80', - 'upper_boundary': '80'}, - 'y': { - 'attributes': {'description': 'Distance between the foremost tank end and the global ' - 'reference point in y-direction.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '-40', - 'upper_boundary': '40'}, - 'z': { - 'attributes': {'description': 'Distance between the foremost middle tank end and the global ' - 'reference point in z-direction.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '-5', - 'upper_boundary': '5'}, - }, - 'mass_properties': { - 'attributes': { - 'description': 'Mass properties of all tanks.'}, - 'mass': { - 'attributes': {'description': 'Total tank mass.'}, - 'value': '0', - 'unit': 'kg', - 'lower_boundary': '0', - 'upper_boundary': 'inf'}, - 'inertia': { - 'attributes': { - 'description': 'Inertia of all tanks with regard to the total center of gravity.'}, - 'j_xx': { - 'attributes': { - 'description': 'Inertia of all tanks in x.'}, - 'value': '0', - 'unit': 'kgm^2', - 'lower_boundary': '-inf', - 'upper_boundary': 'inf'}, - 'j_yy': { - 'attributes': { - 'description': 'Inertia of all tanks in y.'}, - 'value': '0', - 'unit': 'kgm^2', - 'lower_boundary': '-inf', - 'upper_boundary': 'inf'}, - 'j_zz': { - 'attributes': { - 'description': 'Inertia of all tanks in z.'}, - 'value': '0', - 'unit': 'kgm^2', - 'lower_boundary': '-inf', - 'upper_boundary': 'inf'}, - 'j_xy': { - 'attributes': { - 'description': 'Inertia of all tanks in xy.'}, - 'value': '0', - 'unit': 'kgm^2', - 'lower_boundary': '-inf', - 'upper_boundary': 'inf'}, - 'j_xz': { - 'attributes': { - 'description': 'Inertia of all tanks in xz.'}, - 'value': '0', - 'unit': 'kgm^2', - 'lower_boundary': '-inf', - 'upper_boundary': 'inf'}, - 'j_yx': { - 'attributes': { - 'description': 'Inertia of all tanks in yx.'}, - 'value': '0', - 'unit': 'kgm^2', - 'lower_boundary': '-inf', - 'upper_boundary': 'inf'}, - 'j_yz': { - 'attributes': { - 'description': 'Inertia of all tanks in yz.'}, - 'value': '0', - 'unit': 'kgm^2', - 'lower_boundary': '-inf', - 'upper_boundary': 'inf'}, - 'j_zx': { - 'attributes': { - 'description': 'Inertia of all tanks in zx.'}, - 'value': '0', - 'unit': 'kgm^2', - 'lower_boundary': '-inf', - 'upper_boundary': 'inf'}, - 'j_zy': { - 'attributes': { - 'description': 'Inertia of all tanks in zy.'}, - 'value': '0', - 'unit': 'kgm^2', - 'lower_boundary': '-inf', - 'upper_boundary': 'inf'}, - }, - 'center_of_gravity': { - 'attributes': { - 'description': 'Center of gravity of all tanks.'}, - 'x': { - 'attributes': { - 'description': 'Center of gravity in x-direction with regard to the global reference ' - 'point.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '-80', - 'upper_boundary': '80'}, - 'y': { - 'attributes': { - 'description': 'Center of gravity in y-direction with regard to the global reference ' - 'point.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '-40', - 'upper_boundary': '40'}, - 'z': { - 'attributes': { - 'description': 'Center of gravity in z-direction with regard to the global reference ' - 'point.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '-5', - 'upper_boundary': '5'} - } - }, - 'specific': { - 'attributes': { - 'description': 'Tank design specific output parameter.'}, - 'tank': { - 'attributes': { - 'ID': '0', - 'description': 'Description of one tank.'}, - 'name': { - 'attributes': { - 'description': 'Name of the tank.'}, - 'value': 'tank_0'}, - 'designator': { - 'attributes': { - 'description': 'Designator of the tank.'}, - 'value': 'placeholder_designator'}, - 'position': { - 'attributes': { - 'description': 'Local reference point, position of one tank wrt. the tank reference point.'}, - 'x': { - 'attributes': { - 'description': 'Distance between the foremost tank end of one tank and the tank ' - 'reference point in x-direction.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '-80', - 'upper_boundary': '80'}, - 'y': { - 'attributes': { - 'description': 'Distance between the foremost tank end of one tank and the tank ' - 'reference point in y-direction.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '-40', - 'upper_boundary': '40'}, - 'z': { - 'attributes': { - 'description': 'Distance between the foremost tank end of one tank and the tank ' - 'reference point in z-direction.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '-5', - 'upper_boundary': '5'} - }, - 'direction': { - 'attributes': { - 'description': 'Unit vector according to global coordinate system for direction ' - 'applied at position.'}, - 'x': { - 'attributes': { - 'description': 'X component of the unit vector.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '0', - 'upper_boundary': '1'}, - 'y': { - 'attributes': { - 'description': 'Y component of the unit vector.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '0', - 'upper_boundary': '1'}, - 'z': { - 'attributes': { - 'description': 'Z component of the unit vector.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '0', - 'upper_boundary': '1'} - }, - 'mass_properties': { - 'attributes': { - 'description': 'Mass properties of one tank.'}, - 'mass': { - 'attributes': {'description': 'Total dry mass of one tank.'}, - 'value': '0', - 'unit': 'kg', - 'lower_boundary': '0', - 'upper_boundary': 'inf'}, - 'inertia': { - 'attributes': { - 'description': 'Inertia of one tank with regard to its center of gravity.'}, - 'j_xx': { - 'attributes': { - 'description': 'Inertia of the of one tank in x.'}, - 'value': '0', - 'unit': 'kgm^2', - 'lower_boundary': '-inf', - 'upper_boundary': 'inf'}, - 'j_yy': { - 'attributes': { - 'description': 'Inertia of the of one tank in y.'}, - 'value': '0', - 'unit': 'kgm^2', - 'lower_boundary': '-inf', - 'upper_boundary': 'inf'}, - 'j_zz': { - 'attributes': { - 'description': 'Inertia of the of one tank in z.'}, - 'value': '0', - 'unit': 'kgm^2', - 'lower_boundary': '-inf', - 'upper_boundary': 'inf'}, - 'j_xy': { - 'attributes': { - 'description': 'Inertia of one tank in xy.'}, - 'value': '0', - 'unit': 'kgm^2', - 'lower_boundary': '-inf', - 'upper_boundary': 'inf'}, - 'j_xz': { - 'attributes': { - 'description': 'Inertia of one tank in xz.'}, - 'value': '0', - 'unit': 'kgm^2', - 'lower_boundary': '-inf', - 'upper_boundary': 'inf'}, - 'j_yx': { - 'attributes': { - 'description': 'Inertia of one tank in yx.'}, - 'value': '0', - 'unit': 'kgm^2', - 'lower_boundary': '-inf', - 'upper_boundary': 'inf'}, - 'j_yz': { - 'attributes': { - 'description': 'Inertia of one tank in yz.'}, - 'value': '0', - 'unit': 'kgm^2', - 'lower_boundary': '-inf', - 'upper_boundary': 'inf'}, - 'j_zx': { - 'attributes': { - 'description': 'Inertia of one tank in zx.'}, - 'value': '0', - 'unit': 'kgm^2', - 'lower_boundary': '-inf', - 'upper_boundary': 'inf'}, - 'j_zy': { - 'attributes': { - 'description': 'Inertia of one tank in zy.'}, - 'value': '0', - 'unit': 'kgm^2', - 'lower_boundary': '-inf', - 'upper_boundary': 'inf'}}, - 'center_of_gravity': { - 'attributes': {'description': 'Center of gravity of one tank.'}, - 'x': { - 'attributes': { - 'description': 'Center of gravity in x-direction with regard to the global ' - 'reference point.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '-80', - 'upper_boundary': '80'}, - 'y': { - 'attributes': { - 'description': 'Center of gravity in y-direction with regard to the global ' - 'reference point.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '-40', - 'upper_boundary': '40'}, - 'z': { - 'attributes': { - 'description': 'Center of gravity in z-direction with regard to the global ' - 'reference point.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '-5', - 'upper_boundary': '5'} - }, - 'centroid': { - 'attributes': {'description': 'Centroid of one tank.'}, - 'x': { - 'attributes': { - 'description': 'Centroid in x-direction with regard to the local ' - 'reference point.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '-80', - 'upper_boundary': '80'}, - 'y': { - 'attributes': { - 'description': 'Centroid in y-direction with regard to the local ' - 'reference point.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '-40', - 'upper_boundary': '40'}, - 'z': { - 'attributes': { - 'description': 'Centroid in z-direction with regard to the local ' - 'reference point.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '-5', - 'upper_boundary': '5'} - } - }, - 'maximum_energy_capacity': { - 'attributes': {'description': 'Maximum energy capacity of one tank.'}, - 'value': '0', - 'unit': 'J', - 'lower_boundary': '0', - 'upper_boundary': 'inf'}, - 'geometry': { - 'attributes': {'description': 'Geometrical description of one tank.'}, - 'cross_section': { - 'attributes': { - 'ID': '0', - 'description': 'Geometrical description of one tank cross section.'}, - 'name': { - 'attributes': { - 'description': 'Name of tank cross section.'}, - 'value': 'tank_section_0'}, - 'position': { - 'attributes': { - 'description': 'Position of tank cross section with regard to the local ' - 'reference point.'}, - 'x': { - 'attributes': { - 'description': 'Distance between the tank cross section and the local ' - 'reference point in x-direction.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '-80', - 'upper_boundary': '80'}, - 'y': { - 'attributes': { - 'description': 'Distance between the tank cross section and the local ' - 'reference point in y-direction.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '-40', - 'upper_boundary': '40'}, - 'z': { - 'attributes': { - 'description': 'Distance between the tank cross section and the local ' - 'reference point in z-direction.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '-5', - 'upper_boundary': '5'}}, - 'shape': { - 'attributes': { - 'description': 'Description of the shape of the cross section (circular, ' - 'rectangular, elliptical).'}, - 'value': 'misc'}, - 'height': { - 'attributes': { - 'description': 'Height of the cross section.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '0', - 'upper_boundary': 'inf'}, - 'width': { - 'attributes': { - 'description': 'Width of the cross section.'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '0', - 'upper_boundary': 'inf'}, - 'length': { - 'attributes': { - 'description': 'Length of the cross section (if length greater than 0: curved ' - 'cross section, e.g., dashed tank end cap).'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '0', - 'upper_boundary': 'inf'} - } - } - }, - 'additional_fuselage_length': { - 'attributes': { - 'description': 'Additional fuselage length (if smaller than 0: shrink ' - 'fuselage, if greater than 0: stretch fuselage).'}, - 'value': '0', - 'unit': 'm', - 'lower_boundary': '0', - 'upper_boundary': 'inf' - } - } - } - } - } - - paths_and_names = prepare_element_tree_for_module_key_parameter(paths_and_names, module_key_parameters_dict) - - # Run 'user_method_data_output_preparation' from 'usermethoddatapreparation.py'. - 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'] - - """Write data to aircraft exchange file.""" - # Extract root and path to aircraft exchange file. - root_of_aircraft_exchange_tree = paths_and_names['root_of_aircraft_exchange_tree'] - path_to_aircraft_exchange_file = paths_and_names['path_to_aircraft_exchange_file'] - # Write key data to aircraft exchange file. - write_key_data_to_aircraft_exchange_file(root_of_aircraft_exchange_tree, path_to_aircraft_exchange_file, - paths_to_key_parameters_list, key_output_dict, tool_level, runtime_output) - - """Method-specific postprocessing.""" - # Run 'method_data_postprocessing' from 'datapostprocessingmodule'. - data_dict['module_key_parameters_dict'] = module_key_parameters_dict - data_dict['paths_to_key_parameters_list'] = paths_to_key_parameters_list - method_data_postprocessing(paths_and_names, routing_dict, data_dict, - method_specific_output_dict, runtime_output) +"""Module providing functions for data postprocessing.""" +# Import standard modules. + +# Import own modules. +from datapostprocessingmodule import method_data_postprocessing +from datapostprocessingmodule import write_key_data_to_aircraft_exchange_file +from datapostprocessingmodule import prepare_element_tree_for_module_key_parameter + + +def data_postprocessing(paths_and_names, routing_dict, data_dict, runtime_output): + """Perform data postprocessing and write data to an aircraft exchange file. + + This function is responsible for data postprocessing that involves the following steps: + (1) Data preparation: The module manager prepares a list containing all paths to the key parameters that must + be written to the aircraft exchange file. + (2) Write data to the aircraft exchange file: The results of the module execution are passed on to the function + responsible for properly writing the data to the aircraft exchange file. + (3) Method-specific data postprocessing: User-defined method-specific postprocessing is conducted as needed. + + :param dict paths_and_names: Dictionary containing system paths and ElementTrees + :param dict routing_dict: Dictionary containing routing parameters + :param dict data_dict: Dictionary containing the result of the module execution (direct operating cost estimation) + :param logging.Logger runtime_output: Logging object used for capturing log messages in the module + :return: None + """ + + """Data preparation.""" + # Changes to this list are the sole responsibility of the module manager! + paths_to_key_parameters_list = [ + './component_design/tank/position/x', + './component_design/tank/position/y', + './component_design/tank/position/z', + './component_design/tank/mass_properties/mass', + './component_design/tank/mass_properties/inertia/j_xx', + './component_design/tank/mass_properties/inertia/j_yy', + './component_design/tank/mass_properties/inertia/j_zz', + './component_design/tank/mass_properties/inertia/j_xy', + './component_design/tank/mass_properties/inertia/j_xz', + './component_design/tank/mass_properties/inertia/j_yx', + './component_design/tank/mass_properties/inertia/j_yz', + './component_design/tank/mass_properties/inertia/j_zx', + './component_design/tank/mass_properties/inertia/j_zy', + './component_design/tank/mass_properties/center_of_gravity/x', + './component_design/tank/mass_properties/center_of_gravity/y', + './component_design/tank/mass_properties/center_of_gravity/z', + './component_design/tank/specific/additional_fuselage_length', + './component_design/tank/specific/tank[@ID="0"]/name', + './component_design/tank/specific/tank[@ID="0"]/designator', + './component_design/tank/specific/tank[@ID="0"]/position/x', + './component_design/tank/specific/tank[@ID="0"]/position/y', + './component_design/tank/specific/tank[@ID="0"]/position/z', + './component_design/tank/specific/tank[@ID="0"]/direction/x', + './component_design/tank/specific/tank[@ID="0"]/direction/y', + './component_design/tank/specific/tank[@ID="0"]/direction/z', + './component_design/tank/specific/tank[@ID="0"]/mass_properties/mass', + './component_design/tank/specific/tank[@ID="0"]/mass_properties/inertia/j_xx', + './component_design/tank/specific/tank[@ID="0"]/mass_properties/inertia/j_yy', + './component_design/tank/specific/tank[@ID="0"]/mass_properties/inertia/j_zz', + './component_design/tank/specific/tank[@ID="0"]/mass_properties/inertia/j_xy', + './component_design/tank/specific/tank[@ID="0"]/mass_properties/inertia/j_xz', + './component_design/tank/specific/tank[@ID="0"]/mass_properties/inertia/j_yx', + './component_design/tank/specific/tank[@ID="0"]/mass_properties/inertia/j_yz', + './component_design/tank/specific/tank[@ID="0"]/mass_properties/inertia/j_zx', + './component_design/tank/specific/tank[@ID="0"]/mass_properties/inertia/j_zy', + './component_design/tank/specific/tank[@ID="0"]/mass_properties/center_of_gravity/x', + './component_design/tank/specific/tank[@ID="0"]/mass_properties/center_of_gravity/y', + './component_design/tank/specific/tank[@ID="0"]/mass_properties/center_of_gravity/z', + './component_design/tank/specific/tank[@ID="0"]/maximum_energy_capacity', + './component_design/tank/specific/tank[@ID="0"]/energy_capacity_required_for_mission', + './component_design/tank/specific/tank[@ID="0"]/mass_properties/centroid/x', + './component_design/tank/specific/tank[@ID="0"]/mass_properties/centroid/y', + './component_design/tank/specific/tank[@ID="0"]/mass_properties/centroid/z', + './component_design/tank/specific/tank[@ID="0"]/geometry/cross_section[@ID="0"]/name', + './component_design/tank/specific/tank[@ID="0"]/geometry/cross_section[@ID="0"]/position/x', + './component_design/tank/specific/tank[@ID="0"]/geometry/cross_section[@ID="0"]/position/y', + './component_design/tank/specific/tank[@ID="0"]/geometry/cross_section[@ID="0"]/position/z', + './component_design/tank/specific/tank[@ID="0"]/geometry/cross_section[@ID="0"]/shape', + './component_design/tank/specific/tank[@ID="0"]/geometry/cross_section[@ID="0"]/height', + './component_design/tank/specific/tank[@ID="0"]/geometry/cross_section[@ID="0"]/width', + './component_design/tank/specific/tank[@ID="0"]/geometry/cross_section[@ID="0"]/length' + ] + module_key_parameters_dict = { + 'component_design': { + 'tank': { + 'attributes': { + 'description': 'Description of aircraft tanks.', + 'tool_level': '0'}, + 'position': { + 'attributes': {'description': + 'Tank reference point, position of the tanks with regard to the global reference ' + 'point.'}, + 'x': { + 'attributes': {'description': 'Distance between the foremost tank end and the global ' + 'reference point in x-direction.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '-80', + 'upper_boundary': '80'}, + 'y': { + 'attributes': {'description': 'Distance between the foremost tank end and the global ' + 'reference point in y-direction.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '-40', + 'upper_boundary': '40'}, + 'z': { + 'attributes': {'description': 'Distance between the foremost middle tank end and the global ' + 'reference point in z-direction.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '-5', + 'upper_boundary': '5'}, + }, + 'mass_properties': { + 'attributes': { + 'description': 'Mass properties of all tanks.'}, + 'mass': { + 'attributes': {'description': 'Total tank mass.'}, + 'value': '0', + 'unit': 'kg', + 'lower_boundary': '0', + 'upper_boundary': 'inf'}, + 'inertia': { + 'attributes': { + 'description': 'Inertia of all tanks with regard to the total center of gravity.'}, + 'j_xx': { + 'attributes': { + 'description': 'Inertia of all tanks in x.'}, + 'value': '0', + 'unit': 'kgm^2', + 'lower_boundary': '-inf', + 'upper_boundary': 'inf'}, + 'j_yy': { + 'attributes': { + 'description': 'Inertia of all tanks in y.'}, + 'value': '0', + 'unit': 'kgm^2', + 'lower_boundary': '-inf', + 'upper_boundary': 'inf'}, + 'j_zz': { + 'attributes': { + 'description': 'Inertia of all tanks in z.'}, + 'value': '0', + 'unit': 'kgm^2', + 'lower_boundary': '-inf', + 'upper_boundary': 'inf'}, + 'j_xy': { + 'attributes': { + 'description': 'Inertia of all tanks in xy.'}, + 'value': '0', + 'unit': 'kgm^2', + 'lower_boundary': '-inf', + 'upper_boundary': 'inf'}, + 'j_xz': { + 'attributes': { + 'description': 'Inertia of all tanks in xz.'}, + 'value': '0', + 'unit': 'kgm^2', + 'lower_boundary': '-inf', + 'upper_boundary': 'inf'}, + 'j_yx': { + 'attributes': { + 'description': 'Inertia of all tanks in yx.'}, + 'value': '0', + 'unit': 'kgm^2', + 'lower_boundary': '-inf', + 'upper_boundary': 'inf'}, + 'j_yz': { + 'attributes': { + 'description': 'Inertia of all tanks in yz.'}, + 'value': '0', + 'unit': 'kgm^2', + 'lower_boundary': '-inf', + 'upper_boundary': 'inf'}, + 'j_zx': { + 'attributes': { + 'description': 'Inertia of all tanks in zx.'}, + 'value': '0', + 'unit': 'kgm^2', + 'lower_boundary': '-inf', + 'upper_boundary': 'inf'}, + 'j_zy': { + 'attributes': { + 'description': 'Inertia of all tanks in zy.'}, + 'value': '0', + 'unit': 'kgm^2', + 'lower_boundary': '-inf', + 'upper_boundary': 'inf'}, + }, + 'center_of_gravity': { + 'attributes': { + 'description': 'Center of gravity of all tanks.'}, + 'x': { + 'attributes': { + 'description': 'Center of gravity in x-direction with regard to the global reference ' + 'point.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '-80', + 'upper_boundary': '80'}, + 'y': { + 'attributes': { + 'description': 'Center of gravity in y-direction with regard to the global reference ' + 'point.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '-40', + 'upper_boundary': '40'}, + 'z': { + 'attributes': { + 'description': 'Center of gravity in z-direction with regard to the global reference ' + 'point.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '-5', + 'upper_boundary': '5'} + } + }, + 'specific': { + 'attributes': { + 'description': 'Tank design specific output parameter.'}, + 'tank': { + 'attributes': { + 'ID': '0', + 'description': 'Description of one tank.'}, + 'name': { + 'attributes': { + 'description': 'Name of the tank.'}, + 'value': 'tank_0'}, + 'designator': { + 'attributes': { + 'description': 'Designator of the tank.'}, + 'value': 'placeholder_designator'}, + 'position': { + 'attributes': { + 'description': 'Local reference point, position of one tank wrt. the tank reference point.'}, + 'x': { + 'attributes': { + 'description': 'Distance between the foremost tank end of one tank and the tank ' + 'reference point in x-direction.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '-80', + 'upper_boundary': '80'}, + 'y': { + 'attributes': { + 'description': 'Distance between the foremost tank end of one tank and the tank ' + 'reference point in y-direction.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '-40', + 'upper_boundary': '40'}, + 'z': { + 'attributes': { + 'description': 'Distance between the foremost tank end of one tank and the tank ' + 'reference point in z-direction.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '-5', + 'upper_boundary': '5'} + }, + 'direction': { + 'attributes': { + 'description': 'Unit vector according to global coordinate system for direction ' + 'applied at position.'}, + 'x': { + 'attributes': { + 'description': 'X component of the unit vector.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '0', + 'upper_boundary': '1'}, + 'y': { + 'attributes': { + 'description': 'Y component of the unit vector.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '0', + 'upper_boundary': '1'}, + 'z': { + 'attributes': { + 'description': 'Z component of the unit vector.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '0', + 'upper_boundary': '1'} + }, + 'mass_properties': { + 'attributes': { + 'description': 'Mass properties of one tank.'}, + 'mass': { + 'attributes': {'description': 'Total dry mass of one tank.'}, + 'value': '0', + 'unit': 'kg', + 'lower_boundary': '0', + 'upper_boundary': 'inf'}, + 'inertia': { + 'attributes': { + 'description': 'Inertia of one tank with regard to its center of gravity.'}, + 'j_xx': { + 'attributes': { + 'description': 'Inertia of the of one tank in x.'}, + 'value': '0', + 'unit': 'kgm^2', + 'lower_boundary': '-inf', + 'upper_boundary': 'inf'}, + 'j_yy': { + 'attributes': { + 'description': 'Inertia of the of one tank in y.'}, + 'value': '0', + 'unit': 'kgm^2', + 'lower_boundary': '-inf', + 'upper_boundary': 'inf'}, + 'j_zz': { + 'attributes': { + 'description': 'Inertia of the of one tank in z.'}, + 'value': '0', + 'unit': 'kgm^2', + 'lower_boundary': '-inf', + 'upper_boundary': 'inf'}, + 'j_xy': { + 'attributes': { + 'description': 'Inertia of one tank in xy.'}, + 'value': '0', + 'unit': 'kgm^2', + 'lower_boundary': '-inf', + 'upper_boundary': 'inf'}, + 'j_xz': { + 'attributes': { + 'description': 'Inertia of one tank in xz.'}, + 'value': '0', + 'unit': 'kgm^2', + 'lower_boundary': '-inf', + 'upper_boundary': 'inf'}, + 'j_yx': { + 'attributes': { + 'description': 'Inertia of one tank in yx.'}, + 'value': '0', + 'unit': 'kgm^2', + 'lower_boundary': '-inf', + 'upper_boundary': 'inf'}, + 'j_yz': { + 'attributes': { + 'description': 'Inertia of one tank in yz.'}, + 'value': '0', + 'unit': 'kgm^2', + 'lower_boundary': '-inf', + 'upper_boundary': 'inf'}, + 'j_zx': { + 'attributes': { + 'description': 'Inertia of one tank in zx.'}, + 'value': '0', + 'unit': 'kgm^2', + 'lower_boundary': '-inf', + 'upper_boundary': 'inf'}, + 'j_zy': { + 'attributes': { + 'description': 'Inertia of one tank in zy.'}, + 'value': '0', + 'unit': 'kgm^2', + 'lower_boundary': '-inf', + 'upper_boundary': 'inf'}}, + 'center_of_gravity': { + 'attributes': {'description': 'Center of gravity of one tank.'}, + 'x': { + 'attributes': { + 'description': 'Center of gravity in x-direction with regard to the global ' + 'reference point.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '-80', + 'upper_boundary': '80'}, + 'y': { + 'attributes': { + 'description': 'Center of gravity in y-direction with regard to the global ' + 'reference point.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '-40', + 'upper_boundary': '40'}, + 'z': { + 'attributes': { + 'description': 'Center of gravity in z-direction with regard to the global ' + 'reference point.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '-5', + 'upper_boundary': '5'} + }, + 'centroid': { + 'attributes': {'description': 'Centroid of one tank.'}, + 'x': { + 'attributes': { + 'description': 'Centroid in x-direction with regard to the local ' + 'reference point.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '-80', + 'upper_boundary': '80'}, + 'y': { + 'attributes': { + 'description': 'Centroid in y-direction with regard to the local ' + 'reference point.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '-40', + 'upper_boundary': '40'}, + 'z': { + 'attributes': { + 'description': 'Centroid in z-direction with regard to the local ' + 'reference point.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '-5', + 'upper_boundary': '5'} + } + }, + 'maximum_energy_capacity': { + 'attributes': {'description': 'Maximum energy capacity of one tank.'}, + 'value': '0', + 'unit': 'J', + 'lower_boundary': '0', + 'upper_boundary': 'inf'}, + 'energy_capacity_required_for_mission': { + 'attributes': {'description': 'Energy amount required for mission.'}, + 'value': 'True'}, + 'geometry': { + 'attributes': {'description': 'Geometrical description of one tank.'}, + 'cross_section': { + 'attributes': { + 'ID': '0', + 'description': 'Geometrical description of one tank cross section.'}, + 'name': { + 'attributes': { + 'description': 'Name of tank cross section.'}, + 'value': 'tank_section_0'}, + 'position': { + 'attributes': { + 'description': 'Position of tank cross section with regard to the local ' + 'reference point.'}, + 'x': { + 'attributes': { + 'description': 'Distance between the tank cross section and the local ' + 'reference point in x-direction.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '-80', + 'upper_boundary': '80'}, + 'y': { + 'attributes': { + 'description': 'Distance between the tank cross section and the local ' + 'reference point in y-direction.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '-40', + 'upper_boundary': '40'}, + 'z': { + 'attributes': { + 'description': 'Distance between the tank cross section and the local ' + 'reference point in z-direction.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '-5', + 'upper_boundary': '5'}}, + 'shape': { + 'attributes': { + 'description': 'Description of the shape of the cross section (circular, ' + 'rectangular, elliptical).'}, + 'value': 'misc'}, + 'height': { + 'attributes': { + 'description': 'Height of the cross section.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '0', + 'upper_boundary': 'inf'}, + 'width': { + 'attributes': { + 'description': 'Width of the cross section.'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '0', + 'upper_boundary': 'inf'}, + 'length': { + 'attributes': { + 'description': 'Length of the cross section (if length greater than 0: curved ' + 'cross section, e.g., dashed tank end cap).'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '0', + 'upper_boundary': 'inf'} + } + } + }, + 'additional_fuselage_length': { + 'attributes': { + 'description': 'Additional fuselage length (if smaller than 0: shrink ' + 'fuselage, if greater than 0: stretch fuselage).'}, + 'value': '0', + 'unit': 'm', + 'lower_boundary': '0', + 'upper_boundary': 'inf' + } + } + } + } + } + + paths_and_names = prepare_element_tree_for_module_key_parameter(paths_and_names, module_key_parameters_dict) + + # Run 'user_method_data_output_preparation' from 'usermethoddatapreparation.py'. + 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'] + + """Write data to aircraft exchange file.""" + # Extract root and path to aircraft exchange file. + root_of_aircraft_exchange_tree = paths_and_names['root_of_aircraft_exchange_tree'] + path_to_aircraft_exchange_file = paths_and_names['path_to_aircraft_exchange_file'] + # Write key data to aircraft exchange file. + write_key_data_to_aircraft_exchange_file(root_of_aircraft_exchange_tree, path_to_aircraft_exchange_file, + paths_to_key_parameters_list, key_output_dict, tool_level, runtime_output) + + """Method-specific postprocessing.""" + # Run 'method_data_postprocessing' from 'datapostprocessingmodule'. + data_dict['module_key_parameters_dict'] = module_key_parameters_dict + data_dict['paths_to_key_parameters_list'] = paths_to_key_parameters_list + method_data_postprocessing(paths_and_names, routing_dict, data_dict, + method_specific_output_dict, runtime_output) diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/call_functions/_00_readenergycarrierandtankconfiguration.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/call_functions/_00_readenergycarrierandtankconfiguration.py index 4470da73eb0ee5577363a0e5bc083fa75aa9e64c..b4c787bc0980caeb1909d164cdd1b04c0dd9a64e 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/call_functions/_00_readenergycarrierandtankconfiguration.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/call_functions/_00_readenergycarrierandtankconfiguration.py @@ -2,6 +2,9 @@ # Import standard libraries. import sys +# Import own libraries. +from pyunitconversion import constants + def read_energy_carrier_and_tank_configuration(paths_and_names, dict_ac_data, dict_tank_design, runtime_output): """Determine energy carrier dependent parameter. @@ -36,10 +39,6 @@ def read_energy_carrier_and_tank_configuration(paths_and_names, dict_ac_data, di tank_location = dict_ac_data['tank_location'] tank_position = dict_ac_data['tank_position'] tank_energy_share = dict_ac_data['energy_share'] - gravimetric_energy_densities = {'kerosene': dict_ac_data['kerosene_gravimetric_energy_density'], - 'liquid_hydrogen': dict_ac_data['liquid_hydrogen_gravimetric_energy_density']} - volumetric_energy_densities = {'kerosene': dict_ac_data['kerosene_volumetric_energy_density'], - 'liquid_hydrogen': dict_ac_data['liquid_hydrogen_volumetric_energy_density']} except KeyError: runtime_output.critical('Error: Missing data in aircraft exchange file. Energy carrier and tank configuration ' +' preparation not possible. Program aborted.') @@ -51,6 +50,11 @@ def read_energy_carrier_and_tank_configuration(paths_and_names, dict_ac_data, di # energy_carrier_lookup = {0: {'energy_carrier': 'kerosene'}, 1: {'energy_carrier': 'liquid_hydrogen'}} energy_carrier_lookup = {int(key.split('ID')[-1]): {'energy_carrier': value} for key, value in energy_carrier_name.items()} + + gravimetric_energy_densities = {'kerosene': constants.KEROSENE_GRAVIMETRIC_ENERGY_DENSITY, + 'liquid_hydrogen': constants.LIQUID_HYDROGEN_GRAVIMETRIC_ENERGY_DENSITY} + volumetric_energy_densities = {'kerosene': constants.KEROSENE_VOLUMETRIC_ENERGY_DENSITY, + 'liquid_hydrogen': constants.LIQUID_HYDROGEN_VOLUMETRIC_ENERGY_DENSITY} # Map mission energy ID to energy carrier ID and update the lookup dictionary. for key, val in mission_energy_id.items(): diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/call_functions/_01_checktankconfiguration.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/call_functions/_01_checktankconfiguration.py index f65fc225bb9942dcce9f6db89dc700505366a68c..44617f75b84057a8816596712ade89703a39d3b7 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/call_functions/_01_checktankconfiguration.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/call_functions/_01_checktankconfiguration.py @@ -86,15 +86,23 @@ def check_tank_configuration(paths_and_names, dict_ac_data, dict_tank_design, ru if valid_wing_tanks and not (trim_tank or additional_center_tank): number_of_tanks = 5 tank_configuration = 'wing_all_tanks' + # Print. + runtime_output.print('Valid tank configuration found in acXML: ' + tank_configuration) elif valid_wing_with_trim_tank and not additional_center_tank: number_of_tanks = 6 tank_configuration = 'wing_with_trim_tank' + # Print. + runtime_output.print('Valid tank configuration found in acXML: ' + tank_configuration) elif valid_wing_with_additional_center_tank and not trim_tank: number_of_tanks = 6 tank_configuration = 'wing_with_additional_center_tank' + # Print. + runtime_output.print('Valid tank configuration found in acXML: ' + tank_configuration) elif valid_wing_with_additional_center_and_trim_tank: number_of_tanks = 7 tank_configuration = 'wing_with_additional_center_and_trim_tank' + # Print. + runtime_output.print('Valid tank configuration found in acXML: ' + tank_configuration) # Invalid tank configuration. else: number_of_tanks = 0 diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/call_functions/_02_preparegeometricaldata.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/call_functions/_02_preparegeometricaldata.py index f8aa2372f5be564005033ce33c00c94f2ee87546..43246f5a4a27cd718bb94ac2fea083bc601d3e87 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/call_functions/_02_preparegeometricaldata.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/call_functions/_02_preparegeometricaldata.py @@ -224,26 +224,28 @@ def prepare_wing_tanks(dict_ac_data, dict_tank_design, runtime_output): } # Add further values to 'tmp_dict'. - # TODO: thickness_max from aircraftGeometry2. for i, section in enumerate(wing_section_list): # Add span index to section. tmp_dict['sections'][section]['span_index'] = abs(int(round( tmp_dict['sections'][section]['position']['y']*100, 0))) + # Add thickness_ratio. + tmp_dict['sections'][section]['thickness_ratio'] = dict_ac_data['wing_thickness_to_length'][i] # Add 'thickness_ratio' (derived from the profile name). - profile_name = tmp_dict['sections'][section]['profile'] - if profile_name.startswith(("naca", "n", "F15")): - tmp_dict['sections'][section]['thickness_ratio'] = int(profile_name[-2:])/100 - else: - # Not implemented yet. - runtime_output.critical('Error: Thickness ratio cannot be extracted! Program aborted.') - sys.exit('Exit information: Thickness ratio cannot be extracted from profile name!') + # profile_name = tmp_dict['sections'][section]['profile'] + # if profile_name.startswith(("naca", "n", "F15")): + # tmp_dict['sections'][section]['thickness_ratio'] = int(profile_name[-2:])/100 + # else: + # # Not implemented yet. + # runtime_output.critical('Error: Thickness ratio cannot be extracted! Program aborted.') + # sys.exit('Exit information: Thickness ratio cannot be extracted from profile name!') # Add 'tmp_dict' to 'dict_tank_design'. dict_tank_design['geometry']['wing'] = tmp_dict """Interpolate necessary values.""" # Calculate number of points for value interpolation (one point per mm). - number_of_points = abs(int(round(dict_tank_design['geometry']['wing']['sections']['wing_tip']['position']['y']*100, 0))) + number_of_points = abs(int(round(dict_tank_design['geometry']['wing']['sections']['wing_tip']['position']['y'] + *100, 0))) # Write wing section span indexes (equal distances in mm) to list. section_span_indexes = [tmp_dict['sections'][section]['span_index'] for section in wing_section_list] @@ -425,27 +427,28 @@ def prepare_trim_tank(dict_ac_data, dict_tank_design, runtime_output): } # Add further values to 'tmp_dict'. - # TODO: thickness_max from aircraftGeometry2. for i, section in enumerate(section_list): # Add span index to section. tmp_dict['sections'][section]['span_index'] = abs(int(round( tmp_dict['sections'][section]['position']['y']*100, 0))) - # Add 'thickness_ratio' (derived from the profile name). - profile_name = tmp_dict['sections'][section]['profile'] - if profile_name.startswith(("naca", "n", "F15")): - tmp_dict['sections'][section]['thickness_ratio'] = int(profile_name[-2:])/100 - else: - # Not implemented yet. - runtime_output.critical('Error: Thickness ratio cannot be extracted! Program aborted.') - sys.exit('Exit information: Thickness ratio cannot be extracted from profile name!') + # Add thickness_ratio. + tmp_dict['sections'][section]['thickness_ratio'] = dict_ac_data['horizontal_stabilizer_thickness_to_length'][i] + # # Add 'thickness_ratio' (derived from the profile name). + # profile_name = tmp_dict['sections'][section]['profile'] + # if profile_name.startswith(("naca", "n", "F15")): + # tmp_dict['sections'][section]['thickness_ratio'] = int(profile_name[-2:])/100 + # else: + # # Not implemented yet. + # runtime_output.critical('Error: Thickness ratio cannot be extracted! Program aborted.') + # sys.exit('Exit information: Thickness ratio cannot be extracted from profile name!') # Add 'tmp_dict' to 'dict_tank_design'. dict_tank_design['geometry']['horizontal_stabilizer'] = tmp_dict """Interpolate necessary values.""" # Calculate number of points for value interpolation (one point per mm). - number_of_points = int(round( - dict_tank_design['geometry']['horizontal_stabilizer']['sections']['tip']['position']['y']*100, 0)) + number_of_points = abs(int(round( + dict_tank_design['geometry']['horizontal_stabilizer']['sections']['tip']['position']['y']*100, 0))) # Write wing section span indexes (equal distances in mm) to list. section_span_indexes = [tmp_dict['sections'][section]['span_index'] for section in section_list] diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/tankdesigntuberlin.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/tankdesigntuberlin.py index ff978858dad1cb3777dfedea1faa2c3d81368411..9ed9201281e6fbe07c32708bf7018dfc05198954 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/tankdesigntuberlin.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/tankdesigntuberlin.py @@ -1,167 +1,196 @@ -"""Module providing calculating functions for the tank design of tube-and-wing aircraft according to TU Berlin.""" -# Import standard libraries. -import importlib -import os -import sys -import pycoordinatesystemconversion as py11csc - -def tank_design_tu_berlin(paths_and_names, routing_dict, dict_ac_data, runtime_output): - """Tank design method for tube-and-wing aircraft. - - The first step is to check whether the tank design is feasible. This applies if there is only one wing and one - fuselage. If this is true, the tank design method calls general (independent on energy carrier) functions for the - tank design of tube-and-wing aircraft. - The output dictionary 'dict_tank_design' contains the results of the tank design preparation. The output dictionary - is structured according to the following scheme: - dict_tank_design = { - 'tank_0': {...}, - 'tank_1': {...}, - ... - 'number_of_tanks': (int, number of tanks available), - 'tank_configuration': (str, name of tank configuration), - 'valid_tank_definition': (bool, information if tank configuration is valid), - 'geometry':{...} - } - The detailed structure of the sub dictionaries depends on the energy carrier and can be found in the docStrings of - the '_00_readenergycarrierandtankconfiguration.py' and '_01_preparegeometricaldata.py' files. - - :param dict paths_and_names: Dictionary containing system paths and ElementTrees - :param dict routing_dict: Dictionary containing routing parameters - :param dict dict_ac_data: Dictionary containing data from aircraft exchange and module configuration file - :param logging.Logger runtime_output: Logging object used for capturing log messages in the module - :raises ModuleNotFoundError: Raised if calculation module not found - :return dict dict_tank_design: Dictionary containing tank design parameter - """ - """Convert coordinates from CGAL to aircraft coordinate system.""" - # List of parameters to be considered for CGAL to aircraft coordinate system conversion. - param_list = ["fuselage_position", "wing_position", "empennage_position", "fuselage_entity_position", "fuselage_section_origin", "fuselage_payload_deck_origin", - "wing_section_chord_origin", "empennage_entity_position", "empennage_section_chord_origin"] - - # for param in param_list: - # if dict_ac_data[param + "_x"] is not None: - # if type(dict_ac_data[param + "_x"]) != float: - # for key in dict_ac_data[param + "_x"].keys(): - # index = key.split("_").index("x") + 1 - # if dict_ac_data[param + "_x"][param + "_x_" + key.split("_", index)[-1]] is not None: - # element3D = py11csc.Element3D(dict_ac_data[param + "_x"][param + "_x_" + key.split("_", index)[-1]], - # dict_ac_data[param + "_y"][param + "_y_" + key.split("_", index)[-1]], - # dict_ac_data[param + "_z"][param + "_z_" + key.split("_", index)[-1]]) - # element3D.CGAL2AC() - # dict_ac_data[param + "_x"][param + "_x_" + key.split("_", index)[-1]] = element3D.x() - # dict_ac_data[param + "_y"][param + "_y_" + key.split("_", index)[-1]] = element3D.y() - # dict_ac_data[param + "_z"][param + "_z_" + key.split("_", index)[-1]] = element3D.z() - # else: - # runtime_output.warning("WARNING: The parameter " + param + "_x is missing in the aircraft exchange file. No need to convert the coordinates.") - # else: - # element3D = py11csc.Element3D(dict_ac_data[param + "_x"], - # dict_ac_data[param + "_y"], - # dict_ac_data[param + "_z"]) - # element3D.CGAL2AC() - # dict_ac_data[param + "_x"] = element3D.x() - # dict_ac_data[param + "_y"] = element3D.y() - # dict_ac_data[param + "_z"] = element3D.z() - # else: - # runtime_output.warning("WARNING: The parameter " + param + "_x is missing in the aircraft exchange file. No need to convert the coordinates.") - - # Transform fuselage cgal coordinates to aircraft coordinates. - # Store the current values of each sub-dict in temporary variables - temp_x_values = dict_ac_data['fuselage_section_origin_x'].values() - temp_y_values = dict_ac_data['fuselage_section_origin_y'].values() - temp_z_values = dict_ac_data['fuselage_section_origin_z'].values() - # Update each dictionary's values by iterating over keys - # Swap values: z -> x, y -> z, x -> y - dict_ac_data['fuselage_section_origin_x'] = dict(zip(dict_ac_data['fuselage_section_origin_x'].keys(), temp_z_values)) - dict_ac_data['fuselage_section_origin_y'] = dict(zip(dict_ac_data['fuselage_section_origin_y'].keys(), temp_x_values)) - dict_ac_data['fuselage_section_origin_z'] = dict(zip(dict_ac_data['fuselage_section_origin_z'].keys(), temp_y_values)) - - # Transform wing cgal coordinates to aircraft coordinates. - # Store the current values of each sub-dict in temporary variables - temp_y_values = dict_ac_data['wing_section_chord_origin_y'].values() - temp_z_values = dict_ac_data['wing_section_chord_origin_z'].values() - # Update each dictionary's values by iterating over keys - # Swap values: z -> y, y -> z - dict_ac_data['wing_section_chord_origin_y'] = dict(zip(dict_ac_data['wing_section_chord_origin_y'].keys(), temp_z_values)) - dict_ac_data['wing_section_chord_origin_z'] = dict(zip(dict_ac_data['wing_section_chord_origin_z'].keys(), temp_y_values)) - - # Transform empennage cgal coordinates to aircraft coordinates. - # Store the current values of each sub-dict in temporary variables - temp_y_values = dict_ac_data['empennage_section_chord_origin_y'].values() - temp_z_values = dict_ac_data['empennage_section_chord_origin_z'].values() - # Update each dictionary's values by iterating over keys - # Swap values: z -> y, y -> z - dict_ac_data['empennage_section_chord_origin_y'] = dict(zip(dict_ac_data['empennage_section_chord_origin_y'].keys(), temp_z_values)) - dict_ac_data['empennage_section_chord_origin_z'] = dict(zip(dict_ac_data['empennage_section_chord_origin_z'].keys(), temp_y_values)) - - """Check if tank design is feasible.""" - number_of_fuselages = len(dict_ac_data['fuselage_entity_position_x'].keys()) - if number_of_fuselages < 1: - # If no fuselage is available, the tank design is impossible and the program aborted. - runtime_output.critical('Error: Tank design not possible without fuselage! Program aborted.') - sys.exit('Exit information: Tank design not possible without fuselage definition!') - elif number_of_fuselages > 1: - # If more than one fuselage is available, the tank design is impossible and the program aborted. - runtime_output.critical('Error: Tank design not possible for more than one fuselage! Program aborted.') - sys.exit('Exit information: Tank design not possible for more than one fuselage!') - - number_of_wings = len(dict_ac_data['wing_name'].keys()) - if number_of_wings < 1: - # If no fuselage is available, the tank design is impossible and the program aborted. - runtime_output.critical('Error: Tank design not possible without wing! Program aborted.') - sys.exit('Exit information: Tank design not possible without wing definition!') - elif number_of_wings > 1: - # If more than one fuselage is available, the tank design is impossible and the program aborted. - runtime_output.critical('Error: Tank design not possible for more than one wing! Program aborted.') - sys.exit('Exit information: Tank design not possible for more than one wing!') - - """Run tank design preparation functions.""" - # Initialize local parameter. - dict_tank_design = {} - # Define function names and remove underscores. - function_names = ['read_energy_carrier_and_tank_configuration', 'check_tank_configuration', - 'prepare_geometrical_data'] - cleaned_function_names = [name.replace('_', '') for name in function_names] - - # If the script is compiled, sys._MEIPASS gives the temp directory for PyInstaller. - if getattr(sys, 'frozen', False): - # Read all python files from 'call_functions' directory, delete cache folder, and sort 'call_function_list'. - call_function_list = os.listdir(sys._MEIPASS + '/' - + routing_dict['module_import_name'].replace('.', '/') + '/general/call_functions') - # If running as a regular Python script, use the current directory. - else: - # Read all python files from 'call_functions' directory, delete cache folder, and sort 'call_function_list'. - call_function_list = os.listdir(paths_and_names['working_directory'] + '/' - + routing_dict['module_import_name'].replace('.', '/') + '/general/call_functions') - - if '__pycache__' in call_function_list: - call_function_list.remove('__pycache__') - if '.DS_Store' in call_function_list: - call_function_list.remove('.DS_Store') - call_function_list = sorted(call_function_list, key=lambda x: (int(x.split('_')[1]), x)) - - # Loop across all *.py files from 'call_functions' directory and find matching entries in 'function_names'. - for call_function in call_function_list: - try: - # Generate function import string. - import_call_function = (routing_dict['module_import_name'] - + '.general.call_functions.' + os.path.splitext(call_function)[0]) - # Extract function name from 'function_names' list. - for idx, cleaned_name in enumerate(cleaned_function_names): - # Check if the cleaned name is present in the 'call_function' string. - if cleaned_name in call_function: - function_name = function_names[idx] - break - # Import calculation module. - module = importlib.import_module(import_call_function) - dict_tank_design = getattr(module, function_name)(paths_and_names, dict_ac_data, dict_tank_design, - runtime_output) - - # Exception handling for module import error. - except ModuleNotFoundError as module_import_error: - runtime_output.critical('Error: ' + str(module_import_error) + ' found in ' + routing_dict['module_name'] - + '. Program aborted.') - sys.exit('Exit information: The own python module to be imported could not be found!') - - """Debug print.""" - runtime_output.debug('Debug: The "tank_design_tu_berlin" function was successfully executed.') - - return dict_tank_design +"""Module providing calculating functions for the tank design of tube-and-wing aircraft according to TU Berlin.""" +# Import standard libraries. +import importlib +import os +import sys +import pycoordinatesystemconversion as py11csc +import pyaixml as aixml +import pyaircraftgeometry2 as geom2 + +def tank_design_tu_berlin(paths_and_names, routing_dict, dict_ac_data, runtime_output): + """Tank design method for tube-and-wing aircraft. + + The first step is to check whether the tank design is feasible. This applies if there is only one wing and one + fuselage. If this is true, the tank design method calls general (independent on energy carrier) functions for the + tank design of tube-and-wing aircraft. + The output dictionary 'dict_tank_design' contains the results of the tank design preparation. The output dictionary + is structured according to the following scheme: + dict_tank_design = { + 'tank_0': {...}, + 'tank_1': {...}, + ... + 'number_of_tanks': (int, number of tanks available), + 'tank_configuration': (str, name of tank configuration), + 'valid_tank_definition': (bool, information if tank configuration is valid), + 'geometry':{...} + } + The detailed structure of the sub dictionaries depends on the energy carrier and can be found in the docStrings of + the '_00_readenergycarrierandtankconfiguration.py' and '_01_preparegeometricaldata.py' files. + + :param dict paths_and_names: Dictionary containing system paths and ElementTrees + :param dict routing_dict: Dictionary containing routing parameters + :param dict dict_ac_data: Dictionary containing data from aircraft exchange and module configuration file + :param logging.Logger runtime_output: Logging object used for capturing log messages in the module + :raises ModuleNotFoundError: Raised if calculation module not found + :return dict dict_tank_design: Dictionary containing tank design parameter + """ + """ Extract aerodynamic surfaces values using the aircraftGeometry2 library.""" + # Create the factory + airfoil_data_dir = f'{paths_and_names["project_directory"]}/geometryData/airfoilData' + aircraft_exchange_file = aixml.openDocument(paths_and_names['path_to_aircraft_exchange_file']) + factory = geom2.factory.WingFactory(aircraft_exchange_file, airfoil_data_dir) + # Create the surface + wing = factory.create("wing/specific/geometry/aerodynamic_surface@0") + horizontal_stabilizer = factory.create("empennage/specific/geometry/aerodynamic_surface@1") + # Extract wing y coordinates and thickness-to-length ratio. + thickness_to_length = [] + for i in range(len(wing.sections)): + z = wing.sections[i].origin.z() + chord = geom2.measure.chord(wing, z) + thickness = geom2.measure.thickness_max(wing.sections[i]) + thickness_to_length.append(thickness/chord) + dict_ac_data['wing_thickness_to_length'] = thickness_to_length + + # Extract horizontal stabilizer y coordinates and thickness-to-length ratio. + thickness_to_length = [] + for i in range(len(horizontal_stabilizer.sections)): + z = horizontal_stabilizer.sections[i].origin.z() + chord = geom2.measure.chord(horizontal_stabilizer, z) + thickness = geom2.measure.thickness_max(horizontal_stabilizer.sections[i]) + thickness_to_length.append(thickness/chord) + dict_ac_data['horizontal_stabilizer_thickness_to_length'] = thickness_to_length + + """Convert coordinates from CGAL to aircraft coordinate system.""" + # List of parameters to be considered for CGAL to aircraft coordinate system conversion. + param_list = ["fuselage_position", "wing_position", "empennage_position", "fuselage_entity_position", + "fuselage_section_origin", "fuselage_payload_deck_origin", "wing_section_chord_origin", + "empennage_entity_position", "empennage_section_chord_origin"] + + # for param in param_list: + # if dict_ac_data[param + "_x"] is not None: + # if type(dict_ac_data[param + "_x"]) != float: + # for key in dict_ac_data[param + "_x"].keys(): + # index = key.split("_").index("x") + 1 + # if dict_ac_data[param + "_x"][param + "_x_" + key.split("_", index)[-1]] is not None: + # element3D = py11csc.Element3D(dict_ac_data[param + "_x"][param + "_x_" + key.split("_", index)[-1]], + # dict_ac_data[param + "_y"][param + "_y_" + key.split("_", index)[-1]], + # dict_ac_data[param + "_z"][param + "_z_" + key.split("_", index)[-1]]) + # element3D.CGAL2AC() + # dict_ac_data[param + "_x"][param + "_x_" + key.split("_", index)[-1]] = element3D.x() + # dict_ac_data[param + "_y"][param + "_y_" + key.split("_", index)[-1]] = element3D.y() + # dict_ac_data[param + "_z"][param + "_z_" + key.split("_", index)[-1]] = element3D.z() + # else: + # runtime_output.warning("WARNING: The parameter " + param + "_x is missing in the aircraft exchange file. No need to convert the coordinates.") + # else: + # element3D = py11csc.Element3D(dict_ac_data[param + "_x"], + # dict_ac_data[param + "_y"], + # dict_ac_data[param + "_z"]) + # element3D.CGAL2AC() + # dict_ac_data[param + "_x"] = element3D.x() + # dict_ac_data[param + "_y"] = element3D.y() + # dict_ac_data[param + "_z"] = element3D.z() + # else: + # runtime_output.warning("WARNING: The parameter " + param + "_x is missing in the aircraft exchange file. No need to convert the coordinates.") + + # Transform fuselage cgal coordinates to aircraft coordinates. + # Store the current values of each sub-dict in temporary variables + temp_x_values = dict_ac_data['fuselage_section_origin_x'].values() + temp_y_values = dict_ac_data['fuselage_section_origin_y'].values() + temp_z_values = dict_ac_data['fuselage_section_origin_z'].values() + # Update each dictionary's values by iterating over keys + # Swap values: z -> x, y -> z, x -> y + dict_ac_data['fuselage_section_origin_x'] = dict(zip(dict_ac_data['fuselage_section_origin_x'].keys(), temp_z_values)) + dict_ac_data['fuselage_section_origin_y'] = dict(zip(dict_ac_data['fuselage_section_origin_y'].keys(), temp_x_values)) + dict_ac_data['fuselage_section_origin_z'] = dict(zip(dict_ac_data['fuselage_section_origin_z'].keys(), temp_y_values)) + + # Transform wing cgal coordinates to aircraft coordinates. + # Store the current values of each sub-dict in temporary variables + temp_y_values = dict_ac_data['wing_section_chord_origin_y'].values() + temp_z_values = dict_ac_data['wing_section_chord_origin_z'].values() + # Update each dictionary's values by iterating over keys + # Swap values: z -> y, y -> z + dict_ac_data['wing_section_chord_origin_y'] = dict(zip(dict_ac_data['wing_section_chord_origin_y'].keys(), temp_z_values)) + dict_ac_data['wing_section_chord_origin_z'] = dict(zip(dict_ac_data['wing_section_chord_origin_z'].keys(), temp_y_values)) + + # Transform empennage cgal coordinates to aircraft coordinates. + # Store the current values of each sub-dict in temporary variables + temp_y_values = dict_ac_data['empennage_section_chord_origin_y'].values() + temp_z_values = dict_ac_data['empennage_section_chord_origin_z'].values() + # Update each dictionary's values by iterating over keys + # Swap values: z -> y, y -> z + dict_ac_data['empennage_section_chord_origin_y'] = dict(zip(dict_ac_data['empennage_section_chord_origin_y'].keys(), temp_z_values)) + dict_ac_data['empennage_section_chord_origin_z'] = dict(zip(dict_ac_data['empennage_section_chord_origin_z'].keys(), temp_y_values)) + + """Check if tank design is feasible.""" + number_of_fuselages = len(dict_ac_data['fuselage_entity_position_x'].keys()) + if number_of_fuselages < 1: + # If no fuselage is available, the tank design is impossible and the program aborted. + runtime_output.critical('Error: Tank design not possible without fuselage! Program aborted.') + sys.exit('Exit information: Tank design not possible without fuselage definition!') + elif number_of_fuselages > 1: + # If more than one fuselage is available, the tank design is impossible and the program aborted. + runtime_output.critical('Error: Tank design not possible for more than one fuselage! Program aborted.') + sys.exit('Exit information: Tank design not possible for more than one fuselage!') + + number_of_wings = len(dict_ac_data['wing_name'].keys()) + if number_of_wings < 1: + # If no wing is available, the tank design is impossible and the program aborted. + runtime_output.critical('Error: Tank design not possible without wing! Program aborted.') + sys.exit('Exit information: Tank design not possible without wing definition!') + elif number_of_wings > 1: + # If more than one wing is available, the tank design is impossible and the program aborted. + runtime_output.critical('Error: Tank design not possible for more than one wing! Program aborted.') + sys.exit('Exit information: Tank design not possible for more than one wing!') + + """Run tank design preparation functions.""" + # Initialize local parameter. + dict_tank_design = {} + # Define function names and remove underscores. + function_names = ['read_energy_carrier_and_tank_configuration', 'check_tank_configuration', + 'prepare_geometrical_data'] + cleaned_function_names = [name.replace('_', '') for name in function_names] + + # If the script is compiled, sys._MEIPASS gives the temp directory for PyInstaller. + if getattr(sys, 'frozen', False): + # Read all python files from 'call_functions' directory, delete cache folder, and sort 'call_function_list'. + call_function_list = os.listdir(sys._MEIPASS + '/' + + routing_dict['module_import_name'].replace('.', '/') + '/general/call_functions') + # If running as a regular Python script, use the current directory. + else: + # Read all python files from 'call_functions' directory, delete cache folder, and sort 'call_function_list'. + call_function_list = os.listdir(paths_and_names['working_directory'] + '/' + + routing_dict['module_import_name'].replace('.', '/') + '/general/call_functions') + + if '__pycache__' in call_function_list: + call_function_list.remove('__pycache__') + if '.DS_Store' in call_function_list: + call_function_list.remove('.DS_Store') + call_function_list = sorted(call_function_list, key=lambda x: (int(x.split('_')[1]), x)) + + # Loop across all *.py files from 'call_functions' directory and find matching entries in 'function_names'. + for call_function in call_function_list: + try: + # Generate function import string. + import_call_function = (routing_dict['module_import_name'] + + '.general.call_functions.' + os.path.splitext(call_function)[0]) + # Extract function name from 'function_names' list. + for idx, cleaned_name in enumerate(cleaned_function_names): + # Check if the cleaned name is present in the 'call_function' string. + if cleaned_name in call_function: + function_name = function_names[idx] + break + # Import calculation module. + module = importlib.import_module(import_call_function) + dict_tank_design = getattr(module, function_name)(paths_and_names, dict_ac_data, dict_tank_design, + runtime_output) + + # Exception handling for module import error. + except ModuleNotFoundError as module_import_error: + runtime_output.critical('Error: ' + str(module_import_error) + ' found in ' + routing_dict['module_name'] + + '. Program aborted.') + sys.exit('Exit information: The own python module to be imported could not be found!') + + """Debug print.""" + runtime_output.debug('Debug: The "tank_design_tu_berlin" function was successfully executed.') + + return dict_tank_design diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/kerosene/calculatetanks.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/kerosene/calculatetanks.py index b2e2e982096c4d65fc65da00d989ab68b267f95a..b95bba00108caf1d1f4a6dd438b5df4541c35e66 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/kerosene/calculatetanks.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/kerosene/calculatetanks.py @@ -1,1010 +1,1475 @@ -""" Module containing functions for kerosene tank calculation. """ -# Import standard libraries. -import sys -import math -import numpy as np - - -def calculate_tank_span(span_index_inner_surface, span_index_outer_surface): - """Tank span in m. - - Span index equals position in mm. - - :param int span_index_inner_surface: Index of span position of inner surface - :param int span_index_outer_surface: Index of span position of outer surface - :return float: Tank span in m - """ - tank_span = (span_index_outer_surface - span_index_inner_surface)/100 - - return tank_span - - -def calculate_obelisk_volume(method, dict_wing_geometry, inner_surface_str, outer_surface_str): - """Calculate geometrical volume of obelisk. - - This function calculates the obelisk volume according to the selected method (Torenbeek [1] or Simpson). - - [1] E. Torenbeek "Synthesis of Subsonic Airplane Design" (1982), Fig. B-4 [1] - - :param str method: Calculation method name - :param dict dict_wing_geometry: Dictionary containing wing geometry parameters - :param str inner_surface_str: Name of inner obelisk surface - :param str outer_surface_str: Name of outer obelisk surface - :return float: Volume of obelisk in m^3 - """ - interpolated_values = dict_wing_geometry['interpolated_values'] - - inner_span_index = abs(dict_wing_geometry['sections'][inner_surface_str]['span_index']) - outer_span_index = abs(dict_wing_geometry['sections'][outer_surface_str]['span_index']) - tank_span = (outer_span_index - inner_span_index)/100 - maximum_tank_height_inner = interpolated_values['tank_heights'][inner_span_index] - maximum_tank_height_outer = interpolated_values['tank_heights'][outer_span_index] - maximum_tank_length_inner = interpolated_values['tank_lengths'][inner_span_index] - maximum_tank_length_outer = interpolated_values['tank_lengths'][outer_span_index] - area_inner = maximum_tank_height_inner*maximum_tank_length_inner - area_outer = maximum_tank_height_outer*maximum_tank_length_outer - - match method: - case 'Torenbeek': - # Calculate volume of obelisk according to Torenbeek. - obelisk_volume = (tank_span/3*(area_inner + area_outer - + ((maximum_tank_height_inner*maximum_tank_length_outer) - + (maximum_tank_length_inner*maximum_tank_height_outer))/2)) - case 'Simpson': - # Calculate volume of obelisk according to Simpson's rule. - middle_area = (area_inner + area_outer)/2 - obelisk_volume = tank_span / 6.0 * (area_inner + 4.0 * middle_area + area_outer) - - return obelisk_volume - - -def calculate_kerosene_tanks(dict_ac_data, dict_tank_design, runtime_output): - """Calculate kerosene tanks of tube-and-wing aircraft. - - This function adds the following sub dictionaries to the 'dict_tank_design' dictionary: - ... - - :param dict dict_ac_data: Dictionary containing data from aircraft exchange and module configuration file - :param dict dict_tank_design: Dictionary containing tank design parameter - :param logging.Logger runtime_output: Logging object used for capturing log messages in the module - :return dict dict_tank_design: Dictionary containing tank design parameter - """ - # Extract data and initialize variables. - total_tank_energy = 0 - additional_tank_energy = 0 - wing_tank_energy_without_wing_center_tank = 0 - - """Check if initial sizing loop and estimate energy demand if necessary.""" - initial_sizing_loop = all(value is None for value in dict_ac_data['mission_energy_amount'].values()) - if initial_sizing_loop: - runtime_output.print('Initial sizing loop! Energy demand is estimated (3.5 liters per PAX per 100 km).') - energy_demand = 3.35 # in liter per passenger - mission_fuel_amount = dict_ac_data['number_of_passengers']*dict_ac_data['range']/1000*energy_demand/100 - mission_energy_amount = (mission_fuel_amount/1000* - dict_ac_data['kerosene_volumetric_energy_density']) - else: - mission_energy_amount = dict_ac_data['mission_energy_amount']['mission_energy_amount_ID0'] - - """ Calculate wing tank volume. """ - # Wing tank volume must be calculated for all possible tank configurations. - dict_tank_design = calculate_wing_tanks(dict_ac_data, dict_tank_design, runtime_output, mission_energy_amount) - total_tank_energy += dict_tank_design['tmp_energies']['wing_tank_with_center_tank'] - wing_tank_energy_without_wing_center_tank += dict_tank_design['tmp_energies']['wing_tank_without_center_tank'] - # Check if wing tanks can store mission energy amount. - if wing_tank_energy_without_wing_center_tank >= mission_energy_amount: - runtime_output.print('Wing center tank created but unnecessary and remains empty.') - energy_demand_covered = True - elif total_tank_energy >= mission_energy_amount: - runtime_output.print('Wing center tank necessary.') - energy_demand_covered = True - else: - energy_demand_covered = False - - """ Print information or calculate other fuel tanks. """ - if energy_demand_covered: - # Energy amount that can be stored in wing tanks already covers mission energy amount. All other tanks are - # generated but remain empty ('volume_available' and 'energy_available' are 0). - match dict_tank_design['tank_configuration']: - case 'wing_with_trim_tank': - runtime_output.print('Trim tank is generated but unnecessary and remains empty.') - case 'wing_with_additional_center_tank': - runtime_output.print('Additional center tank is generated but unnecessary and remains empty.') - case 'wing_with_additional_center_and_trim_tank': - runtime_output.print('Trim and additional center tanks are generated but unnecessary and ' - + 'remain empty.') - else: - # If energy amount not covered by wing tanks: Calculate other fuel tanks (depending on tank configuration). - # Calculate additional tank volumes according to defined tank configuration. - match dict_tank_design['tank_configuration']: - case 'wing_with_trim_tank': - dict_tank_design = calculate_trim_tank(dict_ac_data, dict_tank_design, runtime_output) - total_tank_energy += dict_tank_design['tmp_energies']['trim_tank'] - if total_tank_energy >= mission_energy_amount: - energy_demand_covered = True - case 'wing_with_additional_center_tank': - dict_tank_design = calculate_additional_center_tank(dict_ac_data, dict_tank_design, runtime_output) - total_tank_energy += ( - dict_tank_design['tmp_energies']['additional_center_tank']) - if total_tank_energy >= mission_energy_amount: - energy_demand_covered = True - case 'wing_with_additional_center_and_trim_tank': - dict_tank_design = calculate_trim_tank(dict_ac_data, dict_tank_design, runtime_output) - total_tank_energy += dict_tank_design['tmp_energies']['trim_tank'] - if total_tank_energy >= mission_energy_amount: - energy_demand_covered = True - runtime_output.print('Additional center tank is generated but unnecessary and remains empty.') - else: - dict_tank_design = calculate_additional_center_tank(dict_ac_data, dict_tank_design, runtime_output) - additional_tank_energy += dict_tank_design['tmp_energies']['additional_center_tank'] - total_tank_energy += additional_tank_energy - if total_tank_energy >= mission_energy_amount: - energy_demand_covered = True - - # Final check if tanks can store mission energy amount. - if energy_demand_covered: - runtime_output.print('Tank design successful.') - else: - energy_missing = mission_energy_amount - total_tank_energy - volume_missing = energy_missing/dict_ac_data['kerosene_volumetric_energy_density'] - runtime_output.print('Attention: Necessary amount of fuel cannot be stored in selected tanks! ' - + str(int(round(energy_missing,0))) + 'J (' + str(round(volume_missing,2)) + - 'L) missing.') - - """ Calculate total tank parameters. """ - dict_tank_design['total_tank_parameters'] = {} - # Tank mass (integral tank -> equals zero). - dict_tank_design['total_tank_parameters']['mass'] = 0.0 - # Tank position. - dict_tank_design['total_tank_parameters']['position'] = {} - dict_tank_design['total_tank_parameters']['position']['x'] = ( - dict_tank_design['geometry']['wing']['position']['x']) - dict_tank_design['total_tank_parameters']['position']['y'] = ( - dict_tank_design['geometry']['wing']['position']['y']) - dict_tank_design['total_tank_parameters']['position']['z'] = ( - dict_tank_design['geometry']['wing']['position']['z']) - # Inertia (are set to zero). - inertia_list = ['j_xx', 'j_yy', 'j_zz', 'j_xy', 'j_xz', 'j_yx', 'j_yz', 'j_zx', 'j_zy'] - dict_tank_design['total_tank_parameters']['inertia'] = {} - for inertia in inertia_list: - dict_tank_design['total_tank_parameters']['inertia'][inertia] = 0.0 - # Center of gravity (equals zero since integral tank has no mass). - dict_tank_design['total_tank_parameters']['center_of_gravity'] = {} - dict_tank_design['total_tank_parameters']['center_of_gravity']['x'] = 0.0 - dict_tank_design['total_tank_parameters']['center_of_gravity']['y'] = 0.0 - dict_tank_design['total_tank_parameters']['center_of_gravity']['z'] = 0.0 - - # Additional fuselage length equals zero for kerosene driven aircraft. - dict_tank_design['total_tank_parameters']['additional_fuselage_length'] = 0.0 - - # Prints. - runtime_output.debug('Debug: The "calculate_tanks" function was successfully executed.') - - return dict_tank_design - - -def calculate_wing_tanks(dict_ac_data, dict_tank_design, runtime_output, mission_energy_amount): - """Calculate energy contained in wing tanks. - - In the first step, the volume of the obelisks (according to Torenbeek or Simpson) is calculated for every given - tank entity. Afterwards, the actual tank volume is calculated, considering volume loss due to internal structure - of integral tanks and buffer for temperature-dependent expansion of fuel. All the volumes are summed up - ('volume_obelisks_without_center_tank'), except for the center tank volume that is saved in a separate parameter - ('volume_obelisk_center_tank'). The energy contained in the tank can than be calculated by multiplying the actual - volume with the volumetric energy density of kerosene. - Finally it is checked if the wing center tank is necessary. If the left and right inner and outer wing tanks are - big enough to store the energy required, the wing center tank values are set to zero. - - :param dict dict_design: Dictionary containing data from aircraft exchange and module configuration file - :param dict dict_tank_design: Dictionary containing tank design parameter - :param logging.Logger runtime_output: Logging object used for capturing log messages in the module - :param float mission_energy_amount: Mission energy amount in Joule - :return dict dict_tank_design: Dictionary containing tank design parameter - """ - # Extract necessary data from 'dict_ac_data'. - factor_volume_usable = dict_ac_data['factor_volume_usable'] - # Since a vent tank is considered, no temperature expansion allowance has to be considered for wing tanks. - temperature_expansion_allowance = 1.0 - - wing_geometry_dict = dict_tank_design['geometry']['wing'] - wing_section_dict = dict_tank_design['geometry']['wing']['sections'] - wing_symmetry = dict_tank_design['geometry']['wing']['information']['wing_symmetry'] - - # Get all tank entities that are located at the wing. - wing_tank_entity_list = [k for k, v in dict_tank_design.items() - if k.startswith('tank_') and k[5:].isdigit() and v['tank_location'] == 'wing'] - wing_center_tank_entity = [k for k in wing_tank_entity_list - if dict_tank_design[k]['tank_position'].endswith('center')][0] - # Calculate distance from wing root to wing tip. - distance_wing_root_to_tip = (wing_section_dict['wing_tip']['position']['y'] - - wing_section_dict['wing_root']['position']['y']) - # Calculate span offset for tank sections. - center_tank_span_offset = -(wing_section_dict['wing_root']['position']['y']* - dict_ac_data['buffer_center_tank_segment']) - inner_wing_tank_span_offset = distance_wing_root_to_tip*dict_ac_data['buffer_inner_tank_segment'] - kink_section_span_offset = 0.0 - outer_vent_tank_span_offset = -(distance_wing_root_to_tip*dict_ac_data['buffer_outer_tank_segment']) - - # Set tank sections according to number of wing sections. - number_of_wing_sections = len(wing_section_dict) - if number_of_wing_sections < 3: - runtime_output.critical('Error: Not enough wing sections given. ' - + 'Program aborted.') - sys.exit(1) - elif number_of_wing_sections == 3: - sections = {'outer_center_tank_section': 'wing_root', - 'section_0': 'wing_root', - 'section_1': 'wing_tip', - 'outer_vent_tank_section': 'wing_tip'} - vent_tank_inner_surface = 'section_1' - # Span offsets for 'center_tank', 'section_0', 'section_1', and 'outer_vent_tank_section'. - span_offset_list = [center_tank_span_offset, inner_wing_tank_span_offset, kink_section_span_offset, - outer_vent_tank_span_offset] - elif number_of_wing_sections == 4: - sections = {'outer_center_tank_section': 'wing_root', - 'section_0': 'wing_root', - 'section_1': 'kink', - 'section_2': 'wing_tip', - 'outer_vent_tank_section': 'wing_tip'} - vent_tank_inner_surface = 'section_2' - # Span offsets for 'center_tank', 'section_0', 'section_1', 'section_2', and 'outer_vent_tank_section'. - span_offset_list = [center_tank_span_offset, inner_wing_tank_span_offset, kink_section_span_offset, - outer_vent_tank_span_offset, outer_vent_tank_span_offset] - else: - runtime_output.critical('Error: Too many wing sections given. ' - + 'Program aborted.') - sys.exit(1) - - # Prepare tank sections. - i = 0 - for sec, template in sections.items(): - wing_section_dict[sec] = {} - wing_section_dict[sec]['position'] = {} - wing_section_dict[sec]['position']['y'] = ( - wing_section_dict[template]['position']['y'] + span_offset_list[i]) - wing_section_dict[sec]['span_index'] = abs(int(round( - wing_section_dict[sec]['position']['y']*100, 0))) - wing_section_dict[sec]['position']['x'] = ( - wing_geometry_dict['interpolated_values']['x_coordinate_leading_edge'] - [wing_section_dict[sec]['span_index']]) - wing_section_dict[sec]['position']['z'] = ( - #wing_section_dict[template]['position']['z']) - wing_geometry_dict['interpolated_values']['z_coordinate_leading_edge'] - [wing_section_dict[sec]['span_index']]) - # span_index = wing_section_dict[sec]['span_index'] - i += 1 - - """Calculate vent tank.""" - # Add vent tank to tank design dict. - dict_tank_design['vent_tank'] = {} - dict_tank_design['vent_tank']['tank_location'] = 'wing' - dict_tank_design['vent_tank']['tank_position'] = 'vent' - # CS 25.969: Vent tank capacity must at least be equal to 2 percent of tank capacity (1 percent per side). - dict_tank_design['vent_tank']['energy_required'] = 0.01*mission_energy_amount - vent_tank_outer_surface = 'outer_vent_tank_section' - - # Calculate energy contained in vent tank iteratively. Position of inner surface is adjusted in 1 mm steps. - vent_tank_energy = 0 - while vent_tank_energy < dict_tank_design['vent_tank']['energy_required']: - vent_tank_volume = calculate_obelisk_volume(dict_ac_data['obelisk_volume_calculation_method'], - wing_geometry_dict, - vent_tank_inner_surface, vent_tank_outer_surface) - vent_tank_volume_actual = factor_volume_usable*temperature_expansion_allowance*vent_tank_volume - vent_tank_energy = vent_tank_volume_actual*dict_ac_data['kerosene_volumetric_energy_density'] - wing_section_dict[vent_tank_inner_surface]['span_index'] -= 1 - - # Update x position of 'section_2' aka. 'vent_tank_inner_surface'. - span_idx = wing_section_dict[vent_tank_inner_surface]['span_index'] - wing_section_dict[vent_tank_inner_surface]['position']['x'] = ( - wing_geometry_dict['interpolated_values']['x_coordinate_leading_edge'][span_idx]) - wing_section_dict[vent_tank_inner_surface]['position']['y'] = span_idx/100 - - # TODO: Eventuell löschen, wenn plot nicht mehr benötigt. - dict_tank_design['vent_tank']['geometry'] = {} - dict_tank_design['vent_tank']['geometry']['total'] = {} - dict_tank_design['vent_tank']['geometry']['total']['position'] = {} - dict_tank_design['vent_tank']['geometry']['total']['position']['x'] = ( - wing_geometry_dict['interpolated_values']['x_coordinate_front_spar'][span_idx]) - dict_tank_design['vent_tank']['geometry']['total']['position']['y'] = ( - wing_section_dict[vent_tank_inner_surface]['position']['y']) - dict_tank_design['vent_tank']['geometry']['inner_surface'] = {} - dict_tank_design['vent_tank']['geometry']['inner_surface']['width'] = ( - wing_geometry_dict['interpolated_values']['tank_lengths'][span_idx]) - dict_tank_design['vent_tank']['volume_available'] = vent_tank_volume_actual - dict_tank_design['vent_tank']['energy_available'] = vent_tank_energy - - """Calculate tank volumes (wing geometry does not change from here).""" - # Initialize parameter. - actual_volume_center_tank = 0 - actual_volume_without_center_tank = 0 - - # Iterate through tank entities. - wing_tank_entity_list = [key for key, value in dict_tank_design.items() if key.startswith('tank_') - and key[5:].isdigit() and value['tank_location'] == 'wing'] - for wing_tank_entity in wing_tank_entity_list: - # Initialize empty dictionary and lists. - dict_tank_design[wing_tank_entity]['geometry'] = {} - dict_tank_design[wing_tank_entity]['geometry']['inner_surface'] = {} - dict_tank_design[wing_tank_entity]['geometry']['outer_surface'] = {} - inner_surface_str = str() - outer_surface_str = str() - - if wing_symmetry: - if number_of_wing_sections == 4: - # Set sections depending on tank position. - match dict_tank_design[wing_tank_entity]['tank_position']: - case 'inner_left' | 'inner_right': - inner_surface_str = 'section_0' - outer_surface_str = 'section_1' - case 'outer_left' | 'outer_right': - inner_surface_str = 'section_1' - outer_surface_str = 'section_2' - case 'center': - inner_surface_str = 'fuselage_center_line' - outer_surface_str = 'outer_center_tank_section' - else: - runtime_output.critical('Error: Wing without kink not implemented yet! Program aborted.') - sys.exit(1) - else: - runtime_output.critical('Error: Wing not symmetric! ' - + 'Program aborted.') - sys.exit(1) - - # Calculate obelisk volume. - obelisk_volume = calculate_obelisk_volume(dict_ac_data['obelisk_volume_calculation_method'], - wing_geometry_dict, inner_surface_str, outer_surface_str) - # Calculate actual tank volume (considering volume loss due to internal structure of integral tanks and buffer - # for temperature-dependent expansion of fuel). - if dict_tank_design[wing_tank_entity]['tank_position'] == 'center': - obelisk_volume *= 2 - actual_volume = factor_volume_usable*temperature_expansion_allowance*obelisk_volume - # Sum up volume of all tanks except wing center tank. - if dict_tank_design[wing_tank_entity]['tank_position'] != 'center': - actual_volume_without_center_tank += actual_volume - # Set volume of wing center tank. - else: - actual_volume_center_tank = actual_volume - - # Write data to 'dict_tank_design'. - dict_tank_design[wing_tank_entity]['volume_available'] = actual_volume - dict_tank_design[wing_tank_entity]['energy_available'] = ( - actual_volume*dict_ac_data['kerosene_volumetric_energy_density']) - - # # Add required output data: x, y, and z position, shape, height, width, and length. - # for surface_str, surface_name in zip([inner_surface_str, outer_surface_str], - # ['inner_surface', 'outer_surface']): - # surface_data = wing_section_dict[surface_str] - # span_idx = surface_data['span_index'] - - # # Extract x and z coordinates. - # x_coordinate = wing_geometry_dict['interpolated_values']['x_coordinate_front_spar'][span_idx] - # z_coordinate = surface_data['position']['z'] - - # # Adjust y coordinate for left hand tanks. - # y_coordinate = surface_data['position']['y'] - # if 'left' in dict_tank_design[wing_tank_entity]['tank_position']: - # y_coordinate = -(surface_data['position']['y']) - - # dict_tank_design[wing_tank_entity]['geometry'][surface_name] = { - # 'section_name': surface_str, - # 'position': { - # 'x': x_coordinate, - # 'y': y_coordinate, - # 'z': z_coordinate, - # }, - # 'shape': 'rectangular', - # 'height': wing_geometry_dict['interpolated_values']['tank_heights'][span_idx], - # 'width': wing_geometry_dict['interpolated_values']['tank_lengths'][span_idx], - # 'length': 0.0 - # } - - # # Correct wing center tank information. - # if 'center' in dict_tank_design[wing_tank_entity]['tank_position']: - # dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['position']['y'] = ( - # dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['position']['y'] - # ) - # dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['position']['y'] = -( - # dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['position']['y'] - # ) - - # Calculate tank mass (equals zero due to integral tank). - dict_tank_design[wing_tank_entity]['geometry']['total'] = {} - dict_tank_design[wing_tank_entity]['geometry']['total']['mass'] = 0.0 - # Set inertia to zero. - inertia_list = ['j_xx', 'j_yy', 'j_zz', 'j_xy', 'j_xz', 'j_yx', 'j_yz', 'j_zx', 'j_zy'] - dict_tank_design[wing_tank_entity]['geometry']['total']['inertia'] = {} - for inertia in inertia_list: - dict_tank_design[wing_tank_entity]['geometry']['total']['inertia'][inertia] = 0.0 - # Center of gravity equals zero since integral tank has no mass. - dict_tank_design[wing_tank_entity]['geometry']['total']['center_of_gravity'] = {} - dict_tank_design[wing_tank_entity]['geometry']['total']['center_of_gravity']['x'] = 0.0 - dict_tank_design[wing_tank_entity]['geometry']['total']['center_of_gravity']['y'] = 0.0 - dict_tank_design[wing_tank_entity]['geometry']['total']['center_of_gravity']['z'] = 0.0 - - # Calculate position of tank entity (foremost point of tank). - dict_tank_design[wing_tank_entity]['geometry']['total']['position'] = {} - dict_tank_design[wing_tank_entity]['geometry']['total']['position']['x'] = ( - wing_geometry_dict['interpolated_values']['x_coordinate_front_spar'][ - wing_section_dict[inner_surface_str]['span_index'] - ]) - if (dict_tank_design[wing_tank_entity]['tank_position'] == 'inner_left') or ( - dict_tank_design[wing_tank_entity]['tank_position'] == 'outer_left'): - dict_tank_design[wing_tank_entity]['geometry']['total']['position']['y'] = -abs( - wing_section_dict[inner_surface_str]['position']['y']) - else: - dict_tank_design[wing_tank_entity]['geometry']['total']['position']['y'] = abs( - wing_section_dict[inner_surface_str]['position']['y']) - dict_tank_design[wing_tank_entity]['geometry']['total']['position']['z'] = ( - wing_section_dict[inner_surface_str]['position']['z']) - # Direction. - dict_tank_design[wing_tank_entity]['geometry']['total']['direction'] = {} - dict_tank_design[wing_tank_entity]['geometry']['total']['direction']['x'] = 0 - dict_tank_design[wing_tank_entity]['geometry']['total']['direction']['y'] = 1 - dict_tank_design[wing_tank_entity]['geometry']['total']['direction']['z'] = 0 - - # Add required output data: x, y, and z position, shape, height, width, and length. - for surface_str, surface_name in zip([inner_surface_str, outer_surface_str], - ['inner_surface', 'outer_surface']): - surface_data = wing_section_dict[surface_str] - span_idx = surface_data['span_index'] - # Inner surface equals tank entity position. - if surface_name == 'inner_surface': - x_coordinate = 0.0 - y_coordinate = 0.0 - z_coordinate = 0.0 - else: - # Adjust y coordinate for left hand tanks. - y_coordinate_entity = dict_tank_design[wing_tank_entity]['geometry']['total']['position']['y'] - if 'left' in dict_tank_design[wing_tank_entity]['tank_position']: - y_coordinate_section = -surface_data['position']['y'] - else: - y_coordinate_section = surface_data['position']['y'] - y_coordinate = y_coordinate_section - y_coordinate_entity - # if 'left' in dict_tank_design[wing_tank_entity]['tank_position']: - # y_coordinate = y_coordinate*(-1) - # x coordinate. - x_coordinate_entity = dict_tank_design[wing_tank_entity]['geometry']['total']['position']['x'] - x_coordinate_section = wing_geometry_dict['interpolated_values']['x_coordinate_front_spar'][span_idx] - if x_coordinate_entity < 0 and x_coordinate_section > 0 and x_coordinate_section > x_coordinate_entity: - delta_x = x_coordinate_entity - x_coordinate_section - else: - delta_x = x_coordinate_section - x_coordinate_entity - # if x_coordinate_section < 0 and x_coordinate_section < x_coordinate_entity: - # delta_x = x_coordinate_section - x_coordinate_entity - # elif x_coordinate_section > 0 and x_coordinate_section < x_coordinate_entity: - # delta_x = x_coordinate_entity - x_coordinate_section - # elif x_coordinate_section > 0 and x_coordinate_entity == 0: - # delta_x = x_coordinate_section - x_coordinate_entity - # elif x_coordinate_section > x_coordinate_entity: - # delta_x = x_coordinate_entity - x_coordinate_section - # else: - # delta_x = 0 - x_coordinate = delta_x - # z coordinate. - z_coordinate_entity = dict_tank_design[wing_tank_entity]['geometry']['total']['position']['z'] - z_coordinate_section = surface_data['position']['z'] - # if z_coordinate_section < 0 and z_coordinate_section < z_coordinate_entity: - # delta_z = z_coordinate_section - z_coordinate_entity - # elif z_coordinate_section > 0 and z_coordinate_section < z_coordinate_entity: - # delta_z = z_coordinate_entity - z_coordinate_section - # elif z_coordinate_section > 0 and z_coordinate_entity == 0: - # delta_z = z_coordinate_section - z_coordinate_entity - # elif z_coordinate_section > z_coordinate_entity: - # delta_z = z_coordinate_entity - z_coordinate_section - # else: - # delta_z = 0 - z_coordinate = z_coordinate_section - z_coordinate_entity - - dict_tank_design[wing_tank_entity]['geometry'][surface_name] = { - 'section_name': surface_str, - 'position': { - 'x': x_coordinate, - 'y': y_coordinate, - 'z': z_coordinate, - }, - 'shape': 'rectangular', - 'height': wing_geometry_dict['interpolated_values']['tank_heights'][span_idx], - 'width': wing_geometry_dict['interpolated_values']['tank_lengths'][span_idx], - 'length': 0.0 - } - - # Correct wing center tank information. - if 'center' in dict_tank_design[wing_tank_entity]['tank_position']: - dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['position']['y'] = ( - dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['position']['y']/2 - ) - dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['position']['y'] = -( - dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['position']['y'] - ) - - # Extract x and z coordinates. - # x_coordinate = wing_geometry_dict['interpolated_values']['x_coordinate_front_spar'][span_idx] - # z_coordinate = surface_data['position']['z'] - - # # Adjust y coordinate for left hand tanks. - # y_coordinate = surface_data['position']['y'] - # if 'left' in dict_tank_design[wing_tank_entity]['tank_position']: - # y_coordinate = -(surface_data['position']['y']) - - # dict_tank_design[wing_tank_entity]['geometry'][surface_name] = { - # 'section_name': surface_str, - # 'position': { - # 'x': x_coordinate, - # 'y': y_coordinate, - # 'z': z_coordinate, - # }, - # 'shape': 'rectangular', - # 'height': wing_geometry_dict['interpolated_values']['tank_heights'][span_idx], - # 'width': wing_geometry_dict['interpolated_values']['tank_lengths'][span_idx], - # 'length': 0.0 - # } - - # Centroid. - # Assumption: inner surface area always greater than outer surface area. - inner_surface_area = (dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['height'] - *dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['width']) - outer_surface_area = (dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['height'] - *dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['width']) - y_position_outer_surface = dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['position']['y'] - y_position_inner_surface = dict_tank_design[wing_tank_entity]['geometry']['total']['position']['y'] - tank_span = y_position_outer_surface - y_position_inner_surface - y_position_center_of_mass = y_position_inner_surface + (tank_span/4*((inner_surface_area - + 2*math.sqrt(inner_surface_area*outer_surface_area) - + outer_surface_area)/(inner_surface_area + outer_surface_area))) - centroid_y = y_position_inner_surface + ((tank_span - *((dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['height'] - *dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['width']) - +(dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['height'] - *dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['width']) - +(dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['height'] - *dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['width']) - +(3*dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['height'] - *dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['width']))) - /(2 - *(2*dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['height'] - *dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['width']) - + (dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['height'] - *dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['width']) - + (dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['height'] - *dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['width']) - + (2*dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['height'] - *dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['width']))) - x_position_cog_inner_surface = dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['width']/2 - x_position_cog_outer_surface = dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['width']/2 - z_position_cog_inner_surface = dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['height']/2 - z_position_cog_outer_surface = dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['height']/2 - x_position_center_of_mass = ((inner_surface_area*x_position_cog_inner_surface - + outer_surface_area*x_position_cog_outer_surface) - /(inner_surface_area + outer_surface_area)) - z_position_center_of_mass = ((inner_surface_area*z_position_cog_inner_surface - + outer_surface_area*z_position_cog_outer_surface) - /(inner_surface_area + outer_surface_area)) - - dict_tank_design[wing_tank_entity]['geometry']['total']['centroid'] = {} - if wing_tank_entity == wing_center_tank_entity: - dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['x'] = x_position_center_of_mass - # dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['x'] = ( - # dict_tank_design['tank_4']['geometry']['inner_surface']['position']['x'] - # + dict_tank_design['tank_4']['geometry']['inner_surface']['width']/2 - # ) - dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['y'] = 0 - dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['z'] = 0 - else: - dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['x'] = x_position_center_of_mass - dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['y'] = y_position_center_of_mass - dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['z'] = z_position_center_of_mass - - # Sum up. - volume_wing_tank_without_center_tank = actual_volume_without_center_tank - volume_wing_tank_with_center_tank = actual_volume_without_center_tank + actual_volume_center_tank - - """Calculate fuel mass and energy.""" - # Convert wing fuel volume to energy. - energy_wing_tank_without_center_tank = (volume_wing_tank_without_center_tank - *dict_ac_data['kerosene_volumetric_energy_density']) - energy_wing_tank_with_center_tank = (volume_wing_tank_with_center_tank - *dict_ac_data['kerosene_volumetric_energy_density']) - - """Check if tanks can store required energy.""" - # If left and right inner and outer wing tanks are big enough to store required amount of energy, wing center tank - # remains empty. - if energy_wing_tank_without_center_tank >= mission_energy_amount: - dict_tank_design[wing_center_tank_entity]['volume_available'] = 0 - dict_tank_design[wing_center_tank_entity]['energy_available'] = 0 - - # Debug print. - runtime_output.debug('Debug: The "calculate_wing_tanks" function was successfully executed.') - - # Prepare data output. - dict_tank_design['tmp_energies'] = {} - dict_tank_design['tmp_energies']['wing_tank_without_center_tank'] = float(energy_wing_tank_without_center_tank) - dict_tank_design['tmp_energies']['wing_tank_with_center_tank'] = float(energy_wing_tank_with_center_tank) - - return dict_tank_design - - -def calculate_additional_center_tank(dict_ac_data, dict_tank_design, runtime_output): - """Calculate energy contained in additional center tank. - - This code enables the installation of an additional center tank as LD3-45 container. Therefore, it is checked - initially whether the existing cargo compartment provides sufficient height. If this is not the case, all export - values are set to zero. Otherwise, the LD3-45 container is installed 10 cm behind the end of the landing gear bay. - It is assumed that this is approximately in line with the trailing edge of the wing. The volume and dimensions of - the container are taken from [1]. - - Attention: Please note that it is not possible to calculate more than one additional center tank at the moment! - - [1] https://www.lufthansa-cargo.com/de/fleet-ulds/ulds/containers - - :param dict dict_design: Dictionary containing data from aircraft exchange and module configuration file - :param dict dict_tank_design: Dictionary containing tank design parameter - :param logging.Logger runtime_output: Logging object used for capturing log messages in the module - :return dict dict_tank_design: Dictionary containing tank design parameter - """ - # Extract necessary data from 'dict_ac_data'. - factor_volume_usable = dict_ac_data['factor_volume_usable'] - temperature_expansion_allowance = dict_ac_data['temperature_expansion_allowance'] - - # Get all tank entities that are located at the fuselage (additional center tanks). - additional_center_tank_entity_list = [k for k, v in dict_tank_design.items() if k.startswith('tank_') - and k[5:].isdigit() and v['tank_location'] == 'fuselage'] - number_of_additional_center_tanks = len(additional_center_tank_entity_list) - if number_of_additional_center_tanks > 1: - runtime_output.critical('Error: Tank design not possible for more than one additional center tank! ' - + 'Program aborted.') - sys.exit('Exit information: Tank design not possible for more than one additional center tank!') - # Extract identifier of center tank. - center_tank_entity = additional_center_tank_entity_list[0] - - """Prepare data of LD3-45 container.""" - # Assumption: Angle at base equals 45 degree and usable volume of container taken from [1] (not calculated). - height_container = 1.15 - width_base_container = 1.53 - width_top_container = 2.44 - length_container = 1.56 - height_base_part_container = (width_top_container - width_base_container)*0.5 - height_top_part_container = height_container - height_base_part_container - volume_container = 3.4 - # Calculate areas and volumes for later use. - area_base_part = width_base_container*length_container - area_top_part = width_top_container*length_container - volume_base_part = ( - (height_base_part_container*width_top_container*length_container) - - height_base_part_container*height_base_part_container*length_container) - volume_top_part = width_top_container*length_container*height_top_part_container - - # Check, if cargo compartment height sufficient to store LD3-45 container. - if dict_tank_design['geometry']['fuselage']['cargo_compartment_height'] < height_container: - additional_center_tank_design_possible = False - runtime_output.warning('Warning: Cargo compartment height too small. Storage of LD3-45 container not ' - + 'possible. All values are set to 0.') - else: - additional_center_tank_design_possible = True - - """Calculate additional center tank.""" - # Calculate usable volume and convert to energy. - center_tank_volume = volume_container*factor_volume_usable*temperature_expansion_allowance - energy_kerosene_available = center_tank_volume*dict_ac_data['kerosene_volumetric_energy_density'] - - # Calculate ACT and necessary values if possible. - if additional_center_tank_design_possible: - # Write data to 'dict_tank_design'. - dict_tank_design[center_tank_entity]['volume_available'] = center_tank_volume - dict_tank_design[center_tank_entity]['energy_available'] = energy_kerosene_available - - # Prepare geometrical data for additional center tank. - # Assumption: Landing gear bay ends at wing trailing edge position. - # x_coordinate_wing_leading_edge = dict_ac_data['wing_position_x'] - chord_length_at_fuselage_center_line = ( - dict_tank_design['geometry']['wing']['sections']['fuselage_center_line']['chord_length']) - # x_coordinate_wing_trailing_edge = x_coordinate_wing_leading_edge + chord_length_at_fuselage_center_line - buffer = 0.1 - x_coordinate = chord_length_at_fuselage_center_line + buffer - y_coordinate = [width_top_container*0.5, width_base_container*0.5, 0.0, - -width_base_container*0.5, -width_top_container*0.5] - cargo_floor_below_wing = (dict_tank_design['geometry']['fuselage']['z_coordinate_cargo_floor'] - < dict_tank_design['geometry']['wing']['position']['z']) - z_offset_wing_to_cargo_floor = abs(dict_tank_design['geometry']['wing']['position']['z'] - - dict_tank_design['geometry']['fuselage']['z_coordinate_cargo_floor']) - if cargo_floor_below_wing: - relative_z_position_cargo_floor_to_wing = -z_offset_wing_to_cargo_floor - else: - relative_z_position_cargo_floor_to_wing = z_offset_wing_to_cargo_floor - z_coordinate = relative_z_position_cargo_floor_to_wing + np.array([ - # plane_0 - height_base_part_container + height_top_part_container*0.5, - # plane_1, plane_2, plane_3 - height_container*0.5, height_container*0.5, height_container*0.5, - # plane_4 - height_base_part_container + height_top_part_container*0.5]) - heights = [height_top_part_container, height_container, height_container, - height_container, height_top_part_container] - plane_names = ['cross_section_0', 'cross_section_1', 'cross_section_2', 'cross_section_3', 'cross_section_4'] - - # Create the dictionary for center tank geometry. - dict_tank_design[center_tank_entity]['geometry'] = { - plane: { - 'section_name': plane, - 'position': { - 'x': x_coordinate, - 'y': y_coordinate[i], - 'z': z_coordinate[i] - }, - 'shape': 'rectangular', - 'height': heights[i], - 'width': length_container, - 'length': 0.0 - } - for i, plane in enumerate(plane_names) - } - - # Define total tank geometry values. - # Calculate tank mass (equals zero due to integral tank). - dict_tank_design[center_tank_entity]['geometry']['total'] = {} - dict_tank_design[center_tank_entity]['geometry']['total']['mass'] = 300 - # Set inertia to zero. - inertia_list = ['j_xx', 'j_yy', 'j_zz', 'j_xy', 'j_xz', 'j_yx', 'j_yz', 'j_zx', 'j_zy'] - dict_tank_design[center_tank_entity]['geometry']['total']['inertia'] = {} - for inertia in inertia_list: - dict_tank_design[center_tank_entity]['geometry']['total']['inertia'][inertia] = 0.0 - # Center of gravity. - # Prepare data for z coordinate calculation. - h = height_base_part_container - a_1 = area_top_part - a_2 = area_base_part - y_0 = ((h*(a_1 + 2*math.sqrt(a_1*a_2) + 3*a_2)) - /(4*(a_1 + math.sqrt(a_1*a_2)) + a_2)) - z_coordinate_cog_base_part = (dict_tank_design['geometry']['fuselage']['z_coordinate_cargo_floor'] - + (h - y_0)) - z_coordinate_cog_top_part = (dict_tank_design['geometry']['fuselage']['z_coordinate_cargo_floor'] - + height_base_part_container + height_top_part_container*0.5) - z_cog = ((z_coordinate_cog_base_part*volume_base_part + z_coordinate_cog_top_part*volume_top_part) - /(volume_base_part + volume_top_part)) - - dict_tank_design[center_tank_entity]['geometry']['total']['center_of_gravity'] = {} - dict_tank_design[center_tank_entity]['geometry']['total']['center_of_gravity']['x'] = ( - x_coordinate + length_container*0.5) - dict_tank_design[center_tank_entity]['geometry']['total']['center_of_gravity']['y'] = 0.0 - dict_tank_design[center_tank_entity]['geometry']['total']['center_of_gravity']['z'] = z_cog - - # Calculate position of tank entity (foremost point of tank). - dict_tank_design[center_tank_entity]['geometry']['total']['position'] = {} - dict_tank_design[center_tank_entity]['geometry']['total']['position']['x'] = x_coordinate - dict_tank_design[center_tank_entity]['geometry']['total']['position']['y'] = 0.0 - dict_tank_design[center_tank_entity]['geometry']['total']['position']['z'] = z_coordinate[2] - # Direction. - dict_tank_design[center_tank_entity]['geometry']['total']['direction'] = {} - dict_tank_design[center_tank_entity]['geometry']['total']['direction']['x'] = 0 - dict_tank_design[center_tank_entity]['geometry']['total']['direction']['y'] = 1 - dict_tank_design[center_tank_entity]['geometry']['total']['direction']['z'] = 0 - - else: - # Set all export values to 0 if tank design not possible. - dict_tank_design[center_tank_entity]['geometry'] = { - 'cross_section_0': { - 'section_name': 'cross_section_0', - 'position': { - 'x': 0.0, - 'y': 0.0, - 'z': 0.0 - }, - 'shape': None, - 'height': 0.0, - 'width': 0.0, - 'length': 0.0 - } - } - # Tank mass. - dict_tank_design[center_tank_entity]['geometry']['total'] = {} - dict_tank_design[center_tank_entity]['geometry']['total']['mass'] = 0.0 - # Inertia. - inertia_list = ['j_xx', 'j_yy', 'j_zz', 'j_xy', 'j_xz', 'j_yx', 'j_yz', 'j_zx', 'j_zy'] - dict_tank_design[center_tank_entity]['geometry']['total']['inertia'] = {} - for inertia in inertia_list: - dict_tank_design[center_tank_entity]['geometry']['total']['inertia'][inertia] = 0.0 - # Center of gravity. - dict_tank_design[center_tank_entity]['geometry']['total']['center_of_gravity'] = {} - dict_tank_design[center_tank_entity]['geometry']['total']['center_of_gravity']['x'] = 0.0 - dict_tank_design[center_tank_entity]['geometry']['total']['center_of_gravity']['y'] = 0.0 - dict_tank_design[center_tank_entity]['geometry']['total']['center_of_gravity']['z'] = 0.0 - # Position of tank entity (foremost point of tank). - dict_tank_design[center_tank_entity]['geometry']['total']['position'] = {} - dict_tank_design[center_tank_entity]['geometry']['total']['position']['x'] = 0.0 - dict_tank_design[center_tank_entity]['geometry']['total']['position']['y'] = 0.0 - dict_tank_design[center_tank_entity]['geometry']['total']['position']['z'] = 0.0 - # Direction. - dict_tank_design[center_tank_entity]['geometry']['total']['direction'] = {} - dict_tank_design[center_tank_entity]['geometry']['total']['direction']['x'] = 0 - dict_tank_design[center_tank_entity]['geometry']['total']['direction']['y'] = 1 - dict_tank_design[center_tank_entity]['geometry']['total']['direction']['z'] = 0 - - # Debug print. - runtime_output.debug('Debug: The "calculate_additional_center_tank" function was successfully executed.') - - # Prepare data output. - dict_tank_design['tmp_energies'] = {} - dict_tank_design['tmp_energies']['additional_center_tank'] = float(energy_kerosene_available) - - return dict_tank_design - - -def calculate_trim_tank(dict_ac_data, dict_tank_design, runtime_output): - """Calculate energy contained in trim tank. - - [Add some more information here...] - - :param dict dict_design: Dictionary containing data from aircraft exchange and module configuration file - :param dict dict_tank_design: Dictionary containing tank design parameter - :param logging.Logger runtime_output: Logging object used for capturing log messages in the module - :return dict dict_tank_design: Dictionary containing tank design parameter - """ - # Extract values from 'dict_ac_data'. - factor_volume_usable = dict_ac_data['factor_volume_usable'] - temperature_expansion_allowance = dict_ac_data['temperature_expansion_allowance'] - - geometry_dict = dict_tank_design['geometry']['horizontal_stabilizer'] - section_dict = dict_tank_design['geometry']['horizontal_stabilizer']['sections'] - symmetry = dict_tank_design['geometry']['horizontal_stabilizer']['information']['symmetry'] - - # Get all tank entities that are located at the horizontal stabilizer (trim tank). - tank_entity = [k for k, v in dict_tank_design.items() if k.startswith('tank_') and k[5:].isdigit() - and v['tank_location'] == 'horizontal_stabilizer'][0] - - # Calculate distance from wing root to wing tip. - span = section_dict['tip']['position']['y'] - section_dict['fuselage_center_line']['position']['y'] - # Calculate span offset for tank. - outer_tank_span_offset = -(span*dict_ac_data['buffer_outer_tank_segment']) - - # Set tank sections according to number of wing sections. - number_of_sections = len(section_dict) - if number_of_sections < 2: - runtime_output.critical('Error: Not enough horizontal stabilizer sections given. Program aborted.') - sys.exit(1) - elif number_of_sections ==2: - sections = {'section_0': 'fuselage_center_line', - 'section_1': 'tip'} - # Span offsets for 'section_0' and 'section_1'. - span_offset_list = [0.0, outer_tank_span_offset] - elif number_of_sections == 3: - sections = {'section_0': 'fuselage_center_line', - 'section_1': 'tip'} - # Span offsets for 'section_0' and 'section_1'. - span_offset_list = [0.0, outer_tank_span_offset] - else: - runtime_output.critical('Error: Too many horizontal stabilizer sections given. ' - + 'Program aborted.') - sys.exit(1) - - # Prepare tank sections. - i = 0 - for sec, template in sections.items(): - section_dict[sec] = {} - section_dict[sec]['position'] = {} - section_dict[sec]['position']['y'] = section_dict[template]['position']['y'] + span_offset_list[i] - section_dict[sec]['span_index'] = int(round(section_dict[sec]['position']['y']*100, 0)) - section_dict[sec]['position']['x'] = ( - geometry_dict['interpolated_values']['x_coordinate_leading_edge'][section_dict[sec]['span_index']]) - section_dict[sec]['position']['z'] = ( - geometry_dict['interpolated_values']['z_coordinate_leading_edge'][section_dict[sec]['span_index']]) - i += 1 - - """Calculate tank volumes (geometry does not change from here).""" - # Initialize empty dictionary and lists. - dict_tank_design[tank_entity]['geometry'] = {} - dict_tank_design[tank_entity]['geometry']['inner_surface'] = {} - dict_tank_design[tank_entity]['geometry']['outer_surface'] = {} - inner_surface_str = str() - outer_surface_str = str() - - if symmetry: - if number_of_sections == 2: - inner_surface_str = 'section_0' - outer_surface_str = 'section_1' - elif number_of_sections == 3: - inner_surface_str = 'section_0' - outer_surface_str = 'section_1' - else: - runtime_output.critical('Error: Horizontal stabilizer not symmetric! Program aborted.') - sys.exit(1) - - # Calculate obelisk volume. - obelisk_volume = calculate_obelisk_volume(dict_ac_data['obelisk_volume_calculation_method'], - geometry_dict, inner_surface_str, outer_surface_str) - obelisk_volume *= 2 - - # Calculate actual tank volume (considering volume loss due to internal structure of integral tanks and buffer for - # temperature-dependent expansion of fuel). - volume_trim_tank = factor_volume_usable*temperature_expansion_allowance*obelisk_volume - - # Calculate energy and write data to 'dict_tank_design'. - dict_tank_design[tank_entity]['volume_available'] = volume_trim_tank - dict_tank_design[tank_entity]['energy_available'] = ( - volume_trim_tank*dict_ac_data['kerosene_volumetric_energy_density']) - - # Add required output data: x, y, and z position, shape, height, width, and length. - for surface_str, surface_name in zip([inner_surface_str, outer_surface_str], ['inner_surface', 'outer_surface']): - surface_data = section_dict[surface_str] - span_idx = surface_data['span_index'] - - # Extract x and z coordinates. - x_coordinate = geometry_dict['interpolated_values']['x_coordinate_front_spar'][span_idx] - y_coordinate = surface_data['position']['y'] - z_coordinate = surface_data['position']['z'] - - dict_tank_design[tank_entity]['geometry'][surface_name] = { - 'section_name': surface_str, - 'position': { - 'x': x_coordinate, - 'y': y_coordinate, - 'z': z_coordinate, - }, - 'shape': 'rectangular', - 'height': geometry_dict['interpolated_values']['tank_heights'][span_idx], - 'width': geometry_dict['interpolated_values']['tank_lengths'][span_idx], - 'length': 0.0 - } - # Rename and restructure dict. - dict_tank_design[tank_entity]['geometry']['cross_section_1'] = dict_tank_design[tank_entity]['geometry'].pop( - 'inner_surface') - dict_tank_design[tank_entity]['geometry']['cross_section_2'] = dict_tank_design[tank_entity]['geometry'].pop( - 'outer_surface') - - def deep_copy_dict(d): - if isinstance(d, dict): - return {key: deep_copy_dict(value) for key, value in d.items()} - elif isinstance(d, list): - return [deep_copy_dict(item) for item in d] - else: - return d - - independent_copy = deep_copy_dict(dict_tank_design[tank_entity]['geometry']['cross_section_2']) - independent_copy['position']['y'] *= -1 - dict_tank_design[tank_entity]['geometry']['cross_section_0'] = independent_copy - - """Set or calculate total tank parameters.""" - # Set tank mass to zero (integral tank). - dict_tank_design[tank_entity]['geometry']['total'] = {} - dict_tank_design[tank_entity]['geometry']['total']['mass'] = 0.0 - # Set inertia to zero. - inertia_list = ['j_xx', 'j_yy', 'j_zz', 'j_xy', 'j_xz', 'j_yx', 'j_yz', 'j_zx', 'j_zy'] - dict_tank_design[tank_entity]['geometry']['total']['inertia'] = {} - for inertia in inertia_list: - dict_tank_design[tank_entity]['geometry']['total']['inertia'][inertia] = 0.0 - # Center of gravity equals zero since integral tank has no mass. - dict_tank_design[tank_entity]['geometry']['total']['center_of_gravity'] = {} - dict_tank_design[tank_entity]['geometry']['total']['center_of_gravity']['x'] = 0.0 - dict_tank_design[tank_entity]['geometry']['total']['center_of_gravity']['y'] = 0.0 - dict_tank_design[tank_entity]['geometry']['total']['center_of_gravity']['z'] = 0.0 - # Calculate position of tank entity (foremost point of tank). - distance_wing_to_horizontal_stabilizer = (dict_tank_design['geometry']['horizontal_stabilizer']['position']['x'] - - dict_tank_design['geometry']['wing']['position']['x']) - wing_lower_than_horizontal_stabilizer = (dict_tank_design['geometry']['wing']['position']['z'] - < dict_tank_design['geometry']['horizontal_stabilizer']['position']['z']) - z_offset_wing_to_horizontal_stabilizer = abs(dict_tank_design['geometry']['horizontal_stabilizer']['position']['z'] - - dict_tank_design['geometry']['wing']['position']['z']) - if wing_lower_than_horizontal_stabilizer: - relative_z_position_horizontal_stabilizer_to_wing = z_offset_wing_to_horizontal_stabilizer - else: - relative_z_position_horizontal_stabilizer_to_wing = -z_offset_wing_to_horizontal_stabilizer - dict_tank_design[tank_entity]['geometry']['total']['position'] = {} - dict_tank_design[tank_entity]['geometry']['total']['position']['x'] = ( - distance_wing_to_horizontal_stabilizer - + geometry_dict['interpolated_values']['x_coordinate_front_spar'][section_dict[inner_surface_str] - ['span_index']]) - dict_tank_design[tank_entity]['geometry']['total']['position']['y'] = ( - section_dict[inner_surface_str]['position']['y']) - dict_tank_design[tank_entity]['geometry']['total']['position']['z'] = ( - relative_z_position_horizontal_stabilizer_to_wing) - # Direction. - dict_tank_design[tank_entity]['geometry']['total']['direction'] = {} - dict_tank_design[tank_entity]['geometry']['total']['direction']['x'] = 0 - dict_tank_design[tank_entity]['geometry']['total']['direction']['y'] = 1 - dict_tank_design[tank_entity]['geometry']['total']['direction']['z'] = 0 - - # Debug print. - runtime_output.debug('Debug: The "calculate_trim_tank" function was successfully executed.') - - # Prepare data output. - dict_tank_design['tmp_energies'] = {} - dict_tank_design['tmp_energies']['trim_tank'] = float(dict_tank_design[tank_entity]['energy_available']) - - return dict_tank_design +""" Module containing functions for kerosene tank calculation. """ +# Import standard libraries. +import sys +import math +import numpy as np + +# Import own libraries. +from pyunitconversion import constants + + +def calculate_tank_span(span_index_inner_surface, span_index_outer_surface): + """Tank span in m. + + Span index equals position in mm. + + :param int span_index_inner_surface: Index of span position of inner surface + :param int span_index_outer_surface: Index of span position of outer surface + :return float: Tank span in m + """ + tank_span = (span_index_outer_surface - span_index_inner_surface)/100 + + return tank_span + + +def calculate_obelisk_volume(method, dict_wing_geometry, inner_surface_str, outer_surface_str): + """Calculate geometrical volume of obelisk. + + This function calculates the obelisk volume according to the selected method (Torenbeek [1] or Simpson). + + [1] E. Torenbeek "Synthesis of Subsonic Airplane Design" (1982), Fig. B-4 [1] + + :param str method: Calculation method name + :param dict dict_wing_geometry: Dictionary containing wing geometry parameters + :param str inner_surface_str: Name of inner obelisk surface + :param str outer_surface_str: Name of outer obelisk surface + :return float: Volume of obelisk in m^3 + """ + interpolated_values = dict_wing_geometry['interpolated_values'] + + inner_span_index = abs(dict_wing_geometry['sections'][inner_surface_str]['span_index']) + outer_span_index = abs(dict_wing_geometry['sections'][outer_surface_str]['span_index']) + tank_span = (outer_span_index - inner_span_index)/100 + maximum_tank_height_inner = interpolated_values['tank_heights'][inner_span_index] + maximum_tank_height_outer = interpolated_values['tank_heights'][outer_span_index] + maximum_tank_length_inner = interpolated_values['tank_lengths'][inner_span_index] + maximum_tank_length_outer = interpolated_values['tank_lengths'][outer_span_index] + area_inner = maximum_tank_height_inner*maximum_tank_length_inner + area_outer = maximum_tank_height_outer*maximum_tank_length_outer + + match method: + case 'Torenbeek': + # Calculate volume of obelisk according to Torenbeek. + obelisk_volume = (tank_span/3*(area_inner + area_outer + + ((maximum_tank_height_inner*maximum_tank_length_outer) + + (maximum_tank_length_inner*maximum_tank_height_outer))/2)) + case 'Simpson': + # Calculate volume of obelisk according to Simpson's rule. + middle_area = (area_inner + area_outer)/2 + obelisk_volume = tank_span / 6.0 * (area_inner + 4.0 * middle_area + area_outer) + + return obelisk_volume + + +def calculate_kerosene_tanks(dict_ac_data, dict_tank_design, runtime_output): + """Calculate kerosene tanks of tube-and-wing aircraft. + + This function adds the following sub dictionaries to the 'dict_tank_design' dictionary: + ... + + :param dict dict_ac_data: Dictionary containing data from aircraft exchange and module configuration file + :param dict dict_tank_design: Dictionary containing tank design parameter + :param logging.Logger runtime_output: Logging object used for capturing log messages in the module + :return dict dict_tank_design: Dictionary containing tank design parameter + """ + # Extract data and initialize variables. + total_tank_energy = 0 + wing_tank_energy_without_wing_center_tank = 0 + kerosene_volumetric_energy_density = constants.KEROSENE_VOLUMETRIC_ENERGY_DENSITY + + """Check if initial sizing loop and estimate energy demand if necessary.""" + initial_sizing_loop = all(value is None for value in dict_ac_data['mission_energy_amount'].values()) + if initial_sizing_loop: + runtime_output.print('Initial sizing loop! Energy demand is estimated (3.35 liters per PAX per 100 km).') + energy_demand = 3.35 # in liter per passenger + mission_fuel_amount = dict_ac_data['number_of_passengers']*dict_ac_data['range']/1000*energy_demand/100 + mission_energy_amount = (mission_fuel_amount/1000*kerosene_volumetric_energy_density) + else: + mission_energy_amount = dict_ac_data['mission_energy_amount']['mission_energy_amount_ID0'] + + """ Calculate wing tank volume. """ + # Wing tank volume must be calculated for all possible tank configurations. + dict_tank_design = calculate_wing_tanks(dict_ac_data, dict_tank_design, runtime_output, mission_energy_amount) + total_tank_energy += dict_tank_design['tmp_energies']['wing_tank_with_center_tank'] + wing_tank_energy_without_wing_center_tank += dict_tank_design['tmp_energies']['wing_tank_without_center_tank'] + # Check if wing tanks can store mission energy amount. + if wing_tank_energy_without_wing_center_tank >= mission_energy_amount: + runtime_output.print('Energy check: Wing center tank created but unnecessary to store required energy ' + + 'amount.') + runtime_output.print('Energy check: Energy demand covered.') + # Set 'energy_required' to 0. + wing_tank_entity_list = [k for k, v in dict_tank_design.items() + if k.startswith('tank_') and k[5:].isdigit() and v['tank_location'] == 'wing'] + wing_center_tank_entity = [k for k in wing_tank_entity_list + if dict_tank_design[k]['tank_position'].endswith('center')][0] + dict_tank_design[wing_center_tank_entity]['energy_required_for_mission'] = False + # Set energy demand flag. + energy_demand_covered = True + elif total_tank_energy >= mission_energy_amount: + runtime_output.print('Energy check: Wing center tank necessary to store required energy amount.') + runtime_output.print('Energy check: Energy demand covered.') + energy_demand_covered = True + else: + energy_demand_covered = False + + # TEST + # If energy amount not covered by wing tanks: Calculate other fuel tanks (depending on tank configuration). + # Calculate additional tank volumes according to defined tank configuration. + match dict_tank_design['tank_configuration']: + case 'wing_with_trim_tank': + if not energy_demand_covered: + runtime_output.print('Energy check: Demand not covered yet. Continue with design of trim tank.') + dict_tank_design = calculate_trim_tank(dict_ac_data, dict_tank_design, runtime_output, + energy_demand_covered) + total_tank_energy += dict_tank_design['tmp_energies']['trim_tank'] + if energy_demand_covered: + runtime_output.print('Trim tank is generated but unnecessary to store required energy amount.') + else: + if total_tank_energy >= mission_energy_amount: + energy_demand_covered = True + runtime_output.print('Energy check: Energy demand covered.') + case 'wing_with_additional_center_tank': + if not energy_demand_covered: + runtime_output.print('Energy check: Demand not covered yet. Continue with design of additional ' + + 'center tank.') + dict_tank_design = calculate_additional_center_tank(dict_ac_data, dict_tank_design, runtime_output, + energy_demand_covered) + total_tank_energy += (dict_tank_design['tmp_energies']['additional_center_tank']) + if energy_demand_covered: + runtime_output.print('Additional center tank is generated but unnecessary to store required energy ' + + 'amount.') + else: + if total_tank_energy >= mission_energy_amount: + energy_demand_covered = True + runtime_output.print('Energy check: Energy demand covered.') + + case 'wing_with_additional_center_and_trim_tank': + first_act_then_trim = (dict_tank_design['tank_5']['tank_location'] == 'fuselage') + first_trim_then_act = (dict_tank_design['tank_5']['tank_location'] == 'horizontal_stabilizer') + if first_act_then_trim and not first_trim_then_act: + if not energy_demand_covered: + runtime_output.print('Energy check: Demand not covered yet. Continue with design of additional ' + + 'center tank.') + dict_tank_design = calculate_additional_center_tank(dict_ac_data, dict_tank_design, runtime_output, + energy_demand_covered) + total_tank_energy += dict_tank_design['tmp_energies']['additional_center_tank'] + if energy_demand_covered: + runtime_output.print('Additional center tank is generated but unnecessary to store required energy' + + ' amount.') + else: + if total_tank_energy >= mission_energy_amount: + energy_demand_covered = True + runtime_output.print('Energy check: Energy demand covered.') + else: + runtime_output.print('Energy check: Demand not covered yet. Continue with design of trim ' + + 'tank.') + dict_tank_design = calculate_trim_tank(dict_ac_data, dict_tank_design, runtime_output, + energy_demand_covered) + total_tank_energy += dict_tank_design['tmp_energies']['trim_tank'] + if energy_demand_covered: + runtime_output.print('Trim tank is generated but unnecessary to store required energy ' + + 'amount.') + else: + if total_tank_energy >= mission_energy_amount: + energy_demand_covered = True + runtime_output.print('Energy check: Energy demand covered.') + if first_trim_then_act and not first_act_then_trim: + if not energy_demand_covered: + runtime_output.print('Energy check: Demand not covered yet. Continue with design of trim tank.') + dict_tank_design = calculate_trim_tank(dict_ac_data, dict_tank_design, runtime_output, + energy_demand_covered) + total_tank_energy += dict_tank_design['tmp_energies']['trim_tank'] + if energy_demand_covered: + runtime_output.print('Trim tank is generated but unnecessary to store required energy ' + + 'amount.') + else: + if total_tank_energy >= mission_energy_amount: + energy_demand_covered = True + runtime_output.print('Energy check: Energy demand covered.') + else: + runtime_output.print('Energy check: Demand not covered yet. Continue with design of ' + + 'additional center tank.') + dict_tank_design = calculate_additional_center_tank(dict_ac_data, dict_tank_design, runtime_output, + energy_demand_covered) + total_tank_energy += dict_tank_design['tmp_energies']['additional_center_tank'] + if energy_demand_covered: + runtime_output.print('Additional center tank is generated but unnecessary to store required energy ' + + 'amount.') + else: + if total_tank_energy >= mission_energy_amount: + energy_demand_covered = True + runtime_output.print('Energy check: Energy demand covered.') + + # Final check if tanks can store mission energy amount. + if energy_demand_covered: + runtime_output.print('Tank design successful.') + else: + energy_missing = mission_energy_amount - total_tank_energy + volume_missing = energy_missing/kerosene_volumetric_energy_density + runtime_output.print('Attention: Necessary amount of fuel cannot be stored in selected tanks! ' + + str(int(round(energy_missing,0))) + 'J (' + str(round(volume_missing,2)) + + 'L) missing.') + + # TEST END + + # """ Print information or calculate other fuel tanks. """ + # if energy_demand_covered: + # # Energy amount that can be stored in wing tanks already covers mission energy amount. All other tanks are + # # generated but remain empty ('volume_available' and 'energy_available' are 0.0). + # match dict_tank_design['tank_configuration']: + # case 'wing_with_trim_tank': + # runtime_output.print('Trim tank is generated but unnecessary and remains empty.') + # case 'wing_with_additional_center_tank': + # runtime_output.print('Additional center tank is generated but unnecessary and remains empty.') + # case 'wing_with_additional_center_and_trim_tank': + # runtime_output.print('Trim and additional center tank are generated but unnecessary and ' + # + 'remain empty.') + # else: + # # If energy amount not covered by wing tanks: Calculate other fuel tanks (depending on tank configuration). + # # Calculate additional tank volumes according to defined tank configuration. + # match dict_tank_design['tank_configuration']: + # case 'wing_with_trim_tank': + # dict_tank_design = calculate_trim_tank(dict_ac_data, dict_tank_design, runtime_output) + # total_tank_energy += dict_tank_design['tmp_energies']['trim_tank'] + # if total_tank_energy >= mission_energy_amount: + # energy_demand_covered = True + # case 'wing_with_additional_center_tank': + # dict_tank_design = calculate_additional_center_tank(dict_ac_data, dict_tank_design, runtime_output) + # total_tank_energy += ( + # dict_tank_design['tmp_energies']['additional_center_tank']) + # if total_tank_energy >= mission_energy_amount: + # energy_demand_covered = True + # case 'wing_with_additional_center_and_trim_tank': + # first_act_then_trim = (dict_tank_design['tank_5']['tank_location'] == 'fuselage') + # first_trim_then_act = (dict_tank_design['tank_5']['tank_location'] == 'horizontal_stabilizer') + # if first_act_then_trim and not first_trim_then_act: + # dict_tank_design = calculate_additional_center_tank(dict_ac_data, dict_tank_design, runtime_output) + # total_tank_energy += dict_tank_design['tmp_energies']['additional_center_tank'] + # if total_tank_energy >= mission_energy_amount: + # energy_demand_covered = True + # runtime_output.print('Energy check: Energy demand covered.') + # runtime_output.print('Trim tank is generated but unnecessary and remains empty.') + # else: + # runtime_output.print('Energy check: Demand not covered yet. Continue with design of trim ' + # + 'tank.') + # dict_tank_design = calculate_trim_tank(dict_ac_data, dict_tank_design, runtime_output) + # additional_tank_energy += dict_tank_design['tmp_energies']['trim_tank'] + # total_tank_energy += additional_tank_energy + # if total_tank_energy >= mission_energy_amount: + # energy_demand_covered = True + # if first_trim_then_act and not first_act_then_trim: + # dict_tank_design = calculate_trim_tank(dict_ac_data, dict_tank_design, runtime_output) + # total_tank_energy += dict_tank_design['tmp_energies']['trim_tank'] + # if total_tank_energy >= mission_energy_amount: + # energy_demand_covered = True + # runtime_output.print('Energy check: Energy demand covered.') + # runtime_output.print('Additional center tank is generated but unnecessary and remains empty.') + # else: + # runtime_output.print('Energy check: Demand not covered yet. Continue with design of ' + # + 'additional center tank.') + # dict_tank_design = calculate_additional_center_tank(dict_ac_data, dict_tank_design, + # runtime_output) + # additional_tank_energy += dict_tank_design['tmp_energies']['additional_center_tank'] + # total_tank_energy += additional_tank_energy + # if total_tank_energy >= mission_energy_amount: + # energy_demand_covered = True + + # # Final check if tanks can store mission energy amount. + # if energy_demand_covered: + # runtime_output.print('Tank design successful.') + # else: + # energy_missing = mission_energy_amount - total_tank_energy + # volume_missing = energy_missing/kerosene_volumetric_energy_density + # runtime_output.print('Attention: Necessary amount of fuel cannot be stored in selected tanks! ' + # + str(int(round(energy_missing,0))) + 'J (' + str(round(volume_missing,2)) + + # 'L) missing.') + + """ Calculate total tank parameters. """ + dict_tank_design['total_tank_parameters'] = {} + # Tank mass (integral tank -> equals zero). + dict_tank_design['total_tank_parameters']['mass'] = 0.0 + # Tank position. + dict_tank_design['total_tank_parameters']['position'] = {} + dict_tank_design['total_tank_parameters']['position']['x'] = ( + dict_tank_design['geometry']['wing']['position']['x']) + dict_tank_design['total_tank_parameters']['position']['y'] = ( + dict_tank_design['geometry']['wing']['position']['y']) + dict_tank_design['total_tank_parameters']['position']['z'] = ( + dict_tank_design['geometry']['wing']['position']['z']) + # Inertia (are set to zero). + inertia_list = ['j_xx', 'j_yy', 'j_zz', 'j_xy', 'j_xz', 'j_yx', 'j_yz', 'j_zx', 'j_zy'] + dict_tank_design['total_tank_parameters']['inertia'] = {} + for inertia in inertia_list: + dict_tank_design['total_tank_parameters']['inertia'][inertia] = 0.0 + # Center of gravity (equals zero since integral tank has no mass). + dict_tank_design['total_tank_parameters']['center_of_gravity'] = {} + dict_tank_design['total_tank_parameters']['center_of_gravity']['x'] = 0.0 + dict_tank_design['total_tank_parameters']['center_of_gravity']['y'] = 0.0 + dict_tank_design['total_tank_parameters']['center_of_gravity']['z'] = 0.0 + + # Additional fuselage length equals zero for kerosene driven aircraft. + dict_tank_design['total_tank_parameters']['additional_fuselage_length'] = 0.0 + + # Prints. + runtime_output.print('Debug: The "calculate_tanks" function was successfully executed.') + + return dict_tank_design + + +def calculate_wing_tanks(dict_ac_data, dict_tank_design, runtime_output, mission_energy_amount): + """Calculate energy contained in wing tanks. + + In the first step, the volume of the obelisks (according to Torenbeek or Simpson) is calculated for every given + tank entity. Afterwards, the actual tank volume is calculated, considering volume loss due to internal structure + of integral tanks and buffer for temperature-dependent expansion of fuel. All the volumes are summed up + ('volume_obelisks_without_center_tank'), except for the center tank volume that is saved in a separate parameter + ('volume_obelisk_center_tank'). The energy contained in the tank can than be calculated by multiplying the actual + volume with the volumetric energy density of kerosene. + Finally it is checked if the wing center tank is necessary. If the left and right inner and outer wing tanks are + big enough to store the energy required, the wing center tank values are set to zero. + + :param dict dict_design: Dictionary containing data from aircraft exchange and module configuration file + :param dict dict_tank_design: Dictionary containing tank design parameter + :param logging.Logger runtime_output: Logging object used for capturing log messages in the module + :param float mission_energy_amount: Mission energy amount in Joule + :return dict dict_tank_design: Dictionary containing tank design parameter + """ + # Extract necessary data from 'dict_ac_data'. + factor_volume_usable = dict_ac_data['factor_volume_usable'] + # Since a vent tank is considered, no temperature expansion allowance has to be considered for wing tanks. + temperature_expansion_allowance = 1.0 + # Extract kerosene energy density from library. + kerosene_volumetric_energy_density = constants.KEROSENE_VOLUMETRIC_ENERGY_DENSITY + + # Print. + runtime_output.print('Wing tank design started...') + + wing_geometry_dict = dict_tank_design['geometry']['wing'] + wing_section_dict = dict_tank_design['geometry']['wing']['sections'] + wing_symmetry = dict_tank_design['geometry']['wing']['information']['wing_symmetry'] + + # Get all tank entities that are located at the wing. + wing_tank_entity_list = [k for k, v in dict_tank_design.items() + if k.startswith('tank_') and k[5:].isdigit() and v['tank_location'] == 'wing'] + wing_center_tank_entity = [k for k in wing_tank_entity_list + if dict_tank_design[k]['tank_position'].endswith('center')][0] + # Calculate distance from wing root to wing tip. + distance_wing_root_to_tip = (wing_section_dict['wing_tip']['position']['y'] + - wing_section_dict['wing_root']['position']['y']) + # Calculate span offset for tank sections. + center_tank_span_offset = -(wing_section_dict['wing_root']['position']['y']* + dict_ac_data['buffer_center_tank_segment']) + inner_wing_tank_span_offset = distance_wing_root_to_tip*dict_ac_data['buffer_inner_tank_segment'] + kink_section_span_offset = 0.0 + outer_vent_tank_span_offset = -(distance_wing_root_to_tip*dict_ac_data['buffer_outer_tank_segment']) + + # Set tank sections according to number of wing sections. + number_of_wing_sections = len(wing_section_dict) + if number_of_wing_sections < 3: + runtime_output.critical('Error: Not enough wing sections given. ' + + 'Program aborted.') + sys.exit(1) + elif number_of_wing_sections == 3: + sections = {'outer_center_tank_section': 'wing_root', + 'section_0': 'wing_root', + 'section_1': 'wing_tip', + 'outer_vent_tank_section': 'wing_tip'} + vent_tank_inner_surface = 'section_1' + # Span offsets for 'center_tank', 'section_0', 'section_1', and 'outer_vent_tank_section'. + span_offset_list = [center_tank_span_offset, inner_wing_tank_span_offset, kink_section_span_offset, + outer_vent_tank_span_offset] + elif number_of_wing_sections == 4: + sections = {'outer_center_tank_section': 'wing_root', + 'section_0': 'wing_root', + 'section_1': 'kink', + 'section_2': 'wing_tip', + 'outer_vent_tank_section': 'wing_tip'} + vent_tank_inner_surface = 'section_2' + # Span offsets for 'center_tank', 'section_0', 'section_1', 'section_2', and 'outer_vent_tank_section'. + span_offset_list = [center_tank_span_offset, inner_wing_tank_span_offset, kink_section_span_offset, + outer_vent_tank_span_offset, outer_vent_tank_span_offset] + else: + runtime_output.critical('Error: Too many wing sections given. ' + + 'Program aborted.') + sys.exit(1) + + # Prepare tank sections. + i = 0 + for sec, template in sections.items(): + wing_section_dict[sec] = {} + wing_section_dict[sec]['position'] = {} + wing_section_dict[sec]['position']['y'] = ( + wing_section_dict[template]['position']['y'] + span_offset_list[i]) + wing_section_dict[sec]['span_index'] = abs(int(round( + wing_section_dict[sec]['position']['y']*100, 0))) + wing_section_dict[sec]['position']['x'] = ( + wing_geometry_dict['interpolated_values']['x_coordinate_leading_edge'] + [wing_section_dict[sec]['span_index']]) + wing_section_dict[sec]['position']['z'] = ( + #wing_section_dict[template]['position']['z']) + wing_geometry_dict['interpolated_values']['z_coordinate_leading_edge'] + [wing_section_dict[sec]['span_index']]) + # span_index = wing_section_dict[sec]['span_index'] + i += 1 + + # -------------------------- START OF NEW VENT TANK CALCULATION ------------------------- + def calculate_wing_tanks_volume(): + """Calculate tank volumes (wing geometry does not change from here).""" + # Initialize parameter. + actual_volume_center_tank = 0 + actual_volume_without_center_tank = 0 + + # Iterate through tank entities. + wing_tank_entity_list = [key for key, value in dict_tank_design.items() if key.startswith('tank_') + and key[5:].isdigit() and value['tank_location'] == 'wing'] + for wing_tank_entity in wing_tank_entity_list: + # Initialize empty dictionary and lists. + dict_tank_design[wing_tank_entity]['geometry'] = {} + dict_tank_design[wing_tank_entity]['geometry']['inner_surface'] = {} + dict_tank_design[wing_tank_entity]['geometry']['outer_surface'] = {} + inner_surface_str = str() + outer_surface_str = str() + + if wing_symmetry: + if number_of_wing_sections == 4: + # Set sections depending on tank position. + match dict_tank_design[wing_tank_entity]['tank_position']: + case 'inner_left' | 'inner_right': + inner_surface_str = 'section_0' + outer_surface_str = 'section_1' + case 'outer_left' | 'outer_right': + inner_surface_str = 'section_1' + outer_surface_str = 'section_2' + case 'center': + inner_surface_str = 'fuselage_center_line' + outer_surface_str = 'outer_center_tank_section' + else: + runtime_output.critical('Error: Wing without kink not implemented yet! Program aborted.') + sys.exit(1) + else: + runtime_output.critical('Error: Wing not symmetric! ' + + 'Program aborted.') + sys.exit(1) + + # Calculate obelisk volume. + obelisk_volume = calculate_obelisk_volume(dict_ac_data['obelisk_volume_calculation_method'], + wing_geometry_dict, inner_surface_str, outer_surface_str) + # Calculate actual tank volume (considering volume loss due to internal structure of integral tanks and buffer + # for temperature-dependent expansion of fuel). + if dict_tank_design[wing_tank_entity]['tank_position'] == 'center': + obelisk_volume *= 2 + actual_volume = factor_volume_usable*temperature_expansion_allowance*obelisk_volume + # Sum up volume of all tanks except wing center tank. + if dict_tank_design[wing_tank_entity]['tank_position'] != 'center': + actual_volume_without_center_tank += actual_volume + # Set volume of wing center tank. + else: + actual_volume_center_tank = actual_volume + + # Write data to 'dict_tank_design'. + dict_tank_design[wing_tank_entity]['volume_available'] = actual_volume + dict_tank_design[wing_tank_entity]['energy_available'] = ( + actual_volume*kerosene_volumetric_energy_density) + # Print. + # inner left wing tank (ID="0") calculated. Volume (energy) available: 3.51 l (). + # f'{a*1000:,.2f}' + # Transform volume to liter for print. + actual_volume_l = actual_volume*1e3 + if dict_tank_design[wing_tank_entity]['tank_position'] == 'center' and print_outputs: + runtime_output.print('Wing center tank (' + wing_tank_entity + ') calculated. ' + + 'Volume (energy) available: ' + f"{actual_volume_l:,.2f}" + ' L (' + + f"{dict_tank_design[wing_tank_entity]['energy_available']/1e6:,.2f}" + ' MJ)') + else: + if print_outputs: + print_position = (dict_tank_design[wing_tank_entity]['tank_position'].split('_')[0].capitalize() + + ' ' + dict_tank_design[wing_tank_entity]['tank_position'].split('_')[1] + ' ') + runtime_output.print(print_position + 'wing tank (' + wing_tank_entity + ') calculated. ' + + 'Volume (energy) available: ' + f"{actual_volume_l:,.2f}" + ' L (' + + f"{dict_tank_design[wing_tank_entity]['energy_available']/1e6:,.2f}" + + ' MJ)') + + # Calculate tank mass (equals zero due to integral tank). + dict_tank_design[wing_tank_entity]['geometry']['total'] = {} + dict_tank_design[wing_tank_entity]['geometry']['total']['mass'] = 0.0 + # Set inertia to zero. + inertia_list = ['j_xx', 'j_yy', 'j_zz', 'j_xy', 'j_xz', 'j_yx', 'j_yz', 'j_zx', 'j_zy'] + dict_tank_design[wing_tank_entity]['geometry']['total']['inertia'] = {} + for inertia in inertia_list: + dict_tank_design[wing_tank_entity]['geometry']['total']['inertia'][inertia] = 0.0 + # Center of gravity equals zero since integral tank has no mass. + dict_tank_design[wing_tank_entity]['geometry']['total']['center_of_gravity'] = {} + dict_tank_design[wing_tank_entity]['geometry']['total']['center_of_gravity']['x'] = 0.0 + dict_tank_design[wing_tank_entity]['geometry']['total']['center_of_gravity']['y'] = 0.0 + dict_tank_design[wing_tank_entity]['geometry']['total']['center_of_gravity']['z'] = 0.0 + + # Calculate position of tank entity (foremost point of tank). + dict_tank_design[wing_tank_entity]['geometry']['total']['position'] = {} + dict_tank_design[wing_tank_entity]['geometry']['total']['position']['x'] = ( + wing_geometry_dict['interpolated_values']['x_coordinate_front_spar'][ + wing_section_dict[inner_surface_str]['span_index'] + ]) + if (dict_tank_design[wing_tank_entity]['tank_position'] == 'inner_left') or ( + dict_tank_design[wing_tank_entity]['tank_position'] == 'outer_left'): + dict_tank_design[wing_tank_entity]['geometry']['total']['position']['y'] = -abs( + wing_section_dict[inner_surface_str]['position']['y']) + else: + dict_tank_design[wing_tank_entity]['geometry']['total']['position']['y'] = abs( + wing_section_dict[inner_surface_str]['position']['y']) + dict_tank_design[wing_tank_entity]['geometry']['total']['position']['z'] = ( + wing_section_dict[inner_surface_str]['position']['z']) + # Direction. + dict_tank_design[wing_tank_entity]['geometry']['total']['direction'] = {} + dict_tank_design[wing_tank_entity]['geometry']['total']['direction']['x'] = 0 + dict_tank_design[wing_tank_entity]['geometry']['total']['direction']['y'] = 1 + dict_tank_design[wing_tank_entity]['geometry']['total']['direction']['z'] = 0 + # Set required energy. + dict_tank_design[wing_tank_entity]['energy_required_for_mission'] = True + + # Add required output data: x, y, and z position, shape, height, width, and length. + for surface_str, surface_name in zip([inner_surface_str, outer_surface_str], + ['inner_surface', 'outer_surface']): + surface_data = wing_section_dict[surface_str] + span_idx = surface_data['span_index'] + # Inner surface equals tank entity position. + if surface_name == 'inner_surface': + x_coordinate = 0.0 + y_coordinate = 0.0 + z_coordinate = 0.0 + else: + # Adjust y coordinate for left hand tanks. + y_coordinate_entity = dict_tank_design[wing_tank_entity]['geometry']['total']['position']['y'] + if 'left' in dict_tank_design[wing_tank_entity]['tank_position']: + y_coordinate_section = -surface_data['position']['y'] + y_coordinate = -(abs(y_coordinate_section) - abs(y_coordinate_entity)) + else: + y_coordinate_section = surface_data['position']['y'] + y_coordinate = (abs(y_coordinate_section) - abs(y_coordinate_entity)) + # old + # y_coordinate = y_coordinate_section - y_coordinate_entity + # NEW: + # y_coordinate = -(abs(y_coordinate_section) - abs(y_coordinate_entity)) + # if 'left' in dict_tank_design[wing_tank_entity]['tank_position']: + # y_coordinate = y_coordinate*(-1) + # x coordinate. + x_coordinate_entity = dict_tank_design[wing_tank_entity]['geometry']['total']['position']['x'] + x_coordinate_section = wing_geometry_dict['interpolated_values']['x_coordinate_front_spar'][span_idx] + if x_coordinate_entity < 0 and x_coordinate_section > 0 and x_coordinate_section > x_coordinate_entity: + delta_x = x_coordinate_entity - x_coordinate_section + else: + delta_x = x_coordinate_section - x_coordinate_entity + x_coordinate = delta_x + # z coordinate. + z_coordinate_entity = dict_tank_design[wing_tank_entity]['geometry']['total']['position']['z'] + z_coordinate_section = surface_data['position']['z'] + z_coordinate = z_coordinate_section - z_coordinate_entity + + dict_tank_design[wing_tank_entity]['geometry'][surface_name] = { + 'section_name': surface_str, + 'position': { + 'x': x_coordinate, + 'y': y_coordinate, + 'z': z_coordinate, + }, + 'shape': 'rectangular', + 'height': wing_geometry_dict['interpolated_values']['tank_heights'][span_idx], + 'width': wing_geometry_dict['interpolated_values']['tank_lengths'][span_idx], + 'length': 0.0 + } + + # Correct wing center tank information. + if 'center' in dict_tank_design[wing_tank_entity]['tank_position']: + dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['position']['y'] = ( + dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['position']['y']/2 + ) + dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['position']['y'] = -( + dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['position']['y'] + ) + + # Centroid. + # Assumption: inner surface area always greater than outer surface area. + inner_surface_area = (dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['height'] + *dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['width']) + outer_surface_area = (dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['height'] + *dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['width']) + y_position_outer_surface = dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['position']['y'] + y_position_inner_surface = dict_tank_design[wing_tank_entity]['geometry']['total']['position']['y'] + tank_span = y_position_outer_surface - y_position_inner_surface + y_position_center_of_mass = y_position_inner_surface + (tank_span/4*((inner_surface_area + + 2*math.sqrt(inner_surface_area*outer_surface_area) + + outer_surface_area)/(inner_surface_area + outer_surface_area))) + centroid_y = y_position_inner_surface + ((tank_span + *((dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['height'] + *dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['width']) + +(dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['height'] + *dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['width']) + +(dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['height'] + *dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['width']) + +(3*dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['height'] + *dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['width']))) + /(2 + *(2*dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['height'] + *dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['width']) + + (dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['height'] + *dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['width']) + + (dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['height'] + *dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['width']) + + (2*dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['height'] + *dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['width']))) + x_position_cog_inner_surface = dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['width']/2 + x_position_cog_outer_surface = dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['width']/2 + z_position_cog_inner_surface = dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['height']/2 + z_position_cog_outer_surface = dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['height']/2 + x_position_center_of_mass = ((inner_surface_area*x_position_cog_inner_surface + + outer_surface_area*x_position_cog_outer_surface) + /(inner_surface_area + outer_surface_area)) + z_position_center_of_mass = ((inner_surface_area*z_position_cog_inner_surface + + outer_surface_area*z_position_cog_outer_surface) + /(inner_surface_area + outer_surface_area)) + + dict_tank_design[wing_tank_entity]['geometry']['total']['centroid'] = {} + if wing_tank_entity == wing_center_tank_entity: + dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['x'] = x_position_center_of_mass + # dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['x'] = ( + # dict_tank_design['tank_4']['geometry']['inner_surface']['position']['x'] + # + dict_tank_design['tank_4']['geometry']['inner_surface']['width']/2 + # ) + dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['y'] = 0 + dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['z'] = 0 + else: + dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['x'] = x_position_center_of_mass + dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['y'] = y_position_center_of_mass + dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['z'] = z_position_center_of_mass + + # Sum up. + volume_wing_tank_without_center_tank = actual_volume_without_center_tank + volume_wing_tank_with_center_tank = actual_volume_without_center_tank + actual_volume_center_tank + + """Calculate fuel mass and energy.""" + # Convert wing fuel volume to energy. + energy_wing_tank_without_center_tank = (volume_wing_tank_without_center_tank + *kerosene_volumetric_energy_density) + energy_wing_tank_with_center_tank = (volume_wing_tank_with_center_tank + *kerosene_volumetric_energy_density) + + return energy_wing_tank_without_center_tank, energy_wing_tank_with_center_tank + + + def calculate_vent_tank_volume(wing_tank_energy_amount): + """Calculate vent tank.""" + # § 25.969 Fuel tank expansion space. Each fuel tank must have an expansion space of not less than 2 percent + # of the tank capacity. It must be impossible to fill the expansion space inadvertently with the airplane in the + # normal ground attitude. For pressure fueling systems, compliance with this section may be shown with the means + # provided to comply with §25.979(b). + # Add vent tank to tank design dict. + dict_tank_design['vent_tank'] = {} + dict_tank_design['vent_tank']['tank_location'] = 'wing' + dict_tank_design['vent_tank']['tank_position'] = 'vent' + # CS 25.969: Vent tank capacity must at least be equal to 2 percent of tank capacity (1 percent per side). + dict_tank_design['vent_tank']['energy_required'] = 0.01*wing_tank_energy_amount + vent_tank_outer_surface = 'outer_vent_tank_section' + + # Calculate energy contained in vent tank iteratively. Position of inner surface is adjusted in 1 mm steps. + vent_tank_energy = 0 + while vent_tank_energy < dict_tank_design['vent_tank']['energy_required']: + vent_tank_volume = calculate_obelisk_volume(dict_ac_data['obelisk_volume_calculation_method'], + wing_geometry_dict, + vent_tank_inner_surface, vent_tank_outer_surface) + vent_tank_volume_actual = factor_volume_usable*temperature_expansion_allowance*vent_tank_volume + vent_tank_energy = vent_tank_volume_actual*kerosene_volumetric_energy_density + wing_section_dict[vent_tank_inner_surface]['span_index'] -= 1 + + # Update x position of 'section_2' aka. 'vent_tank_inner_surface'. + span_idx = wing_section_dict[vent_tank_inner_surface]['span_index'] + wing_section_dict[vent_tank_inner_surface]['position']['x'] = ( + wing_geometry_dict['interpolated_values']['x_coordinate_leading_edge'][span_idx]) + wing_section_dict[vent_tank_inner_surface]['position']['y'] = span_idx/100 + + # TODO: Eventuell löschen, wenn plot nicht mehr benötigt. + dict_tank_design['vent_tank']['geometry'] = {} + dict_tank_design['vent_tank']['geometry']['total'] = {} + dict_tank_design['vent_tank']['geometry']['total']['position'] = {} + dict_tank_design['vent_tank']['geometry']['total']['position']['x'] = ( + wing_geometry_dict['interpolated_values']['x_coordinate_front_spar'][span_idx]) + dict_tank_design['vent_tank']['geometry']['total']['position']['y'] = ( + wing_section_dict[vent_tank_inner_surface]['position']['y']) + dict_tank_design['vent_tank']['geometry']['inner_surface'] = {} + dict_tank_design['vent_tank']['geometry']['inner_surface']['width'] = ( + wing_geometry_dict['interpolated_values']['tank_lengths'][span_idx]) + dict_tank_design['vent_tank']['volume_available'] = vent_tank_volume_actual + dict_tank_design['vent_tank']['energy_available'] = vent_tank_energy + # Print. + runtime_output.print('Vent tank (located near each wing tip) calculated.') + + print_outputs = False + energy_wing_tank_without_center_tank, energy_wing_tank_with_center_tank = calculate_wing_tanks_volume() + print_outputs = True + calculate_vent_tank_volume(energy_wing_tank_with_center_tank) + energy_wing_tank_without_center_tank, energy_wing_tank_with_center_tank = calculate_wing_tanks_volume() + # calculate_wing_tanks_volume() + # output is volume_wing_tank_with_center_tank + # this is input for calculate_vent_tank() + # then calculate_wing_tanks_volume() + # -------------------------- END OF NEW VENT TANK CALCULATION ------------------------- + + # -------------------------- START OF OLD VENT TANK CALCULATION ------------------------- + # """Calculate vent tank.""" + # # § 25.969 Fuel tank expansion space. Each fuel tank must have an expansion space of not less than 2 percent + # # of the tank capacity. It must be impossible to fill the expansion space inadvertently with the airplane in the + # # normal ground attitude. For pressure fueling systems, compliance with this section may be shown with the means + # # provided to comply with §25.979(b). + # # Add vent tank to tank design dict. + # dict_tank_design['vent_tank'] = {} + # dict_tank_design['vent_tank']['tank_location'] = 'wing' + # dict_tank_design['vent_tank']['tank_position'] = 'vent' + # # CS 25.969: Vent tank capacity must at least be equal to 2 percent of tank capacity (1 percent per side). + # dict_tank_design['vent_tank']['energy_required'] = 0.01*mission_energy_amount + # vent_tank_outer_surface = 'outer_vent_tank_section' + + # # Calculate energy contained in vent tank iteratively. Position of inner surface is adjusted in 1 mm steps. + # vent_tank_energy = 0 + # while vent_tank_energy < dict_tank_design['vent_tank']['energy_required']: + # vent_tank_volume = calculate_obelisk_volume(dict_ac_data['obelisk_volume_calculation_method'], + # wing_geometry_dict, + # vent_tank_inner_surface, vent_tank_outer_surface) + # vent_tank_volume_actual = factor_volume_usable*temperature_expansion_allowance*vent_tank_volume + # vent_tank_energy = vent_tank_volume_actual*kerosene_volumetric_energy_density + # wing_section_dict[vent_tank_inner_surface]['span_index'] -= 1 + + # # Update x position of 'section_2' aka. 'vent_tank_inner_surface'. + # span_idx = wing_section_dict[vent_tank_inner_surface]['span_index'] + # wing_section_dict[vent_tank_inner_surface]['position']['x'] = ( + # wing_geometry_dict['interpolated_values']['x_coordinate_leading_edge'][span_idx]) + # wing_section_dict[vent_tank_inner_surface]['position']['y'] = span_idx/100 + + # # TODO: Eventuell löschen, wenn plot nicht mehr benötigt. + # dict_tank_design['vent_tank']['geometry'] = {} + # dict_tank_design['vent_tank']['geometry']['total'] = {} + # dict_tank_design['vent_tank']['geometry']['total']['position'] = {} + # dict_tank_design['vent_tank']['geometry']['total']['position']['x'] = ( + # wing_geometry_dict['interpolated_values']['x_coordinate_front_spar'][span_idx]) + # dict_tank_design['vent_tank']['geometry']['total']['position']['y'] = ( + # wing_section_dict[vent_tank_inner_surface]['position']['y']) + # dict_tank_design['vent_tank']['geometry']['inner_surface'] = {} + # dict_tank_design['vent_tank']['geometry']['inner_surface']['width'] = ( + # wing_geometry_dict['interpolated_values']['tank_lengths'][span_idx]) + # dict_tank_design['vent_tank']['volume_available'] = vent_tank_volume_actual + # dict_tank_design['vent_tank']['energy_available'] = vent_tank_energy + # # Print. + # runtime_output.print('Vent tank (located near each wing tip) calculated.') + + # """Calculate tank volumes (wing geometry does not change from here).""" + # # Initialize parameter. + # actual_volume_center_tank = 0 + # actual_volume_without_center_tank = 0 + + # # Iterate through tank entities. + # wing_tank_entity_list = [key for key, value in dict_tank_design.items() if key.startswith('tank_') + # and key[5:].isdigit() and value['tank_location'] == 'wing'] + # for wing_tank_entity in wing_tank_entity_list: + # # Initialize empty dictionary and lists. + # dict_tank_design[wing_tank_entity]['geometry'] = {} + # dict_tank_design[wing_tank_entity]['geometry']['inner_surface'] = {} + # dict_tank_design[wing_tank_entity]['geometry']['outer_surface'] = {} + # inner_surface_str = str() + # outer_surface_str = str() + + # if wing_symmetry: + # if number_of_wing_sections == 4: + # # Set sections depending on tank position. + # match dict_tank_design[wing_tank_entity]['tank_position']: + # case 'inner_left' | 'inner_right': + # inner_surface_str = 'section_0' + # outer_surface_str = 'section_1' + # case 'outer_left' | 'outer_right': + # inner_surface_str = 'section_1' + # outer_surface_str = 'section_2' + # case 'center': + # inner_surface_str = 'fuselage_center_line' + # outer_surface_str = 'outer_center_tank_section' + # else: + # runtime_output.critical('Error: Wing without kink not implemented yet! Program aborted.') + # sys.exit(1) + # else: + # runtime_output.critical('Error: Wing not symmetric! ' + # + 'Program aborted.') + # sys.exit(1) + + # # Calculate obelisk volume. + # obelisk_volume = calculate_obelisk_volume(dict_ac_data['obelisk_volume_calculation_method'], + # wing_geometry_dict, inner_surface_str, outer_surface_str) + # # Calculate actual tank volume (considering volume loss due to internal structure of integral tanks and buffer + # # for temperature-dependent expansion of fuel). + # if dict_tank_design[wing_tank_entity]['tank_position'] == 'center': + # obelisk_volume *= 2 + # actual_volume = factor_volume_usable*temperature_expansion_allowance*obelisk_volume + # # Sum up volume of all tanks except wing center tank. + # if dict_tank_design[wing_tank_entity]['tank_position'] != 'center': + # actual_volume_without_center_tank += actual_volume + # # Set volume of wing center tank. + # else: + # actual_volume_center_tank = actual_volume + + # # Write data to 'dict_tank_design'. + # dict_tank_design[wing_tank_entity]['volume_available'] = actual_volume + # dict_tank_design[wing_tank_entity]['energy_available'] = ( + # actual_volume*kerosene_volumetric_energy_density) + # # Print. + # # inner left wing tank (ID="0") calculated. Volume (energy) available: 3.51 l (). + # # f'{a*1000:,.2f}' + # if dict_tank_design[wing_tank_entity]['tank_position'] == 'center': + # runtime_output.print('Wing center tank (' + wing_tank_entity + ') calculated. ' + # + 'Volume (energy) available: ' + f"{actual_volume:,.2f}" + ' L (' + # + f"{dict_tank_design[wing_tank_entity]['energy_available']/1e6:,.2f}" + ' MJ)') + # else: + # print_position = (dict_tank_design[wing_tank_entity]['tank_position'].split('_')[0].capitalize() + # + ' ' + dict_tank_design[wing_tank_entity]['tank_position'].split('_')[1] + ' ') + # runtime_output.print(print_position + 'wing tank (' + wing_tank_entity + ') calculated. ' + # + 'Volume (energy) available: ' + f"{actual_volume:,.2f}" + ' L (' + # + f"{dict_tank_design[wing_tank_entity]['energy_available']/1e6:,.2f}" + ' MJ)') + + # # Calculate tank mass (equals zero due to integral tank). + # dict_tank_design[wing_tank_entity]['geometry']['total'] = {} + # dict_tank_design[wing_tank_entity]['geometry']['total']['mass'] = 0.0 + # # Set inertia to zero. + # inertia_list = ['j_xx', 'j_yy', 'j_zz', 'j_xy', 'j_xz', 'j_yx', 'j_yz', 'j_zx', 'j_zy'] + # dict_tank_design[wing_tank_entity]['geometry']['total']['inertia'] = {} + # for inertia in inertia_list: + # dict_tank_design[wing_tank_entity]['geometry']['total']['inertia'][inertia] = 0.0 + # # Center of gravity equals zero since integral tank has no mass. + # dict_tank_design[wing_tank_entity]['geometry']['total']['center_of_gravity'] = {} + # dict_tank_design[wing_tank_entity]['geometry']['total']['center_of_gravity']['x'] = 0.0 + # dict_tank_design[wing_tank_entity]['geometry']['total']['center_of_gravity']['y'] = 0.0 + # dict_tank_design[wing_tank_entity]['geometry']['total']['center_of_gravity']['z'] = 0.0 + + # # Calculate position of tank entity (foremost point of tank). + # dict_tank_design[wing_tank_entity]['geometry']['total']['position'] = {} + # dict_tank_design[wing_tank_entity]['geometry']['total']['position']['x'] = ( + # wing_geometry_dict['interpolated_values']['x_coordinate_front_spar'][ + # wing_section_dict[inner_surface_str]['span_index'] + # ]) + # if (dict_tank_design[wing_tank_entity]['tank_position'] == 'inner_left') or ( + # dict_tank_design[wing_tank_entity]['tank_position'] == 'outer_left'): + # dict_tank_design[wing_tank_entity]['geometry']['total']['position']['y'] = -abs( + # wing_section_dict[inner_surface_str]['position']['y']) + # else: + # dict_tank_design[wing_tank_entity]['geometry']['total']['position']['y'] = abs( + # wing_section_dict[inner_surface_str]['position']['y']) + # dict_tank_design[wing_tank_entity]['geometry']['total']['position']['z'] = ( + # wing_section_dict[inner_surface_str]['position']['z']) + # # Direction. + # dict_tank_design[wing_tank_entity]['geometry']['total']['direction'] = {} + # dict_tank_design[wing_tank_entity]['geometry']['total']['direction']['x'] = 0 + # dict_tank_design[wing_tank_entity]['geometry']['total']['direction']['y'] = 1 + # dict_tank_design[wing_tank_entity]['geometry']['total']['direction']['z'] = 0 + # # Set required energy. + # dict_tank_design[wing_tank_entity]['energy_required_for_mission'] = True + + # # Add required output data: x, y, and z position, shape, height, width, and length. + # for surface_str, surface_name in zip([inner_surface_str, outer_surface_str], + # ['inner_surface', 'outer_surface']): + # surface_data = wing_section_dict[surface_str] + # span_idx = surface_data['span_index'] + # # Inner surface equals tank entity position. + # if surface_name == 'inner_surface': + # x_coordinate = 0.0 + # y_coordinate = 0.0 + # z_coordinate = 0.0 + # else: + # # Adjust y coordinate for left hand tanks. + # y_coordinate_entity = dict_tank_design[wing_tank_entity]['geometry']['total']['position']['y'] + # if 'left' in dict_tank_design[wing_tank_entity]['tank_position']: + # y_coordinate_section = -surface_data['position']['y'] + # y_coordinate = -(abs(y_coordinate_section) - abs(y_coordinate_entity)) + # else: + # y_coordinate_section = surface_data['position']['y'] + # y_coordinate = (abs(y_coordinate_section) - abs(y_coordinate_entity)) + # # old + # # y_coordinate = y_coordinate_section - y_coordinate_entity + # # NEW: + # # y_coordinate = -(abs(y_coordinate_section) - abs(y_coordinate_entity)) + # # if 'left' in dict_tank_design[wing_tank_entity]['tank_position']: + # # y_coordinate = y_coordinate*(-1) + # # x coordinate. + # x_coordinate_entity = dict_tank_design[wing_tank_entity]['geometry']['total']['position']['x'] + # x_coordinate_section = wing_geometry_dict['interpolated_values']['x_coordinate_front_spar'][span_idx] + # if x_coordinate_entity < 0 and x_coordinate_section > 0 and x_coordinate_section > x_coordinate_entity: + # delta_x = x_coordinate_entity - x_coordinate_section + # else: + # delta_x = x_coordinate_section - x_coordinate_entity + # x_coordinate = delta_x + # # z coordinate. + # z_coordinate_entity = dict_tank_design[wing_tank_entity]['geometry']['total']['position']['z'] + # z_coordinate_section = surface_data['position']['z'] + # z_coordinate = z_coordinate_section - z_coordinate_entity + + # dict_tank_design[wing_tank_entity]['geometry'][surface_name] = { + # 'section_name': surface_str, + # 'position': { + # 'x': x_coordinate, + # 'y': y_coordinate, + # 'z': z_coordinate, + # }, + # 'shape': 'rectangular', + # 'height': wing_geometry_dict['interpolated_values']['tank_heights'][span_idx], + # 'width': wing_geometry_dict['interpolated_values']['tank_lengths'][span_idx], + # 'length': 0.0 + # } + + # # Correct wing center tank information. + # if 'center' in dict_tank_design[wing_tank_entity]['tank_position']: + # dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['position']['y'] = ( + # dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['position']['y']/2 + # ) + # dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['position']['y'] = -( + # dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['position']['y'] + # ) + + # # Centroid. + # # Assumption: inner surface area always greater than outer surface area. + # inner_surface_area = (dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['height'] + # *dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['width']) + # outer_surface_area = (dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['height'] + # *dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['width']) + # y_position_outer_surface = dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['position']['y'] + # y_position_inner_surface = dict_tank_design[wing_tank_entity]['geometry']['total']['position']['y'] + # tank_span = y_position_outer_surface - y_position_inner_surface + # y_position_center_of_mass = y_position_inner_surface + (tank_span/4*((inner_surface_area + # + 2*math.sqrt(inner_surface_area*outer_surface_area) + # + outer_surface_area)/(inner_surface_area + outer_surface_area))) + # centroid_y = y_position_inner_surface + ((tank_span + # *((dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['height'] + # *dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['width']) + # +(dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['height'] + # *dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['width']) + # +(dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['height'] + # *dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['width']) + # +(3*dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['height'] + # *dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['width']))) + # /(2 + # *(2*dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['height'] + # *dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['width']) + # + (dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['height'] + # *dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['width']) + # + (dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['height'] + # *dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['width']) + # + (2*dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['height'] + # *dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['width']))) + # x_position_cog_inner_surface = dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['width']/2 + # x_position_cog_outer_surface = dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['width']/2 + # z_position_cog_inner_surface = dict_tank_design[wing_tank_entity]['geometry']['inner_surface']['height']/2 + # z_position_cog_outer_surface = dict_tank_design[wing_tank_entity]['geometry']['outer_surface']['height']/2 + # x_position_center_of_mass = ((inner_surface_area*x_position_cog_inner_surface + # + outer_surface_area*x_position_cog_outer_surface) + # /(inner_surface_area + outer_surface_area)) + # z_position_center_of_mass = ((inner_surface_area*z_position_cog_inner_surface + # + outer_surface_area*z_position_cog_outer_surface) + # /(inner_surface_area + outer_surface_area)) + + # dict_tank_design[wing_tank_entity]['geometry']['total']['centroid'] = {} + # if wing_tank_entity == wing_center_tank_entity: + # dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['x'] = x_position_center_of_mass + # # dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['x'] = ( + # # dict_tank_design['tank_4']['geometry']['inner_surface']['position']['x'] + # # + dict_tank_design['tank_4']['geometry']['inner_surface']['width']/2 + # # ) + # dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['y'] = 0 + # dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['z'] = 0 + # else: + # dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['x'] = x_position_center_of_mass + # dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['y'] = y_position_center_of_mass + # dict_tank_design[wing_tank_entity]['geometry']['total']['centroid']['z'] = z_position_center_of_mass + + # # Sum up. + # volume_wing_tank_without_center_tank = actual_volume_without_center_tank + # volume_wing_tank_with_center_tank = actual_volume_without_center_tank + actual_volume_center_tank + + # """Calculate fuel mass and energy.""" + # # Convert wing fuel volume to energy. + # energy_wing_tank_without_center_tank = (volume_wing_tank_without_center_tank + # *kerosene_volumetric_energy_density) + # energy_wing_tank_with_center_tank = (volume_wing_tank_with_center_tank + # *kerosene_volumetric_energy_density) + + # -------------------------- END OF OLD VENT TANK CALCULATION ------------------------- + + """Check if tanks can store required energy.""" + # If left and right inner and outer wing tanks are big enough to store required amount of energy, wing center tank + # remains empty. + if energy_wing_tank_without_center_tank >= mission_energy_amount: + dict_tank_design[wing_center_tank_entity]['volume_available'] = 0 + dict_tank_design[wing_center_tank_entity]['energy_available'] = 0 + + # Prints. + runtime_output.print('Wing tanks successfully calculated.') + runtime_output.debug('Debug: The "calculate_wing_tanks" function was successfully executed.') + + # Prepare data output. + dict_tank_design['tmp_energies'] = {} + dict_tank_design['tmp_energies']['wing_tank_without_center_tank'] = float(energy_wing_tank_without_center_tank) + dict_tank_design['tmp_energies']['wing_tank_with_center_tank'] = float(energy_wing_tank_with_center_tank) + + return dict_tank_design + + +def calculate_additional_center_tank(dict_ac_data, dict_tank_design, runtime_output, energy_demand_covered): + """Calculate energy contained in additional center tank. + + This code enables the installation of an additional center tank as LD3-45 container. Therefore, it is checked + initially whether the existing cargo compartment provides sufficient height. If this is not the case, all export + values are set to zero. Otherwise, the LD3-45 container is installed 10 cm behind the end of the landing gear bay. + It is assumed that this is approximately in line with the trailing edge of the wing. The volume and dimensions of + the container are taken from [1]. + + Attention: Please note that it is not possible to calculate more than one additional center tank at the moment! + + [1] https://www.lufthansa-cargo.com/de/fleet-ulds/ulds/containers + + :param dict dict_design: Dictionary containing data from aircraft exchange and module configuration file + :param dict dict_tank_design: Dictionary containing tank design parameter + :param logging.Logger runtime_output: Logging object used for capturing log messages in the module + :param energy_demand_covered: Information if mission energy demand already covered + :return dict dict_tank_design: Dictionary containing tank design parameter + """ + # Extract necessary data from 'dict_ac_data'. + factor_volume_usable = dict_ac_data['factor_volume_usable'] + temperature_expansion_allowance = dict_ac_data['temperature_expansion_allowance'] + + # Extract kerosene energy density from library. + kerosene_volumetric_energy_density = constants.KEROSENE_VOLUMETRIC_ENERGY_DENSITY + + # Print. + runtime_output.print('Additional center tank design started...') + + # Get all tank entities that are located at the fuselage (additional center tanks). + additional_center_tank_entity_list = [k for k, v in dict_tank_design.items() if k.startswith('tank_') + and k[5:].isdigit() and v['tank_location'] == 'fuselage'] + number_of_additional_center_tanks = len(additional_center_tank_entity_list) + if number_of_additional_center_tanks > 1: + runtime_output.critical('Error: Tank design not possible for more than one additional center tank! ' + + 'Program aborted.') + sys.exit('Exit information: Tank design not possible for more than one additional center tank!') + # Extract identifier of center tank. + center_tank_entity = additional_center_tank_entity_list[0] + + """Prepare data of LD3-45 container.""" + # Assumption: Angle at base equals 45 degree and usable volume of container taken from [1] (not calculated). + height_container = 1.15 + width_base_container = 1.53 + width_top_container = 2.44 + length_container = 1.56 + height_base_part_container = (width_top_container - width_base_container)*0.5 + height_top_part_container = height_container - height_base_part_container + volume_container = 3.4 + # Calculate areas and volumes for later use. + area_base_part = width_base_container*length_container + area_top_part = width_top_container*length_container + volume_base_part = ( + (height_base_part_container*width_top_container*length_container) + - height_base_part_container*height_base_part_container*length_container) + volume_top_part = width_top_container*length_container*height_top_part_container + + # Check, if cargo compartment height sufficient to store LD3-45 container. + if dict_tank_design['geometry']['fuselage']['cargo_compartment_height'] < height_container: + additional_center_tank_design_possible = False + runtime_output.warning('Warning: Cargo compartment height too small. Storage of LD3-45 container not ' + + 'possible. All values are set to 0.') + else: + additional_center_tank_design_possible = True + + """Calculate additional center tank.""" + # Calculate usable volume and convert to energy. + center_tank_volume = volume_container*factor_volume_usable*temperature_expansion_allowance + energy_kerosene_available = center_tank_volume*kerosene_volumetric_energy_density + center_tank_volume_l = center_tank_volume*1e3 + # Calculate ACT and necessary values if possible. + if additional_center_tank_design_possible: + # Write data to 'dict_tank_design'. + dict_tank_design[center_tank_entity]['volume_available'] = center_tank_volume + dict_tank_design[center_tank_entity]['energy_available'] = energy_kerosene_available + if energy_demand_covered: + dict_tank_design[center_tank_entity]['energy_required_for_mission'] = False + else: + dict_tank_design[center_tank_entity]['energy_required_for_mission'] = True + + # Print. + runtime_output.print('Additional center tank (' + center_tank_entity + ') calculated. ' + + 'Volume (energy) available: ' + f"{center_tank_volume_l:,.2f}" + ' L (' + + f"{dict_tank_design[center_tank_entity]['energy_available']/1e6:,.2f}" + ' MJ)') + + # Prepare geometrical data for additional center tank. + # Assumption: Landing gear bay ends at wing trailing edge position. + # x_coordinate_wing_leading_edge = dict_ac_data['wing_position_x'] + chord_length_at_fuselage_center_line = ( + dict_tank_design['geometry']['wing']['sections']['fuselage_center_line']['chord_length']) + # x_coordinate_wing_trailing_edge = x_coordinate_wing_leading_edge + chord_length_at_fuselage_center_line + buffer = 0.1 + x_coordinate = chord_length_at_fuselage_center_line + buffer + y_coordinate = [width_top_container*0.5, width_base_container*0.5, 0.0, + -width_base_container*0.5, -width_top_container*0.5] + cargo_floor_below_wing = (dict_tank_design['geometry']['fuselage']['z_coordinate_cargo_floor'] + < dict_tank_design['geometry']['wing']['position']['z']) + z_offset_wing_to_cargo_floor = abs(dict_tank_design['geometry']['wing']['position']['z'] + - dict_tank_design['geometry']['fuselage']['z_coordinate_cargo_floor']) + if cargo_floor_below_wing: + relative_z_position_cargo_floor_to_wing = -z_offset_wing_to_cargo_floor + else: + relative_z_position_cargo_floor_to_wing = z_offset_wing_to_cargo_floor + z_coordinate = relative_z_position_cargo_floor_to_wing + np.array([ + # plane_0 + height_base_part_container + height_top_part_container*0.5, + # plane_1, plane_2, plane_3 + height_container*0.5, height_container*0.5, height_container*0.5, + # plane_4 + height_base_part_container + height_top_part_container*0.5]) + heights = [height_top_part_container, height_container, height_container, + height_container, height_top_part_container] + plane_names = ['cross_section_0', 'cross_section_1', 'cross_section_2', 'cross_section_3', 'cross_section_4'] + + # Create the dictionary for center tank geometry. + dict_tank_design[center_tank_entity]['geometry'] = { + plane: { + 'section_name': plane, + 'position': { + 'x': x_coordinate, + 'y': y_coordinate[i], + 'z': z_coordinate[i] + }, + 'shape': 'rectangular', + 'height': heights[i], + 'width': length_container, + 'length': 0.0 + } + for i, plane in enumerate(plane_names) + } + + # Define total tank geometry values. + # Calculate tank mass (equals zero due to integral tank). + dict_tank_design[center_tank_entity]['geometry']['total'] = {} + dict_tank_design[center_tank_entity]['geometry']['total']['mass'] = 300 + # Set inertia to zero. + inertia_list = ['j_xx', 'j_yy', 'j_zz', 'j_xy', 'j_xz', 'j_yx', 'j_yz', 'j_zx', 'j_zy'] + dict_tank_design[center_tank_entity]['geometry']['total']['inertia'] = {} + for inertia in inertia_list: + dict_tank_design[center_tank_entity]['geometry']['total']['inertia'][inertia] = 0.0 + # Center of gravity. + # Prepare data for z coordinate calculation. + h = height_base_part_container + a_1 = area_top_part + a_2 = area_base_part + y_0 = ((h*(a_1 + 2*math.sqrt(a_1*a_2) + 3*a_2)) + /(4*(a_1 + math.sqrt(a_1*a_2)) + a_2)) + z_coordinate_cog_base_part = (dict_tank_design['geometry']['fuselage']['z_coordinate_cargo_floor'] + + (h - y_0)) + z_coordinate_cog_top_part = (dict_tank_design['geometry']['fuselage']['z_coordinate_cargo_floor'] + + height_base_part_container + height_top_part_container*0.5) + z_cog = ((z_coordinate_cog_base_part*volume_base_part + z_coordinate_cog_top_part*volume_top_part) + /(volume_base_part + volume_top_part)) + + dict_tank_design[center_tank_entity]['geometry']['total']['center_of_gravity'] = {} + dict_tank_design[center_tank_entity]['geometry']['total']['center_of_gravity']['x'] = ( + x_coordinate + length_container*0.5) + dict_tank_design[center_tank_entity]['geometry']['total']['center_of_gravity']['y'] = 0.0 + dict_tank_design[center_tank_entity]['geometry']['total']['center_of_gravity']['z'] = z_cog + + # Calculate position of tank entity (foremost point of tank). + dict_tank_design[center_tank_entity]['geometry']['total']['position'] = {} + dict_tank_design[center_tank_entity]['geometry']['total']['position']['x'] = x_coordinate + dict_tank_design[center_tank_entity]['geometry']['total']['position']['y'] = 0.0 + dict_tank_design[center_tank_entity]['geometry']['total']['position']['z'] = z_coordinate[2] + # Direction. + dict_tank_design[center_tank_entity]['geometry']['total']['direction'] = {} + dict_tank_design[center_tank_entity]['geometry']['total']['direction']['x'] = 0 + dict_tank_design[center_tank_entity]['geometry']['total']['direction']['y'] = 1 + dict_tank_design[center_tank_entity]['geometry']['total']['direction']['z'] = 0 + + # Centroid. + dict_tank_design[center_tank_entity]['geometry']['total']['centroid'] = {} + dict_tank_design[center_tank_entity]['geometry']['total']['centroid']['x'] = length_container/2 + dict_tank_design[center_tank_entity]['geometry']['total']['centroid']['y'] = 0 + dict_tank_design[center_tank_entity]['geometry']['total']['centroid']['z'] = z_cog + + else: + # Set all export values to 0 if tank design not possible. + dict_tank_design[center_tank_entity]['geometry'] = { + 'cross_section_0': { + 'section_name': 'cross_section_0', + 'position': { + 'x': 0.0, + 'y': 0.0, + 'z': 0.0 + }, + 'shape': None, + 'height': 0.0, + 'width': 0.0, + 'length': 0.0 + } + } + # Tank mass. + dict_tank_design[center_tank_entity]['geometry']['total'] = {} + dict_tank_design[center_tank_entity]['geometry']['total']['mass'] = 0.0 + # Inertia. + inertia_list = ['j_xx', 'j_yy', 'j_zz', 'j_xy', 'j_xz', 'j_yx', 'j_yz', 'j_zx', 'j_zy'] + dict_tank_design[center_tank_entity]['geometry']['total']['inertia'] = {} + for inertia in inertia_list: + dict_tank_design[center_tank_entity]['geometry']['total']['inertia'][inertia] = 0.0 + # Center of gravity. + dict_tank_design[center_tank_entity]['geometry']['total']['center_of_gravity'] = {} + dict_tank_design[center_tank_entity]['geometry']['total']['center_of_gravity']['x'] = 0.0 + dict_tank_design[center_tank_entity]['geometry']['total']['center_of_gravity']['y'] = 0.0 + dict_tank_design[center_tank_entity]['geometry']['total']['center_of_gravity']['z'] = 0.0 + # Position of tank entity (foremost point of tank). + dict_tank_design[center_tank_entity]['geometry']['total']['position'] = {} + dict_tank_design[center_tank_entity]['geometry']['total']['position']['x'] = 0.0 + dict_tank_design[center_tank_entity]['geometry']['total']['position']['y'] = 0.0 + dict_tank_design[center_tank_entity]['geometry']['total']['position']['z'] = 0.0 + # Direction. + dict_tank_design[center_tank_entity]['geometry']['total']['direction'] = {} + dict_tank_design[center_tank_entity]['geometry']['total']['direction']['x'] = 0 + dict_tank_design[center_tank_entity]['geometry']['total']['direction']['y'] = 1 + dict_tank_design[center_tank_entity]['geometry']['total']['direction']['z'] = 0 + # Centroid. + dict_tank_design[center_tank_entity]['geometry']['total']['centroid'] = {} + dict_tank_design[center_tank_entity]['geometry']['total']['centroid']['x'] = 0 + dict_tank_design[center_tank_entity]['geometry']['total']['centroid']['y'] = 0 + dict_tank_design[center_tank_entity]['geometry']['total']['centroid']['z'] = 0 + + # Prints. + runtime_output.print('Additional center tank design completed.') + runtime_output.debug('Debug: The "calculate_additional_center_tank" function was successfully executed.') + + # Prepare data output. + dict_tank_design['tmp_energies'] = {} + dict_tank_design['tmp_energies']['additional_center_tank'] = float(energy_kerosene_available) + + return dict_tank_design + + +def calculate_trim_tank(dict_ac_data, dict_tank_design, runtime_output, energy_demand_covered): + """Calculate energy contained in trim tank. + + [Add some more information here...] + + :param dict dict_design: Dictionary containing data from aircraft exchange and module configuration file + :param dict dict_tank_design: Dictionary containing tank design parameter + :param logging.Logger runtime_output: Logging object used for capturing log messages in the module + :param energy_demand_covered: Information if mission energy demand already covered + :return dict dict_tank_design: Dictionary containing tank design parameter + """ + #TODO: Implement vent tank design from wing tank. + # Extract values from 'dict_ac_data'. + factor_volume_usable = dict_ac_data['factor_volume_usable'] + temperature_expansion_allowance = dict_ac_data['temperature_expansion_allowance'] + # Extract kerosene energy density from library. + kerosene_volumetric_energy_density = constants.KEROSENE_VOLUMETRIC_ENERGY_DENSITY + + # Print. + runtime_output.print('Trim tank design started...') + + geometry_dict = dict_tank_design['geometry']['horizontal_stabilizer'] + section_dict = dict_tank_design['geometry']['horizontal_stabilizer']['sections'] + symmetry = dict_tank_design['geometry']['horizontal_stabilizer']['information']['symmetry'] + + # Get all tank entities that are located at the horizontal stabilizer (trim tank). + tank_entity = [k for k, v in dict_tank_design.items() if k.startswith('tank_') and k[5:].isdigit() + and v['tank_location'] == 'horizontal_stabilizer'][0] + + # Calculate distance from wing root to wing tip. + span = section_dict['tip']['position']['y'] - section_dict['fuselage_center_line']['position']['y'] + # Calculate span offset for tank. + outer_tank_span_offset = -(span*dict_ac_data['buffer_outer_tank_segment']) + + # Set tank sections according to number of wing sections. + number_of_sections = len(section_dict) + if number_of_sections < 2: + runtime_output.critical('Error: Not enough horizontal stabilizer sections given. Program aborted.') + sys.exit(1) + elif number_of_sections ==2: + sections = {'section_0': 'fuselage_center_line', + 'section_1': 'tip'} + # Span offsets for 'section_0' and 'section_1'. + span_offset_list = [0.0, outer_tank_span_offset] + elif number_of_sections == 3: + sections = {'section_0': 'fuselage_center_line', + 'section_1': 'tip'} + # Span offsets for 'section_0' and 'section_1'. + span_offset_list = [0.0, outer_tank_span_offset] + else: + runtime_output.critical('Error: Too many horizontal stabilizer sections given. ' + + 'Program aborted.') + sys.exit(1) + + # Prepare tank sections. + i = 0 + for sec, template in sections.items(): + section_dict[sec] = {} + section_dict[sec]['position'] = {} + section_dict[sec]['position']['y'] = section_dict[template]['position']['y'] + span_offset_list[i] + section_dict[sec]['span_index'] = int(round(section_dict[sec]['position']['y']*100, 0)) + section_dict[sec]['position']['x'] = ( + geometry_dict['interpolated_values']['x_coordinate_leading_edge'][section_dict[sec]['span_index']]) + section_dict[sec]['position']['z'] = ( + geometry_dict['interpolated_values']['z_coordinate_leading_edge'][section_dict[sec]['span_index']]) + i += 1 + + """Calculate tank volumes (geometry does not change from here).""" + # Initialize empty dictionary and lists. + dict_tank_design[tank_entity]['geometry'] = {} + dict_tank_design[tank_entity]['geometry']['inner_surface'] = {} + dict_tank_design[tank_entity]['geometry']['outer_surface'] = {} + inner_surface_str = str() + outer_surface_str = str() + + if symmetry: + if number_of_sections == 2: + inner_surface_str = 'section_0' + outer_surface_str = 'section_1' + elif number_of_sections == 3: + inner_surface_str = 'section_0' + outer_surface_str = 'section_1' + else: + runtime_output.critical('Error: Horizontal stabilizer not symmetric! Program aborted.') + sys.exit(1) + + # Calculate obelisk volume. + obelisk_volume = calculate_obelisk_volume(dict_ac_data['obelisk_volume_calculation_method'], + geometry_dict, inner_surface_str, outer_surface_str) + obelisk_volume *= 2 + + # Calculate actual tank volume (considering volume loss due to internal structure of integral tanks and buffer for + # temperature-dependent expansion of fuel). + volume_trim_tank = factor_volume_usable*temperature_expansion_allowance*obelisk_volume + + # Calculate energy and write data to 'dict_tank_design'. + dict_tank_design[tank_entity]['volume_available'] = volume_trim_tank + dict_tank_design[tank_entity]['energy_available'] = ( + volume_trim_tank*kerosene_volumetric_energy_density) + if energy_demand_covered: + dict_tank_design[tank_entity]['energy_required_for_mission'] = False + else: + dict_tank_design[tank_entity]['energy_required_for_mission'] = True + + # Add required output data: x, y, and z position, shape, height, width, and length. + for surface_str, surface_name in zip([inner_surface_str, outer_surface_str], ['inner_surface', 'outer_surface']): + surface_data = section_dict[surface_str] + span_idx = surface_data['span_index'] + + # Extract x and z coordinates. + x_coordinate = geometry_dict['interpolated_values']['x_coordinate_front_spar'][span_idx] + y_coordinate = surface_data['position']['y'] + z_coordinate = surface_data['position']['z'] + + dict_tank_design[tank_entity]['geometry'][surface_name] = { + 'section_name': surface_str, + 'position': { + 'x': x_coordinate, + 'y': y_coordinate, + 'z': z_coordinate, + }, + 'shape': 'rectangular', + 'height': geometry_dict['interpolated_values']['tank_heights'][span_idx], + 'width': geometry_dict['interpolated_values']['tank_lengths'][span_idx], + 'length': 0.0 + } + # Rename and restructure dict. + dict_tank_design[tank_entity]['geometry']['cross_section_1'] = dict_tank_design[tank_entity]['geometry'].pop( + 'inner_surface') + dict_tank_design[tank_entity]['geometry']['cross_section_2'] = dict_tank_design[tank_entity]['geometry'].pop( + 'outer_surface') + + def deep_copy_dict(d): + if isinstance(d, dict): + return {key: deep_copy_dict(value) for key, value in d.items()} + elif isinstance(d, list): + return [deep_copy_dict(item) for item in d] + else: + return d + + independent_copy = deep_copy_dict(dict_tank_design[tank_entity]['geometry']['cross_section_2']) + independent_copy['position']['y'] *= -1 + dict_tank_design[tank_entity]['geometry']['cross_section_0'] = independent_copy + + """Set or calculate total tank parameters.""" + # Set tank mass to zero (integral tank). + dict_tank_design[tank_entity]['geometry']['total'] = {} + dict_tank_design[tank_entity]['geometry']['total']['mass'] = 0.0 + # Set inertia to zero. + inertia_list = ['j_xx', 'j_yy', 'j_zz', 'j_xy', 'j_xz', 'j_yx', 'j_yz', 'j_zx', 'j_zy'] + dict_tank_design[tank_entity]['geometry']['total']['inertia'] = {} + for inertia in inertia_list: + dict_tank_design[tank_entity]['geometry']['total']['inertia'][inertia] = 0.0 + # Center of gravity equals zero since integral tank has no mass. + dict_tank_design[tank_entity]['geometry']['total']['center_of_gravity'] = {} + dict_tank_design[tank_entity]['geometry']['total']['center_of_gravity']['x'] = 0.0 + dict_tank_design[tank_entity]['geometry']['total']['center_of_gravity']['y'] = 0.0 + dict_tank_design[tank_entity]['geometry']['total']['center_of_gravity']['z'] = 0.0 + # Calculate position of tank entity (foremost point of tank). + distance_wing_to_horizontal_stabilizer = (dict_tank_design['geometry']['horizontal_stabilizer']['position']['x'] + - dict_tank_design['geometry']['wing']['position']['x']) + wing_lower_than_horizontal_stabilizer = (dict_tank_design['geometry']['wing']['position']['z'] + < dict_tank_design['geometry']['horizontal_stabilizer']['position']['z']) + z_offset_wing_to_horizontal_stabilizer = abs(dict_tank_design['geometry']['horizontal_stabilizer']['position']['z'] + - dict_tank_design['geometry']['wing']['position']['z']) + if wing_lower_than_horizontal_stabilizer: + relative_z_position_horizontal_stabilizer_to_wing = z_offset_wing_to_horizontal_stabilizer + else: + relative_z_position_horizontal_stabilizer_to_wing = -z_offset_wing_to_horizontal_stabilizer + dict_tank_design[tank_entity]['geometry']['total']['position'] = {} + dict_tank_design[tank_entity]['geometry']['total']['position']['x'] = ( + distance_wing_to_horizontal_stabilizer + + geometry_dict['interpolated_values']['x_coordinate_front_spar'][section_dict[inner_surface_str] + ['span_index']]) + dict_tank_design[tank_entity]['geometry']['total']['position']['y'] = ( + section_dict[inner_surface_str]['position']['y']) + dict_tank_design[tank_entity]['geometry']['total']['position']['z'] = ( + relative_z_position_horizontal_stabilizer_to_wing) + # Direction. + dict_tank_design[tank_entity]['geometry']['total']['direction'] = {} + dict_tank_design[tank_entity]['geometry']['total']['direction']['x'] = 0 + dict_tank_design[tank_entity]['geometry']['total']['direction']['y'] = 1 + dict_tank_design[tank_entity]['geometry']['total']['direction']['z'] = 0 + + # Centroid. + # Assumption: inner surface area always greater than outer surface area. + inner_surface = dict_tank_design['tank_5']['geometry']['cross_section_1'] + outer_surface = dict_tank_design['tank_5']['geometry']['cross_section_2'] + inner_surface_area = (inner_surface['height'] + *inner_surface['width']) + outer_surface_area = (outer_surface['height'] + *outer_surface['width']) + y_position_outer_surface = outer_surface['position']['y'] + y_position_inner_surface = inner_surface['position']['y'] + tank_span = y_position_outer_surface - y_position_inner_surface + y_position_center_of_mass = 0 + centroid_y = 0 + x_position_cog_inner_surface = inner_surface['width']/2 + x_position_cog_outer_surface = outer_surface['width']/2 + z_position_cog_inner_surface = inner_surface['height']/2 + z_position_cog_outer_surface = outer_surface['height']/2 + x_position_center_of_mass = ((inner_surface_area*x_position_cog_inner_surface + + outer_surface_area*x_position_cog_outer_surface) + /(inner_surface_area + outer_surface_area)) + z_position_center_of_mass = ((inner_surface_area*z_position_cog_inner_surface + + outer_surface_area*z_position_cog_outer_surface) + /(inner_surface_area + outer_surface_area)) + + dict_tank_design[tank_entity]['geometry']['total']['centroid'] = {} + dict_tank_design[tank_entity]['geometry']['total']['centroid']['x'] = x_position_center_of_mass + dict_tank_design[tank_entity]['geometry']['total']['centroid']['y'] = y_position_center_of_mass + dict_tank_design[tank_entity]['geometry']['total']['centroid']['z'] = z_position_center_of_mass + + # Transform volume to liter for print. + volume_trim_tank_l = volume_trim_tank*1e3 + # Print. + runtime_output.print('Trim tank (' + tank_entity + ') calculated. ' + + 'Volume (energy) available: ' + f"{volume_trim_tank_l:,.2f}" + ' L (' + + f"{dict_tank_design[tank_entity]['energy_available']/1e6:,.2f}" + ' MJ)') + + # Prints. + runtime_output.print('Trim tank design completed.') + runtime_output.debug('Debug: The "calculate_trim_tank" function was successfully executed.') + + # Prepare data output. + dict_tank_design['tmp_energies'] = {} + dict_tank_design['tmp_energies']['trim_tank'] = float(dict_tank_design[tank_entity]['energy_available']) + + return dict_tank_design diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/kerosene/methodkerosene.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/kerosene/methodkerosene.py index 7b7223856943eb44de11c3d25328a448a884e289..63cb3425b48b635435dc0e9ddf133fd10d3f4822 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/kerosene/methodkerosene.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/kerosene/methodkerosene.py @@ -59,6 +59,6 @@ def method_kerosene(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_co # Debug print. runtime_output.debug('Debug: The "method_kerosene" function was successfully executed.') - runtime_output.print('Tank design successfully completed.') + runtime_output.print('Tank design completed.') return kerosene_output_dict diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/usermethoddatapreparation.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/usermethoddatapreparation.py index df5b8efa8dd1477bbd2ddad9872dd0854f0ddd93..8b8525a535177b507ca76bbe0168be588721e4c4 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/usermethoddatapreparation.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/usermethoddatapreparation.py @@ -1,494 +1,499 @@ -"""Module providing functions for the preparation of user data.""" -import pycoordinatesystemconversion as py11csc - - -def user_method_data_input_preparation(routing_dict): - """Prepare necessary input data for the user method from aircraft exchange and module configuration files. - - In this function, the user is responsible for preparing the data needed for the user method. Relevant general data - are obtained from the aircraft exchange file and calculation specific parameter from the module configuration file. - The user must submit the data in the following format: - dict = {'parameter_name': [path to parameter node, expected data type], ...} - - :param dict routing_dict: Dictionary containing information on necessary data from module configuration file - :returns: - - dict data_to_extract_from_aircraft_exchange_dict: Dictionary containing parameter name, path to parameter, - and expected data type of parameters to be extracted from aircraft exchange file - - dict data_to_extract_from_module_configuration_dict: Dictionary containing parameter name, path to parameter, - and expected data type of parameters to be extracted from module configuration file - """ - - """Aircraft exchange file.""" - # Temporary paths for shorter lines. - tmp_design_specs_path = './requirements_and_specifications/design_specification/' - tmp_top_level_aircraft_requirements_path = ( - './requirements_and_specifications/requirements/top_level_aircraft_requirements/') - tmp_fuselage_entity_path = './component_design/fuselage/specific/geometry/fuselage[@ID="0"]/' - tmp_fuselage_section_path = tmp_fuselage_entity_path + 'sections/section[@ID="0"]/' - tmp_fuselage_accommodation_path = tmp_fuselage_entity_path + 'fuselage_accommodation/' - tmp_fuselage_payload_deck_path = (tmp_fuselage_accommodation_path - + 'payload_tube[@ID="0"]/payload_decks/payload_deck[@ID="0"]/') - tmp_wing_path = './component_design/wing/specific/geometry/aerodynamic_surface[@ID="0"]/' - tmp_wing_section_path = tmp_wing_path + 'parameters/sections/section[@ID="0"]/' - tmp_spar_path = ( - './component_design/wing/specific/geometry/aerodynamic_surface[@ID="0"]/parameters/spars/spar[@ID="0"]/') - tmp_empennage_path = './component_design/empennage/specific/geometry/aerodynamic_surface[@ID="0"]/' - tmp_empennage_section_path = tmp_empennage_path + 'parameters/sections/section[@ID="0"]/' - tmp_empennage_spar_path = ( - './component_design/empennage/specific/geometry/aerodynamic_surface[@ID="0"]/parameters/spars/spar[@ID="0"]/') - - # Enter all parameters to be extracted from the aircraft exchange file. - data_to_extract_from_aircraft_exchange_dict = { - # Data necessary for initial energy consumption estimation. - 'number_of_passengers': - [tmp_design_specs_path + 'transport_task/passenger_definition/total_number_passengers', float], - 'range': - [tmp_top_level_aircraft_requirements_path + 'design_mission/range', float], - # Energy information. - 'mission_energy_amount': - ['./analysis/mission/design_mission/mission_energy[@ID="0"]/consumed_energy', float], - 'mission_energy_ID': - ['./analysis/mission/design_mission/mission_energy[@ID="0"]/energy_carrier_ID', int], - 'energy_carrier_name': - [tmp_design_specs_path + 'energy_carriers/energy_carrier[@ID="0"]/type', str], - # Tank configuration. - 'tank_energy_ID': - [tmp_design_specs_path + 'configuration/tank_definition/tank[@ID="0"]/energy_carrier_ID', int], - 'tank_location': - [tmp_design_specs_path + 'configuration/tank_definition/tank[@ID="0"]/location', str], - 'tank_position': - [tmp_design_specs_path + 'configuration/tank_definition/tank[@ID="0"]/position', str], - 'energy_share': - [tmp_design_specs_path + 'configuration/tank_definition/tank[@ID="0"]/energy_share', float], - # Fuselage data. - 'fuselage_position_x': ['./component_design/fuselage/position/x', float], - 'fuselage_position_y': ['./component_design/fuselage/position/y', float], - 'fuselage_position_z': ['./component_design/fuselage/position/z', float], - 'fuselage_entity_position_x': [tmp_fuselage_entity_path + 'position/x', float], - 'fuselage_entity_position_y': [tmp_fuselage_entity_path + 'position/y', float], - 'fuselage_entity_position_z': [tmp_fuselage_entity_path + 'position/z', float], - 'fuselage_section_origin_x': [tmp_fuselage_section_path + 'origin/x', float], - 'fuselage_section_origin_y': [tmp_fuselage_section_path + 'origin/y', float], - 'fuselage_section_origin_z': [tmp_fuselage_section_path + 'origin/z', float], - 'fuselage_section_upper_height': [tmp_fuselage_section_path + 'upper_height', float], - 'fuselage_section_lower_height': [tmp_fuselage_section_path + 'lower_height', float], - 'fuselage_section_width': [tmp_fuselage_section_path + 'width', float], - 'fuselage_payload_deck_origin_x': [tmp_fuselage_payload_deck_path + 'position/x', float], - 'fuselage_payload_deck_origin_y': [tmp_fuselage_payload_deck_path + 'position/y', float], - 'fuselage_payload_deck_origin_z': [tmp_fuselage_payload_deck_path + 'position/z', float], - 'fuselage_payload_deck_structural_floor_thickness': [tmp_fuselage_payload_deck_path + - 'payload_deck_structural_floor_thickness', float], - # Wing data. - 'wing_name': [tmp_wing_path + 'name', str], - 'wing_position_x': ['./component_design/wing/position/x', float], - 'wing_position_y': ['./component_design/wing/position/y', float], - 'wing_position_z': ['./component_design/wing/position/z', float], - 'wing_symmetry': [tmp_wing_path + 'parameters/symmetric', bool], - 'wing_section_chord_origin_x': [tmp_wing_section_path + 'chord_origin/x', float], - 'wing_section_chord_origin_y': [tmp_wing_section_path + 'chord_origin/y', float], - 'wing_section_chord_origin_z': [tmp_wing_section_path + 'chord_origin/z', float], - 'wing_section_chord_length': [tmp_wing_section_path + 'chord_length', float], - 'wing_section_profile': [tmp_wing_section_path + 'profile', str], - # Wing spar data. - 'spar_position_inner_spanwise': [tmp_spar_path + 'position/inner_position/spanwise', float], - 'spar_position_inner_chord': [tmp_spar_path + 'position/inner_position/chord/to', float], - 'spar_position_outer_spanwise': [tmp_spar_path + 'position/outer_position/spanwise', float], - 'spar_position_outer_chord': [tmp_spar_path + 'position/outer_position/chord/to', float], - # Empennage data. - 'empennage_name': [tmp_empennage_path + 'name', str], - 'empennage_position_x': ['./component_design/empennage/position/x', float], - 'empennage_position_y': ['./component_design/empennage/position/y', float], - 'empennage_position_z': ['./component_design/empennage/position/z', float], - 'empennage_symmetry': [tmp_empennage_path + 'parameters/symmetric', bool], - 'empennage_entity_position_x': [tmp_empennage_path + 'position/x', float], - 'empennage_entity_position_y': [tmp_empennage_path + 'position/y', float], - 'empennage_entity_position_z': [tmp_empennage_path + 'position/z', float], - 'empennage_section_chord_origin_x': [tmp_empennage_section_path + 'chord_origin/x', float], - 'empennage_section_chord_origin_y': [tmp_empennage_section_path + 'chord_origin/y', float], - 'empennage_section_chord_origin_z': [tmp_empennage_section_path + 'chord_origin/z', float], - 'empennage_section_chord_length': [tmp_empennage_section_path + 'chord_length', float], - 'empennage_section_profile': [tmp_empennage_section_path + 'profile', str], - # Empennage spar data. - 'empennage_spar_position_inner_spanwise': [tmp_empennage_spar_path + - 'position/inner_position/spanwise', float], - 'empennage_spar_position_inner_chord': [tmp_empennage_spar_path + 'position/inner_position/chord/to', float], - 'empennage_spar_position_outer_spanwise': [tmp_empennage_spar_path + - 'position/outer_position/spanwise', float], - 'empennage_spar_position_outer_chord': [tmp_empennage_spar_path + 'position/outer_position/chord/to', float], - } - - """Module configuration file.""" - # Enter all general parameters to be extracted from the module configuration file. 'general parameters' means - # parameters that do not differ according to the user layer. It should be noted that 'tmp_general' is only used to - # shorten the path information in the 'general_data_to_extract_from_module_configuration_dict'. - tmp_general = ('./program_settings/configuration[@ID="tube_and_wing"]/fidelity[@ID="empirical"]/' - + 'tank_design_tu_berlin/general/') - general_data_to_extract_from_module_configuration_dict = { - 'mass_technology_factor': [tmp_general + 'mass_technology_factor', float] - } - - # Enter all specific parameters to be extracted from the module configuration file. 'specific parameters' means - # parameters that differ according to the user layer. It should be noted that 'tmp_specific' is only used to - # shorten the path information in the 'specific_data_to_extract_from_module_configuration_dict'. - tmp_specific = ('./program_settings/configuration[@ID="tube_and_wing"]/fidelity[@ID="empirical"]/' - + 'tank_design_tu_berlin/specific/') - specific_data_to_extract_from_module_configuration_dict = { - 'kerosene_gravimetric_energy_density': - [tmp_specific + 'kerosene_gravimetric_energy_density', float], - 'kerosene_volumetric_energy_density': - [tmp_specific + 'kerosene_volumetric_energy_density', float], - 'liquid_hydrogen_gravimetric_energy_density': - [tmp_specific + 'liquid_hydrogen_tank_structure/liquid_hydrogen_gravimetric_energy_density', float], - 'liquid_hydrogen_volumetric_energy_density': - [tmp_specific + 'liquid_hydrogen_tank_structure/liquid_hydrogen_volumetric_energy_density', float], - 'material_wall': - [tmp_specific + 'liquid_hydrogen_tank_structure/material_wall', str], - 'density_wall': - [tmp_specific + 'liquid_hydrogen_tank_structure/density_wall', float], - 'thickness_wall_calculation_method': - [tmp_specific + 'liquid_hydrogen_tank_structure/thickness_wall_calculation_method', str], - 'mass_pumps': - [tmp_specific + 'liquid_hydrogen_tank_structure/mass_pumps', float], - 'mass_baffle': - [tmp_specific + 'liquid_hydrogen_tank_structure/mass_baffle', float], - 'type_insulation': - [tmp_specific + 'liquid_hydrogen_tank_insulation/type_insulation', str], - 'material_insulation': - [tmp_specific + 'liquid_hydrogen_tank_insulation/material_insulation', str], - 'thickness_insulation': - [tmp_specific + 'liquid_hydrogen_tank_insulation/thickness_insulation', float], - 'density_insulation': - [tmp_specific + 'liquid_hydrogen_tank_insulation/density_insulation', float], - 'type_end_cap': - [tmp_specific + 'liquid_hydrogen_tank_design_parameter/type_end_cap', str], - 'internal_pressure': - [tmp_specific + 'liquid_hydrogen_tank_design_parameter/internal_pressure', float], - 'factor_usable_diameter': - [tmp_specific + 'miscellaneous/factor_usable_diameter', float], - 'factor_volume_allowance': - [tmp_specific + 'miscellaneous/factor_volume_allowance', float], - 'a_to_d_factor': - [tmp_specific + 'kerosene_tank_design_parameter/a_to_d_factor', float], - 'factor_volume_usable': - [tmp_specific + 'kerosene_tank_design_parameter/factor_volume_usable', float], - 'temperature_expansion_allowance': - [tmp_specific + 'kerosene_tank_design_parameter/temperature_expansion_allowance', float], - # Obelisk calculation method name. - 'obelisk_volume_calculation_method': - [tmp_specific + 'kerosene_tank_design_parameter/obelisk_calculation_method', str], - 'buffer_inner_tank_segment': - [tmp_specific + 'kerosene_tank_design_parameter/buffer_inner_tank_segment', float], - 'buffer_outer_tank_segment': - [tmp_specific + 'kerosene_tank_design_parameter/buffer_outer_tank_segment', float], - 'buffer_center_tank_segment': - [tmp_specific + 'kerosene_tank_design_parameter/buffer_center_tank_segment', float] - } - - # Merge module configuration dictionaries. - data_to_extract_from_module_configuration_dict = (general_data_to_extract_from_module_configuration_dict - | specific_data_to_extract_from_module_configuration_dict) - - return data_to_extract_from_aircraft_exchange_dict, data_to_extract_from_module_configuration_dict - - -def user_method_data_output_preparation(data_dict): - """Prepare user-specific output data based on the calculation method results. - - This function is responsible for preparing the user-specific output data based on the results of the calculation - method. The 'data_dict' input parameter contains the results of the module execution. - The data for the key parameters output must be specified in the following format in order to be written correctly - to the aircraft exchange file by the 'write_key_data_to_aircraft_exchange_file' function in the following step: - dict = {'parameter_name': [path to parameter node, value, name (if needed)], ...} - Important notes: - (1) It should be noted that only key parameters may be written that have been previously defined by the module - manager. - (2) Attention must be paid to the proper path specifications, otherwise warnings may be issued or, in the worst - case, errors may occur subsequently resulting in the write process and consequently the entire program being - aborted. - (3) If the path specifications contain IDs, these must start at '0' and be defined in ascending order without - gaps. - For the method-specific output, a path list and a dictionary is necessary to properly write the data to the - method-specific XML file. - the dictionary must be specified in the following format: - dict = ... - Note: If the user wants to export data from the design and study mission, two path lists and dictionaries are - necessary. - - :param dict data_dict: Dictionary containing the results of the module execution - :returns: - - dict key_output_dict: Output dictionary containing key parameters that are written to aircraft XML file - - dict method_specific_output_dict: Dictionary containing specific parameters that are written to - method-specific output XML - """ - - """ Convert coordinates from aircraft coordinate system to CGAL.""" - # Implement function here... - - """Key parameters output.""" - total_position_path = './component_design/tank/position/' - total_mass_path = './component_design/tank/mass_properties/' - total_inertia_path = './component_design/tank/mass_properties/inertia/' - total_cog_path = './component_design/tank/mass_properties/center_of_gravity/' - # data_dict['total_tank_parameters']['position'] = \ - # coordinate_system_conversion(data_dict['total_tank_parameters']['position']) - - key_output_dict = { - # Total tank parameter. - 'x': - [total_position_path + 'x', - data_dict['total_tank_parameters']['position']['x']], - 'y': - [total_position_path + 'y', - data_dict['total_tank_parameters']['position']['y']], - 'z': - [total_position_path + 'z', - data_dict['total_tank_parameters']['position']['z']], - 'mass': - [total_mass_path + 'mass', - data_dict['total_tank_parameters']['mass']], - 'j_xx': - [total_inertia_path + 'j_xx', - data_dict['total_tank_parameters']['inertia']['j_xx']], - 'j_yy': - [total_inertia_path + 'j_yy', - data_dict['total_tank_parameters']['inertia']['j_yy']], - 'j_zz': - [total_inertia_path + 'j_zz', - data_dict['total_tank_parameters']['inertia']['j_zz']], - 'j_xy': - [total_inertia_path + 'j_xy', - data_dict['total_tank_parameters']['inertia']['j_xy']], - 'j_xz': - [total_inertia_path + 'j_xz', - data_dict['total_tank_parameters']['inertia']['j_xz']], - 'j_yx': - [total_inertia_path + 'j_yx', - data_dict['total_tank_parameters']['inertia']['j_yx']], - 'j_yz': - [total_inertia_path + 'j_yz', - data_dict['total_tank_parameters']['inertia']['j_yz']], - 'j_zx': - [total_inertia_path + 'j_zx', - data_dict['total_tank_parameters']['inertia']['j_zx']], - 'j_zy': - [total_inertia_path + 'j_zy', - data_dict['total_tank_parameters']['inertia']['j_zy']], - 'cog_x': - [total_cog_path + 'x', - data_dict['total_tank_parameters']['center_of_gravity']['x']], - 'cog_y': - [total_cog_path + 'y', - data_dict['total_tank_parameters']['center_of_gravity']['y']], - 'cog_z': - [total_cog_path + 'z', - data_dict['total_tank_parameters']['center_of_gravity']['z']] - } - - # Generate dictionary for each tank entity. - j = 0 - while j < data_dict['number_of_tanks']: - tank_path_id = './component_design/tank/specific/tank[@ID="' + str(j) + '"]/' - tank_id = 'tank_' + str(j) - data_dict[tank_id]['geometry']['total']['position'] = \ - coordinate_system_conversion(data_dict[tank_id]['geometry']['total']['position']) - tank_output_dict = { - 'name_id' + str(j): - [tank_path_id + 'name', - 'tank_' + str(j)], - 'designator_id' + str(j): - [tank_path_id + 'designator', - data_dict[tank_id]['tank_location'] + '_' + data_dict[tank_id]['tank_position']], - 'x_id' + str(j): - [tank_path_id + 'position/x', - round(data_dict[tank_id]['geometry']['total']['position']['x'], 9)], - 'y_id' + str(j): - [tank_path_id + 'position/y', - round(data_dict[tank_id]['geometry']['total']['position']['y'], 9)], - 'z_id' + str(j): - [tank_path_id + 'position/z', - round(data_dict[tank_id]['geometry']['total']['position']['z'], 9)], - 'direction_x_id' + str(j): - [tank_path_id + 'direction/x', - data_dict[tank_id]['geometry']['total']['direction']['x']], - 'direction_y_id' + str(j): - [tank_path_id + 'direction/y', - data_dict[tank_id]['geometry']['total']['direction']['y']], - 'direction_z_id' + str(j): - [tank_path_id + 'direction/z', - data_dict[tank_id]['geometry']['total']['direction']['z']], - 'mass_id' + str(j): - [tank_path_id + 'mass_properties/mass', - data_dict[tank_id]['geometry']['total']['mass']], - 'j_xx_id' + str(j): - [tank_path_id + 'mass_properties/inertia/j_xx', - data_dict[tank_id]['geometry']['total']['inertia']['j_xx']], - 'j_yy_id' + str(j): - [tank_path_id + 'mass_properties/inertia/j_yy', - data_dict[tank_id]['geometry']['total']['inertia']['j_yy']], - 'j_zz_id' + str(j): - [tank_path_id + 'mass_properties/inertia/j_zz', - data_dict[tank_id]['geometry']['total']['inertia']['j_zz']], - 'j_xy_id' + str(j): - [tank_path_id + 'mass_properties/inertia/j_xy', - data_dict[tank_id]['geometry']['total']['inertia']['j_xy']], - 'j_xz_id' + str(j): - [tank_path_id + 'mass_properties/inertia/j_xz', - data_dict[tank_id]['geometry']['total']['inertia']['j_xz']], - 'j_yx_id' + str(j): - [tank_path_id + 'mass_properties/inertia/j_yx', - data_dict[tank_id]['geometry']['total']['inertia']['j_yx']], - 'j_yz_id' + str(j): - [tank_path_id + 'mass_properties/inertia/j_yz', - data_dict[tank_id]['geometry']['total']['inertia']['j_yz']], - 'j_zx_id' + str(j): - [tank_path_id + 'mass_properties/inertia/j_zx', - data_dict[tank_id]['geometry']['total']['inertia']['j_zx']], - 'j_zy_id' + str(j): - [tank_path_id + 'mass_properties/inertia/j_zy', - data_dict[tank_id]['geometry']['total']['inertia']['j_zy']], - 'cog_x_id' + str(j): - [tank_path_id + 'mass_properties/center_of_gravity/x', - data_dict[tank_id]['geometry']['total']['center_of_gravity']['x']], - 'cog_y_id' + str(j): - [tank_path_id + 'mass_properties/center_of_gravity/y', - data_dict[tank_id]['geometry']['total']['center_of_gravity']['y']], - 'cog_z_id' + str(j): - [tank_path_id + 'mass_properties/center_of_gravity/z', - data_dict[tank_id]['geometry']['total']['center_of_gravity']['z']], - 'energy_id' + str(j): - [tank_path_id + 'maximum_energy_capacity', - data_dict[tank_id]['energy_available']], - 'centroid_x_id' + str(j): - [tank_path_id + 'mass_properties/centroid/x', - round(data_dict[tank_id]['geometry']['total']['centroid']['x'], 9)], - 'centroid_y_id' + str(j): - [tank_path_id + 'mass_properties/centroid/y', - round(data_dict[tank_id]['geometry']['total']['centroid']['y'], 9)], - 'centroid_z_id' + str(j): - [tank_path_id + 'mass_properties/centroid/z', - round(data_dict[tank_id]['geometry']['total']['centroid']['z'], 9)], - } - - # Iterate over tank sections of each tank entity. - i = 0 - if data_dict[tank_id]['energy_carrier_name'] == 'kerosene': - number_of_sections = 2 - section_names = ['inner_surface', 'outer_surface'] - if data_dict[tank_id]['tank_location'] == 'fuselage' and data_dict[tank_id]['tank_position'] == 'center': - number_of_sections = sum(1 for k in data_dict[tank_id]['geometry'].keys() - if k.startswith('cross_section_')) - section_names = [k for k in data_dict[tank_id]['geometry'].keys() if k.startswith('cross_section_')] - section_names = sorted(section_names, key=lambda x: int(x.split('_')[-1])) - if data_dict[tank_id]['tank_location'] == 'horizontal_stabilizer' and ( - data_dict[tank_id]['tank_position'] == 'total'): - number_of_sections = sum(1 for k in data_dict[tank_id]['geometry'].keys() - if k.startswith('cross_section_')) - section_names = [k for k in data_dict[tank_id]['geometry'].keys() if k.startswith('cross_section_')] - section_names = sorted(section_names, key=lambda x: int(x.split('_')[-1])) - else: - number_of_sections = sum(1 for k in data_dict[tank_id]['geometry'].keys() - if k.startswith('cross_section_')) - section_names = [k for k in data_dict[tank_id]['geometry'].keys() if k.startswith('cross_section_')] - section_names = sorted(section_names, key=lambda x: int(x.split('_')[-1])) - while i < number_of_sections: - tank_section_path = tank_path_id + 'geometry/cross_section[@ID="' + str(i) + '"]/' - tank_section_name = 'tank_section_' + str(i) - # data_dict[tank_id]['geometry'][section_names[i]]['position'] = \ - # coordinate_system_conversion(data_dict[tank_id]['geometry'][section_names[i]]['position']) - # Check if the energy carrier of current tank is kerosene - # -> if true: -> use coordinate transformation for wing tanks. - if data_dict['tank_' + str(j)]['energy_carrier_name'] == 'kerosene': - tank_section_output_dict = { - 'cross_section_name_id' + str(j) + '_id' + str(i): - [tank_section_path + 'name', - tank_section_name], - 'cross_section_x_id' + str(j) + '_id' + str(i): - [tank_section_path + 'position/x', - round(data_dict[tank_id]['geometry'][section_names[i]]['position']['x'], 9)], - 'cross_section_y_id' + str(j) + '_id' + str(i): - [tank_section_path + 'position/y', - round(data_dict[tank_id]['geometry'][section_names[i]]['position']['z'], 9)], - 'cross_section_z_id' + str(j) + '_id' + str(i): - [tank_section_path + 'position/z', - round(data_dict[tank_id]['geometry'][section_names[i]]['position']['y'] * -1, 9)], - 'shape_id' + str(j) + '_id' + str(i): - [tank_section_path + 'shape', - data_dict[tank_id]['geometry'][section_names[i]]['shape']], - 'height_id' + str(j) + '_id' + str(i): - [tank_section_path + 'height', - data_dict[tank_id]['geometry'][section_names[i]]['height']], - 'width_id' + str(j) + '_id' + str(i): - [tank_section_path + 'width', - data_dict[tank_id]['geometry'][section_names[i]]['width']], - 'length_id' + str(j) + '_id' + str(i): - [tank_section_path + 'length', - data_dict[tank_id]['geometry'][section_names[i]]['length']] - } - # Else condition: The energy carrier of current tank is not kerosene - # -> use coordinate transformation for fuselage tanks. - else: - tank_section_output_dict = { - 'cross_section_name_id' + str(j) + '_id' + str(i): - [tank_section_path + 'name', - tank_section_name], - 'cross_section_x_id' + str(j) + '_id' + str(i): - [tank_section_path + 'position/x', - round(data_dict[tank_id]['geometry'][section_names[i]]['position']['y'] * -1, 9)], - 'cross_section_y_id' + str(j) + '_id' + str(i): - [tank_section_path + 'position/y', - round(data_dict[tank_id]['geometry'][section_names[i]]['position']['z'], 9)], - 'cross_section_z_id' + str(j) + '_id' + str(i): - [tank_section_path + 'position/z', - round(data_dict[tank_id]['geometry'][section_names[i]]['position']['x'], 9)], - 'shape_id' + str(j) + '_id' + str(i): - [tank_section_path + 'shape', - data_dict[tank_id]['geometry'][section_names[i]]['shape']], - 'height_id' + str(j) + '_id' + str(i): - [tank_section_path + 'height', - data_dict[tank_id]['geometry'][section_names[i]]['height']], - 'width_id' + str(j) + '_id' + str(i): - [tank_section_path + 'width', - data_dict[tank_id]['geometry'][section_names[i]]['width']], - 'length_id' + str(j) + '_id' + str(i): - [tank_section_path + 'length', - data_dict[tank_id]['geometry'][section_names[i]]['length']] - } - # merge dictionary to one single output dictionary - tank_output_dict.update(tank_section_output_dict) - i += 1 - - key_output_dict.update(tank_output_dict) - j += 1 - - # Add additional fuselage length. - additional_length_dict = { - 'additional_fuselage_length': ['./component_design/tank/specific/additional_fuselage_length', - data_dict['total_tank_parameters']['additional_fuselage_length']] - } - - key_output_dict.update(additional_length_dict) - - """Method-specific output.""" - method_specific_output_dict = {} - for key, value in key_output_dict.items(): - if 'mass_properties' not in value[0]: - method_specific_output_dict[key] = value - - return key_output_dict, method_specific_output_dict - - -def coordinate_system_conversion(element): - if len(element.keys()) > 3: - element_3d = py11csc.Element3D(element["j_xx"], element["j_xy"], element["j_xz"], - element["j_yx"], element["j_yy"], element["j_yz"], - element["j_zx"], element["j_zy"], element["j_zz"]) - element_3d.AC2CGAL() - for k in element.keys(): - element[k] = getattr(element_3d, k.split("_")[-1])() - return element - else: - element_3d = py11csc.Element3D(element["x"], element["y"], element["z"]) - element_3d.AC2CGAL() - for k in element.keys(): - element[k] = getattr(element_3d, k)() - return element +"""Module providing functions for the preparation of user data.""" +import pycoordinatesystemconversion as py11csc + + +def user_method_data_input_preparation(routing_dict): + """Prepare necessary input data for the user method from aircraft exchange and module configuration files. + + In this function, the user is responsible for preparing the data needed for the user method. Relevant general data + are obtained from the aircraft exchange file and calculation specific parameter from the module configuration file. + The user must submit the data in the following format: + dict = {'parameter_name': [path to parameter node, expected data type], ...} + + :param dict routing_dict: Dictionary containing information on necessary data from module configuration file + :returns: + - dict data_to_extract_from_aircraft_exchange_dict: Dictionary containing parameter name, path to parameter, + and expected data type of parameters to be extracted from aircraft exchange file + - dict data_to_extract_from_module_configuration_dict: Dictionary containing parameter name, path to parameter, + and expected data type of parameters to be extracted from module configuration file + """ + + """Aircraft exchange file.""" + # Temporary paths for shorter lines. + tmp_design_specs_path = './requirements_and_specifications/design_specification/' + tmp_top_level_aircraft_requirements_path = ( + './requirements_and_specifications/requirements/top_level_aircraft_requirements/') + tmp_fuselage_entity_path = './component_design/fuselage/specific/geometry/fuselage[@ID="0"]/' + tmp_fuselage_section_path = tmp_fuselage_entity_path + 'sections/section[@ID="0"]/' + tmp_fuselage_accommodation_path = tmp_fuselage_entity_path + 'fuselage_accommodation/' + tmp_fuselage_payload_deck_path = (tmp_fuselage_accommodation_path + + 'payload_tube[@ID="0"]/payload_decks/payload_deck[@ID="0"]/') + tmp_wing_path = './component_design/wing/specific/geometry/aerodynamic_surface[@ID="0"]/' + tmp_wing_section_path = tmp_wing_path + 'parameters/sections/section[@ID="0"]/' + tmp_spar_path = ( + './component_design/wing/specific/geometry/aerodynamic_surface[@ID="0"]/parameters/spars/spar[@ID="0"]/') + tmp_empennage_path = './component_design/empennage/specific/geometry/aerodynamic_surface[@ID="0"]/' + tmp_empennage_section_path = tmp_empennage_path + 'parameters/sections/section[@ID="0"]/' + tmp_empennage_spar_path = ( + './component_design/empennage/specific/geometry/aerodynamic_surface[@ID="0"]/parameters/spars/spar[@ID="0"]/') + tmp_mission_analysis_path = './analysis/mission/design_mission/' + + # Enter all parameters to be extracted from the aircraft exchange file. + data_to_extract_from_aircraft_exchange_dict = { + # Data necessary for initial energy consumption estimation. + 'number_of_passengers': + [tmp_design_specs_path + 'transport_task/passenger_definition/total_number_passengers', float], + 'range': + [tmp_top_level_aircraft_requirements_path + 'design_mission/range', float], + # Energy information. + 'mission_energy_amount': + [tmp_mission_analysis_path +'loaded_mission_energy/mission_energy[@ID="0"]/consumed_energy', float], + 'mission_energy_ID': + [tmp_mission_analysis_path +'loaded_mission_energy/mission_energy[@ID="0"]/energy_carrier_ID', int], + # TODO here only loaded mission energy is used, but there are also trip, taxi & landing energy + 'energy_carrier_name': + [tmp_design_specs_path + 'energy_carriers/energy_carrier[@ID="0"]/type', str], + # Tank configuration. + 'tank_energy_ID': + [tmp_design_specs_path + 'configuration/tank_definition/tank[@ID="0"]/energy_carrier_ID', int], + 'tank_location': + [tmp_design_specs_path + 'configuration/tank_definition/tank[@ID="0"]/location', str], + 'tank_position': + [tmp_design_specs_path + 'configuration/tank_definition/tank[@ID="0"]/position', str], + 'energy_share': + [tmp_design_specs_path + 'configuration/tank_definition/tank[@ID="0"]/energy_share', float], + # Fuselage data. + 'fuselage_position_x': ['./component_design/fuselage/position/x', float], + 'fuselage_position_y': ['./component_design/fuselage/position/y', float], + 'fuselage_position_z': ['./component_design/fuselage/position/z', float], + 'fuselage_entity_position_x': [tmp_fuselage_entity_path + 'position/x', float], + 'fuselage_entity_position_y': [tmp_fuselage_entity_path + 'position/y', float], + 'fuselage_entity_position_z': [tmp_fuselage_entity_path + 'position/z', float], + 'fuselage_section_origin_x': [tmp_fuselage_section_path + 'origin/x', float], + 'fuselage_section_origin_y': [tmp_fuselage_section_path + 'origin/y', float], + 'fuselage_section_origin_z': [tmp_fuselage_section_path + 'origin/z', float], + 'fuselage_section_upper_height': [tmp_fuselage_section_path + 'upper_height', float], + 'fuselage_section_lower_height': [tmp_fuselage_section_path + 'lower_height', float], + 'fuselage_section_width': [tmp_fuselage_section_path + 'width', float], + 'fuselage_payload_deck_origin_x': [tmp_fuselage_payload_deck_path + 'position/x', float], + 'fuselage_payload_deck_origin_y': [tmp_fuselage_payload_deck_path + 'position/y', float], + 'fuselage_payload_deck_origin_z': [tmp_fuselage_payload_deck_path + 'position/z', float], + 'fuselage_payload_deck_structural_floor_thickness': [tmp_fuselage_payload_deck_path + + 'payload_deck_structural_floor_thickness', float], + # Wing data. + 'wing_name': [tmp_wing_path + 'name', str], + 'wing_position_x': ['./component_design/wing/position/x', float], + 'wing_position_y': ['./component_design/wing/position/y', float], + 'wing_position_z': ['./component_design/wing/position/z', float], + 'wing_symmetry': [tmp_wing_path + 'parameters/symmetric', bool], + 'wing_section_chord_origin_x': [tmp_wing_section_path + 'chord_origin/x', float], + 'wing_section_chord_origin_y': [tmp_wing_section_path + 'chord_origin/y', float], + 'wing_section_chord_origin_z': [tmp_wing_section_path + 'chord_origin/z', float], + 'wing_section_chord_length': [tmp_wing_section_path + 'chord_length', float], + 'wing_section_profile': [tmp_wing_section_path + 'profile', str], + # Wing spar data. + 'spar_position_inner_spanwise': [tmp_spar_path + 'position/inner_position/spanwise', float], + 'spar_position_inner_chord': [tmp_spar_path + 'position/inner_position/chord/to', float], + 'spar_position_outer_spanwise': [tmp_spar_path + 'position/outer_position/spanwise', float], + 'spar_position_outer_chord': [tmp_spar_path + 'position/outer_position/chord/to', float], + # Empennage data. + 'empennage_name': [tmp_empennage_path + 'name', str], + 'empennage_position_x': ['./component_design/empennage/position/x', float], + 'empennage_position_y': ['./component_design/empennage/position/y', float], + 'empennage_position_z': ['./component_design/empennage/position/z', float], + 'empennage_symmetry': [tmp_empennage_path + 'parameters/symmetric', bool], + 'empennage_entity_position_x': [tmp_empennage_path + 'position/x', float], + 'empennage_entity_position_y': [tmp_empennage_path + 'position/y', float], + 'empennage_entity_position_z': [tmp_empennage_path + 'position/z', float], + 'empennage_section_chord_origin_x': [tmp_empennage_section_path + 'chord_origin/x', float], + 'empennage_section_chord_origin_y': [tmp_empennage_section_path + 'chord_origin/y', float], + 'empennage_section_chord_origin_z': [tmp_empennage_section_path + 'chord_origin/z', float], + 'empennage_section_chord_length': [tmp_empennage_section_path + 'chord_length', float], + 'empennage_section_profile': [tmp_empennage_section_path + 'profile', str], + # Empennage spar data. + 'empennage_spar_position_inner_spanwise': [tmp_empennage_spar_path + + 'position/inner_position/spanwise', float], + 'empennage_spar_position_inner_chord': [tmp_empennage_spar_path + 'position/inner_position/chord/to', float], + 'empennage_spar_position_outer_spanwise': [tmp_empennage_spar_path + + 'position/outer_position/spanwise', float], + 'empennage_spar_position_outer_chord': [tmp_empennage_spar_path + 'position/outer_position/chord/to', float], + } + + """Module configuration file.""" + # Enter all general parameters to be extracted from the module configuration file. 'general parameters' means + # parameters that do not differ according to the user layer. It should be noted that 'tmp_general' is only used to + # shorten the path information in the 'general_data_to_extract_from_module_configuration_dict'. + tmp_general = ('./program_settings/configuration[@ID="tube_and_wing"]/fidelity[@ID="empirical"]/' + + 'tank_design_tu_berlin/general/') + general_data_to_extract_from_module_configuration_dict = { + 'mass_technology_factor': [tmp_general + 'mass_technology_factor', float] + } + + # Enter all specific parameters to be extracted from the module configuration file. 'specific parameters' means + # parameters that differ according to the user layer. It should be noted that 'tmp_specific' is only used to + # shorten the path information in the 'specific_data_to_extract_from_module_configuration_dict'. + tmp_specific = ('./program_settings/configuration[@ID="tube_and_wing"]/fidelity[@ID="empirical"]/' + + 'tank_design_tu_berlin/specific/') + specific_data_to_extract_from_module_configuration_dict = { + 'kerosene_gravimetric_energy_density': + [tmp_specific + 'kerosene_gravimetric_energy_density', float], + 'kerosene_volumetric_energy_density': + [tmp_specific + 'kerosene_volumetric_energy_density', float], + 'liquid_hydrogen_gravimetric_energy_density': + [tmp_specific + 'liquid_hydrogen_tank_structure/liquid_hydrogen_gravimetric_energy_density', float], + 'liquid_hydrogen_volumetric_energy_density': + [tmp_specific + 'liquid_hydrogen_tank_structure/liquid_hydrogen_volumetric_energy_density', float], + 'material_wall': + [tmp_specific + 'liquid_hydrogen_tank_structure/material_wall', str], + 'density_wall': + [tmp_specific + 'liquid_hydrogen_tank_structure/density_wall', float], + 'thickness_wall_calculation_method': + [tmp_specific + 'liquid_hydrogen_tank_structure/thickness_wall_calculation_method', str], + 'mass_pumps': + [tmp_specific + 'liquid_hydrogen_tank_structure/mass_pumps', float], + 'mass_baffle': + [tmp_specific + 'liquid_hydrogen_tank_structure/mass_baffle', float], + 'type_insulation': + [tmp_specific + 'liquid_hydrogen_tank_insulation/type_insulation', str], + 'material_insulation': + [tmp_specific + 'liquid_hydrogen_tank_insulation/material_insulation', str], + 'thickness_insulation': + [tmp_specific + 'liquid_hydrogen_tank_insulation/thickness_insulation', float], + 'density_insulation': + [tmp_specific + 'liquid_hydrogen_tank_insulation/density_insulation', float], + 'type_end_cap': + [tmp_specific + 'liquid_hydrogen_tank_design_parameter/type_end_cap', str], + 'internal_pressure': + [tmp_specific + 'liquid_hydrogen_tank_design_parameter/internal_pressure', float], + 'factor_usable_diameter': + [tmp_specific + 'miscellaneous/factor_usable_diameter', float], + 'factor_volume_allowance': + [tmp_specific + 'miscellaneous/factor_volume_allowance', float], + 'a_to_d_factor': + [tmp_specific + 'kerosene_tank_design_parameter/a_to_d_factor', float], + 'factor_volume_usable': + [tmp_specific + 'kerosene_tank_design_parameter/factor_volume_usable', float], + 'temperature_expansion_allowance': + [tmp_specific + 'kerosene_tank_design_parameter/temperature_expansion_allowance', float], + # Obelisk calculation method name. + 'obelisk_volume_calculation_method': + [tmp_specific + 'kerosene_tank_design_parameter/obelisk_calculation_method', str], + 'buffer_inner_tank_segment': + [tmp_specific + 'kerosene_tank_design_parameter/buffer_inner_tank_segment', float], + 'buffer_outer_tank_segment': + [tmp_specific + 'kerosene_tank_design_parameter/buffer_outer_tank_segment', float], + 'buffer_center_tank_segment': + [tmp_specific + 'kerosene_tank_design_parameter/buffer_center_tank_segment', float] + } + + # Merge module configuration dictionaries. + data_to_extract_from_module_configuration_dict = (general_data_to_extract_from_module_configuration_dict + | specific_data_to_extract_from_module_configuration_dict) + + return data_to_extract_from_aircraft_exchange_dict, data_to_extract_from_module_configuration_dict + + +def user_method_data_output_preparation(data_dict): + """Prepare user-specific output data based on the calculation method results. + + This function is responsible for preparing the user-specific output data based on the results of the calculation + method. The 'data_dict' input parameter contains the results of the module execution. + The data for the key parameters output must be specified in the following format in order to be written correctly + to the aircraft exchange file by the 'write_key_data_to_aircraft_exchange_file' function in the following step: + dict = {'parameter_name': [path to parameter node, value, name (if needed)], ...} + Important notes: + (1) It should be noted that only key parameters may be written that have been previously defined by the module + manager. + (2) Attention must be paid to the proper path specifications, otherwise warnings may be issued or, in the worst + case, errors may occur subsequently resulting in the write process and consequently the entire program being + aborted. + (3) If the path specifications contain IDs, these must start at '0' and be defined in ascending order without + gaps. + For the method-specific output, a path list and a dictionary is necessary to properly write the data to the + method-specific XML file. + the dictionary must be specified in the following format: + dict = ... + Note: If the user wants to export data from the design and study mission, two path lists and dictionaries are + necessary. + + :param dict data_dict: Dictionary containing the results of the module execution + :returns: + - dict key_output_dict: Output dictionary containing key parameters that are written to aircraft XML file + - dict method_specific_output_dict: Dictionary containing specific parameters that are written to + method-specific output XML + """ + + """ Convert coordinates from aircraft coordinate system to CGAL.""" + # Implement function here... + + """Key parameters output.""" + total_position_path = './component_design/tank/position/' + total_mass_path = './component_design/tank/mass_properties/' + total_inertia_path = './component_design/tank/mass_properties/inertia/' + total_cog_path = './component_design/tank/mass_properties/center_of_gravity/' + # data_dict['total_tank_parameters']['position'] = \ + # coordinate_system_conversion(data_dict['total_tank_parameters']['position']) + + key_output_dict = { + # Total tank parameter. + 'x': + [total_position_path + 'x', + data_dict['total_tank_parameters']['position']['x']], + 'y': + [total_position_path + 'y', + data_dict['total_tank_parameters']['position']['y']], + 'z': + [total_position_path + 'z', + data_dict['total_tank_parameters']['position']['z']], + 'mass': + [total_mass_path + 'mass', + data_dict['total_tank_parameters']['mass']], + 'j_xx': + [total_inertia_path + 'j_xx', + data_dict['total_tank_parameters']['inertia']['j_xx']], + 'j_yy': + [total_inertia_path + 'j_yy', + data_dict['total_tank_parameters']['inertia']['j_yy']], + 'j_zz': + [total_inertia_path + 'j_zz', + data_dict['total_tank_parameters']['inertia']['j_zz']], + 'j_xy': + [total_inertia_path + 'j_xy', + data_dict['total_tank_parameters']['inertia']['j_xy']], + 'j_xz': + [total_inertia_path + 'j_xz', + data_dict['total_tank_parameters']['inertia']['j_xz']], + 'j_yx': + [total_inertia_path + 'j_yx', + data_dict['total_tank_parameters']['inertia']['j_yx']], + 'j_yz': + [total_inertia_path + 'j_yz', + data_dict['total_tank_parameters']['inertia']['j_yz']], + 'j_zx': + [total_inertia_path + 'j_zx', + data_dict['total_tank_parameters']['inertia']['j_zx']], + 'j_zy': + [total_inertia_path + 'j_zy', + data_dict['total_tank_parameters']['inertia']['j_zy']], + 'cog_x': + [total_cog_path + 'x', + data_dict['total_tank_parameters']['center_of_gravity']['x']], + 'cog_y': + [total_cog_path + 'y', + data_dict['total_tank_parameters']['center_of_gravity']['y']], + 'cog_z': + [total_cog_path + 'z', + data_dict['total_tank_parameters']['center_of_gravity']['z']] + } + + # Generate dictionary for each tank entity. + j = 0 + while j < data_dict['number_of_tanks']: + tank_path_id = './component_design/tank/specific/tank[@ID="' + str(j) + '"]/' + tank_id = 'tank_' + str(j) + # data_dict[tank_id]['geometry']['total']['position'] = \ + # coordinate_system_conversion(data_dict[tank_id]['geometry']['total']['position']) + tank_output_dict = { + 'name_id' + str(j): + [tank_path_id + 'name', + 'tank_' + str(j)], + 'designator_id' + str(j): + [tank_path_id + 'designator', + data_dict[tank_id]['tank_location'] + '_' + data_dict[tank_id]['tank_position']], + 'x_id' + str(j): + [tank_path_id + 'position/x', + round(data_dict[tank_id]['geometry']['total']['position']['x'], 9)], + 'y_id' + str(j): + [tank_path_id + 'position/y', + round(data_dict[tank_id]['geometry']['total']['position']['y'], 9)], + 'z_id' + str(j): + [tank_path_id + 'position/z', + round(data_dict[tank_id]['geometry']['total']['position']['z'], 9)], + 'direction_x_id' + str(j): + [tank_path_id + 'direction/x', + data_dict[tank_id]['geometry']['total']['direction']['x']], + 'direction_y_id' + str(j): + [tank_path_id + 'direction/y', + data_dict[tank_id]['geometry']['total']['direction']['y']], + 'direction_z_id' + str(j): + [tank_path_id + 'direction/z', + data_dict[tank_id]['geometry']['total']['direction']['z']], + 'mass_id' + str(j): + [tank_path_id + 'mass_properties/mass', + data_dict[tank_id]['geometry']['total']['mass']], + 'j_xx_id' + str(j): + [tank_path_id + 'mass_properties/inertia/j_xx', + data_dict[tank_id]['geometry']['total']['inertia']['j_xx']], + 'j_yy_id' + str(j): + [tank_path_id + 'mass_properties/inertia/j_yy', + data_dict[tank_id]['geometry']['total']['inertia']['j_yy']], + 'j_zz_id' + str(j): + [tank_path_id + 'mass_properties/inertia/j_zz', + data_dict[tank_id]['geometry']['total']['inertia']['j_zz']], + 'j_xy_id' + str(j): + [tank_path_id + 'mass_properties/inertia/j_xy', + data_dict[tank_id]['geometry']['total']['inertia']['j_xy']], + 'j_xz_id' + str(j): + [tank_path_id + 'mass_properties/inertia/j_xz', + data_dict[tank_id]['geometry']['total']['inertia']['j_xz']], + 'j_yx_id' + str(j): + [tank_path_id + 'mass_properties/inertia/j_yx', + data_dict[tank_id]['geometry']['total']['inertia']['j_yx']], + 'j_yz_id' + str(j): + [tank_path_id + 'mass_properties/inertia/j_yz', + data_dict[tank_id]['geometry']['total']['inertia']['j_yz']], + 'j_zx_id' + str(j): + [tank_path_id + 'mass_properties/inertia/j_zx', + data_dict[tank_id]['geometry']['total']['inertia']['j_zx']], + 'j_zy_id' + str(j): + [tank_path_id + 'mass_properties/inertia/j_zy', + data_dict[tank_id]['geometry']['total']['inertia']['j_zy']], + 'cog_x_id' + str(j): + [tank_path_id + 'mass_properties/center_of_gravity/x', + data_dict[tank_id]['geometry']['total']['center_of_gravity']['x']], + 'cog_y_id' + str(j): + [tank_path_id + 'mass_properties/center_of_gravity/y', + data_dict[tank_id]['geometry']['total']['center_of_gravity']['y']], + 'cog_z_id' + str(j): + [tank_path_id + 'mass_properties/center_of_gravity/z', + data_dict[tank_id]['geometry']['total']['center_of_gravity']['z']], + 'energy_id' + str(j): + [tank_path_id + 'maximum_energy_capacity', + data_dict[tank_id]['energy_available']], + 'required_energy_id' + str(j): + [tank_path_id + 'energy_capacity_required_for_mission', + data_dict[tank_id]['energy_required_for_mission']], + 'centroid_x_id' + str(j): + [tank_path_id + 'mass_properties/centroid/x', + round(data_dict[tank_id]['geometry']['total']['centroid']['x'], 9)], + 'centroid_y_id' + str(j): + [tank_path_id + 'mass_properties/centroid/y', + round(data_dict[tank_id]['geometry']['total']['centroid']['y'], 9)], + 'centroid_z_id' + str(j): + [tank_path_id + 'mass_properties/centroid/z', + round(data_dict[tank_id]['geometry']['total']['centroid']['z'], 9)], + } + + # Iterate over tank sections of each tank entity. + i = 0 + if data_dict[tank_id]['energy_carrier_name'] == 'kerosene': + number_of_sections = 2 + section_names = ['inner_surface', 'outer_surface'] + if data_dict[tank_id]['tank_location'] == 'fuselage' and data_dict[tank_id]['tank_position'] == 'center': + number_of_sections = sum(1 for k in data_dict[tank_id]['geometry'].keys() + if k.startswith('cross_section_')) + section_names = [k for k in data_dict[tank_id]['geometry'].keys() if k.startswith('cross_section_')] + section_names = sorted(section_names, key=lambda x: int(x.split('_')[-1])) + if data_dict[tank_id]['tank_location'] == 'horizontal_stabilizer' and ( + data_dict[tank_id]['tank_position'] == 'total'): + number_of_sections = sum(1 for k in data_dict[tank_id]['geometry'].keys() + if k.startswith('cross_section_')) + section_names = [k for k in data_dict[tank_id]['geometry'].keys() if k.startswith('cross_section_')] + section_names = sorted(section_names, key=lambda x: int(x.split('_')[-1])) + else: + number_of_sections = sum(1 for k in data_dict[tank_id]['geometry'].keys() + if k.startswith('cross_section_')) + section_names = [k for k in data_dict[tank_id]['geometry'].keys() if k.startswith('cross_section_')] + section_names = sorted(section_names, key=lambda x: int(x.split('_')[-1])) + while i < number_of_sections: + tank_section_path = tank_path_id + 'geometry/cross_section[@ID="' + str(i) + '"]/' + tank_section_name = 'tank_section_' + str(i) + # data_dict[tank_id]['geometry'][section_names[i]]['position'] = \ + # coordinate_system_conversion(data_dict[tank_id]['geometry'][section_names[i]]['position']) + # Check if the energy carrier of current tank is kerosene + # -> if true: -> use coordinate transformation for wing tanks. + if data_dict['tank_' + str(j)]['energy_carrier_name'] == 'kerosene': + tank_section_output_dict = { + 'cross_section_name_id' + str(j) + '_id' + str(i): + [tank_section_path + 'name', + tank_section_name], + 'cross_section_x_id' + str(j) + '_id' + str(i): + [tank_section_path + 'position/x', + round(data_dict[tank_id]['geometry'][section_names[i]]['position']['x'], 9)], + 'cross_section_y_id' + str(j) + '_id' + str(i): + [tank_section_path + 'position/y', + round(data_dict[tank_id]['geometry'][section_names[i]]['position']['z'], 9)], + 'cross_section_z_id' + str(j) + '_id' + str(i): + [tank_section_path + 'position/z', + round(data_dict[tank_id]['geometry'][section_names[i]]['position']['y'], 9)], + 'shape_id' + str(j) + '_id' + str(i): + [tank_section_path + 'shape', + data_dict[tank_id]['geometry'][section_names[i]]['shape']], + 'height_id' + str(j) + '_id' + str(i): + [tank_section_path + 'height', + data_dict[tank_id]['geometry'][section_names[i]]['height']], + 'width_id' + str(j) + '_id' + str(i): + [tank_section_path + 'width', + data_dict[tank_id]['geometry'][section_names[i]]['width']], + 'length_id' + str(j) + '_id' + str(i): + [tank_section_path + 'length', + data_dict[tank_id]['geometry'][section_names[i]]['length']] + } + # Else condition: The energy carrier of current tank is not kerosene + # -> use coordinate transformation for fuselage tanks. + else: + tank_section_output_dict = { + 'cross_section_name_id' + str(j) + '_id' + str(i): + [tank_section_path + 'name', + tank_section_name], + 'cross_section_x_id' + str(j) + '_id' + str(i): + [tank_section_path + 'position/x', + round(data_dict[tank_id]['geometry'][section_names[i]]['position']['y'] * -1, 9)], + 'cross_section_y_id' + str(j) + '_id' + str(i): + [tank_section_path + 'position/y', + round(data_dict[tank_id]['geometry'][section_names[i]]['position']['z'], 9)], + 'cross_section_z_id' + str(j) + '_id' + str(i): + [tank_section_path + 'position/z', + round(data_dict[tank_id]['geometry'][section_names[i]]['position']['x'], 9)], + 'shape_id' + str(j) + '_id' + str(i): + [tank_section_path + 'shape', + data_dict[tank_id]['geometry'][section_names[i]]['shape']], + 'height_id' + str(j) + '_id' + str(i): + [tank_section_path + 'height', + data_dict[tank_id]['geometry'][section_names[i]]['height']], + 'width_id' + str(j) + '_id' + str(i): + [tank_section_path + 'width', + data_dict[tank_id]['geometry'][section_names[i]]['width']], + 'length_id' + str(j) + '_id' + str(i): + [tank_section_path + 'length', + data_dict[tank_id]['geometry'][section_names[i]]['length']] + } + # merge dictionary to one single output dictionary + tank_output_dict.update(tank_section_output_dict) + i += 1 + + key_output_dict.update(tank_output_dict) + j += 1 + + # Add additional fuselage length. + additional_length_dict = { + 'additional_fuselage_length': ['./component_design/tank/specific/additional_fuselage_length', + data_dict['total_tank_parameters']['additional_fuselage_length']] + } + + key_output_dict.update(additional_length_dict) + + """Method-specific output.""" + method_specific_output_dict = {} + for key, value in key_output_dict.items(): + if 'mass_properties' not in value[0]: + method_specific_output_dict[key] = value + + return key_output_dict, method_specific_output_dict + + +def coordinate_system_conversion(element): + if len(element.keys()) > 3: + element_3d = py11csc.Element3D(element["j_xx"], element["j_xy"], element["j_xz"], + element["j_yx"], element["j_yy"], element["j_yz"], + element["j_zx"], element["j_zy"], element["j_zz"]) + element_3d.AC2CGAL() + for k in element.keys(): + element[k] = getattr(element_3d, k.split("_")[-1])() + return element + else: + element_3d = py11csc.Element3D(element["x"], element["y"], element["z"]) + element_3d.AC2CGAL() + for k in element.keys(): + element[k] = getattr(element_3d, k)() + return element diff --git a/tank_design/tank_design_conf.xml b/tank_design/tank_design_conf.xml index d205a1e2c14a4149b7ac6a70378638bbf0596994..cf99c495d6f789eb71e93c2ba8e9d88473c26457 100644 --- a/tank_design/tank_design_conf.xml +++ b/tank_design/tank_design_conf.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> - <module_configuration_file Name="Tank Design Runtime Configuration"> <!-- Change naming according to module name --> + <module_configuration_file Name="Tank Design Runtime Configuration"> <control_settings description="General control settings for this tool"> <aircraft_exchange_file_name description="Specify the name of the exchange file"> <value>csmr-2020.xml</value> @@ -10,42 +10,47 @@ <own_tool_level description="Specify the tool level of this tool"> <value>2</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> - </console_output> - <log_file_output description="Selector to specify the log file output. Selector: mode_0 (Off) / mode_1 (only out/err/warn) / mode_2 (1 + info) / mode_3 (2 + debug)"> - <value>mode_1</value> - </log_file_output> - <plot_output description="Specify the way plotting shall be handled"> - <enable description="Switch to enable plotting. Switch: true (On) / false (Off)"> - <value>true</value> - </enable> - <copy_plotting_files description="Switch if plotting files shall be copied. Switch: true (On) / false (Off)"> - <value>true</value> - </copy_plotting_files> - <delete_plotting_files_from_tool_folder description="Switch if plotting files shall be deleted from folder. Switch: true (On) / false (Off)"> - <value>true</value> - </delete_plotting_files_from_tool_folder> - </plot_output> - <report_output description="Switch to generate an HTML report. Switch: true (On) / false (Off)"> - <value>true</value> - </report_output> - <tex_report description="Switch to generate a Tex report. Switch: true (On) / false (Off)"> - <value>true</value> - </tex_report> - <write_info_files description="Switch to generate info files. Switch: true (On) / false (Off)"> - <value>true</value> - </write_info_files> - <log_file description="Specify the name of the log file"> - <value>tank_design.log</value> - </log_file> - <inkscape_path description="Path to the inkscape application (DEFAULT: Use inkscape from the UNICADO repo structure)"> - <value>DEFAULT</value> - </inkscape_path> - <gnuplot_path description="Path to the gnuplot application (DEFAULT: Use gnuplot from the UNICADO repo structure)"> - <value>DEFAULT</value> - </gnuplot_path> - </control_settings> + <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> + </console_output> + <log_file_output description="Selector to specify the log file output. Selector: mode_0 (Off) / mode_1 (only out/err/warn) / mode_2 (1 + info) / mode_3 (2 + debug)"> + <value>mode_1</value> + </log_file_output> + <plot_output description="Specify the way plotting shall be handled"> + <enable description="Switch to enable plotting. Switch: true (On) / false (Off)"> + <value>true</value> + </enable> + <copy_plotting_files description="Switch if plotting files shall be copied. Switch: true (On) / false (Off)"> + <value>true</value> + </copy_plotting_files> + <delete_plotting_files_from_tool_folder description="Switch if plotting files shall be deleted from folder. Switch: true (On) / false (Off)"> + <value>true</value> + </delete_plotting_files_from_tool_folder> + </plot_output> + <report_output description="Switch to generate an HTML report. Switch: true (On) / false (Off)"> + <value>true</value> + </report_output> + <tex_report description="Switch to generate a Tex report. Switch: true (On) / false (Off)"> + <value>true</value> + </tex_report> + <write_info_files description="Switch to generate info files. Switch: true (On) / false (Off)"> + <value>true</value> + </write_info_files> + <log_file description="Specify the name of the log file"> + <value>tank_design.log</value> + </log_file> + <inkscape_path description="Path to the inkscape application (DEFAULT: Use inkscape from the UNICADO repo structure)"> + <value>DEFAULT</value> + </inkscape_path> + <gnuplot_path description="Path to the gnuplot application (DEFAULT: Use gnuplot from the UNICADO repo structure)"> + <value>DEFAULT</value> + </gnuplot_path> + <program_specific_control_settings description="Program specific control settings for this tool"> + <xml_output description="Switch to export module specific data to XML ('true': On, 'false': Off)"> + <value>true</value> + </xml_output> + </program_specific_control_settings> + </control_settings> <program_settings description="program settings"> <configuration ID="tube_and_wing"> <fidelity_name description="Select fidelity name (options: empirical, numerical,...)"> @@ -68,111 +73,12 @@ </general> <specific> <!-- Specific parameters for kerosene tank design. --> - <kerosene_gravimetric_energy_density description="Gravimetric energy density Jet A-1."> - <value>43100000</value> - <unit>J/kg</unit> - <lower_boundary>0</lower_boundary> - <upper_boundary>100000000</upper_boundary> - <default>43100000</default> - </kerosene_gravimetric_energy_density> - <kerosene_volumetric_energy_density description="Volumetric energy density Jet A-1."> - <value>34100000000</value> - <unit>J/m^3</unit> - <lower_boundary>0</lower_boundary> - <upper_boundary>40000000000</upper_boundary> - <default>34100000000</default> - </kerosene_volumetric_energy_density> - <liquid_hydrogen_tank_structure> - <liquid_hydrogen_gravimetric_energy_density description="Gravimetric energy density."> - <value>119930000</value> - <unit>J/kg</unit> - <lower_boundary>0</lower_boundary> - <upper_boundary>120000000</upper_boundary> - <default>119930000</default> - </liquid_hydrogen_gravimetric_energy_density> - <liquid_hydrogen_volumetric_energy_density description="Volumetric energy density."> - <value>8491000000</value> - <unit>J/m^3</unit> - <lower_boundary>0</lower_boundary> - <upper_boundary>8500000000</upper_boundary> - <default>8491000000</default> - </liquid_hydrogen_volumetric_energy_density> - <!-- Tank structure related parameters--> - <material_wall description="Tank wall material (options: aluminum)"> - <value>aluminum</value> - <default>aluminum</default> - </material_wall> - <density_wall description="Density of wall material (for aluminum: 2850)"> - <value>2850</value> - <unit>kg/m^3</unit> - <lower_boundary>0</lower_boundary> - <upper_boundary>5000</upper_boundary> - <default>2850</default> - </density_wall> - <thickness_wall_calculation_method description="Method for calculation of wall thickness (options: asme, ad-2000)"> - <value>asme</value> - <default>asme</default> - </thickness_wall_calculation_method> - <mass_baffle description="Additional mass of baffles"> - <value>45</value> - <unit>kg</unit> - <lower_boundary>0</lower_boundary> - <upper_boundary>100</upper_boundary> - <default>45</default> - </mass_baffle> - <mass_pumps description="Additional mass of pumps"> - <value>9.26</value> - <unit>kg</unit> - <lower_boundary>0</lower_boundary> - <upper_boundary>100</upper_boundary> - <default>9.26</default> - </mass_pumps> - </liquid_hydrogen_tank_structure> - <liquid_hydrogen_tank_insulation> - <!-- Tank insulation related parameters--> - <type_insulation description="Insulation type (options: internal, external)"> - <value>external</value> - <default>external</default> - </type_insulation> - <material_insulation description="Insulation material (options: polyurethane_foam)"> - <value>polyurethane_foam</value> - <default>polyurethane_foam</default> - </material_insulation> - <thickness_insulation description="Thickness of insulation"> - <value>0.2</value> - <unit>m</unit> - <lower_boundary>0</lower_boundary> - <upper_boundary>1.0</upper_boundary> - <default>0.2</default> - </thickness_insulation> - <density_insulation description="Density of insulation material (for polyurethane foam: 32)"> - <value>32</value> - <unit>kg/m^3</unit> - <lower_boundary>0</lower_boundary> - <upper_boundary>100</upper_boundary> - <default>32</default> - </density_insulation> - </liquid_hydrogen_tank_insulation> - <liquid_hydrogen_tank_design_parameter> - <!-- Design parameters--> - <type_end_cap description="Tank end cap type (options: hemispherical, torispherical)"> - <value>torispherical</value> - <default>torispherical</default> - </type_end_cap> - <internal_pressure description="Internal tank pressure (venting pressure)"> - <value>1.5</value> - <unit>bar</unit> - <lower_boundary>1</lower_boundary> - <upper_boundary>5</upper_boundary> - <default>1.5</default> - </internal_pressure> - </liquid_hydrogen_tank_design_parameter> - <kerosene_tank_design_parameter> + <kerosene_tank_design_parameter description="Parameters for kerosene tank design"> <obelisk_calculation_method description="Obelisk volume calculation method (Torenbeek or Simpson)."> <value>Simpson</value> <default>Torenbeek</default> </obelisk_calculation_method> - <a_to_d_factor> + <a_to_d_factor description="Factor ..."> <value>0.9</value> <unit>1</unit> <lower_boundary>0</lower_boundary> @@ -215,6 +121,75 @@ <default>0.01</default> </buffer_center_tank_segment> </kerosene_tank_design_parameter> + <!-- Specific parameters for liquid hydrogen tank design. --> + <liquid_hydrogen_tank_design_parameter description="Parameters for liquid hydrogen tank design"> + <!-- Design parameters--> + <type_end_cap description="Tank end cap type (options: hemispherical, torispherical)"> + <value>torispherical</value> + <default>torispherical</default> + </type_end_cap> + <internal_pressure description="Internal tank pressure (venting pressure)"> + <value>1.5</value> + <unit>bar</unit> + <lower_boundary>1</lower_boundary> + <upper_boundary>5</upper_boundary> + <default>1.5</default> + </internal_pressure> + <!-- Tank insulation related parameters--> + <type_insulation description="Insulation type (options: internal, external)"> + <value>external</value> + <default>external</default> + </type_insulation> + <material_insulation description="Insulation material (options: polyurethane_foam)"> + <value>polyurethane_foam</value> + <default>polyurethane_foam</default> + </material_insulation> + <thickness_insulation description="Thickness of insulation"> + <value>0.2</value> + <unit>m</unit> + <lower_boundary>0</lower_boundary> + <upper_boundary>1.0</upper_boundary> + <default>0.2</default> + </thickness_insulation> + <density_insulation description="Density of insulation material (for polyurethane foam: 32)"> + <value>32</value> + <unit>kg/m^3</unit> + <lower_boundary>0</lower_boundary> + <upper_boundary>100</upper_boundary> + <default>32</default> + </density_insulation> + <!-- Tank structure related parameters--> + <material_wall description="Tank wall material (options: aluminum)"> + <value>aluminum</value> + <default>aluminum</default> + </material_wall> + <density_wall description="Density of wall material (for aluminum: 2850)"> + <value>2850</value> + <unit>kg/m^3</unit> + <lower_boundary>0</lower_boundary> + <upper_boundary>5000</upper_boundary> + <default>2850</default> + </density_wall> + <thickness_wall_calculation_method description="Method for calculation of wall thickness (options: asme, ad-2000)"> + <value>asme</value> + <default>asme</default> + </thickness_wall_calculation_method> + <mass_baffle description="Additional mass of baffles"> + <value>45</value> + <unit>kg</unit> + <lower_boundary>0</lower_boundary> + <upper_boundary>100</upper_boundary> + <default>45</default> + </mass_baffle> + <mass_pumps description="Additional mass of pumps"> + <value>9.26</value> + <unit>kg</unit> + <lower_boundary>0</lower_boundary> + <upper_boundary>100</upper_boundary> + <default>9.26</default> + </mass_pumps> + </liquid_hydrogen_tank_design_parameter> + <!-- Miscellaneous parameters for tank design. --> <miscellaneous> <!-- Miscellaneous parameters--> <factor_usable_diameter description="Usable fraction of fuselage outside diameter">