def test_create_branch(self): """Test the function 'create_branch'""" tixi_handle = open_tixi(self.CPACS_IN_PATH) update_branch = '/cpacs/header/updates/update' new_branch = '/cpacs/header/updates/update[3]/test/test1/test2' # This branch should be added tixi = create_branch(tixi_handle, update_branch, True) # This branch should not be added tixi = create_branch(tixi, update_branch, False) # 'new_branch' should be added tixi = create_branch(tixi, new_branch) # Save modified tixi in the output CPACS file close_tixi(tixi, self.CPACS_OUT_PATH) # Reopen the output CPACS file to check if branches have been added tixi_out = open_tixi(self.CPACS_OUT_PATH) # Check if the number of "update" child is equal to 3 namedchild_nb = tixi_out.getNamedChildrenCount('/cpacs/header/updates', 'update') self.assertEqual(namedchild_nb, 3) # Check if 'new_branch' has been added branch_check = tixi_out.checkElement(new_branch) self.assertEqual(branch_check, True)
def test_open_tigl(self): """Test the function 'open_tigl'""" tixi_handle = open_tixi(self.CPACS_IN_PATH) tigl_handle = open_tigl(tixi_handle) self.assertIsNotNone(tigl_handle)
def test_fuse_nb(self): '''Test if the number of fuselage is equal to 1''' tixi_handle = open_tixi(self.CPACS_IN_PATH) fuse_nb = tixi_handle.getNamedChildrenCount( '/cpacs/vehicles/aircraft\ /model/fuselages', 'fuselage') self.assertEqual(fuse_nb, 1)
def test_wing_nb(self): '''Test if the number of wing is equal to 1''' tixi_handle = open_tixi(self.CPACS_IN_PATH) wing_nb = tixi_handle.getNamedChildrenCount( '/cpacs/vehicles/aircraft\ /model/wings', 'wing') self.assertEqual(wing_nb, 1)
def test_close_tixi(self): """Test the function 'close_tixi'""" tixi_handle = open_tixi(self.CPACS_IN_PATH) # Save unmodified tixi in the output CPACS file close_tixi(tixi_handle, self.CPACS_OUT_PATH) # Read Input and Ouput CPACS file as text, to compare them with open(self.CPACS_IN_PATH) as file_in: lines_cpacs_in = file_in.readlines() with open(self.CPACS_OUT_PATH) as file_out: lines_cpacs_out = file_out.readlines() self.assertEqual(lines_cpacs_in, lines_cpacs_out)
def test_copy_branch(self): """Test the function 'copy_branch'""" tixi_handle = open_tixi(self.CPACS_IN_PATH) # Create a new 'header' branch and copy the original 'header' into it xpath_new = '/cpacs/header' xpath_from = '/cpacs/header[1]' xpath_to = '/cpacs/header[2]' tixi = create_branch(tixi_handle, xpath_new, True) tixi = copy_branch(tixi, xpath_from, xpath_to) # Check if a specific element has been copied xpath_elem_from = '/cpacs/header[1]/updates/update[1]/timestamp' xpath_elem_to = '/cpacs/header[2]/updates/update[1]/timestamp' elem_from = tixi_handle.getTextElement(xpath_elem_from) elem_to = tixi.getTextElement(xpath_elem_to) self.assertEqual(elem_from, elem_to) # Check if a specific attribute has been copied attrib_text_from = tixi_handle.getTextAttribute(xpath_elem_from, 'uID') attrib_text_to = tixi.getTextAttribute(xpath_elem_to, 'uID') self.assertEqual(attrib_text_from, attrib_text_to)
def convert_cpacs_to_sumo(cpacs_path): """ Function to convert a CPACS file geometry into a SUMO file geometry Function 'convert_cpacs_to_sumo' open an input cpacs file with TIXI handle and via two main loop, one for fuselage(s), one for wing(s) it convert every element (as much as possible) in the SUMO (.smx) format, which is also an xml file. Due to some differences between both format, some CPACS definition could lead to issues. The output sumo file is saved in the folder /ToolOutput Source : CPACS documentation ARGUMENTS (str) cpacs_path -- Path to the CPACS file RETURNS (str) OUTPUT_SMX -- Path to the SUMO file (./ToolOutput/Toolouput.smx) """ MODULE_DIR = os.path.dirname(os.path.abspath(__file__)) EMPTY_SMX = MODULE_DIR + '/files/sumo_empty.smx' OUTPUT_SMX = MODULE_DIR + '/ToolOutput/ToolOutput.smx' tixi = open_tixi(cpacs_path) sumo = open_tixi(EMPTY_SMX) # Fuslage(s) ----- FUSELAGES_PATH = '/cpacs/vehicles/aircraft/model/fuselages' if tixi.checkElement(FUSELAGES_PATH): fus_cnt = tixi.getNamedChildrenCount(FUSELAGES_PATH, 'fuselage') log.info(str(fus_cnt) + ' fuselage has been found.') else: fus_cnt = 0 if fus_cnt == 0: log.warning('No fuselage has been found in this CPACS file!') for i_fus in range(fus_cnt): fus_path = FUSELAGES_PATH + '/fuselage[' + str(i_fus+1) + ']' fus_uid = tixi.getTextAttribute(fus_path, 'uID') fus_transf = Transformation() fus_transf.get_cpacs_transf(tixi, fus_path + '/transformation') # Create new body (SUMO) sumo.createElementAtIndex('/Assembly', 'BodySkeleton', i_fus+1) body_path = '/Assembly/BodySkeleton[' + str(i_fus+1) + ']' sumo.addTextAttribute(body_path, 'akimatg', 'false') sumo.addTextAttribute(body_path, 'name', fus_uid) body_tansf = Transformation() body_tansf.translation = fus_transf.translation # Convert angles body_tansf.rotation = euler2fix(fus_transf.rotation) # Add body rotation body_rot_str = str(math.radians(body_tansf.rotation.x)) + ' ' \ + str(math.radians(body_tansf.rotation.y)) + ' ' \ + str(math.radians(body_tansf.rotation.z)) sumo.addTextAttribute(body_path, 'rotation', body_rot_str) # Add body origin body_ori_str = str(body_tansf.translation.x) + ' ' \ + str(body_tansf.translation.y) + ' ' \ + str(body_tansf.translation.z) sumo.addTextAttribute(body_path, 'origin', body_ori_str) # Positionings if tixi.checkElement(fus_path + '/positionings'): pos_cnt = tixi.getNamedChildrenCount(fus_path + '/positionings', 'positioning') log.info(str(fus_cnt) + ' "Positionning" has been found : ') pos_x_list = [] pos_y_list = [] pos_z_list = [] from_sec_list = [] to_sec_list = [] for i_pos in range(pos_cnt): pos_path = fus_path + '/positionings/positioning[' \ + str(i_pos+1) + ']' length = tixi.getDoubleElement(pos_path + '/length') sweep_deg = tixi.getDoubleElement(pos_path + '/sweepAngle') sweep = math.radians(sweep_deg) dihedral_deg = tixi.getDoubleElement(pos_path+'/dihedralAngle') dihedral = math.radians(dihedral_deg) # Get the corresponding translation of each positionning pos_x_list.append(length * math.sin(sweep)) pos_y_list.append(length * math.cos(dihedral) * math.cos(sweep)) pos_z_list.append(length * math.sin(dihedral) * math.cos(sweep)) # Get which section are connected by the positionning if tixi.checkElement(pos_path + '/fromSectionUID'): from_sec = tixi.getTextElement(pos_path +'/fromSectionUID') else: from_sec = '' from_sec_list.append(from_sec) if tixi.checkElement(pos_path + '/toSectionUID'): to_sec = tixi.getTextElement(pos_path + '/toSectionUID') else: to_sec = '' to_sec_list.append(to_sec) # Re-loop though the positionning to re-order them for j_pos in range(pos_cnt): if from_sec_list[j_pos] == '': prev_pos_x = 0 prev_pos_y = 0 prev_pos_z = 0 elif from_sec_list[j_pos] == to_sec_list[j_pos-1]: prev_pos_x = pos_x_list[j_pos-1] prev_pos_y = pos_y_list[j_pos-1] prev_pos_z = pos_z_list[j_pos-1] else: index_prev = to_sec_list.index(from_sec_list[j_pos]) prev_pos_x = pos_x_list[index_prev] prev_pos_y = pos_y_list[index_prev] prev_pos_z = pos_z_list[index_prev] pos_x_list[j_pos] += prev_pos_x pos_y_list[j_pos] += prev_pos_y pos_z_list[j_pos] += prev_pos_z else: log.warning('No "positionings" have been found!') pos_cnt = 0 #Sections sec_cnt = tixi.getNamedChildrenCount(fus_path + '/sections', 'section') log.info(" -" + str(sec_cnt) + ' fuselage sections have been found') if pos_cnt == 0: pos_x_list = [0.0] * sec_cnt pos_y_list = [0.0] * sec_cnt pos_z_list = [0.0] * sec_cnt for i_sec in range(sec_cnt): sec_path = fus_path + '/sections/section[' + str(i_sec+1) + ']' sec_uid = tixi.getTextAttribute(sec_path, 'uID') sec_transf = Transformation() sec_transf.get_cpacs_transf(tixi, sec_path + '/transformation') if (sec_transf.rotation.x or sec_transf.rotation.y or sec_transf.rotation.z): log.warning('Sections "' + sec_uid + '" is rotated, it is \ not possible to take that into acount in SUMO !') # Elements elem_cnt = tixi.getNamedChildrenCount(sec_path + '/elements', 'element') if elem_cnt > 1: log.warning("Sections " + sec_uid + " contains multiple \ element, it could be an issue for the conversion \ to SUMO!") for i_elem in range(elem_cnt): elem_path = sec_path + '/elements/element[' \ + str(i_elem + 1) + ']' elem_uid = tixi.getTextAttribute(elem_path, 'uID') elem_transf = Transformation() elem_transf.get_cpacs_transf(tixi,elem_path +'/transformation') if (elem_transf.rotation.x or elem_transf.rotation.y or elem_transf.rotation.z): log.warning('Element "' + elem_uid + '" is rotated, it \ is not possible to take that into acount in \ SUMO !') # Fuselage profiles prof_uid = tixi.getTextElement(elem_path+'/profileUID') prof_path = tixi.uIDGetXPath(prof_uid) prof_vect_x_str = tixi.getTextElement(prof_path+'/pointList/x') prof_vect_y_str = tixi.getTextElement(prof_path+'/pointList/y') prof_vect_z_str = tixi.getTextElement(prof_path+'/pointList/z') # Transform sting into list of float prof_vect_x = [] for i, item in enumerate(prof_vect_x_str.split(';')): if item: prof_vect_x.append(float(item)) prof_vect_y = [] for i, item in enumerate(prof_vect_y_str.split(';')): if item: prof_vect_y.append(float(item)) prof_vect_z = [] for i, item in enumerate(prof_vect_z_str.split(';')): if item: prof_vect_z.append(float(item)) prof_size_y = (max(prof_vect_y) - min(prof_vect_y))/2 prof_size_z = (max(prof_vect_z) - min(prof_vect_z))/2 prof_vect_y[:] = [y / prof_size_y for y in prof_vect_y] prof_vect_z[:] = [z / prof_size_z for z in prof_vect_z] prof_min_y = min(prof_vect_y) prof_max_y = max(prof_vect_y) prof_min_z = min(prof_vect_z) prof_max_z = max(prof_vect_z) prof_vect_y[:] = [y - 1 - prof_min_y for y in prof_vect_y] prof_vect_z[:] = [z - 1 - prof_min_z for z in prof_vect_z] # Could be a problem if they are less positionings than secions # TODO: solve that! pos_y_list[i_sec] += (1 + prof_min_y) * prof_size_y pos_z_list[i_sec] += (1 + prof_min_z) * prof_size_z #To Plot a particular section # if i_sec==30: # plt.plot(prof_vect_z, prof_vect_y,'x') # plt.xlabel('y') # plt.ylabel('z') # plt.grid(True) # plt.show() # Put value in SUMO format body_frm_center_x = ( elem_transf.translation.x \ + sec_transf.translation.x \ + pos_x_list[i_sec]) \ * fus_transf.scale.x body_frm_center_y = ( elem_transf.translation.y \ * sec_transf.scale.y \ + sec_transf.translation.y \ + pos_y_list[i_sec]) \ * fus_transf.scale.y body_frm_center_z = ( elem_transf.translation.z \ * sec_transf.scale.z \ + sec_transf.translation.z \ + pos_z_list[i_sec]) \ * fus_transf.scale.z body_frm_height = prof_size_z * 2 * elem_transf.scale.z \ * sec_transf.scale.z * fus_transf.scale.z if body_frm_height < 0.01: body_frm_height = 0.01 body_frm_width = prof_size_y * 2 * elem_transf.scale.y \ * sec_transf.scale.y * fus_transf.scale.y if body_frm_width < 0.01: body_frm_width = 0.01 # Convert the profile points in the SMX format prof_str = '' teta_list = [] teta_half = [] prof_vect_y_half = [] prof_vect_z_half = [] check_max = 0 check_min = 0 # Use polar angle to keep point in the correct order for i, item in enumerate(prof_vect_y): teta_list.append(math.atan2(prof_vect_z[i],prof_vect_y[i])) for t, teta in enumerate(teta_list): HALF_PI = math.pi/2 EPSILON = 0.04 if abs(teta) <= HALF_PI-EPSILON: teta_half.append(teta) prof_vect_y_half.append(prof_vect_y[t]) prof_vect_z_half.append(prof_vect_z[t]) elif abs(teta) < HALF_PI+EPSILON: # Check if not the last element of the list if not t == len(teta_list)-1: next_val = prof_vect_z[t+1] # Check if it is better to keep next point if not abs(next_val) > abs(prof_vect_z[t]): if prof_vect_z[t] > 0 and not check_max: teta_half.append(teta) # Force y=0, to get symmetrical profile prof_vect_y_half.append(0) prof_vect_z_half.append(prof_vect_z[t]) check_max = 1 elif prof_vect_z[t] < 0 and not check_min: teta_half.append(teta) # Force y=0, to get symmetrical profile prof_vect_y_half.append(0) prof_vect_z_half.append(prof_vect_z[t]) check_min = 1 # Sort points by teta value, to fit the SUMO profile format teta_half, prof_vect_z_half, prof_vect_y_half = \ (list(t) for t in zip(*sorted(zip(teta_half, prof_vect_z_half, prof_vect_y_half)))) # Write profile as a string and add y=0 point at the begining # and at the end to ensure symmmetry if not check_min: prof_str += str(0) + ' ' + str(prof_vect_z_half[0]) + ' ' for i, item in enumerate(prof_vect_z_half): prof_str += str(round(prof_vect_y_half[i], 4)) + ' ' \ + str(round(prof_vect_z_half[i], 4)) + ' ' if not check_max: prof_str += str(0) + ' ' + str(prof_vect_z_half[i]) + ' ' # Write the SUMO file sumo.addTextElementAtIndex(body_path, 'BodyFrame', prof_str, i_sec+1) frame_path = body_path + '/BodyFrame[' + str(i_sec+1) + ']' body_center_str = str(body_frm_center_x) + ' ' + \ str(body_frm_center_y) + ' ' + \ str(body_frm_center_z) sumo.addTextAttribute(frame_path, 'center', body_center_str) sumo.addTextAttribute(frame_path,'height',str(body_frm_height)) sumo.addTextAttribute(frame_path, 'width', str(body_frm_width)) sumo.addTextAttribute(frame_path, 'name', sec_uid) # To remove the default BodySkeleton if fus_cnt == 0: sumo.removeElement('/Assembly/BodySkeleton') else: sumo.removeElement('/Assembly/BodySkeleton[' + str(fus_cnt+1) + ']') # Wing(s) ----- WINGS_PATH = '/cpacs/vehicles/aircraft/model/wings' if tixi.checkElement(WINGS_PATH): wing_cnt = tixi.getNamedChildrenCount(WINGS_PATH, 'wing') log.info(str(wing_cnt) + ' wings has been found.') else: wing_cnt = 0 if wing_cnt == 0: log.warning('No wings has been found in this CPACS file!') for i_wing in range(wing_cnt): wing_path = WINGS_PATH + '/wing[' + str(i_wing+1) + ']' wing_uid = tixi.getTextAttribute(wing_path, 'uID') wing_transf = Transformation() wing_transf.get_cpacs_transf(tixi, wing_path + '/transformation') # Create new wing (SUMO) sumo.createElementAtIndex('/Assembly', 'WingSkeleton', i_wing+1) wg_sk_path = '/Assembly/WingSkeleton[' + str(i_wing+1) + ']' sumo.addTextAttribute(wg_sk_path, 'akimatg', 'false') sumo.addTextAttribute(wg_sk_path, 'name', wing_uid) # Create a class for the transformation of the WingSkeleton wg_sk_tansf = Transformation() # Convert WingSkeleton rotation and add it to SUMO wg_sk_tansf.rotation = euler2fix(wing_transf.rotation) wg_sk_rot_str = str(math.radians(wg_sk_tansf.rotation.x)) + ' ' \ + str(math.radians(wg_sk_tansf.rotation.y)) + ' ' \ + str(math.radians(wg_sk_tansf.rotation.z)) sumo.addTextAttribute(wg_sk_path, 'rotation', wg_sk_rot_str) # Add WingSkeleton origin wg_sk_tansf.translation = wing_transf.translation wg_sk_ori_str = str(wg_sk_tansf.translation.x) + ' ' \ + str(wg_sk_tansf.translation.y) + ' ' \ + str(wg_sk_tansf.translation.z) sumo.addTextAttribute(wg_sk_path, 'origin', wg_sk_ori_str) if tixi.checkAttribute(wing_path, 'symmetry'): if tixi.getTextAttribute(wing_path, 'symmetry') == 'x-z-plane': sumo.addTextAttribute(wg_sk_path, 'flags', 'autosym,detectwinglet') else: sumo.addTextAttribute(wg_sk_path, 'flags', 'detectwinglet') # Positionings if tixi.checkElement(wing_path + '/positionings'): pos_cnt = tixi.getNamedChildrenCount(wing_path + '/positionings', 'positioning') log.info(str(wing_cnt) + ' "positionning" has been found : ') pos_x_list = [] pos_y_list = [] pos_z_list = [] from_sec_list = [] to_sec_list = [] for i_pos in range(pos_cnt): pos_path = wing_path + '/positionings/positioning[' \ + str(i_pos+1) + ']' length = tixi.getDoubleElement(pos_path + '/length') sweep_deg = tixi.getDoubleElement(pos_path + '/sweepAngle') sweep = math.radians(sweep_deg) dihedral_deg = tixi.getDoubleElement(pos_path+'/dihedralAngle') dihedral = math.radians(dihedral_deg) # Get the corresponding translation of each positionning pos_x_list.append(length * math.sin(sweep)) pos_y_list.append(length * math.cos(dihedral)*math.cos(sweep)) pos_z_list.append(length * math.sin(dihedral)*math.cos(sweep)) # Get which section are connected by the positionning if tixi.checkElement(pos_path + '/fromSectionUID'): from_sec = tixi.getTextElement(pos_path +'/fromSectionUID') else: from_sec = '' from_sec_list.append(from_sec) if tixi.checkElement(pos_path + '/toSectionUID'): to_sec = tixi.getTextElement(pos_path + '/toSectionUID') else: to_sec = '' to_sec_list.append(to_sec) # Re-loop though the positionning to re-order them for j_pos in range(pos_cnt): if from_sec_list[j_pos] == '': prev_pos_x = 0 prev_pos_y = 0 prev_pos_z = 0 elif from_sec_list[j_pos] == to_sec_list[j_pos-1]: prev_pos_x = pos_x_list[j_pos-1] prev_pos_y = pos_y_list[j_pos-1] prev_pos_z = pos_z_list[j_pos-1] else: index_prev = to_sec_list.index(from_sec_list[j_pos]) prev_pos_x = pos_x_list[index_prev] prev_pos_y = pos_y_list[index_prev] prev_pos_z = pos_z_list[index_prev] pos_x_list[j_pos] += prev_pos_x pos_y_list[j_pos] += prev_pos_y pos_z_list[j_pos] += prev_pos_z else: log.warning('No "positionings" have been found!') pos_cnt = 0 #Sections sec_cnt = tixi.getNamedChildrenCount(wing_path + '/sections','section') log.info(" -" + str(sec_cnt) + ' wing sections have been found') wing_sec_index = 1 if pos_cnt == 0: pos_x_list = [0.0] * sec_cnt pos_y_list = [0.0] * sec_cnt pos_z_list = [0.0] * sec_cnt for i_sec in reversed(range(sec_cnt)): sec_path = wing_path + '/sections/section[' + str(i_sec+1) + ']' sec_uid = tixi.getTextAttribute(sec_path, 'uID') sec_transf = Transformation() sec_transf.get_cpacs_transf(tixi, sec_path + '/transformation') # Elements elem_cnt = tixi.getNamedChildrenCount(sec_path + '/elements', 'element') if elem_cnt > 1: log.warning("Sections " + sec_uid + " contains multiple \ element, it could be an issue for the conversion \ to SUMO!") for i_elem in range(elem_cnt): elem_path = sec_path + '/elements/element[' \ + str(i_elem + 1) + ']' elem_uid = tixi.getTextAttribute(elem_path, 'uID') elem_transf = Transformation() elem_transf.get_cpacs_transf(tixi,elem_path +'/transformation') # Wing profile (airfoil) prof_uid = tixi.getTextElement(elem_path+'/airfoilUID') prof_path = tixi.uIDGetXPath(prof_uid) try: tixi.checkElement(prof_path) except: log.error('No profile "' + prof_uid + '" has been found!') prof_vect_x_str = tixi.getTextElement(prof_path+'/pointList/x') prof_vect_y_str = tixi.getTextElement(prof_path+'/pointList/y') prof_vect_z_str = tixi.getTextElement(prof_path+'/pointList/z') # Transform airfoil points (string) into list of float prof_vect_x = [] for i, item in enumerate(prof_vect_x_str.split(';')): if item: prof_vect_x.append(float(item)) prof_vect_y = [] for i, item in enumerate(prof_vect_y_str.split(';')): if item: prof_vect_y.append(float(item)) prof_vect_z = [] for i, item in enumerate(prof_vect_z_str.split(';')): if item: prof_vect_z.append(float(item)) if sum(prof_vect_z[0:len(prof_vect_z)//2]) \ < sum(prof_vect_z[len(prof_vect_z)//2:-1]): log.info("Airfoil's points will be reversed.") tmp_vect_x = [] tmp_vect_y = [] tmp_vect_z = [] for i in range(len(prof_vect_x)): tmp_vect_x.append(prof_vect_x[len(prof_vect_x)-1-i]) tmp_vect_y.append(prof_vect_y[len(prof_vect_y)-1-i]) tmp_vect_z.append(prof_vect_z[len(prof_vect_z)-1-i]) prof_vect_x = tmp_vect_x prof_vect_y = tmp_vect_y prof_vect_z = tmp_vect_z # Apply scaling for i, item in enumerate(prof_vect_x): prof_vect_x[i] = item * elem_transf.scale.x \ * sec_transf.scale.x * wing_transf.scale.x for i, item in enumerate(prof_vect_y): prof_vect_y[i] = item * elem_transf.scale.y \ * sec_transf.scale.y * wing_transf.scale.y for i, item in enumerate(prof_vect_z): prof_vect_z[i] = item * elem_transf.scale.z \ * sec_transf.scale.z * wing_transf.scale.z # if (i_sec>8 and i_sec<=10): # plt.plot(prof_vect_x, prof_vect_z,'x') # plt.xlabel('x') # plt.ylabel('z') # plt.grid(True) # plt.show() prof_size_x = (max(prof_vect_x) - min(prof_vect_x)) prof_size_y = (max(prof_vect_y) - min(prof_vect_y)) prof_size_z = (max(prof_vect_z) - min(prof_vect_z)) if prof_size_y == 0: prof_vect_x[:] = [x / prof_size_x for x in prof_vect_x] prof_vect_z[:] = [z / prof_size_x for z in prof_vect_z] # Is it correct to divide by prof_size_x ???? wg_sec_chord = prof_size_x else: log.error("An airfoil profile is not define correctly") # SUMO variable for WingSection wg_sec_center_x = ( elem_transf.translation.x \ + sec_transf.translation.x \ + pos_x_list[i_sec]) \ * wing_transf.scale.x wg_sec_center_y = ( elem_transf.translation.y \ * sec_transf.scale.y \ + sec_transf.translation.y \ + pos_y_list[i_sec]) \ * wing_transf.scale.y wg_sec_center_z = ( elem_transf.translation.z \ * sec_transf.scale.z \ + sec_transf.translation.z \ + pos_z_list[i_sec]) \ * wing_transf.scale.z # Add roation from element and sections # Adding the two angles: Maybe not work in every case!!! add_rotation = SimpleNamespace() add_rotation.x = elem_transf.rotation.x + sec_transf.rotation.x add_rotation.y = elem_transf.rotation.y + sec_transf.rotation.y add_rotation.z = elem_transf.rotation.z + sec_transf.rotation.z # Get Section rotation for SUMO wg_sec_rot = euler2fix(add_rotation) wg_sec_dihed = math.radians(wg_sec_rot.x) wg_sec_twist = math.radians(wg_sec_rot.y) wg_sec_yaw = math.radians(wg_sec_rot.z) # Convert point list into string prof_str = '' # Airfoil points order : shoud be from TE (1 0) to LE (0 0) # then TE(1 0), but not reverse way. # to avoid double zero, not accepted by SUMO for i, item in (enumerate(prof_vect_x)): # if not (prof_vect_x[i] == prof_vect_x[i-1] or \ # round(prof_vect_z[i],4) == round(prof_vect_z[i-1],4)): if round(prof_vect_z[i],4) != round(prof_vect_z[i-1],4): prof_str += str(round(prof_vect_x[i], 4)) + ' ' \ + str(round(prof_vect_z[i], 4)) + ' ' sumo.addTextElementAtIndex(wg_sk_path, 'WingSection', prof_str, wing_sec_index) wg_sec_path = wg_sk_path + '/WingSection[' \ + str(wing_sec_index) + ']' sumo.addTextAttribute(wg_sec_path, 'airfoil', prof_uid) sumo.addTextAttribute(wg_sec_path, 'name', sec_uid) wg_sec_center_str = str(wg_sec_center_x) + ' ' + \ str(wg_sec_center_y) + ' ' + \ str(wg_sec_center_z) sumo.addTextAttribute(wg_sec_path, 'center', wg_sec_center_str) sumo.addTextAttribute(wg_sec_path, 'chord', str(wg_sec_chord)) sumo.addTextAttribute(wg_sec_path, 'dihedral', str(wg_sec_dihed)) sumo.addTextAttribute(wg_sec_path, 'twist', str(wg_sec_twist)) sumo.addTextAttribute(wg_sec_path, 'yaw', str(wg_sec_yaw)) sumo.addTextAttribute(wg_sec_path, 'napprox', '-1') sumo.addTextAttribute(wg_sec_path, 'reversed', 'false') sumo.addTextAttribute(wg_sec_path, 'vbreak', 'false') wing_sec_index += 1 # Add Wing caps sumo.createElementAtIndex(wg_sk_path, "Cap", 1) sumo.addTextAttribute(wg_sk_path+'/Cap[1]', 'height', '0') sumo.addTextAttribute(wg_sk_path+'/Cap[1]', 'shape', 'LongCap') sumo.addTextAttribute(wg_sk_path+'/Cap[1]', 'side', 'south') sumo.createElementAtIndex(wg_sk_path, 'Cap', 2) sumo.addTextAttribute(wg_sk_path+'/Cap[2]', 'height', '0') sumo.addTextAttribute(wg_sk_path+'/Cap[2]', 'shape', 'LongCap') sumo.addTextAttribute(wg_sk_path+'/Cap[2]', 'side', 'north') # Save the SMX file close_tixi(sumo, OUTPUT_SMX) return OUTPUT_SMX
def create_config(cpacs_path, su2_path, config_file): """ 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. Source : SU2 configuration file template https://github.com/su2code/SU2/blob/master/config_template.cfg ARGUMENTS (str) cpacs_path -- Path to CPACS file (str) su2_path -- Path to SU2 mesh (str) config_file -- Path to default configuration file RETURNS () Output New configuration file save in /ToolOutput/ToolOutput.cfg """ MODULE_DIR = os.path.dirname(os.path.abspath(__file__)) CONFIG_FILE_OUTPUT = MODULE_DIR + '/ToolOutput/ToolOutput.cfg' try: config_file_object = open(config_file, '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 # Modify or add parameters ----------- tixi = open_tixi(cpacs_path) config_dict_modif = config_dict # General parmeters config_dict_modif['MESH_FILENAME'] = su2_path config_dict_modif['EXT_ITER'] = 300 # Get reference value ref_len = get_value(tixi, '/cpacs/vehicles/aircraft/model/reference/length') ref_area = get_value(tixi, '/cpacs/vehicles/aircraft/model/reference/area') config_dict_modif['REF_LENGTH'] = ref_len config_dict_modif['REF_AREA'] = ref_area # Flow conditions (from /toolSpecific?, possibility to have several cases) aoa = 2.0 aos = 0.0 mach = 0.5 alt = 12000 Atm = get_atmosphere(alt) pressure = Atm.pres temp = Atm.temp 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) # Get value from /toolSpecific?, which one? config_dict_modif['FIXED_CL_MODE'] = 'YES' config_dict_modif['TARGET_CL'] = '0.5' config_dict_modif['DCL_DALPHA'] = '0.1' config_dict_modif['UPDATE_ALPHA'] = '8' config_dict_modif['ITER_DCL_DALPHA'] = '80' # Mesh Marker marker_list = get_mesh_marker(su2_path) euler_parts = '(' + ','.join(marker_list) + ')' config_dict_modif['MARKER_EULER'] = euler_parts config_dict_modif['MARKER_FAR'] = ' (Farfield)' config_dict_modif['MARKER_SYM'] = ' (0)' config_dict_modif['MARKER_PLOTTING'] = euler_parts config_dict_modif['MARKER_MONITORING'] = euler_parts config_dict_modif['MARKER_MOVING'] = euler_parts # 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_FILE_OUTPUT, 'w') config_file_new.writelines(config_file_lines) config_file_new.close() log.info('ToolOutput.cfg has been written in /ToolOutput.')