Skip to content

Commit 1862005

Browse files
implemented the capability to choose between absorbed dose in air and effective dose
1 parent d746c9e commit 1862005

File tree

1 file changed

+75
-33
lines changed

1 file changed

+75
-33
lines changed

openmc/material.py

Lines changed: 75 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from openmc.stats import Univariate, Discrete, Mixture, Tabular
2727
from openmc.data.data import _get_element_symbol, BARN_PER_CM_SQ, JOULE_PER_EV
2828
from openmc.data.function import Combination, Tabulated1D, Sum
29-
from openmc.data import mu_en_coefficients
29+
from openmc.data import mu_en_coefficients, dose_coefficients
3030
from openmc.data.photon_attenuation import linear_attenuation_xs
3131

3232

@@ -478,11 +478,11 @@ def get_photon_mass_attenuation(self) -> Sum | None:
478478
return Sum(terms) if terms else None
479479

480480

481-
def get_photon_contact_dose_rate(self, by_nuclide: bool = False) -> float | dict[str, float]:
481+
def get_photon_contact_dose_rate(self, dose_quantity:str = "absorbed-air", build_up:float = 2.0, by_nuclide: bool = False) -> float | dict[str, float]:
482482
"""Compute the photon contact dose rate (CDR) produced by radioactive decay
483483
of the material.
484484
485-
A slab-geometry approximation and a fixed photon build-up factor are used.
485+
A slab-geometry approximation and a photon build-up factor are used.
486486
487487
The method implemented here follows the approach described in FISPACT-II
488488
manual (UKAEA-CCFE-RE(21)02 - May 2021). Appendix C.7.1.
@@ -508,17 +508,27 @@ def get_photon_contact_dose_rate(self, by_nuclide: bool = False) -> float | dic
508508
509509
Parameters
510510
----------
511+
dose_quantity : {'absorbed-air', 'effective'}, optional
512+
Specifies the dose quantity to be calculated.
513+
The only supported options are 'aborbed-air' which implements a the methodology
514+
from FISPACT-II, and 'effective' which uses ICRP-116 effective dose coefficients.
515+
build_up : float, optional. The default value is 2.0 as suggested in the FISPACT-II
516+
manual.
511517
by_nuclide : bool, optional
512518
Specifies if the cdr should be returned for the material as a
513519
whole or per nuclide. Default is False.
514520
515521
Returns
516522
-------
517523
cdr : float or dict[str, float]
518-
Photon Contact Dose Rate due to material decay in [Sv/hr].
524+
Photon Contact Dose Rate due to material decay
525+
If the dose quantity is [Sv/hr].
519526
"""
520527

521528
cv.check_type("by_nuclide", by_nuclide, bool)
529+
cv.check_type("dose_quantity", dose_quantity, str)
530+
cv.check_value("dose_quantity", dose_quantity, ['absorbed-air', 'effective'])
531+
cv.check_type("build_up", build_up, float)
522532

523533
# Mass density of the material [g/cm^3]
524534
rho = self.get_mass_density() # g/cm^3
@@ -529,11 +539,6 @@ def get_photon_contact_dose_rate(self, by_nuclide: bool = False) -> float | dic
529539
"cannot compute mass attenuation coefficient."
530540
)
531541

532-
# mu_en/ rho for air distribution, [eV, cm2/g]
533-
mu_en_x, mu_en_y = mu_en_coefficients("air", data_source="nist126")
534-
mu_en_air = Tabulated1D(mu_en_x, mu_en_y, breakpoints=[len(mu_en_x)], interpolation=[5])
535-
536-
537542
# photon mass attenuation distribution as a function of energy
538543
# distribution values in [cm2/g]
539544
mass_attenuation_dist = self.get_photon_mass_attenuation()
@@ -546,24 +551,45 @@ def get_photon_contact_dose_rate(self, by_nuclide: bool = False) -> float | dic
546551
# CDR computation
547552
cdr = {}
548553

549-
# build up factor - as reported from fispact reference
550-
B = 2.0
551554
geometry_factor_slab = 0.5
552555

553556
# ancillary conversion factors for clarity
554557
seconds_per_hour = 3600.0
555558
grams_per_kg = 1000.0
559+
sv_per_psv = 1e-12
560+
561+
if dose_quantity == 'absorbed-air':
562+
563+
# mu_en/ rho for air distribution, [eV, cm2/g]
564+
response_f_x, response_f_y = mu_en_coefficients("air", data_source="nist126")
565+
566+
# converts [eV barns-1 cm-1 s-1] to [Gy hr-1]
567+
multiplier = (
568+
build_up
569+
* geometry_factor_slab
570+
* seconds_per_hour
571+
* grams_per_kg
572+
* (1 / rho)
573+
* BARN_PER_CM_SQ
574+
* JOULE_PER_EV
575+
)
556576

557-
# converts [eV barns-1 cm-1 s-1] to [Sv hr-1]
558-
multiplier = (
559-
B
560-
* geometry_factor_slab
561-
* seconds_per_hour
562-
* grams_per_kg
563-
* (1 / rho)
564-
* BARN_PER_CM_SQ
565-
* JOULE_PER_EV
566-
)
577+
elif dose_quantity == 'effective':
578+
579+
# effective dose as a function of photon fluence [pSv cm2]
580+
response_f_x, response_f_y = dose_coefficients("photon", geometry='AP', data_source='icrp116')
581+
582+
# converts [pSv g barns-1 cm-1 s-1] to [Sv hr-1]
583+
multiplier = (
584+
build_up
585+
* geometry_factor_slab
586+
* seconds_per_hour
587+
* sv_per_psv
588+
* (1 / rho)
589+
* BARN_PER_CM_SQ
590+
)
591+
592+
response_f = Tabulated1D(response_f_x, response_f_y, breakpoints=[len(response_f_x)], interpolation=[5])
567593

568594
for nuc, nuc_atoms_per_bcm in self.get_nuclide_atom_densities().items():
569595

@@ -580,7 +606,7 @@ def get_photon_contact_dose_rate(self, by_nuclide: bool = False) -> float | dic
580606
e_vals = np.array(photon_source_per_atom.x)
581607
p_vals = np.array(photon_source_per_atom.p)
582608

583-
e_lists = [e_vals, mu_en_air.x]
609+
e_lists = [e_vals, response_f.x]
584610
e_lists.extend(mass_attenuation_e_lists)
585611

586612
# clip distributions for values outside the tabulated values
@@ -604,18 +630,20 @@ def get_photon_contact_dose_rate(self, by_nuclide: bool = False) -> float | dic
604630
raise ValueError(
605631
f"Mass attenuation coefficient <= 0 at energies: {zero_vals}"
606632
)
607-
# units [eV atoms-1 s-1]
608-
cdr_nuc += np.sum((mu_en_air(e_vals) / mu_vals) * p_vals * e_vals)
633+
if dose_quantity == 'absorbed-air':
634+
# units [eV atoms-1 s-1]
635+
cdr_nuc += np.sum((response_f(e_vals) / mu_vals) * p_vals * e_vals)
636+
elif dose_quantity == 'effective':
637+
# units [pSv g atoms-1 s-1]
638+
cdr_nuc += np.sum((response_f(e_vals) / mu_vals) * p_vals)
639+
609640

610641
elif isinstance(photon_source_per_atom, Tabular):
611642

612643
# generate the tabulated1D functions
613644
e_p_dist = Tabulated1D(
614645
e_vals, p_vals, breakpoints=[len(e_vals)], interpolation=[1]
615646
)
616-
e_e_dist = Tabulated1D(
617-
e_vals, e_vals, breakpoints=[len(e_vals)], interpolation=[2]
618-
)
619647

620648

621649
# limit the computation to the tabulated mu_en_air range
@@ -632,10 +660,22 @@ def get_photon_contact_dose_rate(self, by_nuclide: bool = False) -> float | dic
632660
f"Mass attenuation coefficient <= 0 at energies: {zero_vals}"
633661
)
634662

635-
integrand_operator = Combination(
636-
functions=[mu_en_air, e_p_dist, e_e_dist, mass_attenuation_dist],
637-
operations=[np.multiply, np.multiply, np.divide],
638-
)
663+
if dose_quantity == 'absorbed-air':
664+
# units [eV atoms-1 s-1]
665+
e_e_dist = Tabulated1D(
666+
e_vals, e_vals, breakpoints=[len(e_vals)], interpolation=[2]
667+
)
668+
integrand_operator = Combination(
669+
functions=[response_f, e_p_dist, e_e_dist, mass_attenuation_dist],
670+
operations=[np.multiply, np.multiply, np.divide],
671+
)
672+
elif dose_quantity == 'effective':
673+
# units [pSv g atoms-1 s-1]
674+
integrand_operator = Combination(
675+
functions=[response_f, e_p_dist, mass_attenuation_dist],
676+
operations=[np.multiply, np.divide],
677+
)
678+
639679

640680
y_evaluated = integrand_operator(e_union)
641681

@@ -646,10 +686,12 @@ def get_photon_contact_dose_rate(self, by_nuclide: bool = False) -> float | dic
646686
cdr_nuc += integrand_function.integral()[-1]
647687

648688

649-
# units [eV barns-1 cm-1 s-1]
689+
# units effective dose [eV barns-1 cm-1 s-1]
690+
# units air-absorbed dose [pSv g barns-1 cm-1 s-1]
650691
cdr_nuc *= nuc_atoms_per_bcm
651692

652-
# units [Sv hr-1] - includes build up factor
693+
# units effective dose [Sv hr-1]
694+
# units air-absorbed dose [Gy hr-1]
653695
cdr_nuc *= multiplier
654696

655697
cdr[nuc] = cdr_nuc

0 commit comments

Comments
 (0)