mirror of
https://github.com/pim-n/pg-rad
synced 2026-03-22 21:48:11 +01:00
Compare commits
8 Commits
abc1195c91
...
845f006cd3
| Author | SHA1 | Date | |
|---|---|---|---|
| 845f006cd3 | |||
| ac8c38592d | |||
| a4fb4a7c57 | |||
| e8bf687563 | |||
| 49a0dcd301 | |||
| 26f96b06fe | |||
| 55258d7727 | |||
| 82331f3bbd |
@ -31,6 +31,14 @@ With the virtual environment activated, run:
|
||||
pip install -e .[dev]
|
||||
```
|
||||
|
||||
## Running example landscape
|
||||
|
||||
The example landscape can be generated using the command-line interface. Still in the virtual environment, run
|
||||
|
||||
```
|
||||
pgrad --test --loglevel DEBUG
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
Tests can be run with `pytest` from the root directory of the repository. With the virtual environment activated, run:
|
||||
|
||||
@ -7,6 +7,7 @@ where = ["src"]
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
"pg_rad.data" = ["*.csv"]
|
||||
"pg_rad.configs" = ["*.yml"]
|
||||
|
||||
[project]
|
||||
name = "pg-rad"
|
||||
@ -26,6 +27,9 @@ dependencies = [
|
||||
license = "MIT"
|
||||
license-files = ["LICEN[CS]E*"]
|
||||
|
||||
[project.scripts]
|
||||
pgrad = "pg_rad.main:main"
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/pim-n/pg-rad"
|
||||
Issues = "https://github.com/pim-n/pg-rad/issues"
|
||||
|
||||
3
src/pg_rad/configs/filepaths.py
Normal file
3
src/pg_rad/configs/filepaths.py
Normal file
@ -0,0 +1,3 @@
|
||||
ATTENUATION_TABLE = 'attenuation_table.csv'
|
||||
TEST_EXP_DATA = 'test_path_coords.csv'
|
||||
LOGGING_CONFIG = 'logging.yml'
|
||||
@ -3,7 +3,7 @@ from .isotope import Isotope
|
||||
|
||||
class CS137(Isotope):
|
||||
def __init__(self):
|
||||
super.__init__(
|
||||
super().__init__(
|
||||
name="Cs-137",
|
||||
E=661.66,
|
||||
b=0.851
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
# do not expose internal logger when running mkinit
|
||||
__ignore__ = ["logger"]
|
||||
|
||||
from pg_rad.landscape import director
|
||||
from pg_rad.landscape import landscape
|
||||
|
||||
from pg_rad.landscape.director import (LandscapeDirector,)
|
||||
from pg_rad.landscape.landscape import (Landscape, LandscapeBuilder,)
|
||||
|
||||
__all__ = ['Landscape', 'LandscapeBuilder', 'landscape']
|
||||
__all__ = ['Landscape', 'LandscapeBuilder', 'LandscapeDirector', 'director',
|
||||
'landscape']
|
||||
|
||||
23
src/pg_rad/landscape/director.py
Normal file
23
src/pg_rad/landscape/director.py
Normal file
@ -0,0 +1,23 @@
|
||||
from importlib.resources import files
|
||||
import logging
|
||||
|
||||
from pg_rad.configs.filepaths import TEST_EXP_DATA
|
||||
from pg_rad.isotopes import CS137
|
||||
from pg_rad.landscape.landscape import LandscapeBuilder
|
||||
from pg_rad.objects import PointSource
|
||||
|
||||
|
||||
class LandscapeDirector:
|
||||
def __init__(self):
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.logger.debug("LandscapeDirector initialized.")
|
||||
|
||||
def build_test_landscape(self):
|
||||
fp = files('pg_rad.data').joinpath(TEST_EXP_DATA)
|
||||
source = PointSource(activity=100, isotope=CS137(), pos=(0, 0, 0))
|
||||
lb = LandscapeBuilder("Test landscape")
|
||||
lb.set_air_density(1.243)
|
||||
lb.set_path_from_experimental_data(fp, z=0)
|
||||
lb.set_point_sources(source)
|
||||
landscape = lb.build()
|
||||
return landscape
|
||||
@ -16,6 +16,7 @@ class Landscape:
|
||||
"""
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
path: Path,
|
||||
point_sources: list[PointSource] = [],
|
||||
size: tuple[int, int, int] = [500, 500, 50],
|
||||
@ -35,12 +36,13 @@ class Landscape:
|
||||
TypeError: _description_
|
||||
"""
|
||||
|
||||
self.name = name
|
||||
self.path = path
|
||||
self.point_sources = point_sources
|
||||
self.size = size
|
||||
self.air_density = air_density
|
||||
|
||||
logger.debug("Landscape initialized.")
|
||||
logger.debug(f"Landscape created: {self.name}")
|
||||
|
||||
def calculate_fluence_at(self, pos: tuple):
|
||||
total_phi = 0.
|
||||
@ -61,12 +63,15 @@ class Landscape:
|
||||
|
||||
|
||||
class LandscapeBuilder:
|
||||
def __init__(self):
|
||||
def __init__(self, name: str = "Unnamed landscape"):
|
||||
self.name = name
|
||||
self._path = None
|
||||
self._point_sources = []
|
||||
self._size = None
|
||||
self._air_density = None
|
||||
|
||||
logger.debug(f"LandscapeBuilder initialized: {self.name}")
|
||||
|
||||
def set_air_density(self, air_density) -> Self:
|
||||
"""Set the air density of the world."""
|
||||
self._air_density = air_density
|
||||
@ -95,7 +100,8 @@ class LandscapeBuilder:
|
||||
self._path = path_from_RT90(
|
||||
df=df,
|
||||
east_col=east_col,
|
||||
north_col=north_col
|
||||
north_col=north_col,
|
||||
z=z
|
||||
)
|
||||
|
||||
# The size of the landscape will be updated if
|
||||
@ -108,11 +114,12 @@ class LandscapeBuilder:
|
||||
|
||||
if needs_resize:
|
||||
if not self._size:
|
||||
logger.info("Landscape size set to path dimensions.")
|
||||
logger.debug("Because no Landscape size was set, "
|
||||
"it will now set to path dimensions.")
|
||||
else:
|
||||
logger.warning(
|
||||
"Path exceeds current landscape size. "
|
||||
"Expanding landscape to accommodate it."
|
||||
"Landscape size will be expanded to accommodate path."
|
||||
)
|
||||
|
||||
self.set_landscape_size(self._path.size)
|
||||
@ -142,9 +149,13 @@ class LandscapeBuilder:
|
||||
self._point_sources = sources
|
||||
|
||||
def build(self):
|
||||
return Landscape(
|
||||
landscape = Landscape(
|
||||
name=self.name,
|
||||
path=self._path,
|
||||
point_sources=self._point_sources,
|
||||
size=self._size,
|
||||
air_density=self._air_density
|
||||
)
|
||||
|
||||
logger.info(f"Landscape built successfully: {landscape.name}")
|
||||
return landscape
|
||||
|
||||
5
src/pg_rad/logger/__init__.py
Normal file
5
src/pg_rad/logger/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from pg_rad.logger import logger
|
||||
|
||||
from pg_rad.logger.logger import (setup_logger,)
|
||||
|
||||
__all__ = ['logger', 'setup_logger']
|
||||
@ -1,8 +1,10 @@
|
||||
import logging
|
||||
import pathlib
|
||||
import logging.config
|
||||
from importlib.resources import files
|
||||
|
||||
import yaml
|
||||
|
||||
from pg_rad.configs.filepaths import LOGGING_CONFIG
|
||||
|
||||
|
||||
def setup_logger(log_level: str = "WARNING"):
|
||||
levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
||||
@ -10,8 +12,7 @@ def setup_logger(log_level: str = "WARNING"):
|
||||
if log_level not in levels:
|
||||
raise ValueError(f"Log level must be one of {levels}.")
|
||||
|
||||
base_dir = pathlib.Path(__file__).resolve().parent
|
||||
config_file = base_dir / "configs" / "logging.yml"
|
||||
config_file = files('pg_rad.configs').joinpath(LOGGING_CONFIG)
|
||||
|
||||
with open(config_file) as f:
|
||||
config = yaml.safe_load(f)
|
||||
@ -1,5 +0,0 @@
|
||||
from pg_rad.logging import logger
|
||||
|
||||
from pg_rad.logging.logger import (setup_logger,)
|
||||
|
||||
__all__ = ['logger', 'setup_logger']
|
||||
33
src/pg_rad/main.py
Normal file
33
src/pg_rad/main.py
Normal file
@ -0,0 +1,33 @@
|
||||
import argparse
|
||||
|
||||
from pg_rad.logger import setup_logger
|
||||
from pg_rad.landscape import LandscapeDirector
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="pg-rad",
|
||||
description="Primary Gamma RADiation landscape tool"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--test",
|
||||
action="store_true",
|
||||
help="Load and run the test landscape"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--loglevel",
|
||||
default="INFO",
|
||||
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
setup_logger(args.loglevel)
|
||||
|
||||
if args.test:
|
||||
landscape = LandscapeDirector().build_test_landscape()
|
||||
print(landscape.name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -6,7 +6,7 @@ import numpy as np
|
||||
class BaseObject:
|
||||
def __init__(
|
||||
self,
|
||||
pos: tuple[float, float, float],
|
||||
pos: tuple[float, float, float] = (0, 0, 0),
|
||||
name: str = "Unnamed object",
|
||||
color: str = 'grey'):
|
||||
"""
|
||||
|
||||
@ -11,22 +11,23 @@ class PointSource(BaseObject):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
pos: tuple,
|
||||
activity: int,
|
||||
isotope: Isotope,
|
||||
pos: tuple[float, float, float] = (0, 0, 0),
|
||||
name: str | None = None,
|
||||
color: str = "red"):
|
||||
color: str = 'red'
|
||||
):
|
||||
"""A point source.
|
||||
|
||||
Args:
|
||||
pos (tuple): a position vector of length 3 (x,y,z).
|
||||
activity (int): Activity A in MBq.
|
||||
isotope (Isotope): The isotope.
|
||||
name (str | None, optional): Can give the source a unique name.
|
||||
Defaults to None, making the name sequential.
|
||||
(Source-1, Source-2, etc.).
|
||||
color (str, optional): Matplotlib compatible color string.
|
||||
Defaults to "red".
|
||||
pos (tuple[float, float, float], optional):
|
||||
Position of the PointSource.
|
||||
name (str, optional): Can give the source a unique name.
|
||||
If not provided, point sources are sequentially
|
||||
named: Source1, Source2, ...
|
||||
color (str, optional): Matplotlib compatible color string
|
||||
"""
|
||||
|
||||
self.id = PointSource._id_counter
|
||||
|
||||
@ -42,14 +42,18 @@ class Path:
|
||||
def __init__(
|
||||
self,
|
||||
coord_list: Sequence[tuple[float, float]],
|
||||
z: float = 0
|
||||
z: float = 0.,
|
||||
z_box: float = 50.
|
||||
):
|
||||
"""Construct a path of sequences based on a list of coordinates.
|
||||
|
||||
Args:
|
||||
coord_list (Sequence[tuple[float, float]]): List of x,y
|
||||
coordinates.
|
||||
z (float, optional): Height of the path. Defaults to 0.
|
||||
z (float, optional): position of the path in z-direction in meters.
|
||||
Defaults to 0 meters.
|
||||
z_box (float, optional): How much empty space to set
|
||||
above the path in meters. Defaults to 50 meters.
|
||||
"""
|
||||
|
||||
if len(coord_list) < 2:
|
||||
@ -73,7 +77,7 @@ class Path:
|
||||
self.size = (
|
||||
np.ceil(max(self.x_list)),
|
||||
np.ceil(max(self.y_list)),
|
||||
z
|
||||
z + z_box
|
||||
)
|
||||
|
||||
logger.debug("Path created.")
|
||||
|
||||
@ -3,11 +3,13 @@ from importlib.resources import files
|
||||
from pandas import read_csv
|
||||
from scipy.interpolate import interp1d
|
||||
|
||||
from pg_rad.configs.filepaths import ATTENUATION_TABLE
|
||||
|
||||
|
||||
def get_mass_attenuation_coeff(
|
||||
*args
|
||||
) -> float:
|
||||
csv = files('pg_rad.data').joinpath('attenuation_table.csv')
|
||||
csv = files('pg_rad.data').joinpath(ATTENUATION_TABLE)
|
||||
data = read_csv(csv)
|
||||
x = data["energy_mev"].to_numpy()
|
||||
y = data["mu"].to_numpy()
|
||||
|
||||
Reference in New Issue
Block a user