def test_get_value_or_default(): """Test the function 'get_value_or_default'""" tixi = open_tixi(CPACS_IN_PATH) # Check if the correct value (float) is return from an xpath xpath = '/cpacs/vehicles/aircraft/model/reference/area' tixi, value = get_value_or_default(tixi, xpath, 2.0) assert value == 1.0 # Check if the correct value (text) is return from an xpath xpath = '/cpacs/vehicles/aircraft/model/name' tixi, value = get_value_or_default(tixi, xpath, 'name') assert value == 'Cpacs2Test' # Check if a non exitant xpath leads to its creation (integer) xpath = '/cpacs/vehicles/aircraft/model/reference/newSpan' tixi, value = get_value_or_default(tixi, xpath, 100) assert value == 100 new_elem = tixi.getDoubleElement(xpath) assert new_elem == 100 # Check if a non exitant xpath leads to its creation (float) xpath = '/cpacs/vehicles/aircraft/model/reference/newArea' tixi, value = get_value_or_default(tixi, xpath, 1000.0) assert value == 1000.0 new_elem = tixi.getDoubleElement(xpath) assert new_elem == 1000.0 # Check if a non exitant xpath leads to its creation (text) xpath = '/cpacs/vehicles/aircraft/model/reference/newRef' tixi, value = get_value_or_default(tixi, xpath, 'test') assert value == 'test' new_elem = tixi.getTextElement(xpath) assert new_elem == 'test'
def get_inside_dim(self, cpacs_path): """ Get user input from the CPACS file The function 'get_inside_dim' extracts from the CPACS file the required aircraft inside dimension, the code will use the default value when they are missing. Args: cpacs_path (str): Path to CPACS file """ tixi = open_tixi(cpacs_path) # Get inside dimension from the CPACS file if exit self.seat_width = get_value_or_default(tixi, GEOM_XPATH + '/seatWidth', 0.525) self.seat_length = get_value_or_default(tixi, GEOM_XPATH + '/seatLength', self.seat_length) self.aisle_width = get_value_or_default(tixi, GEOM_XPATH + '/aisleWidth', 0.42) self.fuse_thick = get_value_or_default(tixi, GEOM_XPATH + '/fuseThick', 6.63) self.toilet_length = get_value_or_default(tixi, GEOM_XPATH + '/toiletLength', self.toilet_length) close_tixi(tixi, cpacs_path)
def add_skin_friction(cpacs_path, cpacs_out_path): """ Function to add the skin frinction drag coeffienct to the CPACS file Function 'add_skin_friction' add the skin friction drag 'cd0' to the CPACS file, then it could be added to the drag coeffienct obtain with Euler calcualtions or other methods Args: cpacs_path (str): Path to CPACS file cpacs_out_path (str): Path to CPACS output file """ tixi = open_tixi(cpacs_path) tigl = open_tigl(tixi) wing_area_max, wing_span_max = get_largest_wing_dim(tixi, tigl) analysis_xpath = '/cpacs/toolspecific/CEASIOMpy/geometry/analysis' range_xpath = '/cpacs/toolspecific/CEASIOMpy/ranges' # Requiered input data from CPACS wetted_area = get_value(tixi, analysis_xpath + '/wettedArea') # Not requiered input data (a default value will be used if no # value has been found in the CPACS file) wing_area_xpath = analysis_xpath + '/wingArea' tixi, wing_area = get_value_or_default(tixi, wing_area_xpath, wing_area_max) if wing_area != wing_area_max: log.warning('Wing area found in the CPACS file /toolspecific is \ different from the one calculated from geometry, \ /toolspecific value will be used') wing_span_xpath = analysis_xpath + '/wingSpan' tixi, wing_span = get_value_or_default(tixi, wing_span_xpath, wing_span_max) if wing_span != wing_span_max: log.warning('Wing span found in the CPACS file /toolspecific is \ different from the one calculated from geometry, \ /toolspecific value will be used') cruise_alt_xpath = range_xpath + '/cruiseAltitude' tixi, cruise_alt = get_value_or_default(tixi, cruise_alt_xpath, 12000) cruise_mach_xpath = range_xpath + '/cruiseMach' tixi, cruise_mach = get_value_or_default(tixi, cruise_mach_xpath, 0.78) # Calculate Cd0 cd0 = estimate_skin_friction_coef(wetted_area,wing_area,wing_span, \ cruise_mach,cruise_alt) # Save Cd0 in the CPACS file cd0_xpath = '/cpacs/toolspecific/CEASIOMpy/aerodynamics/skinFriction/cd0' tixi = create_branch(tixi, cd0_xpath) tixi.updateDoubleElement(cd0_xpath, cd0, '%g') log.info('Skin friction drag coeffienct (cd0) has been saved in the \ CPACS file') close_tixi(tixi, cpacs_out_path)
def get_user_inputs(self, cpacs_path): """Take user inputs from the GUI.""" tixi = cpsf.open_tixi(CPACS_OPTIM_PATH) # Problem setup objectives = cpsf.get_value_or_default(tixi, OPTIM_XPATH + 'objective', 'cl') self.objective = objectives.split(';') self.minmax = cpsf.get_value_or_default(tixi, OPTIM_XPATH + 'minmax', 'max') # Global parameters self.driver = cpsf.get_value_or_default( tixi, OPTIM_XPATH + 'parameters/driver', 'COBYLA') self.max_iter = int( cpsf.get_value_or_default(tixi, OPTIM_XPATH + 'iterationNB', 200)) self.tol = float( cpsf.get_value_or_default(tixi, OPTIM_XPATH + 'tolerance', 1e-3)) self.save_iter = int( cpsf.get_value_or_default(tixi, OPTIM_XPATH + 'saving/perIter', 1)) # Specific DoE parameters self.doedriver = cpsf.get_value_or_default( tixi, OPTIM_XPATH + 'parameters/DoE/driver', 'uniform') self.samplesnb = int( cpsf.get_value_or_default(tixi, OPTIM_XPATH + 'parameters/DoE/sampleNB', 3)) # User specified configuration file path self.user_config = cpsf.get_value_or_default( tixi, OPTIM_XPATH + 'Config/filepath', '../Optimisation/Default_config.csv') cpsf.close_tixi(tixi, CPACS_OPTIM_PATH)
def get_cl(cpacs_path, cpacs_out_path): """ Function to calculate CL requiered as a function of the parameter found in the CPACS file. Function 'get_cl' find input value in the CPACS file, calculate the requiered CL (with calculate_cl) and save the CL value in /cpacs/toolspecific/CEASIOMpy/aerodynamics/su2/targetCL Args: cpacs_path (str): Path to CPACS file cpacs_out_path (str): Path to CPACS output file """ tixi = open_tixi(cpacs_path) # XPath definition model_xpath = '/cpacs/vehicles/aircraft/model' ref_area_xpath = model_xpath + '/reference/area' mtom_xpath = model_xpath + '/analyses/massBreakdown/designMasses/mTOM/mass' range_xpath = '/cpacs/toolspecific/CEASIOMpy/ranges' cruise_alt_xpath = range_xpath + '/cruiseAltitude' cruise_mach_xpath = range_xpath + '/cruiseMach' load_fact_xpath = range_xpath + '/loadFactor' su2_xpath = '/cpacs/toolspecific/CEASIOMpy/aerodynamics/su2' # Requiered input data from CPACS ref_area = get_value(tixi, ref_area_xpath) mtom = get_value(tixi, mtom_xpath) # Requiered input data that could be replace by a default value if missing cruise_alt = get_value_or_default(tixi, cruise_alt_xpath, 12000.0) cruise_mach = get_value_or_default(tixi, cruise_mach_xpath, 0.78) load_fact = get_value_or_default(tixi, load_fact_xpath, 1.05) # Get atmosphere from cruise altitude Atm = get_atmosphere(cruise_alt) # CL calculation target_cl = calculate_cl(ref_area, cruise_alt, cruise_mach, mtom, load_fact) # Save TargetCL create_branch(tixi, su2_xpath) create_branch(tixi, su2_xpath + '/targetCL') create_branch(tixi, su2_xpath + '/fixedCL') tixi.updateDoubleElement(su2_xpath + '/targetCL', target_cl, '%g') tixi.updateTextElement(su2_xpath + '/fixedCL', 'YES') log.info('Target CL has been saved in the CPACS file') close_tixi(tixi, cpacs_out_path)
def get_user_inputs(self, cpacs_path): """Take user inputs from the GUI.""" tixi = cpsf.open_tixi(CPACS_PREDICT_PATH) self.objectives = cpsf.get_value_or_default( tixi, PREDICT_XPATH + 'objective', ['cl']) self.user_config = cpsf.get_value_or_default( tixi, PREDICT_XPATH + 'Config/filepath', 'Variable_history.csv') self.aeromap_case = cpsf.get_value_or_default( tixi, PREDICT_XPATH + 'aeromap_case/IsCase', False) self.doedriver = cpsf.get_value_or_default( tixi, PREDICT_XPATH + 'aeromap_case/DoEdriver', 'LatinHypercube') self.samplesnb = cpsf.get_value_or_default( tixi, PREDICT_XPATH + 'aeromap_case/sampleNB', 3)
def get_wkdir_or_create_new(tixi): """ Function get the wkdir path from CPACS or create a new one Function 'get_wkdir_or_create_new' checks in the CPACS file if a working directory already exit for this run, if not, a new one is created and return. Args: tixi (handle): TIXI handle Returns: wkdir_path (str): Path to the active working directory """ WKDIR_XPATH = '/cpacs/toolspecific/CEASIOMpy/filesPath/wkdirPath' wkdir_path = cpsf.get_value_or_default(tixi, WKDIR_XPATH, '') if wkdir_path is '': wkdir_path = create_new_wkdir() cpsf.create_branch(tixi, WKDIR_XPATH) tixi.updateTextElement(WKDIR_XPATH, wkdir_path) else: # Check if the directory really exists if not os.path.isdir(wkdir_path): wkdir_path = create_new_wkdir() cpsf.create_branch(tixi, WKDIR_XPATH) tixi.updateTextElement(WKDIR_XPATH, wkdir_path) return wkdir_path
def load_surrogate(tixi): """Load a surrogate model object from file Using the pickle module, a surrogate model object is retrieved from a file provided by the user. Args: tixi (Tixi handle): Handle of the current CPACS. Returns: sm (object): The surrogate model. """ file = cpsf.get_value_or_default(tixi, SMUSE_XPATH + 'modelFile', '') log.info('Trying to open file' + file) try: f = open(file, 'rb') except: raise IOError('File could not be opened') Model = pickle.load(f) return Model
def test_get_value_or_default(): """Test the function 'get_value_or_default'""" tixi = cpsf.open_tixi(CPACS_IN_PATH) # Check if the correct value (float) is return from an xpath xpath = '/cpacs/vehicles/aircraft/model/reference/area' value = cpsf.get_value_or_default(tixi, xpath, 2.0) assert value == 1.0 # Check if the correct value (text) is return from an xpath xpath = '/cpacs/vehicles/aircraft/model/name' value = cpsf.get_value_or_default(tixi, xpath, 'name') assert value == 'Cpacs2Test' # Check if boolean are returned from an xpath or default value xpath = '/cpacs/toolspecific/testUtils/testCPACSFunctions/testBoolTrue' value = cpsf.get_value_or_default(tixi, xpath, False) assert value == True xpath = '/cpacs/toolspecific/testUtils/testCPACSFunctions/testBoolFalse' value = cpsf.get_value_or_default(tixi, xpath, True) assert value == False xpath = '/cpacs/toolspecific/testUtils/testCPACSFunctions/notExistTrue' value = cpsf.get_value_or_default(tixi, xpath, True) assert value == True xpath = '/cpacs/toolspecific/testUtils/testCPACSFunctions/notExistFalse' value = cpsf.get_value_or_default(tixi, xpath, False) assert value == False # Check if a non exitant xpath leads to its creation (integer) xpath = '/cpacs/vehicles/aircraft/model/reference/newSpan' value = cpsf.get_value_or_default(tixi, xpath, 100) assert value == 100 new_elem = tixi.getDoubleElement(xpath) assert new_elem == 100 # Check if a non exitant xpath leads to its creation (float) xpath = '/cpacs/vehicles/aircraft/model/reference/newArea' value = cpsf.get_value_or_default(tixi, xpath, 1000.0) assert value == 1000.0 new_elem = tixi.getDoubleElement(xpath) assert new_elem == 1000.0 # Check if a non exitant xpath leads to its creation (text) xpath = '/cpacs/vehicles/aircraft/model/reference/newRef' value = cpsf.get_value_or_default(tixi, xpath, 'test') assert value == 'test' new_elem = tixi.getTextElement(xpath) assert new_elem == 'test'
def check_aeromap(tixi): """Check if aeromap is not used to train the model. To avoid re-writting results on the aeromap that was used to train the model, the uid of the training aeromap is compared to the one that is given by the user to be computed. Stops the program if they match. Args: tixi (Tixi handle): Handle of the current CPACS. Returns: None. """ am_uid_use = cpsf.get_value_or_default(tixi, SMUSE_XPATH+'aeroMapUID', '') am_uid_train = cpsf.get_value_or_default(tixi, SMTRAIN_XPATH+'aeroMapUID', '') if am_uid_train == am_uid_use: sys.exit('Same aeromap that was used to create the model')
def get_normal_param(tixi, entry, outputs): """Add a variable to the optimisation dictionnary. It is checked if the variable has a user-specified initial value, else it will assign a default value or the variable will be excluded from the problem. Args: tixi (Tixi3 handle): Handle of the current CPACS file. entry (object): Current parameter object. Returns: None. """ value = '-' xpath = entry.xpath def_val = entry.default_value if not def_val: if entry.var_type in [float, int]: def_val = 0.0 else: def_val = '-' if entry.var_name not in banned_entries: value = cpsf.get_value_or_default(tixi, xpath, def_val) if entry.var_type == int: value = int(value) if not tls.is_digit(value): log.info('Not a digital value') value = '-' elif entry.var_type == bool: log.info('Boolean, not implemented yet') value = '-' # Ignores values that are not int or float if value != '-': value = str(value) tixi.updateTextElement(xpath, value) var['init'].append(value) var['xpath'].append(xpath) var['Name'].append(entry.var_name) tls.add_type(entry, outputs, objective, var) tls.add_bounds(value, var) log.info('Value : {}'.format(value)) log.info('Added to variable file')
def get_user_inputs(self): """Take user inputs from the GUI.""" cpacs_path = mif.get_toolinput_file_path('SMTrain') tixi = cpsf.open_tixi(cpacs_path) # Search working directory self.wkdir = cpsf.get_value_or_default(tixi, OPTWKDIR_XPATH, '') if self.wkdir == '': self.wkdir = ceaf.get_wkdir_or_create_new(tixi) + '/SM' if not os.path.isdir(self.wkdir): os.mkdir(self.wkdir) self.type = cpsf.get_value_or_default(tixi, SMTRAIN_XPATH + 'modelType', 'KRG') obj = cpsf.get_value_or_default(tixi, SMTRAIN_XPATH + 'objective', 'cl') self.objectives = re.split(';|,', obj) self.user_file = cpsf.get_value_or_default(tixi, SMTRAIN_XPATH + 'trainFile', '') if self.user_file == '': path = cpsf.get_value_or_default(tixi, OPTWKDIR_XPATH, '') if path != '': self.user_file = path + '/Variable_history.csv' self.data_repartition = cpsf.get_value_or_default( tixi, SMTRAIN_XPATH + 'trainingPercentage', 0.9) self.show_plots = cpsf.get_value_or_default( tixi, SMTRAIN_XPATH + 'showPlots', False) self.aeromap_case = cpsf.get_value_or_default( tixi, SMTRAIN_XPATH + 'useAeromap', False) self.aeromap_uid = cpsf.get_value_or_default( tixi, SMTRAIN_XPATH + 'aeroMapUID', '') cpsf.close_tixi(tixi, cpacs_path)
def check_aeromap(tixi, aeromap_uid): """ Check an aeroMap and add missing nodes Function 'check_aeromap' is similar to 'create_empty_aeromap' but for existing aeroMap. It will make sur that all node exist and create the missing ones. Args: tixi (handles): TIXI Handle of the CPACS file aeromap_uid (str): UID of the aeroPerformanceMap to create """ seconds = time.time() local_time = time.ctime(seconds) # If this aeroMap UID did not exist a new one will be create if not tixi.uIDCheckExists(aeromap_uid): log.warning(aeromap_uid + ' aeroMap has not been found!') log.warning('An empty one will be created') description = 'AeroMap created by CEASIOMpy ' + str(local_time) create_empty_aeromap(tixi, aeromap_uid, description) else: aeromap_xpath = tixi.uIDGetXPath(aeromap_uid) log.info('The aeroMap to check as been found') # Check name, description and boundary conditions cpsf.get_value_or_default(tixi, aeromap_xpath + '/name', aeromap_uid) description = 'AeroMap checked and utdated by CEASIOMpy ' + str( local_time) cpsf.get_value_or_default(tixi, aeromap_xpath + '/description', description) aeromap_bc_xpath = aeromap_xpath + '/boundaryConditions' cpsf.create_branch(tixi, aeromap_bc_xpath) cpsf.get_value_or_default(tixi, aeromap_bc_xpath + '/atmosphericModel', 'ISA') # Check AeroPerformanceMap, parameters and coefficients nodes apm_xpath = aeromap_xpath + '/aeroPerformanceMap' cpsf.create_branch(tixi, apm_xpath) #TODO: Replace by a for loop cpsf.create_branch(tixi, apm_xpath + '/altitude') cpsf.create_branch(tixi, apm_xpath + '/machNumber') cpsf.create_branch(tixi, apm_xpath + '/angleOfAttack') cpsf.create_branch(tixi, apm_xpath + '/angleOfSideslip') cpsf.create_branch(tixi, apm_xpath + '/cl') cpsf.create_branch(tixi, apm_xpath + '/cd') cpsf.create_branch(tixi, apm_xpath + '/cs') cpsf.create_branch(tixi, apm_xpath + '/cml') cpsf.create_branch(tixi, apm_xpath + '/cmd') cpsf.create_branch(tixi, apm_xpath + '/cms')
def add_entries(tixi, module_list): """Add the entries of all the modules. Search all the entries that can be used as problem parameters and fills the variable dictionary with the valid entries. Args: tixi (Tixi3 handler): Tixi handle of the CPACS file. """ use_am = cpsf.get_value_or_default(tixi, smu.SMUSE_XPATH+'AeroMapOnly', False) if 'SMUse' in module_list and use_am: get_aero_param(tixi) else: for mod_name, specs in mif.get_all_module_specs().items(): if specs and mod_name in module_list: if mod_name == 'SMUse': get_sm_vars(tixi) else: get_module_vars(tixi, specs)
def __init__(self, tabs, tixi, module_name): """Tab class Note: A tab will only be created if the module actually has any settings which are to be shown Args: tabs (TODO): TODO tixi (handle): Tixi handle module_name (str): String of the module name for which a tab is to be created """ self.var_dict = {} self.group_dict = {} self.module_name = module_name self.tabs = tabs self.tixi = tixi self.tab = tk.Frame(tabs, borderwidth=1) tabs.add(self.tab, text=module_name) # Get GUI dict from specs specs = mif.get_specs_for_module(module_name) self.gui_dict = specs.cpacs_inout.get_gui_dict() #canvas has replaced self.tab in the following lines space_label = tk.Label(self.tab, text=' ') space_label.grid(column=0, row=0) row_pos = 1 for key, (name, def_value, dtype, unit, xpath, description, group) in self.gui_dict.items(): # Create a LabelFrame for new groupe if group: if not group in self.group_dict: self.labelframe = tk.LabelFrame(self.tab, text=group) self.labelframe.grid(column=0, row=row_pos, columnspan=3,sticky= tk.W, padx=5, pady=5) self.group_dict[group] = self.labelframe parent = self.group_dict[group] else: # if not a group, use tab as parent parent = self.tab # Name label for variable if (name is not '__AEROMAP_SELECTION' and name is not '__AEROMAP_CHECHBOX'): self.name_label = tk.Label(parent, text= name) self.name_label.grid(column=0, row=row_pos, sticky= tk.W, padx=5, pady=5) # Type and Value if dtype is bool: self.var_dict[key] = tk.BooleanVar() value = cpsf.get_value_or_default(self.tixi,xpath,def_value) self.var_dict[key].set(value) bool_entry = tk.Checkbutton(parent, text='', variable=self.var_dict[key]) bool_entry.grid(column=1, row=row_pos, padx=5, pady=5) elif dtype is int: value = cpsf.get_value_or_default(self.tixi, xpath, def_value) self.var_dict[key] = tk.IntVar() self.var_dict[key].set(int(value)) value_entry = tk.Entry(parent, bd=2, textvariable=self.var_dict[key]) value_entry.grid(column=1, row=row_pos, padx=5, pady=5) elif dtype is float: value = cpsf.get_value_or_default(self.tixi, xpath, def_value) self.var_dict[key] = tk.DoubleVar() self.var_dict[key].set(value) value_entry = tk.Entry(parent, bd=2, textvariable=self.var_dict[key]) value_entry.grid(column=1, row=row_pos, padx=5, pady=5) elif dtype is 'pathtype': value = cpsf.get_value_or_default(self.tixi,xpath,def_value) self.var_dict[key] = tk.StringVar() self.var_dict[key].set(value) value_entry = tk.Entry(parent, textvariable=self.var_dict[key]) value_entry.grid(column=1, row=row_pos, padx=5, pady=5) self.key = key self.browse_button = tk.Button(parent, text="Browse", command=self._browse_file) self.browse_button.grid(column=2, row=row_pos, padx=5, pady=5) elif dtype is list: if name == '__AEROMAP_SELECTION': # Get the list of all AeroMaps self.aeromap_uid_list = apm.get_aeromap_uid_list(self.tixi) # Try to get the pre-selected AeroMap from the xpath try: selected_aeromap = cpsf.get_value(self.tixi,xpath) selected_aeromap_index = self.aeromap_uid_list.index(selected_aeromap) except: selected_aeromap = '' selected_aeromap_index = 0 self.labelframe = tk.LabelFrame(parent, text='Choose an AeroMap') self.labelframe.grid(column=0, row=row_pos, columnspan=3, sticky=tk.W, padx=5, pady=5) # The Combobox is directly use as the varaible self.var_dict[key] = ttk.Combobox(self.labelframe, values=self.aeromap_uid_list) self.var_dict[key].current(selected_aeromap_index) self.var_dict[key].grid(column=1, row=row_pos, padx=5, pady=5) elif name == '__AEROMAP_CHECHBOX': # Just to find back the name when data are saved self.var_dict[key] = None # __AEROMAP_CHECHBOX is a bit different, data are saved in their own dictionary self.aeromap_var_dict = {} # Get the list of all AeroMaps self.aeromap_uid_list = apm.get_aeromap_uid_list(self.tixi) self.labelframe = tk.LabelFrame(parent, text='Selecte AeroMap(s)') self.labelframe.grid(column=0, row=row_pos, columnspan=3, sticky=tk.W, padx=5, pady=5) # Try to get pre-selected AeroMaps from the xpath try: selected_aeromap = cpsf.get_string_vector(self.tixi,xpath) except: selected_aeromap = '' # Create one checkbox for each AeroMap for aeromap in self.aeromap_uid_list: self.aeromap_var_dict[aeromap] = tk.BooleanVar() #if aeromap in selected_aeromap: # For now, set all to True self.aeromap_var_dict[aeromap].set(True) aeromap_entry = tk.Checkbutton(self.labelframe,text=aeromap,variable=self.aeromap_var_dict[aeromap]) aeromap_entry.pack()#side=tk.TOP, anchor='w') else: # Other kind of list (not aeroMap) # 'def_value' will be the list of possibilies in this case # Try to get the pre-selected AeroMap from the xpath try: # TODO Should be retested... selected_value = cpsf.get_value(self.tixi,xpath) selected_value_index = def_value.index(selected_value) except: selected_value = '' selected_value_index = 0 # The Combobox is directly use as the varaible self.var_dict[key] = ttk.Combobox(parent, values=def_value) self.var_dict[key].current(selected_value_index) self.var_dict[key].grid(column=1, row=row_pos, padx=5, pady=5) else: value = cpsf.get_value_or_default(self.tixi,xpath,def_value) self.var_dict[key] = tk.StringVar() self.var_dict[key].set(value) value_entry = tk.Entry(parent, textvariable=self.var_dict[key]) value_entry.grid(column=1, row=row_pos, padx=5, pady=5) # Units if unit and unit != '1': unit_label = tk.Label(parent, text=pretty_unit(unit)) unit_label.grid(column=2, row=row_pos, padx=5, pady=5) row_pos += 1
def create_config(cpacs_path, cpacs_out_path, su2_mesh_path, config_output_path): """ Function to create configuration file for SU2 calculation Function 'create_config' create an SU2 configuration file from SU2 mesh data (marker) and CPACS file specific related parameter (/toolSpecific). For all other infomation the value from the default SU2 configuration file are used. A new configuration file will be saved in /ToolOutput/ToolOutput.cfg Source: * SU2 configuration file template https://github.com/su2code/SU2/blob/master/config_template.cfg Args: cpacs_path (str): Path to CPACS file cpacs_out_path (str): Path to CPACS output file su2_mesh_path (str): Path to SU2 mesh config_output_path (str): Path to the output configuration file """ DEFAULT_CONFIG_PATH = MODULE_DIR + '/files/DefaultConfig_v6.cfg' # Get value from CPACS tixi = open_tixi(cpacs_path) su2_xpath = '/cpacs/toolspecific/CEASIOMpy/aerodynamics/su2' # Reference values ref_xpath = '/cpacs/vehicles/aircraft/model/reference' ref_len = get_value(tixi, ref_xpath + '/length') ref_area = get_value(tixi, ref_xpath + '/area') # Fixed CL parameters fixed_cl_xpath = su2_xpath + '/fixedCL' target_cl_xpath = su2_xpath + '/targetCL' tixi, fixed_cl = get_value_or_default(tixi, fixed_cl_xpath, 'NO') tixi, target_cl = get_value_or_default(tixi, target_cl_xpath, 1.0) if fixed_cl == 'NO': # Get value from the aeroMap (1 point) active_aeroMap_xpath = su2_xpath + '/aeroMapUID' aeroMap_uid = get_value(tixi, active_aeroMap_xpath) aeroMap_path = tixi.uIDGetXPath(aeroMap_uid) apm_path = aeroMap_path + '/aeroPerformanceMap' #State = get_states(tixi,apm_path) #alt = State.alt_list alt = get_value(tixi, apm_path + '/altitude') mach = get_value(tixi, apm_path + '/machNumber') aoa = get_value(tixi, apm_path + '/angleOfAttack') aos = get_value(tixi, apm_path + '/angleOfSideslip') else: range_xpath = '/cpacs/toolspecific/CEASIOMpy/ranges' cruise_alt_xpath = range_xpath + '/cruiseAltitude' cruise_mach_xpath = range_xpath + '/cruiseMach' # value corresponding to fix CL calulation aoa = 0.0 # Will not be used aos = 0.0 tixi, mach = get_value_or_default(tixi, cruise_mach_xpath, 0.78) tixi, alt = get_value_or_default(tixi, cruise_alt_xpath, 12000) Atm = get_atmosphere(alt) pressure = Atm.pres temp = Atm.temp # Settings settings_xpath = '/cpacs/toolspecific/CEASIOMpy/aerodynamics/su2/settings' max_iter_xpath = settings_xpath + '/maxIter' cfl_nb_xpath = settings_xpath + '/cflNumber' mg_level_xpath = settings_xpath + '/multigridLevel' tixi, max_iter = get_value_or_default(tixi, max_iter_xpath, 200) tixi, cfl_nb = get_value_or_default(tixi, cfl_nb_xpath, 1.0) tixi, mg_level = get_value_or_default(tixi, mg_level_xpath, 3) # Mesh Marker bc_wall_xpath = '/cpacs/toolspecific/CEASIOMpy/aerodynamics/su2/boundaryConditions/wall' bc_wall_list = get_mesh_marker(su2_mesh_path) tixi = create_branch(tixi, bc_wall_xpath) bc_wall_str = ';'.join(bc_wall_list) tixi.updateTextElement(bc_wall_xpath, bc_wall_str) close_tixi(tixi, cpacs_out_path) # Open default configuration file try: config_file_object = open(DEFAULT_CONFIG_PATH, 'r') config_file_lines = config_file_object.readlines() config_file_object.close() log.info('Default configuration file has been found and read') except Exception: log.exception('Problem to open or read default configuration file') # Create a dictionary with all the parameters from the default config file config_dict = {} for line in config_file_lines: if '=' in line: (key, val) = line.split('=') if val.endswith('\n'): val = val[:-1] config_dict[key] = val config_dict_modif = config_dict # General parmeters config_dict_modif['MESH_FILENAME'] = su2_mesh_path config_dict_modif['REF_LENGTH'] = ref_len config_dict_modif['REF_AREA'] = ref_area # Settings config_dict_modif['EXT_ITER'] = int(max_iter) config_dict_modif['CFL_NUMBER'] = cfl_nb config_dict_modif['MGLEVEL'] = int(mg_level) config_dict_modif['AOA'] = aoa config_dict_modif['SIDESLIP_ANGLE'] = aos config_dict_modif['MACH_NUMBER'] = mach config_dict_modif['FREESTREAM_PRESSURE'] = pressure config_dict_modif['FREESTREAM_TEMPERATURE'] = temp # If calculation at CL fix (AOA will not be taken into account) config_dict_modif['FIXED_CL_MODE'] = fixed_cl config_dict_modif['TARGET_CL'] = target_cl config_dict_modif['DCL_DALPHA'] = '0.1' config_dict_modif['UPDATE_ALPHA'] = '8' config_dict_modif['ITER_DCL_DALPHA'] = '80' # Mesh Marker bc_wall_str = '(' + ','.join(bc_wall_list) + ')' config_dict_modif['MARKER_EULER'] = bc_wall_str config_dict_modif['MARKER_FAR'] = ' (Farfield)' config_dict_modif['MARKER_SYM'] = ' (0)' config_dict_modif['MARKER_PLOTTING'] = bc_wall_str config_dict_modif['MARKER_MONITORING'] = bc_wall_str config_dict_modif['MARKER_MOVING'] = bc_wall_str # Change value if needed or add new parameters in the config file for key, value in config_dict_modif.items(): line_nb = 0 # Double loop! There is probably a possibility to do something better. for i, line in enumerate(config_file_lines): if '=' in line: (key_def, val_def) = line.split('=') if key == key_def: line_nb = i break if not line_nb: config_file_lines.append(str(key) + ' = ' + str(value) + '\n') else: if val_def != config_dict_modif[key]: config_file_lines[line_nb] = str(key) + ' = ' \ + str(config_dict_modif[key]) + '\n' config_file_new = open(config_output_path, 'w') config_file_new.writelines(config_file_lines) config_file_new.close() log.info('ToolOutput.cfg has been written in /ToolOutput.')
def run_mesh_deformation(tixi, wkdir): """Function to run all the configuration files with SU2_DEF. Function 'run_mesh_deformation' will check in all config file directory and run SU2_DEF for each config file in order. Args: tixi (handles): TIXI Handle wkdir (str): Path to the working directory """ log.info('All mesh deromation will be preformed.') mesh_dir = os.path.join(wkdir, 'MESH') if not os.path.exists(mesh_dir): raise OSError('The MESH directory : ' + mesh_dir + 'does not exit!') os.chdir(mesh_dir) su2_def_mesh_list = [] ted_dir_list = [dir for dir in os.listdir(mesh_dir) if '_TED_' in dir] # Get number of proc to use nb_proc = cpsf.get_value_or_default(tixi, SU2_XPATH + '/settings/nbProc', 1) # Iterate in all TED directory for dir in sorted(ted_dir_list): ted_dir = os.path.join(mesh_dir, dir) os.chdir(ted_dir) cfg_file_list = [ file for file in os.listdir(ted_dir) if 'Config' in file ] # Run all the config file in the directory with SU2_DEF, in alphabetical # order to respect the order of execution (DEF,ROT_,ROT_sym) for cfg_file in sorted(cfg_file_list): if os.path.isfile(cfg_file): su2f.run_soft('SU2_DEF', cfg_file, ted_dir, nb_proc) else: raise ValueError("Not correct configuration file to run!") tmp_su2_mesh_list = [ file for file in os.listdir(ted_dir) if '.su2' in file ] # Copy in the completly deform mesh in the MESH directory for su2_mesh in tmp_su2_mesh_list: if not su2_mesh.startswith('_'): shutil.copyfile(su2_mesh, os.path.join('..', su2_mesh)) log.info(su2_mesh + ' mesh has been copied in the MESH dir.') su2_def_mesh_list.append(su2_mesh) # Remove all SU2 mesh from the config folder (to save space) os.remove(su2_mesh) log.info(su2_mesh + ' mesh has been deleted from the temp mesh.') # Add the list of available SU2 deformed mesh in the CPACS file su2_def_mesh_xpath = SU2_XPATH + '/availableDeformedMesh' cpsf.add_string_vector(tixi, su2_def_mesh_xpath, su2_def_mesh_list)
def generate_su2_config(cpacs_path, cpacs_out_path, wkdir): """Function to create SU2 confif file. Function 'generate_su2_config' reads data in the CPACS file and generate configuration files for one or multible flight conditions (alt,mach,aoa,aos) Source: * SU2 config template: https://github.com/su2code/SU2/blob/master/config_template.cfg Args: cpacs_path (str): Path to CPACS file cpacs_out_path (str):Path to CPACS output file wkdir (str): Path to the working directory """ # Get value from CPACS tixi = cpsf.open_tixi(cpacs_path) tigl = cpsf.open_tigl(tixi) # Get SU2 mesh path su2_mesh_xpath = '/cpacs/toolspecific/CEASIOMpy/filesPath/su2Mesh' su2_mesh_path = cpsf.get_value(tixi,su2_mesh_xpath) # Get reference values ref_xpath = '/cpacs/vehicles/aircraft/model/reference' ref_len = cpsf.get_value(tixi,ref_xpath + '/length') ref_area = cpsf.get_value(tixi,ref_xpath + '/area') ref_ori_moment_x = cpsf.get_value_or_default(tixi,ref_xpath+'/point/x',0.0) ref_ori_moment_y = cpsf.get_value_or_default(tixi,ref_xpath+'/point/y',0.0) ref_ori_moment_z = cpsf.get_value_or_default(tixi,ref_xpath+'/point/z',0.0) # Get SU2 settings settings_xpath = SU2_XPATH + '/settings' max_iter_xpath = settings_xpath + '/maxIter' max_iter = cpsf.get_value_or_default(tixi, max_iter_xpath,200) cfl_nb_xpath = settings_xpath + '/cflNumber' cfl_nb = cpsf.get_value_or_default(tixi, cfl_nb_xpath,1.0) mg_level_xpath = settings_xpath + '/multigridLevel' mg_level = cpsf.get_value_or_default(tixi, mg_level_xpath,3) # Mesh Marker bc_wall_xpath = SU2_XPATH + '/boundaryConditions/wall' bc_wall_list = su2f.get_mesh_marker(su2_mesh_path) cpsf.create_branch(tixi, bc_wall_xpath) bc_wall_str = ';'.join(bc_wall_list) tixi.updateTextElement(bc_wall_xpath,bc_wall_str) # Fixed CL parameters fixed_cl_xpath = SU2_XPATH + '/fixedCL' fixed_cl = cpsf.get_value_or_default(tixi, fixed_cl_xpath,'NO') target_cl_xpath = SU2_XPATH + '/targetCL' target_cl = cpsf.get_value_or_default(tixi, target_cl_xpath,1.0) if fixed_cl == 'NO': active_aeroMap_xpath = SU2_XPATH + '/aeroMapUID' aeromap_uid = cpsf.get_value(tixi,active_aeroMap_xpath) log.info('Configuration file for ""' + aeromap_uid + '"" calculation will be created.') # Get parameters of the aeroMap (alt,ma,aoa,aos) Param = apmf.get_aeromap(tixi,aeromap_uid) param_count = Param.get_count() if param_count >= 1: alt_list = Param.alt mach_list = Param.mach aoa_list = Param.aoa aos_list = Param.aos else: raise ValueError('No parametre have been found in the aeroMap!') else: # if fixed_cl == 'YES': log.info('Configuration file for fixed CL calculation will be created.') range_xpath = '/cpacs/toolspecific/CEASIOMpy/ranges' # Parameters fixed CL calulation param_count = 1 # These parameters will not be used aoa_list = [0.0] aos_list = [0.0] cruise_mach_xpath= range_xpath + '/cruiseMach' mach = cpsf.get_value_or_default(tixi,cruise_mach_xpath,0.78) mach_list = [mach] cruise_alt_xpath= range_xpath + '/cruiseAltitude' alt = cpsf.get_value_or_default(tixi,cruise_alt_xpath,12000) alt_list = [alt] aeromap_uid = 'aeroMap_fixedCL_SU2' description = 'AeroMap created for SU2 fixed CL value of: ' + str(target_cl) apmf.create_empty_aeromap(tixi, aeromap_uid, description) Parameters = apmf.AeroCoefficient() Parameters.alt = alt_list Parameters.mach = mach_list Parameters.aoa = aoa_list Parameters.aos = aos_list apmf.save_parameters(tixi,aeromap_uid,Parameters) tixi.updateTextElement(SU2_XPATH+ '/aeroMapUID',aeromap_uid) # Get and modify the default configuration file cfg = su2f.read_config(DEFAULT_CONFIG_PATH) # General parmeters cfg['REF_LENGTH'] = ref_len cfg['REF_AREA'] = ref_area cfg['REF_ORIGIN_MOMENT_X'] = ref_ori_moment_x cfg['REF_ORIGIN_MOMENT_Y'] = ref_ori_moment_y cfg['REF_ORIGIN_MOMENT_Z'] = ref_ori_moment_z # Settings cfg['INNER_ITER'] = int(max_iter) cfg['CFL_NUMBER'] = cfl_nb cfg['MGLEVEL'] = int(mg_level) # Fixed CL mode (AOA will not be taken into account) cfg['FIXED_CL_MODE'] = fixed_cl cfg['TARGET_CL'] = target_cl cfg['DCL_DALPHA'] = '0.1' cfg['UPDATE_AOA_ITER_LIMIT'] = '50' cfg['ITER_DCL_DALPHA'] = '80' # TODO: correct value for the 3 previous parameters ?? # Mesh Marker bc_wall_str = '(' + ','.join(bc_wall_list) + ')' cfg['MARKER_EULER'] = bc_wall_str cfg['MARKER_FAR'] = ' (Farfield)' # TODO: maybe make that a variable cfg['MARKER_SYM'] = ' (0)' # TODO: maybe make that a variable? cfg['MARKER_PLOTTING'] = bc_wall_str cfg['MARKER_MONITORING'] = bc_wall_str cfg['MARKER_MOVING'] = '( NONE )' # TODO: when do we need to define MARKER_MOVING? cfg['DV_MARKER'] = bc_wall_str # Parameters which will vary for the different cases (alt,mach,aoa,aos) for case_nb in range(param_count): cfg['MESH_FILENAME'] = su2_mesh_path alt = alt_list[case_nb] mach = mach_list[case_nb] aoa = aoa_list[case_nb] aos = aos_list[case_nb] Atm = get_atmosphere(alt) pressure = Atm.pres temp = Atm.temp cfg['MACH_NUMBER'] = mach cfg['AOA'] = aoa cfg['SIDESLIP_ANGLE'] = aos cfg['FREESTREAM_PRESSURE'] = pressure cfg['FREESTREAM_TEMPERATURE'] = temp cfg['ROTATION_RATE'] = '0.0 0.0 0.0' config_file_name = 'ConfigCFD.cfg' case_dir_name = ''.join(['Case',str(case_nb).zfill(2), '_alt',str(alt), '_mach',str(round(mach,2)), '_aoa',str(round(aoa,1)), '_aos',str(round(aos,1))]) case_dir_path = os.path.join(wkdir,case_dir_name) if not os.path.isdir(case_dir_path): os.mkdir(case_dir_path) config_output_path = os.path.join(wkdir,case_dir_name,config_file_name) su2f.write_config(config_output_path,cfg) # Damping derivatives damping_der_xpath = SU2_XPATH + '/options/clalculateDampingDerivatives' damping_der = cpsf.get_value_or_default(tixi,damping_der_xpath,False) if damping_der: rotation_rate_xpath = SU2_XPATH + '/options/rotationRate' rotation_rate = cpsf.get_value_or_default(tixi,rotation_rate_xpath,1.0) cfg['GRID_MOVEMENT'] = 'ROTATING_FRAME' cfg['ROTATION_RATE'] = str(rotation_rate) + ' 0.0 0.0' os.mkdir(os.path.join(wkdir,case_dir_name+'_dp')) config_output_path = os.path.join(wkdir,case_dir_name+'_dp',config_file_name) su2f.write_config(config_output_path,cfg) cfg['ROTATION_RATE'] = '0.0 ' + str(rotation_rate) + ' 0.0' os.mkdir(os.path.join(wkdir,case_dir_name+'_dq')) config_output_path = os.path.join(wkdir,case_dir_name+'_dq',config_file_name) su2f.write_config(config_output_path,cfg) cfg['ROTATION_RATE'] = '0.0 0.0 ' + str(rotation_rate) os.mkdir(os.path.join(wkdir,case_dir_name+'_dr')) config_output_path = os.path.join(wkdir,case_dir_name+'_dr',config_file_name) su2f.write_config(config_output_path,cfg) log.info('Damping derivatives cases directory has been created.') # Control surfaces deflections control_surf_xpath = SU2_XPATH + '/options/clalculateCotrolSurfacesDeflections' control_surf = cpsf.get_value_or_default(tixi,control_surf_xpath,False) if control_surf: # Get deformed mesh list su2_def_mesh_xpath = SU2_XPATH + '/availableDeformedMesh' if tixi.checkElement(su2_def_mesh_xpath): su2_def_mesh_list = cpsf.get_string_vector(tixi,su2_def_mesh_xpath) else: log.warning('No SU2 deformed mesh has been found!') su2_def_mesh_list = [] for su2_def_mesh in su2_def_mesh_list: mesh_path = os.path.join(wkdir,'MESH',su2_def_mesh) config_dir_path = os.path.join(wkdir,case_dir_name+'_'+su2_def_mesh.split('.')[0]) os.mkdir(config_dir_path) cfg['MESH_FILENAME'] = mesh_path config_file_name = 'ConfigCFD.cfg' config_output_path = os.path.join(wkdir,config_dir_path,config_file_name) su2f.write_config(config_output_path,cfg) # TODO: change that, but if it is save in tooloutput it will be erease by results... cpsf.close_tixi(tixi,cpacs_path)
def main(): log.info("Running PyTornado...") # ===== CPACS inout and output paths ===== MODULE_DIR = os.path.dirname(os.path.abspath(__file__)) cpacs_in_path = mi.get_toolinput_file_path(MODULE_NAME) cpacs_out_path = mi.get_tooloutput_file_path(MODULE_NAME) # ===== Delete old working directories ===== settings_from_CPACS = get_pytornado_settings_from_CPACS(cpacs_in_path) if settings_from_CPACS is not None: if settings_from_CPACS.get('deleteOldWKDIRs', False): wkdirs = glob(os.path.join(DIR_MODULE, 'wkdir_*')) for wkdir in wkdirs: shutil.rmtree(wkdir, ignore_errors=True) # ===== Paths ===== dir_pyt_wkdir = os.path.join(DIR_MODULE, 'wkdir_temp') dir_pyt_aircraft = os.path.join(dir_pyt_wkdir, 'aircraft') dir_pyt_settings = os.path.join(dir_pyt_wkdir, 'settings') dir_pyt_results = os.path.join(dir_pyt_wkdir, '_results') file_pyt_aircraft = os.path.join(dir_pyt_aircraft, 'ToolInput.xml') file_pyt_settings = os.path.join(dir_pyt_settings, 'cpacs_run.json') # ===== Make directories ===== Path(dir_pyt_wkdir).mkdir(parents=True, exist_ok=True) Path(dir_pyt_aircraft).mkdir(parents=True, exist_ok=True) Path(dir_pyt_settings).mkdir(parents=True, exist_ok=True) Path(dir_pyt_results).mkdir(parents=True, exist_ok=True) # ===== Setup ===== shutil.copy(src=cpacs_in_path, dst=file_pyt_aircraft) mi.check_cpacs_input_requirements(cpacs_in_path) # ===== Get PyTornado settings ===== cpacs_settings = get_pytornado_settings(cpacs_in_path) with open(file_pyt_settings, "w") as fp: dump_pretty_json(cpacs_settings, fp) # ===== PyTornado analysis ===== pytornado = import_pytornado('pytornado.stdfun.run') #pytornado.standard_run(args=pytornado.StdRunArgs(run=file_pyt_settings, verbose=True)) results = pytornado.standard_run( args=pytornado.StdRunArgs(run=file_pyt_settings, verbose=True)) # ===== Extract load ===== tixi = cpsf.open_tixi(cpacs_in_path) extract_loads_xpath = '/cpacs/toolspecific/pytornado/save_results/extractLoads' extract_loads = cpsf.get_value_or_default(tixi, extract_loads_xpath, False) if extract_loads: _get_load_fields(results, dir_pyt_results) # ===== Clean up ===== shutil.copy(src=file_pyt_aircraft, dst=cpacs_out_path) wkdir = ceaf.get_wkdir_or_create_new(tixi) dst_pyt_wkdir = os.path.join( wkdir, 'CFD', 'PyTornado', f"wkdir_{datetime.strftime(datetime.now(), '%F_%H%M%S')}") shutil.copytree(src=dir_pyt_wkdir, dst=dst_pyt_wkdir) shutil.rmtree(dir_pyt_wkdir, ignore_errors=True) log.info("PyTornado analysis completed")
def dynamic_stability_analysis(cpacs_path, cpacs_out_path): """Function to analyse a full Aeromap Function 'dynamic_stability_analysis' analyses longitudinal dynamic stability and directionnal dynamic. Args: cpacs_path (str): Path to CPACS file cpacs_out_path (str):Path to CPACS output file plot (boolean): Choise to plot graph or not Returns: (#TODO put that in the documentation) * Adrvertisements certifying if the aircraft is stable or Not * In case of longitudinal dynamic UNstability or unvalid test on data: - Plot cms VS aoa for constant Alt, Mach and different aos - Plot cms VS aoa for const alt and aos and different mach - plot cms VS aoa for constant mach, AOS and different altitudes * In case of directionnal dynamic UNstability or unvalid test on data: - Pcot cml VS aos for constant Alt, Mach and different aoa - Plot cml VS aos for const alt and aoa and different mach - plot cml VS aos for constant mach, AOA and different altitudes * Plot one graph of cruising angles of attack for different mach and altitudes Make the following tests: * Check the CPACS path * For longitudinal dynamic stability analysis: - If there is more than one angle of attack for a given altitude, mach, aos - If cml values are only zeros for a given altitude, mach, aos - If there one aoa value which is repeated for a given altitude, mach, aos * For directionnal dynamic stability analysis: - If there is more than one angle of sideslip for a given altitude, mach, aoa - If cms values are only zeros for a given altitude, mach, aoa - If there one aos value which is repeated for a given altitude, mach, aoa """ # XPATH definition aeromap_uid_xpath = DYNAMIC_ANALYSIS_XPATH + '/aeroMapUid' aircraft_class_xpath = DYNAMIC_ANALYSIS_XPATH + '/class' # Classes 1 2 3 4 small, heavy ... aircraft_cathegory_xpath = DYNAMIC_ANALYSIS_XPATH + '/category' # flight phase A B C selected_mass_config_xpath = DYNAMIC_ANALYSIS_XPATH + '/massConfiguration' longi_analysis_xpath = DYNAMIC_ANALYSIS_XPATH + '/instabilityModes/longitudinal' direc_analysis_xpath = DYNAMIC_ANALYSIS_XPATH + '/instabilityModes/lateralDirectional' show_plot_xpath = DYNAMIC_ANALYSIS_XPATH + '/showPlots' save_plot_xpath = DYNAMIC_ANALYSIS_XPATH + '/savePlots' model_xpath = '/cpacs/vehicles/aircraft/model' ref_area_xpath = model_xpath + '/reference/area' ref_length_xpath = model_xpath + '/reference/length' flight_qualities_case_xpath = model_xpath + '/analyses/flyingQualities/fqCase' masses_location_xpath = model_xpath + '/analyses/massBreakdown/designMasses' # aircraft_class_xpath = flight_qualities_case_xpath + '/class' # Classes 1 2 3 4 small, heavy ... # aircraft_cathegory_xpath = flight_qualities_case_xpath + '/cathegory' # flight phase A B C # Ask user flight path angles : gamma_e thrust_available = None # Thrust data are not available flight_path_angle_deg = [ 0 ] # [-15,-10,-5,0,5,10,15] # The user should have the choice to select them !!!!!!!!!!!!!!!!!!!! flight_path_angle = [ angle * (np.pi / 180) for angle in flight_path_angle_deg ] # flight_path_angle in [rad] tixi = cpsf.open_tixi(cpacs_path) # Get aeromap uid aeromap_uid = cpsf.get_value(tixi, aeromap_uid_xpath) log.info('The following aeroMap will be analysed: ' + aeromap_uid) # Mass configuration: (Maximum landing mass, Maximum ramp mass (the maximum weight authorised for the ground handling), Take off mass, Zero Fuel mass) mass_config = cpsf.get_value(tixi, selected_mass_config_xpath) log.info('The aircraft mass configuration used for analysis is: ' + mass_config) # Analyses to do : longitudinal / Lateral-Directional longitudinal_analysis = cpsf.get_value(tixi, longi_analysis_xpath) lateral_directional_analysis = False # lateral_directional_analysis = cpsf.get_value(tixi, direc_analysis_xpath ) # Plots configuration with Setting GUI show_plots = cpsf.get_value_or_default(tixi, show_plot_xpath, False) save_plots = cpsf.get_value_or_default(tixi, save_plot_xpath, False) mass_config_xpath = masses_location_xpath + '/' + mass_config if tixi.checkElement(mass_config_xpath): mass_xpath = mass_config_xpath + '/mass' I_xx_xpath = mass_config_xpath + '/massInertia/Jxx' I_yy_xpath = mass_config_xpath + '/massInertia/Jyy' I_zz_xpath = mass_config_xpath + '/massInertia/Jzz' I_xz_xpath = mass_config_xpath + '/massInertia/Jxz' else: raise ValueError( 'The mass configuration : {} is not defined in the CPACS file !!!'. format(mass_config)) s = cpsf.get_value( tixi, ref_area_xpath ) # Wing area : s for non-dimonsionalisation of aero data. mac = cpsf.get_value( tixi, ref_length_xpath ) # ref length for non dimensionalisation, Mean aerodynamic chord: mac, # TODO: check that b = s / mac # TODO: find a way to get that xh = 10 # distance Aircaft cg-ac_horizontal-tail-plane. m = cpsf.get_value(tixi, mass_xpath) # aircraft mass dimensional I_xx = cpsf.get_value(tixi, I_xx_xpath) # X inertia dimensional I_yy = cpsf.get_value(tixi, I_yy_xpath) # Y inertia dimensional I_zz = cpsf.get_value(tixi, I_zz_xpath) # Z inertia dimensional I_xz = cpsf.get_value(tixi, I_xz_xpath) # XZ inertia dimensional aircraft_class = cpsf.get_value( tixi, aircraft_class_xpath) # aircraft class 1 2 3 4 flight_phase = cpsf.get_string_vector( tixi, aircraft_cathegory_xpath)[0] # Flight phase A B C Coeffs = apmf.get_aeromap( tixi, aeromap_uid ) # Warning: Empty uID found! This might lead to unknown errors! alt_list = Coeffs.alt mach_list = Coeffs.mach aoa_list = Coeffs.aoa aos_list = Coeffs.aos cl_list = Coeffs.cl cd_list = Coeffs.cd cs_list = Coeffs.cs cml_list = Coeffs.cml cms_list = Coeffs.cms cmd_list = Coeffs.cmd dcsdrstar_list = Coeffs.dcsdrstar dcsdpstar_list = Coeffs.dcsdpstar dcldqstar_list = Coeffs.dcldqstar dcmsdqstar_list = Coeffs.dcmsdqstar dcddqstar_list = Coeffs.dcddqstar dcmldqstar_list = Coeffs.dcmldqstar dcmddpstar_list = Coeffs.dcmddpstar dcmldpstar_list = Coeffs.dcmldpstar dcmldrstar_list = Coeffs.dcmldrstar dcmddrstar_list = Coeffs.dcmddrstar # All different vallues with only one occurence alt_unic = get_unic(alt_list) mach_unic = get_unic(mach_list) aos_unic = get_unic(aos_list) aoa_unic = get_unic(aoa_list) # TODO get from CPACS incrementalMap = False for alt in alt_unic: idx_alt = [i for i in range(len(alt_list)) if alt_list[i] == alt] Atm = get_atmosphere(alt) g = Atm.grav a = Atm.sos rho = Atm.dens for mach in mach_unic: print('Mach : ', mach) idx_mach = [ i for i in range(len(mach_list)) if mach_list[i] == mach ] u0, m_adim, i_xx, i_yy, i_zz, i_xz = adimensionalise( a, mach, rho, s, b, mac, m, I_xx, I_yy, I_zz, I_xz) # u0 is V0 in Cook # Hyp: trim condition when: ( beta = 0 and dCm/dalpha = 0) OR ( aos=0 and dcms/daoa = 0 ) if 0 not in aos_unic: log.warning( 'The aircraft can not be trimmed (requiring symetric flight condition) as beta never equal to 0 for Alt = {}, mach = {}' .format(alt, mach)) else: idx_aos = [i for i in range(len(aos_list)) if aos_list[i] == 0] find_index = get_index(idx_alt, idx_mach, idx_aos) # If there is only one data at (alt, mach, aos) then dont make stability anlysis if len(find_index) <= 1: log.warning( 'Not enough data at : Alt = {} , mach = {}, aos = 0, can not perform stability analysis' .format(alt, mach)) # If there is at leat 2 data at (alt, mach, aos) then, make stability anlysis else: # Calculate trim conditions cms = [] aoa = [] cl = [] for index in find_index: cms.append(cms_list[index]) aoa.append(aoa_list[index] * np.pi / 180) cl.append(cl_list[index]) cl_required = (m * g) / (0.5 * rho * u0**2 * s) (trim_aoa, idx_trim_before, idx_trim_after, ratio) = trim_condition( alt, mach, cl_required, cl, aoa, ) if trim_aoa: trim_aoa_deg = trim_aoa * 180 / np.pi trim_cms = interpolation(cms, idx_trim_before, idx_trim_after, ratio) pitch_moment_derivative_rad = ( cms[idx_trim_after] - cms[idx_trim_before]) / ( aoa[idx_trim_after] - aoa[idx_trim_before]) pitch_moment_derivative_deg = pitch_moment_derivative_rad / ( 180 / np.pi) # Find incremental cms if incrementalMap: for index, mach_number in enumerate(mach_unic, 0): if mach_number == mach: mach_index = index dcms_before = dcms_list[mach_index * len(aoa_unic) + idx_trim_before] dcms_after = dcms_list[mach_index * len(aoa_unic) + idx_trim_after] dcms = dcms_before + ratio * (dcms_after - dcms_before) trim_elevator = -trim_cms / dcms # Trim elevator deflection in [°] else: dcms = None trim_elevator = None else: trim_aoa_deg = None trim_cms = None pitch_moment_derivative_deg = None dcms = None trim_elevator = None # Longitudinal dynamic stability, # Stability analysis if longitudinal_analysis and trim_cms: cl = [] cd = [] dcldqstar = [] dcddqstar = [] dcmsdqstar = [] for index in find_index: cl.append(cl_list[index]) cd.append(cd_list[index]) dcldqstar.append(dcldqstar_list[index]) dcddqstar.append(dcddqstar_list[index]) dcmsdqstar.append(dcmsdqstar_list[index]) # Trimm variables cd0 = interpolation(cd, idx_trim_before, idx_trim_after, ratio) # Dragg coeff at trim cl0 = interpolation(cl, idx_trim_before, idx_trim_after, ratio) # Lift coeff at trim cl_dividedby_cd_trim = cl0 / cd0 # cl/cd ratio at trim, at trim aoa # Lift & drag coefficient derivative with respect to AOA at trimm cl_alpha0 = (cl[idx_trim_after] - cl[idx_trim_before] ) / (aoa[idx_trim_after] - aoa[idx_trim_before]) cd_alpha0 = (cd[idx_trim_after] - cd[idx_trim_before] ) / (aoa[idx_trim_after] - aoa[idx_trim_before]) print(idx_trim_before, idx_trim_after, ratio) dcddqstar0 = interpolation(dcddqstar, idx_trim_before, idx_trim_after, ratio) # x_q dcldqstar0 = interpolation(dcldqstar, idx_trim_before, idx_trim_after, ratio) # z_q dcmsdqstar0 = interpolation(dcmsdqstar, idx_trim_before, idx_trim_after, ratio) # m_q cm_alpha0 = trim_cms # Speed derivatives if there is at least 2 distinct mach values if len(mach_unic) >= 2: dcddm0 = speed_derivative_at_trim( cd_list, mach, mach_list, mach_unic, idx_alt, aoa_list, aos_list, idx_trim_before, idx_trim_after, ratio) if dcddm0 == None: dcddm0 = 0 log.warning( 'Not enough data to determine dcddm or (Cd_mach) at trim condition at Alt = {}, mach = {}, aoa = {}, aos = 0. Assumption: dcddm = 0' .format(alt, mach, round(trim_aoa_deg, 2))) dcldm0 = speed_derivative_at_trim( cl_list, mach, mach_list, mach_unic, idx_alt, aoa_list, aos_list, idx_trim_before, idx_trim_after, ratio) if dcldm0 == None: dcldm0 = 0 log.warning( 'Not enough data to determine dcldm (Cl_mach) at trim condition at Alt = {}, mach = {}, aoa = {}, aos = 0. Assumption: dcldm = 0' .format(alt, mach, round(trim_aoa_deg, 2))) else: dcddm0 = 0 dcldm0 = 0 log.warning( 'Not enough data to determine dcddm (Cd_mach) and dcldm (Cl_mach) at trim condition at Alt = {}, mach = {}, aoa = {}, aos = 0. Assumption: dcddm = dcldm = 0' .format(alt, mach, round(trim_aoa_deg, 2))) # Controls Derivatives to be found in the CPACS (To be calculated) dcddeta0 = 0 dcldeta0 = 0 dcmsdeta0 = 0 dcddtau0 = 0 dcldtau0 = 0 dcmsdtau0 = 0 # Traduction Ceasiom -> Theory Ue = u0 * np.cos( trim_aoa ) # *np.cos(aos) as aos = 0 at trim, cos(aos)=1 We = u0 * np.sin( trim_aoa ) # *np.cos(aos) as aos = 0 at trim, cos(aos)=1 # Dimentionless State Space variables, # In generalised body axes coordinates , # simplifications: Ue=V0, We=0, sin(Theta_e)=0 cos(Theta_e)=0 if thrust_available: # If power data X_u = -(2 * cd0 + mach * dcddm0) + 1 / ( 0.5 * rho * s * a ^ 2 ) * dtaudm0 # dtaudm dimensional Thrust derivative at trim conditions, P340 Michael V. Cook else: # Glider Mode X_u = -(2 * cd0 + mach * dcddm0) Z_u = -(2 * cl0 + mach * dcldm0) M_u = 0 # Negligible for subsonic conditions or better with P289 Yechout (cm_u+2cm0) X_w = (cl0 - cd_alpha0) Z_w = -(cl_alpha0 + cd0) M_w = cm_alpha0 X_q = dcddqstar0 # Normally almost = 0 Z_q = dcldqstar0 M_q = -dcmsdqstar0 X_dotw = 0 # Negligible Z_dotw = 1 / 3 * M_q / u0 / ( xh / mac ) # Thumb rule : M_alpha_dot = 1/3 Mq , ( not true for 747 :caughey P83,M_alpha_dot = 1/6Mq ) M_dotw = 1 / 3 * M_q / u0 # Thumb rule : M_alpha_dot = 1/3 Mq # Controls: X_eta = dcddeta0 # To be found from the cpacs file, and defined by the user! Z_eta = dcldeta0 # To be found from the cpacs file, and defined by the user! M_eta = dcmsdeta0 # To be found from the cpacs file, and defined by the user! X_tau = dcddtau0 # To be found from the cpacs file, and defined by the user! Z_tau = dcldtau0 # To be found from the cpacs file, and defined by the user! M_tau = dcmsdtau0 # To be found from the cpacs file, and defined by the user! # ----------------- Traduction Ceasiom -> Theory END ----------------------------------- # Sign check (Ref: Thomas Yechout Book, P304) check_sign_longi(cd_alpha0, M_w, cl_alpha0, M_dotw, Z_dotw, M_q, Z_q, M_eta, Z_eta) # Laterl-Directional if lateral_directional_analysis: cml = [] # N cmd = [] # L aos = [] aoa = [] # For Ue We cs = [] # For y_v dcsdpstar = [] # y_p dcmddpstar = [] # l_p dcmldpstar = [] # n_p dcsdrstar = [] # y_r dcmldrstar = [] # n_r dcmddrstar = [] # l_r for index in find_index: cml.append(cml_list[index]) # N , N_v cmd.append(cmd_list[index]) # L , L_v aos.append(aos_list[index] * np.pi / 180) aoa.append(aoa_list[index]) # For Ue We cs.append(cs_list[index]) dcsdpstar.append(dcsdpstar_list[index]) # y_p dcmddpstar.append(dcmddpstar_list[index]) # l_p dcmldpstar.append(dcmldpstar_list[index]) # n_p dcsdrstar.append(dcsdrstar_list[index]) # y_r dcmldrstar.append(dcmldrstar_list[index]) # n_r dcmddrstar.append(dcmddrstar_list[index]) # l_r #Trimm condition calculation # speed derivatives : y_v / l_v / n_v / Must be devided by speed given that the hyp v=Beta*U if len(aos_unic) >= 2: print('Mach : ', mach, ' and idx_mach : ', idx_mach) cs_beta0 = speed_derivative_at_trim_lat( cs_list, aos_list, aos_unic, idx_alt, idx_mach, aoa_list, idx_trim_before, idx_trim_after, ratio) # y_v if cs_beta0 == None: cs_beta0 = 0 log.warning( 'Not enough data to determine cs_beta (Y_v) at trim condition at Alt = {}, mach = {}, aoa = {}, aos = 0. Assumption: cs_beta = 0' .format(alt, mach, round(trim_aoa_deg, 2))) cmd_beta0 = speed_derivative_at_trim_lat( cmd_list, aos_list, aos_unic, idx_alt, idx_mach, aoa_list, idx_trim_before, idx_trim_after, ratio) # l_v if cmd_beta0 == None: cmd_beta0 = 0 log.warning( 'Not enough data to determine cmd_beta (L_v) at trim condition at Alt = {}, mach = {}, aoa = {}, aos = 0. Assumption: cmd_beta = 0' .format(alt, mach, round(trim_aoa_deg, 2))) cml_beta0 = speed_derivative_at_trim_lat( cml_list, aos_list, aos_unic, idx_alt, idx_mach, aoa_list, idx_trim_before, idx_trim_after, ratio) # n_v if cml_beta0 == None: cml_beta0 = 0 log.warning( 'Not enough data to determine cml_beta (N_v) at trim condition at Alt = {}, mach = {}, aoa = {}, aos = 0. Assumption: cml_beta = 0' .format(alt, mach, round(trim_aoa_deg, 2))) else: cs_beta0 = 0 cmd_beta0 = 0 cml_beta0 = 0 log.warning( 'Not enough data to determine cs_beta (Y_v), cmd_beta (L_v) and cml_beta (N_v) at trim condition at Alt = {}, mach = {}, aoa = {}, aos = 0. Assumption: cs_beta = cmd_beta = cml_beta = 0' .format(alt, mach, round(trim_aoa_deg, 2))) dcsdpstar0 = interpolation(dcsdpstar, idx_trim_before, idx_trim_after, ratio) # y_p dcmddpstar0 = interpolation(dcmddpstar, idx_trim_before, idx_trim_after, ratio) # l_p dcmldpstar0 = interpolation(dcmldpstar, idx_trim_before, idx_trim_after, ratio) # n_p dcsdrstar0 = interpolation(dcsdrstar, idx_trim_before, idx_trim_after, ratio) # y_r dcmldrstar0 = interpolation(dcmldrstar, idx_trim_before, idx_trim_after, ratio) # n_r dcmddrstar0 = interpolation(dcmddrstar, idx_trim_before, idx_trim_after, ratio) # l_r # TODO: calculate that and find in the cpacs dcsdxi0 = 0 dcmddxi0 = 0 dcmldxi0 = 0 dcsdzeta0 = 0 dcmddzeta0 = 0 dcmldzeta0 = 0 # Traduction Ceasiom -> Theory Y_v = cs_beta0 L_v = cmd_beta0 N_v = cml_beta0 Y_p = -dcsdpstar0 * mac / b L_p = -dcmddpstar0 * mac / b N_p = dcmldpstar0 * mac / b Y_r = dcsdrstar0 * mac / b N_r = -dcmldrstar0 * mac / b # mac/b :Because coefficients in ceasiom are nondimensionalised by the mac instead of the span L_r = dcmddrstar0 * mac / b # Controls: # Ailerons Y_xi = dcsdxi0 # To be found from the cpacs file, and defined by the user! L_xi = dcmddxi0 # To be found from the cpacs file, and defined by the user! N_xi = dcmldxi0 # To be found from the cpacs file, and defined by the user! # Rudder Y_zeta = dcsdzeta0 # To be found from the cpacs file, and defined by the user! L_zeta = dcmddzeta0 # To be found from the cpacs file, and defined by the user! N_zeta = dcmldzeta0 # To be found from the cpacs file, and defined by the user! Ue = u0 * np.cos( trim_aoa ) # *np.cos(aos) as aos = 0 at trim, cos(aos)=1 We = u0 * np.sin( trim_aoa ) # *np.cos(aos) as aos = 0 at trim, cos(aos)=1 # Sign check (Ref: Thomas Yechout Book, P304) check_sign_lat(Y_v, L_v, N_v, Y_p, L_p, Y_r, L_r, N_r, L_xi, Y_zeta, L_zeta, N_zeta) if trim_aoa: for angles in flight_path_angle: theta_e = angles + trim_aoa if longitudinal_analysis: (A_longi, B_longi, x_u,z_u,m_u,x_w,z_w,m_w, x_q,z_q,m_q,x_theta,z_theta,m_theta,x_eta,z_eta,m_eta, x_tau,z_tau,m_tau)\ = concise_derivative_longi(X_u,Z_u,M_u,X_w,Z_w,M_w,\ X_q,Z_q,M_q,X_dotw,Z_dotw,M_dotw,X_eta,Z_eta,M_eta,\ X_tau,Z_tau,M_tau, g, theta_e, u0,We,Ue,mac,m_adim,i_yy) C_longi = np.identity(4) D_longi = np.zeros((4, 2)) # Identify longitudinal roots if longi_root_identification( A_longi )[0] == None: # If longitudinal root not complex conjugate raise warning and plot roots eg_value_longi = longi_root_identification( A_longi)[1] log.warning( 'Longi : charcateristic equation roots are not complex conjugate : {}' .format(eg_value_longi)) legend = [ 'Root1', 'Root2', 'Root3', 'Root4' ] plot_title = 'S-plane longitudinal characteristic equation roots at (Alt = {}, Mach= {}, trimed at aoa = {}°)'.format( alt, mach, trim_aoa) plot_splane(eg_value_longi, plot_title, legend, show_plots, save_plots) else: # Longitudinal roots are complex conjugate (sp1, sp2, ph1, ph2, eg_value_longi , eg_vector_longi, eg_vector_longi_magnitude)\ = longi_root_identification(A_longi) legend = ['sp1', 'sp2', 'ph1', 'ph2'] plot_title = 'S-plane longitudinal characteristic equation roots at (Alt = {}, Mach= {}, trimed at aoa = {}°)'.format( alt, mach, trim_aoa) plot_splane(eg_value_longi, plot_title, legend, show_plots, save_plots) # Modes parameters : damping ratio, frequence, CAP, time tou double amplitude Z_w_dimensional = Z_w * ( 0.5 * rho * s * u0**2 ) # Z_w* (0.5*rho*s*u0**2) is the dimensional form of Z_w, Z_w = -(cl_alpha0 + cd0) P312 Yechout z_alpha = Z_w_dimensional * u0 / m # alpha = w/u0 hence, z_alpha = Z_w_dimensional * u0 [Newton/rad/Kg : m/s^2 /rad] load_factor = -z_alpha / g # number of g's/rad (1g/rad 2g/rad 3g/rad) (sp_freq, sp_damp, sp_cap, ph_freq, ph_damp, ph_t2)\ = longi_mode_characteristic(sp1,sp2,ph1,ph2,load_factor) # Rating sp_damp_rate = short_period_damping_rating( aircraft_class, sp_damp) sp_freq_rate = short_period_frequency_rating( flight_phase, aircraft_class, sp_freq, load_factor) # Plot SP freq vs Load factor legend = 'Alt = {}, Mach= {}, trim aoa = {}°'.format( alt, mach, trim_aoa) if flight_phase == 'A': plot_sp_level_a([load_factor], [sp_freq], legend, show_plots, save_plots) elif flight_phase == 'B': plot_sp_level_b( x_axis, y_axis, legend, show_plots, save_plots) else: plot_sp_level_c( x_axis, y_axis, legend, show_plots, save_plots) sp_cap_rate = cap_rating( flight_phase, sp_cap, sp_damp) ph_rate = phugoid_rating(ph_damp, ph_t2) # Raise warning if unstable mode in the log file if sp_damp_rate == None: log.warning( 'ShortPeriod UNstable at Alt = {}, Mach = {} , due to DampRatio = {} ' .format(alt, mach, round(sp_damp, 4))) if sp_freq_rate == None: log.warning( 'ShortPeriod UNstable at Alt = {}, Mach = {} , due to UnDampedFreq = {} rad/s ' .format(alt, mach, round(sp_freq, 4))) if sp_cap_rate == None: log.warning( 'ShortPeriod UNstable at Alt = {}, Mach = {} , with CAP evaluation, DampRatio = {} , CAP = {} ' .format(alt, mach, round(sp_damp, 4), round(sp_cap, 4))) if ph_rate == None: log.warning( 'Phugoid UNstable at Alt = {}, Mach = {} , DampRatio = {} , UnDampedFreq = {} rad/s' .format(alt, mach, round(ph_damp, 4), round(ph_freq, 4))) # TODO # Compute numerator TF for (Alt, mach, flight_path_angle, aoa_trim, aos=0 if lateral_directional_analysis: (A_direc, B_direc,y_v,l_v,n_v,y_p,y_phi,y_psi,l_p,l_phi,l_psi,n_p,y_r,l_r,n_r,n_phi,n_psi, y_xi,l_xi,n_xi, y_zeta,l_zeta,n_zeta)\ = concise_derivative_lat(Y_v,L_v,N_v,Y_p,L_p,N_p,Y_r,L_r,N_r,\ Y_xi,L_xi,N_xi, Y_zeta,L_zeta,N_zeta,\ g, b, theta_e, u0,We,Ue,m_adim,i_xx,i_zz,i_xz ) C_direc = np.identity(5) D_direc = np.zeros((5, 2)) if direc_root_identification( A_direc )[0] == None: # Lateral-directional roots are correctly identified eg_value_direc = direc_root_identification( A_direc)[1] print( 'Lat-Dir : charcateristic equation roots are not complex conjugate : {}' .format(eg_value_direc)) legend = [ 'Root1', 'Root2', 'Root3', 'Root4' ] plot_title = 'S-plane lateral characteristic equation roots at (Alt = {}, Mach= {}, trimed at aoa = {}°)'.format( alt, mach, trim_aoa) plot_splane(eg_value_direc, plot_title, legend, show_plots, save_plots) else: # Lateral-directional roots are correctly identified (roll, spiral, dr1, dr2, eg_value_direc, eg_vector_direc, eg_vector_direc_magnitude)\ = direc_root_identification(A_direc) legend = ['roll', 'spiral', 'dr1', 'dr2'] plot_title = 'S-plane lateralcharacteristic equation roots at (Alt = {}, Mach= {}, trimed at aoa = {}°)'.format( alt, mach, trim_aoa) plot_splane(eg_value_direc, plot_title, legend, show_plots, save_plots) (roll_timecst, spiral_timecst, spiral_t2, dr_freq, dr_damp, dr_damp_freq) = direc_mode_characteristic( roll, spiral, dr1, dr2) # Rating roll_rate = roll_rating( flight_phase, aircraft_class, roll_timecst) spiral_rate = spiral_rating( flight_phase, spiral_timecst, spiral_t2) dr_rate = dutch_roll_rating( flight_phase, aircraft_class, dr_damp, dr_freq, dr_damp_freq) # Raise warning in the log file if unstable mode if roll_rate == None: log.warning( 'Roll mode UNstable at Alt = {}, Mach = {} , due to roll root = {}, roll time contatant = {} s' .format(alt, mach, round(roll_root, 4), round(roll_timecst, 4))) if spiral_rate == None: log.warning( 'Spiral mode UNstable at Alt = {}, Mach = {} , spiral root = {}, time_double_ampl = {}' .format(alt, mach, round(spiral_root, 4), round(spiral_t2, 4))) if dr_rate == None: log.warning( 'Dutch Roll UNstable at Alt = {}, Mach = {} , Damping Ratio = {} , frequency = {} rad/s ' .format(alt, mach, round(dr_damp, 4), round(dr_freq, 4)))
def get_su2_results(cpacs_path, cpacs_out_path, wkdir): """ Function to write SU2 results in a CPACS file. Function 'get_su2_results' get available results from the latest SU2 calculation and put it at the correct place in the CPACS file. '/cpacs/vehicles/aircraft/model/analyses/aeroPerformance/aerpMap[n]/aeroPerformanceMap' Args: cpacs_path (str): Path to input CPACS file cpacs_out_path (str): Path to output CPACS file wkdir (str): Path to the working directory """ tixi = cpsf.open_tixi(cpacs_path) # TODO Check and reactivate that # save_timestamp(tixi,SU2_XPATH) <-- ceaf.replace by get get_execution_date() if not os.path.exists(wkdir): raise OSError('The working directory : ' + wkdir + 'does not exit!') os.chdir(wkdir) dir_list = os.listdir(wkdir) # Get and save Wetted area wetted_area = get_wetted_area(wkdir) wetted_area_xpath = '/cpacs/toolspecific/CEASIOMpy/geometry/analysis/wettedArea' cpsf.create_branch(tixi, wetted_area_xpath) tixi.updateDoubleElement(wetted_area_xpath, wetted_area, '%g') # Get and save CL/CD ratio fixed_cl_xpath = SU2_XPATH + '/fixedCL' fixed_cl = cpsf.get_value_or_default(tixi, fixed_cl_xpath, 'NO') # TODO # if fixed_cl == 'YES': # find force_file_name = 'forces_breakdown.dat' # cl_cd = get_efficiency(force_path) # lDRatio_xpath = '/cpacs/toolspecific/CEASIOMpy/ranges/lDRatio' # TODO: probalby change xpath and name # cpsf.create_branch(tixi, lDRatio_xpath) # tixi.updateDoubleElement(lDRatio_xpath,cl_cd,'%g') # Save aeroPerformanceMap su2_aeromap_xpath = SU2_XPATH + '/aeroMapUID' aeromap_uid = cpsf.get_value(tixi, su2_aeromap_xpath) # Check if loads shoud be extracted check_extract_loads_xpath = SU2_XPATH + '/results/extractLoads' check_extract_loads = cpsf.get_value_or_default(tixi, check_extract_loads_xpath, False) # Create an oject to store the aerodynamic coefficients apmf.check_aeromap(tixi, aeromap_uid) # TODO: create a function to earase previous results... Coef2 = apmf.get_aeromap(tixi, aeromap_uid) Coef = apmf.AeroCoefficient() Coef.alt = Coef2.alt Coef.mach = Coef2.mach Coef.aoa = Coef2.aoa Coef.aos = Coef2.aos case_dir_list = [dir for dir in dir_list if 'Case' in dir] for config_dir in sorted(case_dir_list): if os.path.isdir(config_dir): os.chdir(config_dir) force_file_name = 'forces_breakdown.dat' if not os.path.isfile(force_file_name): raise OSError('No result force file have been found!') # Read result file with open(force_file_name) as f: for line in f.readlines(): if 'Total CL:' in line: cl = float(line.split(':')[1].split('|')[0]) if 'Total CD:' in line: cd = float(line.split(':')[1].split('|')[0]) if 'Total CSF:' in line: cs = float(line.split(':')[1].split('|')[0]) # TODO: Check which axis name corespond to waht: cml, cmd, cms if 'Total CMx:' in line: cmd = float(line.split(':')[1].split('|')[0]) if 'Total CMy:' in line: cms = float(line.split(':')[1].split('|')[0]) if 'Total CMz:' in line: cml = float(line.split(':')[1].split('|')[0]) if ('Free-stream velocity' in line and 'm/s' in line): velocity = float(line.split(' ')[7]) # Damping derivatives rotation_rate_xpath = SU2_XPATH + '/options/rotationRate' rotation_rate = cpsf.get_value_or_default(tixi, rotation_rate_xpath, 1.0) ref_xpath = '/cpacs/vehicles/aircraft/model/reference' ref_len = cpsf.get_value(tixi, ref_xpath + '/length') adim_rot_rate = rotation_rate * ref_len / velocity if '_dp' in config_dir: dcl = (cl - Coef.cl[-1]) / adim_rot_rate dcd = (cd - Coef.cd[-1]) / adim_rot_rate dcs = (cs - Coef.cs[-1]) / adim_rot_rate dcml = (cml - Coef.cml[-1]) / adim_rot_rate dcmd = (cmd - Coef.cmd[-1]) / adim_rot_rate dcms = (cms - Coef.cms[-1]) / adim_rot_rate Coef.damping_derivatives.add_damping_der_coef( dcl, dcd, dcs, dcml, dcmd, dcms, '_dp') elif '_dq' in config_dir: dcl = (cl - Coef.cl[-1]) / adim_rot_rate dcd = (cd - Coef.cd[-1]) / adim_rot_rate dcs = (cs - Coef.cs[-1]) / adim_rot_rate dcml = (cml - Coef.cml[-1]) / adim_rot_rate dcmd = (cmd - Coef.cmd[-1]) / adim_rot_rate dcms = (cms - Coef.cms[-1]) / adim_rot_rate Coef.damping_derivatives.add_damping_der_coef( dcl, dcd, dcs, dcml, dcmd, dcms, '_dq') elif '_dr' in config_dir: dcl = (cl - Coef.cl[-1]) / adim_rot_rate dcd = (cd - Coef.cd[-1]) / adim_rot_rate dcs = (cs - Coef.cs[-1]) / adim_rot_rate dcml = (cml - Coef.cml[-1]) / adim_rot_rate dcmd = (cmd - Coef.cmd[-1]) / adim_rot_rate dcms = (cms - Coef.cms[-1]) / adim_rot_rate Coef.damping_derivatives.add_damping_der_coef( dcl, dcd, dcs, dcml, dcmd, dcms, '_dr') elif '_TED_' in config_dir: config_dir_split = config_dir.split('_') ted_idx = config_dir_split.index('TED') ted_uid = config_dir_split[ted_idx + 1] defl_angle = float(config_dir.split('_defl')[1]) try: print(Coef.IncrMap.dcl) except AttributeError: Coef.IncrMap = apmf.IncrementMap(ted_uid) # TODO: still in development, for now only 1 ted and 1 defl print(ted_uid, defl_angle) dcl = (cl - Coef.cl[-1]) dcd = (cd - Coef.cd[-1]) dcs = (cs - Coef.cs[-1]) dcml = (cml - Coef.cml[-1]) dcmd = (cmd - Coef.cmd[-1]) dcms = (cms - Coef.cms[-1]) control_parameter = -1 Coef.IncrMap.add_cs_coef(dcl, dcd, dcs, dcml, dcmd, dcms, ted_uid, control_parameter) else: # No damping derivative or control surfaces case Coef.add_coefficients(cl, cd, cs, cml, cmd, cms) if check_extract_loads: results_files_dir = os.path.join(wkdir, config_dir) extract_loads(results_files_dir) os.chdir(wkdir) # Save object Coef in the CPACS file apmf.save_coefficients(tixi, aeromap_uid, Coef) cpsf.close_tixi(tixi, cpacs_out_path)
def create_SU2_mesh(cpacs_path, cpacs_out_path): """ Function to create a simple SU2 mesh form an SUMO file (.smx) Function 'create_mesh' is used to generate an unstructured mesh with SUMO (which integrage Tetgen for the volume mesh) using a SUMO (.smx) geometry file as input. Meshing option could be change manually (only in the script for now) Source : * sumo help, tetgen help (in the folder /doc) Args: cpacs_path (str): Path to the CPACS file cpacs_out_path (str): Path to the output CPACS file """ tixi = cpsf.open_tixi(cpacs_path) wkdir = ceaf.get_wkdir_or_create_new(tixi) sumo_dir = os.path.join(wkdir, 'SUMO') if not os.path.isdir(sumo_dir): os.mkdir(sumo_dir) su2_mesh_path = os.path.join(sumo_dir, 'ToolOutput.su2') meshdir = os.path.join(wkdir, 'MESH') if not os.path.isdir(meshdir): os.mkdir(meshdir) original_dir = os.getcwd() os.chdir(sumo_dir) sumo_file_xpath = '/cpacs/toolspecific/CEASIOMpy/filesPath/sumoFilePath' sumo_file_path = cpsf.get_value_or_default(tixi, sumo_file_xpath, '') if sumo_file_path == '': raise ValueError('No SUMO file to use to create a mesh') # Set mesh parameters log.info('Mesh parameter will be set') refine_level_xpath = '/cpacs/toolspecific/CEASIOMpy/mesh/sumoOptions/refinementLevel' refine_level = cpsf.get_value_or_default(tixi, refine_level_xpath, 0.0) log.info('Refinement level is {}'.format(refine_level)) add_mesh_parameters(sumo_file_path, refine_level) # Check current Operating System current_os = platform.system() if current_os == 'Darwin': log.info('Your OS is Mac\n\n') log.info( '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!') log.info('On MacOS the mesh has to be generated manually.') log.info('To create a SU2Mesh you have to :') log.info('Open the .smx geometry that you will find there:') log.info(sumo_file_path) log.info('Click on the button "Mesh"') log.info('Click on "Create Mesh"') log.info('Click on "Volume Mesh"') log.info('Click on "Run"') log.info('When the mesh generation is completed, click on "Close"') log.info('Go to the Menu "Mesh" -> "Save volume mesh..."') log.info('Chose "SU2 (*.su2)" as File Type"') log.info('Copy/Paste the following line as File Name') log.info(su2_mesh_path) log.info('Click on "Save"') log.info('You can now close SUMO, your workflow will continue.') log.info( 'More information: https://ceasiompy.readthedocs.io/en/latest/user_guide/modules/SUMOAutoMesh/index.html' ) log.info( '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n' ) # For now, I did not find a way to run "sumo -batch" on Mac... # The command just open SUMO GUI, the mesh has to be generate and save manually command = ['open', '/Applications/SUMO/dwfsumo.app/'] os.system(' '.join(command)) elif current_os == 'Linux': log.info('Your OS is Linux') # Check if SUMO is installed soft_dict = ceaf.get_install_path(['sumo']) # Run SUMO in batch output = '-output=su2' options = '-tetgen-options=pq1.16VY' # See Tetgen help for more options, maybe transform that as an input # Command line to run: sumo -batch -output=su2 -tetgen-options=pq1.16VY ToolOutput.smx command = [ soft_dict['sumo'], '-batch', output, options, sumo_file_path ] os.system(' '.join(command)) elif current_os == 'Windows': log.info('Your OS is Windows') # TODO: develop this part log.warning('OS not supported yet by SUMOAutoMesh!') raise OSError('OS not supported yet!') else: raise OSError('OS not recognize!') # Copy the mesh in the MESH directory aircraft_name = cpsf.aircraft_name(tixi) su2_mesh_name = aircraft_name + '_baseline.su2' su2_mesh_new_path = os.path.join(meshdir, su2_mesh_name) shutil.copyfile(su2_mesh_path, su2_mesh_new_path) if os.path.isfile(su2_mesh_new_path): log.info('An SU2 Mesh has been correctly generated.') su2_mesh_xpath = '/cpacs/toolspecific/CEASIOMpy/filesPath/su2Mesh' cpsf.create_branch(tixi, su2_mesh_xpath) tixi.updateTextElement(su2_mesh_xpath, su2_mesh_new_path) os.remove(su2_mesh_path) else: raise ValueError('No SU2 Mesh file has been generated!') cpsf.close_tixi(tixi, cpacs_out_path) os.chdir(original_dir)
def create_SU2_mesh(cpacs_path, cpacs_out_path): """ Function to create a simple SU2 mesh form an SUMO file (.smx) Function 'create_mesh' is used to generate an unstructured mesh with SUMO (which integrage Tetgen for the volume mesh) using a SUMO (.smx) geometry file as input. Meshing option could be change manually (only in the script for now) Source : * sumo help, tetgen help (in the folder /doc) Args: cpacs_path (str): Path to the CPACS file cpacs_out_path (str): Path to the output CPACS file """ tixi = cpsf.open_tixi(cpacs_path) wkdir = ceaf.get_wkdir_or_create_new(tixi) sumo_dir = os.path.join(wkdir, 'SUMO') if not os.path.isdir(sumo_dir): os.mkdir(sumo_dir) mesh_dir = os.path.join(wkdir, 'MESH') if not os.path.isdir(mesh_dir): os.mkdir(mesh_dir) original_dir = os.getcwd() os.chdir(sumo_dir) sumo_file_xpath = '/cpacs/toolspecific/CEASIOMpy/filesPath/sumoFilePath' sumo_file_path = cpsf.get_value_or_default(tixi, sumo_file_xpath, '') if sumo_file_path == '': raise ValueError('No SUMO file to use to create a mesh') # Check if SUMO is installed soft_dict = ceaf.get_install_path(['sumo']) # Run SUMO to create a create a mesh # sumo - batch -output=su2 -tetgen-options=pq1.16VY mesh.smx sumo_output = '-output=su2' # For now, must be SU2 tetgen_options = '-tetgen-options=pq1.16VY' # See Tetgen help for more options, maybe transform that as an input command_line = [ soft_dict['sumo'], '-batch', sumo_output, tetgen_options, sumo_file_path ] # print(' '.join(command_line)) os.system(' '.join(command_line)) # Copy the mesh in the MESH directory su2_mesh_path = os.path.join(sumo_dir, 'ToolOutput.su2') aircraft_name = cpsf.aircraft_name(tixi) su2_mesh_name = aircraft_name + '_baseline.su2' su2_mesh_new_path = os.path.join(mesh_dir, su2_mesh_name) shutil.copyfile(su2_mesh_path, su2_mesh_new_path) if os.path.isfile(su2_mesh_new_path): log.info('An SU2 Mesh has been correctly generated.') su2_mesh_xpath = '/cpacs/toolspecific/CEASIOMpy/filesPath/su2Mesh' cpsf.create_branch(tixi, su2_mesh_xpath) tixi.updateTextElement(su2_mesh_xpath, su2_mesh_new_path) os.remove(su2_mesh_path) else: raise ValueError('No SU2 Mesh file has been generated!') cpsf.close_tixi(tixi, cpacs_out_path) os.chdir(original_dir)
def get_user_inputs(ed, ui, adui, cpacs_in): """Function to extract from the xml file the required input data, the code will use the default value when they are missing. Function 'get_user_inputs' ... Args: ed (int): EngineData class. ui (class): UserInputs class adui (str): AdvancedInputs class. cpacs_in (str): Path to the CPACS file Returns: ed (int): Updated ngineData class. ui (class): Updated UserInputs class adui (str): Updated AdvancedInputs class. """ log.info('Starting data extraction from CPACS file') tixi = cpsf.open_tixi(cpacs_in) # toolspecific CEASIOM_XPATH = '/cpacs/toolspecific/CEASIOMpy' GEOM_XPATH = CEASIOM_XPATH + '/geometry' RANGE_XPATH = CEASIOM_XPATH + '/ranges' WEIGHT_XPATH = CEASIOM_XPATH + '/weight' CREW_XPATH = WEIGHT_XPATH + '/crew' PILOTS_PATH = CREW_XPATH + '/pilots' CAB_CREW_XPATH = CREW_XPATH + '/cabinCrewMembers' PASS_XPATH = WEIGHT_XPATH + '/passengers' ML_XPATH = WEIGHT_XPATH + '/massLimits' PROP_XPATH = CEASIOM_XPATH + '/propulsion' FUEL_XPATH = '/cpacs/toolspecific/CEASIOMpy/fuels' cpsf.create_branch(tixi, FUEL_XPATH, False) cpsf.create_branch(tixi, GEOM_XPATH, False) cpsf.create_branch(tixi, RANGE_XPATH, False) cpsf.create_branch(tixi, PILOTS_PATH, False) cpsf.create_branch(tixi, CAB_CREW_XPATH, False) cpsf.create_branch(tixi, PASS_XPATH, False) cpsf.create_branch(tixi, ML_XPATH, False) cpsf.create_branch(tixi, PROP_XPATH, False) # cpacs/vehicles MC_XPATH = '/cpacs/vehicles/aircraft/model/analyses/massBreakdown/payload/mCargo/massDescription' F_XPATH = '/cpacs/vehicles/fuels/fuel' cpsf.create_branch(tixi, MC_XPATH, False) cpsf.create_branch(tixi, F_XPATH, False) cpsf.add_uid(tixi, F_XPATH, 'kerosene') # Gathering data ========================================================= # Geometry =============================================================== if not tixi.checkElement(GEOM_XPATH + '/description'): tixi.createElement(GEOM_XPATH, 'description') tixi.updateTextElement(GEOM_XPATH + '/description', 'User '\ + 'geometry input') ui.FLOORS_NB = cpsf.get_value_or_default(tixi,GEOM_XPATH + '/floorsNb', ui.FLOORS_NB) adui.VRT_THICK = cpsf.get_value_or_default(tixi,GEOM_XPATH + '/virtualThick', 0.00014263) adui.VRT_STR_DENSITY = cpsf.get_value_or_default(tixi,GEOM_XPATH + '/virtualDensity', 2700.0) ui.H_LIM_CABIN = cpsf.get_value_or_default(tixi,GEOM_XPATH + '/cabinHeight', 2.3) # People ================================================================= # Pilots user input data adui.PILOT_NB = cpsf.get_value_or_default(tixi,PILOTS_PATH + '/pilotNb', 2) adui.MASS_PILOT = cpsf.get_value_or_default(tixi,PILOTS_PATH + '/pilotMass', 102.0) adui.MASS_CABIN_CREW = cpsf.get_value_or_default(tixi,CAB_CREW_XPATH + '/cabinCrewMemberMass', 68.0) adui.MASS_PASS = cpsf.get_value_or_default(tixi,PASS_XPATH + '/passMass', 105.0) adui.PASS_BASE_DENSITY = cpsf.get_value_or_default(tixi,PASS_XPATH + '/passDensity', 1.66) adui.PASS_PER_TOILET = cpsf.get_value_or_default(tixi,PASS_XPATH + '/passPerToilet', 50) # what to to with this input if tixi.checkElement(PASS_XPATH + '/passNb'): temp = tixi.getIntegerElement(PASS_XPATH+ '/passNb') if temp != ui.MAX_PASS and temp > 0: ui.MAX_PASS = temp # Fuel =================================================================== adui.FUEL_DENSITY = cpsf.get_value_or_default(tixi,F_XPATH + '/density', 800) adui.RES_FUEL_PERC = cpsf.get_value_or_default(tixi,F_XPATH + '/resFuelPerc', 0.06) # Weight ================================================================= # Mass limits data if not tixi.checkElement(ML_XPATH + '/description'): tixi.createElement(ML_XPATH, 'description') tixi.updateTextElement(ML_XPATH + '/description', 'Desired max fuel '\ + 'volume [m^3] and payload mass [kg]') ui.MAX_PAYLOAD = cpsf.get_value_or_default(tixi,ML_XPATH + '/maxPayload', 0.0) ui.MAX_FUEL_VOL = cpsf.get_value_or_default(tixi,ML_XPATH + '/maxFuelVol', 0.0) ui.MASS_CARGO = cpsf.get_value_or_default(tixi,MC_XPATH + '/massCargo', 0.0) # If the cargo mass is defined in the UserInputs class will be added # in the CPACS file after the analysis. # Flight ================================================================= ed.TSFC_CRUISE = cpsf.get_value_or_default(tixi,PROP_XPATH + '/tSFC', 0.5) # TODO: These data should be taken from aeroMaps... if not tixi.checkElement(RANGE_XPATH + '/lDRatio'): tixi.createElement(RANGE_XPATH, 'lDRatio') tixi.updateDoubleElement(RANGE_XPATH + '/lDRatio',\ ui.LD, '%g') else: temp = tixi.getIntegerElement(RANGE_XPATH + '/lDRatio') if temp != ui.LD and temp > 0: ui.LD = temp if not tixi.checkElement(RANGE_XPATH + '/cruiseSpeed'): tixi.createElement(RANGE_XPATH, 'cruiseSpeed') tixi.updateDoubleElement(RANGE_XPATH + '/cruiseSpeed',\ ui.CRUISE_SPEED, '%g') else: temp = tixi.getIntegerElement(RANGE_XPATH + '/cruiseSpeed') if temp != ui.CRUISE_SPEED and temp > 0: ui.CRUISE_SPEED = temp # TODO: see how to enter input for Engines if not tixi.checkElement(PROP_XPATH + '/userEngineOption'): tixi.createElement(PROP_XPATH, 'userEngineOption') if ui.USER_ENGINES: tixi.updateTextElement(PROP_XPATH + '/userEngineOption', 'True') else: tixi.updateTextElement(PROP_XPATH + '/userEngineOption', 'False') else: temp = tixi.getTextElement(PROP_XPATH + '/userEngineOption') if temp == 'False': ui.USER_ENGINES = False else: ui.USER_ENGINES = True if not tixi.checkElement(PROP_XPATH + '/singleHydraulics'): tixi.createElement(PROP_XPATH, 'singleHydraulics') if adui.SINGLE_HYDRAULICS: tixi.updateTextElement(PROP_XPATH + '/singleHydraulics', 'True') else: tixi.updateTextElement(PROP_XPATH + '/singleHydraulics', 'False') else: temp = tixi.getTextElement(PROP_XPATH + '/singleHydraulics') if temp == 'False': adui.SINGLE_HYDRAULICS = False else: adui.SINGLE_HYDRAULICS = True log.info('Data from CPACS file succesfully extracted') cpsf.close_tixi(tixi, cpacs_in) return(ed, ui, adui)
None. """ am_uid_use = cpsf.get_value_or_default(tixi, SMUSE_XPATH + 'aeroMapUID', '') am_uid_train = cpsf.get_value_or_default(tixi, SMTRAIN_XPATH + 'aeroMapUID', '') if am_uid_train == am_uid_use: sys.exit('Same aeromap that was used to create the model') if __name__ == "__main__": log.info('----- Start of ' + os.path.basename(__file__) + ' -----') # Load the model tixi = cpsf.open_tixi(cpacs_path) Model = load_surrogate(tixi) check_aeromap(tixi) if cpsf.get_value_or_default(tixi, SMUSE_XPATH + 'AeroMapOnly', False): aeromap_calculation(Model.sm, tixi) else: predict_output(Model) cpsf.close_tixi(tixi, cpacs_path_out) log.info('----- End of ' + os.path.basename(__file__) + ' -----')
def get_user_inputs(self, cpacs_path): """ Get user input from the CPACS file The function 'get_user_inputs' extracts from the CPACS file the required input data, the code will use the default value when they are missing. Args: cpacs_path (str): Path to CPACS file """ tixi = open_tixi(cpacs_path) description = 'User geometry input' get_value_or_default(tixi, GEOM_XPATH + '/description', description) self.IS_DOUBLE_FLOOR = get_value_or_default( tixi, GEOM_XPATH + '/isDoubleFloor', 0) self.PILOT_NB = get_value_or_default(tixi, pilots_xpath + '/pilotNb', 2) self.MASS_PILOT = get_value_or_default(tixi, pilots_xpath + '/pilotMass', 102) self.MASS_CABIN_CREW = get_value_or_default( tixi, CC_XPATH + '/cabinCrewMemberMass', 68) self.MASS_PASS = get_value_or_default(tixi, PASS_XPATH + '/passMass', 105) self.PASS_PER_TOILET = get_value_or_default( tixi, PASS_XPATH + '/passPerToilet', 50) description = 'Desired max fuel volume [m^3] and payload mass [kg]' get_value_or_default(tixi, ML_XPATH + '/description', description) self.MAX_PAYLOAD = get_value_or_default(tixi, ML_XPATH + '/maxPayload', 0) self.MAX_FUEL_VOL = get_value_or_default(tixi, ML_XPATH + '/maxFuelVol', 0) self.MASS_CARGO = get_value_or_default(tixi, MC_XPATH + '/mass', 0.0) self.FUEL_DENSITY = get_value_or_default(tixi, F_XPATH + '/density', 800) self.TURBOPROP = get_value_or_default(tixi, PROP_XPATH + '/turboprop', False) self.RES_FUEL_PERC = get_value_or_default(tixi, FUEL_XPATH + '/resFuelPerc', 0.06) add_uid(tixi, F_XPATH, 'kerosene') close_tixi(tixi, cpacs_path)
def plot_aero_coef(cpacs_path, cpacs_out_path): """Plot Aero coefficients from the chosen aeroMap in the CPACS file Function 'plot_aero_coef' can plot one or several aeromap from the CPACS file according to some user option, these option will be shown in the the SettingGUI or default values will be used. Args: cpacs_path (str): Path to CPACS file cpacs_out_path (str):Path to CPACS output file """ # Open TIXI handle tixi = cpsf.open_tixi(cpacs_path) aircraft_name = cpsf.aircraft_name(tixi) # Get aeroMap list to plot aeromap_to_plot_xpath = PLOT_XPATH + '/aeroMapToPlot' aeromap_uid_list = [] # Option to select aeromap manualy manual_selct = cpsf.get_value_or_default(tixi, PLOT_XPATH + '/manualSelection', False) if manual_selct: aeromap_uid_list = call_select_aeromap(tixi) cpsf.create_branch(tixi, aeromap_to_plot_xpath) cpsf.add_string_vector(tixi, aeromap_to_plot_xpath, aeromap_uid_list) else: try: aeromap_uid_list = cpsf.get_string_vector(tixi, aeromap_to_plot_xpath) except: # If aeroMapToPlot is not define, select manualy anyway aeromap_uid_list = call_select_aeromap(tixi) cpsf.create_branch(tixi, aeromap_to_plot_xpath) cpsf.add_string_vector(tixi, aeromap_to_plot_xpath, aeromap_uid_list) # Create DataFrame from aeromap(s) aeromap_df_list = [] for aeromap_uid in aeromap_uid_list: aeromap_df = apmf.get_datafram_aeromap(tixi, aeromap_uid) aeromap_df['uid'] = aeromap_uid aeromap_df_list.append(aeromap_df) aeromap = pd.concat(aeromap_df_list, ignore_index=True) if len(aeromap_uid_list) > 1: uid_crit = None else: uid_crit = aeromap_uid_list[0] # Default options title = aircraft_name criterion = pd.Series([True] * len(aeromap.index)) groupby_list = ['uid', 'mach', 'alt', 'aos'] # Get criterion from CPACS crit_xpath = PLOT_XPATH + '/criterion' alt_crit = cpsf.get_value_or_default(tixi, crit_xpath + '/alt', 'None') mach_crit = cpsf.get_value_or_default(tixi, crit_xpath + '/mach', 'None') aos_crit = cpsf.get_value_or_default(tixi, crit_xpath + '/aos', 'None') cpsf.close_tixi(tixi, cpacs_out_path) # Modify criterion and title according to user option if len(aeromap['alt'].unique()) == 1: title += ' - Alt = ' + str(aeromap['alt'].loc[0]) groupby_list.remove('alt') elif alt_crit not in NONE_LIST: criterion = criterion & (aeromap.alt == alt_crit) title += ' - Alt = ' + str(alt_crit) groupby_list.remove('alt') if len(aeromap['mach'].unique()) == 1: title += ' - Mach = ' + str(aeromap['mach'].loc[0]) groupby_list.remove('mach') elif mach_crit not in NONE_LIST: criterion = criterion & (aeromap.mach == mach_crit) title += ' - Mach = ' + str(mach_crit) groupby_list.remove('mach') if len(aeromap['aos'].unique()) == 1: title += ' - AoS = ' + str(aeromap['aos'].loc[0]) groupby_list.remove('aos') elif aos_crit not in NONE_LIST: criterion = criterion & (aeromap.aos == aos_crit) title += ' - AoS = ' + str(aos_crit) groupby_list.remove('aos') if uid_crit is not None and len(groupby_list) > 1: criterion = criterion & (aeromap.uid == uid_crit) title += ' - ' + uid_crit groupby_list.remove('uid') # Plot settings fig, axs = plt.subplots(2, 3) fig.suptitle(title, fontsize=14) fig.set_figheight(8) fig.set_figwidth(15) fig.subplots_adjust(left=0.06) axs[0, 1].axhline(y=0.0, color='k', linestyle='-') # Line at Cm=0 # Plot aerodynamic coerfficients for value, grp in aeromap.loc[criterion].groupby(groupby_list): legend = write_legend(groupby_list, value) axs[0, 0].plot(grp['aoa'], grp['cl'], 'x-', label=legend) axs[1, 0].plot(grp['aoa'], grp['cd'], 'x-') axs[0, 1].plot(grp['aoa'], grp['cms'], 'x-') axs[1, 1].plot(grp['aoa'], grp['cl'] / grp['cd'], 'x-') axs[0, 2].plot(grp['cd'], grp['cl'], 'x-') axs[1, 2].plot(grp['cl'], grp['cl'] / grp['cd'], 'x-') # Set subplot options subplot_options(axs[0, 0], 'CL', 'AoA') subplot_options(axs[1, 0], 'CD', 'AoA') subplot_options(axs[0, 1], 'Cm', 'AoA') subplot_options(axs[1, 1], 'CL/CD', 'AoA') subplot_options(axs[0, 2], 'CL', 'CD') subplot_options(axs[1, 2], 'CL/CD', 'CL') fig.legend(loc='upper right') plt.show()
def generate_mesh_def_config(tixi, wkdir, ted_uid, wing_uid, sym_dir, defl_list): """Function to create config file for a TED. Function 'generate_mesh_def_config' will create SU2 configuration files to create SU2 deformed mesh for a specific Trailing Edge Device (TED) at several deflection angle (from defl_list) Args: tixi (handle): TIXI handle wkdir (str): Path to the working directory ted_uid (str): uID of the TED wing_uid (str): uID of the coresponding wing sym_dir (str): Direction of the axis of symmetry ('x','y','z' or '') defl_list (str): List of deflction angles to generate """ tigl = cpsf.open_tigl(tixi) aircraft_name = cpsf.aircraft_name(tixi) DEFAULT_CONFIG_PATH = MODULE_DIR + '/files/DefaultConfig_v7.cfg' cfg = su2f.read_config(DEFAULT_CONFIG_PATH) config_dir_name = aircraft_name + '_TED_' + ted_uid # TODO: add check or remove if alread exist? os.mkdir(os.path.join(wkdir, 'MESH', config_dir_name)) # Get TED and hinge line definition ted_corner = get_ted_corner(tixi, tigl, ted_uid) ted_corner_list, ted_corner_sym_list = get_ffd_box(ted_corner, sym_dir) ted_hinge = get_ted_hinge(tixi, tigl, ted_uid) hinge_list, hinge_sym_list = get_hinge_lists(ted_hinge, sym_dir) # General parmeters ref_len = cpsf.get_value(tixi, REF_XPATH + '/length') ref_area = cpsf.get_value(tixi, REF_XPATH + '/area') ref_ori_moment_x = cpsf.get_value_or_default(tixi, REF_XPATH + '/point/x', 0.0) ref_ori_moment_y = cpsf.get_value_or_default(tixi, REF_XPATH + '/point/y', 0.0) ref_ori_moment_z = cpsf.get_value_or_default(tixi, REF_XPATH + '/point/z', 0.0) cfg['REF_LENGTH'] = ref_len cfg['REF_AREA'] = ref_area cfg['REF_ORIGIN_MOMENT_X'] = ref_ori_moment_x cfg['REF_ORIGIN_MOMENT_Y'] = ref_ori_moment_y cfg['REF_ORIGIN_MOMENT_Z'] = ref_ori_moment_z cfg['GRID_MOVEMENT'] = 'NONE' cfg['ROTATION_RATE'] = '0.0 0.0 0.0' # TODO: is it the best way or should be pass as arg? mesh_dir = os.path.join(wkdir, 'MESH') su2_mesh_path = os.path.join(mesh_dir, aircraft_name + '_baseline.su2') cfg['MESH_FILENAME'] = '../' + aircraft_name + '_baseline.su2' # Mesh Marker bc_wall_list = su2f.get_mesh_marker(su2_mesh_path) bc_wall_str = '(' + ','.join(bc_wall_list) + ')' cfg['MARKER_EULER'] = bc_wall_str cfg['MARKER_FAR'] = ' (Farfield)' cfg['MARKER_SYM'] = ' (0)' cfg['MARKER_PLOTTING'] = bc_wall_str cfg['MARKER_MONITORING'] = bc_wall_str cfg['MARKER_MOVING'] = '( NONE )' cfg['DV_MARKER'] = bc_wall_str # FFD BOX definition cfg['DV_KIND'] = 'FFD_SETTING' cfg['DV_MARKER'] = '( ' + wing_uid + ')' cfg['FFD_CONTINUITY'] = '2ND_DERIVATIVE' cfg['FFD_DEFINITION'] = '( ' + ted_uid + ', ' + ','.join( ted_corner_list) + ')' cfg['FFD_DEGREE'] = '( 6, 10, 3 )' # TODO: how to chose/calculate these value? if sym_dir: cfg['FFD_DEFINITION'] += '; (' + ted_uid + '_sym, ' + ','.join( ted_corner_sym_list) + ')' cfg['FFD_DEGREE'] += ';( 6, 10, 3 )' # TODO: how to chose/calculate these value? cfg['MESH_OUT_FILENAME'] = '_mesh_ffd_box.su2' # Write Config definition for FFD box config_file_name = 'ConfigDEF.cfg' config_path = os.path.join(wkdir, 'MESH', config_dir_name, config_file_name) su2f.write_config(config_path, cfg) log.info(config_path + ' have has been written.') # FFD BOX rotation for defl in defl_list: cfg['DV_KIND'] = 'FFD_ROTATION' cfg['DV_MARKER'] = '( ' + wing_uid + ')' cfg['DV_PARAM'] = '( ' + ted_uid + ', ' + ','.join(hinge_list) + ')' cfg['DV_VALUE'] = str(defl / 1000) # SU2 use 1/1000 degree... cfg['MESH_FILENAME'] = '_mesh_ffd_box.su2' defl_mesh_name = aircraft_name + '_TED_' + ted_uid + '_defl' + str( defl) + '.su2' if sym_dir: defl_mesh_name = '_' + defl_mesh_name cfg['MESH_OUT_FILENAME'] = defl_mesh_name # Write Config rotation for FFD box config_file_name = 'ConfigROT_defl' + str(defl) + '.cfg' config_path = os.path.join(wkdir, 'MESH', config_dir_name, config_file_name) su2f.write_config(config_path, cfg) log.info(config_path + ' have has been written.') if sym_dir: # TODO: add a condition for anti symetric deflection (e.g. ailerons) cfg['DV_MARKER'] = '( ' + wing_uid + ')' cfg['DV_PARAM'] = '( ' + ted_uid + '_sym, ' + ','.join( hinge_sym_list) + ')' cfg['DV_VALUE'] = str(defl / 1000) # SU2 use 1/1000 degree... cfg['MESH_FILENAME'] = defl_mesh_name defl_mesh_sym_name = aircraft_name + '_TED_' + ted_uid + '_defl' + str( defl) + '_sym.su2' cfg['MESH_OUT_FILENAME'] = defl_mesh_sym_name config_file_name = 'ConfigROT_sym_defl' + str(defl) + '.cfg' config_path = os.path.join(wkdir, 'MESH', config_dir_name, config_file_name) su2f.write_config(config_path, cfg) log.info(config_path + ' have has been written.')
def add_skin_friction(cpacs_path, cpacs_out_path): """ Function to add the skin frinction drag coeffienct to aerodynamic coefficients Function 'add_skin_friction' add the skin friction drag 'cd0' to the SU2 and pyTornado aeroMap, if their UID is not geven, it will add skin friction to all aeroMap. For each aeroMap it creates a new aeroMap where the skin friction drag coeffienct is added with the correct projcetions. Args: cpacs_path (str): Path to CPACS file cpacs_out_path (str): Path to CPACS output file """ tixi = cpsf.open_tixi(cpacs_path) tigl = cpsf.open_tigl(tixi) wing_area_max, wing_span_max = get_largest_wing_dim(tixi, tigl) analyses_xpath = '/cpacs/toolspecific/CEASIOMpy/geometry/analysis' # Requiered input data from CPACS wetted_area = cpsf.get_value(tixi, analyses_xpath + '/wettedArea') # Wing area/span, default values will be calated if no value found in the CPACS file wing_area_xpath = analyses_xpath + '/wingArea' wing_area = cpsf.get_value_or_default(tixi, wing_area_xpath, wing_area_max) wing_span_xpath = analyses_xpath + '/wingSpan' wing_span = cpsf.get_value_or_default(tixi, wing_span_xpath, wing_span_max) aeromap_uid_list = [] # Try to get aeroMapToCalculate aeroMap_to_clculate_xpath = SF_XPATH + '/aeroMapToCalculate' if tixi.checkElement(aeroMap_to_clculate_xpath): aeromap_uid_list = cpsf.get_string_vector(tixi, aeroMap_to_clculate_xpath) else: aeromap_uid_list = [] # If no aeroMap in aeroMapToCalculate, get all existing aeroMap if len(aeromap_uid_list) == 0: try: aeromap_uid_list = apmf.get_aeromap_uid_list(tixi) except: raise ValueError( 'No aeroMap has been found in this CPACS file, skin friction cannot be added!' ) # Get unique aeroMap list aeromap_uid_list = list(set(aeromap_uid_list)) new_aeromap_uid_list = [] # Add skin friction to all listed aeroMap for aeromap_uid in aeromap_uid_list: log.info('adding skin friction coefficients to: ' + aeromap_uid) # Get orignial aeroPerformanceMap AeroCoef = apmf.get_aeromap(tixi, aeromap_uid) AeroCoef.complete_with_zeros() # Create new aeroCoefficient object to store coef with added skin friction AeroCoefSF = apmf.AeroCoefficient() AeroCoefSF.alt = AeroCoef.alt AeroCoefSF.mach = AeroCoef.mach AeroCoefSF.aoa = AeroCoef.aoa AeroCoefSF.aos = AeroCoef.aos # Iterate over all cases case_count = AeroCoef.get_count() for case in range(case_count): # Get parameters for this case alt = AeroCoef.alt[case] mach = AeroCoef.mach[case] aoa = AeroCoef.aoa[case] aos = AeroCoef.aos[case] # Calculate Cd0 for this case cd0 = estimate_skin_friction_coef(wetted_area,wing_area,wing_span, \ mach,alt) # Projection of cd0 on cl, cd and cs axis #TODO: Should Cd0 be projected or not??? aoa_rad = math.radians(aoa) aos_rad = math.radians(aos) cd0_cl = cd0 * math.sin(aoa_rad) cd0_cd = cd0 * math.cos(aoa_rad) * math.cos(aos_rad) cd0_cs = cd0 * math.sin(aos_rad) # Update aerodynamic coefficients cl = AeroCoef.cl[case] + cd0_cl cd = AeroCoef.cd[case] + cd0_cd cs = AeroCoef.cs[case] + cd0_cs # Shoud we change something? e.i. if a force is not apply at aero center...? if len(AeroCoef.cml): cml = AeroCoef.cml[case] else: cml = 0.0 # Shoud be change, just to test pyTornado if len(AeroCoef.cmd): cmd = AeroCoef.cmd[case] else: cmd = 0.0 if len(AeroCoef.cms): cms = AeroCoef.cms[case] else: cms = 0.0 # Add new coefficients into the aeroCoefficient object AeroCoefSF.add_coefficients(cl, cd, cs, cml, cmd, cms) # Create new aeroMap UID aeromap_sf_uid = aeromap_uid + '_SkinFriction' new_aeromap_uid_list.append(aeromap_sf_uid) # Create new description description_xpath = tixi.uIDGetXPath(aeromap_uid) + '/description' sf_description = cpsf.get_value( tixi, description_xpath) + ' Skin friction has been add to this AeroMap.' apmf.create_empty_aeromap(tixi, aeromap_sf_uid, sf_description) # Save aeroCoefficient object Coef in the CPACS file apmf.save_parameters(tixi, aeromap_sf_uid, AeroCoefSF) apmf.save_coefficients(tixi, aeromap_sf_uid, AeroCoefSF) # Get aeroMap list to plot plot_xpath = '/cpacs/toolspecific/CEASIOMpy/aerodynamics/plotAeroCoefficient' aeromap_to_plot_xpath = plot_xpath + '/aeroMapToPlot' if tixi.checkElement(aeromap_to_plot_xpath): aeromap_uid_list = cpsf.get_string_vector(tixi, aeromap_to_plot_xpath) new_aeromap_to_plot = aeromap_uid_list + new_aeromap_uid_list new_aeromap_to_plot = list(set(new_aeromap_to_plot)) cpsf.add_string_vector(tixi, aeromap_to_plot_xpath, new_aeromap_to_plot) else: cpsf.create_branch(tixi, aeromap_to_plot_xpath) cpsf.add_string_vector(tixi, aeromap_to_plot_xpath, new_aeromap_uid_list) log.info('AeroMap "' + aeromap_uid + '" has been added to the CPACS file') cpsf.close_tixi(tixi, cpacs_out_path)
def generate_config_deformed_mesh(cpacs_path, cpacs_out_path, skip_config=False, skip_su2=False): """Function to generate all deform meshes with SU2 from CPACS data Function 'generate_config_deformed_mesh' reads data in the CPACS file and generate all the corresponding directory and config file which allow to generate deformed meshes. Args: cpacs_path (str): Path to CPACS file cpacs_out_path (str):Path to CPACS output file skip_config (bool): skip_su2 (bool): """ tixi = cpsf.open_tixi(cpacs_path) wkdir = ceaf.get_wkdir_or_create_new(tixi) # Get SU2 mesh path su2_mesh_xpath = '/cpacs/toolspecific/CEASIOMpy/filesPath/su2Mesh' su2_mesh_path = cpsf.get_value(tixi, su2_mesh_xpath) if wkdir in su2_mesh_path: log.info('The Baseline SU2 mesh is already in the working directory.') else: mesh_dir = os.path.join(wkdir, 'MESH') if not os.path.isdir(mesh_dir): os.mkdir(mesh_dir) aircraft_name = cpsf.aircraft_name(tixi) su2_mesh_new_path = os.path.join(mesh_dir, aircraft_name + '_baseline.su2') shutil.copyfile(su2_mesh_path, su2_mesh_new_path) tixi.updateTextElement(su2_mesh_xpath, su2_mesh_new_path) if not skip_config: # Control surfaces deflections control_surf_xpath = SU2_XPATH + '/options/clalculateCotrolSurfacesDeflections' control_surf = cpsf.get_value_or_default(tixi, control_surf_xpath, False) if not control_surf: log.warning( 'The CPACS file indicate that Control surface deflection should not be calculated!' ) active_ted_list = [] else: ted_df = get_ted_list(tixi) # TODO: option to calculate only TED selected in cpacs # if ... # active_ted_xpath = SU2_XPATH + '/options/....' # # check element # active_ted_list = cpsf.get_string_vector(tixi,active_ted_xpath) # else: calculate all TED adn all deflections from CPACS # active_ted_list = ted_list for i, row in ted_df.iterrows(): # Unwrap TED data from the dataframe ted_uid = row['ted_uid'] wing_uid = row['wing_uid'] sym_dir = row['sym_dir'] defl_list = row['defl_list'] generate_mesh_def_config(tixi, wkdir, ted_uid, wing_uid, sym_dir, defl_list) if not skip_su2: run_mesh_deformation(tixi, wkdir) cpsf.close_tixi(tixi, cpacs_out_path)