Skip to content

Commit d312218

Browse files
authored
Merge pull request #110 from jcurtis2/add_histograms
add histograms
2 parents 3eae84a + b8f5077 commit d312218

File tree

4 files changed

+205
-3
lines changed

4 files changed

+205
-3
lines changed

src/bin_grid.F90

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,51 @@ subroutine f_bin_grid_centers(ptr_c, arr_data, arr_size) bind(C)
7070
arr_data = bin_grid%centers
7171
end subroutine
7272

73+
subroutine f_bin_grid_histogram_1d(x_bin_grid_ptr_c, x_data, weight_data, &
74+
arr_size, output_data, bin_grid_size) bind(C)
75+
76+
type(c_ptr), intent(in) :: x_bin_grid_ptr_c
77+
type(bin_grid_t), pointer :: bin_grid => null()
78+
integer(c_int), intent(in) :: arr_size
79+
integer(c_int), intent(in) :: bin_grid_size
80+
real(c_double), dimension(bin_grid_size) :: output_data
81+
real(c_double), dimension(arr_size) :: x_data
82+
real(c_double), dimension(arr_size) :: weight_data
83+
84+
call c_f_pointer(x_bin_grid_ptr_c, bin_grid)
85+
output_data = bin_grid_histogram_1d(bin_grid, x_data, weight_data)
86+
87+
end subroutine
88+
89+
subroutine f_bin_grid_histogram_2d(x_bin_grid_ptr_c, x_data, &
90+
y_bin_grid_ptr_c, y_data, weight_data, &
91+
arr_size, output_data, x_bin_grid_size, y_bin_grid_size) bind(C)
92+
93+
type(c_ptr), intent(in) :: x_bin_grid_ptr_c
94+
type(c_ptr), intent(in) :: y_bin_grid_ptr_c
95+
type(bin_grid_t), pointer :: x_bin_grid => null()
96+
type(bin_grid_t), pointer :: y_bin_grid => null()
97+
integer(c_int), intent(in) :: arr_size
98+
integer(c_int), intent(in) :: x_bin_grid_size
99+
integer(c_int), intent(in) :: y_bin_grid_size
100+
real(c_double), dimension(x_bin_grid_size*y_bin_grid_size), intent(out) :: output_data
101+
real(c_double), dimension(arr_size), intent(in) :: x_data, y_data
102+
real(c_double), dimension(arr_size), intent(in) :: weight_data
103+
real(c_double), allocatable :: output_data_local(:,:)
104+
integer :: i, j
105+
106+
call c_f_pointer(x_bin_grid_ptr_c, x_bin_grid)
107+
call c_f_pointer(y_bin_grid_ptr_c, y_bin_grid)
108+
allocate(output_data_local(x_bin_grid_size,y_bin_grid_size))
109+
output_data_local = bin_grid_histogram_2d(x_bin_grid, x_data, y_bin_grid, &
110+
y_data, weight_data)
111+
112+
do i = 1,x_bin_grid_size
113+
do j = 1,y_bin_grid_size
114+
output_data((i-1)*y_bin_grid_size + j) = output_data_local(i,j)
115+
end do
116+
end do
117+
118+
end subroutine
119+
73120
end module

src/bin_grid.hpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ extern "C" void f_bin_grid_init(
2121
extern "C" void f_bin_grid_size(const void *ptr, int *val) noexcept;
2222
extern "C" void f_bin_grid_edges(const void *ptr, void *arr_data, const int *arr_size) noexcept;
2323
extern "C" void f_bin_grid_centers(const void *ptr, void *arr_data, const int *arr_size) noexcept;
24+
extern "C" void f_bin_grid_histogram_1d(const void *x_bin_grid_ptr_c, const void *x_data,
25+
const void *weight_data, const int *arr_size, void *output_data, const int *grid_size) noexcept;
26+
extern "C" void f_bin_grid_histogram_2d(const void *x_bin_grid_ptr_c, const void *x_data,
27+
const void *y_bin_grid_ptr_c, const void *y_data,
28+
const void *weight_data, const int *arr_size, void *output_data, const int *x_grid_size,
29+
const int *y_grid_size) noexcept;
2430

2531
struct BinGrid {
2632
PMCResource ptr;
@@ -61,3 +67,48 @@ struct BinGrid {
6167
return data;
6268
}
6369
};
70+
71+
static std::valarray<double> histogram_1d(
72+
const BinGrid &bin_grid,
73+
std::valarray<double> values,
74+
std::valarray<double> weights){
75+
76+
int len;
77+
f_bin_grid_size(&bin_grid.ptr, &len);
78+
int data_size = values.size();
79+
std::valarray<double> data(len);
80+
f_bin_grid_histogram_1d(&bin_grid.ptr, begin(values), begin(weights),
81+
&data_size, begin(data), &len);
82+
83+
return data;
84+
}
85+
86+
static std::vector<std::vector<double>> histogram_2d(
87+
const BinGrid &x_bin_grid,
88+
std::valarray<double> x_values,
89+
const BinGrid &y_bin_grid,
90+
std::valarray<double> y_values,
91+
std::valarray<double> weights){
92+
93+
int x_len;
94+
f_bin_grid_size(&x_bin_grid.ptr, &x_len);
95+
int y_len;
96+
f_bin_grid_size(&y_bin_grid.ptr, &y_len);
97+
int data_size = x_values.size();
98+
99+
std::vector<std::vector<double>> data( x_len , std::vector<double>(y_len,0));
100+
std::valarray<double> data_flat(x_len*y_len);
101+
f_bin_grid_histogram_2d(&x_bin_grid.ptr, begin(x_values),
102+
&y_bin_grid.ptr, begin(y_values), begin(weights),
103+
&data_size, begin(data_flat), &x_len, &y_len);
104+
105+
for(int i = 0; i < x_len; i++)
106+
{
107+
for(int j = 0; j < y_len; j++)
108+
{
109+
data[i][j] = data_flat[i*y_len + j];
110+
}
111+
}
112+
113+
return data;
114+
}

src/pypartmc.cpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,20 @@ PYBIND11_MODULE(_PyPartMC, m) {
176176
py::class_<BinGrid>(m,"BinGrid")
177177
.def(py::init<const double, const py::str, const double, const double>())
178178
.def("__len__", BinGrid::__len__)
179-
.def_property_readonly("edges", BinGrid::edges)
180-
.def_property_readonly("centers", BinGrid::centers)
179+
.def_property_readonly("edges", BinGrid::edges, "Bin edges")
180+
.def_property_readonly("centers", BinGrid::centers, "Bin centers")
181181
;
182+
183+
m.def(
184+
"histogram_1d", &histogram_1d, py::return_value_policy::copy,
185+
"Return a 1D histogram with of the given weighted data, scaled by the bin sizes."
186+
);
187+
188+
m.def(
189+
"histogram_2d", &histogram_2d, py::return_value_policy::copy,
190+
"Return a 2D histogram with of the given weighted data, scaled by the bin sizes."
191+
);
192+
182193
// TODO #120: auto util = m.def_submodule("util", "...");
183194
m.def(
184195
"pow2_above", &pow2_above, py::return_value_policy::copy,
@@ -199,6 +210,8 @@ PYBIND11_MODULE(_PyPartMC, m) {
199210
"Scenario",
200211
"condense_equilib_particles",
201212
"run_part",
202-
"pow2_above"
213+
"pow2_above",
214+
"histogram_1d",
215+
"histogram_2d"
203216
);
204217
}

tests/test_bin_grid.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,94 @@ def test_invalid_grid():
9797
_ = ppmc.BinGrid(grid_size, "X", 1, 100)
9898
except ValueError as error:
9999
assert str(error) == "Invalid grid spacing."
100+
101+
@staticmethod
102+
def test_histogram_1d():
103+
# arrange
104+
n_data = 1000
105+
grid = ppmc.BinGrid(100,"linear",0,1000)
106+
vals = np.random.random(n_data)*1000
107+
weights = np.ones(n_data)
108+
hist, bin_edges = np.histogram(vals, bins=grid.edges)
109+
110+
# act
111+
data = ppmc.histogram_1d(grid, vals, weights)
112+
113+
# assert
114+
np.testing.assert_array_almost_equal(data, hist/(bin_edges[1]-bin_edges[0]))
115+
116+
@staticmethod
117+
def test_histogram_2d_linear_linear():
118+
# arrange
119+
n_data = 1000
120+
x_grid = ppmc.BinGrid(15, "linear", 0, 1000)
121+
y_grid = ppmc.BinGrid(12, "linear", 0, 500)
122+
x_vals = np.random.random(n_data)*1000
123+
y_vals = np.random.random(n_data)*500
124+
weights = np.random.random(n_data)
125+
data_numpy, bin_edges_x, bin_edges_y = np.histogram2d(
126+
x_vals, y_vals,
127+
bins=[x_grid.edges, y_grid.edges],
128+
weights=weights
129+
)
130+
cell_size = (bin_edges_x[1]-bin_edges_x[0]) * (bin_edges_y[1]-bin_edges_y[0])
131+
132+
# act
133+
data = ppmc.histogram_2d(x_grid, x_vals, y_grid, y_vals, weights)
134+
135+
# assert
136+
np.testing.assert_array_almost_equal(np.array(data), data_numpy/cell_size, decimal=15)
137+
138+
@staticmethod
139+
def test_histogram_2d_linear_log():
140+
# arrange
141+
n_data = 100
142+
y_data_min = .1
143+
y_data_max = 10.
144+
x_grid = ppmc.BinGrid(15,"linear",0,1000)
145+
y_grid = ppmc.BinGrid(12,"log",y_data_min,y_data_max)
146+
x_vals = np.random.random(n_data)*1000
147+
y_vals = y_data_min*10**(np.log10(y_data_max/y_data_min) * np.random.random(n_data))
148+
weights = np.random.random(n_data)
149+
data_numpy, bin_edges_x, bin_edges_y = np.histogram2d(
150+
x_vals, y_vals,
151+
bins=[x_grid.edges, y_grid.edges],
152+
weights=weights
153+
)
154+
cell_size = (bin_edges_x[1]-bin_edges_x[0]) * np.log(bin_edges_y[1]/bin_edges_y[0])
155+
156+
# act
157+
data = ppmc.histogram_2d(x_grid, x_vals, y_grid, y_vals, weights)
158+
159+
# assert
160+
np.testing.assert_array_almost_equal(np.array(data), data_numpy/cell_size, decimal=15)
161+
162+
@staticmethod
163+
def test_histogram_2d_log_log():
164+
# arrange
165+
n_data = 100
166+
x_data_min = 1
167+
x_data_max = 1000
168+
y_data_min = .1
169+
y_data_max = 10.
170+
x_grid = ppmc.BinGrid(15,"log", x_data_min, x_data_max)
171+
y_grid = ppmc.BinGrid(12,"log", y_data_min, y_data_max)
172+
x_vals = x_data_min * 10**(np.log10(x_data_max / x_data_min) * np.random.random(n_data))
173+
y_vals = y_data_min * 10**(np.log10(y_data_max / y_data_min) * np.random.random(n_data))
174+
weights = np.random.random(n_data)
175+
data_numpy, bin_edges_x, bin_edges_y = np.histogram2d(
176+
x_vals, y_vals,
177+
bins=[x_grid.edges, y_grid.edges],
178+
weights=weights
179+
)
180+
cell_size = np.log(
181+
bin_edges_x[1] / bin_edges_x[0]
182+
) * np.log(
183+
bin_edges_y[1] / bin_edges_y[0]
184+
)
185+
186+
# act
187+
data = ppmc.histogram_2d(x_grid, x_vals, y_grid, y_vals, weights)
188+
189+
# assert
190+
np.testing.assert_array_almost_equal(np.array(data), data_numpy/cell_size, decimal=13)

0 commit comments

Comments
 (0)