atmorad-py 0.1.1__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 (54) hide show
  1. atmorad_py-0.1.1/.github/workflows/ci.yml +47 -0
  2. atmorad_py-0.1.1/.gitignore +12 -0
  3. atmorad_py-0.1.1/.python-version +1 -0
  4. atmorad_py-0.1.1/LICENSE +21 -0
  5. atmorad_py-0.1.1/PKG-INFO +165 -0
  6. atmorad_py-0.1.1/README.md +146 -0
  7. atmorad_py-0.1.1/TODO.md +34 -0
  8. atmorad_py-0.1.1/example.py +54 -0
  9. atmorad_py-0.1.1/examples/adjacency_effect.toml +36 -0
  10. atmorad_py-0.1.1/pyproject.toml +51 -0
  11. atmorad_py-0.1.1/simulation.toml +98 -0
  12. atmorad_py-0.1.1/src/atmorad/__init__.py +18 -0
  13. atmorad_py-0.1.1/src/atmorad/builder.py +108 -0
  14. atmorad_py-0.1.1/src/atmorad/cli.py +121 -0
  15. atmorad_py-0.1.1/src/atmorad/config/__init__.py +19 -0
  16. atmorad_py-0.1.1/src/atmorad/config/defaults.toml +94 -0
  17. atmorad_py-0.1.1/src/atmorad/config/models.py +59 -0
  18. atmorad_py-0.1.1/src/atmorad/config/parser.py +55 -0
  19. atmorad_py-0.1.1/src/atmorad/config/simulation.toml +98 -0
  20. atmorad_py-0.1.1/src/atmorad/constants.py +13 -0
  21. atmorad_py-0.1.1/src/atmorad/detectors/__init__.py +5 -0
  22. atmorad_py-0.1.1/src/atmorad/detectors/base.py +24 -0
  23. atmorad_py-0.1.1/src/atmorad/detectors/boundary_flux.py +80 -0
  24. atmorad_py-0.1.1/src/atmorad/detectors/builder.py +31 -0
  25. atmorad_py-0.1.1/src/atmorad/detectors/fate.py +42 -0
  26. atmorad_py-0.1.1/src/atmorad/detectors/flux.py +59 -0
  27. atmorad_py-0.1.1/src/atmorad/detectors/heating.py +42 -0
  28. atmorad_py-0.1.1/src/atmorad/detectors/paths.py +59 -0
  29. atmorad_py-0.1.1/src/atmorad/detectors/plane_flux.py +120 -0
  30. atmorad_py-0.1.1/src/atmorad/detectors/results.py +37 -0
  31. atmorad_py-0.1.1/src/atmorad/engine/__init__.py +7 -0
  32. atmorad_py-0.1.1/src/atmorad/engine/core.py +130 -0
  33. atmorad_py-0.1.1/src/atmorad/engine/runner.py +91 -0
  34. atmorad_py-0.1.1/src/atmorad/environment/__init__.py +27 -0
  35. atmorad_py-0.1.1/src/atmorad/environment/atmosphere.py +186 -0
  36. atmorad_py-0.1.1/src/atmorad/environment/scene.py +89 -0
  37. atmorad_py-0.1.1/src/atmorad/environment/surface.py +175 -0
  38. atmorad_py-0.1.1/src/atmorad/models/__init__.py +7 -0
  39. atmorad_py-0.1.1/src/atmorad/models/batch.py +31 -0
  40. atmorad_py-0.1.1/src/atmorad/models/context.py +10 -0
  41. atmorad_py-0.1.1/src/atmorad/output/__init__.py +4 -0
  42. atmorad_py-0.1.1/src/atmorad/output/analyzer.py +201 -0
  43. atmorad_py-0.1.1/src/atmorad/output/data_io.py +123 -0
  44. atmorad_py-0.1.1/src/atmorad/physics/__init__.py +14 -0
  45. atmorad_py-0.1.1/src/atmorad/physics/geometry.py +53 -0
  46. atmorad_py-0.1.1/src/atmorad/physics/reflection.py +64 -0
  47. atmorad_py-0.1.1/src/atmorad/physics/registry.py +18 -0
  48. atmorad_py-0.1.1/src/atmorad/physics/scattering.py +73 -0
  49. atmorad_py-0.1.1/tests/__init__.py +0 -0
  50. atmorad_py-0.1.1/tests/configs/demo_config.toml +79 -0
  51. atmorad_py-0.1.1/tests/configs/test_config.toml +48 -0
  52. atmorad_py-0.1.1/tests/conftest.py +12 -0
  53. atmorad_py-0.1.1/tests/test_basic.py +44 -0
  54. atmorad_py-0.1.1/uv.lock +1106 -0
@@ -0,0 +1,47 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [ "main" ]
6
+ pull_request:
7
+ branches: [ "main" ]
8
+
9
+ jobs:
10
+ test-and-lint:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ resolution: ["locked", "lowest-direct"]
15
+
16
+ steps:
17
+ - name: Checkout Repository
18
+ uses: actions/checkout@v6
19
+
20
+ - name: Install uv
21
+ uses: astral-sh/setup-uv@v8.1.0
22
+ with:
23
+ enable-cache: true
24
+ cache-dependency-glob: "uv.lock"
25
+
26
+ - name: Set up Python
27
+ uses: actions/setup-python@v6
28
+ with:
29
+ python-version-file: "pyproject.toml"
30
+
31
+ - name: Install Dependencies
32
+ run: |
33
+ if [ "${{ matrix.resolution }}" = "lowest-direct" ]; then
34
+ uv venv
35
+ uv pip install -e ".[dev]" --resolution lowest-direct
36
+ else
37
+ uv sync --all-extras --dev
38
+ fi
39
+
40
+ - name: Ruff
41
+ if: matrix.resolution == 'locked'
42
+ run: |
43
+ uv run ruff check .
44
+ uv run ruff format --check .
45
+
46
+ - name: Pytests
47
+ run: uv run pytest tests/
@@ -0,0 +1,12 @@
1
+ .venv
2
+ env/
3
+ .ruff_cache
4
+ .provenance
5
+ __pycache__/
6
+ fig/*
7
+ *egg-info
8
+ build/
9
+ dist/
10
+ results*/
11
+ *.png
12
+ *.npz
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Karol Dąbrowski
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,165 @@
1
+ Metadata-Version: 2.4
2
+ Name: atmorad-py
3
+ Version: 0.1.1
4
+ Summary: A 3D monte carlo simulation of Atmospheric Radiative Transfer
5
+ Author-email: Karol Dąbrowski <atmorad@kdabr.com>
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Topic :: Scientific/Engineering :: Physics
12
+ Requires-Python: >=3.10
13
+ Requires-Dist: cmocean>=2.0.0
14
+ Requires-Dist: matplotlib>=3.5.0
15
+ Requires-Dist: numpy>=1.26.0
16
+ Requires-Dist: seaborn>=0.11.0
17
+ Requires-Dist: tqdm>=4.60.0
18
+ Description-Content-Type: text/markdown
19
+
20
+ # AtmoRad
21
+ ## A vectorized Monte Carlo simulation of atmospheric radiative transfer.
22
+
23
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
24
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
25
+
26
+ | **2D Surface absorption map** | **Sample photon paths** |
27
+ | :--- | :--- |
28
+ | ![map](https://raw.githubusercontent.com/dabrokarol/atmorad-py/main/docs/img/surface_absorption_map.png) | ![paths](https://raw.githubusercontent.com/dabrokarol/atmorad-py/main/docs/img/3d_photon_paths.png) |
29
+ | **Vertical flux profile** | **Vertical absorption profile** |
30
+ | ![profile](https://raw.githubusercontent.com/dabrokarol/atmorad-py/main/docs/img/vertical_flux_profile.png)| ![hist](https://raw.githubusercontent.com/dabrokarol/atmorad-py/main/docs/img/heating_profile.png) |
31
+
32
+ ## Overview:
33
+
34
+ This project simulates the propagation of light through a plane-parallel atmosphere over a horizontally mixed surface and its interactions with the ground boundary. Developed as a student project, created to learn computational physics and software development.
35
+
36
+ ### Physical model
37
+ - **Analog Monte Carlo Approach**: Light is simulated using discrete photon packets. Final flux is calculated as a fraction of the total detected packets.
38
+ - **Plane-parallel approximation**: The atmosphere consists of horizontally uniform layers.
39
+ - **Multi-material atmospheric layers**: Layers can consist of multiple atmospheric materials simultaneously. A photon is assigned a material randomly when it is initialized and again when it crosses into a new layer. Each material has its own extinction coefficient, SSA and phase function.
40
+ - **Custom Phase Functions**: Henyey-Greenstein and Rayleigh phase functions are already implemented in the simulation, but any custom user-defined function can be constructed using the `Scattering` class.
41
+ - **Surface Reflections**: The surface consists of materials, each with its own albedo, a predefined BRDF reflection model (`Lambertian`, `Mirror`), and a `ProceduralMap` that outputs material ID based on spatial coordinates.
42
+ - **Photon Properties**: Light is treated as monochromatic, non-polarized particles. During the simulation they can be scattered, reflected, or absorbed.
43
+ - **Incident Irradiance & Adjacency Effect**: Custom detectors allow measuring downward/upward incident flux at any arbitrary altitude - helpful for visualizing the adjacency effect.
44
+
45
+
46
+ ## Technical implementation:
47
+ - The simulation uses `numpy` to simulate photons simultaneously in large batches.
48
+ - The results are plotted using `matplotlib` and `seaborn` (e.g., photon paths, flux profile, 2D ground flux maps)
49
+ - The code uses multiprocessing to run batches in parallel.
50
+
51
+ ## Installation:
52
+ - Using `uv` ([install uv](https://docs.astral.sh/uv/getting-started/installation/)):
53
+ ```bash
54
+ uv tool install atmorad-py
55
+ ```
56
+ - Using `pip`:
57
+ ```bash
58
+ pip install atmorad-py
59
+ ```
60
+ - Run the simulation:
61
+ ```bash
62
+ atmorad --init
63
+ atmorad simulation.toml
64
+ ```
65
+ - Check `results/` directory for simulation artifacts.
66
+
67
+ ## Project Structure
68
+ - `engine/`: Divides photons into batches and runs the simulation.
69
+ - `physics/`: Contains a rotation function, scattering phase functions, reflection functions.
70
+ - `environment/`: Keeps track of the environment. Contains `Scene`, `Atmosphere` and `Surface` classes.
71
+ - `detectors/`: Provides functionality for tracking photons during the simulation and generates results.
72
+ - `output/`: Handles results and figure generation.
73
+ - `config/` and `builder.py`: Parses `.toml` configuration file and generates simulation context.
74
+ - `cli.py`: Provides CLI for `atmorad`.
75
+
76
+ ## Customization
77
+ You can define your own surface reflection algorithms and scattering phase functions using decorators as shown below:
78
+
79
+ ```python
80
+ import numpy as np
81
+ from atmorad import build_context, MCRadiationRunner, DataIO, ResultAnalyzer
82
+ from atmorad import SurfaceReflection, register_reflection, orientation
83
+ from atmorad import Scattering, register_scattering
84
+
85
+ # 1. Register a custom surface reflection
86
+ @register_reflection("custom-reflection")
87
+ class CustomReflection(SurfaceReflection):
88
+ # Specify arbitrary custom parameters
89
+ def __init__(self, param_1, param_2):
90
+ self.param_1 = param_1
91
+ self.param_2 = param_2
92
+ def reflect(self, direction, rand_1, rand_2):
93
+ # Your custom reflection physics here
94
+ cos_theta = np.sqrt(rand_1)
95
+ sin_theta = np.sqrt(1.0 - rand_1)
96
+
97
+ # you can also use specified parameters
98
+ # self.param_1, self.param_2
99
+
100
+ phi = rand_2 * 2 * np.pi
101
+ cos_phi, sin_phi = np.cos(phi), np.sin(phi)
102
+
103
+ return orientation(cos_theta, sin_theta, cos_phi, sin_phi)
104
+
105
+ # 2. Register a custom scattering phase function
106
+ @register_scattering("custom-scattering")
107
+ class CustomScattering(Scattering):
108
+ # Specify arbitrary custom parameters
109
+ def __init__(self, g, resolution=1000):
110
+ # Define a pdf array
111
+ self.g = g
112
+ cos_grid = np.linspace(-1, 1, resolution)
113
+
114
+ pdf = (1 - g**2) / (2 * (1 + g**2 - 2 * g * cos_grid) ** 1.5)
115
+
116
+ super().__init__(pdf_array=pdf, resolution=resolution)
117
+
118
+ # 3. Run the simulation using custom names in your config
119
+ if __name__ == "__main__":
120
+ context = build_context("simulation.toml")
121
+ runner = MCRadiationRunner(context)
122
+ runner.run()
123
+
124
+ # 4. Save and analyze results
125
+ results = runner.get_results()
126
+ outputs = DataIO(context.config)
127
+ analyzer = ResultAnalyzer(results, context.config)
128
+
129
+ outputs.save_all_artifacts(analyzer, results)
130
+ outputs.save_metadata(context.config, results)
131
+ outputs.save_results(results)
132
+ ```
133
+ In `simulation.toml` you can specify your defined scatterings and reflections:
134
+ ```toml
135
+ [atmosphere_materials.custom-atm-material]
136
+ ssa = 0.9
137
+ scattering = {type = "custom-scattering", g=0.8}
138
+
139
+ [surface_materials.custom-surf-material]
140
+ albedo = 0.5
141
+ reflection = {type = "custom-reflection", param_1=2, param_2=1.3} # match param names defined in python
142
+ ```
143
+ Then you can use your defined materials for atmospheric layers and surface maps:
144
+ ```toml
145
+ [[layer]]
146
+ z_range_km = [0, 2]
147
+ materials = [{type = "custom-atm-material", weight = 1.0}]
148
+
149
+ # ...
150
+
151
+ [surface]
152
+ name = "uniform"
153
+ material = "custom-surf-material"
154
+ ```
155
+
156
+ ## References and Literature
157
+ - (in Polish) Script for Lecture about [Radiative Processes in the Atmosphere](https://www.igf.fuw.edu.pl/~kmark/stacja/wyklady/ProcesyRadiacyjne/2013/WykladRadiacjaKlimat.pdf), Prof. K. Markowicz, Faculty of Physics, University of Warsaw, 2013.
158
+
159
+ ## Acknowledgments
160
+ - This project was inspired by the lectures on *Radiative Processes in the Atmosphere* by Prof. K. Markowicz, Faculty of Physics, University of Warsaw.
161
+ - Large Language Models were used for code debugging and learning best Python practices (e.g. `dataclasses`, `__init__.py` import interfaces, class responsibilities, config parsing).
162
+
163
+ ## Contributing
164
+ Feel free to open an [Issue](https://github.com/dabrokarol/atmorad-py/issues) or submit a Pull Request if you'd like to contribute or report a bug.
165
+
@@ -0,0 +1,146 @@
1
+ # AtmoRad
2
+ ## A vectorized Monte Carlo simulation of atmospheric radiative transfer.
3
+
4
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ | **2D Surface absorption map** | **Sample photon paths** |
8
+ | :--- | :--- |
9
+ | ![map](https://raw.githubusercontent.com/dabrokarol/atmorad-py/main/docs/img/surface_absorption_map.png) | ![paths](https://raw.githubusercontent.com/dabrokarol/atmorad-py/main/docs/img/3d_photon_paths.png) |
10
+ | **Vertical flux profile** | **Vertical absorption profile** |
11
+ | ![profile](https://raw.githubusercontent.com/dabrokarol/atmorad-py/main/docs/img/vertical_flux_profile.png)| ![hist](https://raw.githubusercontent.com/dabrokarol/atmorad-py/main/docs/img/heating_profile.png) |
12
+
13
+ ## Overview:
14
+
15
+ This project simulates the propagation of light through a plane-parallel atmosphere over a horizontally mixed surface and its interactions with the ground boundary. Developed as a student project, created to learn computational physics and software development.
16
+
17
+ ### Physical model
18
+ - **Analog Monte Carlo Approach**: Light is simulated using discrete photon packets. Final flux is calculated as a fraction of the total detected packets.
19
+ - **Plane-parallel approximation**: The atmosphere consists of horizontally uniform layers.
20
+ - **Multi-material atmospheric layers**: Layers can consist of multiple atmospheric materials simultaneously. A photon is assigned a material randomly when it is initialized and again when it crosses into a new layer. Each material has its own extinction coefficient, SSA and phase function.
21
+ - **Custom Phase Functions**: Henyey-Greenstein and Rayleigh phase functions are already implemented in the simulation, but any custom user-defined function can be constructed using the `Scattering` class.
22
+ - **Surface Reflections**: The surface consists of materials, each with its own albedo, a predefined BRDF reflection model (`Lambertian`, `Mirror`), and a `ProceduralMap` that outputs material ID based on spatial coordinates.
23
+ - **Photon Properties**: Light is treated as monochromatic, non-polarized particles. During the simulation they can be scattered, reflected, or absorbed.
24
+ - **Incident Irradiance & Adjacency Effect**: Custom detectors allow measuring downward/upward incident flux at any arbitrary altitude - helpful for visualizing the adjacency effect.
25
+
26
+
27
+ ## Technical implementation:
28
+ - The simulation uses `numpy` to simulate photons simultaneously in large batches.
29
+ - The results are plotted using `matplotlib` and `seaborn` (e.g., photon paths, flux profile, 2D ground flux maps)
30
+ - The code uses multiprocessing to run batches in parallel.
31
+
32
+ ## Installation:
33
+ - Using `uv` ([install uv](https://docs.astral.sh/uv/getting-started/installation/)):
34
+ ```bash
35
+ uv tool install atmorad-py
36
+ ```
37
+ - Using `pip`:
38
+ ```bash
39
+ pip install atmorad-py
40
+ ```
41
+ - Run the simulation:
42
+ ```bash
43
+ atmorad --init
44
+ atmorad simulation.toml
45
+ ```
46
+ - Check `results/` directory for simulation artifacts.
47
+
48
+ ## Project Structure
49
+ - `engine/`: Divides photons into batches and runs the simulation.
50
+ - `physics/`: Contains a rotation function, scattering phase functions, reflection functions.
51
+ - `environment/`: Keeps track of the environment. Contains `Scene`, `Atmosphere` and `Surface` classes.
52
+ - `detectors/`: Provides functionality for tracking photons during the simulation and generates results.
53
+ - `output/`: Handles results and figure generation.
54
+ - `config/` and `builder.py`: Parses `.toml` configuration file and generates simulation context.
55
+ - `cli.py`: Provides CLI for `atmorad`.
56
+
57
+ ## Customization
58
+ You can define your own surface reflection algorithms and scattering phase functions using decorators as shown below:
59
+
60
+ ```python
61
+ import numpy as np
62
+ from atmorad import build_context, MCRadiationRunner, DataIO, ResultAnalyzer
63
+ from atmorad import SurfaceReflection, register_reflection, orientation
64
+ from atmorad import Scattering, register_scattering
65
+
66
+ # 1. Register a custom surface reflection
67
+ @register_reflection("custom-reflection")
68
+ class CustomReflection(SurfaceReflection):
69
+ # Specify arbitrary custom parameters
70
+ def __init__(self, param_1, param_2):
71
+ self.param_1 = param_1
72
+ self.param_2 = param_2
73
+ def reflect(self, direction, rand_1, rand_2):
74
+ # Your custom reflection physics here
75
+ cos_theta = np.sqrt(rand_1)
76
+ sin_theta = np.sqrt(1.0 - rand_1)
77
+
78
+ # you can also use specified parameters
79
+ # self.param_1, self.param_2
80
+
81
+ phi = rand_2 * 2 * np.pi
82
+ cos_phi, sin_phi = np.cos(phi), np.sin(phi)
83
+
84
+ return orientation(cos_theta, sin_theta, cos_phi, sin_phi)
85
+
86
+ # 2. Register a custom scattering phase function
87
+ @register_scattering("custom-scattering")
88
+ class CustomScattering(Scattering):
89
+ # Specify arbitrary custom parameters
90
+ def __init__(self, g, resolution=1000):
91
+ # Define a pdf array
92
+ self.g = g
93
+ cos_grid = np.linspace(-1, 1, resolution)
94
+
95
+ pdf = (1 - g**2) / (2 * (1 + g**2 - 2 * g * cos_grid) ** 1.5)
96
+
97
+ super().__init__(pdf_array=pdf, resolution=resolution)
98
+
99
+ # 3. Run the simulation using custom names in your config
100
+ if __name__ == "__main__":
101
+ context = build_context("simulation.toml")
102
+ runner = MCRadiationRunner(context)
103
+ runner.run()
104
+
105
+ # 4. Save and analyze results
106
+ results = runner.get_results()
107
+ outputs = DataIO(context.config)
108
+ analyzer = ResultAnalyzer(results, context.config)
109
+
110
+ outputs.save_all_artifacts(analyzer, results)
111
+ outputs.save_metadata(context.config, results)
112
+ outputs.save_results(results)
113
+ ```
114
+ In `simulation.toml` you can specify your defined scatterings and reflections:
115
+ ```toml
116
+ [atmosphere_materials.custom-atm-material]
117
+ ssa = 0.9
118
+ scattering = {type = "custom-scattering", g=0.8}
119
+
120
+ [surface_materials.custom-surf-material]
121
+ albedo = 0.5
122
+ reflection = {type = "custom-reflection", param_1=2, param_2=1.3} # match param names defined in python
123
+ ```
124
+ Then you can use your defined materials for atmospheric layers and surface maps:
125
+ ```toml
126
+ [[layer]]
127
+ z_range_km = [0, 2]
128
+ materials = [{type = "custom-atm-material", weight = 1.0}]
129
+
130
+ # ...
131
+
132
+ [surface]
133
+ name = "uniform"
134
+ material = "custom-surf-material"
135
+ ```
136
+
137
+ ## References and Literature
138
+ - (in Polish) Script for Lecture about [Radiative Processes in the Atmosphere](https://www.igf.fuw.edu.pl/~kmark/stacja/wyklady/ProcesyRadiacyjne/2013/WykladRadiacjaKlimat.pdf), Prof. K. Markowicz, Faculty of Physics, University of Warsaw, 2013.
139
+
140
+ ## Acknowledgments
141
+ - This project was inspired by the lectures on *Radiative Processes in the Atmosphere* by Prof. K. Markowicz, Faculty of Physics, University of Warsaw.
142
+ - Large Language Models were used for code debugging and learning best Python practices (e.g. `dataclasses`, `__init__.py` import interfaces, class responsibilities, config parsing).
143
+
144
+ ## Contributing
145
+ Feel free to open an [Issue](https://github.com/dabrokarol/atmorad-py/issues) or submit a Pull Request if you'd like to contribute or report a bug.
146
+
@@ -0,0 +1,34 @@
1
+ <!-- Robię teraz parsofanie configa. -->
2
+ <!-- Trzeba zrobić konstruktory stosownych klas i stworzyć rejestry mapujące -->
3
+ <!-- Gdy będą gotowe, trzeba zrobić parser configa -->
4
+
5
+ <!-- Drugie zadanie: Results: tworzenie results/data-nazwa/ dla każdego eksperymentu
6
+ w folderze będzie config, matadane, wyniki, fig/{wykresy} -->
7
+
8
+ <!-- Trzecie zadanie: argparse
9
+ - --help-config - wypluwa informacje o tym co trzeba do configa
10
+ - --config=path - ścieżka do configa żeby łatwo wywoływać program -->
11
+
12
+ Czwarte zadanie:
13
+ dodanie modeli odbicia i rozpraszania
14
+ - RoughSpecularReflection
15
+
16
+ Piąte zadanie:
17
+ <!-- - zwróć uwagę na dzielenie przez zero w scene.py (żeby było np.inf, a nie NaN) -->
18
+ - zaimplementuj wskaźnik ile fotonów przetwarza się na sekundę (może nawet do wykresów)
19
+
20
+ <!-- Szóste zadanie: -->
21
+ <!-- - detektory klasa basedetector -->
22
+ <!-- - w scenie (lub symulacji) znajdują się eventy -->
23
+ <!-- - każdy detektor dostaje pozycje i kierunki fotonów, które biorą udział w evencie -->
24
+
25
+ <!-- BUG:
26
+ energia się nie dodaje -->
27
+
28
+ Zadanie:
29
+ Napisać mega basic testy
30
+
31
+ Zadanie:
32
+ Pobawić się
33
+
34
+ Zadanie:
@@ -0,0 +1,54 @@
1
+ import numpy as np
2
+
3
+ from atmorad import build_context, MCRadiationRunner, register_reflection, SurfaceReflection, orientation, Scattering, register_scattering, DataIO, ResultAnalyzer, rotate
4
+
5
+ @register_reflection("my-example-reflection")
6
+ class ExampleReflection(SurfaceReflection):
7
+ def reflect(self, direction, rand_1, rand_2):
8
+ # your reflection code
9
+
10
+ return orientation(cos_theta, sin_theta, cos_phi, sin_phi)
11
+
12
+ @register_reflection("lambertian")
13
+ class LambertianReflection(SurfaceReflection):
14
+ def reflect(self, direction, rand_1, rand_2):
15
+ phi = rand_2 * 2 * np.pi
16
+
17
+ cos_theta = np.sqrt(rand_1)
18
+ sin_theta = np.sqrt(1.0 - rand_1)
19
+
20
+ cos_phi = np.cos(phi)
21
+ sin_phi = np.sin(phi)
22
+ return (rotate())
23
+ return orientation(cos_theta, sin_theta, cos_phi, sin_phi)
24
+
25
+ @register_scattering("my-example-scattering")
26
+ class ExampleScattering(Scattering):
27
+ def __init__(self, example-param, )
28
+
29
+ @register_scattering("hg")
30
+ class HenyeyGreensteinScattering(Scattering):
31
+ def __init__(self, g: float, resolution=1000):
32
+ self.g = g
33
+ cos_grid = np.linspace(-1, 1, resolution)
34
+
35
+ if np.isclose(g, 1.0, atol=1e-9):
36
+ pdf = np.isclose(cos_grid, 1.0, atol=1e-9).astype(float)
37
+ elif np.isclose(g, -1.0, atol=1e-9):
38
+ pdf = np.isclose(cos_grid, -1.0, atol=1e-9).astype(float)
39
+ else:
40
+ pdf = (1 - g**2) / (2 * (1 + g**2 - 2 * g * cos_grid) ** 1.5)
41
+
42
+ super().__init__(pdf_array=pdf, resolution=resolution)
43
+
44
+ context = build_context("simulation.toml")
45
+ runner = MCRadiationRunner(context)
46
+ runner.run()
47
+
48
+ results = runner.get_results()
49
+
50
+ outputs = DataIO(context.config)
51
+ analyzer = ResultAnalyzer(results, context.config)
52
+ outputs.save_all_artifacts(analyzer, results)
53
+ outputs.save_metadata(context.config, results)
54
+ outputs.save_results(results)
@@ -0,0 +1,36 @@
1
+ [metadata]
2
+ experiment_name = "adjacency001"
3
+ description = "Demo showcasing adjacency effect"
4
+
5
+ [engine]
6
+ num_photons = 1_000_000
7
+ batch_size = 100_000
8
+ cpu_cores = 4
9
+
10
+ [source]
11
+ theta_sun_deg = 30
12
+
13
+ [geometry]
14
+ domain_size_x_km = 100
15
+ domain_size_y_km = 100
16
+
17
+ [detectors]
18
+ num_full_paths = 0
19
+ incident_flux_heights_km = [1e-5, 4.0]
20
+
21
+ [output]
22
+ save_incident_flux_maps = true
23
+ overwrite = true
24
+ save_plots = true
25
+
26
+
27
+ [[layer]]
28
+ z_range_km = [0, 4]
29
+ materials = [
30
+ {type = "dark-clouds", weight = 1.0}
31
+ ]
32
+
33
+ [surface]
34
+ name = "split-half-x"
35
+ material_left = "snow"
36
+ material_right = "ocean"
@@ -0,0 +1,51 @@
1
+ [build-system]
2
+ requires = ["hatchling", "hatch-vcs"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "atmorad-py"
7
+ dynamic = ["version"]
8
+ description = "A 3D monte carlo simulation of Atmospheric Radiative Transfer"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ authors = [
12
+ { name = "Karol Dąbrowski", email = "atmorad@kdabr.com" }
13
+ ]
14
+ license = { text = "MIT" }
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Operating System :: OS Independent",
19
+ "Topic :: Scientific/Engineering :: Physics",
20
+ ]
21
+ dependencies = [
22
+ "numpy>=1.26.0",
23
+ "matplotlib>=3.5.0",
24
+ "seaborn>=0.11.0",
25
+ "cmocean>=2.0.0",
26
+ "tqdm>=4.60.0",
27
+ ]
28
+
29
+ [project.scripts]
30
+ atmorad = "atmorad.cli:main"
31
+
32
+ [tool.hatch.version]
33
+ source = "vcs"
34
+
35
+ [tool.hatch.build.targets.wheel]
36
+ packages = ["src/atmorad"]
37
+ include-package-data = true
38
+
39
+
40
+ [dependency-groups]
41
+ dev = [
42
+ "pytest>=9.0.3",
43
+ "ruff>=0.15.13",
44
+ ]
45
+
46
+ [tool.ruff]
47
+ line-length = 100
48
+
49
+ [tool.ruff.lint]
50
+ select = ["E", "F", "I"]
51
+ ignore = ["E501"]
@@ -0,0 +1,98 @@
1
+ [metadata]
2
+ experiment_name = "demo001"
3
+ description = "A demo simulation."
4
+
5
+ [engine]
6
+ num_photons = 100_000
7
+ batch_size = 100_000
8
+ random_seed = 42
9
+ cpu_cores = 4
10
+
11
+ [source]
12
+ theta_sun_deg = 30
13
+ phi_sun_deg = 0
14
+ wavelength_nm = 530
15
+
16
+ [geometry]
17
+ domain_size_x_km = 100
18
+ domain_size_y_km = 100
19
+ boundary_condition = "periodic"
20
+
21
+ [detectors]
22
+ vertical_flux_resolution_km = 1.0
23
+ map2d_resolution_km = 1.0
24
+ num_full_paths = 200
25
+ incident_flux_heights_km = [1e-5, 4.0]
26
+
27
+ [output]
28
+ save_absorption_maps = true
29
+ save_incident_flux_maps = true
30
+ save_vertical_profile = true
31
+ save_photon_paths = true
32
+ save_heating_rates = true
33
+ save_plots = true
34
+ overwrite = true
35
+ path = 'results'
36
+
37
+ [surface_materials]
38
+ [surface_materials.snow]
39
+ albedo = 0.85
40
+ reflection = {type = "lambertian"}
41
+ [surface_materials.ocean]
42
+ albedo = 0.01
43
+ reflection = {type = "mirror", roughness = 0.0}
44
+
45
+
46
+ [atmosphere_materials]
47
+ [atmosphere_materials.air]
48
+ extinction_coeff_per_km = 0.01
49
+ ssa = 0.9
50
+ scattering = {type = "rayleigh"} # rayleigh, hg, isotropic
51
+ [atmosphere_materials.light-clouds]
52
+ extinction_coeff_per_km = 1
53
+ ssa = 0.999999
54
+ scattering = {type = "hg", g = 0.85}
55
+ [atmosphere_materials.dark-clouds]
56
+ extinction_coeff_per_km = 5
57
+ ssa = 0.999999
58
+ scattering = {type = "hg", g = 0.85}
59
+
60
+ # Define atmospheric structure from bottom to top
61
+ [[layer]]
62
+ z_range_km = [0, 2]
63
+ materials = [{type = "air", weight = 1.0}]
64
+
65
+ [[layer]]
66
+ z_range_km = [2, 6]
67
+ materials = [
68
+ {type = "air", weight = 0.1},
69
+ {type = "dark-clouds", weight = 0.9}
70
+ ]
71
+
72
+ [[layer]]
73
+ z_range_km = [6, 10]
74
+ materials = [{type = "air", weight = 1.0}]
75
+
76
+ # --- Surface Map Configuration ---
77
+ # Choose one surface map by commenting out the others
78
+
79
+ # [surface]
80
+ # name = "uniform"
81
+ # material = "snow"
82
+
83
+ # [surface]
84
+ # name = "split-half-x"
85
+ # material_left = "snow"
86
+ # material_right = "ocean"
87
+
88
+ # [surface]
89
+ # name = "checkerboard"
90
+ # tile_size_km = 10
91
+ # material_a = "snow"
92
+ # material_b = "ocean"
93
+
94
+ [surface]
95
+ name = "circle"
96
+ radius_km = 20
97
+ material_in = "snow"
98
+ material_out = "ocean"