def writeGrid(self, baseName=None): """ Write the grids of each instance Parameters ---------- baseName : str or None a base namve that will be used to generate filenames for all instance mesh files. Ney Secco 2017-03 """ # We need to reexplode the original file if self.myID == 0: # Load the CGNS file combined_file = cs.readGrid(self.CGNSFile) # Explode the CGNS file by zones (this will only work if the user used cgns_utils combine # to create the input CGNS file, since the explosion uses the domain names) grids, zoneNames = cs.explodeByZoneName(combined_file) # Save temporary grid files with the exploded zones for grid, zoneName in zip(grids, zoneNames): grid.writeToCGNS("_" + zoneName + ".cgns") # Delete grids to free space del grids del combined_file # Assign baseName if user provided none if baseName is None: baseName = "IDWarpMulti" # Loop over all instances to write their meshes for instanceID, mesh in enumerate(self.meshes): # Generate a fileName fileName = baseName + "_inst%03d.cgns" % (instanceID) # Call function to write the mesh of the current instance mesh.writeGrid(fileName) # Now the root proc can remove the temporary grid files if self.myID == 0: for zoneName in zoneNames: os.system("rm _" + zoneName + ".cgns")
def main(): parser = get_parser() # Get the arguments we need! args = parser.parse_args() # ------------------------------------------- # Selection of the task # ------------------------------------------- # The time combine is special. First we generate the list of files we # need to deal with. if args.mode == "timeCombine": # Get the directory name where the baseName is: path = os.path.dirname(os.path.abspath(args.baseName)) # Get the full list of files in this directory: allFiles = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))] files = [] parts = args.baseName.split("%d") maxLength = 0 for f in allFiles: if (parts[0] == "" or parts[0] in f) and (parts[1] == "" or parts[1] in f): # Make sure there is a .cgns in there somwhere if ".cgns" in f: files.append(f) maxLength = max(maxLength, len(f)) files = sorted(files) if args.outFile is None: outFile = "unsteady.plt" else: outFile = args.outFile # Now we make a character array of the file names, and hand if off to # fortran for all the actual reading/writing. fileNames = numpy.zeros((len(files), 256), "c") for i in range(len(files)): fileNames[i, 0 : len(files[i])] = files[i] libcgns_utils.utils.time_combine(fileNames, outFile) sys.exit(0) if args.mode == "testBlock": nx = args.nx ny = args.ny nz = args.nz X = numpy.zeros((nx, ny, nz, 3)) Xcart = [] Xcart.append(numpy.linspace(0, 1, nx)) Xcart.append(numpy.linspace(0, 1, ny)) Xcart.append(numpy.linspace(0, 1, nz)) Xx, Xy, Xz = numpy.meshgrid(Xcart[0], Xcart[1], Xcart[2], indexing="ij") X[:, :, :, 0] = Xx X[:, :, :, 1] = Xy X[:, :, :, 2] = Xz b = Block("domain.00001", [nx, ny, nz], X) # Add bocos so we can run it: b.addBoco(Boco("iMin", BC["bcfarfield"], [[1, 1], [1, ny], [1, nz]], "far")) b.addBoco(Boco("iMax", BC["bcfarfield"], [[nx, nx], [1, ny], [1, nz]], "far")) b.addBoco(Boco("jMin", BC["bcsymmetryplane"], [[1, nx], [1, 1], [1, nz]], "sym")) b.addBoco(Boco("jMax", BC["bcsymmetryplane"], [[1, nx], [ny, ny], [1, nz]], "sym")) b.addBoco(Boco("kMin", BC["bcwallviscous"], [[1, nx], [1, ny], [1, 1]], "wall")) b.addBoco(Boco("kMax", BC["bcfarfield"], [[1, nx], [1, ny], [nz, nz]], "far")) g = Grid() g.addBlock(b) g.writeToCGNS(args.outFile) sys.exit(0) # The 'combine' function is done first sicne it is the only function # that reads multiple cgns files. if args.mode == "combine": grids = [] for fName in args.gridFiles: grid = readGrid(fName) grids.append(grid) combinedGrid = combineGrids(grids) combinedGrid.writeToCGNS(args.outFile) # This task is now finished sys.exit(0) if args.mode == "explicitCart": # This task doesn't have args.gridFile so do it first xMin = [args.xmin, args.ymin, args.zmin] xMax = [args.xmax, args.ymax, args.zmax] simpleCart(xMin, xMax, args.dh, args.hExtra, args.nExtra, args.sym, args.mgcycle, args.outFile) sys.exit(0) if args.mode == "plot3d2cgns": # This doesn't read a cgns file so do this first too. convertPlot3d(args.plot3dFile, args.gridFile) sys.exit(0) # Get the current working grid 'curGrid' by reading the input curGrid = readGrid(args.gridFile) # The following are "special" and done first since they do not # have a CGNS output. if args.mode == "extractSurface": curGrid.extractSurface(args.surfFile) sys.exit(0) if args.mode == "extractSpecifiedSurface": curGrid.extractSpecifiedSurface( args.surfFile, args.blockID, args.imin, args.imax, args.jmin, args.jmax, args.kmin, args.kmax ) sys.exit(0) if args.mode == "cgns2plot3d": curGrid.writePlot3d(args.plot3dFile) sys.exit(0) if args.mode == "blockSizes": curGrid.printBlockInfo() sys.exit(0) # Determine if we have an output file: try: if args.outFile is None: # Determine where to put a file: dirpath = tempfile.mkdtemp() # Define a temp output file outFileName = os.path.join(dirpath, "tmp.cgns") else: outFileName = args.outFile except Exception: outFile = None # Perform one of the following actions: if args.mode == "flip": curGrid.flip(args.axis) elif args.mode == "scale": curGrid.scale(args.scale) elif args.mode == "mirror": curGrid = mirrorGrid(curGrid, args.axis, args.tol) elif args.mode == "coarsen": curGrid.coarsen() elif args.mode == "refine": curGrid.refine(args.axes) elif args.mode == "split": curGrid = splitGrid(curGrid, args.splitFile) elif args.mode == "merge": curGrid = mergeGrid(curGrid) elif args.mode == "connect": if args.connectSelf: curGrid.connectSelfOnly(args.tol) else: curGrid.connect(args.tol) elif args.mode == "divide": curGrid = divideGrid(curGrid) elif args.mode == "autoBC": curGrid.autoBC(args.radius, args.sym, [args.xOffset, args.yOffset, args.zOffset]) elif args.mode == "overwriteFamilies": curGrid.overwriteFamilies(args.familyFile) elif args.mode == "writeSubfaceFamily": curGrid.writeSubfaceFamily(args.familyFile) elif args.mode == "copyFamilyInfo": sourceGrid = readGrid(args.sourceFile) curGrid.copyFamilyInfo(sourceGrid) elif args.mode == "overwriteBC": curGrid.overwriteBCs(args.bcFile) elif args.mode == "removeBC": curGrid.removeBCs() elif args.mode == "rebunch": curGrid.rebunch(args.spacing, args.extraCells, args.nodes) elif args.mode == "randomize": curGrid.randomize(args.seed, args.keepRHS) elif args.mode == "reorder": curGrid.reorder(args.intDigits) elif args.mode == "symmZero": curGrid.symmZero(args.sym) elif args.mode == "symmZeroNoBC": curGrid.symmZeroNoBC(args.sym, args.tol) elif args.mode == "double2D": curGrid.double2D() elif args.mode == "removeBlocks": curGrid.removeBlocks(args.blockIDs) elif args.mode == "cartesian": found_overset = False for block in curGrid.blocks: for boco in block.bocos: if boco.type == BC["bcoverset"]: found_overset = True if found_overset: curGrid.cartesian(args.cartFile, args.outFile) else: print("The CGNS file has no overset boundary conditions") sys.exit(0) elif args.mode == "simpleCart": curGrid.simpleCart(args.dh, args.hExtra, args.nExtra, args.sym, args.mgcycle, args.outFile) sys.exit(0) elif args.mode == "simpleOCart": curGrid.simpleOCart(args.dh, args.hExtra, args.nExtra, args.sym, args.mgcycle, args.outFile) sys.exit(0) elif args.mode == "translate": curGrid.translate(args.dx, args.dy, args.dz) elif args.mode == "rotate": curGrid.rotate(args.vx, args.vy, args.vz, args.theta) elif args.mode == "autoOversetBC": curGrid.autoOversetBC(args.sym, args.connectSelf, args.tol) elif args.mode == "autoNearfieldBC": curGrid.autoNearfieldBC(args.sym) elif args.mode == "autoFarfieldBC": curGrid.autoFarfieldBC(args.sym) elif args.mode == "fillOpenBCs": curGrid.fillOpenBCs(BC[args.bocoType], args.famName) elif args.mode == "include": toWrite = [] for spec in args.rangeSpec.split(","): if "-" in spec: tmp = spec.split("-") start = int(tmp[0]) end = int(tmp[1]) else: start = int(spec) end = int(spec) for i in range(start, end + 1): toWrite.append(i) toWrite = numpy.unique(toWrite) toWrite.sort() curGrid.writeToCGNSSelected(args.outFile, toWrite) sys.exit(0) elif args.mode == "section": if len(curGrid.blocks) != 1: print("section command works only on grids with 1 block") sys.exit(0) curGrid.blocks[0].section(args.iStart, args.iEnd, args.jStart, args.jEnd, args.kStart, args.kEnd) elif args.mode == "explode": # Split original multiblock grid in a list of single-block grids gridList = explodeGrid(curGrid) # Check if the user gave a reference name. Otherwise, use the input name as reference if args.outFile is None: # Get the base name outFile = os.path.splitext(os.path.basename(args.gridFile))[0] else: outFile = args.outFile # Generate a list of names for the files by adding integers to the reference name fileNames = [outFile + "_%03d" % index + ".cgns" for index in range(1, len(gridList) + 1)] # Save each grid for index in range(len(gridList)): gridList[index].writeToCGNS(fileNames[index]) # Finish execution sys.exit(0) elif args.mode == "explodeKmin": # Split original multiblock grid in a list of single-block grids # that contains just the K = 1 face gridList = explodeGrid(curGrid, kMin=True) # Check if the user gave a reference name. Otherwise, use the input name as reference if args.outFile is None: # Get the base name outFile = os.path.splitext(os.path.basename(args.gridFile))[0] else: outFile = args.outFile # Generate a list of names for the files by adding integers to the reference name fileNames = [outFile + "_%03d" % index + ".xyz" for index in range(1, len(gridList) + 1)] # Save each grid for index in range(len(gridList)): gridList[index].writePlot3d(fileNames[index]) # Finish execution sys.exit(0) elif args.mode == "explodeByZoneName": # Split original multiblock grid into a list of multiblock grids # that correspond to each component based on zone names gridList, nameList = explodeByZoneName(curGrid) # Save each grid for grid in gridList: fileName = grid.name grid.writeToCGNS(fileName + ".cgns") # Finish execution sys.exit(0) elif args.mode == "info": curGrid.printInfo() sys.exit(0) elif args.mode == "extrude": curGrid.extrude(args.direction) elif args.mode == "revolve": if args.normalDirection == args.axis: print("ERROR: Normal direction and revolve axis cannot be the same. Exiting...") sys.exit(0) curGrid.revolve(args.normalDirection, args.axis, args.startAngle, args.endAngle, args.nThetas) elif args.mode == "extractConv": # extracts the convergence history contained in the CGNS file and saves it in a pickle file # Check if the user gave a reference name. Otherwise, use the input name as reference if args.outFile is None: # Get the base name outFile = os.path.splitext(os.path.basename(args.gridFile))[0] # Add extension based on output type if args.outType == "pickle": outFile = outFile + ".pickle" elif args.outType == "tecplot": outFile = outFile + ".dat" else: outFile = args.outFile # The function readGrid already read all the convergence history arrays. # Now we just need to save them in a file! if args.outType == "pickle": with open(outFile, "w") as fid: pickle.dump(curGrid.convArray, fid) elif args.outType == "tecplot": # Create a single 2D array that will contain all data data = [] # Get the number of iterations numIter = len(curGrid.convArray[curGrid.convArray.keys()[0]]) # Append iteration counter data.append(range(1, numIter + 1)) for entry in curGrid.convArray.keys(): data.append(curGrid.convArray[entry]) # Convert data to array data = numpy.array(data).T # Write tecplot results write_tecplot_file(outFile, "Convergence", ["Iteration"] + curGrid.convArray.keys(), data) # Print log print("Saved convergence history into:") print(outFile) # Finish execution sys.exit(0) # Write out the grid. curGrid.writeToCGNS(outFileName) # Possibly copy back to the original: if args.outFile is None: shutil.copyfile(outFileName, args.gridFile) shutil.rmtree(dirpath)
def __init__(self, CGNSFile, optionsDict, comm=None, dtype="d", debug=False): """ Create the MultiUSMesh object. INPUTS: CGNSFile: string -> file name of the CGNS file. This CGNS file should be generated with cgns_utils combine, so that the domain names have the appropriate convention. That is, domains will have the same name as their original files. Domains that share the same name will be grouped to make an IDWarp instance. optionsDict: dictionary of dictionaries -> Dictionary containing dictionaries that will be used to initialize multiple IDWarp instances. The keys are domain names and the values are dictionaries of standard IDWarp options that will be applied to this domain. The domains of the full CGNS file that do not have a corresponding entry in optionsDict will not be warped. For instance, if the CGNS file has the domains wing.00000, wing.00001, and wing.00002 associated with a wing mesh that we want to warp, then optionsDict should have an entry for 'wing'. Ney Secco 2017-02 """ # Check if cs was imported correctly: if cs is None: raise Error("cgns_utils could not be loaded correctly. MultiUSMesh " "requires cgns_utils to function.") # Assign communicator if we do not have one yet if comm is None: comm = MPI.COMM_WORLD # Check if warp has already been set by the complex version try: self.warp except AttributeError: curDir = os.path.basename(os.path.dirname(os.path.realpath(__file__))) self.warp = MExt("libidwarp", curDir, debug=debug)._module # Store communicator self.comm = comm # Store original file name self.CGNSFile = CGNSFile # Store scalar type self.dtype = dtype # Only the root processor will take the combined CGNS file # and explode it by instance. if self.myID == 0: # Initialize list to store the block IDs that belong to each IDWarp instance. # For example, suppose that our combined CGNS file has 21 blocks. # Blocks 1 to 5 belong to the fuselage # Blocks 6 to 12 belong to the wing # Blocks 13 to 21 belong to the background mesh # Then cgnsBlockIntervals = [[0,5],[5,12],[12,21] self.cgnsBlockIntervals = [] # Initialize array to store volume nodes CGNS intervals for each instance self.cgnsVolNodeIntervals = [] # Initialize block counter blockCounter = 0 # Initialize node counter nodeCounter = 0 # Load the CGNS file combined_file = cs.readGrid(CGNSFile) # Explode the CGNS file by zones (this will only work if the user used cgns_utils combine # to create the input CGNS file, since the explosion uses the domain names) grids, zoneNames = cs.explodeByZoneName(combined_file) # Save temporary grid files with the exploded zones for grid, zoneName in zip(grids, zoneNames): grid.writeToCGNS("_" + zoneName + ".cgns") # Store the number of blocks in each zone self.cgnsBlockIntervals.append([blockCounter, blockCounter + len(grid.blocks)]) # Count the number of nodes (here is degrees of freedom or nodes*3 totalNodes = 0 for blk in grid.blocks: totalNodes += blk.dims[0] * blk.dims[1] * blk.dims[2] * 3 # Store the number of volume nodes in each zone self.cgnsVolNodeIntervals.append([nodeCounter, nodeCounter + totalNodes]) # Update block counter blockCounter = blockCounter + len(grid.blocks) # Update node counter nodeCounter = nodeCounter + totalNodes # Delete grids to free space del grids del combined_file else: # Initialize variables to get results in the end zoneNames = None self.cgnsBlockIntervals = None self.cgnsVolNodeIntervals = None # Send information to all procs zoneNames = self.comm.bcast(zoneNames, root=0) self.cgnsBlockIntervals = self.comm.bcast(self.cgnsBlockIntervals, root=0) self.cgnsVolNodeIntervals = self.comm.bcast(self.cgnsVolNodeIntervals, root=0) # Get names for nearfield meshes. # The nearfield mesh names will be the keys of the options dictionary. nearfieldNames = optionsDict.keys() # Initialize list of IDWarp instances self.meshes = [] # Initialize list to hold indices of the background zones self.backgroundInstanceIDs = [] # Loop over all zones that we found in the combined CGNS file for zoneNumber, zoneName in enumerate(zoneNames): # Check if the zone belongs to a nearfield mesh if zoneName in nearfieldNames: # ------------------------------------------------------ # READING NEARFIELD MESHES (The ones that will be warped) # # Assign the name of the temporary CGNS file to the options. # This is the file that contains the mesh o a single component. # Remember that we should use the temporary grid file. optionsDict[zoneName]["gridFile"] = "_" + zoneName + ".cgns" # Initialize an IDWarp instance with the current options if self.dtype == "d": currMesh = self.warp.USMesh(options=optionsDict[zoneName], comm=self.comm) elif self.dtype == "D": currMesh = self.warp.USMesh_C(options=optionsDict[zoneName], comm=self.comm) else: # We have a background mesh # Regenerate the temporary filename for the background grid bgFile = "_" + zoneName + ".cgns" # ------------------------------------------------------ # READING BACKGROUND MESHES # =========================================================# # THIS IS A MESSY (HOPEFULLY TEMPORARY) WAY OF LOADING THE # BACKGROUND MESH NODES. IF YOU COME UP WITH A BETTER WAY # TO GET volNodes, PLEASE ADD IT HERE. # volNodes is a flattened vector that contains the background # mesh volume nodes that belong to the current proc. # Let's try using IDWarp's CGNS loader to extract the bakground nodes. # However, we will have to trick IDWarp in order to do this, since it # expects a surface mesh in the file. # So we will make a copy of the background mesh file, assign an arbitrary # wall surface, and then load it with IDWarp # Only the root proc will modify the input file if self.myID == 0: # Make a copy of the background mesh file os.system("cp " + bgFile + " tmp_bg_file.cgns") # Create a temporary BC file with open("tmp_bcdata.dat", "w") as fid: fid.write("1 iLow BCwall wall\n") # Use CGNS utils to modify the BCs os.system("cgns_utils overwritebc tmp_bg_file.cgns tmp_bcdata.dat") # Create dummy set of options just to load the CGNS file dummyOptions = { "gridFile": "tmp_bg_file.cgns", "warpType": "unstructured", } # Initialize an IDWarp instance with the current options if self.dtype == "d": currMesh = self.warp.USMesh(options=dummyOptions, comm=self.comm) elif self.dtype == "D": currMesh = self.warp.USMesh_C(options=dummyOptions, comm=self.comm) # Initialize a dummy surface in the background mesh """ if self.myID == 0: print('===========================================') print('ATTENTION: This is a dummy initialization for background mesh warping.') pts = np.array([[1.0, 0.0, 1.0], [2.0, 0.0, 1.0], [2.0, 1.0, 1.0], [1.0, 1.0, 1.0]])*(self.myID+1) conn = np.array([0,1,2,3]) faceSizes = np.array([4]) currMesh.setSurfaceDefinition(pts, conn, faceSizes) if self.myID == 0: print('Dummy initialization is Done!') print('===========================================') """ if self.myID == 0: print("===========================================") print("ATTENTION: This is a dummy initialization for background mesh warping.") currMesh._setInternalSurface() if self.myID == 0: print("Dummy initialization is Done!") print("===========================================") # The root proc can remove the temporary files if self.myID == 0: # Make a copy of the background mesh file os.system("rm tmp_bg_file.cgns") os.system("rm tmp_bcdata.dat") # Store the ID of this zone self.backgroundInstanceIDs.append(zoneNumber) # Append the instance to the list. # We will store even the background mesh instances for now, # but we will delete them as soon as we call self.setExternalMeshIndices(). self.meshes.append(currMesh) # Now the root proc can remove the temporary grid files if self.myID == 0: for zoneName in zoneNames: os.system("rm _" + zoneName + ".cgns") # ------------------------------------------------------ # Initialize other fields for completness self.numSurfNodes = None # How many solver surface nodes we have in the current proc, for all instances self.numVolNodes = None # How many solver volume nodes we have in the current proc, for all instances self.cgnsVolNodeMasks = [] # Mask used to filter which volume nodes given by the solver belong to each instance