Commit 6c54ce72 authored by Roel Aaij's avatar Roel Aaij
Browse files

Fix generation of values and coincidences. Tests work.

parent c9b7f6f5
......@@ -113,7 +113,6 @@ endif()
set(generate_SOURCES
src/generate/generate.cpp
src/generate/storage.cpp
src/generate/generate_scalar.cpp)
if (USE_AVX2)
......
......@@ -74,14 +74,17 @@ public:
return 1.0e9f / m_rates[0];
}
size_t n_expect(const long interval) const {
float total_rate = std::accumulate(begin(m_rates), end(m_rates), 0.f);
size_t n_expect(const long interval, bool include_l0) const {
using namespace Constants;
float l0_rate = include_l0 ? m_rates[0] : 0.f;
float l1_rate = std::accumulate(std::next(begin(m_rates)), end(m_rates), 0.f);
// Fudge with 30% extra space to avoid reallocating
double n_per_pmt = 1.3 * total_rate * (double)(interval) / 1e9;
if (n_per_pmt > std::numeric_limits<float>::max()) {
double n_per_pmt = 1.3 * (l0_rate + 3.f * l1_rate) * (double)(interval) / 1e9;
auto n_pmts = n_dom * n_mod * n_pmt;
if (n_per_pmt > std::numeric_limits<float>::max() / n_pmts) {
throw std::domain_error{"rate of " + std::to_string(n_per_pmt) + " is too large"};
}
return std::lround(Constants::n_dom * Constants::n_mod * (float)n_per_pmt);
return std::lround(n_pmts * (float)n_per_pmt);
}
};
......@@ -112,8 +115,8 @@ unsigned int random_index(const Container& buffer, const double random) {
}
}
std::tuple<std::array<unsigned int, 4>, size_t>
fill_coincidences(storage_t& times, size_t idx,
std::tuple<size_t, size_t>
fill_coincidences(storage_t& times, pmts_t& pmts, size_t idx,
const long time_start, const long time_end,
Generators& gens);
......
......@@ -15,23 +15,15 @@
*/
#pragma once
#ifdef HAVE_CUDA
#include <thrust/host_vector.h>
#include <thrust/system/cuda/experimental/pinned_allocator.h>
#else
#include <vector>
#endif
#include "aligned_allocator.h"
namespace storage {
extern int n_per_mod;
}
#ifdef HAVE_CUDA
using storage_t = thrust::host_vector<long, thrust::cuda::experimental::pinned_allocator<int>>;
#ifdef USE_AVX2
#include <Vc/Allocator>
using storage_t = std::vector<long, Vc::Allocator<long>>;
using pmts_t = std::vector<int, Vc::Allocator<int>>;
#else
using storage_t = std::vector<long, aligned_allocator<long>>;
using storage_t = std::vector<long>;
using pmts_t = std::vector<int>;
#endif
using queue_t = std::vector<std::tuple<storage_t, storage_t>>;
......@@ -27,7 +27,6 @@
namespace {
using std::cout;
using std::endl;
using std::pair;
using std::vector;
using std::array;
......@@ -75,8 +74,8 @@ float cross_prob(const float ct) {
return std::exp(ct * (Constants::p2 + ct * (Constants::p3 + ct * Constants::p4)));
}
std::tuple<array<unsigned int, 4>, size_t>
fill_coincidences(storage_t& times, size_t idx,
std::tuple<size_t, size_t>
fill_coincidences(storage_t& times, pmts_t& pmts, size_t idx,
const long time_start, const long time_end,
Generators& gen) {
const auto& prob1D = gen.prob1D;
......@@ -85,21 +84,23 @@ fill_coincidences(storage_t& times, size_t idx,
auto& mt = gen.mt;
auto& flat = gen.flat;
array<unsigned int, 4> pmts;
std::fill(begin(pmts), end(pmts), 0);
if (gen.coincidence_rate < 0.001) {
return {pmts, 0};
return {0, 0};
}
pmts.clear();
// Fill coincidences
size_t n = 0;
for (long t1 = time_start ; t1 < time_end; t1 += gen.coincidence(mt)) {
++n;
// generate two-fold coincidence
const unsigned int pmt1 = random_index(prob1D, flat(mt));
const unsigned int pmt2 = random_index(prob2D[pmt1], flat(mt));
pmts[n++] = pmt1;
pmts[n++] = pmt2;
pmts.emplace_back(pmt1);
pmts.emplace_back(pmt2);
std::normal_distribution<double> gauss(t1, 0.5);
times[++idx] = std::lround(gauss(mt));
......@@ -117,28 +118,28 @@ fill_coincidences(storage_t& times, size_t idx,
probND[pmtN] = 0.0;
pmtN = random_index(probND, flat(mt));
pmts[n++] = pmtN;
pmts.emplace_back(pmtN);
times[++idx] = std::lround(gauss(mt));
}
}
}
catch (const std::domain_error&) {}
}
return {pmts, n};
return {pmts.size(), n};
}
std::tuple<storage_t, storage_t> generate(const long start, const long end,
Generators& gens, bool use_avx2) {
#ifdef USE_AVX2
if (use_avx2) {
std::cout << "Generating AVX2" << std::endl;
std::cout << "Generating AVX2\n";
return generate_avx2(start, end, gens);
} else {
std::cout << "Generating scalar" << std::endl;
std::cout << "Generating scalar\n";
return generate_scalar(start, end, gens);
}
#else
std::cout << "Generating scalar" << std::endl;
std::cout << "Generating scalar\n";
return generate_scalar(start, end, gens);
#endif
}
......@@ -15,6 +15,7 @@
*/
#include <cassert>
#include <tuple>
#include <functional>
#include <Vc/Vc>
#include <instrset.h>
......@@ -58,6 +59,45 @@ inline int_v scan_AVX(int_v x) {
return x;
}
auto int_v_to_long_v(const int_v& in) -> pair<long_v, long_v>
{
return {Vc::AvxIntrinsics::cvtepi32_epi64(Vc::AVX::lo128(in.data())),
Vc::AvxIntrinsics::cvtepi32_epi64(Vc::AVX::hi128(in.data()))};
};
void fill_values_avx2(long idx_start, long idx_end, storage_t& values, Ranvec1& random,
int dom, int mod,
std::function<int_v(size_t)> pmt_fun) {
// fill values
size_t n = 0;
const long value_end = idx_end + (2 * long_v::size() - (idx_end % 2 * long_v::size()));
for (long vidx = idx_start; vidx < value_end; vidx += 2 * long_v::size()) {
int_v pmt1 = pmt_fun(n++);
int_v pmt2 = pmt_fun(n++);
auto u1 = float_v{random.random8f()};
auto u2 = float_v{random.random8f()} * Constants::two_pi;
auto fact = sqrt(-2.0f * log(u1));
float_v z0, z1;
sincos(u2, &z0, &z1);
z0 = fact * tot_sigma * z0 + tot_mean;
z1 = fact * tot_sigma * z1 + tot_mean;
auto val0 = simd_cast<int_v>(z0) | (pmt1 << 8) | ((100 * (dom + 1) + mod + 1) << 13);
auto [val0_first, val0_second] = int_v_to_long_v(val0);
val0_first.store(&values[vidx]);
val0_second.store(&values[vidx + long_v::size()]);
auto val1 = simd_cast<int_v>(z1) | (pmt2 << 8) | ((100 * (dom + 1) + mod + 1) << 13);
auto [val1_first, val1_second] = int_v_to_long_v(val1);
val1_first.store(&values[vidx + 2 * long_v::size()]);
val1_second.store(&values[vidx + 3 *long_v::size()]);
}
}
std::tuple<storage_t, storage_t> generate_avx2(const long time_start, const long time_end,
Generators& gens) {
......@@ -67,27 +107,22 @@ std::tuple<storage_t, storage_t> generate_avx2(const long time_start, const long
// Assume times.size() is multiple of 8 here
// assert(storage::n_hits % 16 == 0);
const size_t n_expect = gens.n_expect(time_end - time_start);
const size_t n_expect = gens.n_expect(time_end - time_start, true);
const size_t n_expect_pmts = gens.n_expect(time_end - time_start, false);
const float tau_l0 = gens.tau_l0();
size_t storage_size = n_expect + long_v::size() - n_expect % long_v::size();
storage_t times; times.resize(storage_size);
storage_t values; values.resize(storage_size + 2 * long_v::size());
auto int_v_to_long_v = [] (const int_v& in) -> pair<long_v, long_v>
{
return {Vc::AvxIntrinsics::cvtepi32_epi64(Vc::AVX::lo128(in.data())),
Vc::AvxIntrinsics::cvtepi32_epi64(Vc::AVX::hi128(in.data()))};
};
pmts_t pmts(n_expect_pmts + long_v::size() - n_expect_pmts % long_v::size(), 0);
size_t idx = 0;
// First generate some data
for (int dom = 0; dom < Constants::n_dom; ++dom) {
for (int mod = 0; mod < Constants::n_mod; ++mod) {
size_t mod_start = idx;
for (int pmt = 0; pmt < Constants::n_pmt; ++pmt) {
size_t pmt_start = idx;
long_v offset;
offset.data() = _mm256_set1_epi64x(time_start);
long last = time_start;
......@@ -121,38 +156,20 @@ std::tuple<storage_t, storage_t> generate_avx2(const long time_start, const long
// When filling, fill the past and current indices
idx += 2 * long_v::size();
}
}
// Coincidences
auto [pmts, n_coincidence] = fill_coincidences(times, idx, time_start, time_end, gens);
idx += n_coincidence;
// fill values
const size_t value_end = idx + (2 * long_v::size() - (idx % 2 * long_v::size()));
for (size_t vidx = mod_start; vidx < value_end; vidx += 2 * long_v::size()) {
int_v pmt1{random.random8i(0, 31)};
int_v pmt2{random.random8i(0, 31)};
fill_values_avx2(pmt_start, idx, values, random, dom, mod,
[pmt](size_t) { return int_v(pmt); });
auto u1 = float_v{random.random8f()};
auto u2 = float_v{random.random8f()} * Constants::two_pi;
auto fact = sqrt(-2.0f * log(u1));
float_v z0, z1;
sincos(u2, &z0, &z1);
z0 = fact * tot_sigma * z0 + tot_mean;
z1 = fact * tot_sigma * z1 + tot_mean;
auto val0 = simd_cast<int_v>(z0) | (pmt1 << 8) | ((100 * (dom + 1) + mod + 1) << 13);
auto [val0_first, val0_second] = int_v_to_long_v(val0);
}
val0_first.store(&values[vidx]);
val0_second.store(&values[vidx + long_v::size()]);
// Coincidences
auto [n_times, _] = fill_coincidences(times, pmts, idx, time_start, time_end, gens);
fill_values_avx2(idx, idx + n_times, values, random, dom, mod,
[&pmts](size_t n) {
return int_v(pmts.data() + n * int_v::size());
});
idx += n_times;
auto val1 = simd_cast<int_v>(z1) | (pmt2 << 8) | ((100 * (dom + 1) + mod + 1) << 13);
auto [val1_first, val1_second] = int_v_to_long_v(val1);
val1_first.store(&values[vidx + 2 * long_v::size()]);
val1_second.store(&values[vidx + 3 *long_v::size()]);
}
}
}
times.resize(idx);
......
......@@ -16,6 +16,8 @@
#include <cassert>
#include <stdexcept>
#include <vector>
#include <functional>
#include <iostream>
#include <tuple>
#include <optional>
......@@ -28,9 +30,7 @@ namespace {
const float tot_mean = Constants::tot_mean;
const float tot_sigma = Constants::tot_sigma;
using namespace storage;
using std::cout;
using std::endl;
using std::array;
}
......@@ -43,26 +43,15 @@ float GenScalar::dot_product(const std::array<float, 3>& left, const std::array<
}
void fill_values_scalar(long idx_start, long idx_end, storage_t& values, Generators& gens, int dom, int mod,
const std::optional<array<unsigned int, 4>>& pmts) {
std::function<unsigned int(size_t)> pmt_fun) {
// fill values
std::uniform_int_distribution<long> flat_pmt(0, 31);
auto& mt = gens.mt;
auto& flat = gens.flat;
if (pmts) {
assert((idx_end - idx_start) < pmts->size());
}
size_t n = 0;
for (long vidx = idx_start; vidx < idx_end; vidx += 2) {
unsigned int pmt1 = 0, pmt2 = 0;
if (pmts) {
pmt1 = (*pmts)[n++];
pmt2 = (*pmts)[n++];
} else {
pmt1 = flat_pmt(mt);
pmt2 = flat_pmt(mt);
}
auto pmt1 = pmt_fun(n++);
auto pmt2 = pmt_fun(n++);
auto u1 = flat(mt);
auto u2 = flat(mt) * Constants::two_pi;
......@@ -88,36 +77,39 @@ std::tuple<storage_t, storage_t> generate_scalar(const long time_start, const lo
auto& mt = gens.mt;
auto& flat = gens.flat;
const size_t n_expect = gens.n_expect(time_end - time_start);
const size_t n_expect = gens.n_expect(time_end - time_start, true);
const float tau_l0 = gens.tau_l0();
storage_t times; times.resize(n_expect);
storage_t values; values.resize(n_expect + 1);
const size_t n_expect_pmts = gens.n_expect(time_end - time_start, false);
pmts_t pmts(n_expect_pmts, 0);
size_t idx = 0;
// First generate some data
for (int dom = 0; dom < Constants::n_dom; ++dom) {
for (int mod = 0; mod < Constants::n_mod; ++mod) {
size_t mod_start = idx;
for (int pmt = 0; pmt < Constants::n_pmt; ++pmt) {
long last = time_start;
while(last < time_end && idx < times.size() - 2) {
// Generate times
float r = -1.f * tau_l0 * log(flat(mt));
last += static_cast<long>(r + 0.5);
times[idx++] = last;
}
size_t pmt_start = idx;
long last = time_start;
while(last < time_end && idx < times.size() - 2) {
// Generate times
float r = -1.f * tau_l0 * log(flat(mt));
last += static_cast<long>(r + 0.5);
times[idx++] = last;
}
fill_values_scalar(pmt_start, idx, values, gens, dom, mod,
[pmt](size_t) { return pmt; });
}
fill_values_scalar(mod_start, idx, values, gens, dom, mod, {});
// Coincidences
auto [pmts, n_coincidence] = fill_coincidences(times, idx, time_start, time_end, gens);
idx += n_coincidence;
fill_values_scalar(mod_start, idx, values, gens, dom, mod, pmts);
auto [n_times, _] = fill_coincidences(times, pmts, idx, time_start, time_end, gens);
fill_values_scalar(idx, idx + n_times, values, gens, dom, mod,
[&pmts](size_t n) {
assert(n < pmts.size());
return pmts[n];
});
idx += n_times;
}
}
times.resize(idx);
......
/*
* Copyright 2018-2019 NWO-I
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "storage.h"
int storage::n_per_mod = 21760;
......@@ -37,10 +37,10 @@ generate_k40(const long time_start, const long time_end, Generators& gens, bool
// factory for lambda's to shift and mask the values back into their
// components.
auto shift_mask_fact = [&values] (const size_t shift, const long mask) {
return [&values, shift, mask] (const auto val) {
return (val >> shift) & mask;
};
auto shift_mask_fact = [] (const size_t shift, const long mask) {
return [shift, mask] (const auto val) {
return (val >> shift) & mask;
};
};
// column in the output array
......
......@@ -59,6 +59,8 @@ if (ROOT_FOUND)
generate
test_functions
${ROOT_LIBRARIES})
add_test(TestK40ROOT test_k40gen_root)
endif()
find_package(Python3 COMPONENTS Interpreter)
......
#pragma once
#include <array>
#include <unordered_map>
#include <tuple>
template <std::size_t N>
......@@ -15,5 +14,4 @@ struct get_n {
std::pair<double, double> generate_l0(float l0_rate, long dt, bool use_avx2);
std::tuple<double, double, std::unordered_map<size_t, double>>
coincidence_rate(std::array<float, 4> rates);
std::tuple<double, double> coincidence_rate(std::array<float, 4> rates);
......@@ -9,29 +9,26 @@
using namespace std;
tuple<double, double, unordered_map<size_t, double>>
tuple<double, double>
coincidence_rate(array<float, 4> rates) {
Generators gens{1052, 9523, rates};
auto l1_rate = std::accumulate(std::next(begin(rates)), end(rates), 0.);
long dt = std::lround(1e9);
long dt = std::lround(1e7);
const size_t n_expect = gens.n_expect(dt);
storage_t times; times.resize(n_expect);
storage_t values; values.resize(n_expect);
storage_t times; times.resize(3 * l1_rate);
storage_t values; values.resize(3 * l1_rate);
pmts_t pmts(3 * l1_rate);
long time_start = 0, time_end = 0;
double av = 0.;
const int n_iter = 10000;
unordered_map<size_t, double> count;
const int n_iter = 5000;
for (int i = 0; i < n_iter; ++i) {
time_end += dt;
auto [pmts, n_coincidence] = fill_coincidences(times, 0ul, time_start, time_end, gens);
count[n_coincidence] += 1 / double{n_iter};
av += n_coincidence / double{n_iter};
auto [n_times, n_coincidences] = fill_coincidences(times, pmts, 0ul, time_start, time_end, gens);
av += n_coincidences / double{n_iter};
time_start = time_end;
}
return {gens.coincidence_rate, av, count};
return {gens.coincidence_rate, av};
}
......@@ -18,7 +18,7 @@ pair<double, double> generate_l0(float l0_rate, long dt, bool use_avx2) {
double av = 0.;
const size_t n_times = times.size();
for (size_t i = 0; i < n_times - 1; ++i) {
if (((values[i + 1]) >> 13) == (values[i] >> 13)) {
if (((values[i + 1]) >> 8) == (values[i] >> 8)) {
av += static_cast<double>(times[i + 1] - times[i]) / n_times;
}
}
......
......@@ -8,11 +8,11 @@ using namespace std;
TEST_CASE( "Coincidences make sense", "[coincidence]" ) {
auto check_coincidence = [](array<float, 4> rates) {
const auto [rate, av, counts] = coincidence_rate(rates);
const auto [rate, av] = coincidence_rate(rates);
auto conf_rate = std::accumulate(std::next(begin(rates)), end(rates), 0.);
REQUIRE(rate == conf_rate);
if (rate != 0.) {
REQUIRE(std::abs(rate - av) / rate < 1e-3);
REQUIRE(std::abs(rate - av) / rate < 2e-3);
}
};
......
#include <numeric>
#include <iostream>
#include <array>
#include <iomanip>
#include <generate.h>
#include <storage.h>
......@@ -14,12 +15,19 @@
#include <TCanvas.h>
#include <TROOT.h>
#define CATCH_CONFIG_RUNNER
#include "catch2/catch.hpp"
using namespace std;
int main() {
int main(int argc, char* argv[]) {
gROOT->SetBatch();
return Catch::Session().run( argc, argv );
}
TEST_CASE( "Rates makes sense [ROOT]", "[rates_ROOT]" ) {
map<bool, unique_ptr<TH1I>> histos;
array<float, 4> rates{7000., 0., 0., 0.};
......@@ -31,15 +39,14 @@ int main() {
Generators gens{1052, 9523, rates};
long dt = std::lround(1e6);
long dt = std::lround(1e7);
long time_start = 0, time_end = time_start + dt;
auto [times, values] = generate(time_start, time_end, gens, use_avx2);
const size_t n_times = times.size();
for (size_t i = 0; i < n_times - 1; ++i) {
if (((values[i + 1]) >> 13) == (values[i] >> 13)) {
if (((values[i + 1]) >> 8) == (values[i] >> 8)) {
time_histo->Fill(times[i + 1] - times[i]);
}
}
......@@ -47,7 +54,7 @@ int main() {
time_histo->GetBinCenter(1 + time_histo->GetNbinsX())};
auto fit = time_histo->Fit(&expo, "RS");
// parameter is negative
cout << std::fabs(rates[0] + (fit->Parameter(1) * 1e9)) / rates[0] << endl;
REQUIRE(std::fabs(rates[0] + (fit->Parameter(1) * 1e9)) / rates[0] < 1e-3);
}
TCanvas canvas{"canvas", "canvas", 600, 800};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment