mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-28 22:26:08 -04:00
Refactor ParticleType to use PDG Monte Carlo numbering scheme (#3756)
Co-authored-by: GuySten <62616591+GuySten@users.noreply.github.com> Co-authored-by: Amanda Lund <alund1187@gmail.com>
This commit is contained in:
parent
fc0d9eec65
commit
b41e22f68b
62 changed files with 1401 additions and 558 deletions
125
openmc/source.py
125
openmc/source.py
|
|
@ -1,7 +1,6 @@
|
|||
from __future__ import annotations
|
||||
from abc import ABC, abstractmethod
|
||||
from collections.abc import Iterable, Sequence
|
||||
from enum import IntEnum
|
||||
from numbers import Real
|
||||
from pathlib import Path
|
||||
import warnings
|
||||
|
|
@ -19,6 +18,8 @@ from openmc.stats.multivariate import UnitSphere, Spatial
|
|||
from openmc.stats.univariate import Univariate
|
||||
from ._xml import get_elem_list, get_text
|
||||
from .mesh import MeshBase, StructuredMesh, UnstructuredMesh
|
||||
from .particle_type import ParticleType
|
||||
from .statepoint import _VERSION_STATEPOINT
|
||||
from .utility_funcs import input_path
|
||||
|
||||
|
||||
|
|
@ -265,8 +266,8 @@ class IndependentSource(SourceBase):
|
|||
time distribution of source sites
|
||||
strength : float
|
||||
Strength of the source
|
||||
particle : {'neutron', 'photon', 'electron', 'positron'}
|
||||
Source particle type
|
||||
particle : str or int or openmc.ParticleType
|
||||
Source particle type (name, PDG number, or type)
|
||||
domains : iterable of openmc.Cell, openmc.Material, or openmc.Universe
|
||||
Domains to reject based on, i.e., if a sampled spatial location is not
|
||||
within one of these domains, it will be rejected.
|
||||
|
|
@ -302,10 +303,9 @@ class IndependentSource(SourceBase):
|
|||
type : str
|
||||
Indicator of source type: 'independent'
|
||||
|
||||
.. versionadded:: 0.14.0
|
||||
|
||||
particle : {'neutron', 'photon', 'electron', 'positron'}
|
||||
Source particle type
|
||||
.. versionadded:: 0.14.0
|
||||
particle : str or int or openmc.ParticleType
|
||||
Source particle type (alias, PDG number, or GNDS nuclide name)
|
||||
constraints : dict
|
||||
Constraints on sampled source particles. Valid keys include
|
||||
'domain_type', 'domain_ids', 'time_bounds', 'energy_bounds',
|
||||
|
|
@ -320,7 +320,7 @@ class IndependentSource(SourceBase):
|
|||
energy: openmc.stats.Univariate | None = None,
|
||||
time: openmc.stats.Univariate | None = None,
|
||||
strength: float = 1.0,
|
||||
particle: str = 'neutron',
|
||||
particle: str | int | ParticleType = 'neutron',
|
||||
domains: Sequence[openmc.Cell | openmc.Material |
|
||||
openmc.Universe] | None = None,
|
||||
constraints: dict[str, Any] | None = None
|
||||
|
|
@ -405,14 +405,12 @@ class IndependentSource(SourceBase):
|
|||
self._time = time
|
||||
|
||||
@property
|
||||
def particle(self):
|
||||
def particle(self) -> ParticleType:
|
||||
return self._particle
|
||||
|
||||
@particle.setter
|
||||
def particle(self, particle):
|
||||
cv.check_value('source particle', particle,
|
||||
['neutron', 'photon', 'electron', 'positron'])
|
||||
self._particle = particle
|
||||
self._particle = ParticleType(particle)
|
||||
|
||||
def populate_xml_element(self, element):
|
||||
"""Add necessary source information to an XML element
|
||||
|
|
@ -423,7 +421,7 @@ class IndependentSource(SourceBase):
|
|||
XML element containing source data
|
||||
|
||||
"""
|
||||
element.set("particle", self.particle)
|
||||
element.set("particle", str(self.particle))
|
||||
if self.space is not None:
|
||||
element.append(self.space.to_xml_element())
|
||||
if self.angle is not None:
|
||||
|
|
@ -898,76 +896,6 @@ class FileSource(SourceBase):
|
|||
return cls(**kwargs)
|
||||
|
||||
|
||||
class ParticleType(IntEnum):
|
||||
"""
|
||||
IntEnum class representing a particle type. Type
|
||||
values mirror those found in the C++ class.
|
||||
"""
|
||||
NEUTRON = 0
|
||||
PHOTON = 1
|
||||
ELECTRON = 2
|
||||
POSITRON = 3
|
||||
|
||||
@classmethod
|
||||
def from_string(cls, value: str):
|
||||
"""
|
||||
Constructs a ParticleType instance from a string.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
value : str
|
||||
The string representation of the particle type.
|
||||
|
||||
Returns
|
||||
-------
|
||||
The corresponding ParticleType instance.
|
||||
"""
|
||||
try:
|
||||
return cls[value.upper()]
|
||||
except KeyError:
|
||||
raise ValueError(
|
||||
f"Invalid string for creation of {cls.__name__}: {value}")
|
||||
|
||||
@classmethod
|
||||
def from_pdg_number(cls, pdg_number: int) -> ParticleType:
|
||||
"""Constructs a ParticleType instance from a PDG number.
|
||||
|
||||
The Particle Data Group at LBNL publishes a Monte Carlo particle
|
||||
numbering scheme as part of the `Review of Particle Physics
|
||||
<10.1103/PhysRevD.110.030001>`_. This method maps PDG numbers to the
|
||||
corresponding :class:`ParticleType`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
pdg_number : int
|
||||
The PDG number of the particle type.
|
||||
|
||||
Returns
|
||||
-------
|
||||
The corresponding ParticleType instance.
|
||||
"""
|
||||
try:
|
||||
return {
|
||||
2112: ParticleType.NEUTRON,
|
||||
22: ParticleType.PHOTON,
|
||||
11: ParticleType.ELECTRON,
|
||||
-11: ParticleType.POSITRON,
|
||||
}[pdg_number]
|
||||
except KeyError:
|
||||
raise ValueError(f"Unrecognized PDG number: {pdg_number}")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""
|
||||
Returns a string representation of the ParticleType instance.
|
||||
|
||||
Returns:
|
||||
str: The lowercase name of the ParticleType instance.
|
||||
"""
|
||||
return self.name.lower()
|
||||
|
||||
# needed for < Python 3.11
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
class SourceParticle:
|
||||
|
|
@ -992,8 +920,8 @@ class SourceParticle:
|
|||
Delayed group particle was created in (neutrons only)
|
||||
surf_id : int
|
||||
Surface ID where particle is at, if any.
|
||||
particle : ParticleType
|
||||
Type of the particle
|
||||
particle : ParticleType or str or int
|
||||
Type of the particle (type, name, or PDG number)
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -1006,7 +934,7 @@ class SourceParticle:
|
|||
wgt: float = 1.0,
|
||||
delayed_group: int = 0,
|
||||
surf_id: int = 0,
|
||||
particle: ParticleType = ParticleType.NEUTRON
|
||||
particle: ParticleType | str | int = ParticleType.NEUTRON
|
||||
):
|
||||
|
||||
self.r = tuple(r)
|
||||
|
|
@ -1018,9 +946,16 @@ class SourceParticle:
|
|||
self.surf_id = surf_id
|
||||
self.particle = particle
|
||||
|
||||
@property
|
||||
def particle(self) -> ParticleType:
|
||||
return self._particle
|
||||
|
||||
@particle.setter
|
||||
def particle(self, particle):
|
||||
self._particle = ParticleType(particle)
|
||||
|
||||
def __repr__(self):
|
||||
name = self.particle.name.lower()
|
||||
return f'<SourceParticle: {name} at E={self.E:.6e} eV>'
|
||||
return f'<SourceParticle: {str(self.particle)} at E={self.E:.6e} eV>'
|
||||
|
||||
def to_tuple(self) -> tuple:
|
||||
"""Return source particle attributes as a tuple
|
||||
|
|
@ -1032,7 +967,7 @@ class SourceParticle:
|
|||
|
||||
"""
|
||||
return (self.r, self.u, self.E, self.time, self.wgt,
|
||||
self.delayed_group, self.surf_id, self.particle.value)
|
||||
self.delayed_group, self.surf_id, self.particle.pdg_number)
|
||||
|
||||
|
||||
def write_source_file(
|
||||
|
|
@ -1116,12 +1051,7 @@ class ParticleList(list):
|
|||
particles = []
|
||||
with mcpl.MCPLFile(filename) as f:
|
||||
for particle in f.particles:
|
||||
# Determine particle type based on the PDG number
|
||||
try:
|
||||
particle_type = ParticleType.from_pdg_number(
|
||||
particle.pdgcode)
|
||||
except ValueError:
|
||||
particle_type = "UNKNOWN"
|
||||
particle_type = ParticleType(particle.pdgcode)
|
||||
|
||||
# Create a source particle instance. Note that MCPL stores
|
||||
# energy in MeV and time in ms.
|
||||
|
|
@ -1179,7 +1109,7 @@ class ParticleList(list):
|
|||
# Extract the attributes of the source particles into a list of tuples
|
||||
data = [(sp.r[0], sp.r[1], sp.r[2], sp.u[0], sp.u[1], sp.u[2],
|
||||
sp.E, sp.time, sp.wgt, sp.delayed_group, sp.surf_id,
|
||||
sp.particle.name.lower()) for sp in self]
|
||||
str(sp.particle)) for sp in self]
|
||||
|
||||
# Define the column names for the DataFrame
|
||||
columns = ['x', 'y', 'z', 'u_x', 'u_y', 'u_z', 'E', 'time', 'wgt',
|
||||
|
|
@ -1226,6 +1156,7 @@ class ParticleList(list):
|
|||
kwargs.setdefault('mode', 'w')
|
||||
with h5py.File(filename, **kwargs) as fh:
|
||||
fh.attrs['filetype'] = np.bytes_("source")
|
||||
fh.attrs['version'] = np.array([_VERSION_STATEPOINT, 2])
|
||||
fh.create_dataset('source_bank', data=arr, dtype=source_dtype)
|
||||
|
||||
|
||||
|
|
@ -1337,7 +1268,7 @@ def read_collision_track_mcpl(file_path):
|
|||
data['material_id'].append(int(values_dict.get('material_id', 0)))
|
||||
data['universe_id'].append(int(values_dict.get('universe_id', 0)))
|
||||
data['n_collision'].append(int(values_dict.get('n_collision', 0)))
|
||||
data['particle'].append(ParticleType.from_pdg_number(p.pdgcode))
|
||||
data['particle'].append(ParticleType(p.pdgcode))
|
||||
data['parent_id'].append(int(values_dict.get('parent_id', 0)))
|
||||
data['progeny_id'].append(int(values_dict.get('progeny_id', 0)))
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue