-
Notifications
You must be signed in to change notification settings - Fork 7
2D Slice Data
FDS writes each &SLCF to a .sf file per mesh. A slice with one
degenerate dimension is 2-D; this page covers those. For slices covering
a volume see 3-D Slice Data.
import os
import pyfdstools as fds
workingDir = os.path.join(
os.path.dirname(fds.__file__), 'examples', 'data', 'case001.zip')
chid = 'case001'
quantities, files, dimensions, meshes, centers, units = \
fds.readSLCFquantities(chid, workingDir)| Return value | Contents |
|---|---|
quantities |
FDS quantity in each file |
files |
path of each .sf file |
dimensions |
[iX, eX, iY, eY, iZ, eZ] extent in cell indices |
meshes |
mesh string parsed from the file name |
centers |
True if the slice is cell-centered |
units |
units of the quantity |
To list the planes as coordinates rather than cell indices, ask for a plane you know does not exist — the error path prints them:
fds.query2dAxisValue(workingDir, chid, 'TEMPERATURE', 3, 1e9)Warning did not find a 2-D slice of TEMPERATURE on axis 3 at value 1000000000.0000
Available slices of qty TEMPERATURE:
Axis Value
1 2.5500
-1 -1.0000
Note an axis of -1 indicates a 3-D slice which is not currently supported in this function.
An axis of -1 means that file holds a 3-D slice.
query2dAxisValue is the routine you want almost always. It finds every
.sf file holding the requested quantity in the requested plane,
assembles them onto one grid, and returns the result.
data, units = fds.query2dAxisValue(
workingDir, chid, 'TEMPERATURE', axis=1, value=2.55, time=30, dt=60)
x = data['x'] # array(N, M) first in-plane coordinate
z = data['z'] # array(N, M) second in-plane coordinate
values = data['datas'] # array(N, M, NT)
times = data['times'] # timestamp of each frame| Argument | Meaning |
|---|---|
axis |
axis the plane is normal to: 1 = x, 2 = y, 3 = z |
value |
coordinate of the plane along that axis |
time, dt
|
frame selection, see Concepts |
atol |
tolerance when matching value against the planes present (default 1e-8) |
printInfo |
print each file as it is read |
verbose |
print progress per mesh |
value is matched against the slice coordinates with atol, which
defaults to 1e-8 — effectively exact. If your slice was written at
2.5499999 and you ask for 2.55, loosen it:
data, units = fds.query2dAxisValue(
workingDir, chid, 'TEMPERATURE', 1, 2.55, atol=1e-3)Anywhere the absolute grid is not covered by a mesh comes back as NaN.
Use the nan-aware reductions:
import numpy as np
print('peak', np.nanmax(values))
print('mean', np.nanmean(values[:, :, -1]))When you want one mesh's raw data on its own grid:
slcfFile = fds.getFileList(workingDir, chid, 'sf')[0]
x, z, values, times, coords = fds.read2dSliceFile(slcfFile, chid)coords is [xmin, xmax, ymin, ymax, zmin, zmax] of the slice; exactly
one axis has equal bounds, which is the plane it lies in.
read2dSliceFile returns None if the file holds a 3-D slice.
Pass cen=True for a cell-centered slice to have the coordinates
shifted to cell centers, and grid= to supply a mesh grid you already
have rather than letting it read the mesh's .xyz file.
For the rawest form, which keeps the singleton axis and does no coordinate work at all:
lims, values, times = fds.readSingleSlcfFile(slcfFile)
# values.shape == (NX+1, NY+1, NZ+1, NT), one axis of length 1Passing dt without time applies a running mean of that width to
every frame, which is the usual way to smooth a noisy field before
plotting:
x, z, smoothed, times, coords = fds.read2dSliceFile(
slcfFile, chid, dt=20.0)Each output frame is the mean of the frames whose timestamps fall within
t ± dt/2. See Time Averaging for writing the
averaged field back out as a slice file smokeview can open.
If the case wrote a 3-D slice rather than a 2-D one, take a plane out of it instead:
grid, values, times, units = fds.readSLCF3Ddata(
chid, workingDir, 'TEMPERATURE', axis=3, value=1.0)Giving axis and value makes readSLCF3Ddata extract only that plane,
which avoids assembling the whole volume. See
3-D Slice Data.
Obstructions occupy part of a slice plane. To mask them out:
blocked = fds.getBlockedCellsInPlane(workingDir, chid, axis=3, value=0.0)
values[blocked[..., None].astype(bool).repeat(values.shape[2], axis=2)] = np.nanblocked is 1 where an obstruction blocks the plane and 0
elsewhere, sized on the same absolute grid.
fds.renderSliceCsvs(data, chid, outdir='.')One file per frame, named <chid>_<time>.csv. Rows are the second
in-plane coordinate, columns the first.
To do it yourself for a single frame:
import pandas as pd
frame = pd.DataFrame(data['datas'][:, :, -1].T,
index=data['z'][0, :],
columns=data['x'][:, 0])
frame.to_csv('final_frame.csv')You can compute a new field and write it as a slice file that smokeview
will display alongside the originals. See
Time Averaging for the mechanics, and
pyfdstools/examples/make_slice_from_devc.py for a worked example that
builds a radiative heat flux slice from a grid of devices.
pyfdstools/examples/dump_2d_slice_to_csv.py does all of the above and
takes its parameters as arguments:
python dump_2d_slice_to_csv.py \
--chid case002 --quantity TEMPERATURE \
--axis 3 --value 7.2 --time 30 --dt -1 \
--working_dir data/case002.zipquery2dAxisValueXYZ and readSLCF2Ddata do the same job using the
.xyz files FDS writes when WRITE_XYZ=.TRUE. is set on &DUMP. They
emit a DeprecationWarning; use query2dAxisValue, which reads the mesh
grids from the smokeview file and so works on any case.
Getting started
Reading results
Working with results
Building models
Reference