From 1bf6cb569c41b57d95ba67c1624a71e1b6abfb1c Mon Sep 17 00:00:00 2001 From: sroscher <s.roscher@tu-berlin.de> Date: Thu, 6 Mar 2025 15:09:01 +0100 Subject: [PATCH 1/7] Changed necessary files --- tank_design/src/datapostprocessing.py | 124 ++++++- .../general/calculatecenterofgravity.py | 25 +- .../_01_checktankconfiguration.py | 81 ++++- .../_02_preparegeometricaldata.py | 246 ++++++++------ .../general/methodhtmlreport.py | 68 ++-- .../liquid_hydrogen/calculateconicaltank.py | 302 ++++++++++++----- .../calculatecylindricaltank.py | 192 ++++++++--- .../calculateliquidhydrogentanks.py | 311 ++++++++++++++++++ .../calculateparametercylinder.py | 6 +- .../calculateparameterendcap.py | 6 +- .../calculateparametertruncatedcone.py | 6 +- .../liquid_hydrogen/calculatethicknesswall.py | 8 +- .../liquid_hydrogen/calculatezposition.py | 41 +++ .../liquid_hydrogen/methodliquidhydrogen.py | 208 ++---------- .../usermethoddatapreparation.py | 90 ++++- 15 files changed, 1249 insertions(+), 465 deletions(-) create mode 100644 tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateliquidhydrogentanks.py create mode 100644 tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculatezposition.py diff --git a/tank_design/src/datapostprocessing.py b/tank_design/src/datapostprocessing.py index dc970c2f..d47d4c2b 100644 --- a/tank_design/src/datapostprocessing.py +++ b/tank_design/src/datapostprocessing.py @@ -99,6 +99,15 @@ def data_postprocessing(paths_and_names, routing_dict, data_dict, runtime_output './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' ] + # Append more paths if energy carrier is hydrogen. + if routing_dict['user_layer'] == 'liquid_hydrogen': + tmp_path = './component_design/tank/specific/tank[@ID="0"]/mass_breakdown/' + paths_to_key_parameters_list.extend([ + tmp_path + 'tank_insulation/component_mass[@ID="0"]/name', + tmp_path + 'tank_insulation/component_mass[@ID="0"]/mass', + tmp_path + 'tank_structure/component_mass[@ID="0"]/name', + tmp_path + 'tank_structure/component_mass[@ID="0"]/mass']) + module_key_parameters_dict = { 'component_design': { 'tank': { @@ -384,7 +393,7 @@ def data_postprocessing(paths_and_names, routing_dict, data_dict, runtime_output 'attributes': {'description': 'Center of gravity of one tank.'}, 'x': { 'attributes': { - 'description': 'Center of gravity in x-direction with regard to the global ' + 'description': 'Center of gravity in x-direction with regard to the local ' 'reference point.'}, 'value': '0', 'unit': 'm', @@ -392,7 +401,7 @@ def data_postprocessing(paths_and_names, routing_dict, data_dict, runtime_output 'upper_boundary': '80'}, 'y': { 'attributes': { - 'description': 'Center of gravity in y-direction with regard to the global ' + 'description': 'Center of gravity in y-direction with regard to the local ' 'reference point.'}, 'value': '0', 'unit': 'm', @@ -400,7 +409,7 @@ def data_postprocessing(paths_and_names, routing_dict, data_dict, runtime_output 'upper_boundary': '40'}, 'z': { 'attributes': { - 'description': 'Center of gravity in z-direction with regard to the global ' + 'description': 'Center of gravity in z-direction with regard to the local ' 'reference point.'}, 'value': '0', 'unit': 'm', @@ -510,22 +519,115 @@ def data_postprocessing(paths_and_names, routing_dict, data_dict, runtime_output 'lower_boundary': '0', 'upper_boundary': 'inf'} } - } + }#, }, + # 'mass_breakdown': { + # 'attributes': { + # 'description': 'Mass breakdown of one tank.'}, + # 'tank_insulation': { + # 'attributes': { + # 'description': 'Mass breakdown of the tank insulation.'}, + # 'component_mass': { + # 'attributes': { + # 'ID': '0', + # 'description': 'Mass of a single component of the tank insulation.'}, + # 'name': { + # 'attributes': { + # 'description': 'Name of the tank insulation component.'}, + # 'value': 'tank_insulation_mass'}, + # 'mass': { + # 'attributes': { + # 'description': 'Mass of the tank insulation component.'}, + # 'value': '0', + # 'unit': 'kg', + # 'lower_boundary': '0', + # 'upper_boundary': '100000'}} + # }, + # 'tank_structure': { + # 'attributes': { + # 'description': 'Mass breakdown of the tank structure.'}, + # 'component_mass': { + # 'attributes': { + # 'ID': '0', + # 'description': 'Mass of a single component of the tank structure.'}, + # 'name': { + # 'attributes': { + # 'description': 'Name of the tank structure component.'}, + # 'value': 'tank_structure_mass'}, + # 'mass': { + # 'attributes': { + # 'description': 'Mass of the tank structure component.'}, + # 'value': '0', + # 'unit': 'kg', + # 'lower_boundary': '0', + # 'upper_boundary': '100000'} + # }, + # } + # } + # }, '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' + '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' } } } } } + if routing_dict['user_layer'] == 'liquid_hydrogen': + liquid_hydrogen_mass_breakdown = { + 'mass_breakdown': { + 'attributes': { + 'description': 'Mass breakdown of one tank.'}, + 'tank_insulation': { + 'attributes': { + 'description': 'Mass breakdown of the tank insulation.'}, + 'component_mass': { + 'attributes': { + 'ID': '0', + 'description': 'Mass of a single component of the tank insulation.'}, + 'name': { + 'attributes': { + 'description': 'Name of the tank insulation component.'}, + 'value': 'tank_insulation_mass'}, + 'mass': { + 'attributes': { + 'description': 'Mass of the tank insulation component.'}, + 'value': '0', + 'unit': 'kg', + 'lower_boundary': '0', + 'upper_boundary': '100000'}} + }, + 'tank_structure': { + 'attributes': { + 'description': 'Mass breakdown of the tank structure.'}, + 'component_mass': { + 'attributes': { + 'ID': '0', + 'description': 'Mass of a single component of the tank structure.'}, + 'name': { + 'attributes': { + 'description': 'Name of the tank structure component.'}, + 'value': 'tank_structure_mass'}, + 'mass': { + 'attributes': { + 'description': 'Mass of the tank structure component.'}, + 'value': '0', + 'unit': 'kg', + 'lower_boundary': '0', + 'upper_boundary': '100000'} + } + } + } + } + module_key_parameters_dict['component_design']['tank']['specific']['tank']['mass_breakdown'] = ( + liquid_hydrogen_mass_breakdown['mass_breakdown']) + 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'. diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/calculatecenterofgravity.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/calculatecenterofgravity.py index bb89375b..f1357b75 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/calculatecenterofgravity.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/calculatecenterofgravity.py @@ -18,13 +18,17 @@ # Description: # This file is part of UNICADO. -def calculate_center_of_gravity(tank_shape, diameter_front, diameter_rear, length): - """_summary_ +def calculate_center_of_gravity(runtime_output, tank_shape, diameter_front, diameter_rear, length): + """Calculate center of gravity. + + The coordinates refer to the local reference point. This is located in the lower left corner of the front tank + section. Only the conical or cylindrical parts of the tank are taken into account. The end caps are not considered. - :param _type_ tank_entity: _description_ - :param dict dict_ac_data: Dict containing parameter and values from aircraft exchange and module configuration file - :param dict dict_tank_design: Dict containing results from tank design :param logging.Logger runtime_output: Logging object used for capturing log messages in the module + :param str tank_shape: _description_ + :param float diameter_front: Diameter at front end of tank + :param float diameter_rear: Diameter at rear end of tank + :param float length: Length of cylindrical/conical tank section """ match tank_shape: @@ -34,13 +38,18 @@ def calculate_center_of_gravity(tank_shape, diameter_front, diameter_rear, lengt z_coordinate_center_of_gravity = (diameter_front**2 - diameter_rear**2 + abs(diameter_rear)*(diameter_front + 2*diameter_rear))/( 3*(diameter_front + diameter_rear)) - print('Calculating center of gravity for conical tank.') + runtime_output.debug('Debug: Calculating center of gravity for conical tank.') case 'cylindrical': x_coordinate_center_of_gravity = length/2 z_coordinate_center_of_gravity = diameter_front/2 - print('Calculating center of gravity for cylindrical tank.') + runtime_output.debug('Debug: Calculating center of gravity for cylindrical tank.') y_coordinate_center_of_gravity = 0.0 - return x_coordinate_center_of_gravity, y_coordinate_center_of_gravity, z_coordinate_center_of_gravity + # Debug print. + runtime_output.debug('Debug: The "calculate_center_of_gravity" function was successfully executed.') + + return {'x': float(x_coordinate_center_of_gravity), + 'y': float(y_coordinate_center_of_gravity), + 'z': float(z_coordinate_center_of_gravity)} 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 198fa297..dee8e9ec 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 @@ -174,12 +174,27 @@ def check_tank_configuration(paths_and_names, dict_ac_data, dict_tank_design, ru dict_tank_design['tank_configuration'] = tank_configuration """Liquid hydrogen.""" - # TODO Implement liquid hydrogen. if dict_ac_data['energy_carrier'] == 'liquid_hydrogen': + # Check if tank design possible (only one payload tube w/ one deck possible atm). + number_of_payload_tubes = len(dict_ac_data['fuselage_payload_tube_name'].keys()) + number_of_payload_decks = len(dict_ac_data['fuselage_payload_deck_origin_x'].keys()) + if number_of_payload_tubes < 1: + runtime_output.critical('Error: Not enough payload tubes given. Program aborted.') + sys.exit('Exit information: Not enough payload tubes (less than 1) given!') + elif number_of_payload_tubes == 1: + if number_of_payload_decks > 2: + runtime_output.critical('Error: Too many payload decks given (not more than 2 possible at the moment). ' + + 'Program aborted.') + sys.exit('Exit information: Too many payload decks (more than 2) given!') + elif number_of_payload_tubes > 1: + runtime_output.critical('Error: Too many payload tubes given (not more than 1 possible at the moment). ' + + 'Program aborted.') + sys.exit('Exit information: Too many payload tubes (more than 1) given!') + # Define tank combination dict based on energy carrier. combinations = { # Standard integral wing tanks. 'fuselage': - {'tailcone': False, + {'tail_cone': False, 'rear': False, 'front': False, 'top': False, @@ -188,9 +203,65 @@ def check_tank_configuration(paths_and_names, dict_ac_data, dict_tank_design, ru 'wing': {'pods': False} } + + # Iterate over tank entities of 'tank_entity_list'. + for tank_entity in tank_entity_list: + # Temporary variables to shorten lines. + tmp_location = dict_tank_design[tank_entity]['tank_location'] + tmp_position = dict_tank_design[tank_entity]['tank_position'] + + # Test if current combination of tank location and position is available in 'combinations' dictionary. + try: + tmp_combination = combinations[tmp_location][tmp_position] + + if tmp_combination is False: + # If value is 'False', set value to 'True'. + combinations[tmp_location][tmp_position] = True + else: + # If value is 'True', combination already exists and tank definition is invalid. Abort program. + runtime_output.critical('Error: Tank "' + str(tank_entity) + '" with location "' + + str(tmp_location) + '" and position "' + str(tmp_position) + + '" already defined. ' + 'Invalid tank definition. Program aborted.') + sys.exit('Exit information: Invalid tank definition in aircraft exchange file.' + + 'Additional information on valid tank definitions can be found on the “Getting startedâ€' + +' page of the official documentation.') + # Raise KeyError if combination of tank location and position is not available in 'combinations' dict. + except KeyError: + location_exists = tmp_location in combinations + position_exists = tmp_position in combinations[tmp_location] if location_exists else False + if location_exists and not position_exists: + tmp_str = f"Tank location '{tmp_location}' is valid, but position '{tmp_position}' is not. " + else: + tmp_str = f"Tank location '{tmp_location}' is invalid. " + runtime_output.critical('Error: KeyError found in "check_tank_configuration" function. ' + + tmp_str + 'Program aborted.') + sys.exit('Exit information: Invalid tank definition in aircraft exchange file.' + + 'Additional information on valid tank definitions can be found on the “Getting startedâ€' + +' page of the official documentation.') - runtime_output.critical('Error: Check for valid tank configuration not yet implemented for liquid hydrogen ' - + 'configuration. Program aborted.') - sys.exit('Exit information: Missing function for liquid hydrogen configuration.') + # Valid combinations. + fuselage_rear = combinations['fuselage']['tail_cone'] and combinations['fuselage']['rear'] + fuselage_front = combinations['fuselage']['front'] + fuselage_top = combinations['fuselage']['top'] + fuselage_bottom = combinations['fuselage']['bottom'] + fuselage_all_tanks = fuselage_rear and fuselage_front and fuselage_top and fuselage_bottom + wing_external_pods = combinations['wing']['pods'] + + # Define number of tanks and set tank configuration name for valid tank configurations. + if fuselage_rear and not (fuselage_front and fuselage_top and fuselage_bottom and wing_external_pods): + tank_configuration = 'fuselage_rear_tanks' + number_of_tanks = len(dict_tank_design.keys()) + # Print. + runtime_output.print('Valid tank configuration found in acXML: ' + tank_configuration) + # Invalid tank configuration. + else: + runtime_output.critical('Error: Invalid tank definition. Program aborted.') + sys.exit('Exit information: Invalid tank definition in aircraft exchange file.' + + 'Additional information on valid tank definitions can be found on the “Getting startedâ€' + +' page of the official documentation.') + + # Prepare output. + dict_tank_design['number_of_tanks'] = number_of_tanks + dict_tank_design['tank_configuration'] = tank_configuration return dict_tank_design 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 090609a1..da1437be 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 @@ -655,142 +655,169 @@ def prepare_for_liquid_hydrogen(dict_ac_data, dict_tank_design, runtime_output): * Widths of individual segments * Heights of individual segments (1) The cumulated lengths result in the x-coordinates of the respective segments (x_coord). + Approach: + 1. Extract geometrical values for the fuselage, save to lists, and interpolate widths and heights. + 2. Calculate minimum diameter for every cm of fuselage length. + 3. Calculate maximum usable diameter for every cm of fuselage length. :param dict dict_ac_data: Dict containing parameter and values 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 """ - #TODO: Read position of bulkhead from aircraft exchange file. tail_length_before_bulkhead can then be deleted. - # x_coord_pressure_bulkhead = 0 - tail_length_before_bulkhead = 8.63 # est. length of section from start of tail to bulkhead (from CSR-02) - factor_usable_diameter = dict_ac_data['factor_usable_diameter'] - # Initialize empty lists in dictionary. + # Extract necessary data from data dict. + x_coord_pressure_bulkhead = ( + dict_ac_data['fuselage_position_x'] + + dict_ac_data['fuselage_entity_position_x']['fuselage_entity_position_x_ID0'] + + dict_ac_data['fuselage_accommodation_position_x']['fuselage_accommodation_position_x_ID0'] + + dict_ac_data['fuselage_payload_tube_aft_position_x']['fuselage_payload_tube_aft_position_x_ID0_ID0']) + idx_bulkhead = round(x_coord_pressure_bulkhead*100) + # tail_length_before_bulkhead = 8.63 # est. length of section from start of tail to bulkhead (from CSR-02) + factor_usable_diameter = dict_ac_data['factor_usable_diameter'] + # Initialize empty geometry dictionary. dict_tank_design['geometry'] = {} - dict_tank_design['geometry']['raw_data'] = {} - dict_tank_design['geometry']['raw_data']['lengths'] = [] - dict_tank_design['geometry']['raw_data']['widths'] = [0] - dict_tank_design['geometry']['raw_data']['heights'] = [0] - # Iterate through the dictionary and add single values to lists. - for key, value in dict_ac_data['geometrical_data'].items(): - if key.startswith('length_'): - dict_tank_design['geometry']['raw_data']['lengths'].append(value) - elif key.startswith('width_'): - dict_tank_design['geometry']['raw_data']['widths'].append(value) - elif key.startswith('height_'): - dict_tank_design['geometry']['raw_data']['heights'].append(value) - # Sum up the lengths to a list with x coordinates. + + """Fuselage data.""" + # Initialize empty fuselage dictionary. + dict_tank_design['geometry']['fuselage'] = {} + + # Get widths of fuselage. + dict_tank_design['geometry']['fuselage']['widths'] = [0] + dict_tank_design['geometry']['fuselage']['widths'].extend(dict_ac_data['fuselage_section_width'].values()) + + # Get upper and lower heights of fuselage and sum up to height. + upper_heights = [0] + lower_heights = [0] + upper_heights.extend(dict_ac_data['fuselage_section_upper_height'].values()) + lower_heights.extend(dict_ac_data['fuselage_section_lower_height'].values()) + dict_tank_design['geometry']['fuselage']['upper_heights'] = upper_heights + dict_tank_design['geometry']['fuselage']['lower_heights'] = lower_heights + dict_tank_design['geometry']['fuselage']['heights'] = [a + b for a, b in zip(upper_heights, lower_heights)] + + # Get z origin of fuselage sections. + fuselage_section_origin_z = [0] + fuselage_section_origin_z.extend(dict_ac_data['fuselage_section_origin_z'].values()) + + # Generate a list with x coordinates. x_coord = [0] - for idx, value in enumerate(dict_tank_design['geometry']['raw_data']['lengths']): - x_coord.append(x_coord[idx]+dict_tank_design['geometry']['raw_data']['lengths'][idx]) + x_coord.extend(dict_ac_data['fuselage_section_origin_x'].values()) # Extract fuselage length. length_fuselage = x_coord[-1] - print("Total fuselage length: ", length_fuselage, ' m') - + runtime_output.print(f"Total fuselage length: {float(length_fuselage):,.2f} m") + # Calculate numbers of points (one point per cm). n_points = round(x_coord[-1]*100) x_coord_array = np.linspace(0, x_coord[-1], n_points) + + # Interpolate width and height. width_fuselage_external_interpolated = np.interp(x_coord_array, x_coord, - dict_tank_design['geometry']['raw_data']['widths']) + dict_tank_design['geometry']['fuselage']['widths']) + upper_heights_external_interpolated = np.interp(x_coord_array, x_coord, + dict_tank_design['geometry']['fuselage']['upper_heights']) + lower_heights_external_interpolated = np.interp(x_coord_array, x_coord, + dict_tank_design['geometry']['fuselage']['lower_heights']) height_fuselage_external_interpolated = np.interp(x_coord_array, x_coord, - dict_tank_design['geometry']['raw_data']['heights']) + dict_tank_design['geometry']['fuselage']['heights']) + fuselage_section_origin_z_interpolated = np.interp(x_coord_array, x_coord, fuselage_section_origin_z) + # Calculate minimum diameter for every coordinate (every cm). diameter_outside_max = ([min(width_fuselage_external_interpolated[idx], height_fuselage_external_interpolated[idx]) for idx in range(0, n_points)]) # Calculate maximum usable diameter for every coordinate (every cm). diameter_usable = [diameter_outside_max[idx]*factor_usable_diameter for idx in range(0, n_points)] - dict_tank_design['geometry']['manipulated_data'] = {} - dict_tank_design['geometry']['manipulated_data']['diameter_usable'] = diameter_usable + dict_tank_design['geometry']['fuselage']['diameter_usable'] = diameter_usable + + # TEST + # plt.plot(x_coord, dict_tank_design['geometry']['fuselage']['widths']) + # plt.plot(x_coord, dict_tank_design['geometry']['fuselage']['heights']) + # plt.plot(width_fuselage_external_interpolated[500:600]) + # plt.plot(height_fuselage_external_interpolated[500:600]) + # plt.plot(x_coord_array, fuselage_section_origin_z_interpolated) + # plt.show() + + # widths = dict_tank_design['geometry']['fuselage']['widths'] + # heights = dict_tank_design['geometry']['fuselage']['heights'] + # widths = width_fuselage_external_interpolated + # heights = height_fuselage_external_interpolated + # for i in range(len(widths) - 1): + # # Check if the next width or height is smaller than the current + # smaller_width = round(widths[i + 1],4) < round(widths[i],4) + # smaller_height = round(heights[i + 1], 4) < round(heights[i], 4) + # if smaller_width or smaller_height: + # idx = i # Return the index where the tail section starts + # print(heights[i]) + # print(heights[i + 1]) + # break + # else: + # idx = None + # TEST END + # Find index and x-coordinate of begin of tail section. idx_begin_tail_section = determine_begin_tail_section(width_fuselage_external_interpolated, height_fuselage_external_interpolated) - x_coord_begin_tail_section = x_coord_array[idx_begin_tail_section] + # x_coord_begin_tail_section = x_coord_array[idx_begin_tail_section] # Find x-coordinate of pressure bulkhead. - x_coord_pressure_bulkhead = x_coord_begin_tail_section + tail_length_before_bulkhead - dict_tank_design['geometry']['raw_data']['x_coord_pressure_bulkhead'] = x_coord_pressure_bulkhead + # x_coord_pressure_bulkhead = x_coord_begin_tail_section + tail_length_before_bulkhead + # dict_tank_design['geometry']['raw_data']['x_coord_pressure_bulkhead'] = x_coord_pressure_bulkhead - # Iterate through tank entities. + # Iterate through tank entities and extract necessary geometry. for tank_entity in dict_tank_design.keys(): - if tank_entity.startswith('tank_'): + if tank_entity.startswith('tank_') and tank_entity[5:].isdigit(): # Initialize empty dictionary and lists. dict_tank_design[tank_entity]['geometry'] = {} - dict_tank_design[tank_entity]['geometry']['lengths'] = [] - dict_tank_design[tank_entity]['geometry']['widths_external'] = [] - dict_tank_design[tank_entity]['geometry']['heights_external'] = [] # Extract tank configuration. - tank_location = dict_tank_design[tank_entity]['tank_location'] - tank_position = dict_tank_design[tank_entity]['tank_position'] - - if tank_location == 'fuselage' and tank_position == 'tailcone': - #TODO: Figure out how to determine geometrical data of tailcone. - idx_bulkhead = int(idx_begin_tail_section + tail_length_before_bulkhead*100 + 1) - #TODO: Geometry data, except for box dimensions, necessary? Delete? - print('Tank position is in tailcone of fuselage.') + tank_designator = dict_tank_design[tank_entity]['designator'] + + if tank_designator == 'fuselage_tail_cone': + dict_tank_design[tank_entity]['geometry']['idx_front'] = idx_begin_tail_section + dict_tank_design[tank_entity]['geometry']['idx_rear'] = idx_bulkhead + runtime_output.debug('Preparing data for tank in fuselage tail cone.') dict_tank_design[tank_entity]['tank_shape'] = 'conical' - dict_tank_design[tank_entity]['geometry']['lengths'] = x_coord_array[ - idx_begin_tail_section:idx_bulkhead] - dict_tank_design[tank_entity]['geometry']['widths_external'] = ( - width_fuselage_external_interpolated[idx_begin_tail_section:idx_bulkhead]) - dict_tank_design[tank_entity]['geometry']['heights_external'] = ( - height_fuselage_external_interpolated[idx_begin_tail_section:idx_bulkhead]) - # Extract box dimensions (necessary for calculation). - dict_tank_design[tank_entity]['geometry']['box_dimensions'] = {} - dict_tank_design[tank_entity]['geometry']['box_dimensions']['maximum_length'] = ( - dict_tank_design[tank_entity]['geometry']['lengths'][-1] - - dict_tank_design[tank_entity]['geometry']['lengths'][0] - ) + lengths = x_coord_array[idx_begin_tail_section:idx_bulkhead] + widths_external = width_fuselage_external_interpolated[idx_begin_tail_section:idx_bulkhead] + heights_external = height_fuselage_external_interpolated[idx_begin_tail_section:idx_bulkhead] + # Extract maximum dimensions (necessary for calculation). + dict_tank_design[tank_entity]['geometry']['maximum_length'] = abs(lengths[-1] - lengths[0]) # Assumption: Only the smaller height/width can be used. - dict_tank_design[tank_entity]['geometry']['box_dimensions']['maximum_diameter_front'] = min( - dict_tank_design[tank_entity]['geometry']['widths_external'][0], - dict_tank_design[tank_entity]['geometry']['heights_external'][0] - ) - dict_tank_design[tank_entity]['geometry']['box_dimensions']['maximum_diameter_rear'] = min( - dict_tank_design[tank_entity]['geometry']['widths_external'][-1], - dict_tank_design[tank_entity]['geometry']['heights_external'][-1] - ) - - elif tank_location == 'fuselage' and tank_position == 'rear': - print('Tank position is in rear of fuselage.') + dict_tank_design[tank_entity]['geometry']['maximum_diameter_front'] = min( + widths_external[0], heights_external[0]) + dict_tank_design[tank_entity]['geometry']['maximum_diameter_rear'] = min( + widths_external[-1], heights_external[-1]) + + elif tank_designator == 'fuselage_rear': + dict_tank_design[tank_entity]['geometry']['idx_front'] = None + if dict_tank_design['tank_configuration'] == 'fuselage_rear_tanks': + # Add extra space between tanks in rear and tail cone of fuselage. + # Attention: If this parameter is changed it has to be changed in 'calculateliquidhydrogentanks.py' + # as well! + extra_space = 0.2 + idx_end_of_rear_tank = round(idx_begin_tail_section - extra_space*100) + dict_tank_design[tank_entity]['geometry']['idx_rear'] = idx_end_of_rear_tank + else: + dict_tank_design[tank_entity]['geometry']['idx_rear'] = idx_begin_tail_section-1 + runtime_output.debug('Preparing data for tank in rear of fuselage.') dict_tank_design[tank_entity]['tank_shape'] = 'cylindrical' - dict_tank_design[tank_entity]['geometry']['lengths'] = 0 - dict_tank_design[tank_entity]['geometry']['widths_external'] = ( - width_fuselage_external_interpolated[idx_begin_tail_section-1]) - dict_tank_design[tank_entity]['geometry']['heights_external'] = ( - height_fuselage_external_interpolated[idx_begin_tail_section-1]) - # Box dimensions. - dict_tank_design[tank_entity]['geometry']['box_dimensions'] = {} - dict_tank_design[tank_entity]['geometry']['box_dimensions']['maximum_length'] = 0 - dict_tank_design[tank_entity]['geometry']['box_dimensions']['maximum_diameter_front'] = min( - dict_tank_design[tank_entity]['geometry']['widths_external'], - dict_tank_design[tank_entity]['geometry']['heights_external'] - ) - dict_tank_design[tank_entity]['geometry']['box_dimensions']['maximum_diameter_rear'] = ( - dict_tank_design[tank_entity]['geometry']['box_dimensions']['maximum_diameter_front'] - ) - - elif tank_location == 'fuselage' and tank_position == 'front': - print('Tank position is in front of fuselage.') - dict_tank_design[tank_entity]['tank_shape'] = 'cylindrical' - # TODO: Implement proper extraction of geometric parameters. - dict_tank_design[tank_entity]['geometry']['lengths'] = 0 - dict_tank_design[tank_entity]['geometry']['widths_external'] = ( - width_fuselage_external_interpolated[idx_begin_tail_section-1]) - dict_tank_design[tank_entity]['geometry']['heights_external'] = ( - height_fuselage_external_interpolated[idx_begin_tail_section-1]) - # Box dimensions. - dict_tank_design[tank_entity]['geometry']['box_dimensions'] = {} - dict_tank_design[tank_entity]['geometry']['box_dimensions']['maximum_length'] = 0 - dict_tank_design[tank_entity]['geometry']['box_dimensions']['maximum_diameter_front'] = min( - dict_tank_design[tank_entity]['geometry']['widths_external'], - dict_tank_design[tank_entity]['geometry']['heights_external'] - ) - dict_tank_design[tank_entity]['geometry']['box_dimensions']['maximum_diameter_rear'] = ( - dict_tank_design[tank_entity]['geometry']['box_dimensions']['maximum_diameter_front'] - ) + widths_external = width_fuselage_external_interpolated[idx_end_of_rear_tank] + heights_external = height_fuselage_external_interpolated[idx_end_of_rear_tank] + # Extract maximum dimensions (necessary for calculation). + dict_tank_design[tank_entity]['geometry']['maximum_length'] = 0 + # Assumption: Only the smaller height/width can be used. + dict_tank_design[tank_entity]['geometry']['maximum_diameter_front'] = min( + widths_external, heights_external) + dict_tank_design[tank_entity]['geometry']['maximum_diameter_rear'] = ( + dict_tank_design[tank_entity]['geometry']['maximum_diameter_front']) else: - #TODO: Proper abortion of program. - print('No valid tank configuration. Program aborted.') + runtime_output.critical('Error: Tank combination "' + tank_designator + '" not implemented yet. ' + + 'Program aborted.') + sys.exit('Exit information: Tank combination not implemented yet!') + + # Prepare output data. + dict_tank_design['geometry']['fuselage']['interpolated_widths'] = width_fuselage_external_interpolated + dict_tank_design['geometry']['fuselage']['interpolated_upper_heights'] = upper_heights_external_interpolated + dict_tank_design['geometry']['fuselage']['interpolated_lower_heights'] = lower_heights_external_interpolated + dict_tank_design['geometry']['fuselage']['interpolated_heights'] = height_fuselage_external_interpolated + dict_tank_design['geometry']['fuselage']['interpolated_section_origin_z'] = fuselage_section_origin_z_interpolated # Debug print. runtime_output.debug('Debug: Geometrical data for liquid hydrogen tanks prepared.') @@ -799,15 +826,20 @@ def prepare_for_liquid_hydrogen(dict_ac_data, dict_tank_design, runtime_output): # Determine start of tail section. -def determine_begin_tail_section(width, height): +def determine_begin_tail_section(widths, heights): """_summary_ :param _type_ width: _description_ :param _type_ height: _description_ :return _type_: _description_ """ - idx = 0 - while ((width[idx + 1] >= width[idx]) and (height[idx + 1] >= height[idx])): - idx += 1 - + for idx in range(len(widths) - 1): + # Check if the next width or height is smaller than the current + smaller_width = round(widths[idx + 1],4) < round(widths[idx],4) + smaller_height = round(heights[idx + 1], 4) < round(heights[idx], 4) + if smaller_width or smaller_height: + break + else: + idx = None + return idx diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/methodhtmlreport.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/methodhtmlreport.py index a4f8daaf..faeb7907 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/methodhtmlreport.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/methodhtmlreport.py @@ -48,41 +48,62 @@ def method_html_report(paths_and_names, routing_dict, data_dict, method_specific # Extract data. # Note: Please ensure to format the data here (e.g., round floats)! - # Total energy information data. - tmp_energy = sum(value['energy_available'] for key, value in data_dict.items() if key.startswith("tank_") - and key[5:].isdigit() and not value['energy_required_for_mission']) - tmp_volume = sum(value['volume_available'] for key, value in data_dict.items() if key.startswith("tank_") - and key[5:].isdigit() and not value['energy_required_for_mission']) - energy_missing = data_dict['total_tank_parameters'].get('energy_missing', 0) - volume_missing = data_dict['total_tank_parameters'].get('volume_missing', 0) - energy_information_data = [ ("Tank configuration", - data_dict['tank_configuration']), + data_dict['tank_configuration']), ("Volume required for mission", - f"{data_dict['total_tank_parameters']['volume_required_for_mission']*1000:,.1f} L"), - ("Volume provided in necessary tanks", - f"{(data_dict['total_tank_parameters']['volume_provided_in_all_tanks'] - tmp_volume)*1000:,.1f} L"), + f"{data_dict['total_tank_parameters']['volume_required_for_mission']:.1f} L"), ("Volume provided in all tanks", - f"{data_dict['total_tank_parameters']['volume_provided_in_all_tanks']*1000:,.1f} L"), - ("Volume missing", - f"{volume_missing*1000:,.1f} L"), + f"{data_dict['total_tank_parameters']['volume_provided_in_all_tanks']:.1f} L"), ("Energy required for mission", - f"{data_dict['total_tank_parameters']['energy_required_for_mission']/1E6:,.1f} MJ"), - ("Energy provided in necessary tanks", - f"{(data_dict['total_tank_parameters']['energy_provided_in_all_tanks'] - tmp_energy)/1E6:,.1f} MJ"), + f"{data_dict['total_tank_parameters']['energy_required_for_mission']/1E6:,.1f} MJ"), ("Energy provided in all tanks", - f"{data_dict['total_tank_parameters']['energy_provided_in_all_tanks']/1E6:,.1f} MJ"), - ("Energy missing", - f"{energy_missing/1E6:,.1f} MJ"), + f"{data_dict['total_tank_parameters']['energy_provided_in_all_tanks']/1E6:,.1f} MJ"), ] + if routing_dict['user_layer'] == 'kerosene': + # Total energy information data. + tmp_energy = sum(value['energy_available'] for key, value in data_dict.items() if key.startswith("tank_") + and key[5:].isdigit() and not value['energy_required_for_mission']) + tmp_volume = sum(value['volume_available'] for key, value in data_dict.items() if key.startswith("tank_") + and key[5:].isdigit() and not value['energy_required_for_mission']) + energy_missing = data_dict['total_tank_parameters'].get('energy_missing', 0) + volume_missing = data_dict['total_tank_parameters'].get('volume_missing', 0) + # Add some more information to data dict. + energy_information_data.insert(2, ( + "Volume provided in necessary tanks", + f"{data_dict['total_tank_parameters']['volume_provided_in_all_tanks'] - tmp_volume:.1f} L")) + energy_information_data.insert(4, ("Volume missing", f"{volume_missing:.1f} L")) + energy_information_data.insert(6, ("Energy provided in necessary tanks", + f"{(data_dict['total_tank_parameters']['energy_provided_in_all_tanks'] - tmp_energy)/1E6:,.1f} MJ")) + energy_information_data.insert(8, ("Energy missing", f"{energy_missing:,.1f} MJ")) + + # energy_information_data = [ + # ("Tank configuration", + # data_dict['tank_configuration']), + # ("Volume required for mission", + # f"{data_dict['total_tank_parameters']['volume_required_for_mission']:.1f} L"), + # ("Volume provided in necessary tanks", + # f"{data_dict['total_tank_parameters']['volume_provided_in_all_tanks'] - tmp_volume:.1f} L"), + # ("Volume provided in all tanks", + # f"{data_dict['total_tank_parameters']['volume_provided_in_all_tanks']:.1f} L"), + # ("Volume missing", + # f"{volume_missing:.1f} L"), + # ("Energy required for mission", + # f"{data_dict['total_tank_parameters']['energy_required_for_mission']/1E6:,.1f} MJ"), + # ("Energy provided in necessary tanks", + # f"{(data_dict['total_tank_parameters']['energy_provided_in_all_tanks'] - tmp_energy)/1E6:,.1f} MJ"), + # ("Energy provided in all tanks", + # f"{data_dict['total_tank_parameters']['energy_provided_in_all_tanks']/1E6:,.1f} MJ"), + # ("Energy missing", + # f"{energy_missing:,.1f} MJ"), + # ] energy_information_title = "General information" energy_information_column_titles = ["Parameter", "Value"] energy_information_alignment = ["l", "r"] # Tank entity data. aircraft_tank_data = [ - (key[-1], value['designator'], value['energy_carrier_name'], f"{value['volume_available']*1000:,.1f}", - f"{round(value['energy_available']/1E6,1):,.1f}", f"{round(value['energy_required_for_mission']/1E6,1):,.1f}") + (key[-1], value['designator'], value['energy_carrier_name'], float(round(value['volume_available'],2)), + f"{round(value['energy_available']/1E6,1):,.1f}", value['energy_required_for_mission']) for key, value in data_dict.items() if key.startswith("tank_") and key[5:].isdigit() ] @@ -114,6 +135,7 @@ def method_html_report(paths_and_names, routing_dict, data_dict, method_specific text(f"All outputs of the program '{tool_name}' were created with version {tool_version}") with tag('div', klass="container"): + # Data on the left side. with tag('div', klass="box data"): with tag('h2'): diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateconicaltank.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateconicaltank.py index 9a502dd9..f9a66596 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateconicaltank.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateconicaltank.py @@ -20,12 +20,20 @@ # Import standard libraries. import numpy as np +from matplotlib import pyplot as plt -# Import own modules. -from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculateparameterendcap import calculate_parameter_end_cap -from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculatethicknesswall import calculate_thickness_wall -from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculateparametertruncatedcone import calculate_parameter_truncated_cone -from src.tube_and_wing.empirical.tank_design_tu_berlin.general.calculatecenterofgravity import calculate_center_of_gravity +# Import own libraries. +from pyunitconversion import constants +from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculateparameterendcap import ( + calculate_parameter_end_cap) +from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculatethicknesswall import ( + calculate_thickness_wall) +from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculateparametertruncatedcone import ( + calculate_parameter_truncated_cone) +from src.tube_and_wing.empirical.tank_design_tu_berlin.general.calculatecenterofgravity import ( + calculate_center_of_gravity) +from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculatezposition import ( + calculate_z_position) def calculate_conical_tank(tank_entity, dict_ac_data, dict_tank_design, runtime_output): @@ -34,11 +42,10 @@ def calculate_conical_tank(tank_entity, dict_ac_data, dict_tank_design, runtime_ [Add some text here.] :param str tank_entity: ... - :param float volume_required: ... :param dict dict_ac_data: Dict containing parameter and values from aircraft exchange and module configuration file :param dict dict_tank_design: Dict containing results from tank design :param logging.Logger runtime_output: Logging object used for capturing log messages in the module - :return float list: x coordinates, external fuselage widths in m, external fuselage heights in m + :return dict dict_tank_design: Dict containing results from tank design """ # Extract data from 'dict_ac_data'. mass_pumps = dict_ac_data['mass_pumps'] @@ -48,43 +55,46 @@ def calculate_conical_tank(tank_entity, dict_ac_data, dict_tank_design, runtime_ material_wall = dict_ac_data['material_wall'] internal_pressure = dict_ac_data['internal_pressure'] density_insulation = dict_ac_data['density_insulation'] + mass_vapor_barrier = dict_ac_data['mass_vapor_barrier'] factor_volume_allowance = dict_ac_data['factor_volume_allowance'] + tensile_stress_allowable = dict_ac_data['tensile_stress_allowable'] thickness_wall_calculation_method = dict_ac_data['thickness_wall_calculation_method'] - mass_vapor_barrier = 24 # TODO: Extract from tank_design_xml? thickness_wall = 0.003 thickness_insulation = 0.20 thickness_wall_and_insulation = thickness_wall + thickness_insulation - tensile_stress_allowable = 172 # in N/mm TODO: Read from aircraft exchange file/lib/... thickness_wall_minimum_aluminum = 0.003 # AD-2000: minimum wall thickness AL-2219 is 3 mmm. - """ Calculation. """ - max_length = round(dict_tank_design[tank_entity]['geometry']['box_dimensions']['maximum_length'],2) - # Note: The diameters are actual sizes. This means that the thicknesses for the tank wall and insulation have - # already been taken into account. + liquid_hydrogen_volumetric_energy_density = constants.LIQUID_HYDROGEN_VOLUMETRIC_ENERGY_DENSITY + liquid_hydrogen_gravimetric_energy_density = constants.LIQUID_HYDROGEN_GRAVIMETRIC_ENERGY_DENSITY + + """Calculation. """ + max_length = round(dict_tank_design[tank_entity]['geometry']['maximum_length'],2) + # Note: The calculated diameters are actual sizes. This means that the thicknesses for the tank wall and insulation + # have already been taken into account following the subsequent assumption. # Assumption: Maximum outer tank diameter equals maximum useable fuselage diameter minus twice the # thickness of insulation and wall. The calculated maximum outer diameter is assumed to be the actual tank # diameter. - max_diameter_front = (dict_tank_design[tank_entity]['geometry']['box_dimensions']['maximum_diameter_front'] + max_diameter_front = (dict_tank_design[tank_entity]['geometry']['maximum_diameter_front'] - 2*thickness_wall_and_insulation) - max_diameter_rear = (dict_tank_design[tank_entity]['geometry']['box_dimensions']['maximum_diameter_rear'] + max_diameter_rear = (dict_tank_design[tank_entity]['geometry']['maximum_diameter_rear'] - 2*thickness_wall_and_insulation) diameters = np.linspace(max_diameter_front, max_diameter_rear,int(max_length*100)) - if dict_tank_design[tank_entity]['tank_position'] == 'tailcone': + if dict_tank_design[tank_entity]['tank_position'] == 'tail_cone': # Special case since the geometry of the tail remains the same across the iteration loops. # Find position of end caps. # The position of the bulkhead is known. This is assumed to be the rearmost position of a tank component, # i.e. the tank must end there. Due to this, the rear end cap must start slightly before this position, so # that it does not extend beyond the position of the bulkhead. The beginning is determined iteratively. - # Starting from the position of the bulkhead, the distance (dist_from_bulkhead) is increased until it - # equals the minimum limit (boundary). The latter depends on the type of end cap. In the case of a - # hemispherical design, the required distance to the end cap equals half the diameter. For a torispherical - # end, it must be at least equal to the determined length of the end. + # Starting from the position of the bulkhead, the distance (dist_to_rear) is increased until it equals the + # minimum limit (boundary_rear). The latter depends on the type of end cap. In the case of a hemispherical + # design, the required distance to the end cap equals half the diameter. For a torispherical end, it must be + # at least equal to the determined length of the end. # Set iteration values. - idx_rear = 1 - idx_front = 0 + idx_rear = 1 # beginning of rear end cap + idx_front = 0 # beginning of front end cap increment = 0.01 boundary_rear = 100 boundary_front = 100 @@ -93,92 +103,236 @@ def calculate_conical_tank(tank_entity, dict_ac_data, dict_tank_design, runtime_ # Determine foremost position of front tank end cap. while boundary_front > distance_to_front: - boundary_front = calculate_parameter_end_cap(diameters[idx_front], type_end_cap, thickness_wall, - thickness_insulation, density_wall, density_insulation)[3] + boundary_front = calculate_parameter_end_cap(runtime_output, diameters[idx_front], type_end_cap, + thickness_wall, thickness_insulation, density_wall, + density_insulation)[3] maximum_tank_diameter_front = diameters[idx_front] idx_front += 1 distance_to_front += increment # Determine rearmost position of rear tank end cap. while boundary_rear > distance_to_rear: - boundary_rear = calculate_parameter_end_cap(diameters[-idx_rear], type_end_cap, thickness_wall, - thickness_insulation, density_wall, density_insulation)[3] + boundary_rear = calculate_parameter_end_cap(runtime_output, diameters[-idx_rear], type_end_cap, + thickness_wall, thickness_insulation, density_wall, + density_insulation)[3] maximum_tank_diameter_rear = diameters[-idx_rear] idx_rear += 1 distance_to_rear += increment else: - print('Normal conical tank.') + runtime_output.print('Normal conical tank.') - """ Calculate wall thickness.""" + """Calculate wall thickness.""" # Assumption: Only dependent on diameter (if pressure etc. constant) -> calculation for biggest diameter. - # TODO: Adjust diameter to actual tank outside diameter? or stay conservative? maximum_diameter = max(maximum_tank_diameter_front, maximum_tank_diameter_rear) - thickness_wall = calculate_thickness_wall(maximum_diameter, internal_pressure, tensile_stress_allowable, - thickness_wall_calculation_method) + thickness_wall = calculate_thickness_wall(runtime_output, maximum_diameter, internal_pressure, + tensile_stress_allowable, thickness_wall_calculation_method) if (material_wall == 'aluminum') and (thickness_wall < thickness_wall_minimum_aluminum): thickness_wall = thickness_wall_minimum_aluminum - """ Calculate volumes, masses, and lengths of tank parts.""" + """Calculate volumes, masses, and lengths of tank parts.""" + fuselage_geometry = dict_tank_design['geometry']['fuselage'] # Calculate maximum length of conical tank part w/o end cap. + # Note that the lengths of the end caps already take into account the tank wall and the insulation thickness. length_conical_tank_excl_end_caps = max_length - distance_to_front - distance_to_rear - # Note that the lengths of the end caps already take into account the tank wall and the insulation thickness. # 1) Front end cap. (volume_tank_front_end_cap, mass_wall_front_end_cap, mass_insulation_front_end_cap, - length_front_end_cap) = calculate_parameter_end_cap(maximum_tank_diameter_front, type_end_cap, thickness_wall, - thickness_insulation, density_wall, density_insulation) - # 2) Conical tank section. - volume_tank_conical_section = 0 + length_front_end_cap) = calculate_parameter_end_cap(runtime_output, maximum_tank_diameter_front, type_end_cap, + thickness_wall, thickness_insulation, density_wall, + density_insulation) + + dict_tank_design[tank_entity]['geometry']['front_end_cap'] = { + 'section_name': 'front_end_cap', + 'shape': 'circular', + 'height': 0.0, + 'width': 0.0, + 'length': length_front_end_cap, + 'global_position': { + 'x': dict_tank_design[tank_entity]['geometry']['idx_front']/100, + 'y': 0.0, + 'z': None + } + } + + # 2) Front tank section (begin of conical section). (volume_tank_conical_section, mass_wall_conical_section, - mass_insulation_conical_section) = calculate_parameter_truncated_cone(maximum_tank_diameter_front, + mass_insulation_conical_section) = calculate_parameter_truncated_cone(runtime_output, maximum_tank_diameter_front, maximum_tank_diameter_rear, length_conical_tank_excl_end_caps, thickness_wall, thickness_insulation, density_wall, density_insulation) - # 3) Rear end cap. + + dict_tank_design[tank_entity]['geometry']['front_tank_section'] = { + 'section_name': 'front_tank_section', + 'shape': 'circular', + 'height': maximum_tank_diameter_front, + 'width': maximum_tank_diameter_front, + 'length': 0.0, + 'global_position': { + 'x': (dict_tank_design[tank_entity]['geometry']['front_end_cap']['global_position']['x'] + + length_front_end_cap), + 'y': 0.0, + 'z': None + } + } + idx_front_tank_section = round( + dict_tank_design[tank_entity]['geometry']['front_tank_section']['global_position']['x']*100) + dict_tank_design[tank_entity]['geometry']['front_tank_section']['global_position']['z'] = calculate_z_position( + fuselage_geometry, idx_front_tank_section) + # Set z-coordinate of front end cap. + dict_tank_design[tank_entity]['geometry']['front_end_cap']['global_position']['z'] = ( + dict_tank_design[tank_entity]['geometry']['front_tank_section']['global_position']['z']) + + # 3) Rear tank section (end of conical section). + dict_tank_design[tank_entity]['geometry']['rear_tank_section'] = { + 'section_name': 'rear_tank_section', + 'shape': 'circular', + 'height': maximum_tank_diameter_rear, + 'width': maximum_tank_diameter_rear, + 'length': 0.0, + 'global_position': { + 'x': (dict_tank_design[tank_entity]['geometry']['front_tank_section']['global_position']['x'] + + length_conical_tank_excl_end_caps), + 'y': 0.0, + 'z': None + } + } + + idx_rear_tank_section = round( + dict_tank_design[tank_entity]['geometry']['rear_tank_section']['global_position']['x']*100) + dict_tank_design[tank_entity]['geometry']['rear_tank_section']['global_position']['z'] = calculate_z_position( + fuselage_geometry, idx_rear_tank_section) + + # 4) Rear end cap. (volume_tank_rear_end_cap, mass_wall_rear_end_cap, mass_insulation_rear_end_cap, - length_rear_end_cap) = calculate_parameter_end_cap(maximum_tank_diameter_rear, type_end_cap, thickness_wall, - thickness_insulation, density_wall, density_insulation) - # 4) Total tank. + length_rear_end_cap) = calculate_parameter_end_cap(runtime_output, maximum_tank_diameter_rear, type_end_cap, + thickness_wall, thickness_insulation, density_wall, + density_insulation) + + dict_tank_design[tank_entity]['geometry']['rear_end_cap'] = { + 'section_name': 'rear_end_cap', + 'shape': 'circular', + 'height': 0.0, + 'width': 0.0, + 'length': -length_rear_end_cap, + 'global_position': { + 'x': (dict_tank_design[tank_entity]['geometry']['rear_tank_section']['global_position']['x'] + + length_rear_end_cap), + 'y': 0.0, + 'z': dict_tank_design[tank_entity]['geometry']['rear_tank_section']['global_position']['z'] + } + } + + + # plot test + # upper_contour = (dict_tank_design['geometry']['fuselage']['interpolated_section_origin_z'] + # + dict_tank_design['geometry']['fuselage']['interpolated_upper_heights']) + # lower_contour = (dict_tank_design['geometry']['fuselage']['interpolated_section_origin_z'] + # - dict_tank_design['geometry']['fuselage']['interpolated_lower_heights']) + # middle_line = dict_tank_design['geometry']['fuselage']['interpolated_section_origin_z'] + # front_end_cap_x = [idx_front_end_cap, idx_front_end_cap, idx_front_end_cap] + # front_end_cap_z = [dict_tank_design[tank_entity]['geometry']['front_end_cap']['position']['z'] + # + maximum_tank_diameter_front*0.5, + # dict_tank_design[tank_entity]['geometry']['front_end_cap']['position']['z'], + # dict_tank_design[tank_entity]['geometry']['front_end_cap']['position']['z'] + # - maximum_tank_diameter_front*0.5] + # front_tank_section_x = [idx_front_tank_section, idx_front_tank_section, idx_front_tank_section] + # front_tank_section_z = [dict_tank_design[tank_entity]['geometry']['front_tank_section']['position']['z'] + # + maximum_tank_diameter_front*0.5, + # dict_tank_design[tank_entity]['geometry']['front_tank_section']['position']['z'], + # dict_tank_design[tank_entity]['geometry']['front_tank_section']['position']['z'] + # - maximum_tank_diameter_front*0.5] + # rear_end_cap_x = [idx_rear_end_cap, idx_rear_end_cap, idx_rear_end_cap] + # rear_end_cap_z = [dict_tank_design[tank_entity]['geometry']['rear_end_cap']['position']['z'] + # + maximum_tank_diameter_front*0.5, + # dict_tank_design[tank_entity]['geometry']['rear_end_cap']['position']['z'], + # dict_tank_design[tank_entity]['geometry']['rear_end_cap']['position']['z'] + # - maximum_tank_diameter_front*0.5] + # plt.plot(upper_contour) + # plt.plot(middle_line) + # plt.plot(lower_contour) + # plt.plot(front_end_cap_x, front_end_cap_z, marker='x', markersize=10, color='red') + # plt.plot(front_tank_section_x, front_tank_section_z, marker='x', markersize=10, color='red') + # plt.plot(rear_end_cap_x, rear_end_cap_z, marker='x', markersize=10, color='red') + # plt.show() + # end plot test + + """Calculate overall values.""" volume_tank_available = volume_tank_front_end_cap + volume_tank_conical_section + volume_tank_rear_end_cap volume_tank_usable = volume_tank_available/(1 + factor_volume_allowance) - energy_hydrogen_available = volume_tank_usable * dict_ac_data['liquid_hydrogen_volumetric_energy_density'] - mass_hydrogen_available = energy_hydrogen_available/dict_ac_data['liquid_hydrogen_gravimetric_energy_density'] - - """ Calculate overall values.""" - # Calculate mass of tank wall. - mass_wall = mass_wall_front_end_cap + mass_wall_conical_section + mass_wall_rear_end_cap - # Calculate structural mass of tank. - mass_tank_structure = mass_wall + mass_pumps + mass_baffle - # Calculate mass of insulation. - mass_insulation = ((mass_insulation_front_end_cap + mass_insulation_conical_section + mass_insulation_rear_end_cap) - + mass_vapor_barrier) - # Calculate overall tank mass. - mass_tank_overall = mass_tank_structure + mass_insulation - # Calculate overall tank length. - length_tank_overall = length_front_end_cap + length_conical_tank_excl_end_caps + length_rear_end_cap + energy_hydrogen_available = volume_tank_usable * liquid_hydrogen_volumetric_energy_density + mass_hydrogen_available = energy_hydrogen_available/liquid_hydrogen_gravimetric_energy_density + + # Tank masses. + dict_tank_design[tank_entity]['masses'] = { + 'masses_tank_structure': { + 'mass_wall': (mass_wall_front_end_cap + mass_wall_conical_section + mass_wall_rear_end_cap), + 'mass_pumps': mass_pumps, + 'mass_baffle': mass_baffle + }, + 'masses_tank_insulation': { + 'mass_insulation': (mass_insulation_front_end_cap + mass_insulation_conical_section + + mass_insulation_rear_end_cap), + 'mass_vapor_barrier': mass_vapor_barrier + }, + 'mass_tank_overall': None + } + mass_tank_structure = sum(dict_tank_design[tank_entity]['masses']['masses_tank_structure'].values()) + mass_tank_insulation = sum(dict_tank_design[tank_entity]['masses']['masses_tank_insulation'].values()) + dict_tank_design[tank_entity]['masses']['mass_tank_overall'] = mass_tank_structure + mass_tank_insulation + # Tank length. + # length_tank_overall = length_front_end_cap + length_conical_tank_excl_end_caps + length_rear_end_cap + dict_tank_design[tank_entity]['geometry']['length_tank_overall'] = float(length_front_end_cap + + length_conical_tank_excl_end_caps + + length_rear_end_cap) # Calculate gravimetric efficiency tank. - gravimetric_efficiency = mass_hydrogen_available/(mass_hydrogen_available + mass_tank_overall) + gravimetric_efficiency = mass_hydrogen_available/(mass_hydrogen_available + + dict_tank_design[tank_entity]['masses']['mass_tank_overall']) + dict_tank_design[tank_entity]['gravimetric_efficiency'] = float(gravimetric_efficiency) + + # Inertia (set inertia to zero). + dict_tank_design[tank_entity]['geometry']['total'] = {} + dict_tank_design[tank_entity]['geometry']['total']['inertia'] = {} + inertia_list = ['j_xx', 'j_yy', 'j_zz', 'j_xy', 'j_xz', 'j_yx', 'j_yz', 'j_zx', 'j_zy'] + for inertia in inertia_list: + dict_tank_design[tank_entity]['geometry']['total']['inertia'][inertia] = 0.0 - """ Calculate center of gravity.""" - (x_coordinate_center_of_gravity, y_coordinate_center_of_gravity, z_coordinate_center_of_gravity - ) = calculate_center_of_gravity(dict_tank_design[tank_entity]['tank_shape'], - dict_tank_design[tank_entity]['geometry']['box_dimensions']['maximum_diameter_front'], - dict_tank_design[tank_entity]['geometry']['box_dimensions']['maximum_diameter_rear'], + # Calculate center of gravity. + dict_tank_design[tank_entity]['geometry']['total']['center_of_gravity'] = calculate_center_of_gravity( + runtime_output, dict_tank_design[tank_entity]['tank_shape'], + dict_tank_design[tank_entity]['geometry']['maximum_diameter_front'], + dict_tank_design[tank_entity]['geometry']['maximum_diameter_rear'], length_conical_tank_excl_end_caps) - # Prepare data output. + # Calculate position of tank entity (foremost point of tank). + dict_tank_design[tank_entity]['geometry']['total']['global_position'] = { + 'x': dict_tank_design[tank_entity]['geometry']['front_end_cap']['global_position']['x'], + 'y': dict_tank_design[tank_entity]['geometry']['front_end_cap']['global_position']['y'], + 'z': dict_tank_design[tank_entity]['geometry']['front_end_cap']['global_position']['z'], + } + + # Direction. + dict_tank_design[tank_entity]['geometry']['total']['direction'] = { + 'x': 1, + 'y': 0, + 'z': 0 + } + + # Centroid. + dict_tank_design[tank_entity]['geometry']['total']['centroid'] = { + 'x': dict_tank_design[tank_entity]['geometry']['total']['center_of_gravity']['x'], + 'y': dict_tank_design[tank_entity]['geometry']['total']['center_of_gravity']['y'], + 'z': dict_tank_design[tank_entity]['geometry']['total']['center_of_gravity']['z'] + } + + # Prepare further data output. dict_tank_design[tank_entity]['energy_available'] = float(energy_hydrogen_available) - dict_tank_design[tank_entity]['masses'] = {} - dict_tank_design[tank_entity]['masses']['mass_insulation'] = float(mass_insulation) - dict_tank_design[tank_entity]['masses']['mass_tank_overall'] = float(mass_tank_overall) - dict_tank_design[tank_entity]['masses']['mass_tank_structure'] = float(mass_tank_structure) - dict_tank_design[tank_entity]['geometry']['length_tank_overall'] = float(length_tank_overall) - dict_tank_design[tank_entity]['geometry']['center_of_gravity'] = {} - dict_tank_design[tank_entity]['geometry']['center_of_gravity']['x'] = float(x_coordinate_center_of_gravity) - dict_tank_design[tank_entity]['geometry']['center_of_gravity']['y'] = float(y_coordinate_center_of_gravity) - dict_tank_design[tank_entity]['geometry']['center_of_gravity']['z'] = float(z_coordinate_center_of_gravity) - dict_tank_design[tank_entity]['gravimetric_efficiency'] = float(gravimetric_efficiency) + dict_tank_design[tank_entity]['volume_available'] = float(volume_tank_usable) + dict_tank_design[tank_entity]['energy_required_for_mission'] = True + dict_tank_design[tank_entity]['volume_required_for_mission'] = True + + # Debug print. + runtime_output.debug('Debug: The "calculate_conical_tank" function was successfully executed.') return dict_tank_design diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculatecylindricaltank.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculatecylindricaltank.py index d2b4c6fe..785f5d75 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculatecylindricaltank.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculatecylindricaltank.py @@ -22,10 +22,17 @@ import numpy as np # Import own modules. -from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculateparameterendcap import calculate_parameter_end_cap -from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculatethicknesswall import calculate_thickness_wall -from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculateparametercylinder import calculate_parameter_cylinder -from src.tube_and_wing.empirical.tank_design_tu_berlin.general.calculatecenterofgravity import calculate_center_of_gravity +from pyunitconversion import constants +from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculateparameterendcap import ( + calculate_parameter_end_cap) +from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculatethicknesswall import ( + calculate_thickness_wall) +from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculateparametercylinder import ( + calculate_parameter_cylinder) +from src.tube_and_wing.empirical.tank_design_tu_berlin.general.calculatecenterofgravity import ( + calculate_center_of_gravity) +from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculatezposition import ( + calculate_z_position) def calculate_cylindrical_tank(tank_entity, dict_ac_data, dict_tank_design, runtime_output): @@ -46,53 +53,58 @@ def calculate_cylindrical_tank(tank_entity, dict_ac_data, dict_tank_design, runt material_wall = dict_ac_data['material_wall'] internal_pressure = dict_ac_data['internal_pressure'] density_insulation = dict_ac_data['density_insulation'] + mass_vapor_barrier = dict_ac_data['mass_vapor_barrier'] factor_volume_allowance = dict_ac_data['factor_volume_allowance'] + tensile_stress_allowable = dict_ac_data['tensile_stress_allowable'] thickness_wall_calculation_method = dict_ac_data['thickness_wall_calculation_method'] energy_required = dict_tank_design[tank_entity]['energy_required'] - mass_vapor_barrier = 24 # TODO: Extract from tank_design_xml? thickness_wall = 0.003 thickness_insulation = 0.20 thickness_wall_and_insulation = thickness_wall + thickness_insulation - tensile_stress_allowable = 172 # in N/mm TODO: Read from aircraft exchange file/lib/... thickness_wall_minimum_aluminum = 0.003 # AD-2000: minimum wall thickness AL-2219 is 3 mmm. - volume_required = energy_required/dict_ac_data['liquid_hydrogen_volumetric_energy_density'] + liquid_hydrogen_volumetric_energy_density = constants.LIQUID_HYDROGEN_VOLUMETRIC_ENERGY_DENSITY + liquid_hydrogen_gravimetric_energy_density = constants.LIQUID_HYDROGEN_GRAVIMETRIC_ENERGY_DENSITY - """ Calculation. """ + volume_required = energy_required/liquid_hydrogen_volumetric_energy_density + + """Calculation. """ # Note: The diameters are actual sizes. This means that the thicknesses for the tank wall and insulation have # already been taken into account. # Assumption: Maximum outer tank diameter equals maximum useable fuselage diameter minus twice the # thickness of insulation and wall. The calculated maximum outer diameter is assumed to be the actual tank # diameter. - maximum_tank_diameter = (dict_tank_design[tank_entity]['geometry']['box_dimensions']['maximum_diameter_front'] + maximum_tank_diameter = (dict_tank_design[tank_entity]['geometry']['maximum_diameter_front'] - 2*thickness_wall_and_insulation) - """ Find position of end caps.""" + """Find position of end caps.""" # Set iteration values. increment = 0.01 boundary = 100 distance = 0 while boundary > distance: - boundary = calculate_parameter_end_cap(maximum_tank_diameter, type_end_cap, thickness_wall, - thickness_insulation, density_wall, density_insulation)[3] + boundary = calculate_parameter_end_cap(runtime_output, maximum_tank_diameter, type_end_cap, thickness_wall, + thickness_insulation, density_wall, density_insulation)[3] distance += increment - """ Calculate wall thickness.""" + """Calculate wall thickness.""" maximum_diameter = maximum_tank_diameter - thickness_wall = calculate_thickness_wall(maximum_diameter, internal_pressure, tensile_stress_allowable, - thickness_wall_calculation_method) + thickness_wall = calculate_thickness_wall(runtime_output, maximum_diameter, internal_pressure, + tensile_stress_allowable, thickness_wall_calculation_method) if (material_wall == 'aluminum') and (thickness_wall < thickness_wall_minimum_aluminum): thickness_wall = thickness_wall_minimum_aluminum - """ Calculate volumes, masses, and lengths of tank parts.""" + """Calculate volumes, masses, and lengths of tank parts.""" + fuselage_geometry = dict_tank_design['geometry']['fuselage'] # Note that the lengths of the end caps already take into account the tank wall and the insulation thickness. # 1) Front end cap (equals rear end cap). (volume_tank_end_cap, mass_wall_end_cap, mass_insulation_end_cap, - length_end_cap) = calculate_parameter_end_cap(maximum_tank_diameter, type_end_cap, thickness_wall, - thickness_insulation, density_wall, density_insulation) - # 2) Calculate how much volume the cylindrical part has to hold. + length_end_cap) = calculate_parameter_end_cap(runtime_output, maximum_tank_diameter, type_end_cap, thickness_wall, + thickness_insulation, density_wall, density_insulation) + + # 2) Calculate how much volume is available in both end caps. volume_end_caps_available = 2*volume_tank_end_cap # energy_end_caps_available = volume_end_caps_available * dict_ac_data['liquid_hydrogen_volumetric_energy_density'] # energy_remaining = energy_required - energy_end_caps_available @@ -105,48 +117,152 @@ def calculate_cylindrical_tank(tank_entity, dict_ac_data, dict_tank_design, runt while volume_cylindrical_section_usable < volume_remaining: (volume_cylindrical_section, mass_wall_cylindrical_section, mass_insulation_cylindrical_section) = calculate_parameter_cylinder( - maximum_diameter, length_cylindrical_section, thickness_wall, thickness_insulation, + runtime_output, maximum_diameter, length_cylindrical_section, thickness_wall, thickness_insulation, density_wall, density_insulation) volume_cylindrical_section_usable = volume_cylindrical_section/(1 + factor_volume_allowance) length_cylindrical_section += 0.01 # 4) Total tank. volume_tank_available = volume_end_caps_available + volume_cylindrical_section_usable - energy_hydrogen_available = volume_tank_available * dict_ac_data['liquid_hydrogen_volumetric_energy_density'] - mass_hydrogen_available = energy_hydrogen_available/dict_ac_data['liquid_hydrogen_gravimetric_energy_density'] + energy_hydrogen_available = volume_tank_available * liquid_hydrogen_volumetric_energy_density + mass_hydrogen_available = energy_hydrogen_available/liquid_hydrogen_gravimetric_energy_density + + """Set dictionary values.""" + dict_tank_design[tank_entity]['geometry']['rear_end_cap'] = { + 'section_name': 'rear_end_cap', + 'shape': 'circular', + 'height': 0.0, + 'width': 0.0, + 'length': -length_end_cap, + 'global_position': { + 'x': dict_tank_design[tank_entity]['geometry']['idx_rear']/100, + 'y': 0.0, + 'z': None + } + } + + dict_tank_design[tank_entity]['geometry']['rear_tank_section'] = { + 'section_name': 'rear_tank_section', + 'shape': 'circular', + 'height': maximum_tank_diameter, + 'width': maximum_tank_diameter, + 'length': 0.0, + 'global_position': { + 'x': dict_tank_design[tank_entity]['geometry']['rear_end_cap']['global_position']['x'] - length_end_cap, + 'y': 0.0, + 'z': None + } + } + idx_rear_tank_section = round( + dict_tank_design[tank_entity]['geometry']['rear_tank_section']['global_position']['x']*100) + dict_tank_design[tank_entity]['geometry']['rear_tank_section']['global_position']['z'] = calculate_z_position( + fuselage_geometry, idx_rear_tank_section) + # Set z-coordinate of rear end cap. + dict_tank_design[tank_entity]['geometry']['rear_end_cap']['global_position']['z'] = ( + dict_tank_design[tank_entity]['geometry']['rear_tank_section']['global_position']['z']) + + dict_tank_design[tank_entity]['geometry']['front_tank_section'] = { + 'section_name': 'front_tank_section', + 'shape': 'circular', + 'height': maximum_tank_diameter, + 'width': maximum_tank_diameter, + 'length': 0.0, + 'global_position': { + 'x': (dict_tank_design[tank_entity]['geometry']['rear_tank_section']['global_position']['x'] + - length_cylindrical_section), + 'y': 0.0, + # Assumption: Cylindrical tank with base and top faces aligned vertically (right cylinder). + 'z': dict_tank_design[tank_entity]['geometry']['rear_tank_section']['global_position']['z'] + } + } + + dict_tank_design[tank_entity]['geometry']['front_end_cap'] = { + 'section_name': 'front_end_cap', + 'shape': 'circular', + 'height': 0.0, + 'width': 0.0, + 'length': length_end_cap, + 'global_position': { + 'x': (dict_tank_design[tank_entity]['geometry']['front_tank_section']['global_position']['x'] + - length_end_cap), + 'y': 0.0, + 'z': dict_tank_design[tank_entity]['geometry']['front_tank_section']['global_position']['z'] + } + } - """ Calculate overall values.""" + """Calculate overall values.""" # Calculate mass of tank wall. mass_wall = 2*mass_wall_end_cap + mass_wall_cylindrical_section # Calculate structural mass of tank. mass_tank_structure = mass_wall + mass_pumps + mass_baffle # Calculate mass of insulation. - mass_insulation = mass_insulation_end_cap + mass_insulation_cylindrical_section + mass_vapor_barrier + mass_insulation = mass_insulation_end_cap + mass_insulation_cylindrical_section + mass_tank_insulation = mass_insulation + mass_vapor_barrier # Calculate overall tank mass. - mass_tank_overall = mass_tank_structure + mass_insulation + mass_tank_overall = mass_tank_structure + mass_tank_insulation # Calculate overall tank length. length_tank_overall = 2*length_end_cap + length_cylindrical_section # Calculate gravimetric efficiency tank. gravimetric_efficiency = mass_hydrogen_available/(mass_hydrogen_available + mass_tank_overall) + # + dict_tank_design[tank_entity]['masses'] = { + 'masses_tank_structure': { + 'mass_wall': mass_wall, + 'mass_pumps': mass_pumps, + 'mass_baffle': mass_baffle + }, + 'masses_tank_insulation': { + 'mass_insulation': mass_insulation, + 'mass_vapor_barrier': mass_vapor_barrier + }, + 'mass_tank_overall': None + } + dict_tank_design[tank_entity]['masses']['mass_tank_overall'] = mass_tank_structure + mass_tank_insulation - """ Calculate center of gravity.""" - (x_coordinate_center_of_gravity, y_coordinate_center_of_gravity, z_coordinate_center_of_gravity - ) = calculate_center_of_gravity(dict_tank_design[tank_entity]['tank_shape'], - dict_tank_design[tank_entity]['geometry']['box_dimensions']['maximum_diameter_front'], - dict_tank_design[tank_entity]['geometry']['box_dimensions']['maximum_diameter_front'], + # Inertia (set inertia to zero). + dict_tank_design[tank_entity]['geometry']['total'] = {} + dict_tank_design[tank_entity]['geometry']['total']['inertia'] = {} + inertia_list = ['j_xx', 'j_yy', 'j_zz', 'j_xy', 'j_xz', 'j_yx', 'j_yz', 'j_zx', 'j_zy'] + for inertia in inertia_list: + dict_tank_design[tank_entity]['geometry']['total']['inertia'][inertia] = 0.0 + + # Calculate center of gravity. + dict_tank_design[tank_entity]['geometry']['total']['center_of_gravity'] = calculate_center_of_gravity( + runtime_output, dict_tank_design[tank_entity]['tank_shape'], + dict_tank_design[tank_entity]['geometry']['maximum_diameter_front'], + dict_tank_design[tank_entity]['geometry']['maximum_diameter_rear'], length_cylindrical_section) + # Calculate position of tank entity (foremost point of tank). + dict_tank_design[tank_entity]['geometry']['total']['global_position'] = { + 'x': dict_tank_design[tank_entity]['geometry']['front_end_cap']['global_position']['x'], + 'y': dict_tank_design[tank_entity]['geometry']['front_end_cap']['global_position']['y'], + 'z': dict_tank_design[tank_entity]['geometry']['front_end_cap']['global_position']['z'], + } + + # Direction. + dict_tank_design[tank_entity]['geometry']['total']['direction'] = { + 'x': 1, + 'y': 0, + 'z': 0 + } + + # Centroid. + dict_tank_design[tank_entity]['geometry']['total']['centroid'] = { + 'x': dict_tank_design[tank_entity]['geometry']['total']['center_of_gravity']['x'], + 'y': dict_tank_design[tank_entity]['geometry']['total']['center_of_gravity']['y'], + 'z': dict_tank_design[tank_entity]['geometry']['total']['center_of_gravity']['z'] + } + # Prepare data output. dict_tank_design[tank_entity]['energy_available'] = float(energy_hydrogen_available) - dict_tank_design[tank_entity]['masses'] = {} - dict_tank_design[tank_entity]['masses']['mass_insulation'] = float(mass_insulation) - dict_tank_design[tank_entity]['masses']['mass_tank_overall'] = float(mass_tank_overall) - dict_tank_design[tank_entity]['masses']['mass_tank_structure'] = float(mass_tank_structure) + dict_tank_design[tank_entity]['volume_available'] = float(volume_tank_available) dict_tank_design[tank_entity]['geometry']['length_tank_overall'] = float(length_tank_overall) - dict_tank_design[tank_entity]['geometry']['center_of_gravity'] = {} - dict_tank_design[tank_entity]['geometry']['center_of_gravity']['x'] = float(x_coordinate_center_of_gravity) - dict_tank_design[tank_entity]['geometry']['center_of_gravity']['y'] = float(y_coordinate_center_of_gravity) - dict_tank_design[tank_entity]['geometry']['center_of_gravity']['z'] = float(z_coordinate_center_of_gravity) dict_tank_design[tank_entity]['gravimetric_efficiency'] = float(gravimetric_efficiency) + dict_tank_design[tank_entity]['energy_required_for_mission'] = True + dict_tank_design[tank_entity]['volume_required_for_mission'] = True + + # Debug print. + runtime_output.debug('Debug: The "calculate_cylindrical_tank" function was successfully executed.') return dict_tank_design diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateliquidhydrogentanks.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateliquidhydrogentanks.py new file mode 100644 index 00000000..e921f3e9 --- /dev/null +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateliquidhydrogentanks.py @@ -0,0 +1,311 @@ +# UNICADO - UNIversity Conceptual Aircraft Design and Optimization +# +# Copyright (C) 2025 UNICADO consortium +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. +# +# Description: +# This file is part of UNICADO. + +""" Module containing functions for kerosene tank calculation. """ +# Import standard libraries. + +# Import own libraries. +from pyunitconversion import constants +from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculateconicaltank import calculate_conical_tank +from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculatecylindricaltank import calculate_cylindrical_tank + + +def calculate_liquid_hydrogen_tanks(dict_ac_data, dict_tank_design, runtime_output): + """Calculate liquid hydrogen 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. + number_of_tanks = dict_tank_design['number_of_tanks'] + liquid_hydrogen_volumetric_energy_density = constants.LIQUID_HYDROGEN_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 (13 liter LH2 per PAX per 100 km).') + energy_demand = 13 # in L per passenger per 100 km + mission_fuel_amount = dict_ac_data['number_of_passengers']*(dict_ac_data['range']/1000)*energy_demand/100 + mission_energy_amount = (mission_fuel_amount/1000*liquid_hydrogen_volumetric_energy_density) + else: + mission_energy_amount = dict_ac_data['mission_energy_amount']['mission_energy_amount_ID0'] + + """ Calculate tank volume. """ + # Extract all tank entities from 'dict_tank_design'. + tank_entity_list = [key for key in dict_tank_design if key.startswith('tank_') and key[5:].isdigit()] + # Check, if there is a tank in the tail cone of the aircraft. + tail_cone_tank = next((key for key in tank_entity_list if dict_tank_design[key]['tank_position'] == 'tail_cone'), + None) + rear_tank = next((key for key in tank_entity_list if dict_tank_design[key]['tank_position'] == 'rear'), + None) + front_tank = next((key for key in tank_entity_list if dict_tank_design[key]['tank_position'] == 'front'), + None) + # Sort list such that the key of the tank with the tail cone position is at the first list position. This ensures + # that this tank is calculated first. + if tail_cone_tank: + tank_entity_list.remove(tail_cone_tank) + tank_entity_list.insert(0, tail_cone_tank) + + # Iterate through tank entities (first entity is tail cone tank) and call functions from liquid hydrogen folder. + tank_idx = 0 + energy_available = 0 + mass_tank_overall = 0 + length_tank_overall = 0 + tail_cone_tank_energy = 0 + mass_structure_overall = 0 + mass_insulation_overall = 0 + + for tank_entity in tank_entity_list: + # Call functions according to tank shape. + match dict_tank_design[tank_entity]['tank_shape']: + case 'conical': + dict_tank_design = calculate_conical_tank(tank_entity, dict_ac_data, dict_tank_design, runtime_output) + case 'cylindrical': + dict_tank_design = calculate_cylindrical_tank(tank_entity, dict_ac_data, dict_tank_design, + runtime_output) + + energy_available += dict_tank_design[tank_entity]['energy_available'] + energy_remaining = mission_energy_amount - energy_available + mass_tank_overall += dict_tank_design[tank_entity]['masses']['mass_tank_overall'] + mass_insulation_overall += sum(dict_tank_design[tank_entity]['masses']['masses_tank_insulation'].values()) + mass_structure_overall += sum(dict_tank_design[tank_entity]['masses']['masses_tank_structure'].values()) + length_tank_overall += dict_tank_design[tank_entity]['geometry']['length_tank_overall'] + + # Set missing entries in 'dict_tank_design'. + if tank_entity == tail_cone_tank: + tail_cone_tank_energy = dict_tank_design[tank_entity]['energy_available'] + dict_tank_design[tank_entity]['energy_required'] = dict_tank_design[tank_entity]['energy_available'] + # Set required energy of remaining tanks. + if dict_tank_design['tank_configuration'] == 'fuselage_rear_tanks': + # Since this tank configuration contains only one tank in addition to the tail cone tank, the required + # energy of this tank equals the 'energy_remaining'. + dict_tank_design[tank_entity_list[-1]]['energy_required'] = energy_remaining + elif dict_tank_design['tank_configuration'] == 'fuselage_front_and_rear_tanks': + for tmp_tank in tank_entity_list[1:]: + if (dict_tank_design[tmp_tank]['tank_location'] == 'fuselage' + and dict_tank_design[tmp_tank]['tank_position'] == 'rear'): + # Tank at rear fuselage. + # Assumption: energy_share of rear tank contains energy of tail cone tank. + # energy_required = (mission_energy*energy_share) - energy_tail_cone_tank + dict_tank_design[tmp_tank]['energy_required'] = ( + mission_energy_amount*dict_tank_design[tmp_tank]['energy_share'] + - tail_cone_tank_energy) + elif (dict_tank_design[tmp_tank]['tank_location'] == 'fuselage' + and dict_tank_design[tmp_tank]['tank_position'] == 'front'): + # Tank at front fuselage. + dict_tank_design[tmp_tank]['energy_required'] = ( + mission_energy_amount*dict_tank_design[tmp_tank]['energy_share']) + + # Print. + tank_location = dict_tank_design[tank_entity]['tank_location'] + tank_position = dict_tank_design[tank_entity]['tank_position'] + if '_' in tank_position: + print_position = (tank_location.capitalize() + ' ' + tank_position.split('_')[0] + ' ' + + tank_position.split('_')[1] + ' ') + else: + print_position = tank_location.capitalize() + ' ' + tank_position + ' ' + runtime_output.print( + print_position + 'tank (' + tank_entity + ') calculated: \n' + + ' ' + + '- Volume (energy) available: ' + + f"{dict_tank_design[tank_entity]['volume_available']:,.2f}" + ' L (' + + f"{dict_tank_design[tank_entity]['energy_available']/1e6:,.2f}" + + ' MJ) \n' + + ' ' + + '- Tank length: ' + + f"{dict_tank_design[tank_entity]['geometry']['length_tank_overall']:,.2f}" + ' m \n' + + ' ' + + '- Tank mass (insulation, structure): ' + + f"{dict_tank_design[tank_entity]['masses']['mass_tank_overall']:,.2f}" + ' kg (' + + f"{sum(dict_tank_design[tank_entity]['masses']['masses_tank_insulation'].values()):,.2f}" + ' kg, ' + + f"{sum(dict_tank_design[tank_entity]['masses']['masses_tank_structure'].values()):,.2f}" + ' kg) \n' + + ' ' + + '- Gravimetric efficiency: ' + + f"{dict_tank_design[tank_entity]['gravimetric_efficiency']:,.2f}" + ) + + tank_idx += 1 + + """Set total tank parameters.""" + # Calculate coordinates w.r.t. tank reference point (foremost middle point of foremost tank) and local reference + # points (foremost middle points of single tank entities). + dict_tank_design['total_tank_parameters'] = {} + inertia_list = ['j_xx', 'j_yy', 'j_zz', 'j_xy', 'j_xz', 'j_yx', 'j_yz', 'j_zx', 'j_zy'] + tank_section_list = ['front_end_cap', 'front_tank_section', 'rear_tank_section', 'rear_end_cap'] + + if dict_tank_design['tank_configuration'] == 'fuselage_rear_tanks': + dict_tank_design['total_tank_parameters']['position'] = { + 'x': dict_tank_design[rear_tank]['geometry']['total']['global_position']['x'], + 'y': dict_tank_design[rear_tank]['geometry']['total']['global_position']['y'], + 'z': dict_tank_design[rear_tank]['geometry']['total']['global_position']['z'] + } + # Calculate positions of tank entities. + tank_reference_point = dict_tank_design['total_tank_parameters']['position'] + tank_section_list = ['front_end_cap', 'front_tank_section', 'rear_tank_section', 'rear_end_cap'] + for tank_entity in [rear_tank, tail_cone_tank]: + if tank_entity == rear_tank: + # Local reference point equals tank reference point. + local_reference_point = {key: 0.0 for key in ['x', 'y', 'z']} + else: + # Local reference point must be calculated. + local_reference_point = { + 'x': (dict_tank_design[tail_cone_tank]['geometry']['total']['global_position']['x'] + - tank_reference_point['x']), + 'y': (dict_tank_design[tail_cone_tank]['geometry']['total']['global_position']['y'] + - tank_reference_point['y']), + 'z': (dict_tank_design[tail_cone_tank]['geometry']['total']['global_position']['z'] + - tank_reference_point['z']) + } + dict_tank_design[tank_entity]['geometry']['total']['position'] = local_reference_point + + # Iterate through tank sections. + reference_point = {key: local_reference_point[key] + tank_reference_point[key] + for key in local_reference_point.keys() & tank_reference_point.keys()} + for tank_section in tank_section_list: + dict_tank_design[tank_entity]['geometry'][tank_section]['position'] = { + 'x': (dict_tank_design[tank_entity]['geometry'][tank_section]['global_position']['x'] + - reference_point['x']), + 'y': 0.0, + 'z': dict_tank_design[tank_entity]['geometry'][tank_section]['global_position']['z'] + - reference_point['z'] + } + + # Set mass - just for correct output. + dict_tank_design[tank_entity]['geometry']['total']['mass'] = ( + dict_tank_design[tank_entity]['masses']['mass_tank_overall']) + + # Inertia and mass. + dict_tank_design['total_tank_parameters']['inertia'] = {inertia: 0.0 for inertia in inertia_list} + dict_tank_design['total_tank_parameters']['mass'] = sum( + dict_tank_design[f'tank_{i}']['masses']['mass_tank_overall'] for i in range(number_of_tanks)) + + # Center of gravity (calculate weighted sums). + # TODO: Check - especially z coordinates. + cog_x = cog_y = cog_z = mass_total = 0 + for key, value in ((k, v) for k, v in dict_tank_design.items() if k.startswith('tank_') and isinstance(v, dict)): + # Note: CoG is measured from lower left corner of front tank section, so some adaptions must be made. + x = (value['geometry']['total']['center_of_gravity']['x'] + value['geometry']['front_end_cap']['length'] + + value['geometry']['total']['global_position']['x']) + y = value['geometry']['total']['center_of_gravity']['y'] + if key == rear_tank and dict_tank_design[key]['tank_shape'] == 'cylindrical': + # z position equals z position of front tank section. + z = value['geometry']['front_tank_section']['global_position']['z'] + z_local = 0.0 + else: + z = (value['geometry']['total']['center_of_gravity']['z'] + + (value['geometry']['front_tank_section']['global_position']['z'] + - value['geometry']['front_tank_section']['height']*0.5)) + z_local = (value['geometry']['total']['center_of_gravity']['z'] + - value['geometry']['front_tank_section']['height']*0.5) + cog_x += value['masses']['mass_tank_overall'] * x + cog_y += value['masses']['mass_tank_overall'] * y + cog_z += value['masses']['mass_tank_overall'] * z + mass_total += value['masses']['mass_tank_overall'] + # Set center of gravity in dictionary to corrected center of gravity w.r.t. local reference point. + dict_tank_design[key]['geometry']['total']['center_of_gravity'] = { + 'x': value['geometry']['total']['center_of_gravity']['x'] + value['geometry']['front_end_cap']['length'], + 'y': y, + 'z': z_local} + value['geometry']['total']['centroid'] = value['geometry']['total']['center_of_gravity'] + + dict_tank_design['total_tank_parameters']['center_of_gravity'] = { + 'x': cog_x / mass_total, + 'y': cog_y / mass_total, + 'z': cog_z / mass_total + } + + # Direction. + dict_tank_design['total_tank_parameters']['direction'] = { + 'x': 1, + 'y': 0, + 'z': 0 + } + + # Since all tanks are dimensioned based on the required amount of energy, the total mission energy amount should be + # covered after the tank calculation. However, a final energy check is implemented to ensure that. + if energy_available > mission_energy_amount: + volume_available = energy_available/liquid_hydrogen_volumetric_energy_density + runtime_output.print('Energy check: Energy demand covered.') + runtime_output.print('Tank design successful.') + if energy_available < mission_energy_amount: + energy_missing = mission_energy_amount - energy_available + volume_missing = energy_missing/liquid_hydrogen_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*1000,2)) + + 'L) missing.') + + # Add extra space between tanks. + if dict_tank_design['tank_configuration'] == 'fuselage_rear_tanks': + space_between_tanks = (dict_tank_design['tank_0']['geometry']['total']['position']['x'] + - dict_tank_design[rear_tank]['geometry']['rear_end_cap']['position']['x']) + length_rear_tanks_overall = (dict_tank_design[rear_tank]['geometry']['length_tank_overall'] + + dict_tank_design[tail_cone_tank]['geometry']['length_tank_overall'] + + space_between_tanks) + length_front_tank_overall = 0 + length_print = ('- Overall length of tanks in the rear of the fuselage: ' + + f"{length_rear_tanks_overall:,.2f}" + ' m. \n') + elif dict_tank_design['tank_configuration'] == 'fuselage_front_and_rear_tanks': + space_between_tanks = (dict_tank_design['tank_0']['geometry']['total']['position']['x'] + - dict_tank_design[rear_tank]['geometry']['rear_end_cap']['position']['x']) + length_rear_tanks_overall = (dict_tank_design[rear_tank]['geometry']['length_tank_overall'] + + dict_tank_design[tail_cone_tank]['geometry']['length_tank_overall'] + + space_between_tanks) + length_front_tank_overall = dict_tank_design[front_tank]['geometry']['length_tank_overall'] + length_print = ('- Overall length of tanks in the front of the fuselage: ' + + f"{length_front_tank_overall:,.2f}" + ' m. \n' + + ' ' + + '- Overall length of tanks in the rear of the fuselage: ' + + f"{length_rear_tanks_overall:,.2f}" + ' m. \n') + + # Results print. + runtime_output.print('All tanks calculated: \n' + + ' ' + + '- Volume (energy) available: ' + + f"{volume_available:,.2f}" + ' L (' + + f"{energy_available/1e6:,.2f}" + + ' MJ) \n' + + ' ' + + length_print + + ' ' + + '- Tank mass (insulation, structure): ' + + f"{mass_tank_overall:,.2f}" + ' kg (' + + f"{mass_insulation_overall:,.2f}" + ' kg, ' + + f"{mass_structure_overall:,.2f}" + ' kg)') + + # Prepare data output. + dict_tank_design['total_tank_parameters']['length_rear_tanks_overall'] = length_rear_tanks_overall + dict_tank_design['total_tank_parameters']['length_front_tank_overall'] = length_front_tank_overall + dict_tank_design['total_tank_parameters']['additional_fuselage_length'] = (length_front_tank_overall + + length_rear_tanks_overall) + dict_tank_design['total_tank_parameters']['energy_provided_in_all_tanks'] = energy_available + dict_tank_design['total_tank_parameters']['volume_provided_in_all_tanks'] = volume_available + dict_tank_design['total_tank_parameters']['energy_required_for_mission'] = mission_energy_amount + dict_tank_design['total_tank_parameters']['volume_required_for_mission'] = (mission_energy_amount + /liquid_hydrogen_volumetric_energy_density) + + # Debug print. + runtime_output.debug('Debug: The "calculate_liquid_hydrogen_tanks" function was successfully executed.') + + return dict_tank_design diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateparametercylinder.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateparametercylinder.py index ccb72b78..1f687d4f 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateparametercylinder.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateparametercylinder.py @@ -22,7 +22,7 @@ from math import pi as PI -def calculate_parameter_cylinder(diameter, length, thickness_wall=0.003, thickness_insulation=0.20, +def calculate_parameter_cylinder(runtime_output, diameter, length, thickness_wall=0.003, thickness_insulation=0.20, density_material_wall=2850, density_material_insulation=32): """Calculate parameters for cylinder. @@ -34,6 +34,7 @@ def calculate_parameter_cylinder(diameter, length, thickness_wall=0.003, thickne and insulation are determined based on a hollow cylinder. The mass can then be determined using the corresponding material density. + :param logging.Logger runtime_output: Logging object used for capturing log messages in the module :param float diameter: Diameter of the cylindrical tank section in m :param float length: Length of the cylindrical tank section in m :param float thickness_wall: Wall thickness in m, defaults to 0.003 @@ -55,4 +56,7 @@ def calculate_parameter_cylinder(diameter, length, thickness_wall=0.003, thickne mass_wall = volume_wall*density_material_wall mass_insulation = volume_insulation*density_material_insulation + # Debug print. + runtime_output.debug('Debug: The "calculate_parameter_cylinder" function was successfully executed.') + return volume_tank, mass_wall, mass_insulation diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateparameterendcap.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateparameterendcap.py index 3ceb76c9..d4ea1242 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateparameterendcap.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateparameterendcap.py @@ -23,7 +23,7 @@ import math from numpy import pi as PI -def calculate_parameter_end_cap(diameter, type_end_cap, thickness_wall=0.003, thickness_insulation=0.20, +def calculate_parameter_end_cap(runtime_output, diameter, type_end_cap, thickness_wall=0.003, thickness_insulation=0.20, density_material_wall=2850, density_material_insulation=32): """Calculate parameters of tank end cap. @@ -46,6 +46,7 @@ def calculate_parameter_end_cap(diameter, type_end_cap, thickness_wall=0.003, th Note: Default values are based on aluminum (Al-2219) as the tank wall material and polyurethane foam as insulation. + :param logging.Logger runtime_output: Logging object used for capturing log messages in the module :param float diameter: Tank diameter in m :param str type: Type of end cap ('hemispherical' or 'torispherical') :param float thickness_wall: Wall thickness in m, defaults to 0.003 @@ -107,4 +108,7 @@ def calculate_parameter_end_cap(diameter, type_end_cap, thickness_wall=0.003, th case 'ellipsoidal': print('Ellipsoidal end cap not yet implemented.') + # Debug print. + runtime_output.debug('Debug: The "calculate_parameter_end_cap" function was successfully executed.') + return volume_tank, mass_wall, mass_insulation, length diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateparametertruncatedcone.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateparametertruncatedcone.py index af0b2ae4..202fff64 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateparametertruncatedcone.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateparametertruncatedcone.py @@ -23,7 +23,7 @@ from numpy import pi as PI -def calculate_parameter_truncated_cone(diameter_base, diameter_top, length, +def calculate_parameter_truncated_cone(runtime_output, diameter_base, diameter_top, length, thickness_wall=0.003, thickness_insulation=0.20, density_material_wall=2850, density_material_insulation=32): """Calculate parameters for truncated cone. @@ -36,6 +36,7 @@ def calculate_parameter_truncated_cone(diameter_base, diameter_top, length, and insulation are determined based on a hollow truncated cone. The mass can then be determined using the corresponding material density. + :param logging.Logger runtime_output: Logging object used for capturing log messages in the module :param float diameter_base: Diameter at base of cone in m :param float diameter_top: Diameter at top of cone in m :param float length: Length of cone in m @@ -74,4 +75,7 @@ def calculate_parameter_truncated_cone(diameter_base, diameter_top, length, mass_wall = volume_wall*density_material_wall mass_insulation = volume_insulation*density_material_insulation + # Debug print. + runtime_output.debug('Debug: The "calculate_parameter_truncated_cone" function was successfully executed.') + return volume_tank, mass_wall, mass_insulation diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculatethicknesswall.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculatethicknesswall.py index 23e23571..99f1897e 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculatethicknesswall.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculatethicknesswall.py @@ -22,7 +22,8 @@ import math -def calculate_thickness_wall(diameter, internal_pressure=1.5, tensile_stress_allowable=172, method='asme'): +def calculate_thickness_wall(runtime_output, diameter, internal_pressure=1.5, tensile_stress_allowable=172, + method='asme'): """Calculate wall thickness for aluminum walls with internal pressure of 1.5 bar according to ASME Code. This function calculates the wall thickness of a pressure vessel made of aluminum Al-2219 operated at 1.5 bar @@ -32,6 +33,7 @@ def calculate_thickness_wall(diameter, internal_pressure=1.5, tensile_stress_all TODO: Add source. TODO: Add AD2000 implementation. + :param logging.Logger runtime_output: Logging object used for capturing log messages in the module :param float diameter: Pressure vessel diameter in m :param float internal_pressure: Internal pressure in bar, defaults to 1.5 :param float tensile_stress_allowable: Allowable tensile stress in N/mm, defaults to 172 @@ -78,4 +80,8 @@ def calculate_thickness_wall(diameter, internal_pressure=1.5, tensile_stress_all - 0.6*internal_pressure) thickness_wall = thickness_wall_inch*conv_inch_to_cm/100 # ------------------------------------------------------------------ + + # Debug print. + runtime_output.debug('Debug: The "calculate_thickness_wall" function was successfully executed.') + return thickness_wall diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculatezposition.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculatezposition.py new file mode 100644 index 00000000..4715ad94 --- /dev/null +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculatezposition.py @@ -0,0 +1,41 @@ +# UNICADO - UNIversity Conceptual Aircraft Design and Optimization +# +# Copyright (C) 2025 UNICADO consortium +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. +# +# Description: +# This file is part of UNICADO. + +def calculate_z_position(fuselage_geometry, idx): + """Calculate z position. + + :param _type_ fuselage_geometry: _description_ + :param _type_ tank_dict: _description_ + :param _type_ idx: _description_ + :param _type_ section_name: _description_, defaults to None + :return _type_: _description_ + """ + lower_height = -fuselage_geometry['interpolated_lower_heights'][idx] + height = fuselage_geometry['interpolated_heights'][idx] + z_origin = fuselage_geometry['interpolated_section_origin_z'][idx] + z_middle = lower_height + height * 0.5 + if z_middle > z_origin: + z_position = abs(z_origin) + z_middle + elif z_middle < z_origin: + z_position = abs(z_origin) - abs(z_middle) + else: + z_position = 0.0 + + return z_position diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/methodliquidhydrogen.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/methodliquidhydrogen.py index 3f421668..a95bfb2d 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/methodliquidhydrogen.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/methodliquidhydrogen.py @@ -20,13 +20,11 @@ """Module providing calculation functions provided by the user.""" # Import standard modules. -import sys # Import own modules. from src.tube_and_wing.empirical.tank_design_tu_berlin.general.tankdesigntuberlin import tank_design_tu_berlin -from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculateconicaltank import calculate_conical_tank -from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculatecylindricaltank import calculate_cylindrical_tank -from src.tube_and_wing.empirical.tank_design_tu_berlin.general.calculatecenterofgravity import calculate_center_of_gravity +from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculateliquidhydrogentanks import ( + calculate_liquid_hydrogen_tanks) def method_liquid_hydrogen(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_config, runtime_output): @@ -36,9 +34,13 @@ def method_liquid_hydrogen(paths_and_names, routing_dict, dict_ac_exchange, dict [Add more information here...] The output dictionary 'liquid_hydrogen_output_dict' contains the results of the tank design and is structured according to the following scheme: - liquid_hydrogen_output_dict = {'group_name_1': {'parameter_1': value, 'parameter_2': value, ...}, - 'group_name_2': {'parameter_1': value, 'parameter_2': value, ...}, - ...} + liquid_hydrogen_output_dict = {'tank_0': {'parameter_1': value, 'parameter_2': value, ...}, + 'tank_1': {'parameter_1': value, 'parameter_2': value, ...}, + ..., + 'number_of_tanks': int, + 'tank_configuration': str, + 'geometry': {...}, + 'length_tank_overall': float} :param dict paths_and_names: Dictionary containing system paths and ElementTrees :param dict routing_dict: Dictionary containing routing parameters @@ -48,177 +50,29 @@ def method_liquid_hydrogen(paths_and_names, routing_dict, dict_ac_exchange, dict :return dict liquid_hydrogen_output_dict: Dictionary containing results from calculation for LH2-powered aircraft """ + """ Preparation. """ # Merge both data dictionaries. - dict_complete_data = dict_ac_exchange | dict_mod_config - # Extract general parameters from 'complete_data_dict'. - dict_general_data = {key: value for key, value in dict_complete_data.items() if - not key.endswith('_design') and not key.endswith('_study')} - # Extract design mission related parameters from 'complete_data_dict' and save in 'design_data' (w/o ending). - dict_design_data = {key.replace('_design', ''): value for key, value in dict_complete_data.items() - if key.endswith('_design')} - # Iterate over dictionary. - for key, value in dict_design_data.items(): - # Check, if any value is 'None'. - if value is None: - # If any value is 'None', the tank design is impossible and the program aborted. - runtime_output.critical('Error: Tank design not possible due to missing parameters! ' - + 'Program aborted.') - sys.exit(1) - # Merge design and general data dictionary. - dict_design = dict_general_data | dict_design_data - - #TODO: Delete this section if data extracted from aircraft exchange file. - dict_design['geometrical_data'] = { - # Lengths of aircraft sections. - 'length_nose_section_01': 0.06445732892, - 'length_nose_section_02': 0.1933719868, - 'length_nose_section_03': 0.3222866446, - 'length_nose_section_04': 0.4512013025, - 'length_nose_section_05': 0.5801159603, - 'length_nose_section_06': 0.7090306182, - 'length_nose_section_07': 0.837945276, - 'length_nose_section_08': 0.9668599339, - 'length_nose_section_09': 1.095774592, - 'length_constant_section_01': 32.363204, - 'length_tail_section_01': 1.72542446, - 'length_tail_section_02': 1.72542446, - 'length_tail_section_03': 1.72542446, - 'length_tail_section_04': 1.72542446, - 'length_tail_section_05': 1.72542446, - 'length_tail_section_06': 3.459300377, - 'length_tail_section_07': 3.459300377, - # Widths of aircraft sections. - 'width_nose_section_01': 0.6219072875, - 'width_nose_section_02': 1.232171749, - 'width_nose_section_03': 1.81877794, - 'width_nose_section_04': 2.368913086, - 'width_nose_section_05': 2.868421677, - 'width_nose_section_06': 3.301028172, - 'width_nose_section_07': 3.6471193, - 'width_nose_section_08': 3.881644273, - 'width_nose_section_09': 3.970066, - 'width_constant_section_01': 3.970066, - 'width_tail_section_01': 3.970066, - 'width_tail_section_02': 3.970066, - 'width_tail_section_03': 3.892703799, - 'width_tail_section_04': 3.618301624, - 'width_tail_section_05': 3.142809701, - 'width_tail_section_06': 1.985033, - 'width_tail_section_07': 0.827256299, - # Heights of aircraft sections. - 'height_nose_section_01': 0.647944796, - 'height_nose_section_02': 1.283759313, - 'height_nose_section_03': 1.894925056, - 'height_nose_section_04': 2.468092813, - 'height_nose_section_05': 2.988514422, - 'height_nose_section_06': 3.439232935, - 'height_nose_section_07': 3.799813925, - 'height_nose_section_08': 4.044157799, - 'height_nose_section_09': 4.136281495, - 'height_constant_section_01': 4.136281495, - 'height_tail_section_01': 4.074891643, - 'height_tail_section_02': 3.889781167, - 'height_tail_section_03': 3.578053688, - 'height_tail_section_04': 3.143415874, - 'height_tail_section_05': 2.681089784, - 'height_tail_section_06': 1.754173041, - 'height_tail_section_07': 0.827256299 - } - # END + dict_design = dict_ac_exchange | dict_mod_config dict_design['energy_carrier'] = routing_dict['user_layer'] - - # Design tank(s). - runtime_output.print('----------------------------------------------------------') - runtime_output.print('Tank design results for design mission.') - runtime_output.print('----------------------------------------------------------') - dict_tank_design = tank_design_tu_berlin(paths_and_names, routing_dict, dict_design, runtime_output) - - # TODO: Geometry in 'dict_tank_design' necessary? - # Set iteration variables. - tank_idx = 0 - energy_available = 0 - mass_tank_overall = 0 - tank_location_list = [] - tank_position_list = [] - length_tank_overall = 0 - - # Extract all tank entities from 'dict_tank_design'. - tank_entity_list = [key for key in dict_tank_design if key.startswith('tank_')] - # Check, if there is a tank in the tailcone of the aircraft. - tailcone_tank = next((key for key in tank_entity_list if dict_tank_design[key]['tank_position'] == 'tailcone'), - None) - # Sort list such that the key of the tank with the tailcone position is at the first list position. This ensures - # that this tank is calculated first. - if tailcone_tank: - tank_entity_list.remove(tailcone_tank) - tank_entity_list.insert(0, tailcone_tank) - number_of_tanks = len(tank_entity_list) - - mission_energy_amount = dict_design['mission_energy_amount']['mission_energy_amount_ID0'] - - # Iterate through tank entities and call functions from liquid hydrogen folder. - for tank_entity in tank_entity_list: - print('Calling liquid hydrogen functions for ...') - tank_location_list.append(dict_tank_design[tank_entity]['tank_location']) - tank_position_list.append(dict_tank_design[tank_entity]['tank_position']) - - # Call functions according to tank shape. - match dict_tank_design[tank_entity]['tank_shape']: - case 'conical': - print('... a conical tank.') - dict_tank_design = calculate_conical_tank(tank_entity, dict_design, dict_tank_design, runtime_output) - case 'cylindrical': - print('... a cylindrical tank.') - dict_tank_design = calculate_cylindrical_tank(tank_entity, dict_design, dict_tank_design, runtime_output) - - energy_available += dict_tank_design[tank_entity]['energy_available'] - energy_remaining = mission_energy_amount - energy_available - - # Set missing entries in 'dict_tank_design'. - if dict_tank_design[tank_entity]['tank_position'] == 'tailcone': - dict_tank_design[tank_entity]['energy_required'] = dict_tank_design[tank_entity]['energy_available'] - if number_of_tanks == 2: - # Da der Tank im tailcone immer zuerst berechnet wird und es nur einen gibt, muss bei einer Gesamtzahl - # von zwei Tanks der gesamte zweite Tank das restliche Volumen beinhalten. - dict_tank_design[tank_entity_list[-1]]['energy_required'] = energy_remaining - else: - # ... - for tmp_tank in tank_entity_list[1:]: - if (dict_tank_design[tmp_tank]['tank_location'] == 'fuselage' - and dict_tank_design[tmp_tank]['tank_position'] == 'rear'): - # Tank at rear fuselage. - # Assumption: energy_share von hinterem Tank beinhaltet energy des tailcone Tanks. - # energy_required = (mission_energy*energy_share) - energy_tailcone_tank - dict_tank_design[tmp_tank]['energy_required'] = ( - mission_energy_amount*dict_tank_design[tmp_tank]['energy_share'] - - dict_tank_design[tank_entity]['energy_available']) - elif (dict_tank_design[tmp_tank]['tank_location'] == 'fuselage' - and dict_tank_design[tmp_tank]['tank_position'] == 'front'): - # Tank at front fuselage. - dict_tank_design[tmp_tank]['energy_required'] = ( - mission_energy_amount*dict_tank_design[tmp_tank]['energy_share']) - - length_tank_overall += dict_tank_design[tank_entity]['geometry']['length_tank_overall'] - mass_tank_overall += dict_tank_design[tank_entity]['masses']['mass_tank_overall'] - - if energy_available > mission_energy_amount: - # TODO: Check for unnecessary tank entities. - print('Necessary volume available.') - break - if energy_available < mission_energy_amount and (tank_idx+1) == number_of_tanks: - # Sollte eigentlich nicht vorkommen, weil Tanks so dimensioniert werden, dass alles reinpasst. - print('Error: Necessary volume not available!') - - tank_idx += 1 - - # Add extra space between tanks. - # Check, if all tanks in fuselage. - if len(tank_location_list) > 0 and all(element == 'fuselage' for element in tank_location_list): - if len(tank_position_list) > 0 and all(element in {'tailcone', 'rear'} for element in tank_position_list): - # Add extra space between tanks in rear and tailcone of fuselage. - extra_space = 0.2 - length_tank_overall = length_tank_overall + extra_space - - liquid_hydrogen_output_dict = dict_tank_design + # Iterate over dictionary to check for missing values -> not possible atm. + # for key, value in dict_design.items(): + # # Check, if any value is 'None'. + # if value is None: + # # If any value is 'None', the calculation of the costs is impossible and the program aborted. + # runtime_output.critical('Error: Tank design not possible due to missing parameter (' + # + key + ')! ' + # + 'Program aborted.') + # sys.exit(1) + + """Calculate tanks.""" + # Tank design preparation. + liquid_hydrogen_output_dict = tank_design_tu_berlin(paths_and_names, routing_dict, dict_design, runtime_output) + # Tank calculation. + liquid_hydrogen_output_dict = calculate_liquid_hydrogen_tanks(dict_design, liquid_hydrogen_output_dict, + runtime_output) + + # Debug print. + runtime_output.debug('Debug: The "method_liquid_hydrogen" function was successfully executed.') + runtime_output.print('Tank design completed.') return liquid_hydrogen_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 e8a84ff2..5efdd828 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 @@ -46,6 +46,7 @@ def user_method_data_input_preparation(routing_dict): 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_tube_path = (tmp_fuselage_accommodation_path + 'payload_tube[@ID="0"]/') 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"]/' @@ -72,8 +73,6 @@ def user_method_data_input_preparation(routing_dict): [tmp_mission_analysis_path +'loaded_mission_energy/mission_energy[@ID="0"]/energy_carrier_ID', int], 'energy_carrier_name': [tmp_design_specs_path + 'energy_carriers/energy_carrier[@ID="0"]/type', str], - 'energy_carrier_density': - [tmp_design_specs_path + 'energy_carriers/energy_carrier[@ID="0"]/density', float], # Tank configuration. 'tank_energy_ID': [tmp_design_specs_path + 'configuration/tank_definition/tank[@ID="0"]/energy_carrier_ID', int], @@ -96,6 +95,12 @@ def user_method_data_input_preparation(routing_dict): '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_accommodation_position_x': [tmp_fuselage_accommodation_path + 'position/x', float], + 'fuselage_payload_tube_front_position_x': [tmp_fuselage_payload_tube_path + + 'payload_tube_reference_points/front_reference_points/x', float], + 'fuselage_payload_tube_aft_position_x': [tmp_fuselage_payload_tube_path + + 'payload_tube_reference_points/aft_reference_points/x', float], + 'fuselage_payload_tube_name': [tmp_fuselage_payload_tube_path + 'name', str], '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], @@ -156,29 +161,42 @@ def user_method_data_input_preparation(routing_dict): # 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/') + tmp_liquid_hydrogen = tmp_specific + 'liquid_hydrogen_tank_design_parameter/' 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_design_parameter/material_wall', str], + [tmp_liquid_hydrogen + 'material_wall', str], 'density_wall': - [tmp_specific + 'liquid_hydrogen_tank_design_parameter/density_wall', float], + [tmp_liquid_hydrogen + 'density_wall', float], + 'tensile_stress_allowable': + [tmp_liquid_hydrogen + 'tensile_stress_allowable', float], 'thickness_wall_calculation_method': - [tmp_specific + 'liquid_hydrogen_tank_design_parameter/thickness_wall_calculation_method', str], + [tmp_liquid_hydrogen + 'thickness_wall_calculation_method', str], 'mass_pumps': - [tmp_specific + 'liquid_hydrogen_tank_design_parameter/mass_pumps', float], + [tmp_liquid_hydrogen + 'mass_pumps', float], 'mass_baffle': - [tmp_specific + 'liquid_hydrogen_tank_design_parameter/mass_baffle', float], + [tmp_liquid_hydrogen + 'mass_baffle', float], 'type_insulation': - [tmp_specific + 'liquid_hydrogen_tank_design_parameter/type_insulation', str], + [tmp_liquid_hydrogen + 'type_insulation', str], 'material_insulation': - [tmp_specific + 'liquid_hydrogen_tank_design_parameter/material_insulation', str], + [tmp_liquid_hydrogen + 'material_insulation', str], 'thickness_insulation': - [tmp_specific + 'liquid_hydrogen_tank_design_parameter/thickness_insulation', float], + [tmp_liquid_hydrogen + 'thickness_insulation', float], 'density_insulation': - [tmp_specific + 'liquid_hydrogen_tank_design_parameter/density_insulation', float], + [tmp_liquid_hydrogen + 'density_insulation', float], + 'mass_vapor_barrier': + [tmp_liquid_hydrogen + 'mass_vapor_barrier', float], 'type_end_cap': - [tmp_specific + 'liquid_hydrogen_tank_design_parameter/type_end_cap', str], + [tmp_liquid_hydrogen + 'type_end_cap', str], 'internal_pressure': - [tmp_specific + 'liquid_hydrogen_tank_design_parameter/internal_pressure', float], + [tmp_liquid_hydrogen + 'internal_pressure', float], 'factor_usable_diameter': [tmp_specific + 'miscellaneous/factor_usable_diameter', float], 'factor_volume_allowance': @@ -388,10 +406,48 @@ def user_method_data_output_preparation(data_dict): round(data_dict[tank_id]['geometry']['total']['centroid']['z'], 9)], } + # Generate tank mass breakdown dictionary for current tank. + if data_dict[tank_id]['energy_carrier_name'] == 'liquid_hydrogen': + # Tank structure. + i = 0 + for key, value in data_dict[tank_id]['masses']['masses_tank_structure'].items(): + tank_structure_path = tank_path_id + 'mass_breakdown/tank_structure/component_mass[@ID="' + str(i) \ + + '"]/' + tank_structure_name = key.replace('mass_', '') + tank_structure_mass = data_dict[tank_id]['masses']['masses_tank_structure'][key] + tank_structure_output_dict = { + 'tank_structure_name_id' + str(j) + '_id' + str(i): + [tank_structure_path + 'name', + tank_structure_name], + 'tank_structure_mass_id' + str(j) + '_id' + str(i): + [tank_structure_path + 'mass', + tank_structure_mass], + } + tank_output_dict.update(tank_structure_output_dict) + i += 1 + # Tank insulation. + i = 0 + for key, value in data_dict[tank_id]['masses']['masses_tank_insulation'].items(): + tank_insulation_path = tank_path_id + 'mass_breakdown/tank_insulation/component_mass[@ID="' + str(i) \ + + '"]/' + tank_insulation_name = key.replace('mass_', '') + tank_insulation_mass = data_dict[tank_id]['masses']['masses_tank_insulation'][key] + tank_insulation_output_dict = { + 'tank_insulation_name_id' + str(j) + '_id' + str(i): + [tank_insulation_path + 'name', + tank_insulation_name], + 'tank_insulation_mass_id' + str(j) + '_id' + str(i): + [tank_insulation_path + 'mass', + tank_insulation_mass], + } + tank_output_dict.update(tank_insulation_output_dict) + i += 1 + # 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 = ['front_end_cap', 'front_tank_section', 'rear_tank_section', 'rear_end_cap'] 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() @@ -405,13 +461,11 @@ def user_method_data_output_preparation(data_dict): 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])) + section_names = ['front_end_cap', 'front_tank_section', 'rear_tank_section', 'rear_end_cap'] + number_of_sections = len(section_names) while i < number_of_sections: tank_section_path = tank_path_id + 'geometry/cross_section[@ID="' + str(i) + '"]/' - tank_section_name = 'tank_section_' + str(i) + tank_section_name = section_names[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 -- GitLab From a0db3e6d2e2bfbc1982a6874c08920d4ddc028a5 Mon Sep 17 00:00:00 2001 From: sroscher <s.roscher@tu-berlin.de> Date: Thu, 6 Mar 2025 16:22:25 +0100 Subject: [PATCH 2/7] Changed necessary files --- ...0_readenergycarrierandtankconfiguration.py | 2 +- .../general/methodhtmlreport.py | 58 ++++++++++++++----- .../liquid_hydrogen/calculateconicaltank.py | 14 ++++- .../calculatecylindricaltank.py | 14 ++++- .../calculateliquidhydrogentanks.py | 15 +++-- .../usermethoddatapreparation.py | 2 + 6 files changed, 79 insertions(+), 26 deletions(-) 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 cafe0282..c2658ffd 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 @@ -62,7 +62,7 @@ def read_energy_carrier_and_tank_configuration(paths_and_names, dict_ac_data, di tank_energy_share = dict_ac_data['energy_share'] except KeyError: runtime_output.critical('Error: Missing data in aircraft exchange file. Energy carrier and tank configuration ' - +' preparation not possible. Program aborted.') + + 'preparation not possible. Program aborted.') sys.exit('Exit information: Missing data in aircraft exchange file. Energy carrier and tank configuration ' 'preparation not possible!') diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/methodhtmlreport.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/methodhtmlreport.py index faeb7907..30b38c5e 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/methodhtmlreport.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/methodhtmlreport.py @@ -48,19 +48,31 @@ def method_html_report(paths_and_names, routing_dict, data_dict, method_specific # Extract data. # Note: Please ensure to format the data here (e.g., round floats)! - energy_information_data = [ - ("Tank configuration", - data_dict['tank_configuration']), - ("Volume required for mission", - f"{data_dict['total_tank_parameters']['volume_required_for_mission']:.1f} L"), - ("Volume provided in all tanks", - f"{data_dict['total_tank_parameters']['volume_provided_in_all_tanks']:.1f} L"), - ("Energy required for mission", - f"{data_dict['total_tank_parameters']['energy_required_for_mission']/1E6:,.1f} MJ"), - ("Energy provided in all tanks", - f"{data_dict['total_tank_parameters']['energy_provided_in_all_tanks']/1E6:,.1f} MJ"), - ] + # energy_information_data = [ + # ("Tank configuration", + # data_dict['tank_configuration']), + # ("Volume required for mission", + # f"{data_dict['total_tank_parameters']['volume_required_for_mission']:.1f} L"), + # ("Volume provided in all tanks", + # f"{data_dict['total_tank_parameters']['volume_provided_in_all_tanks']:.1f} L"), + # ("Energy required for mission", + # f"{data_dict['total_tank_parameters']['energy_required_for_mission']/1E6:,.1f} MJ"), + # ("Energy provided in all tanks", + # f"{data_dict['total_tank_parameters']['energy_provided_in_all_tanks']/1E6:,.1f} MJ"), + # ] if routing_dict['user_layer'] == 'kerosene': + energy_information_data = [ + ("Tank configuration", + data_dict['tank_configuration']), + ("Volume required for mission", + f"{data_dict['total_tank_parameters']['volume_required_for_mission']:.1f} L"), + ("Volume provided in all tanks", + f"{data_dict['total_tank_parameters']['volume_provided_in_all_tanks']:.1f} L"), + ("Energy required for mission", + f"{data_dict['total_tank_parameters']['energy_required_for_mission']/1E6:,.1f} MJ"), + ("Energy provided in all tanks", + f"{data_dict['total_tank_parameters']['energy_provided_in_all_tanks']/1E6:,.1f} MJ"), + ] # Total energy information data. tmp_energy = sum(value['energy_available'] for key, value in data_dict.items() if key.startswith("tank_") and key[5:].isdigit() and not value['energy_required_for_mission']) @@ -76,7 +88,19 @@ def method_html_report(paths_and_names, routing_dict, data_dict, method_specific energy_information_data.insert(6, ("Energy provided in necessary tanks", f"{(data_dict['total_tank_parameters']['energy_provided_in_all_tanks'] - tmp_energy)/1E6:,.1f} MJ")) energy_information_data.insert(8, ("Energy missing", f"{energy_missing:,.1f} MJ")) - + elif routing_dict['user_layer'] == 'liquid_hydrogen': + energy_information_data = [ + ("Tank configuration", + data_dict['tank_configuration']), + ("Volume required for mission", + f"{data_dict['total_tank_parameters']['volume_required_for_mission']:.1f} m3"), + ("Volume provided in all tanks", + f"{data_dict['total_tank_parameters']['volume_provided_in_all_tanks']:.1f} m3"), + ("Energy required for mission", + f"{data_dict['total_tank_parameters']['energy_required_for_mission']/1E6:,.1f} MJ"), + ("Energy provided in all tanks", + f"{data_dict['total_tank_parameters']['energy_provided_in_all_tanks']/1E6:,.1f} MJ"), + ] # energy_information_data = [ # ("Tank configuration", # data_dict['tank_configuration']), @@ -108,8 +132,12 @@ def method_html_report(paths_and_names, routing_dict, data_dict, method_specific if key.startswith("tank_") and key[5:].isdigit() ] aircraft_tank_data_title = "Aircraft tank overview" - aircraft_tank_data_column_titles = ["Tank ID", "Designator", "Energy carrier", "Volume in L", "Energy in MJ", - "Energy req. for mission?"] + if routing_dict['user_layer'] == 'kerosene': + aircraft_tank_data_column_titles = ["Tank ID", "Designator", "Energy carrier", "Volume in L", "Energy in MJ", + "Energy req. for mission?"] + elif routing_dict['user_layer'] == 'liquid_hydrogen': + aircraft_tank_data_column_titles = ["Tank ID", "Designator", "Energy carrier", "Volume in m3", "Energy in MJ", + "Energy req. for mission?"] aircraft_tank_data_alignment = ["l", "l", "l", "r", "r", "m"] """Generate report.""" diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateconicaltank.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateconicaltank.py index f9a66596..08be41a4 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateconicaltank.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateconicaltank.py @@ -23,7 +23,7 @@ import numpy as np from matplotlib import pyplot as plt # Import own libraries. -from pyunitconversion import constants +import pyenergycarriers from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculateparameterendcap import ( calculate_parameter_end_cap) from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculatethicknesswall import ( @@ -65,8 +65,16 @@ def calculate_conical_tank(tank_entity, dict_ac_data, dict_tank_design, runtime_ thickness_wall_and_insulation = thickness_wall + thickness_insulation thickness_wall_minimum_aluminum = 0.003 # AD-2000: minimum wall thickness AL-2219 is 3 mmm. - liquid_hydrogen_volumetric_energy_density = constants.LIQUID_HYDROGEN_VOLUMETRIC_ENERGY_DENSITY - liquid_hydrogen_gravimetric_energy_density = constants.LIQUID_HYDROGEN_GRAVIMETRIC_ENERGY_DENSITY + # Get density of liquid hydrogen. + for key, value in dict_ac_data['energy_carrier_name'].items(): + if value == 'liquid_hydrogen': + density_liquid_hydrogen = dict_ac_data['energy_carrier_density'].get( + key.replace('energy_carrier_name', 'energy_carrier_density')) + break + liquid_hydrogen_volumetric_energy_density = ( + pyenergycarriers.EnergyCarrier('liquid_hydrogen', density_liquid_hydrogen).volumetric_energy_density) + liquid_hydrogen_gravimetric_energy_density = ( + pyenergycarriers.EnergyCarrier('liquid_hydrogen', density_liquid_hydrogen).gravimetric_energy_density) """Calculation. """ max_length = round(dict_tank_design[tank_entity]['geometry']['maximum_length'],2) diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculatecylindricaltank.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculatecylindricaltank.py index 785f5d75..56af32fc 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculatecylindricaltank.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculatecylindricaltank.py @@ -22,7 +22,7 @@ import numpy as np # Import own modules. -from pyunitconversion import constants +import pyenergycarriers from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculateparameterendcap import ( calculate_parameter_end_cap) from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculatethicknesswall import ( @@ -64,8 +64,16 @@ def calculate_cylindrical_tank(tank_entity, dict_ac_data, dict_tank_design, runt thickness_wall_and_insulation = thickness_wall + thickness_insulation thickness_wall_minimum_aluminum = 0.003 # AD-2000: minimum wall thickness AL-2219 is 3 mmm. - liquid_hydrogen_volumetric_energy_density = constants.LIQUID_HYDROGEN_VOLUMETRIC_ENERGY_DENSITY - liquid_hydrogen_gravimetric_energy_density = constants.LIQUID_HYDROGEN_GRAVIMETRIC_ENERGY_DENSITY + # Get density of liquid hydrogen. + for key, value in dict_ac_data['energy_carrier_name'].items(): + if value == 'liquid_hydrogen': + density_liquid_hydrogen = dict_ac_data['energy_carrier_density'].get( + key.replace('energy_carrier_name', 'energy_carrier_density')) + break + liquid_hydrogen_volumetric_energy_density = ( + pyenergycarriers.EnergyCarrier('liquid_hydrogen', density_liquid_hydrogen).volumetric_energy_density) + liquid_hydrogen_gravimetric_energy_density = ( + pyenergycarriers.EnergyCarrier('liquid_hydrogen', density_liquid_hydrogen).gravimetric_energy_density) volume_required = energy_required/liquid_hydrogen_volumetric_energy_density diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateliquidhydrogentanks.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateliquidhydrogentanks.py index e921f3e9..cc59a2f4 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateliquidhydrogentanks.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateliquidhydrogentanks.py @@ -22,7 +22,7 @@ # Import standard libraries. # Import own libraries. -from pyunitconversion import constants +import pyenergycarriers from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculateconicaltank import calculate_conical_tank from src.tube_and_wing.empirical.tank_design_tu_berlin.liquid_hydrogen.calculatecylindricaltank import calculate_cylindrical_tank @@ -40,13 +40,20 @@ def calculate_liquid_hydrogen_tanks(dict_ac_data, dict_tank_design, runtime_outp """ # Extract data and initialize variables. number_of_tanks = dict_tank_design['number_of_tanks'] - liquid_hydrogen_volumetric_energy_density = constants.LIQUID_HYDROGEN_VOLUMETRIC_ENERGY_DENSITY + # Get density of liquid hydrogen. + for key, value in dict_ac_data['energy_carrier_name'].items(): + if value == 'liquid_hydrogen': + density_liquid_hydrogen = dict_ac_data['energy_carrier_density'].get( + key.replace('energy_carrier_name', 'energy_carrier_density')) + break + liquid_hydrogen_volumetric_energy_density = ( + pyenergycarriers.EnergyCarrier('liquid_hydrogen', density_liquid_hydrogen).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 (13 liter LH2 per PAX per 100 km).') energy_demand = 13 # in L per passenger per 100 km + runtime_output.print('Initial sizing loop! Energy demand is estimated (' + str(energy_demand) + ' liter LH2 per PAX per 100 km).') mission_fuel_amount = dict_ac_data['number_of_passengers']*(dict_ac_data['range']/1000)*energy_demand/100 mission_energy_amount = (mission_fuel_amount/1000*liquid_hydrogen_volumetric_energy_density) else: @@ -130,7 +137,7 @@ def calculate_liquid_hydrogen_tanks(dict_ac_data, dict_tank_design, runtime_outp print_position + 'tank (' + tank_entity + ') calculated: \n' + ' ' + '- Volume (energy) available: ' - + f"{dict_tank_design[tank_entity]['volume_available']:,.2f}" + ' L (' + + f"{dict_tank_design[tank_entity]['volume_available']:,.2f}" + ' m3 (' + f"{dict_tank_design[tank_entity]['energy_available']/1e6:,.2f}" + ' MJ) \n' + ' ' 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 5efdd828..b40edec1 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 @@ -73,6 +73,8 @@ def user_method_data_input_preparation(routing_dict): [tmp_mission_analysis_path +'loaded_mission_energy/mission_energy[@ID="0"]/energy_carrier_ID', int], 'energy_carrier_name': [tmp_design_specs_path + 'energy_carriers/energy_carrier[@ID="0"]/type', str], + 'energy_carrier_density': + [tmp_design_specs_path + 'energy_carriers/energy_carrier[@ID="0"]/density', float], # Tank configuration. 'tank_energy_ID': [tmp_design_specs_path + 'configuration/tank_definition/tank[@ID="0"]/energy_carrier_ID', int], -- GitLab From d7ad193fa84abd22ff38008ea4d1bcb88e8ad26e Mon Sep 17 00:00:00 2001 From: sroscher <s.roscher@tu-berlin.de> Date: Mon, 10 Mar 2025 14:18:38 +0100 Subject: [PATCH 3/7] -_02_preparegeometricaldata.py: Corrected bug with position bulkhead - calculateliquidhydrogentanks.py: Adapted/corrected calculation of additional_fuselage_length - usermethoddatapreparation.py: Added some more input values from acXML - datapostprocessing.py: Adapted boundaries for tank sections and additional_fuselage_length --- tank_design/src/datapostprocessing.py | 10 +++--- .../_02_preparegeometricaldata.py | 31 +++++++++++++++++++ .../calculateliquidhydrogentanks.py | 15 +++++++-- .../usermethoddatapreparation.py | 9 +++++- 4 files changed, 56 insertions(+), 9 deletions(-) diff --git a/tank_design/src/datapostprocessing.py b/tank_design/src/datapostprocessing.py index d47d4c2b..71c41967 100644 --- a/tank_design/src/datapostprocessing.py +++ b/tank_design/src/datapostprocessing.py @@ -130,15 +130,15 @@ def data_postprocessing(paths_and_names, routing_dict, data_dict, runtime_output 'reference point in y-direction.'}, 'value': '0', 'unit': 'm', - 'lower_boundary': '-40', - 'upper_boundary': '40'}, + 'lower_boundary': '-80', + 'upper_boundary': '80'}, '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'}, + 'lower_boundary': '-80', + 'upper_boundary': '80'}, }, 'mass_properties': { 'attributes': { @@ -571,7 +571,7 @@ def data_postprocessing(paths_and_names, routing_dict, data_dict, runtime_output 'fuselage, if greater than 0: stretch fuselage).'}, 'value': '0', 'unit': 'm', - 'lower_boundary': '0', + 'lower_boundary': '-80', 'upper_boundary': 'inf' } } 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 da1437be..73358bcc 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 @@ -759,6 +759,37 @@ def prepare_for_liquid_hydrogen(dict_ac_data, dict_tank_design, runtime_output): # x_coord_pressure_bulkhead = x_coord_begin_tail_section + tail_length_before_bulkhead # dict_tank_design['geometry']['raw_data']['x_coord_pressure_bulkhead'] = x_coord_pressure_bulkhead + # Check if second iteration loop (tanks already written to acXML). If second iteration loop, the available length + # in the tail cone is assumed to equal the total length of the tail cone tank. + if dict_ac_data['tank_designator']['tank_designator_ID0'] is not None: + tail_cone_tank_length = None + + for key, value in dict_ac_data['tank_designator'].items(): + + if value == 'fuselage_tail_cone': + # Get tank ID of tail cone tank. + tail_cone_tank_ID = key.split('_')[-1] + # Extract IDs. + for cross_key, cross_value in dict_ac_data['tank_cross_section_name'].items(): + ids = cross_key.split('_')[-2:] + + if ids[0] == tail_cone_tank_ID and cross_value == 'rear_end_cap': + # Find rear end cap ID. + rear_end_cap_ID = ids[1] + # Generate position key. + position_key = f"tank_cross_section_position_{tail_cone_tank_ID}_{rear_end_cap_ID}" + + if position_key in dict_ac_data['tank_cross_section_position']: + # Get tail cone tank length. + tail_cone_tank_length = dict_ac_data['tank_cross_section_position'][position_key] + # Generate index of bulkhead. + idx_bulkhead = idx_begin_tail_section + round(tail_cone_tank_length*100) + break + + if tail_cone_tank_length is not None: + break + + # Iterate through tank entities and extract necessary geometry. for tank_entity in dict_tank_design.keys(): if tank_entity.startswith('tank_') and tank_entity[5:].isdigit(): diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateliquidhydrogentanks.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateliquidhydrogentanks.py index cc59a2f4..6f6fcb3e 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateliquidhydrogentanks.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateliquidhydrogentanks.py @@ -285,7 +285,17 @@ def calculate_liquid_hydrogen_tanks(dict_ac_data, dict_tank_design, runtime_outp + ' ' + '- Overall length of tanks in the rear of the fuselage: ' + f"{length_rear_tanks_overall:,.2f}" + ' m. \n') - + + # Calculate additional fuselage length. + additional_fuselage_length_current = length_front_tank_overall + length_rear_tanks_overall + additional_fuselage_length_old = dict_ac_data['additional_fuselage_length'] + if additional_fuselage_length_old is None: + additional_fuselage_length = length_front_tank_overall + length_rear_tanks_overall + else: + length_difference = additional_fuselage_length_old - additional_fuselage_length_current + additional_fuselage_length = (-length_difference if length_difference != 0 + else additional_fuselage_length_current) + # Results print. runtime_output.print('All tanks calculated: \n' + ' ' @@ -304,8 +314,7 @@ def calculate_liquid_hydrogen_tanks(dict_ac_data, dict_tank_design, runtime_outp # Prepare data output. dict_tank_design['total_tank_parameters']['length_rear_tanks_overall'] = length_rear_tanks_overall dict_tank_design['total_tank_parameters']['length_front_tank_overall'] = length_front_tank_overall - dict_tank_design['total_tank_parameters']['additional_fuselage_length'] = (length_front_tank_overall - + length_rear_tanks_overall) + dict_tank_design['total_tank_parameters']['additional_fuselage_length'] = additional_fuselage_length dict_tank_design['total_tank_parameters']['energy_provided_in_all_tanks'] = energy_available dict_tank_design['total_tank_parameters']['volume_provided_in_all_tanks'] = volume_available dict_tank_design['total_tank_parameters']['energy_required_for_mission'] = mission_energy_amount 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 b40edec1..ee1fbc3a 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 @@ -145,7 +145,14 @@ def user_method_data_input_preparation(routing_dict): '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], + 'empennage_spar_position_outer_chord': [tmp_empennage_spar_path + 'position/outer_position/chord/to', float], + # Tank data. + 'tank_designator': ['./component_design/tank/specific/tank[@ID="0"]/designator', str], + 'tank_cross_section_name': [ + './component_design/tank/specific/tank[@ID="0"]/geometry/cross_section[@ID="0"]/name', str], + 'tank_cross_section_position': [ + './component_design/tank/specific/tank[@ID="0"]/geometry/cross_section[@ID="0"]/position/z', float], + 'additional_fuselage_length': ['./component_design/tank/specific/additional_fuselage_length', float] } """Module configuration file.""" -- GitLab From 0e5b3a09ecbaf217abdad891d3c3515ff579b782 Mon Sep 17 00:00:00 2001 From: sroscher <s.roscher@tu-berlin.de> Date: Tue, 11 Mar 2025 11:30:07 +0100 Subject: [PATCH 4/7] - Changed boundaries --- tank_design/src/datapostprocessing.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tank_design/src/datapostprocessing.py b/tank_design/src/datapostprocessing.py index 71c41967..96a2c5a5 100644 --- a/tank_design/src/datapostprocessing.py +++ b/tank_design/src/datapostprocessing.py @@ -481,16 +481,16 @@ def data_postprocessing(paths_and_names, routing_dict, data_dict, runtime_output 'reference point in y-direction.'}, 'value': '0', 'unit': 'm', - 'lower_boundary': '-40', - 'upper_boundary': '40'}, + 'lower_boundary': '-80', + 'upper_boundary': '80'}, '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'}}, + 'lower_boundary': '-80', + 'upper_boundary': '80'}}, 'shape': { 'attributes': { 'description': 'Description of the shape of the cross section (circular, ' -- GitLab From aaeef277f6dfb291a0a1ced862bccdaf8e27713f Mon Sep 17 00:00:00 2001 From: sroscher <s.roscher@tu-berlin.de> Date: Tue, 11 Mar 2025 15:51:00 +0100 Subject: [PATCH 5/7] - methodkerosene.py: Since I included new parameters to be read from acXML, I had to exclude them for the kerosene method - calculateliquidhydrogentanks.py: Adapted additional_fuselage_length value --- .../kerosene/methodkerosene.py | 5 ++++- .../calculateliquidhydrogentanks.py | 17 +++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) 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 eb77311d..7a476fd6 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 @@ -68,9 +68,12 @@ def method_kerosene(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_co """ Preparation. """ # Check if any values from aircraft exchange file are missing. + # Exclude the following values for kerosene. + unnecessary_values_for_kerosene = ['tank_designator', 'tank_cross_section_name', 'tank_cross_section_position', + 'additional_fuselage_length'] for key, value in dict_ac_exchange.items(): # Check, if any value is 'None'. - if value is None: + if (key not in unnecessary_values_for_kerosene) and (value is None): # If any value is 'None', the calculation of the costs is impossible and the program aborted. runtime_output.critical('Error: Tank design not possible due to missing parameter in aircraft exchange ' + 'file (' + key + ')! ' diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateliquidhydrogentanks.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateliquidhydrogentanks.py index 6f6fcb3e..213b87d5 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateliquidhydrogentanks.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateliquidhydrogentanks.py @@ -287,14 +287,15 @@ def calculate_liquid_hydrogen_tanks(dict_ac_data, dict_tank_design, runtime_outp + f"{length_rear_tanks_overall:,.2f}" + ' m. \n') # Calculate additional fuselage length. - additional_fuselage_length_current = length_front_tank_overall + length_rear_tanks_overall - additional_fuselage_length_old = dict_ac_data['additional_fuselage_length'] - if additional_fuselage_length_old is None: - additional_fuselage_length = length_front_tank_overall + length_rear_tanks_overall - else: - length_difference = additional_fuselage_length_old - additional_fuselage_length_current - additional_fuselage_length = (-length_difference if length_difference != 0 - else additional_fuselage_length_current) + # additional_fuselage_length_current = length_front_tank_overall + length_rear_tanks_overall + # additional_fuselage_length_old = dict_ac_data['additional_fuselage_length'] + # if additional_fuselage_length_old is None: + # additional_fuselage_length = length_front_tank_overall + length_rear_tanks_overall + # else: + # length_difference = additional_fuselage_length_old - additional_fuselage_length_current + # additional_fuselage_length = (-length_difference if length_difference != 0 + # else additional_fuselage_length_current) + additional_fuselage_length = length_front_tank_overall + length_rear_tanks_overall # Results print. runtime_output.print('All tanks calculated: \n' -- GitLab From ed8ab687f8d60948e2be164d04ac5c1ee73ceda9 Mon Sep 17 00:00:00 2001 From: sroscher <s.roscher@tu-berlin.de> Date: Wed, 12 Mar 2025 09:27:47 +0100 Subject: [PATCH 6/7] - Deleted dead code/comments --- tank_design/src/datapostprocessing.py | 46 +---------- .../_02_preparegeometricaldata.py | 79 ------------------- .../general/methodhtmlreport.py | 35 +------- .../liquid_hydrogen/calculateconicaltank.py | 33 -------- 4 files changed, 2 insertions(+), 191 deletions(-) diff --git a/tank_design/src/datapostprocessing.py b/tank_design/src/datapostprocessing.py index 96a2c5a5..b8a4cda6 100644 --- a/tank_design/src/datapostprocessing.py +++ b/tank_design/src/datapostprocessing.py @@ -519,52 +519,8 @@ def data_postprocessing(paths_and_names, routing_dict, data_dict, runtime_output 'lower_boundary': '0', 'upper_boundary': 'inf'} } - }#, + } }, - # 'mass_breakdown': { - # 'attributes': { - # 'description': 'Mass breakdown of one tank.'}, - # 'tank_insulation': { - # 'attributes': { - # 'description': 'Mass breakdown of the tank insulation.'}, - # 'component_mass': { - # 'attributes': { - # 'ID': '0', - # 'description': 'Mass of a single component of the tank insulation.'}, - # 'name': { - # 'attributes': { - # 'description': 'Name of the tank insulation component.'}, - # 'value': 'tank_insulation_mass'}, - # 'mass': { - # 'attributes': { - # 'description': 'Mass of the tank insulation component.'}, - # 'value': '0', - # 'unit': 'kg', - # 'lower_boundary': '0', - # 'upper_boundary': '100000'}} - # }, - # 'tank_structure': { - # 'attributes': { - # 'description': 'Mass breakdown of the tank structure.'}, - # 'component_mass': { - # 'attributes': { - # 'ID': '0', - # 'description': 'Mass of a single component of the tank structure.'}, - # 'name': { - # 'attributes': { - # 'description': 'Name of the tank structure component.'}, - # 'value': 'tank_structure_mass'}, - # 'mass': { - # 'attributes': { - # 'description': 'Mass of the tank structure component.'}, - # 'value': '0', - # 'unit': 'kg', - # 'lower_boundary': '0', - # 'upper_boundary': '100000'} - # }, - # } - # } - # }, 'additional_fuselage_length': { 'attributes': { 'description': 'Additional fuselage length (if smaller than 0: shrink ' 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 73358bcc..735d063a 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 @@ -25,28 +25,6 @@ from matplotlib import pyplot as plt import sys import numpy as np -# Import own modules. -# from pyunitconversion import constants -# import pyaircraftgeometry2 as geom2 -# import pyaixml as aixml - - -# def read_aerodynamic_surface(paths, component, wing_id): -# acxml = aixml.openDocument(paths["path_to_aircraft_exchange_file"]) -# airfoil_data_dir = f"{paths["project_directory"]}/geometryData/airfoilData" -# aerodynamic_surface = geom2.factory.WingFactory(acxml, airfoil_data_dir).create( -# f"{component}/specific/geometry/aerodynamic_surface@{wing_id}") - -# return aerodynamic_surface - - -# def read_spar(paths, component, wing_id, spar_id): -# acxml = aixml.openDocument(paths["path_to_aircraft_exchange_file"]) -# airfoil_data_dir = f"{paths["project_directory"]}/geometryData/airfoilData" -# spar = geom2.factory.SparFactory(acxml, airfoil_data_dir).create( -# f"{component}/specific/geometry/aerodynamic_surface@{wing_id}/spars/spar@{spar_id}") - -# return spar def prepare_geometrical_data(paths_and_names, dict_ac_data, dict_tank_design, runtime_output): """Prepare geometrical data for further use. @@ -166,16 +144,6 @@ def prepare_wing_tanks(dict_ac_data, dict_tank_design, runtime_output): :return dict dict_tank_design: Dictionary containing tank design parameter """ # TODO: Divide dict into 'left' and 'right'? - """AircraftGeometry2 Test.""" - # print(constants.AU) - # wing = read_aerodynamic_surface(paths_and_names, 'wing', wing_id) - # span = geom2.measure.span(wing) - # chord_at_tip = geom2.measure.chord(wing,-span/2) - # offset_leading_edge_x = geom2.measure.offset_LE(wing,-span/2).x() - # thickness_maximum_at_root = geom2.measure.thickness_max(wing.sections[0]) - # spar = read_spar(paths_and_names, 'wing', wing_id, '0') - # spar_area = geom2.measure.area(spar) - # print(list(dir(geom2))) """Extract wing information for further operations.""" # Extract wing ID and information on wing symmetry. @@ -247,14 +215,6 @@ def prepare_wing_tanks(dict_ac_data, dict_tank_design, runtime_output): 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!') # Add 'tmp_dict' to 'dict_tank_design'. dict_tank_design['geometry']['wing'] = tmp_dict @@ -450,14 +410,6 @@ def prepare_trim_tank(dict_ac_data, dict_tank_design, runtime_output): tmp_dict['sections'][section]['position']['y']*100, 0))) # 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 @@ -604,8 +556,6 @@ def prepare_additional_center_tank(dict_ac_data, dict_tank_design, runtime_outpu :return _type_: _description_ """ # Extract wing position and index. - # wing_id = next(key for key, value in dict_ac_data['wing_name'].items() - # if value == 'main_wing').split('ID')[-1] position_wing = dict_ac_data['wing_position_x'] idx_wing = int(round(position_wing*100, 0)) # Extract fuselage length. @@ -726,38 +676,9 @@ def prepare_for_liquid_hydrogen(dict_ac_data, dict_tank_design, runtime_output): diameter_usable = [diameter_outside_max[idx]*factor_usable_diameter for idx in range(0, n_points)] dict_tank_design['geometry']['fuselage']['diameter_usable'] = diameter_usable - # TEST - # plt.plot(x_coord, dict_tank_design['geometry']['fuselage']['widths']) - # plt.plot(x_coord, dict_tank_design['geometry']['fuselage']['heights']) - # plt.plot(width_fuselage_external_interpolated[500:600]) - # plt.plot(height_fuselage_external_interpolated[500:600]) - # plt.plot(x_coord_array, fuselage_section_origin_z_interpolated) - # plt.show() - - # widths = dict_tank_design['geometry']['fuselage']['widths'] - # heights = dict_tank_design['geometry']['fuselage']['heights'] - # widths = width_fuselage_external_interpolated - # heights = height_fuselage_external_interpolated - # for i in range(len(widths) - 1): - # # Check if the next width or height is smaller than the current - # smaller_width = round(widths[i + 1],4) < round(widths[i],4) - # smaller_height = round(heights[i + 1], 4) < round(heights[i], 4) - # if smaller_width or smaller_height: - # idx = i # Return the index where the tail section starts - # print(heights[i]) - # print(heights[i + 1]) - # break - # else: - # idx = None - # TEST END - # Find index and x-coordinate of begin of tail section. idx_begin_tail_section = determine_begin_tail_section(width_fuselage_external_interpolated, height_fuselage_external_interpolated) - # x_coord_begin_tail_section = x_coord_array[idx_begin_tail_section] - # Find x-coordinate of pressure bulkhead. - # x_coord_pressure_bulkhead = x_coord_begin_tail_section + tail_length_before_bulkhead - # dict_tank_design['geometry']['raw_data']['x_coord_pressure_bulkhead'] = x_coord_pressure_bulkhead # Check if second iteration loop (tanks already written to acXML). If second iteration loop, the available length # in the tail cone is assumed to equal the total length of the tail cone tank. diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/methodhtmlreport.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/methodhtmlreport.py index 30b38c5e..21f43998 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/methodhtmlreport.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/general/methodhtmlreport.py @@ -46,20 +46,6 @@ def method_html_report(paths_and_names, routing_dict, data_dict, method_specific root_of_module_config_tree = paths_and_names['root_of_module_config_tree'] plot_switch = (eval(root_of_module_config_tree.find('.//plot_output/enable/value').text.capitalize())) - # Extract data. - # Note: Please ensure to format the data here (e.g., round floats)! - # energy_information_data = [ - # ("Tank configuration", - # data_dict['tank_configuration']), - # ("Volume required for mission", - # f"{data_dict['total_tank_parameters']['volume_required_for_mission']:.1f} L"), - # ("Volume provided in all tanks", - # f"{data_dict['total_tank_parameters']['volume_provided_in_all_tanks']:.1f} L"), - # ("Energy required for mission", - # f"{data_dict['total_tank_parameters']['energy_required_for_mission']/1E6:,.1f} MJ"), - # ("Energy provided in all tanks", - # f"{data_dict['total_tank_parameters']['energy_provided_in_all_tanks']/1E6:,.1f} MJ"), - # ] if routing_dict['user_layer'] == 'kerosene': energy_information_data = [ ("Tank configuration", @@ -101,26 +87,7 @@ def method_html_report(paths_and_names, routing_dict, data_dict, method_specific ("Energy provided in all tanks", f"{data_dict['total_tank_parameters']['energy_provided_in_all_tanks']/1E6:,.1f} MJ"), ] - # energy_information_data = [ - # ("Tank configuration", - # data_dict['tank_configuration']), - # ("Volume required for mission", - # f"{data_dict['total_tank_parameters']['volume_required_for_mission']:.1f} L"), - # ("Volume provided in necessary tanks", - # f"{data_dict['total_tank_parameters']['volume_provided_in_all_tanks'] - tmp_volume:.1f} L"), - # ("Volume provided in all tanks", - # f"{data_dict['total_tank_parameters']['volume_provided_in_all_tanks']:.1f} L"), - # ("Volume missing", - # f"{volume_missing:.1f} L"), - # ("Energy required for mission", - # f"{data_dict['total_tank_parameters']['energy_required_for_mission']/1E6:,.1f} MJ"), - # ("Energy provided in necessary tanks", - # f"{(data_dict['total_tank_parameters']['energy_provided_in_all_tanks'] - tmp_energy)/1E6:,.1f} MJ"), - # ("Energy provided in all tanks", - # f"{data_dict['total_tank_parameters']['energy_provided_in_all_tanks']/1E6:,.1f} MJ"), - # ("Energy missing", - # f"{energy_missing:,.1f} MJ"), - # ] + energy_information_title = "General information" energy_information_column_titles = ["Parameter", "Value"] energy_information_alignment = ["l", "r"] diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateconicaltank.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateconicaltank.py index 08be41a4..7dd77c55 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateconicaltank.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/calculateconicaltank.py @@ -233,39 +233,6 @@ def calculate_conical_tank(tank_entity, dict_ac_data, dict_tank_design, runtime_ } - # plot test - # upper_contour = (dict_tank_design['geometry']['fuselage']['interpolated_section_origin_z'] - # + dict_tank_design['geometry']['fuselage']['interpolated_upper_heights']) - # lower_contour = (dict_tank_design['geometry']['fuselage']['interpolated_section_origin_z'] - # - dict_tank_design['geometry']['fuselage']['interpolated_lower_heights']) - # middle_line = dict_tank_design['geometry']['fuselage']['interpolated_section_origin_z'] - # front_end_cap_x = [idx_front_end_cap, idx_front_end_cap, idx_front_end_cap] - # front_end_cap_z = [dict_tank_design[tank_entity]['geometry']['front_end_cap']['position']['z'] - # + maximum_tank_diameter_front*0.5, - # dict_tank_design[tank_entity]['geometry']['front_end_cap']['position']['z'], - # dict_tank_design[tank_entity]['geometry']['front_end_cap']['position']['z'] - # - maximum_tank_diameter_front*0.5] - # front_tank_section_x = [idx_front_tank_section, idx_front_tank_section, idx_front_tank_section] - # front_tank_section_z = [dict_tank_design[tank_entity]['geometry']['front_tank_section']['position']['z'] - # + maximum_tank_diameter_front*0.5, - # dict_tank_design[tank_entity]['geometry']['front_tank_section']['position']['z'], - # dict_tank_design[tank_entity]['geometry']['front_tank_section']['position']['z'] - # - maximum_tank_diameter_front*0.5] - # rear_end_cap_x = [idx_rear_end_cap, idx_rear_end_cap, idx_rear_end_cap] - # rear_end_cap_z = [dict_tank_design[tank_entity]['geometry']['rear_end_cap']['position']['z'] - # + maximum_tank_diameter_front*0.5, - # dict_tank_design[tank_entity]['geometry']['rear_end_cap']['position']['z'], - # dict_tank_design[tank_entity]['geometry']['rear_end_cap']['position']['z'] - # - maximum_tank_diameter_front*0.5] - # plt.plot(upper_contour) - # plt.plot(middle_line) - # plt.plot(lower_contour) - # plt.plot(front_end_cap_x, front_end_cap_z, marker='x', markersize=10, color='red') - # plt.plot(front_tank_section_x, front_tank_section_z, marker='x', markersize=10, color='red') - # plt.plot(rear_end_cap_x, rear_end_cap_z, marker='x', markersize=10, color='red') - # plt.show() - # end plot test - """Calculate overall values.""" volume_tank_available = volume_tank_front_end_cap + volume_tank_conical_section + volume_tank_rear_end_cap volume_tank_usable = volume_tank_available/(1 + factor_volume_allowance) -- GitLab From d518624b8cfd48a779014737c92726268d1469f8 Mon Sep 17 00:00:00 2001 From: sroscher <s.roscher@tu-berlin.de> Date: Wed, 12 Mar 2025 12:14:03 +0100 Subject: [PATCH 7/7] - Added check if any value is missing in acXML or module config XML - methodkerosene.py: Corrected comments --- .../kerosene/methodkerosene.py | 4 +-- .../liquid_hydrogen/methodliquidhydrogen.py | 34 ++++++++++++++----- 2 files changed, 27 insertions(+), 11 deletions(-) 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 7a476fd6..ef4bc6b5 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 @@ -74,7 +74,7 @@ def method_kerosene(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_co for key, value in dict_ac_exchange.items(): # Check, if any value is 'None'. if (key not in unnecessary_values_for_kerosene) and (value is None): - # If any value is 'None', the calculation of the costs is impossible and the program aborted. + # If any value is 'None', the tank design is impossible and the program aborted. runtime_output.critical('Error: Tank design not possible due to missing parameter in aircraft exchange ' + 'file (' + key + ')! ' + 'Program aborted.') @@ -87,7 +87,7 @@ def method_kerosene(paths_and_names, routing_dict, dict_ac_exchange, dict_mod_co for key, value in dict_mod_config.items(): # Check, if any value is 'None'. if key in necessary_values_for_kerosene and value is None: - # If any value is 'None', the calculation of the costs is impossible and the program aborted. + # If any value is 'None', the tank design is impossible and the program aborted. runtime_output.critical('Error: Tank design not possible due to missing parameter in module configuration ' + 'file (' + key + ')! ' + 'Program aborted.') diff --git a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/methodliquidhydrogen.py b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/methodliquidhydrogen.py index a95bfb2d..de950548 100644 --- a/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/methodliquidhydrogen.py +++ b/tank_design/src/tube_and_wing/empirical/tank_design_tu_berlin/liquid_hydrogen/methodliquidhydrogen.py @@ -20,6 +20,7 @@ """Module providing calculation functions provided by the user.""" # Import standard modules. +import sys # Import own modules. from src.tube_and_wing.empirical.tank_design_tu_berlin.general.tankdesigntuberlin import tank_design_tu_berlin @@ -51,18 +52,33 @@ def method_liquid_hydrogen(paths_and_names, routing_dict, dict_ac_exchange, dict """ """ Preparation. """ + # Check if any values from aircraft exchange file are missing. + for key, value in dict_ac_exchange.items(): + # Check, if any value is 'None'. + if value is None: + # If any value is 'None', the tank design is impossible and the program aborted. + runtime_output.critical('Error: Tank design not possible due to missing parameter in aircraft exchange ' + + 'file (' + key + ')! ' + + 'Program aborted.') + sys.exit(1) + # Check if any necessary values from module configuration file are missing. + necessary_values_for_liquid_hydrogen = ['type_end_cap', 'internal_pressure', 'type_insulation', + 'material_insulation', 'thickness_insulation', 'density_insulation', + 'material_wall', 'density_wall', 'thickness_wall_calculation_method', + 'tensile_stress_allowable', 'mass_baffle', 'mass_pumps', + 'mass_vapor_barrier', 'factor_usable_diameter', 'factor_volume_allowance'] + for key, value in dict_mod_config.items(): + # Check, if any value is 'None'. + if key in necessary_values_for_liquid_hydrogen and value is None: + # If any value is 'None', the tank design is impossible and the program aborted. + runtime_output.critical('Error: Tank design not possible due to missing parameter in module configuration ' + + 'file (' + key + ')! ' + + 'Program aborted.') + sys.exit(1) + # Merge both data dictionaries. dict_design = dict_ac_exchange | dict_mod_config dict_design['energy_carrier'] = routing_dict['user_layer'] - # Iterate over dictionary to check for missing values -> not possible atm. - # for key, value in dict_design.items(): - # # Check, if any value is 'None'. - # if value is None: - # # If any value is 'None', the calculation of the costs is impossible and the program aborted. - # runtime_output.critical('Error: Tank design not possible due to missing parameter (' - # + key + ')! ' - # + 'Program aborted.') - # sys.exit(1) """Calculate tanks.""" # Tank design preparation. -- GitLab