Finish improving Material interface

This commit is contained in:
Paul Romano 2019-07-05 08:00:12 -05:00
parent bbf529bef0
commit 0c32470551
6 changed files with 125 additions and 56 deletions

View file

@ -48,7 +48,7 @@ extern "C" {
int openmc_legendre_filter_set_order(int32_t index, int order);
int openmc_load_nuclide(const char* name);
int openmc_material_add_nuclide(int32_t index, const char name[], double density);
int openmc_material_get_densities(int32_t index, int** nuclides, double** densities, int* n);
int openmc_material_get_densities(int32_t index, const int** nuclides, const double** densities, int* n);
int openmc_material_get_id(int32_t index, int32_t* id);
int openmc_material_get_fissionable(int32_t index, bool* fissionable);
int openmc_material_get_density(int32_t index, double* density);

View file

@ -80,6 +80,34 @@ public:
//! \param[in] units Units of density
void set_density(double density, gsl::cstring_span units);
//! Get nuclides in material
//! \return Indices into the global nuclides vector
gsl::span<const int> nuclides() const { return {nuclide_.data(), nuclide_.size()}; }
//! Get densities of each nuclide in material
//! \return Densities in [atom/b-cm]
gsl::span<const double> densities() const { return {atom_density_.data(), atom_density_.size()}; }
//! Set atom densities for the material
//
//! \param[in] name Name of each nuclide
//! \param[in] density Density of each nuclide in [atom/b-cm]
void set_densities(const std::vector<std::string>& name,
const std::vector<double>& density);
//! Get ID of material
//! \return ID of material
int32_t id() const { return id_; }
//! Assign a unique ID to the material
//! \param[in] Unique ID to assign. A value of -1 indicates that an ID
//! should be automatically assigned.
void set_id(int32_t id);
//! Get whether material is fissionable
//! \return Whether material is fissionable
bool fissionable() const { return fissionable_; }
//! Get volume of material
//! \return Volume in [cm^3]
double volume() const;
@ -128,6 +156,9 @@ private:
void calculate_neutron_xs(Particle& p) const;
void calculate_photon_xs(Particle& p) const;
// Data members
gsl::index index_;
};
//==============================================================================

View file

@ -77,8 +77,8 @@ public:
get_all_bins(const Particle* p, int estimator, FilterMatch& match) const = 0;
//! Assign a unique ID to the filter
//
//! \param[in] Unique ID to assign
//! \param[in] Unique ID to assign. A value of -1 indicates that an ID should
//! be automatically assigned
void set_id(int32_t id);
gsl::index index() const { return index_; }

View file

@ -36,4 +36,6 @@ def _error_handler(err, func, args):
elif err == errcode('OPENMC_E_WARNING'):
warn(msg)
elif err < 0:
raise exc.OpenMCError("Unknown error encountered (code {}).".format(err))
if not msg:
msg = "Unknown error encountered (code {}).".format(err)
raise exc.OpenMCError(msg)

View file

@ -47,9 +47,10 @@ std::unordered_map<int32_t, int32_t> material_map;
//==============================================================================
Material::Material(pugi::xml_node node)
: index_{model::materials.size()}
{
if (check_for_node(node, "id")) {
id_ = std::stoi(get_node_value(node, "id"));
this->set_id(std::stoi(get_node_value(node, "id")));
} else {
fatal_error("Must specify id of material in materials XML file.");
}
@ -849,8 +850,39 @@ void Material::calculate_photon_xs(Particle& p) const
}
}
void Material::set_id(int32_t id)
{
Expects(id >= -1);
// Clear entry in material map if an ID was already assigned before
if (id_ != -1) {
model::material_map.erase(id_);
id_ = -1;
}
// Make sure no other material has same ID
if (model::material_map.find(id) != model::material_map.end()) {
throw std::runtime_error{"Two materials have the same ID: " + std::to_string(id)};
}
// If no ID specified, auto-assign next ID in sequence
if (id == -1) {
id = 0;
for (const auto& f : model::materials) {
id = std::max(id, f->id_);
}
++id;
}
// Update ID and entry in material map
id_ = id;
model::material_map[id] = index_;
}
void Material::set_density(double density, gsl::cstring_span units)
{
Expects(density >= 0.0);
if (nuclide_.empty()) {
throw std::runtime_error{"No nuclides exist in material yet."};
}
@ -888,6 +920,39 @@ void Material::set_density(double density, gsl::cstring_span units)
}
}
void Material::set_densities(const std::vector<std::string>& name,
const std::vector<double>& density)
{
auto n = name.size();
Expects(n > 0);
Expects(n == density.size());
if (n != nuclide_.size()) {
nuclide_.resize(n);
atom_density_ = xt::zeros<double>({n});
}
double sum_density = 0.0;
for (gsl::index i = 0; i < n; ++i) {
const auto& nuc {name[i]};
if (data::nuclide_map.find(nuc) == data::nuclide_map.end()) {
int err = openmc_load_nuclide(nuc.c_str());
if (err < 0) throw std::runtime_error{openmc_err_msg};
}
nuclide_[i] = data::nuclide_map.at(nuc);
Expects(density[i] > 0.0);
atom_density_(i) = density[i];
sum_density += density[i];
}
// Set total density to the sum of the vector
this->set_density(sum_density, "atom/b-cm");
// Assign S(a,b) tables
this->init_thermal();
}
double Material::volume() const
{
if (volume_ < 0.0) {
@ -969,9 +1034,7 @@ void Material::add_nuclide(const std::string& name, double density)
// If nuclide wasn't found, extend nuclide/density arrays
int err = openmc_load_nuclide(name.c_str());
if (err < 0) {
throw std::runtime_error{openmc_err_msg};
}
if (err < 0) throw std::runtime_error{openmc_err_msg};
// Append new nuclide/density
int i_nuc = data::nuclide_map[name];
@ -1143,19 +1206,6 @@ void read_materials_xml()
model::materials.push_back(std::make_unique<Material>(material_node));
}
model::materials.shrink_to_fit();
// Populate the material map.
for (int i = 0; i < model::materials.size(); i++) {
int32_t mid = model::materials[i]->id_;
auto search = model::material_map.find(mid);
if (search == model::material_map.end()) {
model::material_map[mid] = i;
} else {
std::stringstream err_msg;
err_msg << "Two or more materials use the same unique ID: " << mid;
fatal_error(err_msg);
}
}
}
void free_memory_material()
@ -1199,14 +1249,14 @@ openmc_material_add_nuclide(int32_t index, const char* name, double density)
}
extern "C" int
openmc_material_get_densities(int32_t index, int** nuclides, double** densities, int* n)
openmc_material_get_densities(int32_t index, const int** nuclides, const double** densities, int* n)
{
if (index >= 0 && index < model::materials.size()) {
auto& mat = model::materials[index];
if (!mat->nuclide_.empty()) {
*nuclides = mat->nuclide_.data();
*densities = mat->atom_density_.data();
*n = mat->nuclide_.size();
if (!mat->nuclides().empty()) {
*nuclides = mat->nuclides().data();
*densities = mat->densities().data();
*n = mat->nuclides().size();
return 0;
} else {
set_errmsg("Material atom density array has not been allocated.");
@ -1235,7 +1285,7 @@ extern "C" int
openmc_material_get_fissionable(int32_t index, bool* fissionable)
{
if (index >= 0 && index < model::materials.size()) {
*fissionable = model::materials[index]->fissionable_;
*fissionable = model::materials[index]->fissionable();
return 0;
} else {
set_errmsg("Index in materials array is out of bounds.");
@ -1247,7 +1297,7 @@ extern "C" int
openmc_material_get_id(int32_t index, int32_t* id)
{
if (index >= 0 && index < model::materials.size()) {
*id = model::materials[index]->id_;
*id = model::materials[index]->id();
return 0;
} else {
set_errmsg("Index in materials array is out of bounds.");
@ -1293,48 +1343,34 @@ extern "C" int
openmc_material_set_densities(int32_t index, int n, const char** name, const double* density)
{
if (index >= 0 && index < model::materials.size()) {
auto& mat {model::materials[index]};
if (n != mat->nuclide_.size()) {
mat->nuclide_.resize(n);
mat->atom_density_ = xt::zeros<double>({n});
try {
model::materials[index]->set_densities({name, name + n}, {density, density + n});
} catch (const std::exception& e) {
set_errmsg(e.what());
return OPENMC_E_UNASSIGNED;
}
double sum_density = 0.0;
for (int i = 0; i < n; ++i) {
std::string nuc {name[i]};
if (data::nuclide_map.find(nuc) == data::nuclide_map.end()) {
int err = openmc_load_nuclide(nuc.c_str());
if (err < 0) return err;
}
mat->nuclide_[i] = data::nuclide_map[nuc];
mat->atom_density_(i) = density[i];
sum_density += density[i];
}
// Set total density to the sum of the vector
mat->set_density(sum_density, "atom/b-cm");
// Assign S(a,b) tables
mat->init_thermal();
return 0;
} else {
set_errmsg("Index in materials array is out of bounds.");
return OPENMC_E_OUT_OF_BOUNDS;
}
return 0;
}
extern "C" int
openmc_material_set_id(int32_t index, int32_t id)
{
if (index >= 0 && index < model::materials.size()) {
model::materials[index]->id_ = id;
model::material_map[id] = index;
return 0;
try {
model::materials.at(index)->set_id(id);
} catch (const std::exception& e) {
set_errmsg(e.what());
return OPENMC_E_UNASSIGNED;
}
} else {
set_errmsg("Index in materials array is out of bounds.");
return OPENMC_E_OUT_OF_BOUNDS;
}
return 0;
}
extern "C" int

View file

@ -113,7 +113,7 @@ def test_material(capi_init):
m.volume = 10.0
assert m.volume == 10.0
with pytest.raises(exc.InvalidArgumentError):
with pytest.raises(exc.OpenMCError):
m.set_density(1.0, 'goblins')
rho = 2.25e-2