def split(nxpe, nype, path="data", output="./", informat="nc", outformat=None): """Split restart files across NXPE x NYPE processors. Returns True on success """ if outformat == None: outformat = informat mxg = 2 myg = 2 npes = nxpe * nype if npes <= 0: print "ERROR: Negative or zero number of processors" return False if path == output: print "ERROR: Can't overwrite restart files" return False file_list = glob.glob(os.path.join(path, "BOUT.restart.*."+informat)) nfiles = len(file_list) if nfiles == 0: print "ERROR: No restart files found" return False # Read old processor layout f = DataFile(os.path.join(path, file_list[0])) # Get list of variables var_list = f.list() if len(var_list) == 0: print "ERROR: No data found" return False old_npes = f.read('NPES') old_nxpe = f.read('NXPE') if nfiles != old_npes: print "WARNING: Number of restart files inconsistent with NPES" print "Setting nfiles = " + str(old_npes) nfiles = old_npes if old_npes % old_nxpe != 0: print "ERROR: Old NPES is not a multiple of old NXPE" return False old_nype = old_npes / old_nxpe if nype % old_nype != 0: print "SORRY: New nype must be a multiple of old nype" return False if nxpe % old_nxpe != 0: print "SORRY: New nxpe must be a multiple of old nxpe" return False # Get dimension sizes old_mxsub = 0 old_mysub = 0 mz = 0 for v in var_list: if f.ndims(v) == 3: s = f.size(v) old_mxsub = s[0] - 2*mxg old_mysub = s[1] - 2*myg mz = s[2] break f.close() # Calculate total size of the grid nx = old_mxsub * old_nxpe ny = old_mysub * old_nype print "Grid sizes: ", nx, ny, mz # Create the new restart files for mype in range(npes): # Calculate X and Y processor numbers pex = mype % nxpe pey = int(mype / nxpe) old_pex = int(pex / xs) old_pey = int(pey / ys) old_x = pex % xs old_y = pey % ys # Old restart file number old_mype = old_nxpe * old_pey + old_pex # Calculate indices in old restart file xmin = old_x*mxsub xmax = xmin + mxsub - 1 + 2*mxg ymin = old_y*mysub ymax = ymin + mysub - 1 + 2*myg print "New: "+str(mype)+" ("+str(pex)+", "+str(pey)+")" print " => "+str(old_mype)+" ("+str(old_pex)+", "+str(old_pey)+") : ("+str(old_x)+", "+str(old_y)+")"
def redistribute(npes, path="data", nxpe=None, output=".", informat=None, outformat=None, mxg=2, myg=2): """Resize restart files across NPES processors. Does not check if new processor arrangement is compatible with the branch cuts. In this respect restart.split is safer. However, BOUT++ checks the topology during initialisation anyway so this is not too serious. Parameters ---------- npes : int number of processors for the new restart files path : string, optional location of old restart files nxpe : int, optional number of processors to use in the x-direction (determines split: npes = nxpe * nype). Default is None which uses the same algorithm as BoutMesh (but without topology information) to determine a suitable value for nxpe. output : string, optional location to save new restart files informat : string, optional specify file format of old restart files (must be a suffix understood by DataFile, e.g. 'nc'). Default uses the format of the first 'BOUT.restart.*' file listed by glob.glob. outformat : string, optional specify file format of new restart files (must be a suffix understood by DataFile, e.g. 'nc'). Default is to use the same as informat. Returns ------- True on success """ if npes <= 0: print("ERROR: Negative or zero number of processors") return False if path == output: print("ERROR: Can't overwrite restart files") return False if informat == None: file_list = glob.glob(os.path.join(path, "BOUT.restart.*")) else: file_list = glob.glob(os.path.join(path, "BOUT.restart.*."+informat)) nfiles = len(file_list) # Read old processor layout f = DataFile(file_list[0]) # Get list of variables var_list = f.list() if len(var_list) == 0: print("ERROR: No data found") return False old_npes = f.read('NPES') old_nxpe = f.read('NXPE') old_nype = int(old_npes/old_nxpe) if nfiles != old_npes: print("WARNING: Number of restart files inconsistent with NPES") print("Setting nfiles = " + str(old_npes)) nfiles = old_npes if nfiles == 0: print("ERROR: No restart files found") return False informat = file_list[0].split(".")[-1] if outformat == None: outformat = informat old_mxsub = 0 old_mysub = 0 mz = 0 for v in var_list: if f.ndims(v) == 3: s = f.size(v) old_mxsub = s[0] - 2*mxg if old_mxsub < 0: if s[0] == 1: old_mxsub = 1 mxg = 0 elif s[0] == 3: old_mxsub = 1 mxg = 1 else: print("Number of x points is wrong?") return False old_mysub = s[1] - 2*myg if old_mysub < 0: if s[1] == 1: old_mysub = 1 myg = 0 elif s[1] == 3: old_mysub = 1 myg = 1 else: print("Number of y points is wrong?") return False mz = s[2] break # Calculate total size of the grid nx = old_mxsub * old_nxpe ny = old_mysub * old_nype print("Grid sizes: ", nx, ny, mz) if nxpe == None: # Copy algorithm from BoutMesh for selecting nxpe ideal = sqrt(float(nx) * float(npes) / float(ny)) # Results in square domain for i in range(1,npes+1): if npes%i == 0 and nx%i == 0 and int(nx/i) >= mxg and ny%(npes/i) == 0: # Found an acceptable value # Warning: does not check branch cuts! if nxpe==None or abs(ideal - i) < abs(ideal - nxpe): nxpe = i # Keep value nearest to the ideal if nxpe == None: print("ERROR: could not find a valid value for nxpe") return False nype = int(npes/nxpe) outfile_list = [] for i in range(npes): outpath = os.path.join(output, "BOUT.restart."+str(i)+"."+outformat) outfile_list.append(DataFile(outpath, write=True, create=True)) infile_list = [] for i in range(old_npes): inpath = os.path.join(path, "BOUT.restart."+str(i)+"."+outformat) infile_list.append(DataFile(inpath)) old_mxsub = int(nx/old_nxpe) old_mysub = int(ny/old_nype) mxsub = int(nx/nxpe) mysub = int(ny/nype) for v in var_list: ndims = f.ndims(v) #collect data if ndims == 0: #scalar data = f.read(v) elif ndims == 2: data = numpy.zeros( (nx+2*mxg,ny+2*nyg) ) for i in range(old_npes): ix = i%old_nxpe iy = int(i/old_nxpe) ixstart = mxg if ix == 0: ixstart = 0 ixend = -mxg if ix == old_nxpe-1: ixend = 0 iystart = myg if iy == 0: iystart = 0 iyend = -myg if iy == old_nype-1: iyend = 0 data[ix*old_mxsub+ixstart:(ix+1)*old_mxsub+2*mxg+ixend, iy*old_mysub+iystart:(iy+1)*old_mysub+2*myg+iyend] = infile_list[i].read(v)[ixstart:old_mxsub+2*mxg+ixend, iystart:old_mysub+2*myg+iyend] elif ndims == 3: data = numpy.zeros( (nx+2*mxg,ny+2*myg,mz) ) for i in range(old_npes): ix = i%old_nxpe iy = int(i/old_nxpe) ixstart = mxg if ix == 0: ixstart = 0 ixend = -mxg if ix == old_nxpe-1: ixend = 0 iystart = myg if iy == 0: iystart = 0 iyend = -myg if iy == old_nype-1: iyend = 0 data[ix*old_mxsub+ixstart:(ix+1)*old_mxsub+2*mxg+ixend, iy*old_mysub+iystart:(iy+1)*old_mysub+2*myg+iyend, :] = infile_list[i].read(v)[ixstart:old_mxsub+2*mxg+ixend, iystart:old_mysub+2*myg+iyend, :] else: print("ERROR: variable found with unexpected number of dimensions,",ndims,v) return False # write data for i in range(npes): ix = i%nxpe iy = int(i/nxpe) outfile = outfile_list[i] if v == "NPES": outfile.write(v,npes) elif v == "NXPE": outfile.write(v,nxpe) elif ndims == 0: # scalar outfile.write(v,data) elif ndims == 2: # Field2D outfile.write(v,data[ix*mxsub:(ix+1)*mxsub+2*mxg, iy*mysub:(iy+1)*mysub+2*myg]) elif ndims == 3: # Field3D outfile.write(v,data[ix*mxsub:(ix+1)*mxsub+2*mxg, iy*mysub:(iy+1)*mysub+2*myg, :]) else: print("ERROR: variable found with unexpected number of dimensions,",f.ndims(v)) f.close() for infile in infile_list: infile.close() for outfile in outfile_list: outfile.close() return True
else: print ("Please respond with 'yes' or 'no' "\ "(or 'y' or 'n').\n") # Loop means more than one variable can be loaded # Perhaps better implemented as a function c = 0 while c == 0: # An iterator to loop over all of the variables in the file it = f.handle.variables.__iter__() print 'Variable Number' + '\t' + 'Name' + '\t' + '\t' + 'Dims.' # For all variables in the file, print the identifier in # the dictionary, its name and the number of dimensions for i in range(np.shape(f.handle.variables.keys())[0]): print str(i) + '\t' + f.handle.variables.keys()[i] + '\t' + '\t' + str(np.shape(f.size(it.next()))[0]) print '\n' + 'Variable number to load?' v = raw_input() print 'Importing variable : ' + f.handle.variables.keys()[v] # If the variable has 4D data, recommend slicing it up if np.shape(f.size(f.handle.variables.keys()[v]))[0] == 4: print 'Time slices (-1 for all), Range=', t # Takes the Max and Min slice range from the user, places into list # that's passed to the collect routine later tind = [int(raw_input('min: ')), int(raw_input('max: '))] # If the minimum or maximum lie outside the range, just choose everything # This is probably not ideal if np.min(tind) < 0 or np.max(tind) > t[1]: