Exemple #1
0
    def face_center(self, i):
        """Return the coordinates (x, y, z) of the center of the face number i"""

        if i == 0:
            kx1, ky1, kz1 = 0, 0, 0
            kx2, ky2, kz2 = 0, 0, -1
            kx3, ky3, kz3 = -1, 0, 0
            kx4, ky4, kz4 = -1, 0, -1
        elif i == 1:
            kx1, ky1, kz1 = -1, 0, 0
            kx2, ky2, kz2 = -1, 0, -1
            kx3, ky3, kz3 = -1, -1, 0
            kx4, ky4, kz4 = -1, -1, -1
        elif i == 2:
            kx1, ky1, kz1 = 0, -1, 0
            kx2, ky2, kz2 = 0, -1, -1
            kx3, ky3, kz3 = -1, -1, 0
            kx4, ky4, kz4 = -1, -1, -1
        elif i == 3:
            kx1, ky1, kz1 = 0, 0, 0
            kx2, ky2, kz2 = 0, 0, -1
            kx3, ky3, kz3 = 0, -1, 0
            kx4, ky4, kz4 = 0, -1, -1
        elif i == 4:
            kx1, ky1, kz1 = 0, 0, 0
            kx2, ky2, kz2 = 0, -1, 0
            kx3, ky3, kz3 = -1, 0, 0
            kx4, ky4, kz4 = -1, -1, 0
        elif i == 5:
            kx1, ky1, kz1 = 0, 0, -1
            kx2, ky2, kz2 = 0, -1, -1
            kx3, ky3, kz3 = -1, 0, -1
            kx4, ky4, kz4 = -1, -1, -1
        else:
            logger.error(f"Invalid face number {i} (must be between 0 and 5)")
        (x1, y1, z1) = self.pos[:, kz1, ky1, kx1]
        (x2, y2, z2) = self.pos[:, kz2, ky2, kx2]
        (x3, y3, z3) = self.pos[:, kz3, ky3, kx3]
        (x4, y4, z4) = self.pos[:, kz4, ky4, kx4]
        return (
            0.25 * (x1 + x2 + x3 + x4),
            0.25 * (y1 + y2 + y3 + y4),
            0.25 * (z1 + z2 + z3 + z4),
        )
Exemple #2
0
    def check_connectivity(self):
        """Check element connectivity, specifically for matching boundary
        conditions and geometry. Errors are reported as logging messages.

        """
        dim = self.ndim
        err = False
        for (iel, el), ibc, iface in product(enumerate(self.elem),
                                             range(self.nbc), range(2 * dim)):
            cbc = el.bcs[ibc, iface][0]
            if cbc == "E" or cbc == "P":
                connected_iel = int(el.bcs[ibc, iface][3]) - 1
                connected_face = int(el.bcs[ibc, iface][4]) - 1
                cbc1 = self.elem[connected_iel].bcs[ibc, connected_face][0]
                iel1 = int(self.elem[connected_iel].bcs[ibc,
                                                        connected_face][3]) - 1
                iface1 = int(
                    self.elem[connected_iel].bcs[ibc, connected_face][4]) - 1
                if iel1 < 0 or iel1 >= self.nel:
                    err = True
                    logger.error(
                        f"face {iface} of element {iel} is connected to face "
                        f"{connected_face} of the nonexistent element {connected_iel}"
                    )
                else:
                    if cbc1 != cbc:
                        err = True
                        logger.error(
                            "mismatched boundary conditions: "
                            f"face {iface + 1} of element {iel + 1} with "
                            f"condition {cbc} is connected to face {connected_face + 1} "
                            f"of element {connected_iel + 1} with condition {cbc1}"
                        )
                    if iel1 != iel:
                        err = True
                        logger.error(
                            "mismatched elements: "
                            f"face {iface + 1} of element {iel + 1} "
                            f"is connected to face {connected_face + 1} "
                            f"of element {connected_iel + 1} but that face is "
                            f"connected to face {iface + 1} of element {iel + 1}"
                        )
                    if iface1 != iface:
                        err = True
                        logger.error(
                            "mismatched faces: "
                            f"face {iface + 1} of element {iel + 1} "
                            f"is connected to face {connected_face + 1} "
                            f"of element {connected_iel + 1} but that face is "
                            f"connected to face {iface + 1} of element {iel + 1}"
                        )
        return not err
Exemple #3
0
    def check_connectivity(self):
        """Check element connectivity, specifically for matching boundary
		conditions and geometry. Errors are reported as logging messages.

		"""
        dim = self.ndim
        err = False
        for (iel, el) in enumerate(self.elem):
            for ibc in range(self.nbc):
                for iface in range(2 * dim):
                    cbc = el.bcs[ibc, iface][0]
                    if cbc == 'E' or cbc == 'P':
                        connected_iel = int(el.bcs[ibc, iface][3]) - 1
                        connected_face = int(el.bcs[ibc, iface][4]) - 1
                        cbc1 = self.elem[connected_iel].bcs[ibc,
                                                            connected_face][0]
                        iel1 = int(self.elem[connected_iel].bcs[
                            ibc, connected_face][3]) - 1
                        iface1 = int(self.elem[connected_iel].bcs[
                            ibc, connected_face][4]) - 1
                        if iel1 < 0 or iel1 >= self.nel:
                            err = True
                            logger.error(
                                "face {:} of element {:} is connected to face {:} of the nonexistent element {:}"
                                .format(iface, iel, connected_face,
                                        connected_iel))
                        else:
                            if cbc1 != cbc:
                                err = True
                                logger.error(
                                    "mismatched boundary conditions: face {:} of element {:} with condition {:} is connected to face {:} of element {:} with condition {:}"
                                    .format(iface + 1, iel + 1, cbc,
                                            connected_face + 1,
                                            connected_iel + 1, cbc1))
                            if iel1 != iel:
                                err = True
                                logger.error(
                                    "mismatched elements: face {:} of element {:} is connected to face {:} of element {:} but that face is connected to face {:} of element {:}"
                                    .format(iface + 1, iel + 1,
                                            connected_face + 1,
                                            connected_iel + 1, iface1 + 1,
                                            iel1 + 1))
                            if iface1 != iface:
                                err = True
                                logger.error(
                                    "mismatched faces: face {:} of element {:} is connected to face {:} of element {:} but that face is connected to face {:} of element {:}"
                                    .format(iface + 1, iel + 1,
                                            connected_face + 1,
                                            connected_iel + 1, iface1 + 1,
                                            iel1 + 1))
        return not err
Exemple #4
0
def readre2(fname):
    """A function for reading .re2 files for nek5000

	Parameters
	----------
	fname : str
		file name
	"""
    #
    try:
        infile = open(fname, 'rb')
    except OSError as e:
        logger.critical(f'I/O error ({e.errno}): {e.strerror}')
        return -1
    # the header for re2 files is 80 ASCII bytes, something like
    # #v002    18669  2    18669 this is the hdr                                      %
    header = infile.read(80).split()
    nel = int(header[1])
    ndim = int(header[2])
    # always double precision
    wdsz = 8
    realtype = 'd'

    # detect endianness
    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 = '<'
        endian = 'little'
    elif (etagB == 6.54321):
        logger.debug('Reading big-endian file\n')
        emode = '>'
        endian = 'big'
    else:
        logger.error('Could not interpret endianness')
        return -3
    #
    # there are no GLL points here, only quad/hex vertices
    lr1 = [2, 2, ndim - 1]
    npel = 2**ndim
    # the file only contains geometry
    var = [ndim, 0, 0, 0, 0]
    # allocate structure
    data = exdat.exadata(ndim, nel, lr1, var, 1)
    #
    # some metadata
    data.wdsz = wdsz
    data.endian = endian
    #
    # read the whole geometry into a buffer
    # for some reason each element is prefixed with 8 bytes of zeros, it's not clear to me why.
    # This is the reason for the +1 here, then the first number is ignored.
    buf = infile.read((ndim * npel + 1) * wdsz * nel)
    elem_shape = [ndim, ndim - 1, 2, 2]  # nvar, lz, ly, lx
    for (iel, el) in enumerate(data.elem):
        fi = np.frombuffer(buf,
                           dtype=emode + realtype,
                           count=ndim * npel + 1,
                           offset=(ndim * npel + 1) * wdsz * iel)
        # 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
                el.pos[idim, iz, 0, 0] = fi[npel * idim + 4 * iz + 1]
                el.pos[idim, iz, 0, 1] = fi[npel * idim + 4 * iz + 2]
                el.pos[idim, iz, 1, 1] = fi[npel * idim + 4 * iz + 3]
                el.pos[idim, iz, 1, 0] = fi[npel * idim + 4 * iz + 4]
    #
    # read curved sides
    # the number of curved sides is stored as a double,
    # then each curved side is 64 bytes:
    # iel iface p1 p2 p3 p4 p5 ctype
    # where p1-5 are f64 parameters and ctype is the type of curvature in ASCII
    ncparam = 8
    buf = infile.read(wdsz)
    ncurv = int(np.frombuffer(buf)[0])
    logger.debug(f'Found {ncurv} curved sides')
    data.ncurv = ncurv
    buf = infile.read(wdsz * (ncparam * ncurv))
    for icurv in range(ncurv):
        # interpret the data
        curv = np.frombuffer(buf,
                             dtype=emode + realtype,
                             count=ncparam,
                             offset=icurv * ncparam * wdsz)
        iel = int(curv[0]) - 1
        iedge = int(curv[1]) - 1
        cparams = curv[2:7]
        # select only the first byte, because it turns out the later bytes may contain garbage.
        # typically, it's b'C\x00\x00\x00\x00\x00\x00\x00' or b'C\x00\x00\x00\x00\x00\xe0?'.
        # AFAIK the curvature types are always one character long anyway.
        ctype = curv[7].tobytes()[:1].decode('utf-8')
        # fill in the data structure
        data.elem[iel].curv[iedge, :] = cparams
        data.elem[iel].ccurv[iedge] = ctype
    #
    # read boundary conditions
    # there can be more than one field, and we won't know until we'vre reached the end
    nbcparam = 8
    buf = infile.read(wdsz)
    ifield = 0
    while buf != b'':
        # the data is initialized with one BC field, we might need to allocate another
        if ifield > 0:
            data.nbc = data.nbc + 1
            for el in data.elem:
                empty_bcs = np.zeros(el.bcs[:1, :].shape, dtype=el.bcs.dtype)
                el.bcs = np.concatenate((el.bcs, empty_bcs))
        nbclines = int(np.frombuffer(buf)[0])
        logger.debug(
            f'Found {nbclines} external boundary conditions for field {ifield}'
        )
        buf = infile.read(wdsz * (nbcparam * nbclines))
        for ibc in range(nbclines):
            # interpret the data
            bc = np.frombuffer(buf,
                               dtype=emode + realtype,
                               count=nbcparam,
                               offset=ibc * nbcparam * wdsz)
            iel = int(bc[0]) - 1
            iface = int(bc[1]) - 1
            bcparams = bc[2:7]
            bctype = bc[7].tobytes().decode(
                'utf-8').rstrip()  # remove trailing spaces
            # fill in the data structure
            data.elem[iel].bcs[ifield, iface][0] = bctype
            data.elem[iel].bcs[ifield, iface][1] = iel + 1
            data.elem[iel].bcs[ifield, iface][2] = iface + 1
            for ipar in range(5):
                data.elem[iel].bcs[ifield, iface][3 + ipar] = bcparams[ipar]
        ifield = ifield + 1
        # try reading the number of conditions in the next field
        buf = infile.read(wdsz)
    infile.close()
    return data
Exemple #5
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 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
Exemple #6
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 OSError as e:
        logger.critical(f'I/O error ({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(f"Variables: {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'):
            # 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
    data.elmap = np.array(elmap, dtype=np.int32)
    if (emode == '<'):
        data.endian = 'little'
    elif (emode == '>'):
        data.endian = 'big'
    #
    def read_file_into_data(data_var, index_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)

        # 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.pos, idim)
    #
    # 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.vel, idim)
    #
    # 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.pres, ivar)
    #
    # 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.temp, ivar)
    #
    # 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.scal, ivar)
    #
    #
    # close file
    infile.close()
    #
    # output
    return data
Exemple #7
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
Exemple #8
0
    def merge(self, other, tol=1e-9, ignore_empty=True):
        """
        Merges another exadata into the current one and connects it

        Parameters
        ----------
        other: exadata
                mesh to merge into self

        tol: float
                maximum distance at which points are considered identical

        ignore_empty: bool
                if True, the faces with an empty boundary condition ('') will be treated as internal faces and will not be merged.
                This is useful if internal boundary conditions are not defined and will make the operation *much* faster,
                but requires boundary conditions to be defined on the faces to be merged.

        """

        # perform some consistency checks
        if self.ndim != other.ndim:
            logger.error(
                f"Cannot merge meshes of dimensions {self.ndim} and {other.ndim}!"
            )
            return -1
        if self.lr1[0] != other.lr1[0]:
            logger.error(
                "Cannot merge meshes of different polynomial orders ({} != {})"
                .format(self.lr1[0], other.lr1[0]))
            return -2

        # add the new elements (in an inconsistent state if there are internal boundary conditions)
        nel1 = self.nel
        self.nel = self.nel + other.nel
        self.ncurv = self.ncurv + other.ncurv
        self.elmap = np.concatenate((self.elmap, other.elmap + nel1))
        # the deep copy is required here to avoid leaving the 'other' mesh in an inconsistent state by modifying its elements
        self.elem = self.elem + copy.deepcopy(other.elem)

        # check how many boundary condition fields we have
        nbc = min(self.nbc, other.nbc)

        # correct the boundary condition numbers:
        # the index of the elements and neighbours have changed
        for iel, ibc, iface in product(range(nel1, self.nel), range(other.nbc),
                                       range(6)):
            self.elem[iel].bcs[ibc, iface][1] = iel + 1
            bc = self.elem[iel].bcs[ibc, iface][0]
            if bc == "E" or bc == "P":
                neighbour = self.elem[iel].bcs[ibc, iface][3]
                self.elem[iel].bcs[ibc, iface][3] = neighbour + nel1

        # glue common faces together
        # only look for the neighbour in the first BC field because it should be the same in all fields.
        # FIXME: this will fail to correct periodic conditions if periodic domains are merged together.
        nfaces = 2 * self.ndim
        nchanges = 0  # counter for the boundary conditions connected
        if nbc == 0:
            # Quickly exit the function
            logger.debug("no pairs of faces to merge")
            return nchanges

        for iel0, iface0 in product(range(nel1, self.nel), range(nfaces)):
            elem0 = self.elem[iel0]
            bc0 = elem0.bcs[0, iface0][0]

            if bc0 != "E" and not (ignore_empty and bc0 == ""):
                # boundary element, look if it can be connected to something
                for iel1, iface1 in product(range(nel1), range(nfaces)):
                    elem1 = self.elem[iel1]
                    bc1 = elem1.bcs[0, iface1][0]

                    if bc1 != "E" and not (ignore_empty and bc1 == ""):
                        # if the centers of the faces are close, connect them together
                        x0, y0, z0 = elem0.face_center(iface0)
                        x1, y1, z1 = elem1.face_center(iface1)
                        dist2 = (x1 - x0)**2 + (y1 - y0)**2 + (z1 - z0)**2
                        if dist2 <= tol**2:
                            # reconnect the periodic faces together (assumes that all fields are periodic)
                            if bc0 == bc1 == "P":

                                iel_p0 = int(elem0.bcs[0, iface0][3]) - 1
                                iel_p1 = int(elem1.bcs[0, iface1][3]) - 1
                                iface_p0 = int(elem0.bcs[0, iface0][4]) - 1
                                iface_p1 = int(elem1.bcs[0, iface1][4]) - 1
                                for ibc in range(nbc):
                                    elem_p0_bcs = self.elem[iel_p0].bcs[
                                        ibc, iface_p0]
                                    elem_p1_bcs = self.elem[iel_p1].bcs[
                                        ibc, iface_p1]

                                    elem_p0_bcs[0] = "P"
                                    elem_p1_bcs[0] = "P"
                                    elem_p0_bcs[3] = iel_p1 + 1
                                    elem_p1_bcs[3] = iel_p0 + 1
                                    elem_p0_bcs[4] = iface_p1 + 1
                                    elem_p1_bcs[4] = iface_p0 + 1

                            for ibc in range(nbc):
                                elem0.bcs[ibc, iface0][0] = "E"
                                elem1.bcs[ibc, iface1][0] = "E"
                                elem0.bcs[ibc, iface0][3] = iel1 + 1
                                elem1.bcs[ibc, iface1][3] = iel0 + 1
                                elem0.bcs[ibc, iface0][4] = iface1 + 1
                                elem1.bcs[ibc, iface1][4] = iface0 + 1
                            nchanges = nchanges + 1

        logger.debug(f"merged {nchanges} pairs of faces")
        return nchanges