11 Commits

22 changed files with 142 additions and 65 deletions

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
# Custom
dev-tools/
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/
*.py[codz] *.py[codz]

View File

@ -1,12 +1,11 @@
window.MathJax = { window.MathJax = {
tex: { tex: {
inlineMath: [["\\(", "\\)"]], inlineMath: [['$', '$'], ["\\(", "\\)"]],
displayMath: [["\\[", "\\]"]], displayMath: [['$$', '$$'], ["\\[", "\\]"]],
processEscapes: true, processEscapes: true,
processEnvironments: true processEnvironments: true
}, },
options: { options: {
ignoreHtmlClass: ".*|",
processHtmlClass: "arithmatex" processHtmlClass: "arithmatex"
} }
}; };

File diff suppressed because one or more lines are too long

View File

@ -33,10 +33,11 @@ markdown_extensions:
extra_javascript: extra_javascript:
- javascripts/mathjax.js - javascripts/mathjax.js
- https://unpkg.com/mathjax@3/es5/tex-mml-chtml.js - https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js
plugins: plugins:
- mkdocs-jupyter - mkdocs-jupyter:
execute: false
- mkdocstrings: - mkdocstrings:
enabled: !ENV [ENABLE_MKDOCSTRINGS, true] enabled: !ENV [ENABLE_MKDOCSTRINGS, true]
default_handler: python default_handler: python

View File

@ -7,7 +7,7 @@ where = ["src"]
[project] [project]
name = "pg-rad" name = "pg-rad"
version = "0.2.0" version = "0.2.1"
authors = [ authors = [
{ name="Pim Nelissen", email="pi0274ne-s@student.lu.se" }, { name="Pim Nelissen", email="pi0274ne-s@student.lu.se" },
] ]
@ -29,4 +29,4 @@ Homepage = "https://github.com/pim-n/pg-rad"
Issues = "https://github.com/pim-n/pg-rad/issues" Issues = "https://github.com/pim-n/pg-rad/issues"
[project.optional-dependencies] [project.optional-dependencies]
dev = ["pytest", "notebook", "mkdocs-material", "mkdocstrings-python", "mkdocs-jupyter"] dev = ["pytest", "mkinit", "notebook", "mkdocs-material", "mkdocstrings-python", "mkdocs-jupyter"]

View File

@ -0,0 +1,8 @@
# do not expose internal logger when running mkinit
__ignore__ = ["logger"]
from pg_rad.dataloader import dataloader
from pg_rad.dataloader.dataloader import (load_data,)
__all__ = ['dataloader', 'load_data']

View File

@ -1,12 +1,13 @@
import logging
import pandas as pd import pandas as pd
from pg_rad.logger import setup_logger
from pg_rad.exceptions import DataLoadError, InvalidCSVError from pg_rad.exceptions import DataLoadError, InvalidCSVError
logger = setup_logger(__name__) logger = logging.getLogger(__name__)
def load_data(filename: str) -> pd.DataFrame: def load_data(filename: str) -> pd.DataFrame:
logger.debug(f"Attempting to load data from {filename}") logger.debug(f"Attempting to load file: {filename}")
try: try:
df = pd.read_csv(filename, delimiter=',') df = pd.read_csv(filename, delimiter=',')
@ -23,4 +24,5 @@ def load_data(filename: str) -> pd.DataFrame:
logger.exception(f"Unexpected error while loading {filename}") logger.exception(f"Unexpected error while loading {filename}")
raise DataLoadError("Unexpected error while loading data") from e raise DataLoadError("Unexpected error while loading data") from e
logger.debug(f"File loaded: {filename}")
return df return df

View File

@ -0,0 +1,10 @@
# do not expose internal logger when running mkinit
__ignore__ = ["logger"]
from pg_rad.exceptions import exceptions
from pg_rad.exceptions.exceptions import (ConvergenceError, DataLoadError,
InvalidCSVError,)
__all__ = ['ConvergenceError', 'DataLoadError', 'InvalidCSVError',
'exceptions']

View File

@ -0,0 +1,8 @@
# do not expose internal logger when running mkinit
__ignore__ = ["logger"]
from pg_rad.isotopes import isotope
from pg_rad.isotopes.isotope import (Isotope,)
__all__ = ['Isotope', 'isotope']

View File

@ -0,0 +1,8 @@
# do not expose internal logger when running mkinit
__ignore__ = ["logger"]
from pg_rad.landscape import landscape
from pg_rad.landscape.landscape import (Landscape, create_landscape_from_path,)
__all__ = ['Landscape', 'create_landscape_from_path', 'landscape']

View File

@ -1,9 +1,13 @@
import logging
from matplotlib import pyplot as plt from matplotlib import pyplot as plt
from matplotlib.patches import Circle from matplotlib.patches import Circle
import numpy as np import numpy as np
from pg_rad.path import Path from pg_rad.path import Path
from pg_rad.sources import PointSource from pg_rad.objects import PointSource
logger = logging.getLogger(__name__)
class Landscape: class Landscape:
"""A generic Landscape that can contain a Path and sources. """A generic Landscape that can contain a Path and sources.
@ -31,6 +35,7 @@ class Landscape:
self.path: Path = None self.path: Path = None
self.sources: list[PointSource] = [] self.sources: list[PointSource] = []
logger.debug("Landscape initialized.")
def plot(self, z = 0): def plot(self, z = 0):
"""Plot a slice of the world at a height `z`. """Plot a slice of the world at a height `z`.

View File

@ -1,17 +0,0 @@
import logging
import logging.config
import pathlib
import yaml
def setup_logger(name):
logger = logging.getLogger(name)
base_dir = pathlib.Path(__file__).resolve().parent
config_file = base_dir / "configs" / "logging.yml"
with open(config_file) as f:
config = yaml.safe_load(f)
logging.config.dictConfig(config)
return logger

View File

@ -0,0 +1,5 @@
from pg_rad.logging import logger
from pg_rad.logging.logger import (setup_logger,)
__all__ = ['logger', 'setup_logger']

View File

@ -0,0 +1,20 @@
import logging
import pathlib
import yaml
def setup_logger(log_level: str = "WARNING"):
levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
if not log_level 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"
with open(config_file) as f:
config = yaml.safe_load(f)
config["loggers"]["root"]["level"] = log_level
logging.config.dictConfig(config)

View File

@ -0,0 +1,13 @@
# do not expose internal logger when running mkinit
__ignore__ = ["logger"]
from pg_rad.objects import detectors
from pg_rad.objects import objects
from pg_rad.objects import sources
from pg_rad.objects.detectors import (Detector,)
from pg_rad.objects.objects import (Object,)
from pg_rad.objects.sources import (PointSource,)
__all__ = ['Detector', 'Object', 'PointSource', 'detectors', 'objects',
'sources']

View File

@ -1,7 +1,7 @@
import math import math
from typing import Self from typing import Self
class Object: class BaseObject:
def __init__( def __init__(
self, self,
x: float, x: float,

View File

@ -1,7 +1,11 @@
from pg_rad.objects import Object import logging
from pg_rad.isotope import Isotope
class PointSource(Object): from .objects import BaseObject
from pg_rad.isotopes import Isotope
logger = logging.getLogger(__name__)
class PointSource(BaseObject):
_id_counter = 1 _id_counter = 1
def __init__( def __init__(
self, self,
@ -38,6 +42,8 @@ class PointSource(Object):
self.activity = activity self.activity = activity
self.isotope = isotope self.isotope = isotope
self.color = color self.color = color
logger.debug(f"Source created: {self.name}")
def __repr__(self): def __repr__(self):
return f"PointSource(name={self.name}, pos={(self.x, self.y, self.z)}, isotope={self.isotope.name}, A={self.activity} MBq)" return f"PointSource(name={self.name}, pos={(self.x, self.y, self.z)}, isotope={self.isotope.name}, A={self.activity} MBq)"

View File

@ -0,0 +1,9 @@
# do not expose internal logger when running mkinit
__ignore__ = ["logger"]
from pg_rad.path import path
from pg_rad.path.path import (Path, PathSegment, path_from_RT90,
simplify_path,)
__all__ = ['Path', 'PathSegment', 'path', 'path_from_RT90', 'simplify_path']

View File

@ -1,4 +1,5 @@
from collections.abc import Sequence from collections.abc import Sequence
import logging
import math import math
from matplotlib import pyplot as plt from matplotlib import pyplot as plt
@ -7,9 +8,8 @@ import pandas as pd
import piecewise_regression import piecewise_regression
from pg_rad.exceptions import ConvergenceError from pg_rad.exceptions import ConvergenceError
from pg_rad.logger import setup_logger
logger = setup_logger(__name__) logger = logging.getLogger(__name__)
class PathSegment: class PathSegment:
def __init__(self, a: tuple[float, float], b: tuple[float, float]): def __init__(self, a: tuple[float, float], b: tuple[float, float]):
@ -73,6 +73,8 @@ class Path:
self.z = z self.z = z
logger.debug("Path created.")
def get_length(self) -> float: def get_length(self) -> float:
return sum([s.length for s in self.segments]) return sum([s.length for s in self.segments])
@ -136,7 +138,7 @@ def simplify_path(
pw_res = pw_fit.get_results() pw_res = pw_fit.get_results()
if pw_res == None: if pw_res == None:
logger.error("Piecewise regression failed to converge.") logger.warning("Piecewise regression failed to converge.")
raise ConvergenceError("Piecewise regression failed to converge.") raise ConvergenceError("Piecewise regression failed to converge.")
est = pw_res['estimates'] est = pw_res['estimates']
@ -184,4 +186,5 @@ def path_from_RT90(
coord_pairs = list(zip(east_arr, north_arr)) coord_pairs = list(zip(east_arr, north_arr))
path = Path(coord_pairs, **kwargs) path = Path(coord_pairs, **kwargs)
logger.debug("Loaded path from provided RT90 coordinates.")
return path return path

View File

@ -1,28 +1,27 @@
import numpy as np import numpy as np
import pytest import pytest
from pg_rad.objects import Source from pg_rad.sources import PointSource
@pytest.fixture @pytest.fixture
def test_sources(): def test_sources():
pos_a = np.random.rand(3) pos_a = np.random.rand(3)
pos_b = np.random.rand(3) pos_b = np.random.rand(3)
a = Source(*tuple(pos_a), strength = None) a = PointSource(*tuple(pos_a), strength = None)
b = Source(*tuple(pos_b), strength = None) b = PointSource(*tuple(pos_b), strength = None)
return pos_a, pos_b, a, b return pos_a, pos_b, a, b
def test_if_distances_equal(test_sources): def test_if_distances_equal(test_sources):
"""_Verify whether from object A to object B is the same as B to A._""" """Verify whether from PointSource A to PointSource B is the same as B to A."""
_, _, a, b = test_sources _, _, a, b = test_sources
assert a.distance_to(b) == b.distance_to(a) assert a.distance_to(b) == b.distance_to(a)
def test_distance_calculation(test_sources): def test_distance_calculation(test_sources):
"""_Verify whether distance between two static objects (e.g. sources) """Verify whether distance between two PointSources is calculated correctly."""
is calculated correctly._"""
pos_a, pos_b, a, b = test_sources pos_a, pos_b, a, b = test_sources