Merge pull request #1190 from pshriwise/capi_id_map

Add ID map function to the capi
This commit is contained in:
Paul Romano 2019-03-15 10:57:51 -05:00 committed by GitHub
commit 04c5c20346
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 357 additions and 31 deletions

View file

@ -70,6 +70,7 @@ extern "C" {
int openmc_next_batch(int* status);
int openmc_nuclide_name(int index, const char** name);
int openmc_plot_geometry();
int openmc_id_map(const void* slice, int32_t* data_out);
int openmc_reset();
int openmc_run();
void openmc_set_seed(int64_t new_seed);

View file

@ -52,6 +52,7 @@ struct RGBColor {
};
typedef xt::xtensor<RGBColor, 2> ImageData;
typedef xt::xtensor<int32_t, 3> IdData;
enum class PlotType {
slice = 1,
@ -72,8 +73,16 @@ enum class PlotColorBy {
//===============================================================================
// Plot class
//===============================================================================
struct PlotBase {
// Members
Position origin_; //!< Plot origin in geometry
Position width_; //!< Plot width in geometry
PlotBasis basis_; //!< Plot basis (XY/XZ/YZ)
std::array<int, 3> pixels_; //!< Plot size in pixels
int level_; //!< Plot universe level
};
class Plot
class Plot : public PlotBase
{
public:
@ -95,17 +104,12 @@ private:
void set_meshlines(pugi::xml_node plot_node);
void set_mask(pugi::xml_node plot_node);
// Members
// Members
public:
int id_; //!< Plot ID
PlotType type_; //!< Plot type (Slice/Voxel)
PlotColorBy color_by_; //!< Plot coloring (cell/material)
Position origin_; //!< Plot origin in geometry
Position width_; //!< Plot width in geometry
PlotBasis basis_; //!< Plot basis (XY/XZ/YZ)
std::array<int, 3> pixels_; //!< Plot size in pixels
int meshlines_width_; //!< Width of lines added to the plot
int level_; //!< Plot universe level
int index_meshlines_mesh_; //!< Index of the mesh to draw on the plot
RGBColor meshlines_color_; //!< Color of meshlines on the plot
RGBColor not_found_; //!< Plot background color

View file

@ -42,7 +42,6 @@ else:
def _dagmc_enabled():
return c_bool.in_dll(_dll, "dagmc_enabled").value
from .error import *
from .core import *
from .nuclide import *
@ -53,3 +52,4 @@ from .filter import *
from .tally import *
from .settings import settings
from .math import *
from .plot import *

220
openmc/capi/plot.py Normal file
View file

@ -0,0 +1,220 @@
from ctypes import c_int, c_int32, c_double, Structure, POINTER
from . import _dll
from .core import _DLLGlobal
from .error import _error_handler
import numpy as np
class _Position(Structure):
"""Definition of an xyz location in space with underlying c-types
C-type Attributes
-----------------
x : c_double
Position's x value (default: 0.0)
y : c_double
Position's y value (default: 0.0)
z : c_double
Position's z value (default: 0.0)
"""
_fields_ = [('x', c_double),
('y', c_double),
('z', c_double)]
def __getitem__(self, idx):
if idx == 0:
return self.x
elif idx == 1:
return self.y
elif idx == 2:
return self.z
else:
raise IndexError("{} index is invalid for _Position".format(key))
def __setitem__(self, idx, val):
if idx == 0:
self.x = val
elif idx == 1:
self.y = val
elif idx == 2:
self.z = val
else:
raise IndexError("{} index is invalid for _Position".format(idx))
def __repr__(self):
return "({}, {}, {})".format(self.x, self.y, self.z)
class _PlotBase(Structure):
"""A structure defining a 2-D geometry slice with underlying c-types
C-Type Attributes
-----------------
origin : openmc.capi.plot._Position
A position defining the origin of the plot.
width_ : openmc.capi.plot._Position
The width of the plot along the x, y, and z axes, respectively
basis_ : c_int
The axes basis of the plot view.
pixels_ : c_int[3]
The resolution of the plot in the horizontal and vertical dimensions
level_ : c_int
The universe level for the plot view
Attributes
----------
origin : tuple or list of ndarray
Origin (center) of the plot
width : float
The horizontal dimension of the plot in geometry units (cm)
height : float
The vertical dimension of the plot in geometry units (cm)
basis : string
One of {'xy', 'xz', 'yz'} indicating the horizontal and vertical
axes of the plot.
h_res : float
The horizontal resolution of the plot in pixels
v_res : float
The vertical resolution of the plot in pixels
level : int
The universe level for the plot (default: -1 -> all universes shown)
"""
_fields_ = [('origin_', _Position),
('width_', _Position),
('basis_', c_int),
('pixels_', 3*c_int),
('level_', c_int)]
def __init__(self):
self.level_ = -1
@property
def origin(self):
return self.origin_
@property
def width(self):
return self.width_.x
@property
def height(self):
return self.width_.y
@property
def basis(self):
if self.basis_ == 1:
return 'xy'
elif self.basis_ == 2:
return 'xz'
elif self.basis_ == 3:
return 'yz'
raise ValueError("Plot basis {} is invalid".format(self.basis_))
@property
def h_res(self):
return self.pixels_[0]
@property
def v_res(self):
return self.pixels_[1]
@property
def level(self):
return int(self.level_)
@origin.setter
def origin(self, origin):
self.origin_.x = origin[0]
self.origin_.y = origin[1]
self.origin_.z = origin[2]
@width.setter
def width(self, width):
self.width_.x = width
@height.setter
def height(self, height):
self.width_.y = height
@basis.setter
def basis(self, basis):
if isinstance(basis, str):
valid_bases = ('xy', 'xz', 'yz')
basis = basis.lower()
if basis not in valid_bases:
raise ValueError("{} is not a valid plot basis.".format(basis))
if basis == 'xy':
self.basis_ = 1
elif basis == 'xz':
self.basis_ = 2
elif basis == 'yz':
self.basis_ = 3
return
if isinstance(basis, int):
valid_bases = (1, 2, 3)
if basis not in valid_bases:
raise ValueError("{} is not a valid plot basis.".format(basis))
self.basis_ = basis
return
raise ValueError("{} of type {} is an"
" invalid plot basis".format(basis, type(basis)))
@h_res.setter
def h_res(self, h_res):
self.pixels_[0] = h_res
@v_res.setter
def v_res(self, v_res):
self.pixels_[1] = v_res
@level.setter
def level(self, level):
self.level_ = level
def __repr__(self):
out_str = ["-----",
"Plot:",
"-----",
"Origin: {}".format(self.origin),
"Width: {}".format(self.width),
"Height: {}".format(self.height),
"Basis: {}".format(self.basis),
"HRes: {}".format(self.h_res),
"VRes: {}".format(self.v_res),
"Level: {}".format(self.level)]
return '\n'.join(out_str)
_dll.openmc_id_map.argtypes = [POINTER(_PlotBase), POINTER(c_int32)]
_dll.openmc_id_map.restype = c_int
_dll.openmc_id_map.errcheck = _error_handler
def id_map(plot):
"""
Generate a 2-D map of (cell_id, material_id). Used for in-memory image
generation.
Parameters
----------
plot : openmc.capi.plot._PlotBase
Object describing the slice of the model to be generated
Returns
-------
id_map : numpy.ndarray
A NumPy array with shape (vertical pixels, horizontal pixels, 2) of
OpenMC property ids with dtype int32
"""
img_data = np.zeros((plot.v_res, plot.h_res, 2),
dtype=np.dtype('int32'))
_dll.openmc_id_map(POINTER(_PlotBase)(plot),
img_data.ctypes.data_as(POINTER(c_int32)))
return img_data

View file

@ -1,5 +1,6 @@
#include "openmc/plot.h"
#include <algorithm>
#include <fstream>
#include <sstream>
@ -27,7 +28,7 @@ namespace openmc {
const RGBColor WHITE {255, 255, 255};
constexpr int PLOT_LEVEL_LOWEST {-1}; //!< lower bound on plot universe level
constexpr int NOT_FOUND {-1};
//==============================================================================
// Global variables
//==============================================================================
@ -133,26 +134,27 @@ void create_ppm(Plot pl)
Direction u {0.5, 0.5, 0.5};
#pragma omp parallel
{
Particle p;
p.r() = r;
p.u() = u;
p.coord_[0].universe = model::root_universe;
#pragma omp parallel
{
Particle p;
p.r() = r;
p.u() = u;
p.coord_[0].universe = model::root_universe;
#pragma omp for
for (int y = 0; y < height; y++) {
p.r()[out_i] = r[out_i] - out_pixel * y;
for (int x = 0; x < width; x++) {
// local variables
RGBColor rgb;
int id;
p.r()[in_i] = r[in_i] + in_pixel * x;
position_rgb(p, pl, rgb, id);
data(x,y) = rgb;
#pragma omp for
for (int y = 0; y < height; y++) {
p.r()[out_i] = r[out_i] - out_pixel * y;
for (int x = 0; x < width; x++) {
// local variables
RGBColor rgb;
int id;
p.r()[in_i] = r[in_i] + in_pixel * x;
position_rgb(p, pl, rgb, id);
data(x,y) = rgb;
}
}
}
}
// draw mesh lines if present
if (pl.index_meshlines_mesh_ >= 0) {draw_mesh_lines(pl, data);}
@ -618,8 +620,8 @@ Plot::set_mask(pugi::xml_node plot_node)
}
}
Plot::Plot(pugi::xml_node plot_node):
index_meshlines_mesh_(-1)
Plot::Plot(pugi::xml_node plot_node)
: index_meshlines_mesh_{-1}
{
set_id(plot_node);
set_type(plot_node);
@ -657,7 +659,7 @@ void position_rgb(Particle p, Plot pl, RGBColor& rgb, int& id)
if (!found_cell) {
// If no cell, revert to default color
rgb = pl.not_found_;
id = -1;
id = NOT_FOUND;
} else {
if (PlotColorBy::mats == pl.color_by_) {
// Assign color based on material
@ -665,11 +667,11 @@ void position_rgb(Particle p, Plot pl, RGBColor& rgb, int& id)
if (c->type_ == FILL_UNIVERSE) {
// If we stopped on a middle universe level, treat as if not found
rgb = pl.not_found_;
id = -1;
id = NOT_FOUND;
} else if (p.material_ == MATERIAL_VOID) {
// By default, color void cells white
rgb = WHITE;
id = -1;
id = NOT_FOUND;
} else {
rgb = pl.colors_[p.material_];
id = model::materials[p.material_]->id_;
@ -952,4 +954,84 @@ RGBColor random_color() {
return {int(prn()*255), int(prn()*255), int(prn()*255)};
}
extern "C" int openmc_id_map(const void* plot, int32_t* data_out)
{
auto plt = reinterpret_cast<const PlotBase*>(plot);
if (!plt) {
set_errmsg("Invalid slice pointer passed to openmc_id_map");
return OPENMC_E_INVALID_ARGUMENT;
}
size_t width = plt->pixels_[0];
size_t height = plt->pixels_[1];
// get pixel size
double in_pixel = (plt->width_[0])/static_cast<double>(width);
double out_pixel = (plt->width_[1])/static_cast<double>(height);
// size data array
IdData data({height, width, 2}, NOT_FOUND);
// setup basis indices and initial position centered on pixel
int in_i, out_i;
Position xyz = plt->origin_;
switch(plt->basis_) {
case PlotBasis::xy :
in_i = 0;
out_i = 1;
break;
case PlotBasis::xz :
in_i = 0;
out_i = 2;
break;
case PlotBasis::yz :
in_i = 1;
out_i = 2;
break;
}
// set initial position
xyz[in_i] = plt->origin_[in_i] - plt->width_[0] / 2. + in_pixel / 2.;
xyz[out_i] = plt->origin_[out_i] + plt->width_[1] / 2. - out_pixel / 2.;
// arbitrary direction
Direction dir = {0.5, 0.5, 0.5};
#pragma omp parallel
{
Particle p;
p.r() = xyz;
p.u() = dir;
p.coord_[0].universe = model::root_universe;
int level = plt->level_;
int j{};
#pragma omp for
for (int y = 0; y < height; y++) {
p.r()[out_i] = xyz[out_i] - out_pixel * y;
for (int x = 0; x < width; x++) {
p.r()[in_i] = xyz[in_i] + in_pixel * x;
p.n_coord_ = 1;
// local variables
bool found_cell = find_cell(&p, 0);
j = p.n_coord_ - 1;
if (level >=0) {j = level + 1;}
if (found_cell) {
Cell* c = model::cells[p.coord_[j].cell].get();
data(y,x,0) = c->id_;
if (c->type_ != FILL_UNIVERSE && p.material_ != MATERIAL_VOID) {
data(y,x,1) = model::materials[p.material_]->id_;
}
}
} // inner for
} // outer for
} // omp parallel
// write id data to array
std::copy(data.begin(), data.end(), data_out);
return 0;
}
} // namespace openmc

View file

@ -400,3 +400,22 @@ def test_load_nuclide(capi_init):
# load non-existent nuclide
with pytest.raises(exc.DataError):
openmc.capi.load_nuclide('Pu3')
def test_id_map(capi_init):
expected_ids = np.array([[(3, 3), (2, 2), (3, 3)],
[(2, 2), (1, 1), (2, 2)],
[(3, 3), (2, 2), (3, 3)]], dtype='int32')
# create a plot object
s = openmc.capi.plot._PlotBase()
s.width = 1.26
s.height = 1.26
s.v_res = 3
s.h_res = 3
s.origin = (0.0, 0.0, 0.0)
s.basis = 'xy'
s.level = -1
ids = openmc.capi.plot.id_map(s)
assert np.array_equal(expected_ids, ids)