#!/usr/bin python import sys import re from sets import Set from copy import copy from numpy import zeros, array, sign, cross, dot, ones, arctan, sin, cos, pi, mod, sqrt, save #from numpy import * from numpy.linalg import norm from pylab import find from operator import add from scipy.optimize import fsolve #from dolfin import File, MeshEditor, Mesh, Cell, facets print "Converting from ANSYS Fluent format (.msh) to Nek5000, semtex or FEniCS format" # Use regular expressions to identify sections and tokens found in a fluent file re_dimline = re.compile(r"\(2\s(\d)\)") re_comment = re.compile(r"\(0\s.*") re_zone0 = re.compile(r"\(10\s\(0\s(\w+)\s(\w+)\s(\d+)\s(\d+)\)\)") re_zone = re.compile(r"\(10\s\((\w+)\s(\w+)\s(\w+)\s(\d+)\s(\d)\)(\(|)") re_face0 = re.compile(r"\(13(\s*)\(0\s+(\w+)\s+(\w+)\s+(0|0 0)\)\)") re_face = re.compile(r"\(13(\s*)\((\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\)(\s*)(\(|)") #re_periodic = re.compile(r"\(18.*\((\w+)\s+(\w+)\s+(\w+)\s+(\w+)\).*\(") re_periodic = re.compile(r"\(18(\s*)\((\w+)\s+(\w+)\s+(\w+)\s+(\w+)\)(\s*)(\(|)") re_pfaces = re.compile(r"((^\s)|)(\w+)(\s*)(\w+)") re_cells0 = re.compile(r"\(12(\s*)\(0(\s+)(\w+)(\s+)(\w+)(\s+)(0|0 0)\)\)") re_cells = re.compile(r"\(12.*\((\w+)\s+(\w+)\s+(\w+)\s+(\d+)\s+(\d+)\)\)") re_cells2 = re.compile(r"\(12(\s*)\((\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\)(\s*)(\(|)") re_zones = re.compile(r"\((45|39)\s+\((\d+)\s+(\S+)\s+(\S+).*\)\((.*|[0-9]+[\.]*[0-9]*)\)\)") re_parant = re.compile(r"(^\s*\)(\s*)|^\s*\)\)(\s*)|^\s*\(\s*)") # The fluent mesh (the .msh file) is basically stored as a list of nodes, and then a # list of faces for each zone of the mesh, the interior and the boundaries. # Declare som maps that will be built when reading in the lists of nodes and faces: cell_map = {} # Maps cell id with nodes cell_face_map = {} # Maps cell id with faces face_cell_map = {} # Maps face id with two cells face_list = [] # List of faces [[id, 2-4 nodes, 2 connecting cells and type]] face_map = {} # For each cell a dictionary with key=face and val=local face number nodes = None # Will be numpy array of nodes # Information about connectivity and boundaries boundary_map = {} # For each cell and local face number, get connected cell and local face number boundary_val_map = {} # Holds boundary conditions, like V, v, P, etc boundary_nodes = {} # List of nodes attached to a boundary. Key is zone id boundary_nodes_face_map = {}# Map of the faces that belongs to a node on a boundary boundary_faces = {} # List of faces attached to a boundary. Key is zone id # Information about mesh periodicity periodic_face_map = {} # Map of face to face periodicity periodic_cell_face_map = {} # Map (cell, local face number) of periodic face to (cell, local face number) of shadow periodic_node_map = {} # Periodic node to node map # Some global values num_cells = {} # Total number of cells in different zones zones = {} # zone information zone_number_of_faces = {} # number of faces for each zone # For Nek5000 temperature_val_map = {} # Boundary conditions for temperature passive_scalar_val_map = [] # Boundary conditions for passive scalars face_node_map = {} # Maps face with nodes mid_point_map = {} # Store location of midpoint for key face # For Nek5000 and semtex that can have curved boundaries curves_map = {} # Holds curve information curved_faces = {} # For each boundary zone store faces that are in curved section curved_nodes = {} # For each boundary zone store nodes that are in curved section # This part is just a default that goes into the top of the rea-file: start_of_rea = """****** PARAMETERS ***** 2.610000 NEKTON VERSION {0:2d} DIMENSIONAL RUN 103 PARAMETERS FOLLOW 1.00000 p1 DENSITY -100.000 p2 VISCOS 0. 0. 0. 0. 1.00000 p7 RHOCP 1.00000 p8 CONDUCT 0. 0. p10 FINTIME 100 p11 NSTEPS -0.10000E-01 p12 DT 0. p13 IOCOMM 0. p14 IOTIME 1000 p15 IOSTEP 0. p16 PSSOLVER 0. -20.00000 p18 GRID -1.00000 p19 INTYPE 10.0000 p20 NORDER 0.100000E-05 p21 DIVERGENCE 0.100000E-06 p22 HELMHOLTZ 0. p23 NPSCAL 0.100000E-01 p24 TOLREL 0.100000E-01 p25 TOLABS 1.00000 p26 COURANT/NTAU 2.00000 p27 TORDER 0. p28 TORDER: mesh velocity (0: p28=p27) 0. p29 magnetic visc if > 0, = -1/Rm if < 0 0. p30 > 0 ==> properties set in uservp() 0. p31 NPERT: #perturbation modes 0. p32 #BCs in re2 file, if > 0 0. 0. 0. 0. 0. 0. 0. 0. 0. p41 1-->multiplicative SEMG 0. p42 0=gmres/1=pcg 0. p43 0=semg/1=schwarz 0. p44 0=E-based/1=A-based prec. 0. p45 Relaxation factor for DTFS 0. p46 reserved 0. p47 vnu: mesh matieral prop 0. 0. 0. 0. 0. p52 IOHIS 0. 0. p54 1,2,3-->fixed flow rate dir=x,y,z 0. p55 vol.flow rate (p54>0) or Ubar (p54<0) 0. 0. 0. 0. p59 !=0 --> full Jac. eval. for each el. 0. p60 !=0 --> init. velocity to small nonzero 0. 0. p62 >0 --> force byte_swap for output 0. p63 =8 --> force 8-byte output 0. p64 =1 --> perturbation restart 1.00000 p65 #iofiles (eg, 0 or 64); <0 --> sep. dirs 4.00000 p66 output : <0=ascii, else binary 4.00000 p67 restart: <0=ascii, else binary 0. p68 iastep: freq for avg_all 0. 0. 0. 0. 0. 0. p74 verbose Helmholtz 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. p84 !=0 --> sets initial timestep if p12>0 0. p85 dt ratio if p84 !=0, for timesteps>0 0. p86 reserved 0. 0. 0. 0. 0. 0. 20.0000 p93 Number of previous pressure solns saved 3.00000 p94 start projecting velocity after p94 step 5.00000 p95 start projecting pressure after p95 step 0. 0. 0. 0. p99 dealiasing: <0--> off/3--> old/4--> new 0. 0. p101 No. of additional filter modes 1.00000 p102 Dump out divergence at each time step -1.00000 p103 weight of stabilizing filter (.01) 4 Lines of passive scalar data follows2 CONDUCT; 2RHOCP 1.00000 1.00000 1.00000 1.00000 1.00000 1.00000 1.00000 1.00000 1.00000 1.00000 1.00000 1.00000 1.00000 1.00000 1.00000 1.00000 1.00000 1.00000 13 LOGICAL SWITCHES FOLLOW T IFFLOW F IFHEAT T IFTRAN T F F F F F F F F F F IFNAV & IFADVC (convection in P.S. fields) F F T T T T T T T T T T IFTMSH (IF mesh for this field is T mesh) F IFAXIS F IFSTRS F IFSPLIT F IFMGRID F IFMODEL F IFKEPS F IFMVBD F IFCHAR 5.00000 5.00000 -2.75000 -2.75000 XFAC,YFAC,XZERO,YZERO """ # This part goes into the bottom of the rea-file: end_of_rea = """ 0 PRESOLVE/RESTART OPTIONS ***** 7 INITIAL CONDITIONS ***** C Default C Default C Default C Default C Default C Default C Default ***** DRIVE FORCE DATA ***** BODY FORCE, FLOW, Q 4 Lines of Drive force data follow C C C C ***** Variable Property Data ***** Overrrides Parameter data. 1 Lines follow. 0 PACKETS OF DATA FOLLOW ***** HISTORY AND INTEGRAL DATA ***** 0 POINTS. Hcode, I,J,H,IEL ***** OUTPUT FIELD SPECIFICATION ***** 6 SPECIFICATIONS FOLLOW F COORDINATES T VELOCITY T PRESSURE F TEMPERATURE F TEMPERATURE GRADIENT 0 PASSIVE SCALARS ***** OBJECT SPECIFICATION ***** 0 Surface Objects 0 Volume Objects 0 Edge Objects 0 Point Objects """ def read_zone_nodes(dim, Nmin, Nmax, ifile): """Scan lines for nodes and return in an array.""" line = ifile.readline() readline = False if re.search(re_parant, line): # check for initial paranthesis readline = True #dummy = lines.pop(0) global nodes nodes = zeros((dim, Nmax - Nmin + 1)) for i in range(Nmin, Nmax + 1): if readline: line = ifile.readline() readline = True nodes[:, i - Nmin] = [eval(x) for x in line.split()] def read_periodic(ifile, periodic_dx): """Scan periodic section and create periodic_face_map. However, if a dictionary periodic_dx has been supplied then simply skip past this section without creating the periodic_face_map.""" while 1: #line = lines.pop(0) line = ifile.readline() a = re.search(re_pfaces, line) if a: if not periodic_dx: periodic_face_map[int(a.group(3), 16)] = int(a.group(5), 16) continue break if not periodic_dx: keys = periodic_face_map.keys() vals = periodic_face_map.itervalues() for key, val in zip(keys, vals): periodic_face_map[val] = key def create_periodic_face_map(periodic_dx): """Create a map between periodic zones. periodic_dx is a dictionary with key a tuple of the connected zones and value (dx, dy, dz) the actual separation between the zones. If periodic_dx is {} the loop is never entered. """ for zone0, zone1 in periodic_dx: print 'Generating map for periodic zones ', zone0, zone1, '\n' face0_list = [] face1_list = [] nodes0 = [] nodes1 = [] # Get the faces of mapped zones for i, face in enumerate(face_list): if face[-1] == zone0: face0_list.append((face, i)) nodes0 += face[1] face[-2] = 8 # bc_type is now periodic elif face[-1] == zone1: face1_list.append((face, i)) nodes1 += face[1] face[-2] = 12 # bc_type is now shadow # Get unique lists of nodes nodes0 = list(Set(nodes0)) nodes1 = list(Set(nodes1)) periodic_node_map[(zone0, zone1)] = {} # Get mapping from zone0 to zone1 dx = array(periodic_dx[(zone0, zone1)]) # Go through all nodes in zone0 and find the periodic match for node in nodes0: original_node = nodes[:, node - 1] for shadow_node in nodes1: if all(abs(abs(nodes[:, shadow_node - 1] - original_node) - abs(dx)) < 1.e-7*norm(dx)): periodic_node_map[(zone0, zone1)][node] = shadow_node nodes1.remove(shadow_node) # Generate periodic face map for face, face_number in face0_list: nodes0 = face[1] true_nodes_of_shadow = [periodic_node_map[(zone0, zone1)][i] for i in nodes0] for face_of_shadow, shadow_number in face1_list: nodes_of_shadow = face_of_shadow[1] if len(Set(nodes_of_shadow + true_nodes_of_shadow)) == 4: periodic_face_map[face_number + 1] = shadow_number + 1 break face1_list.remove((face_of_shadow, shadow_number)) def read_faces(zone_id, Nmin, Nmax, bc_type, face, ifile): """Read all faces and create cell_face_map + some boundary maps.""" line = ifile.readline() readline = False if re.search(re_parant, line): # check for initial paranthesis readline = True ls = [] for i in range(Nmin, Nmax + 1): if readline: line = ifile.readline() readline = True ln = line.split() if face == 0: nd = int(ln[0]) # Number of nodes nds = [int(x, 16) for x in ln[1:(nd + 1)]] cells = [int(x, 16) for x in ln[(nd + 1):]] else: nd = face nds = [int(x, 16) for x in ln[:nd]] cells = [int(x, 16) for x in ln[nd:]] face_list.append([nd, copy(nds), copy(cells), bc_type, zone_id]) if len(nds) == 2: face_cell_map[(nds[0], nds[1])] = copy(cells) face_cell_map[(nds[1], nds[0])] = copy(cells) face_number = len(face_list) if min(cells) == 0: # A boundary zone if zone_id in boundary_nodes: boundary_nodes[zone_id] += nds boundary_faces[zone_id] += [face_number - 1] for nd in nds: if nd in boundary_nodes_face_map[zone_id]: boundary_nodes_face_map[zone_id][nd] += [face_number - 1] else: boundary_nodes_face_map[zone_id][nd] = [face_number - 1] else: boundary_nodes[zone_id] = nds boundary_faces[zone_id] = [face_number - 1] boundary_nodes_face_map[zone_id] = { nd: [face_number - 1]} for c in cells: if c > 0: if not c in cell_face_map: cell_face_map[c] = [face_number] else: # Preliminary cell_face_map. Needs shaping up later cell_face_map[c].append(face_number) if min(cells) == 0: boundary_nodes[zone_id] = list(Set(boundary_nodes[zone_id])) def create_cell_map(dim): """Create cell_map for use with fenics mesh.""" for cell, faces in cell_face_map.iteritems(): for face in faces: nds = face_list[face - 1][1] if not cell in cell_map: cell_map[cell] = copy(nds) else: cell_map[cell] = list(Set(cell_map[cell] + nds)) def create_cell_face_map(dim, mesh_format): """Creates maps from cells to faces and gives faces local numbering.""" if mesh_format == 'fenics': create_cell_map(dim) else: eval('create_cell_face_map_' + str(dim) + 'D()') def create_cell_face_map_2D(): for cell, faces in cell_face_map.iteritems(): for face in faces: nds = face_list[face - 1][1] if not cell in cell_map: cell_map[cell] = copy(nds) elif len(cell_map[cell]) == 4: # Finished pass elif len(Set(cell_map[cell] + nds)) - len(cell_map[cell] + nds) == 0: # No common node, no connectivity pass else: # Add to list preserving rotational connectivity # but not neccessarily right-handed if nds[0] in cell_map[cell]: if nds[0] == cell_map[cell][0]: cell_map[cell].insert(0, nds[1]) else: cell_map[cell].append(nds[1]) else: if nds[1] == cell_map[cell][0]: cell_map[cell].insert(0, nds[0]) else: cell_map[cell].append(nds[0]) # Ensure right-handed mesh for c, nds in cell_map.iteritems(): cross_product = crss2d(nds) if any([i <= 0 for i in cross_product]): cell_map[c].reverse() cross_product = crss2d(nds) # Generate face_map for cell, nds in cell_map.iteritems(): face_map[cell]={(nds[0], nds[1]) : 1, (nds[1], nds[0]) : 1, (nds[1], nds[2]) : 2, (nds[2], nds[1]) : 2, (nds[2], nds[3]) : 3, (nds[3], nds[2]) : 3, (nds[3], nds[0]) : 4, (nds[0], nds[3]) : 4, 1 : (nds[0], nds[1]), 1 : (nds[1], nds[0]), 2 : (nds[1], nds[2]), 2 : (nds[2], nds[1]), 3 : (nds[2], nds[3]), 3 : (nds[3], nds[2]), 4 : (nds[3], nds[0]), 4 : (nds[0], nds[3])} def create_cell_face_map_3D(): """From GambiToNek: PREFERED NEKTON FORMAT: - edge/midpt #'s shown in ( ) 7 ----(6)------- 6 face corner points / . / | 1 {1,2,6,5} (7) . (5) | 2 {2,3,7,6} / (11) / (10) 3 {4,3,7,8} 8 ----(8)------ 5 | 4 {1,4,8,5} | . | | 5 {1,2,3,4} | 3 .....(2).|... 2 6 {5,6,7,8} (12) . (9) / | (3) | (1) | . | / |. |/ 4 ----(4)------ 1 """ local_face_number = {'Bottom':1, 1:'Bottom', 'East':2, 2:'East', 'Top':3, 3:'Top', 'West':4, 4:'West', 'South':5, 5:'South', 'North':6, 6:'North'} # Local node numbering. # Note that order of points is only relevant for the Bottom face that we # place first because all other faces are placed based on matching # nodes with the Bottom face. face_dict_numbering = dict( Bottom = array([1, 2, 6, 5], int)[::-1] - 1, East = array([2, 3, 7, 6], int)[::-1] - 1, Top = array([4, 3, 7, 8], int)[::-1] - 1, West = array([1, 4, 8, 5], int)[::-1] - 1, South = array([1, 2, 3, 4], int)[::-1] - 1, North = array([5, 6, 7, 8], int)[::-1] - 1 ) # Create a map from nodes of the first placed face to the remaining faces. # local_node_map maps nodes 1, 2, 6, 5 correctly to nodes 4,3,7,8 # For new face 'West' node 4 is connected to node 1 of the Bottom face etc. # As a (somewhat irrelevant ) rule we always place the first face in Bottom. local_node_map = dict(West={1:4, 5:8}, East={2:3, 6:7}, Top={1:4, 2:3, 5:8, 6:7}, South={1:4, 2:3}, North={5:8, 6:7}) # Map of opposite faces in the box. shadow_face = {1:3, 3:1, 2:4, 4:2, 5:6, 6:5} for cell, faces in cell_face_map.iteritems(): # Place first face new_face_pos = 1 # Put the first face in Bottom face = faces[0] # The first face from the list face1 = face_list[face - 1] # Some more info for the face nodes1 = array(face1[1], int) # The nodes of first face face_map[cell] = {} # cell/face to local face number map face_map[cell][face] = new_face_pos cm = cell_map[cell] = zeros(8, int) newface = local_face_number[new_face_pos] # Name of new face # Place the first four nodes of the cell cm[face_dict_numbering[newface][:]] = nodes1[:] nodes_of_first_face = array(cm, int) # One face and 4 nodes placed, now to the remaining faces remaining_faces = face_dict_numbering.keys() remaining_faces.remove(newface) # already placed faces.remove(face) for face in faces: # Loop over remaining faces to be placed face2 = face_list[face - 1] nodes2 = array(face2[1], int) # Nodes of new face common_nodes = find(map(lambda x: x in nodes1, nodes2)) new_nodes = find(map(lambda x: x not in cm, nodes2)) if common_nodes.shape[0] == 0: # e.g., opposite Bottom is Top opposite_face = shadow_face[new_face_pos] face_map[cell][face] = opposite_face remaining_faces.remove(local_face_number[opposite_face]) # Don't place any nodes because we don't know connectivity elif common_nodes.shape[0] == 2: matching_nodes = nodes2[common_nodes] pos = [] for i, node in enumerate(matching_nodes): pos.append(find(node == cm)[0]) # The two common nodes determine where the face is located for rface in remaining_faces: rval = face_dict_numbering[rface] if all(map(lambda x: x in rval, pos)): break # break out of loop when we find the position remaining_faces.remove(rface) # Give the new face its local face number in face_map face_map[cell][face] = local_face_number[rface] # Now place the new nodes in cell_map # The connectivity of nodes is known from face_dict_numbering for new_node_orig_pos in new_nodes: if new_node_orig_pos < 3: node_to_right = nodes2[new_node_orig_pos + 1] else: node_to_right = nodes2[0] if new_node_orig_pos > 0: node_to_left = nodes2[new_node_orig_pos - 1] else: node_to_left = nodes2[3] if any(node_to_right == nodes_of_first_face): # if the node to the right is already placed nd = find(node_to_right == nodes_of_first_face)[0] cm[local_node_map[rface][nd + 1] - 1] = \ nodes2[new_node_orig_pos] else: nd = find(node_to_left == nodes_of_first_face)[0] cm[local_node_map[rface][nd + 1] - 1] = \ nodes2[new_node_orig_pos] if any(x < 0. for x in crss(cm)): # We guessed the wrong direction for first face. Reverse direction. cm_copy = copy(cm) cm[[0, 1, 5, 4]] = cm_copy[[4, 5, 1, 0]] cm[[3, 2, 6, 7]] = cm_copy[[7, 6, 2, 3]] # We need change the face map fm = face_map[cell] for k, v in fm.iteritems( ) : if v == 5 : fm[k] = 6 elif v == 6 : fm[k] = 5 def crss2d(nds): """Test that we have the correct orientation for 2D mesh.""" cross_product = [] nds = nodes[:, array(nds) - 1] for i, j, k in zip([2, 3, 1, 4], [4, 1, 3, 2], [1, 2, 4, 3]): xy1 = nds[:, i - 1] xy2 = nds[:, j - 1] xy0 = nds[:, k - 1] v1x = xy1[0] - xy0[0] v2x = xy2[0] - xy0[0] v1y = xy1[1] - xy0[1] v2y = xy2[1] - xy0[1] cross_product.append(v1x*v2y - v1y*v2x) return cross_product def crss(nds): """Test that we have the correct orientation for 3D mesh.""" cross_product = [] nds = nodes[:, array(nds) - 1] for i, j, k, l in zip([2, 3, 1, 4, 6, 7, 5, 8], [4, 1, 3, 2, 8, 5, 7, 6], [5, 6, 8, 7, 1, 2, 4, 3], [1, 2, 4, 3, 5, 6, 8, 7]): p1 = nds[:, i - 1] p2 = nds[:, j - 1] p3 = nds[:, k - 1] p0 = nds[:, l - 1] u = p1 - p0 v = p2 - p0 w = p3 - p0 c = array([u[1]*v[2] - u[2]*v[1], u[2]*v[0] - u[0]*v[2], u[0]*v[1] - u[1]*v[0]]) cross_product.append(dot(w, c)) for i in range(4, 8): cross_product[i] = - cross_product[i] return cross_product def create_periodic_cell_face_map(): """Create maps of type local face 1 of cell 2 is periodic to local face 2 of cell 10.""" for f0, f1 in periodic_face_map.iteritems(): # f0, f1 = periodic face0 - face1 face0 = face_list[f0 - 1] face1 = face_list[f1 - 1] # shadow nd, nds, cells, bc_type, zone_id = [0,]*2, [0,]*2, [0,]*2, [0,]*2, [0,]*2 for i, ff in enumerate([face0, face1]): nd[i], nds[i], cells[i], bc_type[i], zone_id[i] = ff cell_face_pair = [] for i in range(2): c = max(cells[i]) if len(nds[i]) == 2: cell_face_pair.append((c, face_map[c][(nds[i][0], nds[i][1])])) else: cell_face_pair.append((c, face_map[c][eval('f'+str(i))])) periodic_cell_face_map[cell_face_pair[0]] = cell_face_pair[1] periodic_cell_face_map[cell_face_pair[1]] = cell_face_pair[0] def create_boundary_section(bcs, temperature, passive_scalars, mesh_format): if mesh_format == 'fenics': return for i in range(len(passive_scalars)): passive_scalar_val_map.append({}) global bcs_copy if not bcs: bcs_copy = {} else: bcs_copy = bcs for face_number, face in enumerate(face_list): nd, nds, cells, bc_type, zone_id = face if min(cells) == 0: # Boundary face c = max(cells) if len(nds) == 2: local_face = face_map[c][(nds[0], nds[1])] else: local_face = face_map[c][face_number + 1] if bc_type == 8 or bc_type == 12: # Face is periodic. boundary_map[(c, local_face)] = \ periodic_cell_face_map[(c, local_face)] boundary_val_map[(c, local_face)] = 'P' if temperature: temperature_val_map[(c, local_face)] = 'P' for ss, scalar in enumerate(passive_scalars): passive_scalar_val_map[ss][(c, local_face)] = 'P' else: boundary_map[(c, local_face)] = (0, 0) if bcs: boundary_val_map[(c, local_face)] = bcs[zone_id] else: boundary_val_map[(c, local_face)] = zones[zone_id][1][-1] bcs_copy[zone_id] = zones[zone_id][1][-1] if temperature: temperature_val_map[(c, local_face)] = \ temperature[zone_id] for ss, scalar in enumerate(passive_scalars): passive_scalar_val_map[ss][(c, local_face)] = \ passive_scalars[ss][zone_id] else: c0 = cells[0] c1 = cells[1] if len(nds) == 2: local_face0 = face_map[c0][(nds[0], nds[1])] local_face1 = face_map[c1][(nds[0], nds[1])] else: #3D local_face0 = face_map[c0][face_number + 1] local_face1 = face_map[c1][face_number + 1] if bc_type == 2: # interior boundary_map[(c0, local_face0)] = (c1, local_face1) boundary_map[(c1, local_face1)] = (c0, local_face0) boundary_val_map[(c0, local_face0)] = 'E' boundary_val_map[(c1, local_face1)] = 'E' if temperature: temperature_val_map[(c0, local_face0)] = 'E' temperature_val_map[(c1, local_face1)] = 'E' for ss, scalar in enumerate(passive_scalars): passive_scalar_val_map[ss][(c0, local_face0)] = 'E' passive_scalar_val_map[ss][(c1, local_face1)] = 'E' else: raise NotImplementedError def circle_center(x, x0, x1, r): return (r**2 - (x[0] - x0[0])**2 - (x[1] - x0[1])**2, r**2 - (x[0] - x1[0])**2 - (x[1] - x1[1])**2) def read_curved_sides(curves): for curve_zone, curve in curves.iteritems(): cell_face_m = [] curve['x'] = [] spline = [] for face_number in boundary_faces[curve_zone]: face = face_list[face_number] cell = max(face[2]) nds = face[1] cell_face_m.append((cell, face_map[cell][(nds[0], nds[1])], face_number)) if curve['type'] == 'C': for jj in range(curve['depth']): # Put circle on opposite face if jj == 0: if 'circle_center' in curve: cc_center = curve['circle_center'] else: cc_center = fsolve(circle_center, (1e-3, 1e-3), args=(nodes[:, nds[0] - 1], nodes[:, nds[1] - 1], curve['radius']), xtol=1e-12) curve['x'].append([curve['radius'] , 0, 0, 0, 0]) new_radius = curve['radius'] opposite_face_number = mod(face_map[cell][(nds[0], nds[1])] + 1, 4) + 1 cell_face_m.append((cell, opposite_face_number, 0)) nn = opposite_nodes = face_map[cell][opposite_face_number] new_radius = sign(new_radius)*0.5*(sqrt((nodes[0, nn[0] - 1] - cc_center[0])**2 + (nodes[1, nn[0] - 1] - cc_center[1])**2) + sqrt((nodes[0, nn[1] - 1] - cc_center[0])**2 + (nodes[1, nn[1] - 1] - cc_center[1])**2)) curve['x'].append([-new_radius, 0, 0, 0, 0]) # Put circle on opposing cell opp_cell = list(face_cell_map[(nn[0], nn[1])]) opp_cell.remove(cell) opp_cell = opp_cell[0] opposite_face_number = face_map[opp_cell][(nn[0], nn[1])] cell_face_m.append((opp_cell, opposite_face_number, 0)) curve['x'].append([new_radius, 0, 0, 0, 0]) cell = opp_cell nds = copy(nn) elif curve['type'] == 'spline': spline += face[1] # Check for linearity using both faces of a boundary node. # In the case of not linear, add the nodes/faces to the # curved_nodes/faces lists. curved_faces[curve_zone] = [] curved_nodes[curve_zone] = [] for nd, facel in boundary_nodes_face_map[curve_zone].iteritems(): if len(facel) > 1: nds_left = face_list[facel[0]][1] nds_right = face_list[facel[1]][1] n1 = nodes[:, nds_left[0]-1] n2 = nodes[:, nds_left[1]-1] n3 = nodes[:, nds_right[1]-1] #if (abs(n3[0]-n2[0]) < 1e-12 and abs(n3[1]-n2[1]) < 1e-12 or # abs(n3[0]-n1[0]) < 1e-12 and abs(n3[1]-n1[1]) < 1e-12 ) : # n3 = nodes[:, nds_right[0]-1] dnx1 = n2[0] - n1[0] dny1 = n2[1] - n1[1] dnx2 = n3[0] - n2[0] dny2 = n3[1] - n2[1] if ((abs(dny1) < 1e-6 and abs(dny2) < 1e-6) or (abs(dnx1) < 1e-6 and abs(dnx2) < 1e-6)): pass elif ((abs(dny1) < 1e-6 and not abs(dny2) < 1e-6) or (abs(dnx1) < 1e-6 and not abs(dnx2) < 1e-6) or (abs(dnx2/dnx1 - dny2/dny1) > 1.e-6)): # Curved line. Add nodes to curved_nodes curved_faces[curve_zone] += facel curved_nodes[curve_zone] += nds_left curved_nodes[curve_zone] += nds_right curved_faces[curve_zone] = list(Set(curved_faces[curve_zone])) curved_nodes[curve_zone] = list(Set(curved_nodes[curve_zone])) if curve['type'] == 'C': pass elif curve['type'] == 'm': find_mid_point(curve_zone, curve) elif curve['type'] == 'spline': curve['x'] = list(Set(spline)) curves_map[curve_zone] = (cell_face_m, curve) def barycentric_weight(y): N = len(y) w = ones(N, float) for j in range(1, N): for k in range(0, j): w[k] = (y[k] - y[j])*w[k] w[j] *= (y[j] - y[k]) w = 1./w return w def fun(x0, x1, y0, y1, xx, yy): """w are barycentric weights. x is unknown. x0, y0 contain x and y in the four nodes used to compute the midpoint.""" # Look for point of intersection between interpolated curve between nodes in x, y # and the normal to the face between nodes (x0, y0) and (x1, y1) # Transform coordinate axes # Center of face is xs, ys xs = (x0 + x1)/2. ys = (y0 + y1)/2. if abs(y1 - y0) > abs(x1 - x0): theta = arctan((x1 - x0)/(y1 - y0)) theta2 = arctan((xx - xs)/(yy - ys)) dy = (yy - ys)/cos(theta2) xn = copy(xx) yn = copy(yy) xn = dy*sin(theta2 - theta) yn = dy*cos(theta2 - theta) w = barycentric_weight(yn) y2 = - yn f = zeros(len(y2), float) ss = sum(w/y2) f[:] = w/y2/ss dy = dot(f, xn) xny = xs + dy*sin(theta + pi/2.) yny = ys + dy*cos(theta + pi/2.) else: theta = arctan((y1 - y0)/(x1 - x0)) theta2 = arctan((yy - ys)/(xx - xs)) dx = (xx - xs)/cos(theta) xn = copy(xx) yn = copy(yy) xn = dx*cos(theta2 - theta) yn = dx*sin(theta2 - theta) w = barycentric_weight(xn) x2 = - xn f = zeros(len(x2), float) ss = sum(w/x2) f[:] = w/x2/ss dy = dot(f, yn) xny = xs + dy*cos(theta + pi/2.) yny = ys + dy*sin(theta + pi/2.) return xny, yny def find_mid_point(curve_zone, curve): # Get unique list of nodes on boundary nodes0 = boundary_nodes[curve_zone] face0_list = boundary_faces[curve_zone] # Loop over faces and compute midpoint using at least two # nodes of two neighbouring faces for i in face0_list: face = face_list[i] nds = face[1] face_node_map[i] = {'left': None, 'right': None} for j in face0_list: face2 = face_list[j] if len(Set(nds + face2[1])) == 3: if nds[0] in face2[1]: face_node_map[i]['left'] = j else: face_node_map[i]['right'] = j for face in face_node_map: if face in curved_faces[curve_zone]: fl, fr = face_node_map[face]['left'], face_node_map[face]['right'] if fl is not None and fr is not None: common_node_left = map(lambda x: x in face_list[face][1], face_list[fl][1]) node0 = face_list[fl][1][common_node_left.index(False)] node1 = face_list[fl][1][common_node_left.index(True)] common_node_right = map(lambda x: x in face_list[face][1], face_list[fr][1]) node2 = face_list[fr][1][common_node_right.index(True)] node3 = face_list[fr][1][common_node_right.index(False)] x, y = nodes[:, array([node0, node1, node2, node3]) - 1] x0, y0 = x[1], y[1] x1, y1 = x[2], y[2] elif fl is None: common_node_right = map(lambda x: x in face_list[face][1], face_list[fr][1]) node0 = face_list[face][1][common_node_right.index(True)] node1 = face_list[fr][1][common_node_right.index(True)] node2 = face_list[fr][1][common_node_right.index(False)] x, y = nodes[:, array([node0, node1, node2]) - 1] x0, y0 = nodes[:, array(face_list[face][1]) - 1] x0, x1 = x0[:] y0, y1 = y0[:] elif fr is None: common_node_left = map(lambda x: x in face_list[face][1], face_list[fl][1]) node0 = face_list[face][1][common_node_left.index(True)] node1 = face_list[fl][1][common_node_left.index(True)] node2 = face_list[fl][1][common_node_left.index(False)] x, y = nodes[:, array([node0, node1, node2]) - 1] x0, y0 = nodes[:, array(face_list[face][1]) - 1] x0, x1 = x0[:] y0, y1 = y0[:] xn, yn = fun(x0, x1, y0, y1, x, y) mid_point_map[face] = (xn, yn, 0, 0, 0) #def scan_fluent_mesh(lines): def scan_fluent_mesh(ifile): """Scan fluent mesh and generate numerous maps.""" # Warning! Not yet tested for multiple interior zones dim = 0 one = 0 num_faces = 0 while 1: line = ifile.readline() if len(line) == 0: print 'Finished reading file\n' break #try: #line = lines.pop(0) #except: #print 'Finished reading file\n' #break if dim == 0: # Dimension usually comes first a = re.search(re_dimline, line) if a: print 'Reading dimensions\n' dim = int(a.group(1)) print 'Mesh is ' + str(dim) + 'D\n' continue if one == 0: # The total number of nodes a = re.search(re_zone0, line) if a: print 'Reading zone info\n' one, num_vertices, dummy1, dummy2 = int(a.group(1)), \ int(a.group(2), 16), int(a.group(3), 16), int(a.group(4)) continue a = re.search(re_zone, line) # Nodes if a: zone_id, first_id, last_id, dummy1, dummy2 = int(a.group(1), 16), \ int(a.group(2), 16), int(a.group(3), 16), int(a.group(4)), \ int(a.group(5)) print 'Reading ', last_id - first_id + 1,' nodes in zone ', zone_id + 1, '\n' #read_zone_nodes(dim, first_id, last_id, lines) #lines = lines[(last_id - first_id + 1):] read_zone_nodes(dim, first_id, last_id, ifile) continue a = re.search(re_zones,line) # Zone info if a: print 'Reading zone ', line dummy, zone_id, zone_type, zone_name, radius = \ int(a.group(1)), int(a.group(2)), a.group(3), \ a.group(4), a.group(5) zones[zone_id] = [zone_type, zone_name, radius] continue a = re.search(re_cells0, line) # Get total number of cells/elements if a: print 'Reading cell info ', line first_id, tot_num_cells = int(a.group(3),16), int(a.group(5), 16) continue a = re.search(re_cells,line) # Get the cell info. if a: zone_id, first_id, last_id, bc_type, element_type = \ int(a.group(1),16), int(a.group(2), 16), int(a.group(3), 16), \ int(a.group(4), 16), int(a.group(5), 16) print 'Reading ', last_id - first_id + 1,' cells in zone ', zone_id, '\n' if last_id == 0: raise TypeError("Zero elements!") num_cells[zone_id] = [first_id, last_id, bc_type, element_type] continue a = re.search(re_cells2,line) # Get the cell info. if a: raise TypeError("Wrong cell type. Can only handle one single cell type") a = re.search(re_face0, line) if a: print 'Reading total number of faces\n', line num_faces = int(a.group(3),16) continue a = re.search(re_face, line) if a: print 'Reading faces ', line zone_id, first_id, last_id, bc_type, face_type = \ int(a.group(2), 16), int(a.group(3), 16), int(a.group(4), 16), \ int(a.group(5), 16), int(a.group(6), 16) read_faces(zone_id, first_id, last_id, bc_type, face_type, ifile) #lines = lines[(last_id - first_id + 1):] zone_number_of_faces[zone_id] = last_id - first_id + 1 continue a = re.search(re_periodic, line) if a: print 'Reading periodic connectivity\n', line read_periodic(ifile, periodic_dx) continue print 'Line = ',line if any([re.search(st, line) for st in (re_parant, re_comment)]) or \ not line.strip(): continue # Should not make it here print 'Line = ',line raise IOError('Something went wrong reading fluent mesh.') def write_nek5000_file(dim, ofilename, curves, temperature, passive_scalars): tot_num_cells = len(cell_map) ofile = open(ofilename + '.rea', "w") ## Put the mesh in a rea-file print 'Create the rea-file: %s\n' %(ofilename+'.rea') print 'Writing the elements info\n' ofile.write(start_of_rea.format(dim)) ofile.write(' **MESH DATA**\n') ofile.write(' %s %s %s NEL,NDIM,NELV\n' %(tot_num_cells, dim, tot_num_cells)) element_header = ' ELEMENT %s [ 1 ] GROUP 0\n' for i in range(tot_num_cells): ofile.write(element_header %(i + 1)) if dim == 2: n1 = nodes[0, array(cell_map[i + 1]) - 1] n2 = nodes[1, array(cell_map[i + 1]) - 1] ofile.write(reduce(add, ['{0:.8e}'.format(x).rjust(16) for x in n1]) + '\n') ofile.write(reduce(add, ['{0:.8e}'.format(x).rjust(16) for x in n2]) + '\n') else: nn = nodes[:, array(cell_map[i + 1]) - 1] for i in range(3): ofile.write(reduce(add, ['{0:.8e}'.format(x).rjust(16) for x in nn[i, :4]]) + '\n') for i in range(3): ofile.write(reduce(add, ['{0:.8e}'.format(x).rjust(16) for x in nn[i, 4:]]) + '\n') print 'Writing the curved side info\n' ofile.write(" ***** CURVED SIDE DATA ***** \n") cc = "{0:6d} Curved sides follow IEDGE,IEL,CURVE(I),I=1,5, CCURVE \n" if tot_num_cells < 1000: c1 = "{0:3d}{1:3d}" else: c1 = "{0:2d}{1:6d}" c2 = "{0:14.6e}{1:14.6e}{2:14.6e}{3:14.6e}{4:14.6e} {5:s}\n" N = 0 for zone in curves: if curves_map[zone][1]['type'] == 'C': N += len(curves_map[zone][0]) elif curves_map[zone][1]['type'] == 'm': N += len(curved_faces[zone]) ofile.write(cc.format(N)) for zone in curves: if curves_map[zone][1]['type'] == 'C': for i in range(len(curves_map[zone][0])): xx = curves_map[zone][1]['x'][i] ofile.write(c1.format(curves_map[zone][0][i][1], curves_map[zone][0][i][0])) ofile.write(c2.format(xx[0], xx[1], xx[2], xx[3], xx[4], curves_map[zone][1]['type'])) elif curves_map[zone][1]['type'] == 'm': for i in range(zone_number_of_faces[zone]): if curves_map[zone][0][i][2] in curved_faces[zone]: ofile.write(c1.format(curves_map[zone][0][i][1], curves_map[zone][0][i][0])) xx = mid_point_map[curves_map[zone][0][i][2]] ofile.write(c2.format(xx[0], xx[1], xx[2], xx[3], xx[4], curves_map[zone][1]['type'])) print 'Writing the boundary and bottom section\n' ofile.write(' ***** BOUNDARY CONDITIONS ***** \n') ofile.write(' ***** FLUID BOUNDARY CONDITIONS ***** \n') f_str = " {0:s} " if tot_num_cells < 1000: f_str1 = "{0:3d}{1:3d}" elif tot_num_cells < 100000: f_str1 = "{0:5d}{1:1d}" elif tot_num_cells < 1000000: f_str1 = "{0:6d}{1:1d}" f_str2 = "{0:14.7e}{1:14.7e}{2:14.7e}{3:14.7e}{4:14.7e}\n" for i in range(1, tot_num_cells + 1): for j in range(1, 2*dim + 1): bm = boundary_map[(i, j)] ofile.write(f_str.format(boundary_val_map[(i, j)])) ofile.write(f_str1.format(i, j)) ofile.write(f_str2.format(bm[0], bm[1], 0, 0, 0)) if not temperature: ofile.write(' ***** NO THERMAL BOUNDARY CONDITIONS ***** \n') else: ofile.write(' ***** THERMAL BOUNDARY CONDITIONS ***** \n') for i in range(1, tot_num_cells + 1): for j in range(1, 2*dim + 1): bm = boundary_map[(i, j)] ofile.write(f_str %(temperature_val_map[(i, j)], i, j, bm[0], bm[1], 0, 0, 0)) if len(passive_scalars) == 0: pass else: for si, scalar in enumerate(passive_scalars): ofile.write(' ***** PASSIVE SCALAR %s BOUNDARY CONDITIONS ***** \n' %(si + 1)) for i in range(1, tot_num_cells + 1): for j in range(1, 2*dim + 1): bm = boundary_map[(i, j)] ofile.write(f_str %(passive_scalar_val_map[si][(i, j)], i, j, bm[0], bm[1], 0, 0, 0)) ofile.write(end_of_rea) print 'Finished writing the rea-file\n' # Close files ofile.close() def write_semtex_file(dim, ofilename, curves, cylindrical, NZ): tot_num_cells = len(cell_map) if not dim == 2: print 'Error, semtex uses only 2D meshes' raise TypeError print 'Writing semtex mesh file\n' print 'Writing first some default tokens. Be sure to modify these to your own liking later\n' ofile = open(ofilename, "w") ofile.write('\n') if NZ == 1: ofile.write(' u v p\n') else: ofile.write(' u v w p\n') ofile.write('\n\n') #Inserting some default tokens: ofile.write(""" KINVIS = 0.01 D_T = 0.01 N_STEP = 100 CYLINDRICAL = %d CHKPOINT = 1 N_TIME = 2 Lz = 10 N_P = 9 N_Z = %d IO_CFL = 20 IO_FLD = N_STEP \n\n""" %(cylindrical, NZ)) if NZ == 1: ofile.write(""" u = 0.0 v = 0.0 p = 0.0 \n\n""") else: ofile.write(""" u = 0.0 v = 0.0 w = 0.0 p = 0.0 \n\n""") semtex_bnds = {} for key, val in boundary_val_map.iteritems(): if not val == 'E': if val in semtex_bnds: semtex_bnds[val].append(key) else: semtex_bnds[val] = [key] N = 0 for key, val in semtex_bnds.iteritems(): if key == 'P': N += len(val)/2 else: N += len(val) len_bnds = len(semtex_bnds) - {True: 1, False: 0}['P' in semtex_bnds] if len_bnds > 0: ofile.write('\n' %(len_bnds)) bcsc = copy(bcs_copy) for key, val in bcs_copy.iteritems(): bcsc[val] = key groups_str = '{0:4d}{1:>8}{2:>16}\n' i = 0 for key, val in semtex_bnds.iteritems(): if not key == 'P': i += 1 ofile.write(groups_str.format(i, key, zones[bcsc[key]][0])) ofile.write('\n\n') ofile.write('' %(len_bnds)) if NZ == 1: dirichlet_str = """ {0:4d}{1:>4} 3 u = 0.0 v = 0.0 p = 0.0 \n""" neuman_str = """ {0:4d}{1:>4} 3 u = 0.0 v = 0.0 p = 0.0 \n""" axis_str = """ {0:4d}{1:>4} 3 u = 0.0 v = 0.0 p = 0.0 \n""" else: dirichlet_str = """ {0:4d}{1:>4} 4 u = 0.0 v = 0.0 w = 0.0 p = 0.0 \n""" neuman_str = """ {0:4d}{1:>4} 4 u = 0.0 v = 0.0 w = 0.0 p = 0.0 \n""" axis_str = """ {0:4d}{1:>4} 4 u = 0.0 v = 0.0 w = 0.0 p = 0.0 \n""" i = 0 for key, val in semtex_bnds.iteritems(): if not key == 'P': i += 1 if key in ('a', 'A'): ofile.write(axis_str.format(i, key)) elif key in ('o', 'O'): ofile.write(neuman_str.format(i, key)) else: ofile.write(dirichlet_str.format(i, key)) ofile.write('\n\n') print 'Writing nodes\n' nodes_header = '\n' node_str = ' {0:5d} {1:14.7e} {2:14.7e} 0.000000\n' ofile.write(nodes_header %(nodes.shape[1])) for i in range(nodes.shape[1]): ofile.write(node_str.format(i+1, nodes[0, i], nodes[1, i])) ofile.write('\n\n') print 'Writing elements\n' element_header = '\n' element_str = ' {0:5d} {1:4d}{2:4d}{3:4d}{4:4d} \n' ofile.write(element_header %(tot_num_cells)) for i in range(1, tot_num_cells + 1): ofile.write(element_str.format(i, cell_map[i][0], cell_map[i][1], cell_map[i][2], cell_map[i][3])) ofile.write('\n\n') print 'Writing surfaces\n' surfaces_header = '\n' ofile.write(surfaces_header %(N)) surfaces_str = '{0:4d}{1:4d}{2:4d} {3:s} \n' surfaces_pstr = '{0:4d}{1:4d}{2:4d}

{3:4d}{4:4d}

\n' i = 0 for key in semtex_bnds.iterkeys(): for val in semtex_bnds[key]: i += 1 if not key == 'P': ofile.write(surfaces_str.format(i, val[0], val[1], key)) else: pp = periodic_cell_face_map[val] ofile.write(surfaces_pstr.format(i, val[0], val[1], pp[0], pp[1])) semtex_bnds['P'].remove(pp) ofile.write('
\n\n') if len(curves) > 0: print 'Writing curves\n' cc = '\n' c1 = '{0:4d}{1:6d}{2:6d} {3:14.7f} \n' c2 = ' {0:4d} {1:6d} {2:4d} {3:s} \n' tmp_list = [] count = 0 for zone in curves: if curves_map[zone][1]['type'] == 'C': for i in range(len(curves_map[zone][0])): xx = curves_map[zone][1]['x'][i] count += 1 tmp_list.append(c1.format(count, curves_map[zone][0][i][0], curves_map[zone][0][i][1], xx[0])) elif curves_map[zone][1]['type'] == 'spline': spline_file = open(ofilename + '.geom', 'w') spl_nodes = [] for nd in boundary_nodes[zone]: if nd in curved_nodes[zone]: spl_nodes.append([nodes[0, nd - 1], nodes[1, nd - 1]]) spl_nodes = array(spl_nodes) ind = spl_nodes[:, 0].argsort() for k in ind: x, y = spl_nodes[k, :] spline_file.write(' {0:14.6f} {1:14.6f}\n'.format(x, y)) spline_file.close() for i in range(len(curves_map[zone][0])): face = face_list[curves_map[zone][0][i][2]] nds = face[1] if curved_nodes[zone].__contains__(nds[0] or nds[1]): count += 1 tmp_list.append(c2.format(count, curves_map[zone][0][i][0], curves_map[zone][0][i][1], ofilename + '.geom')) ofile.write(cc.format(count)) for tmp in tmp_list: ofile.write(tmp) ofile.write('\n') print 'Finished writing semtex mesh\n' ofile.close() def write_fenics_file(dim, ofilename): ofile = File(ofilename + '.xml') mesh = Mesh() editor = MeshEditor() editor.open(mesh, dim, dim) editor.init_vertices(nodes.shape[1]) editor.init_cells(len(cell_map)) for i in range(nodes.shape[1]): if dim == 2: editor.add_vertex(i, nodes[0, i], nodes[1, i]) else: editor.add_vertex(i, nodes[0, i], nodes[1, i], nodes[2, i]) for i in range(1, len(cell_map)+1): if dim == 2: editor.add_cell(i-1, cell_map[i][0]-1, cell_map[i][1]-1, cell_map[i][2]-1) else: editor.add_cell(i-1, cell_map[i][0]-1, cell_map[i][1]-1, cell_map[i][2]-1, cell_map[i][3]-1) mesh.order() mvc = mesh.domains().markers(dim-1) for zone, faces in boundary_faces.iteritems(): for face in faces: cell = face_list[face][2][0] dolfin_cell = Cell(mesh, cell-1) nodes_of_cell = dolfin_cell.entities(0) nodes_of_face = array(face_list[face][1]) - 1 for jj, ff in enumerate(facets(dolfin_cell)): facet_nodes = ff.entities(0) if all(map(lambda x: x in nodes_of_face, facet_nodes)): local_index = jj break mvc.set_value(cell-1, local_index, zone) ofile << mesh from dolfin import plot plot(mesh, interactive=True) print 'Finished writing FEniCS mesh\n' def convert(fluentmesh, func=None, mesh_format='nek5000', # nek5000, semtex or fenics periodic_dx={}, curves = {}, bcs = False, # nek5000 and semtex temperature=False, passive_scalars=[], # nek5000 only cylindrical=1, NZ=1): # semtex only """Converts a fluent mesh to a mesh format that can be used by Nek5000, semtex or FEniCS. fluentmesh = fluent mesh (*.msh file) func = Optional function of spatial coordinates (x,y) that can be used to modify the fluent mesh. For example, say you have a mesh that is a rectangular geometry with -2 < x < 6 and 0 < y 0.5. Now you want to squeeze this mesh around x = 0 to create a stenosis type of mesh. This can be accomplished by squeezing the mesh in the y-direction through: def func_y(x, y): if abs(x) < 1.: return y*(1 - 0.25*(1 + cos(x*pi))) else: return y and supplying this value to the keyword func like: func={'y': func_y} Otherwise you might just create this stenosis in your mesh generation software and supply nothing for func. Note that in nek5000 you will most likely do this in subroutines userdat or userdat2. mesh_format = 'nek5000', 'semtex' or 'fenics' bcs = False or dictionary of boundary conditions for velocity/pressure (something like {1: 'W', 2: 'v'} for wall in zone 1 and Dirchlet to be specified in zone 2). False indicates that dictionary is not used and in that case we assume the name of the zone ends in the correct letter, like 'somename_W' for zone 1 and 'somename_v' for zone 2. Zonenames are easy to modify at the bottom of the fluent msh files. Don't include periodic zones here. periodic_dx = Dictionary describing any periodicity in the mesh. Keys are tuples of the two periodic zones (zone0, zone1) and values are displacement vectors. Note that the current program also can read a mesh where periodicity has been generated in the mesh generator. However, this author has still not managed to create a periodic 3D mesh correctly in any mesh software. Hence I prefer to define periodicity through this dictionary here and do nothing regarding periodicity in the meshing software. All you need to know are the ids and displacement of the periodic zones. Connectivity will then be computed here. Note that each periodic zone needs to be its own zone. Simply creating a 3D UnitCube in gambit and not assigning names to the 6 zones won't work. We need zone identifiers (for now). Not for FEniCS. curves = Dictionary of curve information. Keys are curve zones and value is either {'type': 'C', 'radius': radius, 'circle_center': (x, y), 'depth': depth} for a circle or {'type': 'm'} for midpoint with nek5000 or {'type': 'spline'} for a curved side with semtex. Here a .geom file containing the spline information will be created. The circle may provide the radius or the center of the circle through 'circle_center'. The curvature may also be used in the internal elements inside the surface through specifying the depth. depth=4 means that the curvature is used throughout the first four elements inside that surface. This is necessary to get good quality meshes in, e.g., a cylinder. The radius for an internal face is allways computed as the distance to the circle_center. Not for FEniCS. temperature = False or dictionary of boundary conditions for temperature (something like {1: 'W', 2: 'v'}) for Wall in zone 1 and Dirchlet specified in .usr in zone 2. False indicates that temperature is not used. (nek5000 only) passive_scalars = [] or list of dictionaries of boundary conditions for passive scalars. Empty means no passive scalars are used. (nek5000 only) cylindrical = 1 for cylindrical mesh and 0 for regular (semtex only) NZ = Number of planes in homogeneous direction (semtex only) """ ofilename = fluentmesh[:-4] ifile = open(fluentmesh, "r") if not nodes: # Read all lines of fluent mesh #lines = ifile.readlines() #if len(lines) == 0: #raise IOError("Empty fluent mesh file") #scan_fluent_mesh(lines) scan_fluent_mesh(ifile) dim = nodes.shape[0] create_cell_face_map(dim, mesh_format) create_periodic_face_map(periodic_dx) create_periodic_cell_face_map() create_boundary_section(bcs, temperature, passive_scalars, mesh_format) # Modify the entire mesh using the shape-function func if func: sz = nodes.shape for i in range(sz[1]): x, y = nodes[:, i] if 'x' in func: xnew = func['x'](x, y) if abs(xnew - x) > 1e-6: nodes[0, i] = xnew if 'y' in func: ynew = func['y'](x, y) if abs(ynew - y) > 1e-6: nodes[1, i] = ynew if mesh_format == 'nek5000': print 'Warning!! Consider using userdat/userdat2 instead!' if not mesh_format == 'fenics': read_curved_sides(curves) # Generate the mesh files for given mesh format if mesh_format == 'nek5000': write_nek5000_file(dim, ofilename, curves, temperature, passive_scalars) elif mesh_format == 'semtex': write_semtex_file(dim, ofilename, curves, cylindrical, NZ) if mesh_format == 'fenics': write_fenics_file(dim, ofilename) ifile.close()