3.1. Fundamentals#
In fire safety engineering compartment fires are an important topic. The development of a fire in a compartment determines the energy release and the spread of smoke through a building. This has direct on the safety of the occupants and how effective the fire brigade can deal with the fire. A good overview on modelling principles of compartment fires is given in the book “Enclosure Fire Dynamics” by Karlsson and Quinitere [KQ99]. We take some of the aspect described in this book and discuss them here in context of the lecture. The simulations themselves are built around some experiments that are fundamental to the field of fire safety engineering.
At first, we look into plume models and experiment data from McCaffrey [McC79], which are described in the report about “Purely Buoyant Diffusion Flames”. These plume experiments and respective models are a good starting point to modelling compartment fires. Using gas burners at different locations, Steckler et al. performed a number of experiments in a regular-sized compartment [SQR82] (\(\mf 2.8~m \times 2.8~m \times 2.18~m\)). The results are provided in the report “Flow Induced by Fire in a Compartment”.
This brief introduction into the fluid dynamics in a compartment fire is based on a characteristic setup and simulations carried out.
Setup#
The compartment has an extension of
and is embeded in a computational domain of
The compartment is build out of individual obstacles, creating the walls, the floor and the ceiling. One of the walls contains an opening, see Fig. 3.3. The domain boundaries are defined as open boundaries.
The heat release rate is defined as a constant value of \(\mf 10~kW\) and the patch emitting the fuel has a surface temperature of \(\mf 100~^\circ C\).
For demonstration purposes, all quantities are visualised in the symmetry plane at \(\mf y=0\), see Fig. 3.4.
Full FDS input file
&HEAD CHID='out_compartment', TITLE='Compartment Fire Example'/
!! &MESH IJK=96,72,60, XB=0.00,4.80,-1.80,1.80,0.00,3.00 /
&MESH ID='1' IJK=48,36,30 XB=0.0000,2.4000,-1.8000,0.0000,0.0000,1.5000 /
&MESH ID='2' IJK=48,36,30 XB=0.0000,2.4000,-1.8000,0.0000,1.5000,3.0000 /
&MESH ID='3' IJK=48,36,30 XB=0.0000,2.4000,0.0000,1.8000,0.0000,1.5000 /
&MESH ID='4' IJK=48,36,30 XB=0.0000,2.4000,0.0000,1.8000,1.5000,3.0000 /
&MESH ID='5' IJK=48,36,30 XB=2.4000,4.8000,-1.8000,0.0000,0.0000,1.5000 /
&MESH ID='6' IJK=48,36,30 XB=2.4000,4.8000,-1.8000,0.0000,1.5000,3.0000 /
&MESH ID='7' IJK=48,36,30 XB=2.4000,4.8000,0.0000,1.8000,0.0000,1.5000 /
&MESH ID='8' IJK=48,36,30 XB=2.4000,4.8000,0.0000,1.8000,1.5000,3.0000 /
&TIME T_END=100.0 /
&MISC TMPA=22. /
&SURF ID='BURNER', HRRPUA=250, TMP_FRONT=100., COLOR='FIREBRICK' /
&VENT XB=1.30,1.50,-0.10,0.10,0.05,0.05, SURF_ID='BURNER' /
&REAC FUEL='METHANE',SOOT_YIELD=0.15 /
&OBST XB=2.70,2.75, -1.40,1.40, 0.05,2.00, COLOR='GRAY', TRANSPARENCY=0.5 / Wall with door or window
&OBST XB=0.20,0.25, -1.40,1.40, 0.05,2.00, COLOR='GRAY', TRANSPARENCY=0.5 / Wall
&OBST XB=0.00,4.80, -1.80,1.80, 0.00,0.05, COLOR='GRAY' / Floor
&OBST XB=0.20,2.75, -1.40,1.40, 2.00,2.05, COLOR='GRAY', TRANSPARENCY=0.5 / Ceiling
&OBST XB=0.20,2.75, -1.46,-1.40, 0.05,2.05, COLOR='GRAY', TRANSPARENCY=0.5 / Wall
&OBST XB=0.20,2.75, 1.40,1.46, 0.05,2.05, COLOR='GRAY', TRANSPARENCY=0.5 / Wall
&HOLE XB=2.65,2.85,-0.20,0.20, 0.05,1.80 / Door Opening
&VENT DB='XMIN',SURF_ID='OPEN'/
&VENT DB='XMAX',SURF_ID='OPEN'/
&VENT DB='YMIN',SURF_ID='OPEN'/
&VENT DB='YMAX',SURF_ID='OPEN'/
&VENT DB='ZMIN',SURF_ID='OPEN'/
&VENT DB='ZMAX',SURF_ID='OPEN'/
&SLCF PBY=0.0, QUANTITY='TEMPERATURE', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='PRESSURE', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='VELOCITY', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='U-VELOCITY', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='V-VELOCITY', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='W-VELOCITY', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='BACKGROUND PRESSURE', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='DENSITY', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='ENTHALPY', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='HRRPUV', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='INTERNAL ENERGY', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='SENSIBLE ENTHALPY', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='SPECIFIC ENTHALPY', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='SPECIFIC HEAT', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='VISCOSITY', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='IDEAL GAS PRESSURE', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='KOLMOGOROV LENGTH SCALE', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='MOLECULAR VISCOSITY', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='SUBGRID KINETIC ENERGY', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='VORTICITY X', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='VORTICITY Y', CELL_CENTERED=.TRUE. /
&SLCF PBY=0.0, QUANTITY='VORTICITY Z', CELL_CENTERED=.TRUE. /
&TAIL /
Pressure#
Thermal expansion and differences in gas densities lead to pressure differences. These in turn are driving the gas flow in and out of the compartment. One can compare the pressuere differences inside and outside of a compartment, as well as changes with height. During different phases of a developing fire the pressure profiles change. In the very early phase it is considerd that there is mostly overpressure inside the room with respect to outside.
When the fire developes a bit further a more pronounced pressure profile is generated. The height where the two graphs cross is considered the location of the “neutral plane” that spans the compartment. This is also highlighted in the plots further down, with the pressure slices. In the figure shown, it can be seen that the pressure inside the room starts out lower than outside. They run parallel for some distance and then the pressure inside increases notably. Around the bend, below the neutral plane, is typically the height of a relatively stable interface between the cold and hot gas layers. These observations form the basis for the zone models.
The further the fire in a compartment can develop, the further the height of the cold gas layer drops. At some point there are no gas layers left to speak of and the room is considered fully envolved in the fire. Furthermore, it is visible that parts of the smoke get mixed into the air that flows into the compartment. Thus, over time the relatively smoke free lower layer get more and more enriched with smoke.
list_q = ['PRESSURE', 'BACKGROUND PRESSURE']
it = sim.slices[0].get_nearest_timestep(250)
for q in list_q:
slice = sim.slices.filter_by_quantity(q)[0]
# print(slice)
### fds / fdsreader BUG workaround BEGIN
### Issue due to SLCF located at MESH boundary
to_del = []
for s in slice:
if s.extent._extents[1][0] > 0:
to_del.append(s.mesh)
for m in to_del:
del slice._subslices[m]
extent = (slice.extent[0][0], slice.extent[0][1],
slice.extent[2][0], slice.extent[2][1])
# print(extent)
### fds / fdsreader BUG workaround END
slice_data = slice.to_global()
if np.min(slice_data[it]) < 0:
v_abs_max = np.max(np.abs(slice_data[it]))
vmin = -v_abs_max
vmax = v_abs_max
else:
vmin = np.min(slice_data[it])
vmax = np.max(slice_data[it])
plt.imshow(slice_data[it].T,
vmin = vmin, vmax = vmax,
origin='lower',
extent=extent,
cmap='seismic')
q = slice.quantity.quantity
u = slice.quantity.unit
plt.colorbar(label=f"{q} / {u}")
plt.show()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Input In [1], in <cell line: 3>()
1 list_q = ['PRESSURE', 'BACKGROUND PRESSURE']
----> 3 it = sim.slices[0].get_nearest_timestep(250)
5 for q in list_q:
6 slice = sim.slices.filter_by_quantity(q)[0]
NameError: name 'sim' is not defined
Flow#
import numpy as np
import matplotlib.pyplot as plt
import fdsreader
plt.rcParams['figure.dpi'] = 150
path_to_data = '../../../../data/compartment/pressure_01/rundir/'
sim = fdsreader.Simulation(path_to_data)
list_q = ['U-VELOCITY', 'V-VELOCITY', 'W-VELOCITY']
it = sim.slices[0].get_nearest_timestep(250)
for q in list_q:
slice = sim.slices.filter_by_quantity(q)[0]
print(slice)
### fds / fdsreader BUG workaround BEGIN
### Issue due to SLCF located at MESH boundary
to_del = []
for s in slice:
if s.extent._extents[1][0] > 0:
to_del.append(s.mesh)
for m in to_del:
del slice._subslices[m]
extent = (slice.extent[0][0], slice.extent[0][1],
slice.extent[2][0], slice.extent[2][1])
# print(extent)
### fds / fdsreader BUG workaround END
slice_data = slice.to_global()
v_abs_max = np.max(np.abs(slice_data[it]))
plt.imshow(slice_data[it].T,
vmin = -v_abs_max, vmax = v_abs_max,
origin='lower',
extent=extent,
cmap='seismic')
q = slice.quantity.quantity
u = slice.quantity.unit
plt.colorbar(label=f"{q} / {u}")
plt.show()
Slice([3D] cell_centered=True, extent=Extent([0.00, 4.80] x [0.00, 0.05] x [0.00, 3.00]))
Slice([3D] cell_centered=True, extent=Extent([0.00, 4.80] x [0.00, 0.05] x [0.00, 3.00]))
Slice([3D] cell_centered=True, extent=Extent([0.00, 4.80] x [0.00, 0.05] x [0.00, 3.00]))
u_slice = sim.slices.filter_by_quantity('U-VELOCITY')[0]
slice_data = u_slice.to_global()
x0 = 2.7
ix = u_slice.get_nearest_index('x', x0)
print(f'Index in x-direction next to x={x0} is {ix}.')
z0 = 1.5
iy = u_slice.get_nearest_index('y', z0)
print(f'Index in z-direction next to z={z0} is {iy}.')
Index in x-direction next to x=2.7 is 54.
Index in z-direction next to z=1.5 is 35.
instant_values = slice_data[:, ix, iy]
n_a = 51
average_values = np.convolve(instant_values, np.ones(n_a)/n_a, mode='valid')
plt.plot(u_slice.times, instant_values, '.', color='grey', alpha=0.5, label='instanteneous values')
plt.plot(u_slice.times[n_a//2:-(n_a//2)], average_values, label='moving average')
plt.xlabel('Time / s')
plt.ylabel('Velocity Component $\sf v_x$ / m/s')
plt.grid()
plt.legend()
plt.title(f'Values at position (x,z)=({x0} m, {z0} m)');
z_max = 2.0
iz_max = u_slice.get_nearest_index('y', z_max)
### TODO: quick solution, needs fix
h = np.arange(0, z_max, 0.05)
list_t = [10, 25, 50, 100, 250]
for t in list_t:
it = sim.slices[0].get_nearest_timestep(t)
plt.plot(slice_data[it, ix, 0:iz_max+5], h, label=f't={t}')
plt.xlabel('Velocity Component $\sf v_x$ / m/s')
plt.ylabel('Height / m')
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x118679d90>
Other Quantities#
list_q = ['TEMPERATURE', 'DENSITY', 'KOLMOGOROV LENGTH SCALE', 'SUBGRID KINETIC ENERGY', 'VORTICITY Y']
it = sim.slices[0].get_nearest_timestep(250)
for q in list_q:
slice = sim.slices.filter_by_quantity(q)[0]
# print(slice)
### fds / fdsreader BUG workaround BEGIN
### Issue due to SLCF located at MESH boundary
to_del = []
for s in slice:
if s.extent._extents[1][0] > 0:
to_del.append(s.mesh)
for m in to_del:
del slice._subslices[m]
extent = (slice.extent[0][0], slice.extent[0][1],
slice.extent[2][0], slice.extent[2][1])
# print(extent)
### fds / fdsreader BUG workaround END
slice_data = slice.to_global()
if np.min(slice_data[it]) < 0:
v_abs_max = np.max(np.abs(slice_data[it]))
vmin = -v_abs_max
vmax = v_abs_max
cmap='seismic'
else:
vmin = np.min(slice_data[it])
vmax = np.max(slice_data[it])
cmap='viridis'
plt.imshow(slice_data[it].T,
vmin = vmin, vmax = vmax,
origin='lower',
extent=extent,
cmap=cmap)
q = slice.quantity.quantity
u = slice.quantity.unit
plt.colorbar(label=f"{q} / {u}")
plt.show()