def writenek(fname, data): """A function for writing binary data in the nek5000 binary format Parameters ---------- fname : str file name data : exadata data organised as readnek() output """ # try: outfile = open(fname, 'wb') except OSError as e: logger.critical(f'I/O error ({e.errno}): {e.strerror}') return -1 # #--------------------------------------------------------------------------- # WRITE HEADER #--------------------------------------------------------------------------- # # multiple files (not implemented) fid = 0 nf = 1 nelf = data.nel # # get fields to be written vars = '' if (data.var[0] > 0): vars += 'X' if (data.var[1] > 0): vars += 'U' if (data.var[2] > 0): vars += 'P' if (data.var[3] > 0): vars += 'T' if (data.var[4] > 0): # TODO: check if header for scalars are written with zeros filled as S01 vars += 'S{:02d}'.format(data.var[4]) # # get word size if (data.wdsz == 4): logger.debug('Writing single-precision file') elif (data.wdsz == 8): logger.debug('Writing double-precision file') else: logger.error('Could not interpret real type (wdsz = %i)' % (data.wdsz)) return -2 # # generate header header = '#std %1i %2i %2i %2i %10i %10i %20.13E %9i %6i %6i %s' % ( data.wdsz, data.lr1[0], data.lr1[1], data.lr1[2], data.nel, nelf, data.time, data.istep, fid, nf, vars) # # write header header = header.ljust(132) outfile.write(header.encode('utf-8')) # # decide endianness if data.endian in ('big', 'little'): byteswap = data.endian != sys.byteorder logger.debug(f'Writing {data.endian}-endian file') else: byteswap = False logger.warning(f'Unrecognized endianness {data.endian}, ' f'writing native {sys.byteorder}-endian file') # def correct_endianness(a): ''' Return the array with the requested endianness''' if byteswap: return a.byteswap() else: return a # # write tag (to specify endianness) endianbytes = np.array([6.54321], dtype=np.float32) correct_endianness(endianbytes).tofile(outfile) # # write element map for the file correct_endianness(data.elmap).tofile(outfile) # #--------------------------------------------------------------------------- # WRITE DATA #--------------------------------------------------------------------------- # # compute total number of points per element npel = data.lr1[0] * data.lr1[1] * data.lr1[2] # def write_ndarray_to_file(a): '''Write a data array to the output file in the requested precision and endianness''' if data.wdsz == 4: correct_endianness(a.astype(np.float32)).tofile(outfile) else: correct_endianness(a).tofile(outfile) # # write geometry for iel in data.elmap: for idim in range( data.var[0]): # if var[0] == 0, geometry is not written write_ndarray_to_file(data.elem[iel - 1].pos[idim, :, :, :]) # # write velocity for iel in data.elmap: for idim in range( data.var[1]): # if var[1] == 0, velocity is not written write_ndarray_to_file(data.elem[iel - 1].vel[idim, :, :, :]) # # write pressure for iel in data.elmap: for ivar in range( data.var[2]): # if var[2] == 0, pressure is not written write_ndarray_to_file(data.elem[iel - 1].pres[ivar, :, :, :]) # # write temperature for iel in data.elmap: for ivar in range( data.var[3]): # if var[3] == 0, temperature is not written write_ndarray_to_file(data.elem[iel - 1].temp[ivar, :, :, :]) # # write scalars # # NOTE: This is not a bug! # Unlike other variables, scalars are in the outer loop and elements # are in the inner loop # for ivar in range(data.var[4]): # if var[4] == 0, scalars are not written for iel in data.elmap: write_ndarray_to_file(data.elem[iel - 1].scal[ivar, :, :, :]) # # write max and min of every field in every element (forced to single precision) if (data.ndim == 3): # for iel in data.elmap: for idim in range(data.var[0]): correct_endianness( np.min(data.elem[iel - 1].pos[idim, :, :, :]).astype( np.float32)).tofile(outfile) correct_endianness( np.max(data.elem[iel - 1].pos[idim, :, :, :]).astype( np.float32)).tofile(outfile) for iel in data.elmap: for idim in range(data.var[1]): correct_endianness( np.min(data.elem[iel - 1].vel[idim, :, :, :]).astype( np.float32)).tofile(outfile) correct_endianness( np.max(data.elem[iel - 1].vel[idim, :, :, :]).astype( np.float32)).tofile(outfile) for iel in data.elmap: for ivar in range(data.var[2]): correct_endianness( np.min(data.elem[iel - 1].pres[ivar, :, :, :]).astype( np.float32)).tofile(outfile) correct_endianness( np.max(data.elem[iel - 1].pres[ivar, :, :, :]).astype( np.float32)).tofile(outfile) for iel in data.elmap: for ivar in range(data.var[3]): correct_endianness( np.min(data.elem[iel - 1].temp[ivar, :, :, :]).astype( np.float32)).tofile(outfile) correct_endianness( np.max(data.elem[iel - 1].temp[ivar, :, :, :]).astype( np.float32)).tofile(outfile) for iel in data.elmap: for ivar in range(data.var[4]): correct_endianness( np.min(data.elem[iel - 1].scal[ivar, :, :, :]).astype( np.float32)).tofile(outfile) correct_endianness( np.max(data.elem[iel - 1].scal[ivar, :, :, :]).astype( np.float32)).tofile(outfile) # close file outfile.close() # # output return 0
def writenek(fname, data): """A function for writing binary data in the nek5000 binary format Parameters ---------- fname : str file name data : exadata data organised as readnek() output """ # try: outfile = open(fname, 'wb') except IOError as e: logger.critical('I/O error ({0}): {1}'.format(e.errno, e.strerror)) return -1 # #--------------------------------------------------------------------------- # WRITE HEADER #--------------------------------------------------------------------------- # # multiple files (not implemented) fid = 0 nf = 1 nelf = data.nel # # get fields to be written vars = '' if (data.var[0] > 0): vars += 'X' if (data.var[1] > 0): vars += 'U' if (data.var[2] > 0): vars += 'P' if (data.var[3] > 0): vars += 'T' if (data.var[4] > 0): logger.warning("Writing scalar variables is an experimental feature") # TODO: check if header for scalars are written with zeros filled as S01 vars += 'S{:02d}'.format(data.var[4]) # # get word size if (data.wdsz == 4): realtype = 'f' elif (data.wdsz == 8): realtype = 'd' else: logger.error('Could not interpret real type (wdsz = %i)' % (data.wdsz)) return -2 # # generate header header = '#std %1i %2i %2i %2i %10i %10i %20.13E %9i %6i %6i %s\n' %( data.wdsz, data.lr1[0], data.lr1[1], data.lr1[2], data.nel, nelf, data.time, data.istep, fid, nf, vars) # # write header header = header.ljust(132) outfile.write(header.encode('utf-8')) # # write tag (to specify endianness) endianbytes = np.array([6.54321], dtype=np.float32) endianbytes.tofile(outfile) # # generate and write element map for the file elmap = np.linspace(1, data.nel, data.nel, dtype=np.int32) elmap.tofile(outfile) # #--------------------------------------------------------------------------- # WRITE DATA #--------------------------------------------------------------------------- # # compute total number of points per element npel = data.lr1[0] * data.lr1[1] * data.lr1[2] # def write_ndarray_to_file(a): if data.wdsz == 4: np.float32(a).tofile(outfile) else: a.tofile(outfile) # # write geometry for iel in elmap: for idim in range(data.var[0]): # if var[0] == 0, geometry is not written write_ndarray_to_file(data.elem[iel-1].pos[idim, :, :, :]) # # write velocity for iel in elmap: for idim in range(data.var[1]): # if var[1] == 0, velocity is not written write_ndarray_to_file(data.elem[iel-1].vel[idim, :, :, :]) # # write pressure for iel in elmap: for ivar in range(data.var[2]): # if var[2] == 0, pressure is not written write_ndarray_to_file(data.elem[iel-1].pres[ivar, :, :, :]) # # write temperature for iel in elmap: for ivar in range(data.var[3]): # if var[3] == 0, temperature is not written write_ndarray_to_file(data.elem[iel-1].temp[ivar, :, :, :]) # # write scalars # # NOTE: This is not a bug! # Unlike other variables, scalars are in the outer loop and elements # are in the inner loop # for ivar in range(data.var[4]): # if var[4] == 0, scalars are not written for iel in elmap: write_ndarray_to_file(data.elem[iel-1].scal[ivar, :, :, :]) # # write max and min of every field in every element (forced to single precision) if (data.ndim==3): # for iel in elmap: for idim in range(data.var[0]): np.min(data.elem[iel-1].pos[idim, :,:,:]).tofile(outfile) np.max(data.elem[iel-1].pos[idim, :,:,:]).tofile(outfile) for idim in range(data.var[1]): np.min(data.elem[iel-1].vel[idim, :,:,:]).tofile(outfile) np.max(data.elem[iel-1].vel[idim, :,:,:]).tofile(outfile) for idim in range(data.var[2]): np.min(data.elem[iel-1].pres[idim, :,:,:]).tofile(outfile) np.max(data.elem[iel-1].pres[idim, :,:,:]).tofile(outfile) for idim in range(data.var[3]): np.min(data.elem[iel-1].temp[idim, :,:,:]).tofile(outfile) np.max(data.elem[iel-1].temp[idim, :,:,:]).tofile(outfile) # close file outfile.close() # # output return 0
def writere2(fname, data): """A function for writing binary .re2 files for nek5000 Parameters ---------- fname : str file name data : exadata data organised as in exadata.py """ # #--------------------------------------------------------------------------- # CHECK INPUT DATA #--------------------------------------------------------------------------- # # We could extract the corners, but for now just return an error if lr1 is too large if data.lr1 != [2, 2, data.ndim - 1]: logger.critical( 'wrong element dimensions for re2 file! {} != {}'.format( data.lr1, [2, 2, data.ndim - 1])) return -2 # if data.var[0] != data.ndim: logger.critical( 'wrong number of geometric variables for re2 file! expected {}, found {}' .format(data.ndim, data.var[0])) return -3 # # Open file try: outfile = open(fname, 'wb') except OSError as e: logger.critical(f'I/O error ({e.errno}): {e.strerror}') return -1 # #--------------------------------------------------------------------------- # WRITE HEADER #--------------------------------------------------------------------------- # # always double precision wdsz = 8 realtype = 'd' nel = data.nel ndim = data.ndim header = f'#v002{nel:9d}{ndim:3d}{nel:9d} this is the hdr' header = header.ljust(80) outfile.write(header.encode('utf-8')) # # decide endianness if data.endian in ('big', 'little'): byteswap = data.endian != sys.byteorder logger.debug(f'Writing {data.endian}-endian file') else: byteswap = False logger.warning(f'Unrecognized endianness {data.endian}, ' f'writing native {sys.byteorder}-endian file') # def correct_endianness(a): ''' Return the array with the requested endianness''' if byteswap: return a.byteswap() else: return a # # write tag (to specify endianness) endianbytes = np.array([6.54321], dtype=np.float32) correct_endianness(endianbytes).tofile(outfile) # #--------------------------------------------------------------------------- # WRITE DATA #--------------------------------------------------------------------------- # # compute total number of points per element npel = 2**ndim # def write_data_to_file(a): '''Write the geometry of an element to the output file in double precision''' correct_endianness(a).tofile(outfile) # # write geometry (adding eight bytes of zeros before each element) xyz = np.zeros( (npel * ndim + 1, ) ) # array storing reordered geometry data (with a zero in the first position) for el in data.elem: # the data is stored in the following order (2D|3D): # x1, x2, x4, x3; | x5, x6, x8, x7; # y1, y2, y4, y3; | y5, y6, y8, y7; # ---------------- # z1, z2, z4, z3; z5, z6, z8, z7; # where 1-8 is the ordering of the points in memory for idim in range(ndim): # x, y, [z] for iz in range( ndim - 1 ): # this does only one iteration in 2D, and in 3D does one iteration for 1-4 and one for 5-8 xyz[npel * idim + 4 * iz + 1] = el.pos[idim, iz, 0, 0] xyz[npel * idim + 4 * iz + 2] = el.pos[idim, iz, 0, 1] xyz[npel * idim + 4 * iz + 3] = el.pos[idim, iz, 1, 1] xyz[npel * idim + 4 * iz + 4] = el.pos[idim, iz, 1, 0] write_data_to_file(xyz) # # write curve sides data # locate curved edges curved_edges = [] for (iel, el) in enumerate(data.elem): for iedge in range(12): if el.ccurv[iedge] != '': curved_edges.append((iel, iedge)) # write number of curved edges ncurv = len(curved_edges) if ncurv != data.ncurv: logger.warning( f'wrong number of curved edges: expected {data.ncurv}, found {ncurv}' ) ncurvf = np.array([ncurv], dtype=np.float64) write_data_to_file(ncurvf) # format curve data cdata = np.zeros((ncurv, ), dtype='f8, f8, f8, f8, f8, f8, f8, S8') for (cdat, (iel, iedge)) in zip(cdata, curved_edges): el = data.elem[iel] cdat[0] = iel + 1 cdat[1] = iedge + 1 # curve parameters for j in range(5): cdat[2 + j] = el.curv[iedge, j] # encode the string as a byte array padded with spaces cdat[7] = el.ccurv[iedge].encode('utf-8') # write to file write_data_to_file(cdata) # # write boundary conditions for each field for ifield in range(data.nbc): # locate faces with boundary conditions bc_faces = [] for (iel, el) in enumerate(data.elem): for iface in range(2 * ndim): bctype = el.bcs[ifield, iface][0] # internal boundary conditions are not written to .re2 files by reatore2 # and are apparently ignored by Nek5000 even in .rea files if bctype != '' and bctype != 'E': bc_faces.append((iel, iface)) nbcs = len(bc_faces) nbcsf = np.array([nbcs], dtype=np.float64) write_data_to_file(nbcsf) # initialize and format data bcdata = np.zeros((nbcs, ), dtype='f8, f8, f8, f8, f8, f8, f8, S8') for (bc, (iel, iface)) in zip(bcdata, bc_faces): el = data.elem[iel] bc[0] = iel + 1 bc[1] = iface + 1 for j in range(5): bc[2 + j] = el.bcs[ifield, iface][3 + j] # encode the string as a byte array padded with spaces bc[7] = el.bcs[ifield, iface][0].encode('utf-8').ljust(8) # write to file write_data_to_file(bcdata) # # close file outfile.close() # rerurn return 0
def readnek(fname): """A function for reading binary data from the nek5000 binary format Parameters ---------- fname : str file name """ # try: infile = open(fname, 'rb') except IOError as e: logger.critical('I/O error ({0}): {1}'.format(e.errno, e.strerror)) return -1 # #--------------------------------------------------------------------------- # READ HEADER #--------------------------------------------------------------------------- # # read header header = infile.read(132).split() logger.debug('Header: {}'.format(b' '.join(header).decode('utf-8'))) # get word size: single or double precision wdsz = int(header[1]) if (wdsz == 4): realtype = 'f' elif (wdsz == 8): realtype = 'd' else: logger.error('Could not interpret real type (wdsz = %i)' % (wdsz)) return -2 # # get polynomial order lr1 = [ int(header[2]), int(header[3]), int(header[4]) ] # # compute total number of points per element npel = lr1[0] * lr1[1] * lr1[2] # # get number of physical dimensions ndim = 2 + (lr1[2]>1) # # get number of elements nel = int(header[5]) # # get number of elements in the file nelf = int(header[6]) # # get current time time = float(header[7]) # # get current time step istep = int(header[8]) # # get file id fid = int(header[9]) # # get tot number of files nf = int(header[10]) # # get variables [XUPTS[01-99]] variables = header[11].decode('utf-8') logger.debug("Variables: {}".format(variables)) var = [0 for i in range(5)] for v in variables: if (v == 'X'): var[0] = ndim elif (v == 'U'): var[1] = ndim elif (v == 'P'): var[2] = 1 elif (v == 'T'): var[3] = 1 elif (v == 'S'): logger.warning("Reading scalar variables is an experimental feature") # For example: variables = 'XS44' index_s = variables.index('S') nb_scalars = int(variables[index_s+1:]) var[4] = nb_scalars # # identify endian encoding etagb = infile.read(4) etagL = struct.unpack('<f', etagb)[0]; etagL = int(etagL*1e5)/1e5 etagB = struct.unpack('>f', etagb)[0]; etagB = int(etagB*1e5)/1e5 if (etagL == 6.54321): logger.debug('Reading little-endian file\n') emode = '<' elif (etagB == 6.54321): logger.debug('Reading big-endian file\n') emode = '>' else: logger.error('Could not interpret endianness') return -3 # # read element map for the file elmap = infile.read(4*nelf) elmap = list(struct.unpack(emode+nelf*'i', elmap)) # #--------------------------------------------------------------------------- # READ DATA #--------------------------------------------------------------------------- # # initialize data structure data = exdat.exadata(ndim, nel, lr1, var, 0) data.time = time data.istep = istep data.wdsz = wdsz if (emode == '<'): data.endian = 'little' elif (emode == '>'): data.endian = 'big' # def read_file_into_data(elem, index_var, name_var): """Read binary file into an array attribute of ``data.elem``""" fi = infile.read(npel*wdsz) fi = np.frombuffer(fi, dtype=emode+realtype, count=npel) # Fetch elem array, for example `data.elem.pos` data_var = getattr(elem, name_var) # Replace elem array in-place with # array read from file after reshaping as elem_shape = lr1[::-1] # lz, ly, lx data_var[index_var, ...] = fi.reshape(elem_shape) # # read geometry for iel in elmap: el = data.elem[iel-1] for idim in range(var[0]): # if var[0] == 0, geometry is not read read_file_into_data(el, idim, 'pos') # # read velocity for iel in elmap: el = data.elem[iel-1] for idim in range(var[1]): # if var[1] == 0, velocity is not read read_file_into_data(el, idim, 'vel') # # read pressure for iel in elmap: el = data.elem[iel-1] for ivar in range(var[2]): # if var[2] == 0, pressure is not read read_file_into_data(el, ivar, 'pres') # # read temperature for iel in elmap: el = data.elem[iel-1] for ivar in range(var[3]): # if var[3] == 0, temperature is not read read_file_into_data(el, ivar, 'temp') # # read scalar fields # # NOTE: This is not a bug! # Unlike other variables, scalars are in the outer loop and elements # are in the inner loop # for ivar in range(var[4]): # if var[4] == 0, scalars are not read for iel in elmap: el = data.elem[iel-1] read_file_into_data(el, ivar, 'scal') # # # close file infile.close() # # output return data