Skip to main content

FDSVisMap

PyPI version Code Quality License Python Versions type checked with mypy


FDSVisMap is a Python tool for the assessment of the visibility of safety signs along routes of egress in the context of performance-based fire safety design.

It provides methods for analyzing and visualizing visibility maps (Vismaps) derived from FDS (Fire Dynamics Simulator) output data.


Installation & Setup

pip install fdsvismap

Installation with uv

# Install fdsvismap in editable mode with its dependencies and the dev dependency group
uv sync

# Additionally install the dependencies for building the documentation
uv sync --extra docs

Running Tests Locally

To run all quality checks (linting, formatting, type checking) as well as the tests, use the following commands:

uv run pre-commit run --all-files
uv run pytest

On Linux and macOS, or with Git Bash on Windows, ./scripts/ci.sh runs both steps at once.

Citation

To cite this work refer to

@article{BORGER2024104269,
title = {A waypoint based approach to visibility in performance based fire safety design},
author = {Kristian Börger and Alexander Belt and Lukas Arnold},
journal = {Fire Safety Journal},
volume = {150},
pages = {104269},
year = {2024},
issn = {0379-7112},
doi = {https://doi.org/10.1016/j.firesaf.2024.104269},
url = {https://www.sciencedirect.com/science/article/pii/S0379711224001826},
}

FDS Slice File (SLCF) Requirements

FDSVisMap requires specific slice file data from your FDS simulation. The tool uses soot extinction coefficient or soot optical density to calculate visibility.

Required FDS Input

Add the following to your FDS input file (.fds):

&SLCF QUANTITY='EXTINCTION COEFFICIENT', CELL_CENTERED=T, PBZ=2.0 /

Or for optical density:

&SLCF QUANTITY='OPTICAL DENSITY', CELL_CENTERED=T, PBZ=2.0 /
  • PBZ=2.0 sets the z-coordinate (position) of the slice plane (adjust as needed).
  • The slice plane height (z-coordinate, corresponding to PBZ in FDS) is selected in Python via fds_slc_height. The horizontal slice closest to that height is read.
  • CELL_CENTERED=T writes the values at the cell centres, as in the examples of this repository.
  • In the FDS output, these quantities are named SOOT EXTINCTION COEFFICIENT and SOOT OPTICAL DENSITY. FDS does not accept these names in the input file.
  • If smoke is defined as a separate species (SPEC_ID), the quantity is named after the species, e.g. MY SMOKE EXTINCTION COEFFICIENT. Select such a slice by its ID with fds_slc_id, or by its index in fdsreader.Simulation(sim_dir).slices with fds_slc_index if it has no ID. The error message for a missing slice lists the slices with their index.

Supported Quantities

Python quantity value Quantity in the FDS output
ext_coef_C0.9H0.1 (default) SOOT EXTINCTION COEFFICIENT
ext_coef_C SOOT EXTINCTION COEFFICIENT
OD_C SOOT OPTICAL DENSITY
OD_C0.9H0.1 SOOT OPTICAL DENSITY

You can set vis.quantity either to these Python-side names (recommended) or to the FDS quantity names (for example, 'EXTINCTION COEFFICIENT', 'SOOT EXTINCTION COEFFICIENT', 'OPTICAL DENSITY' or 'SOOT OPTICAL DENSITY'); all of them are accepted as aliases.

vis = VisMap()

# Default: uses 'SOOT EXTINCTION COEFFICIENT' (Python-side name: "ext_coef_C0.9H0.1")
vis.read_fds_data(sim_dir, fds_slc_height=2.0)

# Or explicitly set the quantity using the Python-side name
vis.quantity = "ext_coef_C"
vis.read_fds_data(sim_dir, fds_slc_height=2.0)

# Or use optical density (Python-side name: "OD_C")
vis.quantity = "OD_C"
vis.read_fds_data(sim_dir, fds_slc_height=2.0)

Integration with fdsreader

FDSVisMap uses fdsreader internally to read FDS output files. The read_fds_data() method automatically handles:

  • Loading the simulation directory via fds.Simulation(sim_dir)
  • Finding the appropriate slice file by quantity and height
  • Extracting grid coordinates and time points

No manual fdsreader usage is required.

Usage Example

The following script is part of the repository as examples/room_fire/room_fire.py, together with the FDS output of the example in examples/room_fire/fds_data. The FDS output is stored with Git LFS, which has to be installed to get the data when cloning the repository. All paths refer to the directory of the script, so it runs from any working directory on Linux, macOS and Windows and saves the plots next to itself:

python examples/room_fire/room_fire.py
"""Example script to create visibility maps."""

import time
from pathlib import Path

import matplotlib.pyplot as plt

from fdsvismap import VisMap

# All paths refer to the directory of this script, so the example runs from any working directory.
example_dir = Path(__file__).parent
sim_dir = example_dir / "fds_data"
bg_img = example_dir / "misc" / "floorplan.png"

# Create instance of VisMap class.
vis = VisMap()

# Read data from FDS simulation directory.
vis.read_fds_data(str(sim_dir), fds_slc_height=2)

# Add background image, extent is the position of its edges as (x_min, x_max, y_min, y_max) in FDS coordinates.
# The image may also extend beyond the simulation domain.
vis.add_background_image(str(bg_img), extent=(0, 20, 0, 10))

# Add the safety signs with their contrast factor c and their viewing direction alpha, measured clockwise from the
# positive y-axis. Use alpha="omni" for a sign that is visible from all directions.
vis.add_sign(1, 8.4, 4.8, 3, 0)
vis.add_sign(2, 9.8, 4, 3, 270)
vis.add_sign(3, 17, 10, 3, 180)

# Add the route of egress along its waypoints, starting at the first one. The route does not have to pass the
# signs, it is enough to see them.
vis.add_route(
    "exit route",
    [(1, 9), (4, 7), (7, 5.5), (9.5, 4.2), (11, 4.2), (15, 6), (17, 9.5)],
    signs=[1, 2, 3],
)

# Set times when the simulation should be evaluated.
times = range(0, 500, 50)
vis.set_time_points(times)

# Add a visual obstruction that affects visibility calculations.
vis.add_visual_obstruction(8, 8.8, 4.6, 4.8)

# Plot the input, the routes with their signs and the obstructions the calculation knows about. This needs no
# calculation and saves the plot as pdf next to this script.
fig, ax = vis.plot_routes(plot_obstructions=True)
routes_file = example_dir / "routes.pdf"
fig.savefig(routes_file, dpi=300)
plt.close(fig)
print(f"Routes and signs saved as '{routes_file}'.")

# Do the required calculations to create the Vismap, progress=True shows progress bars.
print("Starting computation...")
start_time = time.perf_counter()
vis.compute_all(progress=True)
print(f"Computation completed in {time.perf_counter() - start_time:.2f} seconds.")

# Plot ASET map of the route and save it as pdf next to this script.
fig, ax = vis.plot_aset_map(route_id="exit route", plot_obstructions=True)
aset_map_file = example_dir / "aset_map.pdf"
fig.savefig(aset_map_file, dpi=300)
plt.close(fig)
print(f"ASET map saved as '{aset_map_file}'.")

# Plot the time aggregated Vismap of the route and save it as pdf next to this script.
fig, ax = vis.plot_time_agg_vismap(route_id="exit route")
time_agg_vismap_file = example_dir / "time_agg_vismap.pdf"
fig.savefig(time_agg_vismap_file, dpi=300)
plt.close(fig)
print(f"Time aggregated Vismap saved as '{time_agg_vismap_file}'.")

# Plot the Vismaps at a single time point side by side, for the whole route and for one sign only. On the map of
# the route, its sections are colored by whether one of its signs is visible from them.
vismap_time = 300
fig, axes = plt.subplots(1, 2, figsize=(12, 4), layout="compressed")
vis.plot_route_vismap("exit route", vismap_time, ax=axes[0])
axes[0].set_title(f"Route at {vismap_time} s")
vis.plot_sign_vismap(2, vismap_time, ax=axes[1])
axes[1].set_title(f"Sign 2 at {vismap_time} s")
vismap_file = example_dir / f"vismap_{vismap_time}s.pdf"
fig.savefig(vismap_file, dpi=300)
plt.close(fig)
print(f"Vismaps at {vismap_time} s saved as '{vismap_file}'.")

# Set parameters for local evaluations.
simulation_time = 450
x = 2
y = 4
c = 3
sign_id = 2

print()

# Check if a sign is visible from given location at given time.
sign_is_visible = vis.sign_is_visible(simulation_time, x, y, sign_id)
print(
    f"Is sign {sign_id} visible at {simulation_time} s at coordinates X/Y = ({x},{y})?: {sign_is_visible}"
)

# Get distance from a sign to given location.
distance_to_sign = vis.get_distance_to_sign(x, y, sign_id)
print(
    f"The distance from sign {sign_id} to location X/Y = ({x},{y}) is {distance_to_sign} m."
)

# Calculate local visibility at given location and time, considering a specific c factor.
local_visibility = vis.get_local_visibility(simulation_time, x, y, c)
print(
    f"The local visibility at time {simulation_time} s and location X/Y = ({x},{y}) is {local_visibility:.2f} m."
)

# Calculate visibility at given location and time relative to a sign, considering a specific c factor.
visibility = vis.get_visibility_to_sign(simulation_time, x, y, sign_id)
print(
    f"The visibility at time {simulation_time} s and location X/Y = ({x},{y}) relative to sign {sign_id} is {visibility:.2f} m."
)

# Evaluate the route as a whole: the first time at which each of its sections is without a visible sign.
route_aset = vis.get_route_aset("exit route")
print(
    f"The first section of the route loses its sign after {route_aset.min():.0f} s, "
    f"the last one after {route_aset.max():.0f} s."
)

Metadata

Release files for fdsvismap 0.3.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fdsvismap 0.3.2
File Size Uploaded
fdsvismap-0.3.2.tar.gz 49.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fdsvismap 0.3.2
File Interpreter ABI Platform
fdsvismap-0.3.2-py3-none-any.whl Python 3 none any Details

Total release size: 84.1 kB

Release files / fdsvismap-0.3.2.tar.gz

Download URL fdsvismap-0.3.2.tar.gz
Size 49.2 kB
Tags Source
SHA-256 checksum
How to use checksums
bb56cb7214742003acc562a6e34a7ccc0ac65e98e1376cd572cc5a1c431e16a0
BLAKE2b-256 checksum
How to use checksums
12f8a94ae83a3b4ba2f891e22e5203b30baae875a6356cb30b5ef4d8b7d02838
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.25

Release files / fdsvismap-0.3.2-py3-none-any.whl

Download URL fdsvismap-0.3.2-py3-none-any.whl
Size 35.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6bbe6ab90950c398a1069ee35063f79cc8088c3a242905bf41f765419fac264a
BLAKE2b-256 checksum
How to use checksums
9bea53369b447b677746f8aee278b2874e983b281dec0d3c389651225d9c9396
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.25

Release history Release notifications | RSS feed

This release

0.3.2 This release

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page