fdsreader 1.2.0rc2__tar.gz → 1.3.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/LICENSE +0 -0
  2. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/MANIFEST.in +0 -0
  3. {fdsreader-1.2.0rc2/fdsreader.egg-info → fdsreader-1.3.7}/PKG-INFO +2 -2
  4. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/README.md +0 -0
  5. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/__init__.py +0 -0
  6. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/_version.py +1 -1
  7. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/bndf/ObstructionCollection.py +0 -0
  8. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/bndf/__init__.py +0 -0
  9. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/bndf/obstruction.py +14 -9
  10. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/bndf/utils.py +0 -0
  11. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/evac/EvacCollection.py +76 -46
  12. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/evac/__init__.py +0 -0
  13. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/evac/evacuation.py +33 -5
  14. fdsreader-1.3.7/fdsreader/export/__init__.py +3 -0
  15. fdsreader-1.3.7/fdsreader/export/obst_exporter.py +116 -0
  16. fdsreader-1.3.7/fdsreader/export/slcf_exporter.py +63 -0
  17. fdsreader-1.3.7/fdsreader/export/smoke3d_exporter.py +70 -0
  18. {fdsreader-1.2.0rc2/fdsreader/utils → fdsreader-1.3.7/fdsreader}/fds_classes/MeshCollection.py +1 -1
  19. fdsreader-1.3.7/fdsreader/fds_classes/__init__.py +7 -0
  20. {fdsreader-1.2.0rc2/fdsreader/utils → fdsreader-1.3.7/fdsreader}/fds_classes/mesh.py +45 -4
  21. {fdsreader-1.2.0rc2/fdsreader/utils → fdsreader-1.3.7/fdsreader}/fds_classes/surface.py +0 -0
  22. {fdsreader-1.2.0rc2/fdsreader/utils → fdsreader-1.3.7/fdsreader}/fds_classes/ventilation.py +2 -1
  23. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/geom/GeometryCollection.py +0 -0
  24. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/geom/__init__.py +0 -0
  25. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/geom/geometry.py +2 -1
  26. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/isof/IsosurfaceCollection.py +0 -0
  27. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/isof/__init__.py +0 -0
  28. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/isof/isosurface.py +2 -1
  29. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/part/ParticleCollection.py +1 -1
  30. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/part/__init__.py +0 -0
  31. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/part/particle.py +2 -1
  32. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/pl3d/Plot3DCollection.py +0 -0
  33. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/pl3d/__init__.py +0 -0
  34. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/pl3d/pl3d.py +8 -1
  35. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/settings.py +0 -0
  36. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/simulation.py +100 -20
  37. fdsreader-1.3.7/fdsreader/slcf/GeomSliceCollection.py +62 -0
  38. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/slcf/SliceCollection.py +1 -1
  39. fdsreader-1.3.7/fdsreader/slcf/__init__.py +7 -0
  40. fdsreader-1.3.7/fdsreader/slcf/geomslice.py +458 -0
  41. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/slcf/slice.py +137 -25
  42. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/smoke3d/Smoke3DCollection.py +1 -1
  43. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/smoke3d/__init__.py +0 -0
  44. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/smoke3d/smoke3d.py +11 -5
  45. fdsreader-1.3.7/fdsreader/utils/__init__.py +7 -0
  46. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/utils/data.py +25 -0
  47. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/utils/dimension.py +0 -0
  48. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/utils/extent.py +13 -10
  49. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/utils/fortran_data.py +0 -0
  50. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader/utils/misc.py +0 -0
  51. {fdsreader-1.2.0rc2 → fdsreader-1.3.7/fdsreader.egg-info}/PKG-INFO +2 -2
  52. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader.egg-info/SOURCES.txt +12 -6
  53. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader.egg-info/dependency_links.txt +0 -0
  54. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader.egg-info/requires.txt +0 -0
  55. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/fdsreader.egg-info/top_level.txt +0 -0
  56. fdsreader-1.3.7/requirements.txt +118 -0
  57. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/setup.cfg +0 -0
  58. {fdsreader-1.2.0rc2 → fdsreader-1.3.7}/setup.py +2 -2
  59. fdsreader-1.2.0rc2/fdsreader/slcf/__init__.py +0 -3
  60. fdsreader-1.2.0rc2/fdsreader/utils/__init__.py +0 -15
  61. fdsreader-1.2.0rc2/fdsreader/utils/fds_classes/__init__.py +0 -1
  62. fdsreader-1.2.0rc2/requirements.txt +0 -113
File without changes
File without changes
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fdsreader
3
- Version: 1.2.0rc2
3
+ Version: 1.3.7
4
4
  Summary: Python reader for data generated by FDS.
5
- Home-page: https://github.com/JanVogelsang/fdsreader
5
+ Home-page: https://github.com/FireDynamics/fdsreader
6
6
  Author: FZJ IAS-7 (Prof. Dr. Lukas Arnold, Jan Vogelsang)
7
7
  Author-email: j.vogelsang@fz-juelich.de
8
8
  License: UNKNOWN
File without changes
@@ -7,5 +7,5 @@ Provides fdsreader version information.
7
7
 
8
8
  from incremental import Version
9
9
 
10
- __version__ = Version('fdsreader', 1, 2, 0, release_candidate=2)
10
+ __version__ = Version('fdsreader', 1, 3, 7)
11
11
  __all__ = ["__version__"]
@@ -3,7 +3,7 @@ from typing import List, Dict, Tuple, Union, Sequence
3
3
  from typing_extensions import Literal
4
4
  import numpy as np
5
5
 
6
- from fdsreader.utils import Surface, Extent, Quantity, Dimension
6
+ from fdsreader.utils import Extent, Quantity, Dimension
7
7
  import fdsreader.utils.fortran_data as fdtype
8
8
  from fdsreader import settings
9
9
 
@@ -149,12 +149,12 @@ class SubObstruction:
149
149
 
150
150
  :ivar extent: :class:`Extent` object containing 3-dimensional extent information.
151
151
  :ivar bound_indices: Indices used to define obstruction bounds in terms of mesh locations.
152
- :ivar side_surfaces: Tuple of six surfaces for each side of the cuboid.
152
+ :ivar side_surfaces: Tuple of six :class:`Surface` s for each side of the cuboid.
153
153
  :ivar hide_times: List with points in time from when on the SubObstruction will be hidden.
154
154
  :ivar show_times: List with points in time from when on the SubObstruction will be shown.
155
155
  """
156
156
 
157
- def __init__(self, side_surfaces: Tuple[Surface, ...], bound_indices: Tuple[int, int, int, int, int, int],
157
+ def __init__(self, side_surfaces: Tuple, bound_indices: Tuple[int, int, int, int, int, int],
158
158
  extent: Extent):
159
159
  self.extent = extent
160
160
  self.side_surfaces = side_surfaces
@@ -167,15 +167,20 @@ class SubObstruction:
167
167
  self.hide_times = list()
168
168
  self.show_times = list()
169
169
 
170
- def _add_patches(self, bid: int, cell_centered: bool, quantity: str, short_name: str, unit: str, patches: List[Patch],
171
- times: np.ndarray, n_t: int, lower_bounds: np.ndarray, upper_bounds: np.ndarray):
170
+ def _add_patches(self, bid: int, cell_centered: bool, quantity: str, short_name: str, unit: str,
171
+ patches: List[Patch], times: np.ndarray, n_t: int, lower_bounds: np.ndarray,
172
+ upper_bounds: np.ndarray):
172
173
  if bid not in self._boundary_data:
173
- self._boundary_data[bid] = Boundary(Quantity(quantity, short_name, unit), cell_centered, times, n_t, patches,
174
- lower_bounds, upper_bounds)
174
+ self._boundary_data[bid] = Boundary(Quantity(quantity, short_name, unit), cell_centered, times, n_t,
175
+ patches, lower_bounds, upper_bounds)
175
176
 
176
177
  if not settings.LAZY_LOAD:
177
178
  _ = self._boundary_data[bid].data
178
179
 
180
+ @property
181
+ def has_boundary_data(self):
182
+ return len(self._boundary_data) != 0
183
+
179
184
  def get_data(self, quantity: Union[str, Quantity]):
180
185
  if type(quantity) == Quantity:
181
186
  quantity = quantity.name
@@ -303,7 +308,7 @@ class Obstruction:
303
308
  if type(quantity) == Quantity:
304
309
  quantity = quantity.name
305
310
 
306
- ret = [subobst.get_data(quantity) for subobst in self._subobstructions]
311
+ ret = [subobst.get_data(quantity) for subobst in self._subobstructions if subobst.has_boundary_data]
307
312
  if orientation == 0:
308
313
  return ret
309
314
  return [bndf for bndf in ret if orientation in bndf.data.keys()]
@@ -312,7 +317,7 @@ class Obstruction:
312
317
  def has_boundary_data(self):
313
318
  """Whether boundary data has been output in the simulation.
314
319
  """
315
- return len(self._subobstructions[0]._boundary_data) != 0
320
+ return any(subobst.has_boundary_data for subobst in self._subobstructions)
316
321
 
317
322
  def clear_cache(self):
318
323
  """Remove all data from the internal cache that has been loaded so far to free memory.
@@ -4,15 +4,15 @@ from typing import Iterable, Dict, List, Union
4
4
  import numpy as np
5
5
 
6
6
  from fdsreader.evac import Evacuation
7
- from fdsreader.utils import Mesh
7
+ from fdsreader.fds_classes import Mesh
8
8
  from fdsreader.utils.data import FDSDataCollection, Quantity
9
9
  import fdsreader.utils.fortran_data as fdtype
10
10
  from fdsreader import settings
11
11
 
12
12
 
13
13
  class EvacCollection(FDSDataCollection):
14
- """Collection of :class:`Plot3D` objects. Offers extensive functionality for filtering and
15
- using plot3Ds as well as its subclasses such as :class:`SubPlot3D`.
14
+ """Collection of :class:`Evacuation` objects. Next to agent-class specific data (such as trajectories) lots of
15
+ other data such as FED-data is provided via this class.
16
16
 
17
17
  :ivar times: List of all time steps of the simulation.
18
18
  :ivar z_offsets: The offset in z-direction for each mesh where the evac plane lays.
@@ -92,8 +92,8 @@ class EvacCollection(FDSDataCollection):
92
92
  units[i] == "TargetDoorCounter"}
93
93
 
94
94
  @property
95
- def xyz(self) -> np.ndarray:
96
- """
95
+ def xyz(self) -> List[np.ndarray]:
96
+ """List of xyz-data for each mesh.
97
97
  """
98
98
  if not hasattr(self, "_xyz"):
99
99
  self._load_xyz_data()
@@ -121,18 +121,26 @@ class EvacCollection(FDSDataCollection):
121
121
  n_corrs = meta[2]
122
122
  n_devc = fdtype.read(infile, fdtype.INT, 1)[0][0][0]
123
123
 
124
- n_i, n_j, n_k, n = fdtype.read(infile, dtype_grid_meta, 1)[0][0] # n_k will always be 1
124
+ n_i = list()
125
+ n_j = list()
126
+ for g in range(n_grids):
127
+ n_i_g, n_j_g, _, _ = fdtype.read(infile, dtype_grid_meta, 1)[0][0]
128
+ n_i.append(n_i_g)
129
+ n_j.append(n_j_g)
130
+ for i in range(n_i_g):
131
+ for j in range(n_j_g):
132
+ _ = fdtype.read(infile, dtype_grid_data, 1)
125
133
 
126
134
  # Reset pointer (after file header)
127
135
  infile.seek(fdtype.INT.itemsize * 2 + dtype_meta.itemsize)
128
136
 
129
- self._xyz = np.empty((n_grids, n_i, n_j, 3))
137
+ self._xyz = [np.empty((n_i[g], n_j[g], 3)) for g in range(n_grids)]
130
138
 
131
139
  for g in range(n_grids):
132
140
  infile.seek(dtype_grid_meta.itemsize, 1)
133
- for i in range(n_i):
134
- for j in range(n_j):
135
- self._xyz[g, i, j] = fdtype.read(infile, dtype_grid_data, 1)[0][0]
141
+ for i in range(n_i[g]):
142
+ for j in range(n_j[g]):
143
+ self._xyz[g][i, j] = fdtype.read(infile, dtype_grid_data, 1)[0][0]
136
144
 
137
145
  if os.path.exists(self._base_path + ".fed"):
138
146
  self._load_fed_data(n_corrs)
@@ -183,6 +191,9 @@ class EvacCollection(FDSDataCollection):
183
191
  dtype_meta = fdtype.new((('i', 6),))
184
192
  dtype_time = fdtype.new((('f', 2),))
185
193
  dtype_grid_meta = fdtype.new((('i', 4),))
194
+ dtype_corr = fdtype.new((('f', 8),))
195
+ dtype_devs_meta = fdtype.new((('i', 2),))
196
+ dtype_devs_data = fdtype.new((('i', 1), ('f', 1), ('i', 2), ('f', 1)))
186
197
 
187
198
  # File header
188
199
  file_format = fdtype.read(infile, fdtype.INT, 1)[0][0][0]
@@ -195,26 +206,35 @@ class EvacCollection(FDSDataCollection):
195
206
 
196
207
  # Read gridsize from first timestep header
197
208
  infile.seek(dtype_time.itemsize, 1)
198
- n_i, n_j, n_k, n = fdtype.read(infile, dtype_grid_meta, 1)[0][0] # n_k will always be 1
209
+
210
+ n_i = list()
211
+ n_j = list()
212
+ n = list()
213
+ dtype_grid_data = list()
214
+ for g in range(n_grids):
215
+ n_i_g, n_j_g, _, n_g = fdtype.read(infile, dtype_grid_meta, 1)[0][0]
216
+ n_i.append(n_i_g)
217
+ n_j.append(n_j_g)
218
+ n.append(n_g)
219
+ dtype_grid_data.append(fdtype.new((('f', n_g),)))
220
+ for i in range(n_i_g):
221
+ for j in range(n_j_g):
222
+ _ = fdtype.read(infile, dtype_grid_data[-1], 1)
199
223
 
200
224
  # Reset pointer (after file header)
201
225
  infile.seek(dtype_meta.itemsize + fdtype.INT.itemsize * 2)
202
226
 
203
- dtype_grid_data = fdtype.new((('f', n),))
204
- dtype_corr = fdtype.new((('f', 8),))
205
- dtype_devs_meta = fdtype.new((('i', 2),))
206
- dtype_devs_data = fdtype.new((('i', 1), ('f', 1), ('i', 2), ('f', 1)))
207
-
208
- n_t = (os.stat(file_path).st_size - (fdtype.INT.itemsize * 2 + dtype_meta.itemsize)) // \
209
- (dtype_time.itemsize + n_grids * (dtype_grid_meta.itemsize + n_i * n_j * dtype_grid_data.itemsize) +
210
- n_corrs * dtype_corr.itemsize + fdtype.FLOAT.itemsize + n_devc * (
211
- dtype_devs_meta.itemsize + dtype_devs_data.itemsize))
227
+ n_t = (os.stat(file_path).st_size - (fdtype.INT.itemsize * 2 + dtype_meta.itemsize)) // (
228
+ dtype_time.itemsize + sum(
229
+ (dtype_grid_meta.itemsize + n_i[g] * n_j[g] * dtype_grid_data[g].itemsize) for g in
230
+ range(n_grids)) + n_corrs * dtype_corr.itemsize + fdtype.FLOAT.itemsize + n_devc * (
231
+ dtype_devs_meta.itemsize + dtype_devs_data.itemsize))
212
232
 
213
233
  times = list()
214
- self._fed_grid = dict(co_co2_o2=[np.empty((n_t, n_i, n_j)) for _ in range(n_grids)],
215
- soot_dens=[np.empty((n_t, n_i, n_j)) for _ in range(n_grids)],
216
- tmp_g=[np.empty((n_t, n_i, n_j)) for _ in range(n_grids)],
217
- radflux=[np.empty((n_t, n_i, n_j)) for _ in range(n_grids)])
234
+ self._fed_grid = dict(co_co2_o2=[np.empty((n_t, n_i[g], n_j[g])) for g in range(n_grids)],
235
+ soot_dens=[np.empty((n_t, n_i[g], n_j[g])) for g in range(n_grids)],
236
+ tmp_g=[np.empty((n_t, n_i[g], n_j[g])) for g in range(n_grids)],
237
+ radflux=[np.empty((n_t, n_i[g], n_j[g])) for g in range(n_grids)])
218
238
  self._fed_corr = dict(co_co2_o2=[np.empty((n_t, 2)) for _ in range(n_corrs)],
219
239
  soot_dens=[np.empty((n_t, 2)) for _ in range(n_corrs)],
220
240
  tmp_g=[np.empty((n_t, 2)) for _ in range(n_corrs)],
@@ -229,9 +249,9 @@ class EvacCollection(FDSDataCollection):
229
249
 
230
250
  for g in range(n_grids):
231
251
  infile.seek(dtype_grid_meta.itemsize, 1)
232
- for i in range(n_i):
233
- for j in range(n_j):
234
- co_co2_o2, soot_dens, tmp_g, radflux = fdtype.read(infile, dtype_grid_data, 1)[0][0][:4]
252
+ for i in range(n_i[g]):
253
+ for j in range(n_j[g]):
254
+ co_co2_o2, soot_dens, tmp_g, radflux = fdtype.read(infile, dtype_grid_data[g], 1)[0][0][:4]
235
255
  self._fed_grid["co_co2_o2"][g][t, i, j] = co_co2_o2
236
256
  self._fed_grid["soot_dens"][g][t, i, j] = soot_dens
237
257
  self._fed_grid["tmp_g"][g][t, i, j] = tmp_g
@@ -267,29 +287,34 @@ class EvacCollection(FDSDataCollection):
267
287
  def _load_eff_data(self):
268
288
  file_path = self._base_path + ".eff"
269
289
  if not os.path.exists(file_path):
290
+ self._eff = None
270
291
  return
271
292
 
272
293
  with open(file_path, 'rb') as infile:
273
294
  dtype_grid_meta = fdtype.new((('i', 3),))
274
- dtype_grid_data = fdtype.new((('f', 2),))
295
+ dtype_grid_data = fdtype.new((('f', 2),)) # u, v
275
296
 
276
297
  n_grids = fdtype.read(infile, fdtype.INT, 1)[0][0][0]
277
298
 
278
- n_i, n_j, n_k = fdtype.read(infile, dtype_grid_meta, 1)[0][0] # n_k will always be 1
279
-
280
- # Reset pointer (after file header)
281
- infile.seek(fdtype.INT.itemsize)
282
-
283
- n_fields = (os.stat(file_path).st_size - fdtype.INT.itemsize) // (
284
- n_grids * (dtype_grid_meta.itemsize + n_i * n_j * dtype_grid_data.itemsize))
285
-
286
- self._eff = np.empty((n_grids, n_fields, n_i, n_j, 2))
287
- for g in range(n_grids):
288
- for f in range(n_fields):
289
- infile.seek(dtype_grid_meta.itemsize, 1)
290
- for i in range(n_i):
291
- for j in range(n_j):
292
- self._eff[g, f, i, j] = fdtype.read(infile, dtype_grid_data, 1)[0][0]
299
+ # n_fields = (os.stat(file_path).st_size - fdtype.INT.itemsize) // (
300
+ # sum(dtype_grid_meta.itemsize + n_i[g] * n_j[g] * dtype_grid_data.itemsize for g in range(n_grids)))
301
+ #
302
+ # self._eff = np.empty((n_grids, n_fields, n_i, n_j, 2))
303
+ # for g in range(n_grids):
304
+ # for f in range(n_fields):
305
+ # infile.seek(dtype_grid_meta.itemsize, 1)
306
+ # for i in range(n_i[g]):
307
+ # for j in range(n_j[g]):
308
+ # self._eff[g, f, i, j] = fdtype.read(infile, dtype_grid_data, 1)[0][0]
309
+
310
+ self._eff = list()
311
+ meta = fdtype.read(infile, dtype_grid_meta, 1)
312
+ while len(meta) > 0:
313
+ n_i, n_j, _ = meta[0][0]
314
+ for i in range(n_i + 2):
315
+ for j in range(n_j + 2):
316
+ self._eff.append(fdtype.read(infile, dtype_grid_data, 1)[0][0])
317
+ meta = fdtype.read(infile, dtype_grid_meta, 1)
293
318
 
294
319
  def get_unfiltered_positions(self, quantity: Union[Quantity, str] = None):
295
320
  """Convenience function that combines all trajectories into a single array.
@@ -357,7 +382,10 @@ class EvacCollection(FDSDataCollection):
357
382
  for quantity in evac.quantities:
358
383
  evac._data[quantity.name].append(np.empty((size,), dtype=np.float32))
359
384
  evac._positions.append(np.empty((size, 3), dtype=np.float32))
360
- evac._ap.append(np.empty((size, 4), dtype=np.float32))
385
+ evac._body_angles.append(np.empty((size,), dtype=np.float32))
386
+ evac._semi_major_axis.append(np.empty((size,), dtype=np.float32))
387
+ evac._semi_minor_axis.append(np.empty((size,), dtype=np.float32))
388
+ evac._agent_heights.append(np.empty((size,), dtype=np.float32))
361
389
  evac._tags.append(np.empty((size,), dtype=int))
362
390
 
363
391
  for mesh, file_path in self._file_paths.items():
@@ -385,8 +413,10 @@ class EvacCollection(FDSDataCollection):
385
413
  pos = fdtype.read(infile, dtype_positions, 1)[0][0].reshape((n_humans, 7),
386
414
  order='F').astype(float)
387
415
  evac._positions[t][offset: offset + n_humans] = pos[:, :3]
388
- # Todo: What might this be for?
389
- evac._ap[t][offset: offset + n_humans] = pos[:, 3:]
416
+ evac._body_angles[t][offset: offset + n_humans] = pos[:, 3]
417
+ evac._semi_major_axis[t][offset: offset + n_humans] = pos[:, 4]
418
+ evac._semi_minor_axis[t][offset: offset + n_humans] = pos[:, 5]
419
+ evac._agent_heights[t][offset: offset + n_humans] = pos[:, 6]
390
420
  # Read tags
391
421
  dtype_tags = fdtype.new((('i', n_humans),))
392
422
  evac._tags[t][offset: offset + n_humans] = fdtype.read(infile, dtype_tags, 1)[0][0]
@@ -2,7 +2,8 @@ from typing import List, Tuple, Dict, Sequence, Union
2
2
 
3
3
  import numpy as np
4
4
 
5
- from fdsreader.utils import Quantity, Mesh, Extent
5
+ from fdsreader.fds_classes import Mesh
6
+ from fdsreader.utils import Quantity
6
7
 
7
8
 
8
9
  # class Entrance:
@@ -54,7 +55,10 @@ class Evacuation:
54
55
  self.n_humans: Dict[Mesh, List[int]] = dict()
55
56
 
56
57
  self._positions: List[np.ndarray] = list()
57
- self._ap: List[np.ndarray] = list()
58
+ self._body_angles: List[np.ndarray] = list()
59
+ self._semi_major_axis: List[np.ndarray] = list()
60
+ self._semi_minor_axis: List[np.ndarray] = list()
61
+ self._agent_heights: List[np.ndarray] = list()
58
62
  self._tags: List[np.ndarray] = list()
59
63
  self._data: Dict[str, List[np.ndarray]] = {q.name: [] for q in self.quantities}
60
64
  self.times: Sequence[float] = list()
@@ -62,7 +66,7 @@ class Evacuation:
62
66
  self.lower_bounds = {q.name: [] for q in self.quantities}
63
67
  self.upper_bounds = {q.name: [] for q in self.quantities}
64
68
 
65
- self._init_callback = None
69
+ self._init_callback = lambda: None
66
70
 
67
71
  @property
68
72
  def id(self):
@@ -146,12 +150,36 @@ class Evacuation:
146
150
  return self._positions
147
151
 
148
152
  @property
149
- def ap(self) -> List[np.ndarray]:
153
+ def body_angles(self) -> List[np.ndarray]:
150
154
  """
151
155
  """
152
156
  if len(self._positions) == 0 and len(self._tags) == 0:
153
157
  self._init_callback()
154
- return self._ap
158
+ return self._body_angles
159
+
160
+ @property
161
+ def semi_major_axis(self) -> List[np.ndarray]:
162
+ """
163
+ """
164
+ if len(self._positions) == 0 and len(self._tags) == 0:
165
+ self._init_callback()
166
+ return self._semi_major_axis
167
+
168
+ @property
169
+ def semi_minor_axis(self) -> List[np.ndarray]:
170
+ """
171
+ """
172
+ if len(self._positions) == 0 and len(self._tags) == 0:
173
+ self._init_callback()
174
+ return self._semi_minor_axis
175
+
176
+ @property
177
+ def agent_heights(self) -> List[np.ndarray]:
178
+ """
179
+ """
180
+ if len(self._positions) == 0 and len(self._tags) == 0:
181
+ self._init_callback()
182
+ return self._agent_heights
155
183
 
156
184
  def clear_cache(self):
157
185
  """Remove all data from the internal cache that has been loaded so far to free memory.
@@ -0,0 +1,3 @@
1
+ from .smoke3d_exporter import export_smoke_raw
2
+ from .slcf_exporter import export_slcf_raw
3
+ from .obst_exporter import export_obst_raw
@@ -0,0 +1,116 @@
1
+ import os
2
+ from pathlib import Path
3
+ import numpy as np
4
+ from typing_extensions import Literal
5
+ from ..bndf import Obstruction
6
+ from ..bndf.utils import sort_patches_cartesian
7
+
8
+
9
+ def export_obst_raw(obst: Obstruction, output_dir: str, ordering: Literal['C', 'F'] = 'C'):
10
+ """Exports the 3d arrays to raw binary files with corresponding .yaml meta files.
11
+
12
+ :param smoke3d: The :class:`Smoke3D` object to export.
13
+ :param output_dir: The directory in which to save all files.
14
+ :param ordering: Whether to write the data in C or Fortran ordering.
15
+ """
16
+ from pathos.pools import ProcessPool as Pool
17
+ from multiprocess import Lock, Manager
18
+ filename_base = "obst-" + str(obst.id)
19
+ # Create all requested directories if they don't exist yet
20
+ Path(os.path.join(output_dir, filename_base + "-data")).mkdir(parents=True, exist_ok=True)
21
+
22
+ meta = {"BoundingBox": obst.bounding_box.as_list(), "QuantityNum": len(obst.quantities)}
23
+ m = Manager()
24
+ lock = m.Lock()
25
+ meta["Quantities"] = m.list()
26
+
27
+ def worker(quantity, bndf_data):
28
+ quantity_name = quantity.name.replace(" ", "_").replace(".", "-")
29
+ filename = filename_base + "_quantity-" + quantity_name + ".dat"
30
+
31
+ out = {"Quantity": quantity.name.replace(" ", "_").replace(".", "-"), "DataValMax": -100000.,
32
+ "DataValMin": 100000., "ScaleFactor": 1, "DataFile": os.path.join(filename_base + "-data", filename),
33
+ "Orientations": list()}
34
+ for bndf in bndf_data:
35
+ out["DataValMax"] = max(out["DataValMax"], np.max(bndf.upper_bounds))
36
+ out["DataValMin"] = min(out["DataValMin"], np.min(bndf.lower_bounds))
37
+ out["DataValMax"] = float(out["DataValMax"])
38
+ out["DataValMin"] = float(out["DataValMin"])
39
+ out["ScaleFactor"] = 255.0 / out["DataValMax"]
40
+
41
+ # Abort if no useful data is available
42
+ if meta[quantity]["DataValMax"] <= 0:
43
+ return
44
+
45
+ with open(os.path.join(output_dir, quantity_name, filename_base + "-data", filename), 'wb') as rawfile:
46
+ orientations = set()
47
+ for orientation in (-3, -2, -1, 1, 2, 3):
48
+ patches = list()
49
+ for bndf in bndf_data:
50
+ if orientation in bndf.data:
51
+ orientations.add(orientation)
52
+ patches.append(bndf.data[orientation])
53
+
54
+ if len(patches) == 0:
55
+ continue
56
+
57
+ # Combine patches to a single face for plotting
58
+ patches = sort_patches_cartesian(patches)
59
+
60
+ shape_dim1 = sum([patch_row[0].shape[0] for patch_row in patches])
61
+ shape_dim2 = sum([patch.shape[1] for patch in patches[0]])
62
+ n_t = patches[0][0].n_t # Number of timesteps
63
+
64
+ face = np.empty(shape=(n_t, shape_dim1, shape_dim2))
65
+ dim1_pos = 0
66
+ dim2_pos = 0
67
+ for patch_row in patches:
68
+ d1 = patch_row[0].shape[0]
69
+ for patch in patch_row:
70
+ d2 = patch.shape[1]
71
+ face[:, dim1_pos:dim1_pos + d1, dim2_pos:dim2_pos + d2] = patch.data
72
+ dim2_pos += d2
73
+ dim1_pos += d1
74
+ dim2_pos = 0
75
+
76
+ face = (face * meta["ScaleFactor"]).astype(np.uint8)
77
+
78
+ if abs(orientation) == 1:
79
+ spacing1 = (meta["BoundingBox"][3] - meta["BoundingBox"][2]) / face.shape[1]
80
+ spacing2 = (meta["BoundingBox"][5] - meta["BoundingBox"][4]) / face.shape[2]
81
+ elif abs(orientation) == 2:
82
+ spacing1 = (meta["BoundingBox"][1] - meta["BoundingBox"][0]) / face.shape[0]
83
+ spacing2 = (meta["BoundingBox"][5] - meta["BoundingBox"][4]) / face.shape[2]
84
+ else:
85
+ spacing1 = (meta["BoundingBox"][1] - meta["BoundingBox"][0]) / face.shape[0]
86
+ spacing2 = (meta["BoundingBox"][3] - meta["BoundingBox"][2]) / face.shape[1]
87
+
88
+
89
+ out["Orientations"].append({
90
+ "Orientation": orientation,
91
+ "MeshPos": f"{meta['BoundingBox'][0]:.6} {meta['BoundingBox'][2]:.6} {meta['BoundingBox'][4]:.6}",
92
+ "Spacing": f"{bndf_data[0].times[1] - bndf_data[0].times[0]:.6} {spacing1:.6} {spacing2:.6}",
93
+ "DimSize": f"{face.shape[2]} {face.shape[1]} {face.shape[2]}"
94
+ })
95
+
96
+ for d in face[:, [2, 0, 1]]: # Make time the first dimension so we can easily iterate over it
97
+ if ordering == 'F':
98
+ d = d.T
99
+ d.tofile(rawfile)
100
+
101
+ out["NumOrientations"] = len(orientations)
102
+
103
+ with lock:
104
+ meta["Quantities"].append(out)
105
+
106
+ worker_args = list()
107
+ for i, quantity in enumerate(obst.quantities):
108
+ bndf_data = obst.get_boundary_data(quantity)
109
+ worker_args.append((quantity, bndf_data))
110
+ Pool(8).map(lambda args: worker(*args), worker_args)
111
+
112
+ meta["Quantities"] = list(meta["Quantities"])
113
+
114
+ with open(os.path.join(output_dir, filename_base + ".yaml"), 'w') as metafile:
115
+ import yaml
116
+ yaml.dump(meta, metafile)
@@ -0,0 +1,63 @@
1
+ import os
2
+ from pathlib import Path
3
+ import numpy as np
4
+ from typing_extensions import Literal
5
+ from ..slcf import Slice
6
+
7
+
8
+ def export_slcf_raw(slc: Slice, output_dir: str, ordering: Literal['C', 'F'] = 'C'):
9
+ """Exports the 3d arrays to raw binary files with corresponding .yaml meta files.
10
+
11
+ :param slc: The :class:`Slice` object to export.
12
+ :param output_dir: The directory in which to save all files.
13
+ :param ordering: Whether to write the data in C or Fortran ordering.
14
+ """
15
+ from pathos.pools import ProcessPool as Pool
16
+ from multiprocess import Lock, Manager
17
+ slc2d = slc.type == '2D'
18
+ meta = {"DataValMax": float(slc.vmax), "DataValMin": float(slc.vmin), "ScaleFactor": 255. / (float(slc.vmax) - float(slc.vmin)),
19
+ "MeshNum": len(slc.subslices), "Quantity": slc.quantity.name}
20
+
21
+ filename_base = ("slice" + ("2D-" if slc2d else "3D-") + slc.id.lower()).replace(" ", "_").replace(".", "-")
22
+ # Create all requested directories if they don't exist yet
23
+ Path(os.path.join(output_dir, filename_base + "-data")).mkdir(parents=True, exist_ok=True)
24
+
25
+ m = Manager()
26
+ meta["Meshes"] = m.list()
27
+ lock = m.Lock()
28
+
29
+ def worker(mesh, subslice):
30
+ mesh_id = mesh.id.replace(" ", "_").replace(".", "-")
31
+ filename = filename_base + "_mesh-" + mesh_id + ".dat"
32
+
33
+ data = ((subslice.data - meta["DataValMin"]) * meta["ScaleFactor"]).astype(np.uint8)
34
+ shape = data.shape
35
+ if slc2d:
36
+ shape = shape[:subslice.orientation] + (1,) + shape[subslice.orientation:]
37
+
38
+ with open(os.path.join(output_dir, filename_base + "-data", filename), 'wb') as rawfile:
39
+ for d in data:
40
+ if ordering == 'F':
41
+ d = d.T
42
+ d.tofile(rawfile)
43
+
44
+ spacing = [slc.times[1] - slc.times[0],
45
+ mesh.coordinates['x'][1] - mesh.coordinates['x'][0],
46
+ mesh.coordinates['y'][1] - mesh.coordinates['y'][0],
47
+ mesh.coordinates['z'][1] - mesh.coordinates['z'][0]]
48
+ with lock:
49
+ meta["Meshes"].append({
50
+ "Mesh": mesh_id,
51
+ "DataFile": os.path.join(filename_base + "-data", filename),
52
+ "MeshPos": f"{mesh.coordinates['x'][0]:.6} {mesh.coordinates['y'][0]:.6} {mesh.coordinates['z'][0]:.6}",
53
+ "Spacing": f"{spacing[0]:.6} {spacing[1]:.6} {spacing[2]:.6} {spacing[3]:.6}",
54
+ "DimSize": f"{shape[0]} {shape[1]} {shape[2]} {shape[3]}"
55
+ })
56
+
57
+ Pool(8).map(lambda args: worker(*args), list(slc._subslices.items()))
58
+
59
+ meta["Meshes"] = list(meta["Meshes"])
60
+
61
+ with open(os.path.join(output_dir, filename_base + ".yaml"), 'w') as metafile:
62
+ import yaml
63
+ yaml.dump(meta, metafile)
@@ -0,0 +1,70 @@
1
+ import os
2
+ from pathlib import Path
3
+ import numpy as np
4
+ from typing_extensions import Literal
5
+ from ..smoke3d import Smoke3D
6
+
7
+
8
+ def export_smoke_raw(smoke3d: Smoke3D, output_dir: str, ordering: Literal['C', 'F'] = 'C'):
9
+ """Exports the 3d arrays to raw binary files with corresponding .yaml meta files.
10
+
11
+ :param smoke3d: The :class:`Smoke3D` object to export.
12
+ :param output_dir: The directory in which to save all files.
13
+ :param ordering: Whether to write the data in C or Fortran ordering.
14
+ """
15
+ from pathos.pools import ProcessPool as Pool
16
+ from multiprocess import Lock, Manager
17
+ filename_base = ("smoke-" + smoke3d.quantity.name.lower()).replace(" ", "_").replace(".", "-")
18
+ # Create all requested directories if they don't exist yet
19
+ Path(os.path.join(output_dir, filename_base + "-data")).mkdir(parents=True, exist_ok=True)
20
+
21
+ meta = {"DataValMax": -100000., "DataValMin": 100000., "ScaleFactor": 1, "MeshNum": len(smoke3d.subsmokes),
22
+ "Quantity": smoke3d.quantity.name}
23
+
24
+ for subsmoke in smoke3d._subsmokes.values():
25
+ meta["DataValMax"] = max(meta["DataValMax"], np.max(subsmoke.data))
26
+ meta["DataValMin"] = min(meta["DataValMin"], np.min(subsmoke.data))
27
+ meta["DataValMax"] = float(meta["DataValMax"])
28
+ meta["DataValMin"] = float(meta["DataValMin"])
29
+ meta["ScaleFactor"] = 255.0 / meta["DataValMax"]
30
+
31
+ # Abort if no useful data is available
32
+ if meta["DataValMax"] <= 0:
33
+ return
34
+
35
+ m = Manager()
36
+ meta["Meshes"] = m.list()
37
+ lock = m.Lock()
38
+
39
+ def worker(mesh, subsmoke):
40
+ mesh_id = mesh.id.replace(" ", "_").replace(".", "-")
41
+ filename = filename_base + "_mesh-" + mesh_id + ".dat"
42
+
43
+ data = (subsmoke.data * meta["ScaleFactor"]).astype(np.uint8)
44
+
45
+ with open(os.path.join(output_dir, filename_base + "-data", filename), 'wb') as rawfile:
46
+ for d in data:
47
+ if ordering == 'F':
48
+ d = d.T
49
+ d.tofile(rawfile)
50
+
51
+ spacing = [smoke3d.times[1] - smoke3d.times[0],
52
+ mesh.coordinates['x'][1] - mesh.coordinates['x'][0],
53
+ mesh.coordinates['y'][1] - mesh.coordinates['y'][0],
54
+ mesh.coordinates['z'][1] - mesh.coordinates['z'][0]]
55
+ with lock:
56
+ meta["Meshes"].append({
57
+ "Mesh": mesh_id,
58
+ "DataFile": os.path.join(filename_base + "-data", filename),
59
+ "MeshPos": f"{mesh.coordinates['x'][0]:.6} {mesh.coordinates['y'][0]:.6} {mesh.coordinates['z'][0]:.6}",
60
+ "Spacing": f"{spacing[0]:.6} {spacing[1]:.6} {spacing[2]:.6} {spacing[3]:.6}",
61
+ "DimSize": f"{data.shape[0]} {data.shape[1]} {data.shape[2]} {data.shape[3]}"
62
+ })
63
+
64
+ Pool(8).map(lambda args: worker(*args), list(smoke3d._subsmokes.items()))
65
+
66
+ meta["Meshes"] = list(meta["Meshes"])
67
+
68
+ with open(os.path.join(output_dir, filename_base + ".yaml"), 'w') as metafile:
69
+ import yaml
70
+ yaml.dump(meta, metafile)
@@ -1,6 +1,6 @@
1
1
  from typing import Iterable
2
2
 
3
- from fdsreader.utils import Mesh
3
+ from fdsreader.fds_classes import Mesh
4
4
  from fdsreader.utils.data import FDSDataCollection
5
5
 
6
6