difaddec 0.1.0__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.
- difaddec-0.1.0/PKG-INFO +1531 -0
- difaddec-0.1.0/README.md +1501 -0
- difaddec-0.1.0/pyproject.toml +116 -0
- difaddec-0.1.0/setup.cfg +4 -0
- difaddec-0.1.0/src/DifAdDec/__init__.py +20 -0
- difaddec-0.1.0/src/DifAdDec/data/dcf_public.json +62638 -0
- difaddec-0.1.0/src/DifAdDec/data/dcf_workers.json +15745 -0
- difaddec-0.1.0/src/DifAdDec/data/hrtm_subjects_breathing.json +403 -0
- difaddec-0.1.0/src/DifAdDec/data/radionuclidos.csv +758 -0
- difaddec-0.1.0/src/DifAdDec/dosimetry/__init__.py +5 -0
- difaddec-0.1.0/src/DifAdDec/dosimetry/hrtm.py +325 -0
- difaddec-0.1.0/src/DifAdDec/transport/__init__.py +28 -0
- difaddec-0.1.0/src/DifAdDec/transport/diffusion_advection_decay.py +356 -0
- difaddec-0.1.0/src/DifAdDec/transport/indoors_diffusion_advection_decay.py +617 -0
- difaddec-0.1.0/src/DifAdDec/transport/outdoors_diffusion_advection_decay.py +146 -0
- difaddec-0.1.0/src/DifAdDec/transport/windfield.py +116 -0
- difaddec-0.1.0/src/DifAdDec/utils/__init__.py +29 -0
- difaddec-0.1.0/src/DifAdDec/utils/hrtm_aux.py +37 -0
- difaddec-0.1.0/src/DifAdDec/utils/radionuclides.py +51 -0
- difaddec-0.1.0/src/DifAdDec/utils/stability.py +23 -0
- difaddec-0.1.0/src/DifAdDec/visualization/__init__.py +27 -0
- difaddec-0.1.0/src/DifAdDec/visualization/visualization.py +303 -0
- difaddec-0.1.0/src/difaddec.egg-info/PKG-INFO +1531 -0
- difaddec-0.1.0/src/difaddec.egg-info/SOURCES.txt +25 -0
- difaddec-0.1.0/src/difaddec.egg-info/dependency_links.txt +1 -0
- difaddec-0.1.0/src/difaddec.egg-info/requires.txt +9 -0
- difaddec-0.1.0/src/difaddec.egg-info/top_level.txt +1 -0
difaddec-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,1531 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: difaddec
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Three-dimensional simulation of radioactive contaminant transport through diffusion, advection and radioactive decay.
|
|
5
|
+
Author: Marcos
|
|
6
|
+
Project-URL: Homepage, https://github.com/<YOUR-USERNAME>/DifAdDec
|
|
7
|
+
Project-URL: Repository, https://github.com/<YOUR-USERNAME>/DifAdDec
|
|
8
|
+
Project-URL: Issues, https://github.com/<YOUR-USERNAME>/DifAdDec/issues
|
|
9
|
+
Keywords: radioactivity,radioprotection,diffusion,advection,radioactive decay,contaminant transport,environmental modelling,dosimetry,HRTM
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: numpy>=1.24
|
|
23
|
+
Requires-Dist: scipy>=1.10
|
|
24
|
+
Requires-Dist: matplotlib>=3.7
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
28
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
29
|
+
Requires-Dist: black>=24.0; extra == "dev"
|
|
30
|
+
|
|
31
|
+
# DifAdDec
|
|
32
|
+
|
|
33
|
+
Python library for the numerical simulation of radioactive contaminant transport through **diffusion, advection and radioactive decay**, with additional tools for indoor and outdoor environments, wind-field modelling, visualization, data export and inhalation dose estimation using the Human Respiratory Tract Model (HRTM).
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Table of Contents
|
|
38
|
+
|
|
39
|
+
* [Overview](#overview)
|
|
40
|
+
* [Main capabilities](#main-capabilities)
|
|
41
|
+
* [Library structure](#library-structure)
|
|
42
|
+
* [Installation](#installation)
|
|
43
|
+
* [Basic workflow](#basic-workflow)
|
|
44
|
+
* [1. Define the simulation domain](#1-define-the-simulation-domain)
|
|
45
|
+
* [2. Define radioactive sources](#2-define-radioactive-sources)
|
|
46
|
+
* [3. Run a simulation](#3-run-a-simulation)
|
|
47
|
+
* [4. Visualize concentration fields](#4-visualize-concentration-fields)
|
|
48
|
+
* [5. Export concentration data](#5-export-concentration-data)
|
|
49
|
+
* [Indoor simulations](#indoor-simulations)
|
|
50
|
+
* [Outdoor simulations](#outdoor-simulations)
|
|
51
|
+
* [Wind-field models](#wind-field-models)
|
|
52
|
+
* [Dose calculation with HRTM](#dose-calculation-with-hrtm)
|
|
53
|
+
* [Exporting dose results](#exporting-dose-results)
|
|
54
|
+
* [Animations](#animations)
|
|
55
|
+
* [Numerical stability](#numerical-stability)
|
|
56
|
+
* [Complete examples](#complete-examples)
|
|
57
|
+
* [Recommended workflow](#recommended-workflow)
|
|
58
|
+
* [Physical units](#physical-units)
|
|
59
|
+
* [Limitations and considerations](#limitations-and-considerations)
|
|
60
|
+
* [License](#license)
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
# Overview
|
|
65
|
+
|
|
66
|
+
`DifAdDec` is a Python library developed to simulate the transport and radioactive decay of airborne radioactive contaminants in three-dimensional environments.
|
|
67
|
+
|
|
68
|
+
The library solves a numerical formulation of the diffusion-advection-decay equation:
|
|
69
|
+
|
|
70
|
+
$$
|
|
71
|
+
\frac{\partial C}{\partial t}=D_x\frac{\partial^2 C}{\partial x^2} + D_y\frac{\partial^2 C}{\partial y^2} + D_z\frac{\partial^2 C}{\partial z^2} - \vec{v}\cdot\nabla C \lambda C + S
|
|
72
|
+
$$
|
|
73
|
+
|
|
74
|
+
where:
|
|
75
|
+
|
|
76
|
+
* $C$ is the radioactive concentration.
|
|
77
|
+
* $D_x$, $D_y$ and $D_z$ are the diffusion coefficients.
|
|
78
|
+
* $\vec{v}$ is the velocity field.
|
|
79
|
+
* $\lambda$ is the radioactive decay constant.
|
|
80
|
+
* $S$ represents radioactive sources.
|
|
81
|
+
|
|
82
|
+
The computational domain is discretized as a three-dimensional Cartesian grid.
|
|
83
|
+
|
|
84
|
+
The library is designed to support simulations in different environments:
|
|
85
|
+
|
|
86
|
+
* **General diffusion-advection-decay simulations**
|
|
87
|
+
* **Indoor environments**
|
|
88
|
+
* **Outdoor environments**
|
|
89
|
+
* **Different wind-field configurations**
|
|
90
|
+
* **Multiple radioactive sources**
|
|
91
|
+
* **Concentration visualization**
|
|
92
|
+
* **Concentration data export**
|
|
93
|
+
* **Inhalation dose estimation**
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
# Main capabilities
|
|
98
|
+
|
|
99
|
+
The main functionalities of `DifAdDec` are:
|
|
100
|
+
|
|
101
|
+
### Transport simulation
|
|
102
|
+
|
|
103
|
+
The library can simulate:
|
|
104
|
+
|
|
105
|
+
* Diffusion
|
|
106
|
+
* Advection
|
|
107
|
+
* Radioactive decay
|
|
108
|
+
* Continuous radioactive emission
|
|
109
|
+
* Three-dimensional concentration distributions
|
|
110
|
+
|
|
111
|
+
### Indoor environments
|
|
112
|
+
|
|
113
|
+
Indoor simulations additionally support:
|
|
114
|
+
|
|
115
|
+
* Wall deposition
|
|
116
|
+
* Inlets
|
|
117
|
+
* Outlets
|
|
118
|
+
* Ventilation velocities
|
|
119
|
+
* Inlet concentrations
|
|
120
|
+
* Automatically generated velocity fields for the enclosed domain
|
|
121
|
+
|
|
122
|
+
### Outdoor environments
|
|
123
|
+
|
|
124
|
+
Outdoor simulations support externally defined wind models, allowing the use of different spatial and temporal velocity fields.
|
|
125
|
+
|
|
126
|
+
### Wind models
|
|
127
|
+
|
|
128
|
+
Several wind-field models are available:
|
|
129
|
+
|
|
130
|
+
* Uniform wind
|
|
131
|
+
* Vertical shear
|
|
132
|
+
* Gusting wind
|
|
133
|
+
* Vortex flow
|
|
134
|
+
|
|
135
|
+
### Visualization
|
|
136
|
+
|
|
137
|
+
Simulation results can be represented as:
|
|
138
|
+
|
|
139
|
+
* 2D concentration maps
|
|
140
|
+
* 3D concentration maps
|
|
141
|
+
* Time-dependent animations
|
|
142
|
+
|
|
143
|
+
### Data export
|
|
144
|
+
|
|
145
|
+
Concentration and dose fields can be exported to CSV files.
|
|
146
|
+
|
|
147
|
+
### Dosimetry
|
|
148
|
+
|
|
149
|
+
The `HRTM` class can use the saved concentration fields from a transport simulation to calculate inhalation dose distributions based on:
|
|
150
|
+
|
|
151
|
+
* Population type
|
|
152
|
+
* Age group
|
|
153
|
+
* Gender
|
|
154
|
+
* Physical activity
|
|
155
|
+
* Absorption type
|
|
156
|
+
* Exposure time
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
# Library structure
|
|
161
|
+
|
|
162
|
+
A typical `DifAdDec` project follows this structure:
|
|
163
|
+
|
|
164
|
+
```text
|
|
165
|
+
DifAdDec/
|
|
166
|
+
│
|
|
167
|
+
├── diffusion_advection_decay.py
|
|
168
|
+
├── indoors_diffusion_advection_decay.py
|
|
169
|
+
├── outdoors_diffusion_advection_decay.py
|
|
170
|
+
├── windfield.py
|
|
171
|
+
├── hrtm.py
|
|
172
|
+
│
|
|
173
|
+
├── utils/
|
|
174
|
+
│ ├── ...
|
|
175
|
+
│
|
|
176
|
+
└── visualization/
|
|
177
|
+
├── ...
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The main public components are:
|
|
181
|
+
|
|
182
|
+
| Component | Purpose |
|
|
183
|
+
| --------------------------------- | ---------------------------- |
|
|
184
|
+
| `DiffusionAdvectionDecay` | Base transport simulation |
|
|
185
|
+
| `IndoorsDiffusionAdvectionDecay` | Indoor transport simulation |
|
|
186
|
+
| `OutdoorsDiffusionAdvectionDecay` | Outdoor transport simulation |
|
|
187
|
+
| `WindField` | Base class for wind models |
|
|
188
|
+
| `UniformField` | Uniform wind velocity |
|
|
189
|
+
| `ShearField` | Wind varying with height |
|
|
190
|
+
| `GustField` | Time-dependent wind |
|
|
191
|
+
| `VortexField` | Vortex wind field |
|
|
192
|
+
| `HRTM` | Inhalation dose calculation |
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
# Installation
|
|
197
|
+
|
|
198
|
+
Clone the repository:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
git clone <repository-url>
|
|
202
|
+
cd <repository-name>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Create a virtual environment:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
python -m venv .venv
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Activate it on Linux/macOS:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
source .venv/bin/activate
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
or on Windows:
|
|
218
|
+
|
|
219
|
+
```powershell
|
|
220
|
+
.venv\Scripts\activate
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Install the required dependencies:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
pip install -r requirements.txt
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
The library relies on scientific Python packages including:
|
|
230
|
+
|
|
231
|
+
```text
|
|
232
|
+
numpy
|
|
233
|
+
scipy
|
|
234
|
+
matplotlib
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The exact dependencies should be kept synchronized with the project's `requirements.txt`.
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
# Basic workflow
|
|
242
|
+
|
|
243
|
+
A typical `DifAdDec` simulation follows these steps:
|
|
244
|
+
|
|
245
|
+
```text
|
|
246
|
+
1. Define the computational grid
|
|
247
|
+
↓
|
|
248
|
+
2. Define spatial and temporal discretization
|
|
249
|
+
↓
|
|
250
|
+
3. Define radioactive species
|
|
251
|
+
↓
|
|
252
|
+
4. Define radioactive sources
|
|
253
|
+
↓
|
|
254
|
+
5. Select simulation environment
|
|
255
|
+
↓
|
|
256
|
+
6. Run the simulation
|
|
257
|
+
↓
|
|
258
|
+
7. Save concentration fields
|
|
259
|
+
↓
|
|
260
|
+
8. Visualize or export results
|
|
261
|
+
↓
|
|
262
|
+
9. Calculate dose using HRTM
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
The most important principle is:
|
|
266
|
+
|
|
267
|
+
> **The transport simulation must be executed before dose calculations can be performed.**
|
|
268
|
+
|
|
269
|
+
This is because `HRTM` uses the concentration fields saved by the transport simulation.
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
# 1. Define the simulation domain
|
|
274
|
+
|
|
275
|
+
The computational domain is defined using `grid_shape` and `d`.
|
|
276
|
+
|
|
277
|
+
## Grid size
|
|
278
|
+
|
|
279
|
+
`grid_shape` defines the number of nodes in each spatial direction:
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
grid_shape = (50, 50, 50)
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
This corresponds to:
|
|
286
|
+
|
|
287
|
+
```text
|
|
288
|
+
Nx = 50
|
|
289
|
+
Ny = 50
|
|
290
|
+
Nz = 50
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
The grid therefore contains:
|
|
294
|
+
|
|
295
|
+
$$
|
|
296
|
+
N_xN_yN_z
|
|
297
|
+
$$
|
|
298
|
+
|
|
299
|
+
computational nodes.
|
|
300
|
+
|
|
301
|
+
## Spatial and temporal discretization
|
|
302
|
+
|
|
303
|
+
The tuple `d` contains:
|
|
304
|
+
|
|
305
|
+
```python
|
|
306
|
+
d = (dx, dy, dz, dt)
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
For example:
|
|
310
|
+
|
|
311
|
+
```python
|
|
312
|
+
d = (0.5, 0.5, 0.5, 0.1)
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
means:
|
|
316
|
+
|
|
317
|
+
| Parameter | Value | Unit |
|
|
318
|
+
| --------- | ----: | ---- |
|
|
319
|
+
| `dx` | 0.5 | m |
|
|
320
|
+
| `dy` | 0.5 | m |
|
|
321
|
+
| `dz` | 0.5 | m |
|
|
322
|
+
| `dt` | 0.1 | s |
|
|
323
|
+
|
|
324
|
+
The physical dimensions of the domain are therefore determined by both the number of grid points and the spatial discretization.
|
|
325
|
+
|
|
326
|
+
For example:
|
|
327
|
+
|
|
328
|
+
```python
|
|
329
|
+
grid_shape = (50, 50, 50)
|
|
330
|
+
d = (0.5, 0.5, 0.5, 0.1)
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
corresponds to a domain with approximately:
|
|
334
|
+
|
|
335
|
+
```text
|
|
336
|
+
25 m × 25 m × 25 m
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
# 2. Define radioactive sources
|
|
342
|
+
|
|
343
|
+
Radioactive sources are defined using:
|
|
344
|
+
|
|
345
|
+
```text
|
|
346
|
+
simulation = DiffusionAdvectionDecay(
|
|
347
|
+
source_positions
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
and:
|
|
351
|
+
|
|
352
|
+
```text
|
|
353
|
+
emission_rate
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
For example:
|
|
357
|
+
|
|
358
|
+
```text
|
|
359
|
+
source_positions = [
|
|
360
|
+
(25, 25, 25)
|
|
361
|
+
]
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
```
|
|
365
|
+
emission_rate = 3.0
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
defines a source at the grid position:
|
|
369
|
+
|
|
370
|
+
```text
|
|
371
|
+
x = 25
|
|
372
|
+
y = 25
|
|
373
|
+
z = 25
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
The current implementation allows both continuous sources and temporally limited sources.
|
|
377
|
+
|
|
378
|
+
## Continuous radioactive sources
|
|
379
|
+
|
|
380
|
+
By default, the source remains active throughout the simulation. At every time step, the source contribution is injected into the corresponding grid cell.
|
|
381
|
+
|
|
382
|
+
The amount introduced during each time step is:
|
|
383
|
+
|
|
384
|
+
$$
|
|
385
|
+
\Delta C = Q\Delta t
|
|
386
|
+
$$
|
|
387
|
+
|
|
388
|
+
where:
|
|
389
|
+
|
|
390
|
+
$Q$ is the emission rate.
|
|
391
|
+
|
|
392
|
+
$\Delta t$ is the simulation time step.
|
|
393
|
+
|
|
394
|
+
For example:
|
|
395
|
+
|
|
396
|
+
```python
|
|
397
|
+
simulation = DiffusionAdvectionDecay(
|
|
398
|
+
...
|
|
399
|
+
source_positions=[(25, 25, 25)],
|
|
400
|
+
emission_rate=3.0
|
|
401
|
+
)
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
If source_effective_iterations is not specified, the source remains active for the duration of the simulation.
|
|
405
|
+
|
|
406
|
+
## Temporally limited radioactive sources
|
|
407
|
+
|
|
408
|
+
A source can also be active only during a defined number of simulation iterations using:
|
|
409
|
+
|
|
410
|
+
```text
|
|
411
|
+
source_effective_iterations
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
For example:
|
|
415
|
+
|
|
416
|
+
```python
|
|
417
|
+
simulation = DiffusionAdvectionDecay(
|
|
418
|
+
...
|
|
419
|
+
source_positions=[(25, 25, 25)],
|
|
420
|
+
emission_rate=3.0,
|
|
421
|
+
source_effective_iterations=100
|
|
422
|
+
)
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
In this case, the source is injected only during the initial 100 simulation iterations. After this period, no additional activity is introduced by the source, while the radioactive material already present in the domain continues to evolve according to diffusion, advection and radioactive decay.
|
|
426
|
+
|
|
427
|
+
The source duration in physical time is determined by:
|
|
428
|
+
|
|
429
|
+
$$
|
|
430
|
+
T_s = N_s\Delta t
|
|
431
|
+
$$
|
|
432
|
+
|
|
433
|
+
where:
|
|
434
|
+
|
|
435
|
+
$T_s$ is the source emission duration in seconds.
|
|
436
|
+
|
|
437
|
+
$N_s$ is source_effective_iterations.
|
|
438
|
+
|
|
439
|
+
$\Delta t$ is the simulation time step.
|
|
440
|
+
|
|
441
|
+
Therefore, for a simulation with:
|
|
442
|
+
|
|
443
|
+
```python
|
|
444
|
+
dt = 0.1
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
a source intended to remain active for 60 seconds would require:
|
|
448
|
+
|
|
449
|
+
```python
|
|
450
|
+
source_effective_iterations = 600
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
This functionality makes it possible to represent different source-release scenarios, including:
|
|
454
|
+
|
|
455
|
+
continuous emission throughout the simulation;
|
|
456
|
+
|
|
457
|
+
emission during a finite time interval;
|
|
458
|
+
|
|
459
|
+
an initial release followed by a period without further source injection.
|
|
460
|
+
|
|
461
|
+
Important: source_effective_iterations is expressed in number of simulation iterations, rather than directly in seconds.
|
|
462
|
+
|
|
463
|
+
## Multiple point sources
|
|
464
|
+
|
|
465
|
+
Several point sources can be defined simultaneously:
|
|
466
|
+
|
|
467
|
+
```python
|
|
468
|
+
source_positions = [
|
|
469
|
+
(10, 10, 10),
|
|
470
|
+
(25, 25, 25),
|
|
471
|
+
(40, 40, 40)
|
|
472
|
+
]
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
All specified positions are injected while the source is active. If several source points are placed next to each other, they can represent an extended two- or three-dimensional source region.
|
|
476
|
+
|
|
477
|
+
Important: source positions refer to grid indices, not directly to physical coordinates in metres.
|
|
478
|
+
|
|
479
|
+
For a grid spacing of:
|
|
480
|
+
|
|
481
|
+
```python
|
|
482
|
+
d = (0.5, 0.5, 0.5, 0.1)
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
the grid point:
|
|
486
|
+
|
|
487
|
+
```python
|
|
488
|
+
(10, 10, 10)
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
corresponds to approximately:
|
|
492
|
+
|
|
493
|
+
```python
|
|
494
|
+
(5 m, 5 m, 5 m)
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
# 3. Run a simulation
|
|
498
|
+
## General simulation
|
|
499
|
+
|
|
500
|
+
Create a simulation:
|
|
501
|
+
|
|
502
|
+
```python
|
|
503
|
+
simulation = (Indoors/Outdoors)DiffusionAdvectionDecay(
|
|
504
|
+
grid_shape=(50, 50, 50),
|
|
505
|
+
d=(0.5, 0.5, 0.5, 0.1),
|
|
506
|
+
total_time=1000.0,
|
|
507
|
+
diffusion_coefficient=(1e-3, 1e-3, 1e-3),
|
|
508
|
+
species_name="U-234",
|
|
509
|
+
source_positions=[(25, 25, 25)],
|
|
510
|
+
emission_rate=3.0
|
|
511
|
+
...
|
|
512
|
+
)
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
Then execute:
|
|
516
|
+
|
|
517
|
+
```python
|
|
518
|
+
results = simulation.run(
|
|
519
|
+
save_every_X_iteration=100
|
|
520
|
+
)
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
The `run()` method returns the saved concentration fields.
|
|
524
|
+
|
|
525
|
+
The results are stored internally as a dictionary where:
|
|
526
|
+
|
|
527
|
+
```text
|
|
528
|
+
key → simulation time
|
|
529
|
+
value → 3D concentration field
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
For example:
|
|
533
|
+
|
|
534
|
+
```python
|
|
535
|
+
results[0.0]
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
returns the initial concentration field.
|
|
539
|
+
|
|
540
|
+
If a field was saved at 10 seconds:
|
|
541
|
+
|
|
542
|
+
```python
|
|
543
|
+
results[10.0]
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
returns the corresponding three-dimensional concentration distribution.
|
|
547
|
+
|
|
548
|
+
---
|
|
549
|
+
|
|
550
|
+
# 4. Visualize concentration fields
|
|
551
|
+
|
|
552
|
+
After running a simulation, a saved concentration field can be visualized using:
|
|
553
|
+
|
|
554
|
+
```python
|
|
555
|
+
simulation.plot_instant(
|
|
556
|
+
plot_name="Radioactive concentration",
|
|
557
|
+
visualization_type="3d",
|
|
558
|
+
vertical_axis="z",
|
|
559
|
+
time_to_check=100.0
|
|
560
|
+
)
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
The available visualization types are:
|
|
564
|
+
|
|
565
|
+
```python
|
|
566
|
+
visualization_type="3d"
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
and:
|
|
570
|
+
|
|
571
|
+
```python
|
|
572
|
+
visualization_type="2d"
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
For example:
|
|
576
|
+
|
|
577
|
+
```python
|
|
578
|
+
simulation.plot_instant(
|
|
579
|
+
plot_name="Concentration at t = 100 s",
|
|
580
|
+
visualization_type="2d",
|
|
581
|
+
vertical_axis="z",
|
|
582
|
+
time_to_check=100.0
|
|
583
|
+
)
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
The `vertical_axis` parameter determines which coordinate is treated as the vertical/slicing direction.
|
|
587
|
+
|
|
588
|
+
The `levels` parameter can be used to specify the grid levels to display:
|
|
589
|
+
|
|
590
|
+
```python
|
|
591
|
+
simulation.plot_instant(
|
|
592
|
+
plot_name="Selected planes",
|
|
593
|
+
visualization_type="3d",
|
|
594
|
+
vertical_axis="z",
|
|
595
|
+
levels=[10, 25, 40],
|
|
596
|
+
time_to_check=100.0
|
|
597
|
+
)
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
The concentration is normalized for visualization using:
|
|
601
|
+
|
|
602
|
+
$$
|
|
603
|
+
\frac{C}{C_{\max}}
|
|
604
|
+
$$
|
|
605
|
+
|
|
606
|
+
while the maximum concentration is reported in the color bar.
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
# 5. Export concentration data
|
|
611
|
+
|
|
612
|
+
A saved concentration field can be exported to CSV:
|
|
613
|
+
|
|
614
|
+
```python
|
|
615
|
+
simulation.make_csv_for_instant(
|
|
616
|
+
time=100.0,
|
|
617
|
+
filename="concentration_100s.csv"
|
|
618
|
+
)
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
The generated file contains:
|
|
622
|
+
|
|
623
|
+
```text
|
|
624
|
+
x (m)
|
|
625
|
+
y (m)
|
|
626
|
+
z (m)
|
|
627
|
+
concentration (Bq/m³)
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
The coordinates are reconstructed from the spatial discretization:
|
|
631
|
+
|
|
632
|
+
```python
|
|
633
|
+
dx, dy, dz = d[:3]
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
and the concentration field corresponding to the selected simulation time is exported.
|
|
637
|
+
|
|
638
|
+
If no time is provided:
|
|
639
|
+
|
|
640
|
+
```python
|
|
641
|
+
simulation.make_csv_for_instant(
|
|
642
|
+
filename="concentration.csv"
|
|
643
|
+
)
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
the method attempts to use the total simulation time.
|
|
647
|
+
|
|
648
|
+
However, the requested time must have been saved during `run()`.
|
|
649
|
+
|
|
650
|
+
---
|
|
651
|
+
|
|
652
|
+
# Indoor simulations
|
|
653
|
+
|
|
654
|
+
Indoor environments are represented by:
|
|
655
|
+
|
|
656
|
+
```python
|
|
657
|
+
IndoorsDiffusionAdvectionDecay
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
Import it with:
|
|
661
|
+
|
|
662
|
+
```python
|
|
663
|
+
from DifAdDec import (
|
|
664
|
+
IndoorsDiffusionAdvectionDecay
|
|
665
|
+
)
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
The indoor model extends the general diffusion-advection-decay model and adds:
|
|
669
|
+
|
|
670
|
+
* Wall deposition
|
|
671
|
+
* Inlets
|
|
672
|
+
* Outlets
|
|
673
|
+
* Ventilation velocities
|
|
674
|
+
* Inlet concentration
|
|
675
|
+
* An internally calculated velocity field
|
|
676
|
+
|
|
677
|
+
---
|
|
678
|
+
|
|
679
|
+
## Basic indoor example
|
|
680
|
+
|
|
681
|
+
```python
|
|
682
|
+
simulation = IndoorsDiffusionAdvectionDecay(
|
|
683
|
+
grid_shape=(50, 50, 50),
|
|
684
|
+
d=(0.5, 0.5, 0.5, 0.1),
|
|
685
|
+
total_time=1000.0,
|
|
686
|
+
diffusion_coefficient=(1e-3, 1e-3, 1e-3),
|
|
687
|
+
species_name="U-234",
|
|
688
|
+
source_positions=[(25, 25, 25)],
|
|
689
|
+
emission_rate=3.0,
|
|
690
|
+
wall_deposition=1e-4
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
results = simulation.run(
|
|
694
|
+
save_every_X_iteration=100
|
|
695
|
+
)
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
---
|
|
699
|
+
|
|
700
|
+
# Indoor ventilation
|
|
701
|
+
|
|
702
|
+
Ventilation openings are defined through:
|
|
703
|
+
|
|
704
|
+
```python
|
|
705
|
+
inlet_regions
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
and:
|
|
709
|
+
|
|
710
|
+
```python
|
|
711
|
+
outlet_regions
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
Each region specifies:
|
|
715
|
+
|
|
716
|
+
* The wall containing the opening
|
|
717
|
+
* The range of indices defining the opening
|
|
718
|
+
|
|
719
|
+
For example:
|
|
720
|
+
|
|
721
|
+
```python
|
|
722
|
+
inlet_regions = [
|
|
723
|
+
{
|
|
724
|
+
"wall": "xmin",
|
|
725
|
+
"y": (10, 20),
|
|
726
|
+
"z": (10, 20)
|
|
727
|
+
}
|
|
728
|
+
]
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
This defines an inlet located on the `xmin` wall.
|
|
732
|
+
|
|
733
|
+
The available wall names are:
|
|
734
|
+
|
|
735
|
+
```text
|
|
736
|
+
xmin
|
|
737
|
+
xmax
|
|
738
|
+
ymin
|
|
739
|
+
ymax
|
|
740
|
+
zmin
|
|
741
|
+
zmax
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
The same structure is used for outlet regions.
|
|
745
|
+
|
|
746
|
+
Example:
|
|
747
|
+
|
|
748
|
+
```python
|
|
749
|
+
outlet_regions = [
|
|
750
|
+
{
|
|
751
|
+
"wall": "xmax",
|
|
752
|
+
"y": (30, 40),
|
|
753
|
+
"z": (10, 20)
|
|
754
|
+
}
|
|
755
|
+
]
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
Ventilation velocities are specified with:
|
|
759
|
+
|
|
760
|
+
```python
|
|
761
|
+
inlet_wind_velocity=1.0,
|
|
762
|
+
outlet_wind_velocity=1.0
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
and the inlet concentration can be specified using:
|
|
766
|
+
|
|
767
|
+
```python
|
|
768
|
+
inlet_concentration=0.0
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
---
|
|
772
|
+
|
|
773
|
+
# Outdoor simulations
|
|
774
|
+
|
|
775
|
+
Outdoor environments are represented by:
|
|
776
|
+
|
|
777
|
+
```python
|
|
778
|
+
OutdoorsDiffusionAdvectionDecay
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
Import:
|
|
782
|
+
|
|
783
|
+
```python
|
|
784
|
+
from DifAdDec import (
|
|
785
|
+
OutdoorsDiffusionAdvectionDecay
|
|
786
|
+
)
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
Unlike the indoor model, the outdoor model receives a wind model explicitly.
|
|
790
|
+
|
|
791
|
+
The basic structure is:
|
|
792
|
+
|
|
793
|
+
```python
|
|
794
|
+
simulation = OutdoorsDiffusionAdvectionDecay(
|
|
795
|
+
wind_model=wind_model,
|
|
796
|
+
grid_shape=(50, 50, 50),
|
|
797
|
+
d=(0.5, 0.5, 0.5, 0.1),
|
|
798
|
+
total_time=1000.0,
|
|
799
|
+
diffusion_coefficient=(1e-3, 1e-3, 1e-3),
|
|
800
|
+
species_name="U-234",
|
|
801
|
+
source_positions=[(25, 25, 25)],
|
|
802
|
+
emission_rate=3.0
|
|
803
|
+
)
|
|
804
|
+
|
|
805
|
+
results = simulation.run(
|
|
806
|
+
save_every_X_iteration=100
|
|
807
|
+
)
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
---
|
|
811
|
+
|
|
812
|
+
# Wind-field models
|
|
813
|
+
|
|
814
|
+
`DifAdDec` provides several wind-field implementations.
|
|
815
|
+
|
|
816
|
+
Import them from the wind-field module:
|
|
817
|
+
|
|
818
|
+
```python
|
|
819
|
+
from DifAdDec import (
|
|
820
|
+
UniformField,
|
|
821
|
+
ShearField,
|
|
822
|
+
GustField,
|
|
823
|
+
VortexField
|
|
824
|
+
)
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
---
|
|
828
|
+
|
|
829
|
+
## Uniform wind
|
|
830
|
+
|
|
831
|
+
A uniform velocity field is defined with:
|
|
832
|
+
|
|
833
|
+
```python
|
|
834
|
+
wind = UniformField(
|
|
835
|
+
grid_shape=(50, 50, 50),
|
|
836
|
+
initial_velocity=(5.0, 0.0, 0.0)
|
|
837
|
+
)
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
This creates a velocity field where:
|
|
841
|
+
|
|
842
|
+
```text
|
|
843
|
+
u = 5 m/s
|
|
844
|
+
v = 0 m/s
|
|
845
|
+
w = 0 m/s
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
The velocity is constant throughout the computational domain.
|
|
849
|
+
|
|
850
|
+
---
|
|
851
|
+
|
|
852
|
+
## Vertical shear
|
|
853
|
+
|
|
854
|
+
The `ShearField` model creates a wind speed that varies with height:
|
|
855
|
+
|
|
856
|
+
```python
|
|
857
|
+
wind = ShearField(
|
|
858
|
+
grid_shape=(50, 50, 50),
|
|
859
|
+
Uref=5.0,
|
|
860
|
+
zref=10,
|
|
861
|
+
alpha=0.20
|
|
862
|
+
)
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
The horizontal velocity follows a power-law relationship with height.
|
|
866
|
+
|
|
867
|
+
This model is useful for representing wind profiles whose velocity changes vertically.
|
|
868
|
+
|
|
869
|
+
---
|
|
870
|
+
|
|
871
|
+
## Gusting wind
|
|
872
|
+
|
|
873
|
+
The `GustField` model introduces temporal variation:
|
|
874
|
+
|
|
875
|
+
```python
|
|
876
|
+
wind = GustField(
|
|
877
|
+
grid_shape=(50, 50, 50),
|
|
878
|
+
Umean=5.0,
|
|
879
|
+
amplitude=2.0,
|
|
880
|
+
period=120
|
|
881
|
+
)
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
The wind velocity varies sinusoidally with time:
|
|
885
|
+
|
|
886
|
+
$$
|
|
887
|
+
U(t)=U_{\mathrm{mean}}
|
|
888
|
+
+
|
|
889
|
+
A\sin\left(
|
|
890
|
+
\frac{2\pi t}{T}
|
|
891
|
+
\right)
|
|
892
|
+
$$
|
|
893
|
+
|
|
894
|
+
where:
|
|
895
|
+
|
|
896
|
+
* `Umean` is the mean velocity.
|
|
897
|
+
* `amplitude` is the oscillation amplitude.
|
|
898
|
+
* `period` is the oscillation period.
|
|
899
|
+
|
|
900
|
+
The velocity is recalculated during the simulation.
|
|
901
|
+
|
|
902
|
+
---
|
|
903
|
+
|
|
904
|
+
## Vortex field
|
|
905
|
+
|
|
906
|
+
A rotational velocity field can be generated using:
|
|
907
|
+
|
|
908
|
+
```python
|
|
909
|
+
wind = VortexField(
|
|
910
|
+
grid_shape=(50, 50, 50),
|
|
911
|
+
omega=0.02
|
|
912
|
+
)
|
|
913
|
+
```
|
|
914
|
+
|
|
915
|
+
The resulting velocity field rotates around the central vertical axis of the computational domain.
|
|
916
|
+
|
|
917
|
+
---
|
|
918
|
+
|
|
919
|
+
# Complete outdoor example
|
|
920
|
+
|
|
921
|
+
```python
|
|
922
|
+
from DifAdDec import (
|
|
923
|
+
OutdoorsDiffusionAdvectionDecay
|
|
924
|
+
)
|
|
925
|
+
|
|
926
|
+
from DifAdDec import UniformField
|
|
927
|
+
|
|
928
|
+
grid_shape = (50, 50, 50)
|
|
929
|
+
|
|
930
|
+
d = (
|
|
931
|
+
0.5, # dx [m]
|
|
932
|
+
0.5, # dy [m]
|
|
933
|
+
0.5, # dz [m]
|
|
934
|
+
0.1 # dt [s]
|
|
935
|
+
)
|
|
936
|
+
|
|
937
|
+
wind = UniformField(
|
|
938
|
+
grid_shape=grid_shape,
|
|
939
|
+
initial_velocity=(5.0, 0.0, 0.0)
|
|
940
|
+
)
|
|
941
|
+
|
|
942
|
+
simulation = OutdoorsDiffusionAdvectionDecay(
|
|
943
|
+
wind_model=wind,
|
|
944
|
+
grid_shape=grid_shape,
|
|
945
|
+
d=d,
|
|
946
|
+
total_time=1000.0,
|
|
947
|
+
diffusion_coefficient=(1e-3, 1e-3, 1e-3),
|
|
948
|
+
species_name="U-234",
|
|
949
|
+
source_positions=[
|
|
950
|
+
(25, 25, 25)
|
|
951
|
+
],
|
|
952
|
+
emission_rate=3.0
|
|
953
|
+
)
|
|
954
|
+
|
|
955
|
+
results = simulation.run(
|
|
956
|
+
save_every_X_iteration=100
|
|
957
|
+
)
|
|
958
|
+
|
|
959
|
+
simulation.plot_instant(
|
|
960
|
+
plot_name="Outdoor radioactive dispersion",
|
|
961
|
+
visualization_type="3d",
|
|
962
|
+
vertical_axis="z",
|
|
963
|
+
time_to_check=100.0
|
|
964
|
+
)
|
|
965
|
+
```
|
|
966
|
+
|
|
967
|
+
---
|
|
968
|
+
|
|
969
|
+
# Dose calculation with HRTM
|
|
970
|
+
|
|
971
|
+
Once a transport simulation has been completed, the concentration fields can be passed to the `HRTM` class.
|
|
972
|
+
|
|
973
|
+
Import:
|
|
974
|
+
|
|
975
|
+
```python
|
|
976
|
+
from DifAdDec import HRTM
|
|
977
|
+
```
|
|
978
|
+
|
|
979
|
+
Create an HRTM object using the completed simulation:
|
|
980
|
+
|
|
981
|
+
```python
|
|
982
|
+
hrtm = HRTM(
|
|
983
|
+
simulation,
|
|
984
|
+
population_type="public",
|
|
985
|
+
age_group="adult",
|
|
986
|
+
gender="male",
|
|
987
|
+
physical_activity="sitting",
|
|
988
|
+
absorption="F",
|
|
989
|
+
exposition_time=1000.0
|
|
990
|
+
)
|
|
991
|
+
```
|
|
992
|
+
|
|
993
|
+
The HRTM object obtains the concentration fields, radionuclide species and simulation information directly from the transport simulation.
|
|
994
|
+
|
|
995
|
+
---
|
|
996
|
+
|
|
997
|
+
## Calculate effective dose commitment
|
|
998
|
+
|
|
999
|
+
Run:
|
|
1000
|
+
|
|
1001
|
+
```python
|
|
1002
|
+
dose = hrtm.effective_dose_commitment()
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
The method uses:
|
|
1006
|
+
|
|
1007
|
+
1. The simulated concentration fields.
|
|
1008
|
+
2. The selected population characteristics.
|
|
1009
|
+
3. The breathing rate.
|
|
1010
|
+
4. The inhalation dose coefficients.
|
|
1011
|
+
5. The exposure time.
|
|
1012
|
+
|
|
1013
|
+
The resulting dose fields are stored internally and returned by the method.
|
|
1014
|
+
|
|
1015
|
+
Conceptually, the workflow is:
|
|
1016
|
+
|
|
1017
|
+
```text
|
|
1018
|
+
Concentration field
|
|
1019
|
+
↓
|
|
1020
|
+
Breathing rate
|
|
1021
|
+
↓
|
|
1022
|
+
Inhaled activity
|
|
1023
|
+
↓
|
|
1024
|
+
Inhalation dose coefficient
|
|
1025
|
+
↓
|
|
1026
|
+
Dose field
|
|
1027
|
+
```
|
|
1028
|
+
|
|
1029
|
+
---
|
|
1030
|
+
|
|
1031
|
+
# HRTM parameters
|
|
1032
|
+
|
|
1033
|
+
The main parameters are:
|
|
1034
|
+
|
|
1035
|
+
| Parameter | Purpose |
|
|
1036
|
+
| ------------------- | ------------------------------------------------------ |
|
|
1037
|
+
| `population_type` | Defines the population category |
|
|
1038
|
+
| `age_group` | Defines the age group |
|
|
1039
|
+
| `gender` | Defines the gender used for the respiratory parameters |
|
|
1040
|
+
| `physical_activity` | Defines the breathing-rate condition |
|
|
1041
|
+
| `absorption` | Defines the selected absorption type |
|
|
1042
|
+
| `exposition_time` | Exposure duration |
|
|
1043
|
+
|
|
1044
|
+
For example:
|
|
1045
|
+
|
|
1046
|
+
```python
|
|
1047
|
+
hrtm = HRTM(
|
|
1048
|
+
simulation,
|
|
1049
|
+
population_type="public",
|
|
1050
|
+
age_group="adult",
|
|
1051
|
+
gender="male",
|
|
1052
|
+
physical_activity="sitting",
|
|
1053
|
+
absorption="F",
|
|
1054
|
+
exposition_time=600.0
|
|
1055
|
+
)
|
|
1056
|
+
```
|
|
1057
|
+
|
|
1058
|
+
If `exposition_time` is not provided, the simulation total time is used.
|
|
1059
|
+
|
|
1060
|
+
---
|
|
1061
|
+
|
|
1062
|
+
# Exporting dose results
|
|
1063
|
+
|
|
1064
|
+
After:
|
|
1065
|
+
|
|
1066
|
+
```python
|
|
1067
|
+
hrtm.effective_dose_commitment()
|
|
1068
|
+
```
|
|
1069
|
+
|
|
1070
|
+
the calculated dose field can be exported:
|
|
1071
|
+
|
|
1072
|
+
```python
|
|
1073
|
+
hrtm.make_csv_for_instant(
|
|
1074
|
+
time=100.0,
|
|
1075
|
+
filename="dose_100s.csv"
|
|
1076
|
+
)
|
|
1077
|
+
```
|
|
1078
|
+
|
|
1079
|
+
The CSV contains:
|
|
1080
|
+
|
|
1081
|
+
```text
|
|
1082
|
+
x (m)
|
|
1083
|
+
y (m)
|
|
1084
|
+
z (m)
|
|
1085
|
+
dose (Bq)
|
|
1086
|
+
```
|
|
1087
|
+
|
|
1088
|
+
The requested time must correspond to a dose field stored in the HRTM results.
|
|
1089
|
+
|
|
1090
|
+
---
|
|
1091
|
+
|
|
1092
|
+
# Dose visualization
|
|
1093
|
+
|
|
1094
|
+
The dose field can be visualized using:
|
|
1095
|
+
|
|
1096
|
+
```python
|
|
1097
|
+
hrtm.plot_instant(
|
|
1098
|
+
plot_name="Inhalation dose",
|
|
1099
|
+
visualization_type="3d",
|
|
1100
|
+
vertical_axis="z",
|
|
1101
|
+
time_to_check=100.0
|
|
1102
|
+
)
|
|
1103
|
+
```
|
|
1104
|
+
|
|
1105
|
+
As with the concentration visualization, both 2D and 3D representations are supported.
|
|
1106
|
+
|
|
1107
|
+
---
|
|
1108
|
+
|
|
1109
|
+
# Animations
|
|
1110
|
+
|
|
1111
|
+
Both transport simulations and HRTM dose calculations provide an `animate()` method.
|
|
1112
|
+
|
|
1113
|
+
For example:
|
|
1114
|
+
|
|
1115
|
+
```python
|
|
1116
|
+
simulation.animate(
|
|
1117
|
+
plot_name="Concentration evolution"
|
|
1118
|
+
)
|
|
1119
|
+
```
|
|
1120
|
+
|
|
1121
|
+
The animation displays several horizontal slices of the three-dimensional field while the simulation time changes.
|
|
1122
|
+
|
|
1123
|
+
Specific `z` levels can be provided:
|
|
1124
|
+
|
|
1125
|
+
```python
|
|
1126
|
+
simulation.animate(
|
|
1127
|
+
plot_name="Concentration evolution",
|
|
1128
|
+
z_values=[5, 10, 20, 30, 40, 45]
|
|
1129
|
+
)
|
|
1130
|
+
```
|
|
1131
|
+
|
|
1132
|
+
The same approach can be used for HRTM:
|
|
1133
|
+
|
|
1134
|
+
```python
|
|
1135
|
+
hrtm.animate(
|
|
1136
|
+
plot_name="Dose evolution",
|
|
1137
|
+
z_values=[5, 10, 20, 30, 40, 45]
|
|
1138
|
+
)
|
|
1139
|
+
```
|
|
1140
|
+
|
|
1141
|
+
---
|
|
1142
|
+
|
|
1143
|
+
# Numerical stability
|
|
1144
|
+
|
|
1145
|
+
The numerical methods impose stability constraints on the selected discretization.
|
|
1146
|
+
|
|
1147
|
+
`DifAdDec` performs stability checks for:
|
|
1148
|
+
|
|
1149
|
+
* Diffusion
|
|
1150
|
+
* Advection/CFL conditions
|
|
1151
|
+
|
|
1152
|
+
These checks are performed automatically when the corresponding simulation objects are initialized.
|
|
1153
|
+
|
|
1154
|
+
Therefore, the following parameters should not be selected independently:
|
|
1155
|
+
|
|
1156
|
+
```python
|
|
1157
|
+
dx
|
|
1158
|
+
dy
|
|
1159
|
+
dz
|
|
1160
|
+
dt
|
|
1161
|
+
diffusion_coefficient
|
|
1162
|
+
wind_velocity
|
|
1163
|
+
```
|
|
1164
|
+
|
|
1165
|
+
For example, increasing the wind velocity may require a smaller temporal step `dt`.
|
|
1166
|
+
|
|
1167
|
+
Similarly, increasing the diffusion coefficient may impose stricter constraints on the spatial and temporal discretization.
|
|
1168
|
+
|
|
1169
|
+
If the stability conditions are not satisfied, the library raises a `ValueError`.
|
|
1170
|
+
|
|
1171
|
+
---
|
|
1172
|
+
|
|
1173
|
+
# Complete workflow example
|
|
1174
|
+
|
|
1175
|
+
The following example illustrates the recommended workflow from transport simulation to dose calculation.
|
|
1176
|
+
|
|
1177
|
+
```python
|
|
1178
|
+
from DifAdDec import (
|
|
1179
|
+
OutdoorsDiffusionAdvectionDecay
|
|
1180
|
+
UniformField
|
|
1181
|
+
HRTM
|
|
1182
|
+
)
|
|
1183
|
+
|
|
1184
|
+
# --------------------------------------------------
|
|
1185
|
+
# 1. Simulation configuration
|
|
1186
|
+
# --------------------------------------------------
|
|
1187
|
+
|
|
1188
|
+
grid_shape = (50, 50, 50)
|
|
1189
|
+
|
|
1190
|
+
d = (
|
|
1191
|
+
0.5, # dx [m]
|
|
1192
|
+
0.5, # dy [m]
|
|
1193
|
+
0.5, # dz [m]
|
|
1194
|
+
0.1 # dt [s]
|
|
1195
|
+
)
|
|
1196
|
+
|
|
1197
|
+
total_time = 1000.0
|
|
1198
|
+
|
|
1199
|
+
# --------------------------------------------------
|
|
1200
|
+
# 2. Wind field
|
|
1201
|
+
# --------------------------------------------------
|
|
1202
|
+
|
|
1203
|
+
wind = UniformField(
|
|
1204
|
+
grid_shape=grid_shape,
|
|
1205
|
+
initial_velocity=(5.0, 0.0, 0.0)
|
|
1206
|
+
)
|
|
1207
|
+
|
|
1208
|
+
# --------------------------------------------------
|
|
1209
|
+
# 3. Transport model
|
|
1210
|
+
# --------------------------------------------------
|
|
1211
|
+
|
|
1212
|
+
simulation = OutdoorsDiffusionAdvectionDecay(
|
|
1213
|
+
wind_model=wind,
|
|
1214
|
+
grid_shape=grid_shape,
|
|
1215
|
+
d=d,
|
|
1216
|
+
total_time=total_time,
|
|
1217
|
+
diffusion_coefficient=(
|
|
1218
|
+
1e-3,
|
|
1219
|
+
1e-3,
|
|
1220
|
+
1e-3
|
|
1221
|
+
),
|
|
1222
|
+
species_name="U-234",
|
|
1223
|
+
source_positions=[
|
|
1224
|
+
(25, 25, 25)
|
|
1225
|
+
],
|
|
1226
|
+
emission_rate=3.0
|
|
1227
|
+
)
|
|
1228
|
+
|
|
1229
|
+
# --------------------------------------------------
|
|
1230
|
+
# 4. Run simulation
|
|
1231
|
+
# --------------------------------------------------
|
|
1232
|
+
|
|
1233
|
+
results = simulation.run(
|
|
1234
|
+
save_every_X_iteration=100
|
|
1235
|
+
)
|
|
1236
|
+
|
|
1237
|
+
# --------------------------------------------------
|
|
1238
|
+
# 5. Visualize concentration
|
|
1239
|
+
# --------------------------------------------------
|
|
1240
|
+
|
|
1241
|
+
simulation.plot_instant(
|
|
1242
|
+
plot_name="Radioactive dispersion",
|
|
1243
|
+
visualization_type="3d",
|
|
1244
|
+
vertical_axis="z",
|
|
1245
|
+
time_to_check=100.0
|
|
1246
|
+
)
|
|
1247
|
+
|
|
1248
|
+
# --------------------------------------------------
|
|
1249
|
+
# 6. Export concentration
|
|
1250
|
+
# --------------------------------------------------
|
|
1251
|
+
|
|
1252
|
+
simulation.make_csv_for_instant(
|
|
1253
|
+
time=100.0,
|
|
1254
|
+
filename="concentration_100s.csv"
|
|
1255
|
+
)
|
|
1256
|
+
|
|
1257
|
+
# --------------------------------------------------
|
|
1258
|
+
# 7. Create HRTM model
|
|
1259
|
+
# --------------------------------------------------
|
|
1260
|
+
|
|
1261
|
+
hrtm = HRTM(
|
|
1262
|
+
simulation,
|
|
1263
|
+
population_type="public",
|
|
1264
|
+
age_group="adult",
|
|
1265
|
+
gender="male",
|
|
1266
|
+
physical_activity="sitting",
|
|
1267
|
+
absorption="F",
|
|
1268
|
+
exposition_time=100.0
|
|
1269
|
+
)
|
|
1270
|
+
|
|
1271
|
+
# --------------------------------------------------
|
|
1272
|
+
# 8. Calculate dose
|
|
1273
|
+
# --------------------------------------------------
|
|
1274
|
+
|
|
1275
|
+
dose = hrtm.effective_dose_commitment()
|
|
1276
|
+
|
|
1277
|
+
# --------------------------------------------------
|
|
1278
|
+
# 9. Visualize dose
|
|
1279
|
+
# --------------------------------------------------
|
|
1280
|
+
|
|
1281
|
+
hrtm.plot_instant(
|
|
1282
|
+
plot_name="Inhalation dose",
|
|
1283
|
+
visualization_type="3d",
|
|
1284
|
+
vertical_axis="z",
|
|
1285
|
+
time_to_check=100.0
|
|
1286
|
+
)
|
|
1287
|
+
|
|
1288
|
+
# --------------------------------------------------
|
|
1289
|
+
# 10. Export dose
|
|
1290
|
+
# --------------------------------------------------
|
|
1291
|
+
|
|
1292
|
+
hrtm.make_csv_for_instant(
|
|
1293
|
+
time=100.0,
|
|
1294
|
+
filename="dose_100s.csv"
|
|
1295
|
+
)
|
|
1296
|
+
```
|
|
1297
|
+
|
|
1298
|
+
---
|
|
1299
|
+
|
|
1300
|
+
# Recommended workflow
|
|
1301
|
+
|
|
1302
|
+
For most applications, the recommended workflow is:
|
|
1303
|
+
|
|
1304
|
+
### Step 1 — Select the environment
|
|
1305
|
+
|
|
1306
|
+
Choose between:
|
|
1307
|
+
|
|
1308
|
+
```text
|
|
1309
|
+
DiffusionAdvectionDecay
|
|
1310
|
+
IndoorsDiffusionAdvectionDecay
|
|
1311
|
+
OutdoorsDiffusionAdvectionDecay
|
|
1312
|
+
```
|
|
1313
|
+
|
|
1314
|
+
### Step 2 — Define the grid
|
|
1315
|
+
|
|
1316
|
+
Choose:
|
|
1317
|
+
|
|
1318
|
+
```python
|
|
1319
|
+
grid_shape
|
|
1320
|
+
d
|
|
1321
|
+
```
|
|
1322
|
+
|
|
1323
|
+
according to the physical dimensions and desired spatial resolution.
|
|
1324
|
+
|
|
1325
|
+
### Step 3 — Select the radionuclide
|
|
1326
|
+
|
|
1327
|
+
Specify:
|
|
1328
|
+
|
|
1329
|
+
```python
|
|
1330
|
+
species_name
|
|
1331
|
+
```
|
|
1332
|
+
|
|
1333
|
+
The radioactive decay constant is obtained internally from the library.
|
|
1334
|
+
|
|
1335
|
+
### Step 4 — Define the sources
|
|
1336
|
+
|
|
1337
|
+
Specify:
|
|
1338
|
+
|
|
1339
|
+
```python
|
|
1340
|
+
source_positions
|
|
1341
|
+
emission_rate
|
|
1342
|
+
```
|
|
1343
|
+
|
|
1344
|
+
### Step 5 — Configure transport
|
|
1345
|
+
|
|
1346
|
+
Set:
|
|
1347
|
+
|
|
1348
|
+
```python
|
|
1349
|
+
diffusion_coefficient
|
|
1350
|
+
```
|
|
1351
|
+
|
|
1352
|
+
and, when appropriate, a wind model or ventilation configuration.
|
|
1353
|
+
|
|
1354
|
+
### Step 6 — Check numerical stability
|
|
1355
|
+
|
|
1356
|
+
The library performs the stability checks automatically.
|
|
1357
|
+
|
|
1358
|
+
### Step 7 — Run the simulation
|
|
1359
|
+
|
|
1360
|
+
```python
|
|
1361
|
+
results = simulation.run(
|
|
1362
|
+
save_every_X_iteration=100
|
|
1363
|
+
)
|
|
1364
|
+
```
|
|
1365
|
+
|
|
1366
|
+
### Step 8 — Inspect the concentration field
|
|
1367
|
+
|
|
1368
|
+
Use:
|
|
1369
|
+
|
|
1370
|
+
```python
|
|
1371
|
+
plot_instant()
|
|
1372
|
+
```
|
|
1373
|
+
|
|
1374
|
+
and/or:
|
|
1375
|
+
|
|
1376
|
+
```python
|
|
1377
|
+
animate()
|
|
1378
|
+
```
|
|
1379
|
+
|
|
1380
|
+
### Step 9 — Export results
|
|
1381
|
+
|
|
1382
|
+
Use:
|
|
1383
|
+
|
|
1384
|
+
```python
|
|
1385
|
+
make_csv_for_instant()
|
|
1386
|
+
```
|
|
1387
|
+
|
|
1388
|
+
### Step 10 — Calculate dose
|
|
1389
|
+
|
|
1390
|
+
Create an `HRTM` object and execute:
|
|
1391
|
+
|
|
1392
|
+
```python
|
|
1393
|
+
hrtm.effective_dose_commitment()
|
|
1394
|
+
```
|
|
1395
|
+
|
|
1396
|
+
---
|
|
1397
|
+
|
|
1398
|
+
# Physical units
|
|
1399
|
+
|
|
1400
|
+
The current implementation uses SI units for the principal physical quantities.
|
|
1401
|
+
|
|
1402
|
+
| Quantity | Unit |
|
|
1403
|
+
| -------------------------- | ----- |
|
|
1404
|
+
| Spatial coordinates | m |
|
|
1405
|
+
| Time | s |
|
|
1406
|
+
| Diffusion coefficient | m²/s |
|
|
1407
|
+
| Wind velocity | m/s |
|
|
1408
|
+
| Wall deposition velocity | m/s |
|
|
1409
|
+
| Concentration | Bq/m³ |
|
|
1410
|
+
| Radioactive decay constant | s⁻¹ |
|
|
1411
|
+
|
|
1412
|
+
The spatial discretization:
|
|
1413
|
+
|
|
1414
|
+
```python
|
|
1415
|
+
d = (dx, dy, dz, dt)
|
|
1416
|
+
```
|
|
1417
|
+
|
|
1418
|
+
must therefore be defined consistently with these units.
|
|
1419
|
+
|
|
1420
|
+
---
|
|
1421
|
+
|
|
1422
|
+
# Important implementation considerations
|
|
1423
|
+
|
|
1424
|
+
## Grid indices vs physical coordinates
|
|
1425
|
+
|
|
1426
|
+
Source positions are specified using grid indices:
|
|
1427
|
+
|
|
1428
|
+
```python
|
|
1429
|
+
source_positions=[
|
|
1430
|
+
(i, j, k)
|
|
1431
|
+
]
|
|
1432
|
+
```
|
|
1433
|
+
|
|
1434
|
+
rather than physical coordinates.
|
|
1435
|
+
|
|
1436
|
+
The physical position is:
|
|
1437
|
+
|
|
1438
|
+
$$
|
|
1439
|
+
x=i\Delta x
|
|
1440
|
+
$$
|
|
1441
|
+
|
|
1442
|
+
$$
|
|
1443
|
+
y=j\Delta y
|
|
1444
|
+
$$
|
|
1445
|
+
|
|
1446
|
+
$$
|
|
1447
|
+
z=k\Delta z
|
|
1448
|
+
$$
|
|
1449
|
+
|
|
1450
|
+
---
|
|
1451
|
+
|
|
1452
|
+
## Saved simulation times
|
|
1453
|
+
|
|
1454
|
+
Only concentration fields saved by:
|
|
1455
|
+
|
|
1456
|
+
```python
|
|
1457
|
+
run(save_every_X_iteration=...)
|
|
1458
|
+
```
|
|
1459
|
+
|
|
1460
|
+
are available for visualization, export and subsequent HRTM processing.
|
|
1461
|
+
|
|
1462
|
+
For example:
|
|
1463
|
+
|
|
1464
|
+
```python
|
|
1465
|
+
simulation.run(
|
|
1466
|
+
save_every_X_iteration=100
|
|
1467
|
+
)
|
|
1468
|
+
```
|
|
1469
|
+
|
|
1470
|
+
does not necessarily save every time step.
|
|
1471
|
+
|
|
1472
|
+
Consequently, requesting an unsaved time:
|
|
1473
|
+
|
|
1474
|
+
```python
|
|
1475
|
+
simulation.plot_instant(
|
|
1476
|
+
time_to_check=37.0
|
|
1477
|
+
)
|
|
1478
|
+
```
|
|
1479
|
+
|
|
1480
|
+
may result in an error if that time is not present in the saved fields.
|
|
1481
|
+
|
|
1482
|
+
---
|
|
1483
|
+
|
|
1484
|
+
## Memory requirements
|
|
1485
|
+
|
|
1486
|
+
Three-dimensional concentration fields can require significant memory.
|
|
1487
|
+
|
|
1488
|
+
Saving many fields with:
|
|
1489
|
+
|
|
1490
|
+
```python
|
|
1491
|
+
save_every_X_iteration=1
|
|
1492
|
+
```
|
|
1493
|
+
|
|
1494
|
+
can result in a large memory footprint.
|
|
1495
|
+
|
|
1496
|
+
For long simulations or high-resolution grids, it is therefore advisable to select an appropriate saving interval.
|
|
1497
|
+
|
|
1498
|
+
For example:
|
|
1499
|
+
|
|
1500
|
+
```python
|
|
1501
|
+
save_every_X_iteration=100
|
|
1502
|
+
```
|
|
1503
|
+
|
|
1504
|
+
stores considerably fewer fields than:
|
|
1505
|
+
|
|
1506
|
+
```python
|
|
1507
|
+
save_every_X_iteration=1
|
|
1508
|
+
```
|
|
1509
|
+
|
|
1510
|
+
---
|
|
1511
|
+
|
|
1512
|
+
# Limitations and considerations
|
|
1513
|
+
|
|
1514
|
+
This README documents the current implementation of `DifAdDec`. The numerical model and its physical interpretation should be considered carefully before applying the results to real radiological protection assessments.
|
|
1515
|
+
|
|
1516
|
+
In particular:
|
|
1517
|
+
|
|
1518
|
+
* Numerical stability depends on the selected discretization and transport parameters.
|
|
1519
|
+
* Source positions are defined by grid indices.
|
|
1520
|
+
* Saved fields determine which times can subsequently be visualized or exported.
|
|
1521
|
+
* Increasing grid resolution significantly increases computational and memory requirements.
|
|
1522
|
+
* The physical parameters supplied by the user should be consistent in units and representative of the scenario being simulated.
|
|
1523
|
+
* Dose calculations depend on the concentration fields generated by the transport simulation and on the HRTM input parameters.
|
|
1524
|
+
|
|
1525
|
+
For research applications, model assumptions, numerical convergence and sensitivity to discretization should be assessed before interpreting simulation results.
|
|
1526
|
+
|
|
1527
|
+
---
|
|
1528
|
+
|
|
1529
|
+
# Contact
|
|
1530
|
+
|
|
1531
|
+
For questions, suggestions or issues related to `DifAdDec`, please use the GitHub issue tracker associated with this repository.
|