File Formats
In this section we describe the file formats of all files used by the solver.
Grid File format
LAVACurv uses plot3d grids as the format for specifying the
multi-block grids. Since there are nearly unlimited number of formats of
plot3d grids, we will be very specific about what constitutes valid input
to the solver. The plot3d grids for LAVACurv must use the following
format(s):
- Multiblock
- Double precision
- Little-Endian
- With or Without Iblank Array
- With or Without Record Markers
That is there are 4 possible formats : With/Without Iblanks and With/Without Record Markers. The solver can distinguish between these 4 grid types and no further information is required. The following fortran code will write a valid grid without record markers and with an iblank array.
! Each of X,Y,Z,ib are of length nZone and are a list
! of variable sized arrays. Eg:
! type listOfArraysR8
! real(kind=8), dimension(:, :, :), allocatable :: arr
! end type
! type listOfArraysI4
! integer(kind=4), dimension(:, :, :), allocatable :: arr
! end type
type(listOfArraysR8) :: X,Y,Z
type(listOfArraysI4) :: ib
allocate(X(nZone), Y(nZone), Z(nZone), ib(nZone))
! Allocate individual arrays for each zone and set variables
open(newUnit=iUnit, form='unformatted', access='stream')
write(iUnit) nZone ! Number of Zones
write(iUnit) sizes ! Dimensions of (3, nZone)
do i=1, nZone
write(iUnit) X(i)%arr, Y(i)%arr, Z(i)%arr, ib(i)%arr
end do
close(iUnit)
BC and Subset Files
The BCFile and the optional Subset file are used to define contiguous sections of grid blocks. For the BCFile this is used for specifying boundary conditions. For the subset file, these sections are used for a variety of purposes. These files have the following syntax
!------------------------------------------------------------
! Label nz jbeg jend kbeg kend lbeg lend
!------------------------------------------------------------
in 1 1 -1 1 1 1 -1
out 1 1 -1 -1 -1 1 -1
sym 1 1 -1 1 -1 -1 -1
sym 1 1 1 1 -1 1 -1
sym 1 -1 -1 1 -1 1 -1
sym 1 1 -1 1 49 1 1
sym 1 1 -1 129 -1 1 1
wall 1 1 -1 49 129 1 1
All lines that begin with ! are treated as comment lines and ignored.
The first entry is the label. This can be any string that is appropriate
for the specific problem. The second column is the zone (or grid or
block) number the section belong to. The remaining 6 entries (jBeg,
jEnd, kBeg, kEnd, lBeg, lEnd) give the start and end ranges for that
particular subset. A special negative syntax is allowed: The -1 index
refers to the last node of the block. Therefore a range of 1 -1 covers
the entire block range no matter what the block size. Note that multiple
different tags are allowed on a given surface. In the example above, the
L=1 boundary has 3 different (1->49, 49->129 and (129->-1)sections,
with two unique labels (sym and wall). The order of line in the BC file
does not matter. Additional blank and or comments lines may be added as
desired.
B2B Files
The B2B file is used to uniquely describe how two blocks are connected to each other in a multi-block grid. Note that periodic boundary conditions also fall into this category.
! BlkID js je ks ke ls le DonorID djs dje dks dke dls dle tx ty tz
8 1 1 1 -1 1 -1 8 -1 -1 1 -1 1 -1 1 2 3
8 -1 -1 1 -1 1 -1 8 1 1 1 -1 1 -1 1 2 3
Like the BC and subset files, the ! denotes a comment line. The
ordering of the lines do not matter and blank lines are ignored. The
first entry is BlkID the index of grid block this connection applies
to. The next 6 numbers (js/je/ks/ke/ls/le) are describe the range of the
blkID that is connected to another block (or itself in the case of
periodic boundary). DonorID is the block that which previously
described section is connected to. This can be any block number including
itself. The next 6 numbers (djs/dje/dks/dke/dls/dle, the \'d\' means
\"donor\" here) are the subset of the donor block that is connected to
the first range. Note that depending on how the blocks are oriented, the
starting indices may be larger than the end indices. Note that like
the BC and subset files, a special \"negative\" index is allowed where
-1 denotes the last index of the block. The final 3 numbers describe the
\"transform\" which is how the indices of the first block relate to the
indices of the second block. See the explanation on the CGNS website for
more information:
https://cgns.github.io/CGNS_docs_current/sids/cnct.html#Transform
Interp Files
These files are required only when overset meshes are used. The file
must be in \"INTOUT\" file type. This file is typically written by
LAVACurv itself when performing hole cutting but we describe the
format here as well.
open(newunit=iUnit, file="INTOUT", form='unformatted',&
status='replace')
write(iUnit) nOnBlock
do iBlk=1, nZones
write(iUnit) &
blkIntData(iBlk)%arr(:, 1), & ! j index
blkIntData(iBlk)%arr(:, 2), & ! k index
blkIntData(iBlk)%arr(:, 3), & ! l index
blkIntData(iBlk)%arr(:, 4), & ! donor Block
blkIntData(iBlk)%arr(:, 5), & ! donor j
blkIntData(iBlk)%arr(:, 6), & ! donor k
blkIntData(iBlk)%arr(:, 7), & ! donor l
blkRealData(iBlk)%arr(:, 1), & ! fracJ
blkRealData(iBlk)%arr(:, 2), & ! fracK
blkRealData(iBlk)%arr(:, 3) ! fracL
write(iUnit) blkIblank(iBlk)%arr ! iBlank
end do close (iUnit)
The first write is array sized nZones. Each entry contains the number of fringes (or interpolated) points for that block. Then, looping over each zone, we write the jkl indices of the fringe node followed by donor block, lower left-index of the donor cell, and the three fractional weights. Finally, a full iblank array for the zone is written. This iblank array however is not used by the solver and its values do not matter.
Restart Files
These files are written by the solver to restart a previous simulation where it finished. Restart files are used to restart both steady and unsteady simulations. The contents of the restart file depend on the type of simulation being performed: The code automatically determines which variables need to be saved to ensure a seamless restart. When a restart \"file\" is written there are actually 4 files written:
- a
.rstfile - a
.namfile - a
.infofile - a
.iterfile
The .rst is the main file storing the field information. This is a
plot3d function file, written in double precision, native endian,
without record markers. This file may be loaded into tecplot using
the standard Plot3D grid loader.
The .nam is an ascii file that contains the variable names written in
the file. A typical .nam file for a steady RANS simulation might look
like the following:
Note that this file can be loaded into Tecplot to label the variables
from the .rst file.
The .info file is ascii file that contains various pieces key-value
information required by the solver for seamless restarts. This file
should not be modified by the user.
Finally a .iter file stores the iteration-level history of all
\"iteration variables\". This file is identical to the data.iter
file also written by the code. See dataiterfile{.interpreted-text
role="ref"} for more the format of this file.
data.iter File
The data.iter is binary file automatically written by the solver which
records non-linear iteration level data during both steady and unsteady
simulations. This is a binary file and thus it is not human readable. It
can be processed by the plotHist.py routine for performing various
post-processing plotting functions.
The format of the file is as follows:
- a single 32-bit integer, nVar, listing the number of variables
- a single 32-bit integer, varNameLen, listing the length of each variable name
- nVar * varNameLen bytes containing the variable names
- Each subsequent nVar*8 bytes are treated as 64-bit real values corresponding to a single non-linear iteration
The following sample code will read the file using fortran:
module iterLoader
contains
subroutine loadData(fName, varNames, data)
use iso_fortran_env
implicit none
character(len=*) :: fName
real(kind=8), dimension(:, :), allocatable :: data
character(len=:), allocatable, dimension(:) :: varNames
integer :: fSize, iUnit, nVar, varNameLen, n
inquire(file=fName, size=fSize)
fSize = fSize * FILE_STORAGE_SIZE /8 ! Number of bytes
open(newUnit=iUnit, file=fName, status='old', &
form='unformatted', access='stream')
! Read Header Info
read(iUnit) nVar
read(iUnit) varNameLen
allocate(character(len=varNameLen)::varNames(nVar))
read(iUnit) varNames
! Determine the number of "lines" to read:
n = (fSize - 8 - nVar*varNameLen) / (nVar*8)
allocate(data(nVar, n))
read(iUnit) data
close(iUnit)
end subroutine loadData
end module iterLoader
The following sample code will read the file using python:
def loadData(fName):
import os
import numpy as np
fSize = os.path.getsize(fName)
f = open(fName, 'rb')
nVar = np.fromfile(f, dtype='i4',count=1)[0]
strSize = np.fromfile(f, dtype='i4', count=1)[0]
strs = np.fromfile(f, dtype='c', count=nVar*strSize)
keys = []
for i in range(nVar):
keys.append("".join(strs[i*strSize:(i+1)*strSize]).strip().lower())
remainingBytes = fSize - 8 - nVar * strSize
lines = remainingBytes // (nVar * 8)
buf = np.fromfile(f, dtype=float, count=-1, sep='')
data = buf[0:lines*nVar].reshape((nVar, lines), order='f')
f.close()
return keys, data