Skip to content

Commit

Permalink
Adding source option to plot (#2863)
Browse files Browse the repository at this point in the history
Co-authored-by: Jon Shimwell <[email protected]>
Co-authored-by: Paul Romano <[email protected]>
  • Loading branch information
3 people authored Sep 26, 2024
1 parent 890cab5 commit 8b77a8d
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 7 deletions.
5 changes: 2 additions & 3 deletions openmc/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
import warnings
import lxml.etree as ET

import numpy as np

import openmc
import openmc._xml as xml
from .checkvalue import check_type, check_less_than, check_greater_than, PathLike
Expand Down Expand Up @@ -67,7 +65,7 @@ def root_universe(self, root_universe):
self._root_universe = root_universe

@property
def bounding_box(self) -> np.ndarray:
def bounding_box(self) -> openmc.BoundingBox:
return self.root_universe.bounding_box

@property
Expand Down Expand Up @@ -800,6 +798,7 @@ def plot(self, *args, **kwargs):
Units used on the plot axis
**kwargs
Keyword arguments passed to :func:`matplotlib.pyplot.imshow`
Returns
-------
matplotlib.axes.Axes
Expand Down
9 changes: 6 additions & 3 deletions openmc/lib/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,8 +474,11 @@ def run(output=True):
_dll.openmc_run()


def sample_external_source(n_samples=1, prn_seed=None):
"""Sample external source
def sample_external_source(
n_samples: int = 1000,
prn_seed: int | None = None
) -> list[openmc.SourceParticle]:
"""Sample external source and return source particles.
.. versionadded:: 0.13.1
Expand All @@ -490,7 +493,7 @@ def sample_external_source(n_samples=1, prn_seed=None):
Returns
-------
list of openmc.SourceParticle
List of samples source particles
List of sampled source particles
"""
if n_samples <= 0:
Expand Down
116 changes: 116 additions & 0 deletions openmc/model/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import h5py
import lxml.etree as ET
import numpy as np

import openmc
import openmc._xml as xml
Expand Down Expand Up @@ -793,6 +794,121 @@ def calculate_volumes(self, threads=None, output=True, cwd='.',
openmc.lib.materials[domain_id].volume = \
vol_calc.volumes[domain_id].n

def plot(
self,
n_samples: int | None = None,
plane_tolerance: float = 1.,
source_kwargs: dict | None = None,
**kwargs,
):
"""Display a slice plot of the geometry.
.. versionadded:: 0.15.1
Parameters
----------
n_samples : dict
The number of source particles to sample and add to plot. Defaults
to None which doesn't plot any particles on the plot.
plane_tolerance: float
When plotting a plane the source locations within the plane +/-
the plane_tolerance will be included and those outside of the
plane_tolerance will not be shown
source_kwargs : dict
Keyword arguments passed to :func:`matplotlib.pyplot.scatter`.
**kwargs
Keyword arguments passed to :func:`openmc.Universe.plot`
Returns
-------
matplotlib.axes.Axes
Axes containing resulting image
"""

check_type('n_samples', n_samples, int | None)
check_type('plane_tolerance', plane_tolerance, float)
if source_kwargs is None:
source_kwargs = {}
source_kwargs.setdefault('marker', 'x')

ax = self.geometry.plot(**kwargs)
if n_samples:
# Sample external source particles
particles = self.sample_external_source(n_samples)

# Determine plotting parameters and bounding box of geometry
bbox = self.geometry.bounding_box
origin = kwargs.get('origin', None)
basis = kwargs.get('basis', 'xy')
indices = {'xy': (0, 1, 2), 'xz': (0, 2, 1), 'yz': (1, 2, 0)}[basis]

# Infer origin if not provided
if np.isinf(bbox.extent[basis]).any():
if origin is None:
origin = (0, 0, 0)
else:
if origin is None:
# if nan values in the bbox.center they get replaced with 0.0
# this happens when the bounding_box contains inf values
with warnings.catch_warnings():
warnings.simplefilter("ignore", RuntimeWarning)
origin = np.nan_to_num(bbox.center)

slice_index = indices[2]
slice_value = origin[slice_index]

xs = []
ys = []
tol = plane_tolerance
for particle in particles:
if (slice_value - tol < particle.r[slice_index] < slice_value + tol):
xs.append(particle.r[indices[0]])
ys.append(particle.r[indices[1]])
ax.scatter(xs, ys, **source_kwargs)
return ax

def sample_external_source(
self,
n_samples: int = 1000,
prn_seed: int | None = None,
**init_kwargs
) -> list[openmc.SourceParticle]:
"""Sample external source and return source particles.
.. versionadded:: 0.15.1
Parameters
----------
n_samples : int
Number of samples
prn_seed : int
Pseudorandom number generator (PRNG) seed; if None, one will be
generated randomly.
**init_kwargs
Keyword arguments passed to :func:`openmc.lib.init`
Returns
-------
list of openmc.SourceParticle
List of samples source particles
"""
import openmc.lib

# Silence output by default. Also set arguments to start in volume
# calculation mode to avoid loading cross sections
init_kwargs.setdefault('output', False)
init_kwargs.setdefault('args', ['-c'])

with change_directory(tmpdir=True):
# Export model within temporary directory
self.export_to_model_xml()

# Sample external source sites
with openmc.lib.run_in_memory(**init_kwargs):
return openmc.lib.sample_external_source(
n_samples=n_samples, prn_seed=prn_seed
)

def plot_geometry(self, output=True, cwd='.', openmc_exec='openmc'):
"""Creates plot images as specified by the Model.plots attribute
Expand Down
3 changes: 2 additions & 1 deletion openmc/universe.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from __future__ import annotations
import math
from abc import ABC, abstractmethod
from collections.abc import Iterable
Expand Down Expand Up @@ -230,7 +231,7 @@ def cells(self):
return self._cells

@property
def bounding_box(self):
def bounding_box(self) -> openmc.BoundingBox:
regions = [c.region for c in self.cells.values()
if c.region is not None]
if regions:
Expand Down
29 changes: 29 additions & 0 deletions tests/unit_tests/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -591,3 +591,32 @@ def test_single_xml_exec(run_in_tmpdir):

os.mkdir('subdir')
pincell_model.run(path='subdir')


def test_model_plot():
# plots the geometry with source location and checks the resulting
# matplotlib includes the correct coordinates for the scatter plot for all
# basis.

surface = openmc.Sphere(r=600, boundary_type="vacuum")
cell = openmc.Cell(region=-surface)
geometry = openmc.Geometry([cell])
source = openmc.IndependentSource(space=openmc.stats.Point((1, 2, 3)))
settings = openmc.Settings(particles=1, batches=1, source=source)
model = openmc.Model(geometry, settings=settings)

plot = model.plot(n_samples=1, plane_tolerance=4.0, basis="xy")
coords = plot.axes.collections[0].get_offsets().data.flatten()
assert (coords == np.array([1.0, 2.0])).all()

plot = model.plot(n_samples=1, plane_tolerance=4.0, basis="xz")
coords = plot.axes.collections[0].get_offsets().data.flatten()
assert (coords == np.array([1.0, 3.0])).all()

plot = model.plot(n_samples=1, plane_tolerance=4.0, basis="yz")
coords = plot.axes.collections[0].get_offsets().data.flatten()
assert (coords == np.array([2.0, 3.0])).all()

plot = model.plot(n_samples=1, plane_tolerance=0.1, basis="xy")
coords = plot.axes.collections[0].get_offsets().data.flatten()
assert (coords == np.array([])).all()

0 comments on commit 8b77a8d

Please sign in to comment.