Dose calculation
Implementation of dose conversion factors and long/short-term calculations, as well as of an sqlite3 database for storage.
The generated sqlite3 database can be explored with the dashboards in ensi_g14_dashboard.
Photon Point-Kernel Comparison
Warning
The photon point-kernel integrator (PKI) is an opt-in, photon-only, advanced comparison model. It is non-regulatory and is not a replacement for the ENSI-G14 calculation. ENSI-G14 remains the reference model.
PKI results are excluded from default totals. They are stored separately with
model="pki_photon" and is_reference=False, while ENSI-G14 results retain
model="ensi_g14" and is_reference=True. Explicitly request nonreference
results when comparing models.
The packaged spectrum includes Cs-137 at 0.6617 MeV with yield 0.8510 photons
per disintegration, plus the additional supported nuclides listed in the
packaged resource.
The packaged attenuation, buildup, and ISO fluence-to-effective-dose data are
used without extrapolation. The integrator returns a photon dose coefficient
in Sv/Bq.
import numpy as np
from astropy.units import m, s
from ensi_g14_calculator.dose import PointKernelConfig, PointKernelIntegrator
def dispersion_at_points(x, y, z):
# A production callback normally comes from WeatherAverager.
return np.ones_like(x.to_value(m)) * (s / m**3)
integrator = PointKernelIntegrator(
config=PointKernelConfig(max_mean_free_paths=10.0)
)
coefficient = integrator.dose_coefficient(
"Cs-137",
(0.0 * m, 0.0 * m, 0.0 * m),
dispersion_at_points,
)
The input Q to the runner PKI connector is integrated released activity in
Bq, and the resulting dose is integrated in Sv. The connector applies parent
transport decay and shielding only. Daughter ingrowth and kSPE are omitted.
The ISO fluence-to-effective-dose conversion is used.
For runner calculations, global coordinates are east, north, and up relative to the chimney base. Observer height defaults to local terrain, and plume values below terrain are masked. Terrain-intersecting photon rays are not blocked. The first implementation evaluates plume values directly at quadrature points, and the spherical integration is truncated at the configurable maximum number of mean free paths.
Extending Photon Spectra
Advanced users and maintainers can add nuclides to
ensi_g14_calculator/data/pki/photon_spectra.csv:
- Use the package's canonical nuclide spelling.
- Add one row per effective photon line.
- Use MeV and photons per disintegration.
- Keep every line inside the common attenuation and dose-conversion ranges.
- Decide explicitly whether a short-lived daughter line is represented as an effective parent line.
- Add a data-repository test for the new nuclide.
- Add a runner validation or smoke test if the nuclide uses new energy ranges.
Adding a photon line does not model daughter ingrowth. Custom normalized tables
can be supplied through the public PkiDataRepository constructor.
ensi_g14_calculator.dose
Dose calculation APIs, including the opt-in photon point-kernel model.
Classes
PointKernelConfig
dataclass
Numerical settings for spherical point-kernel quadrature.
PointKernelIntegrator
Integrate physical photon dispersion around one observer.
Functions
line_dose_coefficient(line, observer_xyz, dispersion_evaluator)
Return one photon-line coefficient in Sv / Bq.
This is useful for training labels, where energy is sampled
continuously and therefore is not present in the packaged nuclide
spectrum table. line must be a validated PhotonLine-like
object containing an energy and a photon yield.
dose_coefficient(nuclide, observer_xyz, dispersion_evaluator)
Return the integrated photon PKI dose coefficient in Sv / Bq.
dose_coefficient_many(nuclide, observer_xyz, dispersion_evaluator, observer_batch_size=8)
Return PKI coefficients for many observers using batched quadrature.
observer_xyz may be a quantity with shape (n, 3) or a tuple
of three broadcast-compatible length arrays. The observer coordinates
may be anywhere around the chimney, including upwind positions. The
dispersion callback receives source-volume cloud points, not observer
coordinates, and is called once per photon line and observer batch.
PkiDataRepository
Load AND interpolate the normalised photon point-kernel data tables.
PkiSubmersionSurrogate
Vectorized equivalent-dispersion inference for one Gaussian component.
Functions
equivalent_dispersion(downwind, crosswind, vertical_separation, p_y, q_y, p_z, q_z, energy, velocity)
Return equivalent dispersion in s/m^3 with broadcast input shape.
semi_infinite_line_coefficient(energy, maximum_mean_free_paths=40.0, radial_order=128)
Return the unit-yield half-space reference coefficient in Sv m3.
SurrogateDomainError
Bases: ValueError
Raised when an input lies outside the trained surrogate domain.
Modules
dose_database
Classes
DoseDatabase
insert_chimney(chimney_id, latitude, longitude, settings=None)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
settings
|
dict
|
optional {column: value} for the settings columns of the chimneys table, e.g. {'decay_in_plume': 1, 'near_field': 0}; missing columns are NULL (not recorded). |
None
|
insert_transport_factors_bulk(records)
Insert from existing dict directly.
records: {(chimney, group, nuclide): {'avg_chi_k':
insert_dose(chimney, group, nuclide, pathway, dose_by_age_group, model='ensi_g14', is_reference=True, extensions=None)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
extensions
|
iterable of tags of EXTENSIONS in effect ([] for strict G-14), or None (not recorded). |
None
|
format_extensions(extensions)
classmethod
Tags -> sorted comma-separated string ('' for strict G-14); None stays None (not recorded).
insert_doses_for_case(chimney, group, nuclide, doses, model='ensi_g14', is_reference=True, extensions=None)
doses: {'direct': {dict of dose by age group}, 'dairy': {dict of dose by age group}, ...}
get_total_dose(group, age_group, model=None, include_non_reference=False)
Total dose (all chimneys, all nuclides, all pathways) for a group.
get_dose_by_pathway(group, age_group, model=None, include_non_reference=False)
Total dose per pathway (summed over chimneys and nuclides) for a group.
get_dose_by_nuclide(group, age_group, model=None, include_non_reference=False)
Total dose per nuclide (summed over chimneys and pathways) for a group.
get_dose_by_chimney(group, age_group, model=None, include_non_reference=False)
Total dose per chimney (summed over nuclides and pathways) for a group.
get_dose_breakdown(group, age_group, chimney=None, nuclide=None, pathway=None, model=None, include_non_reference=False)
Flexible breakdown: filter by any combination of chimney, nuclide, pathway. Returns one row per (chimney, nuclide, pathway).
dose_template_class
base class that
- loads the numerous dose coefficients from our parsed
CSV/yamlfiles - implements formulae that are identical for long term or short term doses.
Classes
DoseTemplate
always calculates for 3 groups: toddler, child, adult.
scenario_type
property
to be defined by child classes to return 'long_term' or 'short_term' or maybe 'potential'.
decay_constant(nuclide)
Decay constant of a nuclide (Table 4.1), with units (1/year).
has_daughters(nuclide)
Whether the nuclide has daughter products in Table 4.3.1.
decay_chain(nuclide)
Decay constants of a nuclide and of its daughters (Table 4.1 and Table 4.3.1).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nuclide
|
string, e.g. 'Kr-88' |
required |
Returns:
| Type | Description |
|---|---|
|
tuple (lambda_m, [(daughter name, lambda_d), ...]), decay constants with units (1/year) |
integral_exp(lambda_rate, time)
staticmethod
Used a lot in those formulae, (1-exp(lambda_rate * time))/ lambda_rate
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lambda_rate
|
unit 1 / time |
required | |
time
|
unit time |
required |
Returns:
| Type | Description |
|---|---|
|
unit time |
submersion(nuclide, Q, chi_KL_corrected, T_fz=None, k_s=None)
Calculates E_sub (External radiation from plume), appendix G14 A3.1.1. Same formula for all cases (long, short, accident).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nuclide
|
string, e.g., 'Be-7' |
required | |
Q
|
dose in Bq, yearly for long term, or scenario-dependent for short term. |
required | |
chi_KL_corrected
|
short or long term dispersion coefficient, the "corrected" version. Or a DecayedDispersion (decay during transport inside the plume), then no T_fz. |
required | |
T_fz
|
time of flight of the nuclide from emission point to point of interest. if None, using the Richtlinie provided value |
None
|
|
k_s
|
shieding factor, default None. if None, Richtlinie value used. Pass k_s=1 for short term scenarios, when <8h, since no shielding is to be considered then. |
None
|
Returns:
| Type | Description |
|---|---|
|
dictionary {'toddler_1_year': dose_Sv, 'child_10_year': dose_Sv, 'adult': dose_Sv} |
submersion_pki(nuclide, Q, dose_coefficient, T_fz=None, k_s=None)
Calculate photon-only point-kernel submersion dose.
dose_coefficient is an integrated geometrical/spectral coefficient
in Sv / Bq. This connector applies only parent decay and shielding;
it does not use the regulatory infinite-cloud coefficient, K_C,
kSPE, or daughter-product terms.
It is calculated with the point_kernel.py class, integrating over a gaussian plume. see examples in
short-term and long-term scenario runners.
If the coefficient was integrated over a cloud with decay during transport (time_weight of the
averager, see plume_decay.py), pass T_fz=0 * year so that the parent is not decayed twice.
inhalation(nuclide, Q, chi_KL, T_fz=None, increased_breathing_rate_for_adults=False)
Calculates E_inh (Inhalation dose), A3.2.1. Same formula for all cases. Resuspension must be added for accidents.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nuclide
|
string, e.g., 'Be-7' |
required | |
Q
|
dose in Bq, yearly for long term, or scenario-dependent for short term. |
required | |
chi_KL
|
short term dispersion coeff chi_K, or long term version coefficient chi_L. Or a DecayedDispersion (decay during transport inside the plume), then no T_fz. |
required | |
T_fz
|
time of flight of the nuclide from emission point to point of interest. |
None
|
|
increased_breathing_rate_for_adults
|
toggles higher breathing rate for adults, to be used in first 8h of accident scenarios. |
False
|
Returns:
| Type | Description |
|---|---|
|
dictionary {'toddler_1_year': dose_Sv, 'child_10_year': dose_Sv, 'adult': dose_Sv} |
ingestion_tritium_from_air(Q_HTO, chi_k, W, I_N)
Calculates formula in A3.6.1. Same formula for all cases (short-, long- and accident). Confirmed with Andreas Leupin and Mohsen Meshkian from ENSI on 2026-05-18: this formula should indeed also be used for short term.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
Q_HTO
|
Quantity
|
Quantity, Bq, yearly for long term, or scenario-dependent for short term. |
required |
chi_k
|
Quantity
|
dispersion, Quantity, must be cast-able to s/m^3 |
required |
W
|
Quantity
|
washout factor, Quantity, must be cast-able to 1/m^2 |
required |
I_N
|
Quantity
|
Quantity, must be cast-able to m. Rain in summer half rain (long term) OR amout of rain during release event (short term) |
required |
Returns:
| Type | Description |
|---|---|
|
dictionary of doses, first key pathway (meat, dairy, plant ...), second key exposed group. dictionary structure different from other dose methods because we have 3 pathways here! |
ingestion_C14(Q, chi)
Calculates formula in A3.5.1. Same formula for long term and short term. FOR SHORT TERM: MULTIPLY BY 2 per text. The short term runner of this package takes care of this 2 fold factor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
Q
|
average yearly emission in Bq for long term, or scenario-dependent for short term. |
required | |
chi
|
dispersion parameter in s/m^3 |
required |
Returns:
| Type | Description |
|---|---|
|
dictionary of dose by exposued group and pathway |
ingestion_from_water(nuclide, Q, J)
Calculates formula in A.7.1
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nuclide
|
str
|
string, e.g. 'Cs-137'. |
required |
Q
|
Quantity
|
average yearly emission in Bq for long term, or scenario-dependent for short term. |
required |
J
|
Quantity
|
flow rate of the river in m^3/s. (very nuclear-plant centric) |
required |
Returns:
| Type | Description |
|---|---|
|
dictionary of dose by pathway and by exposed group. |
ingestion_from_water_tritium(Q, J)
Calculates formula in A.8.1
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
Q
|
average yearly emission in Bq for long term, or scenario-dependent for short term. |
required | |
J
|
flow rate of the river in m^3/s. (very nuclear-plant centric) |
required |
Returns:
| Type | Description |
|---|---|
|
dictionary of dose by exposure pathway and by exposed group. |
Functions
long_term_dose
Classes
LongTermDoseCalculator
Bases: DoseTemplate
ground_shine(nuclide, Q, Q_current_year, xi_L, a_f_0, a_s_0)
Calculates E_BS (Ground radiation for calendar year), appendix A3.3.1.2 This cumulates both legacy activity (a_0) and current year deposition.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nuclide
|
string, nuclide, e.g. Be-7 |
required | |
Q
|
usual yearly activity release in Bq (for nuclear plants: "100-year average") |
required | |
Q_current_year
|
activity released in the present year. For us (non-nuclear-plant facilities), we can probably set Q_current_year = Q. |
required | |
xi_L
|
long term deposition factor xi_L, in 1/m^2 |
required | |
a_f_0
|
activity in soil, fast component, obtained from the activity_inventory class. |
required | |
a_s_0
|
activity in soil, slow component, obtained from the activity_inventory class. |
required |
Returns:
| Type | Description |
|---|---|
|
dictionary, one entry per age group, in Sv (yearly dose) |
ingestion_plant(nuclide, Q, Q_Vj, xi_p, xi, C_bo_0)
implementation of appendix A3.4.1.2.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nuclide
|
string |
required | |
Q
|
emission this year (for leaves), in Bq |
required | |
Q_Vj
|
average yearly emission in Bq |
required | |
xi_p
|
deposition on plants, 1/m^2 |
required | |
xi
|
deposition on soil, 1/m^2 |
required | |
C_bo_0
|
steady state soil concentration, Bq/kg |
required |
Returns:
| Type | Description |
|---|---|
|
dictionary, one entry per age group, in Sv (yearly dose) |
ingestion_milk_dairy(nuclide, Q, Q_Vj, xi_p, xi, C_bo_0)
implementation of appendix A3.4.2.2
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nuclide
|
string |
required | |
Q
|
emission this year (for leaves), in Bq |
required | |
Q_Vj
|
average yearly emission in Bq |
required | |
xi_p
|
deposition on plants, 1/m^2 |
required | |
xi
|
deposition on soil, 1/m^2 |
required | |
C_bo_0
|
steady state soil concentration, Bq/kg |
required |
Returns:
| Type | Description |
|---|---|
|
dictionary, one entry per age group, in Sv (yearly dose) |
ingestion_meat_eggs(nuclide, Q, Q_Vj, xi_p, xi, C_bo_0)
implementation of appendix A3.4.3.2
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nuclide
|
string |
required | |
Q
|
emission this year (for leaves), in Bq |
required | |
Q_Vj
|
average yearly emission in Bq |
required | |
xi_p
|
deposition on plants, 1/m^2 |
required | |
xi
|
deposition on soil, 1/m^2 |
required | |
C_bo_0
|
steady state soil concentration, Bq/kg |
required |
Returns:
| Type | Description |
|---|---|
|
dictionary, one entry per age group, in Sv (yearly dose) |
Functions
point_kernel
Spherical point-kernel quadrature for photon submersion dose.
Classes
PointKernelConfig
dataclass
Numerical settings for spherical point-kernel quadrature.
PointKernelIntegrator
Integrate physical photon dispersion around one observer.
line_dose_coefficient(line, observer_xyz, dispersion_evaluator)
Return one photon-line coefficient in Sv / Bq.
This is useful for training labels, where energy is sampled
continuously and therefore is not present in the packaged nuclide
spectrum table. line must be a validated PhotonLine-like
object containing an energy and a photon yield.
dose_coefficient(nuclide, observer_xyz, dispersion_evaluator)
Return the integrated photon PKI dose coefficient in Sv / Bq.
dose_coefficient_many(nuclide, observer_xyz, dispersion_evaluator, observer_batch_size=8)
Return PKI coefficients for many observers using batched quadrature.
observer_xyz may be a quantity with shape (n, 3) or a tuple
of three broadcast-compatible length arrays. The observer coordinates
may be anywhere around the chimney, including upwind positions. The
dispersion callback receives source-volume cloud points, not observer
coordinates, and is called once per photon line and observer batch.
point_kernel_data
Loading and validating data for the photon point-kernel integration metohd for submersion dose.
Classes
PhotonSpectrumNotFound
Bases: LookupError
Raised when no packaged photon spectrum exists for a nuclide.
PhotonLine
dataclass
One photon line, with yield expressed per parent disintegration.
PkiDataRepository
Load AND interpolate the normalised photon point-kernel data tables.
short_term_dose
Classes
ShortTermDoseCalculator
Bases: DoseTemplate
Inherits from the template, which does the loading of the parameters and implements some of the dose formulae (those common to all scenarios).
We always need some "total" deposited dose, \(\bar{Q \xi}\) and \(\bar{Q \xi_p}\). These depend on the scenario, so I wrote small classes (PotentialExposureScenario and ShortTermScenario) to handle them -- just for safety.
So, unlike the long term dose class where we just pass a yearly activity "Q", here we will pass an instance of a Scenario, which will compute its own \(\bar{Q \xi}\) quantities.
resuspension(nuclide, scenario, T_exp)
Second formula of Section A3.2.1; to be considered in scenarios of accidents. T_exp must be exactly 1 year or 50 years.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nuclide
|
str |
required | |
scenario
|
Scenario
|
scenario object (from ensi_g14_calculator.short_term_scenarios.release_by_time_interval) |
required |
T_exp
|
Quantity, 1 year or 50 years |
required |
Returns:
| Type | Description |
|---|---|
|
a dose in Sv. |
ground_shine(nuclide, scenario, T_exp)
Calculates E_BS (ground radiation for T_exp years) section A3.3.1.1.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nuclide
|
str |
required | |
scenario
|
Scenario
|
scenario object (from ensi_g14_calculator.short_term_scenarios.release_by_time_interval) |
required |
T_exp
|
Quantity, 1 year or 50 years |
required |
Returns:
| Type | Description |
|---|---|
|
a dose in Sv. |
ingestion_plant(nuclide, scenario, T_exp, release_date, f_cont=None)
Calculates ingestion dose from plants, depending on T_exp, section A3.4.1.1.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nuclide
|
str |
required | |
scenario
|
Scenario
|
scenario object (from ensi_g14_calculator.short_term_scenarios.release_by_time_interval) |
required |
T_exp
|
Quantity, years. Unlike ground_shine and resuspension, T_exp is not limited to 1 or 50 years only. |
required | |
release_date
|
str, release date in 'mm-dd' format (e.g., '06-15' for June 15). |
required | |
f_cont
|
Default None. Share of the produce contaminated within a 5km radius. Can be averaged (through scenario, average \bar{Q \xi} on a 5km radius disk centred on the facility, then pass fcont=1. If not given, then the max was taken (>500m), and fcont is read from the Richtlinie parameters. |
None
|
Returns:
| Type | Description |
|---|---|
|
a dose in Sv. |
ingestion_milk_dairy(nuclide, scenario, T_exp, release_date)
Calculates ingestion dose from milk and dairy depending on T_exp, section A3.4.2.1.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nuclide
|
str |
required | |
scenario
|
Scenario
|
scenario object (from ensi_g14_calculator.short_term_scenarios.release_by_time_interval) |
required |
T_exp
|
Quantity, years. Unlike ground_shine and resuspension, T_exp is not limited to 1 or 50 years only. |
required | |
release_date
|
str, release date in 'mm-dd' format (e.g., '06-15' for June 15). |
required |
Returns:
| Type | Description |
|---|---|
|
a dose in Sv. |
ingestion_meat_eggs(nuclide, scenario, T_exp, release_date)
Calculates ingestion dose from milk and dairy depending on T_exp, section A3.4.3.1.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nuclide
|
str |
required | |
scenario
|
Scenario
|
scenario object (from ensi_g14_calculator.short_term_scenarios.release_by_time_interval) |
required |
T_exp
|
Quantity, years. Unlike ground_shine and resuspension, T_exp is not limited to 1 or 50 years only. |
required | |
release_date
|
str, release date in 'mm-dd' format (e.g., '06-15' for June 15). |
required |
Returns:
| Type | Description |
|---|---|
|
a dose in Sv. |
Functions
submersion_surrogate
NumPy-only inference for the optional local-flat PKI surrogate.
Classes
SurrogateDomainError
Bases: ValueError
Raised when an input lies outside the trained surrogate domain.
PkiSubmersionSurrogate
Vectorized equivalent-dispersion inference for one Gaussian component.
equivalent_dispersion(downwind, crosswind, vertical_separation, p_y, q_y, p_z, q_z, energy, velocity)
Return equivalent dispersion in s/m^3 with broadcast input shape.
semi_infinite_line_coefficient(energy, maximum_mean_free_paths=40.0, radial_order=128)
Return the unit-yield half-space reference coefficient in Sv m3.