def test_add_skin_friction(): """Test function 'add_skin_friction'""" # User the function to add skin frictions add_skin_friction(CPACS_IN_PATH, CPACS_OUT_PATH) # Read the aeromap with the skin friction added in the output cpacs file cpacs = CPACS(CPACS_OUT_PATH) apm_sf = cpacs.get_aeromap_by_uid("test_apm_SkinFriction") # Expected values cl_list_expected = [0.1, 0.102944, 0.1, 0.102944] cd_list_expected = [0.0269518, 0.0266942, 0.0266942, 0.0264406] cs_list_expected = [0.001, 0.001, 0.00394364, 0.00394364] assert all([ a == approx(b, rel=1e-4) for a, b in zip(apm_sf.get("cl"), cl_list_expected) ]) assert all([ a == approx(b, rel=1e-4) for a, b in zip(apm_sf.get("cd"), cd_list_expected) ]) assert all([ a == approx(b, rel=1e-4) for a, b in zip(apm_sf.get("cs"), cs_list_expected) ]) # Remove the output cpacs file if exist if os.path.exists(CPACS_OUT_PATH): os.remove(CPACS_OUT_PATH)
def main(cpacs_path, cpacs_out_path): log.info("----- Start of " + os.path.basename(__file__) + " -----") # Load the model cpacs = CPACS(cpacs_path) Model = load_surrogate(cpacs.tixi) check_aeromap(cpacs.tixi) if get_value_or_default(cpacs.tixi, SMUSE_XPATH + "/AeroMapOnly", False): aeromap_calculation(Model.sm, cpacs) else: predict_output(Model, cpacs) cpacs.save_cpacs(cpacs_out_path, overwrite=True) log.info("----- End of " + os.path.basename(__file__) + " -----")
def __init__(self, master, cpacs_path, cpacs_out_path, submodule_list, **kwargs): tk.Frame.__init__(self, master, **kwargs) self.pack(fill=tk.BOTH) self.submodule_list = submodule_list self.cpacs_out_path = cpacs_out_path # Notebook for tabs self.tabs = ttk.Notebook(self) self.tabs.grid(row=0, column=0, columnspan=3) # pack()#expand=1, side=tk.LEFT) # Open the cpacs file self.cpacs = CPACS(cpacs_path) aeromap_uid_list = self.cpacs.get_aeromap_uid_list() if not aeromap_uid_list: csv_path = os.path.join( MODULE_DIR, "..", "..", "test_files", "AeroMaps", "Aeromap_1point.csv" ) new_aeromap = self.cpacs.create_aeromap_from_csv(csv_path, "AeroMap_1point") new_aeromap.save() aeromap_uid_list = self.cpacs.get_aeromap_uid_list() # Generate AeroMaps Edition tab AeroMapTab(self.tabs, self.cpacs, aeromap_uid_list) # Generate Auto Tab ============= self.tab_list = [] self._update_all() # Check windows size to change position of button if too small row = 1 col = 1 if self.winfo_screenheight() < 1000: row = 0 col = 3 # General button self.update_button = tk.Button(self, text="Update", command=self._update_all) self.update_button.grid(row=row, column=col, sticky="E") self.close_button = tk.Button(self, text="Save & Quit", command=self._save_quit) self.close_button.grid(row=row, column=col + 1, sticky="W")
def export_aeromaps(cpacs_path, cpacs_out_path): cpacs = CPACS(cpacs_path) aeromap_to_export_xpath = CEASIOMPY_XPATH + "/export/aeroMapToExport" aeromap_uid_list = [] aeromap_uid_list = get_string_vector(cpacs.tixi, aeromap_to_export_xpath) results_dir = get_results_directory("ExportCSV") for aeromap_uid in aeromap_uid_list: aeromap = cpacs.get_aeromap_by_uid(aeromap_uid) csv_path = Path(results_dir, f"{aeromap_uid}.csv") aeromap.export_csv(csv_path) log.info(f"Aeromap(s) has been saved to {csv_path}") cpacs.save_cpacs(cpacs_out_path, overwrite=True)
def test_Point(): """Test the class 'Point'""" cpacs = CPACS(CPACS_PATH) tixi = cpacs.tixi point1 = Point() assert point1.x == 0.0 assert point1.y == 0.0 assert point1.z == 0.0 xpath = "/cpacs/vehicles/aircraft/model/fuselages/fuselage/transformation/scaling" point1.get_cpacs_points(tixi, xpath) assert point1.x == 1.0 assert point1.y == 0.5 assert point1.z == 0.5
def routine_launcher(optim_method, module_optim, wkflow_dir): """Run an optimisation routine or DoE using the OpenMDAO library. This function launches the setup for the routine by setting up the problem with the OpenMDAO component, creating of reading a file containing all the problem parameters and launching the driver. It also specifies where to save the case recordings and launches the results plotting at the end of the routine. Args: optim_method (str): Optimisation method to be used module_optim (list): list of modules (ModuleToRun object) in the optimisation loop """ Rt.type = optim_method Rt.modules = module_optim Rt.wkflow_dir = wkflow_dir # Cpacs from the ouput of the last module cpacs_path = str(Rt.modules[0].cpacs_in) cpacs = CPACS(cpacs_path) Rt.get_user_inputs(cpacs.tixi) Rt.optim_dir = Path(wkflow_dir, "Results", optim_method) Rt.optim_dir.mkdir(parents=True, exist_ok=True) Rt.optim_var_dict = create_variable_library(Rt, cpacs.tixi, Rt.optim_dir) Rt.am_dict = create_aeromap_dict(cpacs, Rt.aeromap_uid, Rt.objective) # Instantiate components and subsystems ## prob = om.Problem() create_om_problem(prob) # Run the model ## prob.run_driver() generate_results(prob)
def compute(self, inputs, outputs): """Compute the objective expression""" # Add new variables to dictionnary cpacs = CPACS(str(Rt.modules[-1].cpacs_out)) update_dict(cpacs.tixi, Rt.optim_var_dict) # Save the whole aeromap if needed if Rt.use_aeromap: update_am_dict(cpacs, Rt.aeromap_uid, Rt.am_dict) for obj in Rt.objective: var_list = split("[+*/-]", obj) for v in var_list: if not v.isdigit() and v != "": exec('{} = inputs["{}"]'.format(v, v)) result = eval(obj) if Rt.minmax == "min": outputs["Objective function " + obj] = result else: outputs["Objective function " + obj] = -result
def test_Transfomation(): """Test the class 'Point'""" cpacs = CPACS(CPACS_PATH) tixi = cpacs.tixi trans1 = Transformation() assert trans1.scaling.x == 1.0 assert trans1.scaling.y == 1.0 assert trans1.scaling.z == 1.0 assert trans1.rotation.x == 0.0 assert trans1.translation.y == 0.0 xpath = "/cpacs/vehicles/aircraft/model/fuselages/fuselage" trans1.get_cpacs_transf(tixi, xpath) assert trans1.scaling.x == 1.0 assert trans1.scaling.y == 0.5 assert trans1.scaling.z == 0.5 assert trans1.rotation.x == 0.1 assert trans1.rotation.y == 0.2 assert trans1.rotation.z == 0.3 assert trans1.translation.x == 0.4 assert trans1.translation.y == 0.5 assert trans1.translation.z == 0.6 trans_wing = Transformation() xpath = "/cpacs/vehicles/aircraft/model/wings/wing" trans_wing.get_cpacs_transf(tixi, xpath) trans_wing.get_parent_transformation() assert trans_wing.translation.x == 0.4 assert trans_wing.translation.y == 0.5 assert trans_wing.translation.z == 0.6
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 cpacs = CPACS(cpacs_path) # Get aeroMap list to plot aeromap_to_plot_xpath = PLOT_XPATH + "/aeroMapToPlot" aeromap_uid_list = [] # Option to select aeromap manualy manual_selct = get_value_or_default(cpacs.tixi, PLOT_XPATH + "/manualSelection", False) if manual_selct: aeromap_uid_list = open_select_aeromap_gui(cpacs) create_branch(cpacs.tixi, aeromap_to_plot_xpath) add_string_vector(cpacs.tixi, aeromap_to_plot_xpath, aeromap_uid_list) else: try: aeromap_uid_list = get_string_vector(cpacs.tixi, aeromap_to_plot_xpath) except ValueError: # If aeroMapToPlot is not define, select manualy anyway aeromap_uid_list = open_select_aeromap_gui(cpacs) create_branch(cpacs.tixi, aeromap_to_plot_xpath) add_string_vector(cpacs.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 = cpacs.get_aeromap_by_uid(aeromap_uid).df 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 = cpacs.ac_name criterion = pd.Series([True] * len(aeromap.index)) groupby_list = ["uid", "machNumber", "altitude", "angleOfSideslip"] # Get criterion from CPACS crit_xpath = PLOT_XPATH + "/criterion" alt_crit = get_value_or_default(cpacs.tixi, crit_xpath + "/alt", "None") mach_crit = get_value_or_default(cpacs.tixi, crit_xpath + "/mach", "None") aos_crit = get_value_or_default(cpacs.tixi, crit_xpath + "/aos", "None") cpacs.save_cpacs(cpacs_out_path, overwrite=True) # Modify criterion and title according to user option if len(aeromap["altitude"].unique()) == 1: title += " - Alt = " + str(aeromap["altitude"].loc[0]) groupby_list.remove("altitude") elif alt_crit not in NONE_LIST: criterion = criterion & (aeromap.altitude == alt_crit) title += " - Alt = " + str(alt_crit) groupby_list.remove("altitude") if len(aeromap["machNumber"].unique()) == 1: title += " - Mach = " + str(aeromap["machNumber"].loc[0]) groupby_list.remove("machNumber") elif mach_crit not in NONE_LIST: criterion = criterion & (aeromap.machNumber == mach_crit) title += " - Mach = " + str(mach_crit) groupby_list.remove("machNumber") if len(aeromap["angleOfSideslip"].unique()) == 1: title += " - AoS = " + str(aeromap["angleOfSideslip"].loc[0]) groupby_list.remove("angleOfSideslip") elif aos_crit not in NONE_LIST: criterion = criterion & (aeromap.angleOfSideslip == aos_crit) title += " - AoS = " + str(aos_crit) groupby_list.remove("angleOfSideslip") 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["angleOfAttack"], grp["cl"], "x-", label=legend) axs[1, 0].plot(grp["angleOfAttack"], grp["cd"], "x-") axs[0, 1].plot(grp["angleOfAttack"], grp["cms"], "x-") axs[1, 1].plot(grp["angleOfAttack"], 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()
class SettingGUI(tk.Frame): """Main window. All the other widgets are stored in this window.""" def __init__(self, master, cpacs_path, cpacs_out_path, submodule_list, **kwargs): tk.Frame.__init__(self, master, **kwargs) self.pack(fill=tk.BOTH) self.submodule_list = submodule_list self.cpacs_out_path = cpacs_out_path # Notebook for tabs self.tabs = ttk.Notebook(self) self.tabs.grid(row=0, column=0, columnspan=3) # pack()#expand=1, side=tk.LEFT) # Open the cpacs file self.cpacs = CPACS(cpacs_path) aeromap_uid_list = self.cpacs.get_aeromap_uid_list() if not aeromap_uid_list: csv_path = os.path.join( MODULE_DIR, "..", "..", "test_files", "AeroMaps", "Aeromap_1point.csv" ) new_aeromap = self.cpacs.create_aeromap_from_csv(csv_path, "AeroMap_1point") new_aeromap.save() aeromap_uid_list = self.cpacs.get_aeromap_uid_list() # Generate AeroMaps Edition tab AeroMapTab(self.tabs, self.cpacs, aeromap_uid_list) # Generate Auto Tab ============= self.tab_list = [] self._update_all() # Check windows size to change position of button if too small row = 1 col = 1 if self.winfo_screenheight() < 1000: row = 0 col = 3 # General button self.update_button = tk.Button(self, text="Update", command=self._update_all) self.update_button.grid(row=row, column=col, sticky="E") self.close_button = tk.Button(self, text="Save & Quit", command=self._save_quit) self.close_button.grid(row=row, column=col + 1, sticky="W") def _update_all(self): # Remove existing AutoTab if len(self.tab_list) > 0: for i in range(len(self.tab_list)): self.tabs.forget(1) self.tab_list = [] # Generate new Auto Tab for module_name in self.submodule_list: specs = mi.get_specs_for_module(module_name) if specs is None: # Specs does not exist continue self.gui_dict = specs.cpacs_inout.get_gui_dict() if not self.gui_dict: # Empty dict --> nothing to do continue tab = AutoTab(self.tabs, self.cpacs, module_name) self.tab_list.append(tab) def _save_quit(self): # Iterate over all existing tabs for tab in self.tab_list: # Iterate in Variable dictionary of each tab for key, var in tab.var_dict.items(): # Get the XPath from the GUI setting dictionary and crate a branch name = tab.gui_dict[key][0] xpath = tab.gui_dict[key][4] create_branch(self.cpacs.tixi, xpath) if name == "__AEROMAP_CHECHBOX": aeromap_uid_list_str = "" for aeromap_uid, aeromap_bool in tab.aeromap_var_dict.items(): if aeromap_bool.get(): aeromap_uid_list_str += aeromap_uid + ";" if aeromap_uid_list_str == "": messagebox.showerror( "ValueError", 'In the Tab "' + tab.module_name + '", no value has been selected for "' + name + '" ', ) raise TypeError("No value has been selected for " + name + " !") self.cpacs.tixi.updateTextElement(xpath, aeromap_uid_list_str) # '__AEROMAP_SELECTION' and list Type value will be saved as any other variable else: if str(var.get()) == "": # Not working when it expect an 'int' or a 'float' messagebox.showerror( "ValueError", 'In the Tab "' + tab.module_name + '", no value has been entered for "' + name + '" ', ) raise TypeError("No value has been entered for " + name + " !") try: self.cpacs.tixi.updateTextElement(xpath, str(var.get())) except: messagebox.showerror( "TypeError", 'In the Tab "' + tab.module_name + '", the value "' + name + '" has not the correct type!', ) raise TypeError(name + " has not the correct type!") self.cpacs.save_cpacs(self.cpacs_out_path, overwrite=True) self.quit()
def add_skin_friction(cpacs_path, cpacs_out_path): """Function to add the skin frictions drag coefficient to aerodynamic coefficients Function 'add_skin_friction' add the skin friction drag 'cd0' to the SU2 and pyTornado aeroMap, if their UID is not given, it will add skin friction to all aeroMap. For each aeroMap it creates a new aeroMap where the skin friction drag coefficient is added with the correct projections. Args: cpacs_path (str): Path to CPACS file cpacs_out_path (str): Path to CPACS output file """ # Load a CPACS file cpacs = CPACS(cpacs_path) analyses_xpath = "/cpacs/toolspecific/CEASIOMpy/geometry/analysis" # Required input data from CPACS wetted_area = get_value(cpacs.tixi, analyses_xpath + "/wettedArea") # Wing area/span, default values will be calculated if no value found in the CPACS file wing_area_xpath = analyses_xpath + "/wingArea" wing_area = get_value_or_default(cpacs.tixi, wing_area_xpath, cpacs.aircraft.wing_area) wing_span_xpath = analyses_xpath + "/wingSpan" wing_span = get_value_or_default(cpacs.tixi, wing_span_xpath, cpacs.aircraft.wing_span) # Get aeroMapToCalculate aeroMap_to_calculate_xpath = SF_XPATH + "/aeroMapToCalculate" if cpacs.tixi.checkElement(aeroMap_to_calculate_xpath): aeromap_uid_list = get_string_vector(cpacs.tixi, aeroMap_to_calculate_xpath) else: aeromap_uid_list = [] # If no aeroMap in aeroMapToCalculate, get all existing aeroMap if len(aeromap_uid_list) == 0: aeromap_uid_list = cpacs.get_aeromap_uid_list() if not aeromap_uid_list: 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) aeromap = cpacs.get_aeromap_by_uid(aeromap_uid) # Create new aeromap object to store coef with added skin friction aeromap_sf = cpacs.duplicate_aeromap(aeromap_uid, aeromap_uid + "_SkinFriction") aeromap_sf.description = ( aeromap_sf.description + " Skin friction has been add to this AeroMap.") # Add skin friction to all force coefficient (with projections) aeromap_sf.df["cd"] = aeromap.df.apply( lambda row: row["cd"] + estimate_skin_friction_coef( wetted_area, wing_area, wing_span, row["machNumber"], row[ "altitude"]) * math.cos(math.radians(row["angleOfAttack"])) * math.cos(math.radians(row["angleOfSideslip"])), axis=1, ) aeromap_sf.df["cl"] = aeromap.df.apply( lambda row: row["cl"] + estimate_skin_friction_coef( wetted_area, wing_area, wing_span, row[ "machNumber"], row["altitude"]) * math.sin( math.radians(row["angleOfAttack"])), axis=1, ) aeromap_sf.df["cs"] = aeromap.df.apply( lambda row: row["cs"] + estimate_skin_friction_coef( wetted_area, wing_area, wing_span, row["machNumber"], row[ "altitude"]) * math.sin( math.radians(row["angleOfSideslip"])), axis=1, ) # TODO: Should we change something in moment coef? # e.i. if a force is not apply at aero center...? aeromap_sf.save() # Get aeroMap list to plot plot_xpath = "/cpacs/toolspecific/CEASIOMpy/aerodynamics/plotAeroCoefficient" aeromap_to_plot_xpath = plot_xpath + "/aeroMapToPlot" if cpacs.tixi.checkElement(aeromap_to_plot_xpath): aeromap_uid_list = get_string_vector(cpacs.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)) add_string_vector(cpacs.tixi, aeromap_to_plot_xpath, new_aeromap_to_plot) else: create_branch(cpacs.tixi, aeromap_to_plot_xpath) add_string_vector(cpacs.tixi, aeromap_to_plot_xpath, new_aeromap_uid_list) log.info('AeroMap "' + aeromap_uid + '" has been added to the CPACS file') cpacs.save_cpacs(cpacs_out_path, overwrite=True)
def get_cl(cpacs_path, cpacs_out_path): """Function to calculate CL required as a function of the parameter found in the CPACS file. Function 'get_cl' find input value in the CPACS file, calculate the required CL (with function calculate_cl) and save the CL value in the CPACS file. Args: cpacs_path (str): Path to CPACS file cpacs_out_path (str): Path to CPACS output file """ cpacs = CPACS(cpacs_path) tixi = cpacs.tixi # XPath definition model_xpath = "/cpacs/vehicles/aircraft/model" ref_area_xpath = model_xpath + "/reference/area" mass_type_xpath = "/cpacs/toolspecific/CEASIOMpy/aerodynamics/clCalculation/massType" custom_mass_xpath = "/cpacs/toolspecific/CEASIOMpy/aerodynamics/clCalculation/customMass" percent_fuel_mass_xpath = ( "/cpacs/toolspecific/CEASIOMpy/aerodynamics/clCalculation/percentFuelMass" ) cruise_alt_xpath = CLCALC_XPATH + "/cruiseAltitude" cruise_mach_xpath = CLCALC_XPATH + "/cruiseMach" load_fact_xpath = CLCALC_XPATH + "/loadFactor" # Required input data from CPACS ref_area = get_value(tixi, ref_area_xpath) log.info(f"Aircraft reference area is {ref_area} [m^2]") # Mass mass = None mass_type = get_value_or_default(tixi, mass_type_xpath, "mTOM") if mass_type == "Custom": mass = get_value(tixi, custom_mass_xpath) elif mass_type == "% fuel mass": percent_fuel_mass = get_value(tixi, percent_fuel_mass_xpath) mtom = get_value( tixi, model_xpath + "/analyses/massBreakdown/designMasses/mTOM/mass") mzfm = get_value( tixi, model_xpath + "/analyses/massBreakdown/designMasses/mZFM/mass") if mzfm > mtom: raise ValueError("mZFM is bigger than mTOM!") mass = (mtom - mzfm) * percent_fuel_mass / 100 + mzfm else: mass_xpath = model_xpath + f"/analyses/massBreakdown/designMasses/{mass_type}/mass" mass = get_value(tixi, mass_xpath) # mtom = get_value(tixi,mtom_xpath) if mass: log.info(f"Aircraft mass use for this analysis is {mass} [kg]") else: raise ValueError("The chosen aircraft mass has not been found!") # Required 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) # CL calculation target_cl = calculate_cl(ref_area, cruise_alt, cruise_mach, mass, load_fact) # Save TargetCL and fixedCL option 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") cpacs.save_cpacs(cpacs_out_path, overwrite=True)
def generate_su2_cfd_config(cpacs_path, cpacs_out_path, wkdir): """Function to create SU2 confif file. Function 'generate_su2_cfd_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 cpacs = CPACS(cpacs_path) # Get SU2 mesh path su2_mesh_path = get_value(cpacs.tixi, SU2MESH_XPATH) # Get SU2 settings max_iter_xpath = SU2_XPATH + "/settings/maxIter" max_iter = get_value_or_default(cpacs.tixi, max_iter_xpath, 200) cfl_nb_xpath = SU2_XPATH + "/settings/cflNumber" cfl_nb = get_value_or_default(cpacs.tixi, cfl_nb_xpath, 1.0) mg_level_xpath = SU2_XPATH + "/settings/multigridLevel" mg_level = get_value_or_default(cpacs.tixi, mg_level_xpath, 3) # Mesh Marker bc_wall_xpath = SU2_XPATH + "/boundaryConditions/wall" bc_farfield_xpath = SU2_XPATH + "/boundaryConditions/farfield" bc_wall_list, engine_bc_list = get_mesh_marker(su2_mesh_path) create_branch(cpacs.tixi, bc_wall_xpath) bc_wall_str = ";".join(bc_wall_list) cpacs.tixi.updateTextElement(bc_wall_xpath, bc_wall_str) create_branch(cpacs.tixi, bc_farfield_xpath) bc_farfiled_str = ";".join(engine_bc_list) cpacs.tixi.updateTextElement(bc_farfield_xpath, bc_farfiled_str) # Fixed CL parameters fixed_cl_xpath = SU2_XPATH + "/fixedCL" fixed_cl = get_value_or_default(cpacs.tixi, fixed_cl_xpath, "NO") target_cl_xpath = SU2_XPATH + "/targetCL" target_cl = get_value_or_default(cpacs.tixi, target_cl_xpath, 1.0) if fixed_cl == "NO": active_aeroMap_xpath = SU2_XPATH + "/aeroMapUID" aeromap_uid = get_value(cpacs.tixi, active_aeroMap_xpath) log.info( f'Configuration file for "{aeromap_uid}" calculation will be created.' ) active_aeromap = cpacs.get_aeromap_by_uid(aeromap_uid) # Get parameters of the aeroMap (altitude, machNumber, angleOfAttack, angleOfSideslip) alt_list = active_aeromap.get("altitude").tolist() mach_list = active_aeromap.get("machNumber").tolist() aoa_list = active_aeromap.get("angleOfAttack").tolist() aos_list = active_aeromap.get("angleOfSideslip").tolist() param_count = len(alt_list) else: # if fixed_cl == 'YES': log.info( "Configuration file for fixed CL calculation will be created.") # Parameters fixed CL calulation param_count = 1 # Create a new aeroMap fix_cl_aeromap = cpacs.create_aeromap("aeroMap_fixedCL_SU2") fix_cl_aeromap.description = "AeroMap created for SU2 fixed CL value of: " + str( target_cl) # Get cruise mach and altitude cruise_mach_xpath = RANGE_XPATH + "/cruiseMach" mach = get_value_or_default(cpacs.tixi, cruise_mach_xpath, 0.78) cruise_alt_xpath = RANGE_XPATH + "/cruiseAltitude" alt = get_value_or_default(cpacs.tixi, cruise_alt_xpath, 12000) # Add new parameters to the aeroMap and save it fix_cl_aeromap.add_row(alt=alt, mach=mach, aos=0.0, aoa=0.0) fix_cl_aeromap.save() # Parameter lists alt_list = [alt] mach_list = [mach] aoa_list = [0.0] aos_list = [0.0] # Get and modify the default configuration file cfg = ConfigFile(DEFAULT_CONFIG_PATH) # General parmeters cfg["REF_LENGTH"] = cpacs.aircraft.ref_lenght cfg["REF_AREA"] = cpacs.aircraft.ref_area cfg["REF_ORIGIN_MOMENT_X"] = cpacs.aircraft.ref_point_x cfg["REF_ORIGIN_MOMENT_Y"] = cpacs.aircraft.ref_point_y cfg["REF_ORIGIN_MOMENT_Z"] = cpacs.aircraft.ref_point_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, " + ",".join(engine_bc_list) + ")" 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 = Atmosphere(alt) cfg["MACH_NUMBER"] = mach cfg["AOA"] = aoa cfg["SIDESLIP_ANGLE"] = aos cfg["FREESTREAM_PRESSURE"] = Atm.pressure[0] cfg["FREESTREAM_TEMPERATURE"] = Atm.temperature[0] 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) cfg.write_file(config_output_path, overwrite=True) # Damping derivatives damping_der_xpath = SU2_XPATH + "/options/clalculateDampingDerivatives" damping_der = get_value_or_default(cpacs.tixi, damping_der_xpath, False) if damping_der: rotation_rate_xpath = SU2_XPATH + "/options/rotationRate" rotation_rate = get_value_or_default(cpacs.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) cfg.write_file(config_output_path, overwrite=True) 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) cfg.write_file(config_output_path, overwrite=True) 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) cfg.write_file(config_output_path, overwrite=True) log.info("Damping derivatives cases directory has been created.") # Control surfaces deflections control_surf_xpath = SU2_XPATH + "/options/clalculateCotrolSurfacesDeflections" control_surf = get_value_or_default(cpacs.tixi, control_surf_xpath, False) if control_surf: # Get deformed mesh list su2_def_mesh_xpath = SU2_XPATH + "/availableDeformedMesh" if cpacs.tixi.checkElement(su2_def_mesh_xpath): su2_def_mesh_list = get_string_vector(cpacs.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) cfg.write_file(config_output_path, overwrite=True) # TODO: change that, but if it is save in tooloutput it will be erease by results... cpacs.save_cpacs(cpacs_out_path, overwrite=True)
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 """ cpacs = CPACS(cpacs_path) if not os.path.exists(wkdir): raise OSError("The working directory : " + wkdir + "does not exit!") dir_list = os.listdir(wkdir) # Get and save Wetted area wetted_area = get_wetted_area(wkdir) create_branch(cpacs.tixi, WETTED_AREA_XPATH) cpacs.tixi.updateDoubleElement(WETTED_AREA_XPATH, wetted_area, "%g") # Check if loads shoud be extracted check_extract_loads_xpath = SU2_XPATH + "/results/extractLoads" check_extract_loads = get_value_or_default(cpacs.tixi, check_extract_loads_xpath, False) # Get fixed_cl option fixed_cl_xpath = SU2_XPATH + "/fixedCL" fixed_cl = get_value_or_default(cpacs.tixi, fixed_cl_xpath, "NO") # Get aeroMap uid if fixed_cl == "YES": aeromap_uid = "aeroMap_fixedCL_SU2" elif fixed_cl == "NO": su2_aeromap_xpath = SU2_XPATH + "/aeroMapUID" aeromap_uid = get_value(cpacs.tixi, su2_aeromap_xpath) else: raise ValueError( "The value for fixed_cl is not valid! Should be YES or NO") aeromap = cpacs.get_aeromap_by_uid(aeromap_uid) alt_list = aeromap.get("altitude").tolist() mach_list = aeromap.get("machNumber").tolist() aoa_list = aeromap.get("angleOfAttack").tolist() aos_list = aeromap.get("angleOfSideslip").tolist() case_dir_list = [dir for dir in dir_list if "Case" in dir] for config_dir in sorted(case_dir_list): case_nb = int(config_dir.split("_")[0].split("Case")[1]) aoa = aoa_list[case_nb] aos = aos_list[case_nb] mach = mach_list[case_nb] alt = alt_list[case_nb] config_dir_path = os.path.join(wkdir, config_dir) if os.path.isdir(config_dir_path): force_file_path = os.path.join(config_dir_path, "forces_breakdown.dat") if not os.path.isfile(force_file_path): raise OSError("No result force file have been found!") if fixed_cl == "YES": cl_cd, aoa = get_efficiency_and_aoa(force_file_path) # Replace aoa with the with the value from fixed cl calculation aeromap.df.loc[0, ["angleOfAttack"]] = aoa # Save cl/cd found during the fixed CL calculation # TODO: maybe save cl/cd somewhere else lDRatio_xpath = "/cpacs/toolspecific/CEASIOMpy/ranges/lDRatio" create_branch(cpacs.tixi, lDRatio_xpath) cpacs.tixi.updateDoubleElement(lDRatio_xpath, cl_cd, "%g") # Read result file with open(force_file_path) 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 = get_value_or_default(cpacs.tixi, rotation_rate_xpath, -1.0) ref_len = cpacs.aircraft.ref_lenght adim_rot_rate = rotation_rate * ref_len / velocity coefs = { "cl": cl, "cd": cd, "cs": cs, "cmd": cmd, "cms": cms, "cml": cml } if "_dp" in config_dir: for coef in COEFS: coef_baseline = aeromap.get(coef, alt=alt, mach=mach, aoa=aoa, aos=aos) dcoef = (coefs[coef] - coef_baseline) / adim_rot_rate aeromap.add_damping_derivatives( alt=alt, mach=mach, aos=aos, aoa=aoa, coef=coef, axis="dp", value=dcoef, rate=rotation_rate, ) elif "_dq" in config_dir: for coef in COEFS: coef_baseline = aeromap.get(coef, alt=alt, mach=mach, aoa=aoa, aos=aos) dcoef = (coefs[coef] - coef_baseline) / adim_rot_rate aeromap.add_damping_derivatives( alt=alt, mach=mach, aos=aos, aoa=aoa, coef=coef, axis="dq", value=dcoef, rate=rotation_rate, ) elif "_dr" in config_dir: for coef in COEFS: coef_baseline = aeromap.get(coef, alt=alt, mach=mach, aoa=aoa, aos=aos) dcoef = (coefs[coef] - coef_baseline) / adim_rot_rate aeromap.add_damping_derivatives( alt=alt, mach=mach, aos=aos, aoa=aoa, coef=coef, axis="dr", value=dcoef, rate=rotation_rate, ) elif "_TED_" in config_dir: # TODO: convert when it is possible to save TED in cpacspy raise NotImplementedError("TED not implemented yet") # 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 = a.p.m.f.IncrementMap(ted_uid) # 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: # Baseline coefficients, (no damping derivative or control surfaces case) aeromap.add_coefficients( alt=alt, mach=mach, aos=aos, aoa=aoa, cd=cd, cl=cl, cs=cs, cml=cml, cmd=cmd, cms=cms, ) if check_extract_loads: results_files_dir = os.path.join(wkdir, config_dir) extract_loads(results_files_dir) # Save object Coef in the CPACS file aeromap.save() # Save the CPACS file cpacs.save_cpacs(cpacs_out_path, overwrite=True)