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.
Files changed (27) hide show
  1. difaddec-0.1.0/PKG-INFO +1531 -0
  2. difaddec-0.1.0/README.md +1501 -0
  3. difaddec-0.1.0/pyproject.toml +116 -0
  4. difaddec-0.1.0/setup.cfg +4 -0
  5. difaddec-0.1.0/src/DifAdDec/__init__.py +20 -0
  6. difaddec-0.1.0/src/DifAdDec/data/dcf_public.json +62638 -0
  7. difaddec-0.1.0/src/DifAdDec/data/dcf_workers.json +15745 -0
  8. difaddec-0.1.0/src/DifAdDec/data/hrtm_subjects_breathing.json +403 -0
  9. difaddec-0.1.0/src/DifAdDec/data/radionuclidos.csv +758 -0
  10. difaddec-0.1.0/src/DifAdDec/dosimetry/__init__.py +5 -0
  11. difaddec-0.1.0/src/DifAdDec/dosimetry/hrtm.py +325 -0
  12. difaddec-0.1.0/src/DifAdDec/transport/__init__.py +28 -0
  13. difaddec-0.1.0/src/DifAdDec/transport/diffusion_advection_decay.py +356 -0
  14. difaddec-0.1.0/src/DifAdDec/transport/indoors_diffusion_advection_decay.py +617 -0
  15. difaddec-0.1.0/src/DifAdDec/transport/outdoors_diffusion_advection_decay.py +146 -0
  16. difaddec-0.1.0/src/DifAdDec/transport/windfield.py +116 -0
  17. difaddec-0.1.0/src/DifAdDec/utils/__init__.py +29 -0
  18. difaddec-0.1.0/src/DifAdDec/utils/hrtm_aux.py +37 -0
  19. difaddec-0.1.0/src/DifAdDec/utils/radionuclides.py +51 -0
  20. difaddec-0.1.0/src/DifAdDec/utils/stability.py +23 -0
  21. difaddec-0.1.0/src/DifAdDec/visualization/__init__.py +27 -0
  22. difaddec-0.1.0/src/DifAdDec/visualization/visualization.py +303 -0
  23. difaddec-0.1.0/src/difaddec.egg-info/PKG-INFO +1531 -0
  24. difaddec-0.1.0/src/difaddec.egg-info/SOURCES.txt +25 -0
  25. difaddec-0.1.0/src/difaddec.egg-info/dependency_links.txt +1 -0
  26. difaddec-0.1.0/src/difaddec.egg-info/requires.txt +9 -0
  27. difaddec-0.1.0/src/difaddec.egg-info/top_level.txt +1 -0
@@ -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.