Merge pull request #1249 from liangjg/windows

Compiling OpenMC using Visual Studio
This commit is contained in:
Paul Romano 2019-06-14 22:30:54 -05:00 committed by GitHub
commit b64f17e62e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 113 additions and 80 deletions

View file

@ -72,6 +72,9 @@ endif()
# Set compile/link flags based on which compiler is being used
#===============================================================================
# Skip for Visual Stduio which has its own configurations through GUI
if(NOT MSVC)
if(openmp)
# Requires CMake 3.1+
find_package(OpenMP)
@ -104,6 +107,8 @@ endif()
message(STATUS "OpenMC C++ flags: ${cxxflags}")
message(STATUS "OpenMC Linker flags: ${ldflags}")
endif()
#===============================================================================
# pugixml library
#===============================================================================
@ -159,7 +164,7 @@ target_compile_options(faddeeva PRIVATE ${cxxflags})
# libopenmc
#===============================================================================
add_library(libopenmc SHARED
list(APPEND libopenmc_SOURCES
src/bank.cpp
src/bremsstrahlung.cpp
src/dagmc.cpp
@ -246,6 +251,18 @@ add_library(libopenmc SHARED
src/xml_interface.cpp
src/xsdata.cpp)
# For Visual Studio compilers
if(MSVC)
# Use static library (otherwise explicit symbol portings are needed)
add_library(libopenmc STATIC ${libopenmc_SOURCES})
# To use the shared HDF5 libraries on Windows, the H5_BUILT_AS_DYNAMIC_LIB
# compile definition must be specified.
target_compile_definitions(libopenmc PRIVATE -DH5_BUILT_AS_DYNAMIC_LIB)
else()
add_library(libopenmc SHARED ${libopenmc_SOURCES})
endif()
set_target_properties(libopenmc PROPERTIES
OUTPUT_NAME openmc)

View file

@ -44,7 +44,7 @@ void fatal_error(const std::stringstream& message)
[[noreturn]] inline
void fatal_error(const char* message)
{
fatal_error({message, std::strlen(message)});
fatal_error(std::string{message, std::strlen(message)});
}
void warning(const std::string& message);

View file

@ -187,11 +187,12 @@ read_attribute(hid_t obj_id, const char* name, std::string& str)
{
// Create buffer to read data into
auto n = attribute_typesize(obj_id, name);
char buffer[n];
char* buffer = new char[n];
// Read attribute and set string
read_attr_string(obj_id, name, n, buffer);
str = std::string{buffer, n};
delete[] buffer;
}
// overload for std::vector<std::string>
@ -203,20 +204,21 @@ read_attribute(hid_t obj_id, const char* name, std::vector<std::string>& vec)
// Allocate a C char array to get strings
auto n = attribute_typesize(obj_id, name);
char buffer[m][n];
char* buffer = new char[m*n];
// Read char data in attribute
read_attr_string(obj_id, name, n, buffer[0]);
read_attr_string(obj_id, name, n, buffer);
for (int i = 0; i < m; ++i) {
// Determine proper length of string -- strlen doesn't work because
// buffer[i] might not have any null characters
std::size_t k = 0;
for (; k < n; ++k) if (buffer[i][k] == '\0') break;
for (; k < n; ++k) if (buffer[i*n + k] == '\0') break;
// Create string based on (char*, size_t) constructor
vec.emplace_back(&buffer[i][0], k);
vec.emplace_back(&buffer[i*n], k);
}
delete[] buffer;
}
//==============================================================================
@ -240,7 +242,7 @@ read_dataset(hid_t obj_id, const char* name, std::string& str, bool indep=false)
{
// Create buffer to read data into
auto n = dataset_typesize(obj_id, name);
char buffer[n];
char* buffer = new char[n];
// Read attribute and set string
read_string(obj_id, name, n, buffer, indep);
@ -458,14 +460,17 @@ write_dataset(hid_t obj_id, const char* name, const std::vector<std::string>& bu
}
// Copy data into contiguous buffer
char temp[n][m];
std::fill(temp[0], temp[0] + n*m, '\0');
char* temp = new char[n*m];
std::fill(temp, temp + n*m, '\0');
for (int i = 0; i < n; ++i) {
std::copy(buffer[i].begin(), buffer[i].end(), temp[i]);
std::copy(buffer[i].begin(), buffer[i].end(), temp + i*m);
}
// Write 2D data
write_string(obj_id, 1, dims, m, name, temp[0], false);
write_string(obj_id, 1, dims, m, name, temp, false);
// Free temp array
delete[] temp;
}
template<typename T> inline void

View file

@ -4,6 +4,7 @@
#include <sstream>
#include <set>
#include <string>
#include <cctype>
#include "openmc/capi.h"
#include "openmc/constants.h"
@ -560,7 +561,7 @@ CSGCell::contains_complex(Position r, Direction u, int32_t on_surface) const
{
// Make a stack of booleans. We don't know how big it needs to be, but we do
// know that rpn.size() is an upper-bound.
bool stack[rpn_.size()];
std::vector<bool> stack(rpn_.size());
int i_stack = -1;
for (int32_t token : rpn_) {

View file

@ -402,6 +402,10 @@ void read_ce_cross_sections_xml()
// If no directory is listed in cross_sections.xml, by default select the
// directory in which the cross_sections.xml file resides
auto pos = filename.rfind("/");
if (pos == std::string::npos) {
// no '/' found, probably a Windows directory
pos = filename.rfind("\\");
}
directory = filename.substr(0, pos);
}

View file

@ -118,7 +118,7 @@ int openmc_finalize()
data::energy_max = {INFTY, INFTY};
data::energy_min = {0.0, 0.0};
model::root_universe = -1;
openmc_set_seed(DEFAULT_SEED);
openmc::openmc_set_seed(DEFAULT_SEED);
// Deallocate arrays
free_memory();
@ -160,6 +160,6 @@ int openmc_hard_reset()
simulation::total_gen = 0;
// Reset the random number generator state
openmc_set_seed(DEFAULT_SEED);
openmc::openmc_set_seed(DEFAULT_SEED);
return 0;
}

View file

@ -366,10 +366,11 @@ member_names(hid_t group_id, H5O_type_t type)
i, nullptr, 0, H5P_DEFAULT);
// Read name
char buffer[size];
char* buffer = new char[size];
H5Lget_name_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i,
buffer, size, H5P_DEFAULT);
names.emplace_back(&buffer[0]);
delete[] buffer;
}
return names;
}
@ -404,11 +405,13 @@ object_name(hid_t obj_id)
{
// Determine size and create buffer
size_t size = 1 + H5Iget_name(obj_id, nullptr, 0);
char buffer[size];
char* buffer = new char[size];
// Read and return name
H5Iget_name(obj_id, buffer, size);
return buffer;
std::string str = buffer;
delete[] buffer;
return str;
}
@ -789,6 +792,8 @@ const hid_t H5TypeMap<int>::type_id = H5T_NATIVE_INT;
template<>
const hid_t H5TypeMap<unsigned long>::type_id = H5T_NATIVE_ULONG;
template<>
const hid_t H5TypeMap<unsigned long long>::type_id = H5T_NATIVE_ULLONG;
template<>
const hid_t H5TypeMap<unsigned int>::type_id = H5T_NATIVE_UINT;
template<>
const hid_t H5TypeMap<int64_t>::type_id = H5T_NATIVE_INT64;

View file

@ -68,7 +68,7 @@ int openmc_init(int argc, char* argv[], const void* intracomm)
// Initialize random number generator -- if the user specifies a seed, it
// will be re-initialized later
openmc_set_seed(DEFAULT_SEED);
openmc::openmc_set_seed(DEFAULT_SEED);
// Read XML input files
read_input_xml();

View file

@ -388,7 +388,7 @@ RectLattice::to_hdf5_inner(hid_t lat_group) const
hsize_t nx {static_cast<hsize_t>(n_cells_[0])};
hsize_t ny {static_cast<hsize_t>(n_cells_[1])};
hsize_t nz {static_cast<hsize_t>(n_cells_[2])};
int out[nx*ny*nz];
std::vector<int> out(nx*ny*nz);
for (int m = 0; m < nz; m++) {
for (int k = 0; k < ny; k++) {
@ -401,12 +401,12 @@ RectLattice::to_hdf5_inner(hid_t lat_group) const
}
hsize_t dims[3] {nz, ny, nx};
write_int(lat_group, 3, dims, "universes", out, false);
write_int(lat_group, 3, dims, "universes", out.data(), false);
} else {
hsize_t nx {static_cast<hsize_t>(n_cells_[0])};
hsize_t ny {static_cast<hsize_t>(n_cells_[1])};
int out[nx*ny];
std::vector<int> out(nx*ny);
for (int k = 0; k < ny; k++) {
for (int j = 0; j < nx; j++) {
@ -417,7 +417,7 @@ RectLattice::to_hdf5_inner(hid_t lat_group) const
}
hsize_t dims[3] {1, ny, nx};
write_int(lat_group, 3, dims, "universes", out, false);
write_int(lat_group, 3, dims, "universes", out.data(), false);
}
}
@ -877,7 +877,7 @@ HexLattice::to_hdf5_inner(hid_t lat_group) const
hsize_t nx {static_cast<hsize_t>(2*n_rings_ - 1)};
hsize_t ny {static_cast<hsize_t>(2*n_rings_ - 1)};
hsize_t nz {static_cast<hsize_t>(n_axial_)};
int out[nx*ny*nz];
std::vector<int> out(nx*ny*nz);
for (int m = 0; m < nz; m++) {
for (int k = 0; k < ny; k++) {
@ -897,7 +897,7 @@ HexLattice::to_hdf5_inner(hid_t lat_group) const
}
hsize_t dims[3] {nz, ny, nx};
write_int(lat_group, 3, dims, "universes", out, false);
write_int(lat_group, 3, dims, "universes", out.data(), false);
}
//==============================================================================

View file

@ -110,12 +110,13 @@ void calc_pn_c(int n, double x, double pnx[])
double evaluate_legendre(int n, const double data[], double x)
{
double pnx[n + 1];
double* pnx = new double[n + 1];
double val = 0.0;
calc_pn_c(n, x, pnx);
for (int l = 0; l <= n; l++) {
val += (l + 0.5) * data[l] * pnx[l];
}
delete[] pnx;
return val;
}
@ -536,8 +537,8 @@ void calc_zn(int n, double rho, double phi, double zn[]) {
double sin_phi = std::sin(phi);
double cos_phi = std::cos(phi);
double sin_phi_vec[n + 1]; // Sin[n * phi]
double cos_phi_vec[n + 1]; // Cos[n * phi]
std::vector<double> sin_phi_vec(n + 1); // Sin[n * phi]
std::vector<double> cos_phi_vec(n + 1); // Cos[n * phi]
sin_phi_vec[0] = 1.0;
cos_phi_vec[0] = 1.0;
sin_phi_vec[1] = 2.0 * cos_phi;
@ -554,8 +555,8 @@ void calc_zn(int n, double rho, double phi, double zn[]) {
// ===========================================================================
// Calculate R_pq(rho)
double zn_mat[n + 1][n + 1]; // Matrix forms of the coefficients which are
// easier to work with
// Matrix forms of the coefficients which are easier to work with
std::vector<std::vector<double>> zn_mat(n + 1, std::vector<double>(n + 1));
// Fill the main diagonal first (Eq 3.9 in Chong)
for (int p = 0; p <= n; p++) {
@ -763,7 +764,7 @@ void broaden_wmp_polynomials(double E, double dopp, int n, double factors[])
void spline(int n, const double x[], const double y[], double z[])
{
double c_new[n-1];
std::vector<double> c_new(n-1);
// Set natural boundary conditions
c_new[0] = 0.0;

View file

@ -179,13 +179,13 @@ int RegularMesh::get_bin(Position r) const
}
// Determine indices
int ijk[n_dimension_];
std::vector<int> ijk(n_dimension_);
bool in_mesh;
get_indices(r, ijk, &in_mesh);
get_indices(r, ijk.data(), &in_mesh);
if (!in_mesh) return -1;
// Convert indices to bin
return get_bin_from_indices(ijk);
return get_bin_from_indices(ijk.data());
}
int RegularMesh::get_bin_from_indices(const int* ijk) const
@ -505,11 +505,11 @@ void RegularMesh::bins_crossed(const Particle* p, std::vector<int>& bins,
// Determine the mesh indices for the starting and ending coords.
int n = n_dimension_;
int ijk0[n], ijk1[n];
std::vector<int> ijk0(n), ijk1(n);
bool start_in_mesh;
get_indices(r0, ijk0, &start_in_mesh);
get_indices(r0, ijk0.data(), &start_in_mesh);
bool end_in_mesh;
get_indices(r1, ijk1, &end_in_mesh);
get_indices(r1, ijk1.data(), &end_in_mesh);
// Reset coordinates and check for a mesh intersection if necessary.
if (start_in_mesh) {
@ -519,7 +519,7 @@ void RegularMesh::bins_crossed(const Particle* p, std::vector<int>& bins,
// The initial coords do not lie in the mesh. Check to see if the particle
// eventually intersects the mesh and compute the relevant coords and
// indices.
if (!intersects(r0, r1, ijk0)) return;
if (!intersects(r0, r1, ijk0.data())) return;
}
r1 = r;
@ -527,17 +527,17 @@ void RegularMesh::bins_crossed(const Particle* p, std::vector<int>& bins,
// Find which mesh cells are traversed and the length of each traversal.
while (true) {
if (std::equal(ijk0, ijk0+n, ijk1)) {
if (ijk0 == ijk1) {
// The track ends in this cell. Use the particle end location rather
// than the mesh surface and stop iterating.
double distance = (r1 - r0).norm();
bins.push_back(get_bin_from_indices(ijk0));
bins.push_back(get_bin_from_indices(ijk0.data()));
lengths.push_back(distance / total_distance);
break;
}
// The track exits this cell. Determine the distance to each mesh surface.
double d[n];
std::vector<double> d(n);
for (int k = 0; k < n; ++k) {
if (std::fabs(u[k]) < FP_PRECISION) {
d[k] = INFTY;
@ -551,9 +551,9 @@ void RegularMesh::bins_crossed(const Particle* p, std::vector<int>& bins,
}
// Pick the closest mesh surface and append this traversal to the output.
auto j = std::min_element(d, d+n) - d;
auto j = std::min_element(d.begin(), d.end()) - d.begin();
double distance = d[j];
bins.push_back(get_bin_from_indices(ijk0));
bins.push_back(get_bin_from_indices(ijk0.data()));
lengths.push_back(distance / total_distance);
// Translate to the oncoming mesh surface.
@ -592,18 +592,17 @@ void RegularMesh::surface_bins_crossed(const Particle* p,
// Determine indices for starting and ending location.
int n = n_dimension_;
int ijk0[n], ijk1[n];
std::vector<int> ijk0(n), ijk1(n);
bool start_in_mesh;
get_indices(r0, ijk0, &start_in_mesh);
get_indices(r0, ijk0.data(), &start_in_mesh);
bool end_in_mesh;
get_indices(r1, ijk1, &end_in_mesh);
get_indices(r1, ijk1.data(), &end_in_mesh);
// Check if the track intersects any part of the mesh.
if (!start_in_mesh) {
Position r0_copy = r0;
int ijk0_copy[n];
for (int i = 0; i < n; ++i) ijk0_copy[i] = ijk0[i];
if (!intersects(r0_copy, r1, ijk0_copy)) return;
std::vector<int> ijk0_copy(ijk0);
if (!intersects(r0_copy, r1, ijk0_copy.data())) return;
}
// ========================================================================
@ -661,7 +660,7 @@ void RegularMesh::surface_bins_crossed(const Particle* p,
// Outward current on i max surface
if (in_mesh) {
int i_surf = 4*i + 3;
int i_mesh = get_bin_from_indices(ijk0);
int i_mesh = get_bin_from_indices(ijk0.data());
int i_bin = 4*n*i_mesh + i_surf - 1;
bins.push_back(i_bin);
@ -682,7 +681,7 @@ void RegularMesh::surface_bins_crossed(const Particle* p,
// i min surface
if (in_mesh) {
int i_surf = 4*i + 2;
int i_mesh = get_bin_from_indices(ijk0);
int i_mesh = get_bin_from_indices(ijk0.data());
int i_bin = 4*n*i_mesh + i_surf - 1;
bins.push_back(i_bin);
@ -694,7 +693,7 @@ void RegularMesh::surface_bins_crossed(const Particle* p,
// Outward current on i min surface
if (in_mesh) {
int i_surf = 4*i + 1;
int i_mesh = get_bin_from_indices(ijk0);
int i_mesh = get_bin_from_indices(ijk0.data());
int i_bin = 4*n*i_mesh + i_surf - 1;
bins.push_back(i_bin);
@ -715,7 +714,7 @@ void RegularMesh::surface_bins_crossed(const Particle* p,
// i max surface
if (in_mesh) {
int i_surf = 4*i + 4;
int i_mesh = get_bin_from_indices(ijk0);
int i_mesh = get_bin_from_indices(ijk0.data());
int i_bin = 4*n*i_mesh + i_surf - 1;
bins.push_back(i_bin);

View file

@ -96,7 +96,7 @@ Mgxs::metadata_from_hdf5(hid_t xs_id, const std::vector<double>& temperature,
// Determine the available temperatures
hid_t kT_group = open_group(xs_id, "kTs");
int num_temps = get_num_datasets(kT_group);
char* dset_names[num_temps];
char** dset_names = new char*[num_temps];
for (int i = 0; i < num_temps; i++) {
dset_names[i] = new char[151];
}
@ -111,6 +111,7 @@ Mgxs::metadata_from_hdf5(hid_t xs_id, const std::vector<double>& temperature,
// Done with dset_names, so delete it
delete[] dset_names[i];
}
delete[] dset_names;
std::sort(available_temps.begin(), available_temps.end());
// If only one temperature is available, lets just use nearest temperature

View file

@ -297,7 +297,7 @@ template<int i> double
axis_aligned_plane_distance(Position r, Direction u, bool coincident, double offset)
{
const double f = offset - r[i];
if (coincident or std::abs(f) < FP_COINCIDENT or u[i] == 0.0) return INFTY;
if (coincident || std::abs(f) < FP_COINCIDENT || u[i] == 0.0) return INFTY;
const double d = f / u[i];
if (d < 0.0) return INFTY;
return d;
@ -493,7 +493,7 @@ SurfacePlane::distance(Position r, Direction u, bool coincident) const
{
const double f = A_*r.x + B_*r.y + C_*r.z - D_;
const double projection = A_*u.x + B_*u.y + C_*u.z;
if (coincident or std::abs(f) < FP_COINCIDENT or projection == 0.0) {
if (coincident || std::abs(f) < FP_COINCIDENT || projection == 0.0) {
return INFTY;
} else {
const double d = -f / projection;
@ -573,7 +573,7 @@ axis_aligned_cylinder_distance(Position r, Direction u,
// No intersection with cylinder.
return INFTY;
} else if (coincident or std::abs(c) < FP_COINCIDENT) {
} else if (coincident || std::abs(c) < FP_COINCIDENT) {
// Particle is on the cylinder, thus one distance is positive/negative
// and the other is zero. The sign of k determines if we are facing in or
// out.
@ -743,7 +743,7 @@ double SurfaceSphere::distance(Position r, Direction u, bool coincident) const
// No intersection with sphere.
return INFTY;
} else if (coincident or std::abs(c) < FP_COINCIDENT) {
} else if (coincident || std::abs(c) < FP_COINCIDENT) {
// Particle is on the sphere, thus one distance is positive/negative and
// the other is zero. The sign of k determines if we are facing in or out.
if (k >= 0.0) {
@ -820,7 +820,7 @@ axis_aligned_cone_distance(Position r, Direction u,
// No intersection with cone.
return INFTY;
} else if (coincident or std::abs(c) < FP_COINCIDENT) {
} else if (coincident || std::abs(c) < FP_COINCIDENT) {
// Particle is on the cone, thus one distance is positive/negative
// and the other is zero. The sign of k determines if we are facing in or
// out.
@ -1008,7 +1008,7 @@ SurfaceQuadric::distance(Position r, Direction ang, bool coincident) const
// No intersection with surface.
return INFTY;
} else if (coincident or std::abs(c) < FP_COINCIDENT) {
} else if (coincident || std::abs(c) < FP_COINCIDENT) {
// Particle is on the surface, thus one distance is positive/negative and
// the other is zero. The sign of k determines which distance is zero and
// which is not.
@ -1155,27 +1155,27 @@ void read_surfaces(pugi::xml_node node)
// See if this surface makes part of the global bounding box.
BoundingBox bb = surf->bounding_box();
if (bb.xmin > -INFTY and bb.xmin < xmin) {
if (bb.xmin > -INFTY && bb.xmin < xmin) {
xmin = bb.xmin;
i_xmin = i_surf;
}
if (bb.xmax < INFTY and bb.xmax > xmax) {
if (bb.xmax < INFTY && bb.xmax > xmax) {
xmax = bb.xmax;
i_xmax = i_surf;
}
if (bb.ymin > -INFTY and bb.ymin < ymin) {
if (bb.ymin > -INFTY && bb.ymin < ymin) {
ymin = bb.ymin;
i_ymin = i_surf;
}
if (bb.ymax < INFTY and bb.ymax > ymax) {
if (bb.ymax < INFTY && bb.ymax > ymax) {
ymax = bb.ymax;
i_ymax = i_surf;
}
if (bb.zmin > -INFTY and bb.zmin < zmin) {
if (bb.zmin > -INFTY && bb.zmin < zmin) {
zmin = bb.zmin;
i_zmin = i_surf;
}
if (bb.zmax < INFTY and bb.zmax > zmax) {
if (bb.zmax < INFTY && bb.zmax > zmax) {
zmax = bb.zmax;
i_zmax = i_surf;
}

View file

@ -18,8 +18,8 @@ void
LegendreFilter::get_all_bins(const Particle* p, int estimator,
FilterMatch& match) const
{
double wgt[n_bins_];
calc_pn_c(order_, p->mu_, wgt);
std::vector<double> wgt(n_bins_);
calc_pn_c(order_, p->mu_, wgt.data());
for (int i = 0; i < n_bins_; i++) {
match.bins_.push_back(i);
match.weights_.push_back(wgt[i]);

View file

@ -58,8 +58,8 @@ MeshFilter::text_label(int bin) const
auto& mesh = *model::meshes[mesh_];
int n_dim = mesh.n_dimension_;
int ijk[n_dim];
mesh.get_indices_from_bin(bin, ijk);
std::vector<int> ijk(n_dim);
mesh.get_indices_from_bin(bin, ijk.data());
std::stringstream out;
out << "Mesh Index (" << ijk[0];

View file

@ -35,16 +35,16 @@ SphericalHarmonicsFilter::get_all_bins(const Particle* p, int estimator,
FilterMatch& match) const
{
// Determine cosine term for scatter expansion if necessary
double wgt[order_ + 1];
std::vector<double> wgt(order_ + 1);
if (cosine_ == SphericalHarmonicsCosine::scatter) {
calc_pn_c(order_, p->mu_, wgt);
calc_pn_c(order_, p->mu_, wgt.data());
} else {
for (int i = 0; i < order_ + 1; i++) wgt[i] = 1;
}
// Find the Rn,m values
double rn[n_bins_];
calc_rn(order_, p->u_last_, rn);
std::vector<double> rn(n_bins_);
calc_rn(order_, p->u_last_, rn.data());
int j = 0;
for (int n = 0; n < order_ + 1; n++) {

View file

@ -50,8 +50,8 @@ SpatialLegendreFilter::get_all_bins(const Particle* p, int estimator,
double x_norm = 2.0*(x - min_) / (max_ - min_) - 1.0;
// Compute and return the Legendre weights.
double wgt[order_ + 1];
calc_pn_c(order_, x_norm, wgt);
std::vector<double> wgt(order_ + 1);
calc_pn_c(order_, x_norm, wgt.data());
for (int i = 0; i < order_ + 1; i++) {
match.bins_.push_back(i);
match.weights_.push_back(wgt[i]);

View file

@ -36,8 +36,8 @@ ZernikeFilter::get_all_bins(const Particle* p, int estimator,
if (r <= 1.0) {
// Compute and return the Zernike weights.
double zn[n_bins_];
calc_zn(order_, r, theta, zn);
std::vector<double> zn(n_bins_);
calc_zn(order_, r, theta, zn.data());
for (int i = 0; i < n_bins_; i++) {
match.bins_.push_back(i);
match.weights_.push_back(zn[i]);
@ -93,8 +93,8 @@ ZernikeRadialFilter::get_all_bins(const Particle* p, int estimator,
if (r <= 1.0) {
// Compute and return the Zernike weights.
double zn[n_bins_];
calc_zn_rad(order_, r, zn);
std::vector<double> zn(n_bins_);
calc_zn_rad(order_, r, zn.data());
for (int i = 0; i < n_bins_; i++) {
match.bins_.push_back(i);
match.weights_.push_back(zn[i]);