Advanced Examples¶
Complex PyFDS simulations demonstrating advanced features and techniques.
Overview¶
These examples showcase:
- Multi-room scenarios with complex geometry
- HVAC systems with time-varying controls
- Multi-mesh simulations
- Advanced fire modeling
- Realistic building simulations
Multi-Room Fire Spread¶
Simulate fire spreading between connected rooms.
from pyfds import Simulation
from pyfds.core.geometry import Point3D
sim = Simulation(chid='multi_room_fire')
sim.add(Time(t_end=900.0))
# Two-room domain
sim.add(Mesh(id='ROOM1', ijk=Grid3D.of(60, 50, 25), xb=Bounds3D.of(0, 6, 0, 5, 0, 2.5)))
sim.add(Mesh(id='ROOM2', ijk=Grid3D.of(60, 50, 25), xb=Bounds3D.of(6, 12, 0, 5, 0, 2.5)))
# Ambient conditions
sim.set_misc(tmpa=20.0, humidity=50.0)
# Material: Gypsum walls
sim.add(Material(
id='GYPSUM',
conductivity=0.48,
specific_heat=0.84,
density=1440.0
)
sim.add(Surface(
id='GYPSUM_WALL',
matl_id='GYPSUM',
thickness=0.0127,
color='WHITE'
)
# Room 1 walls (with door opening)
sim.add(Obstruction(xb=Bounds3D.of(0, 0.1, 0, 5, 0, 2.5), surf_id='GYPSUM_WALL') # West wall
sim.add(Obstruction(xb=Bounds3D.of(0, 6, 0, 0.1, 0, 2.5), surf_id='GYPSUM_WALL') # South wall
sim.add(Obstruction(xb=Bounds3D.of(0, 6, 4.9, 5, 0, 2.5), surf_id='GYPSUM_WALL') # North wall
# Dividing wall with door (at x=6)
sim.add(Obstruction(xb=Bounds3D.of(5.9, 6.1, 0, 1.5, 0, 2.5), surf_id='GYPSUM_WALL') # Below door
sim.add(Obstruction(xb=Bounds3D.of(5.9, 6.1, 1.5, 2.5, 2.1, 2.5), surf_id='GYPSUM_WALL') # Above door
sim.add(Obstruction(xb=Bounds3D.of(5.9, 6.1, 2.5, 5, 0, 2.5), surf_id='GYPSUM_WALL') # Above door
# Room 2 walls
sim.add(Obstruction(xb=Bounds3D.of(11.9, 12, 0, 5, 0, 2.5), surf_id='GYPSUM_WALL') # East wall
sim.add(Obstruction(xb=Bounds3D.of(6, 12, 0, 0.1, 0, 2.5), surf_id='GYPSUM_WALL') # South wall
sim.add(Obstruction(xb=Bounds3D.of(6, 12, 4.9, 5, 0, 2.5), surf_id='GYPSUM_WALL') # North wall
# Fire in Room 1 (grows over time)
sim.add(Ramp(id='FIRE_GROWTH', t=[0, 180, 600, 900], f=[0, 1, 1, 0.8]))
sim.add(Surface(id='FIRE', hrrpua=1500.0, ramp_q='FIRE_GROWTH', color='ORANGE'))
sim.add(Obstruction(xb=Bounds3D.of(2, 3, 2, 3, 0, 0.1), surf_id='FIRE'))
# External door in Room 2
sim.add(Vent(xb=Bounds3D.of(12, 12, 2, 3, 0, 2.1), surf_id='OPEN'))
# Devices: Temperature in each room
sim.add(Device(
id='TEMP_ROOM1',
quantity='TEMPERATURE',
xyz=Point3D.of(3, 2.5, 2.0)
)
sim.add(Device(
id='TEMP_ROOM2',
quantity='TEMPERATURE',
xyz=Point3D.of(9, 2.5, 2.0)
)
# Smoke layer height
sim.add(Device(
id='LAYER_ROOM1',
quantity='LAYER HEIGHT',
xyz=Point3D.of(3, 2.5, 1.25)
)
sim.add(Device(
id='LAYER_ROOM2',
quantity='LAYER HEIGHT',
xyz=Point3D.of(9, 2.5, 1.25)
)
sim.write('multi_room_fire.fds')
Features: - Two connected rooms with shared door - Fire originates in Room 1 - Heat and smoke spread to Room 2 - Temperature and layer height monitoring
HVAC System with Control Logic¶
Automatic HVAC shutdown when smoke is detected.
from pyfds import Simulation
sim = Simulation(chid='hvac_control')
sim.add(Time(t_end=900.0))
sim.add(Mesh(ijk=Grid3D.of(80, 60, 30), xb=Bounds3D.of(0, 8, 0, 6, 0, 3)))
sim.set_misc(tmpa=22.0, humidity=50.0)
# Fire starts at t=120s
sim.add(Ramp(id='FIRE_START', t=[0, 120, 240], f=[0, 0, 1]))
sim.add(Surface(id='FIRE', hrrpua=1200.0, ramp_q='FIRE_START'))
sim.add(Obstruction(xb=Bounds3D.of(3.5, 4.5, 2.5, 3.5, 0, 0.1), surf_id='FIRE'))
# Smoke detector at ceiling
sim.add(Device(
id='SMOKE_DET',
quantity='OPTICAL DENSITY',
xyz=Point3D.of(4, 3, 2.9)
)
# Control: HVAC operates until smoke detector activates
# In real PyFDS, this would use CTRL namelist
# Simplified: HVAC operates for first 300s, then shuts off
sim.add(Ramp(id='HVAC_CTRL', t=[0, 300, 301], f=[1, 1, 0]))
# Supply vent (with control)
sim.add(Vent(
xb=Bounds3D.of(1, 1.5, 1, 1.5, 3, 3),
surf_id='HVAC',
volume_flow=0.6,
volume_flow_ramp='HVAC_CTRL'
)
# Exhaust vent (with control)
sim.add(Vent(
xb=Bounds3D.of(6.5, 7, 4.5, 5, 3, 3),
surf_id='HVAC',
volume_flow=-0.5,
volume_flow_ramp='HVAC_CTRL'
)
# Emergency exit door
sim.add(Vent(xb=Bounds3D.of(8, 8, 2.5, 3.5, 0, 2.1), surf_id='OPEN'))
# Temperature devices
for i, x in enumerate([2, 4, 6]):
sim.add(Device(
id=f'TEMP_{i+1}',
quantity='TEMPERATURE',
xyz=Point3D.of(x, 3, 2.5)
)
sim.write('hvac_control.fds')
Features: - HVAC supply and exhaust - Time-based control (simplified) - Smoke detection - Multiple temperature sensors
Atrium Fire¶
Large open space with high ceiling.
from pyfds import Simulation
sim = Simulation(chid='atrium_fire')
sim.add(Time(t_end=600.0))
# Tall atrium space (10m high)
sim.add(Mesh(id='LOWER', ijk=Grid3D.of(80, 80, 40), xb=Bounds3D.of(0, 16, 0, 16, 0, 5)))
sim.add(Mesh(id='UPPER', ijk=Grid3D.of(80, 80, 40), xb=Bounds3D.of(0, 16, 0, 16, 5, 10)))
sim.set_misc(tmpa=20.0)
# Concrete floor/walls
sim.add(Material(
id='CONCRETE',
conductivity=1.8,
specific_heat=0.88,
density=2400.0
)
sim.add(Surface(
id='CONCRETE_SURF',
matl_id='CONCRETE',
thickness=0.3,
color='GRAY'
)
# Walls
sim.add(Obstruction(xb=Bounds3D.of(0, 0.3, 0, 16, 0, 10), surf_id='CONCRETE_SURF') # West
sim.add(Obstruction(xb=Bounds3D.of(15.7, 16, 0, 16, 0, 10), surf_id='CONCRETE_SURF') # East
sim.add(Obstruction(xb=Bounds3D.of(0, 16, 0, 0.3, 0, 10), surf_id='CONCRETE_SURF') # South
sim.add(Obstruction(xb=Bounds3D.of(0, 16, 15.7, 16, 0, 10), surf_id='CONCRETE_SURF') # North
# Growing fire at floor level
sim.add(Ramp(
id='T_SQUARED',
t=[0, 60, 120, 180, 240, 300, 600],
f=[0, 0.25, 0.5, 0.75, 0.9, 1.0, 1.0]
)
sim.add(Surface(id='FIRE', hrrpua=2500.0, ramp_q='T_SQUARED', color='RED'))
sim.add(Obstruction(xb=Bounds3D.of(7, 9, 7, 9, 0, 0.2), surf_id='FIRE'))
# Openings at ground level (four doors)
sim.add(Vent(xb=Bounds3D.of(0, 0, 6, 8, 0, 2.1), surf_id='OPEN') # West door
sim.add(Vent(xb=Bounds3D.of(16, 16, 6, 8, 0, 2.1), surf_id='OPEN') # East door
sim.add(Vent(xb=Bounds3D.of(6, 8, 0, 0, 0, 2.1), surf_id='OPEN') # South door
sim.add(Vent(xb=Bounds3D.of(6, 8, 16, 16, 0, 2.1), surf_id='OPEN') # North door
# Vertical temperature profile
for z in [2, 4, 6, 8, 9.5]:
sim.add(Device(
id=f'TEMP_Z{z:.1f}',
quantity='TEMPERATURE',
xyz=Point3D.of(8, 8, z)
)
# Ceiling temperature
sim.add(Device(
id='CEILING_TEMP',
quantity='CEILING TEMPERATURE',
xyz=Point3D.of(8, 8, 9.8)
)
sim.write('atrium_fire.fds')
Features: - Multi-mesh for tall space - Growing fire (t-squared) - Vertical temperature profile - Multiple exits
Data Center with Racks¶
Realistic data center with equipment racks and cooling.
from pyfds import Simulation
sim = Simulation(chid='data_center')
sim.add(Time(t_end=600.0))
sim.add(Mesh(ijk=Grid3D.of(100, 80, 30), xb=Bounds3D.of(0, 10, 0, 8, 0, 3)))
sim.set_misc(tmpa=18.0, humidity=40.0) # Cool, dry environment
# Raised floor
sim.add(Obstruction(xb=Bounds3D.of(0, 10, 0, 8, 0, 0.3), surf_id='INERT', color='GRAY'))
# Server racks (4 rows)
rack_width = 0.6
rack_depth = 1.0
rack_height = 2.1
for row in range(4):
y_start = 1.5 + row * 1.6
for col in range(8):
x_start = 1.0 + col * 1.1
sim.add(Obstruction(
xb=(x_start, x_start + rack_width,
y_start, y_start + rack_depth,
0.3, 0.3 + rack_height),
surf_id='INERT',
color='BLACK'
)
# Cooling vents (under raised floor)
for x in [2, 5, 8]:
sim.add(Vent(
xb=Bounds3D.of(x, x + 0.5, 3.5, 4, 0.3, 0.3),
surf_id='HVAC',
volume_flow=0.8
)
# Fire starts in one rack (electrical)
sim.add(Ramp(id='ELEC_FIRE', t=[0, 30, 120, 600], f=[0, 1, 1, 0.8]))
sim.add(Surface(id='FIRE', hrrpua=800.0, ramp_q='ELEC_FIRE', color='YELLOW'))
# Fire on rack surface
sim.add(Obstruction(
xb=Bounds3D.of(4.4, 4.45, 3.1, 4.1, 1.5, 2.0),
surf_id='FIRE'
)
# Exit doors
sim.add(Vent(xb=Bounds3D.of(0, 0, 3.5, 4.5, 0.3, 2.4), surf_id='OPEN'))
sim.add(Vent(xb=Bounds3D.of(10, 10, 3.5, 4.5, 0.3, 2.4), surf_id='OPEN'))
# Temperature sensors at rack height
for x in [2, 5, 8]:
for y in [2, 4, 6]:
sim.add(Device(
id=f'T_X{x}Y{y}',
quantity='TEMPERATURE',
xyz=Point3D.of(x, y, 1.5)
)
sim.write('data_center.fds')
Features: - Raised floor with under-floor cooling - Array of server racks - Electrical fire with realistic growth - Comprehensive temperature monitoring
Tunnel Fire with Ventilation¶
Road tunnel with longitudinal ventilation.
from pyfds import Simulation
sim = Simulation(chid='tunnel_fire')
sim.add(Time(t_end=900.0))
# Long tunnel (100m × 8m × 5m)
# Use multiple meshes for efficiency
for i in range(5):
x_start = i * 20
sim.add(Mesh(
id=f'TUNNEL_{i+1}',
ijk=Grid3D.of(100, 40, 25),
xb=Bounds3D.of(x_start, x_start + 20, 0, 8, 0, 5)
)
sim.set_misc(tmpa=15.0) # Cool tunnel environment
# Concrete tunnel
sim.add(Material(
id='CONCRETE',
conductivity=1.8,
specific_heat=0.88,
density=2400.0
)
sim.add(Surface(
id='TUNNEL_WALL',
matl_id='CONCRETE',
thickness=0.4
)
# Tunnel walls and ceiling
sim.add(Obstruction(xb=Bounds3D.of(0, 100, 0, 0.3, 0, 5), surf_id='TUNNEL_WALL') # South wall
sim.add(Obstruction(xb=Bounds3D.of(0, 100, 7.7, 8, 0, 5), surf_id='TUNNEL_WALL') # North wall
sim.add(Obstruction(xb=Bounds3D.of(0, 100, 0, 8, 4.7, 5), surf_id='TUNNEL_WALL') # Ceiling
# Vehicle fire at x=50m (truck fire)
sim.add(Ramp(
id='TRUCK_FIRE',
t=[0, 120, 600, 900],
f=[0, 1.0, 1.0, 0.7]
)
sim.add(Surface(id='FIRE', hrrpua=3000.0, ramp_q='TRUCK_FIRE', color='ORANGE'))
sim.add(Obstruction(xb=Bounds3D.of(48, 52, 3, 5, 0, 2), surf_id='FIRE'))
# Longitudinal ventilation (jet fans create flow from west to east)
sim.set_misc(wind_speed=3.0, wind_direction=0.0) # 3 m/s eastward flow
# Portal openings
sim.add(Vent(xb=Bounds3D.of(0, 0, 0.5, 7.5, 0.5, 4.5), surf_id='OPEN') # West portal
sim.add(Vent(xb=Bounds3D.of(100, 100, 0.5, 7.5, 0.5, 4.5), surf_id='OPEN') # East portal
# Temperature array along tunnel centerline
for x in range(10, 91, 10):
sim.add(Device(
id=f'TEMP_X{x}',
quantity='TEMPERATURE',
xyz=Point3D.of(x, 4, 3.5)
)
# Visibility devices (critical for evacuation)
for x in [30, 50, 70]:
sim.add(Device(
id=f'VIS_X{x}',
quantity='VISIBILITY',
xyz=Point3D.of(x, 4, 1.5)
)
sim.write('tunnel_fire.fds')
Features: - Multi-mesh long tunnel - Vehicle fire (high HRR) - Longitudinal ventilation - Temperature and visibility monitoring
Outdoor Pool Fire¶
Large liquid pool fire outdoors with wind.
from pyfds import Simulation
sim = Simulation(chid='pool_fire')
sim.add(Time(t_end=600.0))
# Large outdoor domain
sim.add(Mesh(ijk=Grid3D.of(120, 120, 60), xb=Bounds3D.of(-30, 30, -30, 30, 0, 30)))
# Outdoor conditions with wind
sim.set_misc(
tmpa=25.0,
humidity=60.0,
wind_speed=5.0, # 5 m/s wind
wind_direction=270.0, # From west
roughness_length=0.03 # Short grass
)
# Open boundaries (all sides)
for mb in ['XMIN', 'XMAX', 'YMIN', 'YMAX', 'ZMAX']:
sim.add(Vent(mb=mb, surf_id='OPEN'))
# Circular pool fire (10m diameter)
sim.add(Surface(
id='POOL_FIRE',
hrrpua=2000.0, # Liquid fuel
color='ORANGE'
)
sim.add(Vent(
xb=Bounds3D.of(-10, 10, -10, 10, 0, 0),
surf_id='POOL_FIRE',
xyz=Point3D.of(0, 0, 0),
radius=5.0 # 5m radius = 10m diameter
)
# Downwind structures (to see radiation effects)
sim.add(Material(id='WOOD', conductivity=0.12, specific_heat=1.0, density=500.0))
sim.add(Surface(id='WOOD_SURF', matl_id='WOOD', thickness=0.02))
# Building at x=15m (downwind)
sim.add(Obstruction(
xb=Bounds3D.of(15, 20, -5, 5, 0, 6),
surf_id='WOOD_SURF',
color='TAN'
)
# Heat flux meters at various distances
for dist in [10, 15, 20, 25]:
sim.add(Device(
id=f'HF_{dist}m',
quantity='GAUGE HEAT FLUX',
xyz=Point3D.of(dist, 0, 3),
ior=1 # Facing fire (-X direction)
)
# Temperature at various heights above fire
for z in [5, 10, 15, 20]:
sim.add(Device(
id=f'TEMP_Z{z}',
quantity='TEMPERATURE',
xyz=Point3D.of(0, 0, z)
)
sim.write('pool_fire.fds')
Features: - Circular pool fire (annular vent) - Wind effects on plume - Radiation to downwind structure - Heat flux measurements
Pyrolysis Material Example¶
Simulate a charring wood fire with detailed pyrolysis kinetics.
from pyfds import Simulation
from pyfds.builders import MaterialBuilder
from pyfds.core.models import PyrolysisReaction, PyrolysisProduct
sim = Simulation(chid='pyrolysis_fire', title='Wood Pyrolysis Fire')
sim.add(Time(t_end=600.0))
# Domain: 4m × 4m × 2m room
sim.add(Mesh(ijk=Grid3D.of(40, 40, 20), xb=Bounds3D.of(0, 4, 0, 4, 0, 2)))
# Define char residue first (referenced by pyrolysis)
char = MaterialBuilder("CHAR") \
.density(150) \
.thermal_conductivity(0.1) \
.specific_heat(1.0) \
.build()
# Wood with structured pyrolysis reactions
wood = MaterialBuilder("WOOD") \
.density(500) \
.thermal_conductivity(0.13) \
.specific_heat(2.5) \
.add_reaction(
PyrolysisReaction(
a=1e10, # Pre-exponential factor [1/s]
e=100000, # Activation energy [kJ/kmol]
heat_of_reaction=500, # Endothermic [kJ/kg]
products=[
PyrolysisProduct(spec_id="WOOD_GAS", nu_spec=0.75),
PyrolysisProduct(matl_id="CHAR", nu_matl=0.25),
]
)
) \
.build()
# Add materials to simulation
sim.add_material(char)
sim.add_material(wood)
# Wood surface (pyrolyzing)
sim.add(Surface(id='WOOD_SURF', matl_id='WOOD', thickness=0.02, color='BROWN'))
# Concrete floor
sim.add(Material(id='CONCRETE', conductivity=1.8, specific_heat=0.88, density=2400))
sim.add(Surface(id='CONCRETE_SURF', matl_id='CONCRETE', thickness=0.1, color='GRAY'))
# Room boundaries
sim.add(Obstruction(xb=Bounds3D.of(0, 4, 0, 4, 0, 0.1), surf_id='CONCRETE_SURF') # Floor
sim.add(Obstruction(xb=Bounds3D.of(0, 4, 0, 4, 1.9, 2), surf_id='WOOD_SURF') # Ceiling
sim.add(Obstruction(xb=Bounds3D.of(0, 0.1, 0, 4, 0, 2), surf_id='WOOD_SURF') # Walls
sim.add(Obstruction(xb=Bounds3D.of(3.9, 4, 0, 4, 0, 2), surf_id='WOOD_SURF'))
sim.add(Obstruction(xb=Bounds3D.of(0, 4, 0, 0.1, 0, 2), surf_id='WOOD_SURF'))
sim.add(Obstruction(xb=Bounds3D.of(0, 4, 3.9, 4, 0, 2), surf_id='WOOD_SURF'))
# Ignition source (small fire to start pyrolysis)
sim.add(Surface(id='IGNITER', hrrpua=50.0, color='RED'))
sim.add(Obstruction(xb=Bounds3D.of(1.9, 2.1, 1.9, 2.1, 0, 0.05), surf_id='IGNITER'))
# Measurements
sim.add(Device(id='TEMP_CENTER', quantity='TEMPERATURE', xyz=Point3D.of(2, 2, 1.5)))
sim.add(Device(id='MLRPUA', quantity='MLRPUA', xyz=Point3D.of(2, 2, 0.01)) # Mass loss rate
sim.write('pyrolysis_fire.fds')
Features Demonstrated:
- Structured pyrolysis API with PyrolysisReaction and PyrolysisProduct
- Multi-step material decomposition (wood → char + gases)
- Material cross-references (wood references char)
- Pyrolysis-driven fire spread
- Mass loss rate measurement
Expected Results: - Initial ignition followed by pyrolysis-driven fire growth - Char layer formation on wood surfaces - Peak temperatures: 800-1000°C - Total mass loss: ~25% (char residue)
Best Practices for Advanced Simulations¶
1. Multi-Mesh Alignment¶
Ensure meshes align at boundaries:
# Good: Meshes align perfectly at x=10
sim.add(Mesh(id='MESH1', ijk=Grid3D.of(50, 40, 25), xb=Bounds3D.of(0, 10, 0, 8, 0, 5)))
sim.add(Mesh(id='MESH2', ijk=Grid3D.of(50, 40, 25), xb=Bounds3D.of(10, 20, 0, 8, 0, 5)))
# Bad: Meshes overlap or have gaps
sim.add(Mesh(id='MESH1', ijk=Grid3D.of(50, 40, 25), xb=Bounds3D.of(0, 10, 0, 8, 0, 5)))
sim.add(Mesh(id='MESH2', ijk=Grid3D.of(50, 40, 25), xb=Bounds3D.of(9.9, 20, 0, 8, 0, 5)) # Gap/overlap
2. Appropriate Fire Growth¶
Use realistic fire growth curves:
# Realistic t-squared growth
sim.add(Ramp(id='GROWTH', t=[0, 100, 200, 300], f=[0, 0.33, 0.67, 1.0]))
# Avoid instantaneous fires (unless justified)
sim.add(Ramp(id='INSTANT', t=[0, 1], f=[0, 1]) # Usually unrealistic
3. Adequate Device Coverage¶
# Good: Distributed measurements
for x in range(2, 9, 2):
for y in range(2, 7, 2):
sim.add(Device(id=f'T_X{x}Y{y}', quantity='TEMPERATURE', xyz=Point3D.of(x, y, 2)))
# Poor: Single measurement point
sim.add(Device(id='TEMP', quantity='TEMPERATURE', xyz=Point3D.of(5, 4, 2)))
Next Steps¶
- User Guide - Detailed feature documentation
- Parametric Studies - Sensitivity analysis
- Workflows - Complete simulation workflows
- API Reference - Full API documentation