Skip to content

Commit c7cb3fd

Browse files
committed
fix: parse DEVC csv header names with a real CSV parser
FDS only quotes a device name in the _devc.csv header when it contains a comma or space. The previous parser split on ',"', which found no split points at all when none of the names needed quoting (e.g. plain identifiers like TC_Door_Single), causing the entire header line to be treated as a single device name and raising StopIteration when looking it up. Found while verifying compatibility against FDS 6.11.1; not version-specific, it can happen with any FDS version whenever no device name needs quoting.
1 parent a07d36a commit c7cb3fd

2 files changed

Lines changed: 54 additions & 1 deletion

File tree

fdsreader/simulation.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import csv
12
import glob
23
import logging
34
import os
@@ -1023,7 +1024,10 @@ def _register_device(self, smv_file: TextIO) -> Tuple[str, Device]:
10231024
def _load_DEVC_data(self):
10241025
with open(self.devc_path) as infile:
10251026
units = infile.readline().split(",")
1026-
names = [name.replace('"', "").replace("\n", "").strip() for name in infile.readline().split(',"')]
1027+
# Device names are only quoted by FDS when they contain a comma or space, so a plain
1028+
# split on "," or ',"' would misparse a header where none (or all) of the names need
1029+
# quoting. Use a real CSV parser instead so it doesn't matter which fields are quoted.
1030+
names = [name.strip() for name in next(csv.reader([infile.readline()]))]
10271031
values = np.genfromtxt(infile, delimiter=",", dtype=np.float32, autostrip=True)
10281032
for k in range(len(names)):
10291033
if isinstance(self.devices[names[k]], list):

tests/test_devc_csv_parsing.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""Regression test for the DEVC csv header parsing: FDS only quotes device names that
2+
contain a comma or space. When none of the names in a given run need quoting (which
3+
happens to be common, e.g. plain identifiers like "TC_Door_Single"), the previous
4+
ad-hoc `split(',"')` parser found no quote-comma boundaries at all and treated the
5+
entire header line as a single device name, raising StopIteration."""
6+
7+
import numpy as np
8+
9+
from fdsreader.devc import Device, DeviceCollection
10+
from fdsreader.simulation import Simulation
11+
from fdsreader.utils import Quantity
12+
13+
14+
def _make_sim(tmp_path, header_line: str, device_ids):
15+
csv_path = tmp_path / "test_devc.csv"
16+
units = ",".join(["s"] + ["C"] * len(device_ids))
17+
csv_path.write_text(f"{units}\n{header_line}\n0.0,1.0,2.0\n1.0,1.5,2.5\n")
18+
19+
devices = {
20+
device_id: Device(device_id, Quantity("TEMPERATURE", "TEMP", ""), (0.0, 0.0, 0.0), (0.0, 0.0, 0.0))
21+
for device_id in device_ids
22+
}
23+
devices["Time"] = Device("Time", Quantity("TIME", "TIME", "s"), (0.0, 0.0, 0.0), (0.0, 0.0, 0.0))
24+
25+
sim = object.__new__(Simulation)
26+
sim.devc_path = str(csv_path)
27+
sim._devices = devices
28+
sim.devices = DeviceCollection(devices.values())
29+
return sim
30+
31+
32+
def test_devc_csv_with_no_quoted_names(tmp_path):
33+
# None of these names need quoting (no commas/spaces), so FDS writes them unquoted.
34+
sim = _make_sim(tmp_path, "Time,TC_Door_Single,BP_Door_Single", ["TC_Door_Single", "BP_Door_Single"])
35+
sim._load_DEVC_data()
36+
37+
assert np.array_equal(sim._devices["Time"]._data, [0.0, 1.0])
38+
assert np.array_equal(sim._devices["TC_Door_Single"]._data, [1.0, 1.5])
39+
assert np.array_equal(sim._devices["BP_Door_Single"]._data, [2.0, 2.5])
40+
41+
42+
def test_devc_csv_with_mixed_quoted_and_unquoted_names(tmp_path):
43+
# "HGL Temp" needs quoting (space), "BP_Door_Single" doesn't.
44+
sim = _make_sim(tmp_path, 'Time,"HGL Temp",BP_Door_Single', ["HGL Temp", "BP_Door_Single"])
45+
sim._load_DEVC_data()
46+
47+
assert np.array_equal(sim._devices["Time"]._data, [0.0, 1.0])
48+
assert np.array_equal(sim._devices["HGL Temp"]._data, [1.0, 1.5])
49+
assert np.array_equal(sim._devices["BP_Door_Single"]._data, [2.0, 2.5])

0 commit comments

Comments
 (0)