Adding per m3 to material functions (#3912)

Co-authored-by: Jon Shimwell <jon@proximafusion.com>
Co-authored-by: Paul Romano <paul.k.romano@gmail.com>
This commit is contained in:
Jonathan Shimwell 2026-04-03 02:00:02 +02:00 committed by GitHub
parent df985e10b3
commit 9ff50499e1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 24 additions and 13 deletions

View file

@ -113,7 +113,7 @@ class Results(list):
---------- ----------
mat : openmc.Material, str mat : openmc.Material, str
Material object or material id to evaluate Material object or material id to evaluate
units : {'Bq', 'Bq/g', 'Bq/kg', 'Bq/cm3'} units : {'Bq', 'Bq/g', 'Bq/kg', 'Bq/cm3', 'Bq/m3'}
Specifies the type of activity to return, options include total Specifies the type of activity to return, options include total
activity [Bq], specific [Bq/g, Bq/kg] or volumetric activity [Bq/cm3]. activity [Bq], specific [Bq/g, Bq/kg] or volumetric activity [Bq/cm3].
by_nuclide : bool by_nuclide : bool
@ -231,7 +231,7 @@ class Results(list):
---------- ----------
mat : openmc.Material, str mat : openmc.Material, str
Material object or material id to evaluate. Material object or material id to evaluate.
units : {'W', 'W/g', 'W/kg', 'W/cm3'} units : {'W', 'W/g', 'W/kg', 'W/cm3', 'W/m3'}
Specifies the units of decay heat to return. Options include total Specifies the units of decay heat to return. Options include total
heat [W], specific [W/g, W/kg] or volumetric heat [W/cm3]. heat [W], specific [W/g, W/kg] or volumetric heat [W/cm3].
by_nuclide : bool by_nuclide : bool

View file

@ -349,7 +349,7 @@ class Material(IDManagerMixin):
clip_tolerance : float clip_tolerance : float
Maximum fraction of :math:`\sum_i x_i p_i` for discrete distributions Maximum fraction of :math:`\sum_i x_i p_i` for discrete distributions
that will be discarded. that will be discarded.
units : {'Bq', 'Bq/g', 'Bq/kg', 'Bq/cm3'} units : {'Bq', 'Bq/g', 'Bq/kg', 'Bq/cm3', 'Bq/m3'}
Specifies the units on the integral of the distribution. Specifies the units on the integral of the distribution.
volume : float, optional volume : float, optional
Volume of the material. If not passed, defaults to using the Volume of the material. If not passed, defaults to using the
@ -367,7 +367,7 @@ class Material(IDManagerMixin):
the total intensity of the photon source in the requested units. the total intensity of the photon source in the requested units.
""" """
cv.check_value('units', units, {'Bq', 'Bq/g', 'Bq/kg', 'Bq/cm3'}) cv.check_value('units', units, {'Bq', 'Bq/g', 'Bq/kg', 'Bq/cm3', 'Bq/m3'})
if exclude_nuclides is not None and include_nuclides is not None: if exclude_nuclides is not None and include_nuclides is not None:
raise ValueError("Cannot specify both exclude_nuclides and include_nuclides") raise ValueError("Cannot specify both exclude_nuclides and include_nuclides")
@ -378,6 +378,8 @@ class Material(IDManagerMixin):
raise ValueError("volume must be specified if units='Bq'") raise ValueError("volume must be specified if units='Bq'")
elif units == 'Bq/cm3': elif units == 'Bq/cm3':
multiplier = 1 multiplier = 1
elif units == 'Bq/m3':
multiplier = 1e6
elif units == 'Bq/g': elif units == 'Bq/g':
multiplier = 1.0 / self.get_mass_density() multiplier = 1.0 / self.get_mass_density()
elif units == 'Bq/kg': elif units == 'Bq/kg':
@ -1383,16 +1385,16 @@ class Material(IDManagerMixin):
def get_activity(self, units: str = 'Bq/cm3', by_nuclide: bool = False, def get_activity(self, units: str = 'Bq/cm3', by_nuclide: bool = False,
volume: float | None = None) -> dict[str, float] | float: volume: float | None = None) -> dict[str, float] | float:
"""Returns the activity of the material or of each nuclide within. """Return the activity of the material or each nuclide within.
.. versionadded:: 0.13.1 .. versionadded:: 0.13.1
Parameters Parameters
---------- ----------
units : {'Bq', 'Bq/g', 'Bq/kg', 'Bq/cm3', 'Ci', 'Ci/m3'} units : {'Bq', 'Bq/g', 'Bq/kg', 'Bq/cm3', 'Bq/m3', 'Ci', 'Ci/m3'}
Specifies the type of activity to return, options include total Specifies the type of activity to return, options include total
activity [Bq,Ci], specific [Bq/g, Bq/kg] or volumetric activity activity [Bq,Ci], specific [Bq/g, Bq/kg] or volumetric activity
[Bq/cm3,Ci/m3]. Default is volumetric activity [Bq/cm3]. [Bq/cm3, Bq/m3, Ci/m3]. Default is volumetric activity [Bq/cm3].
by_nuclide : bool by_nuclide : bool
Specifies if the activity should be returned for the material as a Specifies if the activity should be returned for the material as a
whole or per nuclide. Default is False. whole or per nuclide. Default is False.
@ -1410,7 +1412,7 @@ class Material(IDManagerMixin):
of the material is returned as a float. of the material is returned as a float.
""" """
cv.check_value('units', units, {'Bq', 'Bq/g', 'Bq/kg', 'Bq/cm3', 'Ci', 'Ci/m3'}) cv.check_value('units', units, {'Bq', 'Bq/g', 'Bq/kg', 'Bq/cm3', 'Bq/m3', 'Ci', 'Ci/m3'})
cv.check_type('by_nuclide', by_nuclide, bool) cv.check_type('by_nuclide', by_nuclide, bool)
if volume is None: if volume is None:
@ -1420,6 +1422,8 @@ class Material(IDManagerMixin):
multiplier = volume multiplier = volume
elif units == 'Bq/cm3': elif units == 'Bq/cm3':
multiplier = 1 multiplier = 1
elif units == 'Bq/m3':
multiplier = 1e6
elif units == 'Bq/g': elif units == 'Bq/g':
multiplier = 1.0 / self.get_mass_density() multiplier = 1.0 / self.get_mass_density()
elif units == 'Bq/kg': elif units == 'Bq/kg':
@ -1438,16 +1442,15 @@ class Material(IDManagerMixin):
def get_decay_heat(self, units: str = 'W', by_nuclide: bool = False, def get_decay_heat(self, units: str = 'W', by_nuclide: bool = False,
volume: float | None = None) -> dict[str, float] | float: volume: float | None = None) -> dict[str, float] | float:
"""Returns the decay heat of the material or for each nuclide in the """Return the decay heat of the material or each nuclide within.
material in units of [W], [W/g], [W/kg] or [W/cm3].
.. versionadded:: 0.13.3 .. versionadded:: 0.13.3
Parameters Parameters
---------- ----------
units : {'W', 'W/g', 'W/kg', 'W/cm3'} units : {'W', 'W/g', 'W/kg', 'W/cm3', 'W/m3'}
Specifies the units of decay heat to return. Options include total Specifies the units of decay heat to return. Options include total
heat [W], specific [W/g, W/kg] or volumetric heat [W/cm3]. heat [W], specific [W/g, W/kg] or volumetric heat [W/cm3, W/m3].
Default is total heat [W]. Default is total heat [W].
by_nuclide : bool by_nuclide : bool
Specifies if the decay heat should be returned for the material as a Specifies if the decay heat should be returned for the material as a
@ -1466,13 +1469,15 @@ class Material(IDManagerMixin):
of the material is returned as a float. of the material is returned as a float.
""" """
cv.check_value('units', units, {'W', 'W/g', 'W/kg', 'W/cm3'}) cv.check_value('units', units, {'W', 'W/g', 'W/kg', 'W/cm3', 'W/m3'})
cv.check_type('by_nuclide', by_nuclide, bool) cv.check_type('by_nuclide', by_nuclide, bool)
if units == 'W': if units == 'W':
multiplier = volume if volume is not None else self.volume multiplier = volume if volume is not None else self.volume
elif units == 'W/cm3': elif units == 'W/cm3':
multiplier = 1 multiplier = 1
elif units == 'W/m3':
multiplier = 1e6
elif units == 'W/g': elif units == 'W/g':
multiplier = 1.0 / self.get_mass_density() multiplier = 1.0 / self.get_mass_density()
elif units == 'W/kg': elif units == 'W/kg':

View file

@ -594,6 +594,8 @@ def test_get_activity():
assert pytest.approx(m4.get_activity(units='Bq/g', by_nuclide=True)["H3"]) == 355978108155965.94 # [Bq/g] assert pytest.approx(m4.get_activity(units='Bq/g', by_nuclide=True)["H3"]) == 355978108155965.94 # [Bq/g]
assert pytest.approx(m4.get_activity(units='Bq/cm3')) == 355978108155965.94*3/2 # [Bq/cc] assert pytest.approx(m4.get_activity(units='Bq/cm3')) == 355978108155965.94*3/2 # [Bq/cc]
assert pytest.approx(m4.get_activity(units='Bq/cm3', by_nuclide=True)["H3"]) == 355978108155965.94*3/2 # [Bq/cc] assert pytest.approx(m4.get_activity(units='Bq/cm3', by_nuclide=True)["H3"]) == 355978108155965.94*3/2 # [Bq/cc]
assert pytest.approx(m4.get_activity(units='Bq/m3')) == 355978108155965.94*3/2*1e6 # [Bq/m3]
assert pytest.approx(m4.get_activity(units='Bq/m3', by_nuclide=True)["H3"]) == 355978108155965.94*3/2*1e6 # [Bq/m3]
# volume is required to calculate total activity # volume is required to calculate total activity
m4.volume = 10. m4.volume = 10.
assert pytest.approx(m4.get_activity(units='Bq')) == 355978108155965.94*3/2*10 # [Bq] assert pytest.approx(m4.get_activity(units='Bq')) == 355978108155965.94*3/2*10 # [Bq]
@ -650,6 +652,8 @@ def test_get_decay_heat():
assert pytest.approx(m4.get_decay_heat(units='W/g', by_nuclide=True)["I135"]) == 40175.15720273193 # [W/g] assert pytest.approx(m4.get_decay_heat(units='W/g', by_nuclide=True)["I135"]) == 40175.15720273193 # [W/g]
assert pytest.approx(m4.get_decay_heat(units='W/cm3')) == 40175.15720273193*3/2 # [W/cc] assert pytest.approx(m4.get_decay_heat(units='W/cm3')) == 40175.15720273193*3/2 # [W/cc]
assert pytest.approx(m4.get_decay_heat(units='W/cm3', by_nuclide=True)["I135"]) == 40175.15720273193*3/2 #[W/cc] assert pytest.approx(m4.get_decay_heat(units='W/cm3', by_nuclide=True)["I135"]) == 40175.15720273193*3/2 #[W/cc]
assert pytest.approx(m4.get_decay_heat(units='W/m3')) == 40175.15720273193*3/2*1e6 # [W/m3]
assert pytest.approx(m4.get_decay_heat(units='W/m3', by_nuclide=True)["I135"]) == 40175.15720273193*3/2*1e6 # [W/m3]
# volume is required to calculate total decay heat # volume is required to calculate total decay heat
m4.volume = 10. m4.volume = 10.
assert pytest.approx(m4.get_decay_heat(units='W')) == 40175.15720273193*3/2*10 # [W] assert pytest.approx(m4.get_decay_heat(units='W')) == 40175.15720273193*3/2*10 # [W]
@ -680,6 +684,8 @@ def test_decay_photon_energy():
src_per_bqg = m.get_decay_photon_energy(units='Bq/g') src_per_bqg = m.get_decay_photon_energy(units='Bq/g')
src_per_bqkg = m.get_decay_photon_energy(units='Bq/kg') src_per_bqkg = m.get_decay_photon_energy(units='Bq/kg')
assert pytest.approx(src_per_bqg.integral()) == src_per_bqkg.integral() / 1000. assert pytest.approx(src_per_bqg.integral()) == src_per_bqkg.integral() / 1000.
src_per_bqm3 = m.get_decay_photon_energy(units='Bq/m3')
assert pytest.approx(src_per_bqm3.integral()) == src_per_cm3.integral() * 1e6
# If we add Xe135 (which has a tabular distribution), the photon source # If we add Xe135 (which has a tabular distribution), the photon source
# should be a mixture distribution # should be a mixture distribution