Initial import

This commit is contained in:
Guilhem Lavaux 2023-05-29 10:41:03 +02:00
commit 56a50eead3
820 changed files with 192077 additions and 0 deletions

View file

@ -0,0 +1,23 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/align_helper.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_ALIGN_HELPER_HPP
#define __LIBLSS_ALIGN_HELPER_HPP
#include <Eigen/Core>
namespace LibLSS {
template<typename T>
struct DetectAlignment { enum { Align = Eigen::Unaligned }; };
};
#endif

View file

@ -0,0 +1,22 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/allocator_policy.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_ALLOCATOR_POLICY_HPP
#define __LIBLSS_ALLOCATOR_POLICY_HPP
namespace LibLSS {
struct DefaultAllocationPolicy {
static long getIncrement() { return 1024; }
//static long getIncrement() { return ( 1024 * 1024 ); }
};
};
#endif

View file

@ -0,0 +1,102 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/array_concepts.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_TOOLS_ARRAYCONCEPTS_HPP
#define __LIBLSS_TOOLS_ARRAYCONCEPTS_HPP
#include <type_traits>
#include <utility>
#include <boost/core/enable_if.hpp>
#include <boost/tti/has_static_member_data.hpp>
#include <boost/tti/has_type.hpp>
#include <boost/mpl/and.hpp>
#include <boost/multi_array/base.hpp>
#include <boost/mpl/not.hpp>
namespace LibLSS {
namespace array_concepts {
BOOST_TTI_HAS_TYPE(element);
template <typename T, typename = int>
struct has_shape_info : std::false_type {};
template <typename T>
struct has_shape_info<T, decltype((void)T::Shaped, int(0))>
: std::true_type {};
template <class F, class... Args>
struct is_callable {
template <class U>
static auto test(U *p)
-> decltype((*p)(std::declval<Args>()...), void(), std::true_type());
template <class U>
static auto test(...) -> decltype(std::false_type());
static constexpr bool value = decltype(test<F>(0))::value;
};
// https://stackoverflow.com/questions/1966362/sfinae-to-check-for-inherited-member-functions/8752988
#define MEMBER_FUNC_CHECKER(name, fn, args) \
template <class C, typename ret, typename = void> \
struct name : std::false_type {}; \
template <class C, typename ret> \
struct name< \
C, ret, \
typename std::enable_if<std::is_convertible< \
decltype(std::declval<C>().fn args), ret>::value>::type> \
: std::true_type {};
MEMBER_FUNC_CHECKER(has_member_function_data, data, ())
MEMBER_FUNC_CHECKER(has_member_function_origin, origin, ())
MEMBER_FUNC_CHECKER(has_member_function_reindex, reindex, (0))
template <typename T>
using is_array_like = has_type_element<T>;
template <class C, typename = void>
struct check_element_type {
typedef void element;
};
template <class C>
struct check_element_type<
C, typename std::enable_if<has_type_element<C>::value>::type> {
typedef typename C::element element;
};
template <typename T>
struct is_complex_type : std::false_type {};
template <typename T>
struct is_complex_type<std::complex<T>> : std::true_type {};
template <typename T>
using is_array_storage = boost::mpl::and_<
has_type_element<T>,
has_member_function_data<T, typename check_element_type<T>::element *>>;
template <typename T>
using is_array_sub = boost::mpl::and_<
has_type_element<T>, has_member_function_origin<
T, typename check_element_type<T>::element *>>;
template <typename T>
using is_array_view = boost::mpl::and_<
has_type_element<T>,
boost::mpl::not_<has_member_function_data<
T, typename check_element_type<T>::element *>>,
has_member_function_reindex<T, void>>;
} // namespace array_concepts
} // namespace LibLSS
#endif

View file

@ -0,0 +1,315 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/array_tools.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_ARRAY_TOOLS_HPP
#define __LIBLSS_ARRAY_TOOLS_HPP
#include "libLSS/tools/errors.hpp"
#include "libLSS/tools/align_helper.hpp"
#include <CosmoTool/omptl/omptl>
#include <CosmoTool/omptl/omptl_algorithm>
#include <boost/lambda/lambda.hpp>
#include "libLSS/tools/fused_array.hpp"
namespace LibLSS {
namespace array {
typedef boost::multi_array_types::extent_range erange;
typedef boost::multi_array_types::index_range irange;
template<typename InArray>
struct EigenMap {
typedef typename InArray::element T;
typedef Eigen::Array<T, Eigen::Dynamic, 1> E_Array;
typedef Eigen::Map<E_Array, Eigen::Unaligned> MapArray;
static MapArray map(InArray& a) {
return MapArray(a.data(), a.num_elements());
}
};
template<typename InArray>
typename EigenMap<InArray>::MapArray eigen_map(InArray& a) {
return EigenMap<InArray>::map(a);
}
template<typename Array>
void fill(Array& a, typename Array::element val) {
using boost::lambda::constant;
typedef typename Array::element VArrayType;
LibLSS::copy_array(a, b_fused<VArrayType,Array::dimensionality>(constant(val)));
}
template<typename Array>
void density_rescale(Array& a, typename Array::element val) {
using boost::lambda::_1;
LibLSS::copy_array(a, b_fused<typename Array::element>(a, _1 / val - 1));//omptl::for_each(a.data(), a.data()+a.num_elements(), _1 / val - 1);
}
template<typename InArray, typename OutArray>
void copyArray3d(OutArray& out, const InArray& in, bool in_padded = false)
{
if (!in_padded && (out.shape()[0] < in.shape()[0] ||
out.shape()[1] < in.shape()[1] ||
out.shape()[2] < in.shape()[2])) {
error_helper<ErrorBadState>("Invalid copy shape in copyArray3d");
}
LibLSS::copy_array(out, in);
}
template<typename InOutArray>
void scaleArray3d(InOutArray& out, typename InOutArray::element scale)
{
using boost::lambda::_1;
LibLSS::copy_array(out, b_fused<typename InOutArray::element>(out, _1*scale));
}
template<typename T, size_t n>
T product(std::array<T,n> const& d) {
T a = T(1);
for (size_t i = 0; i < n; i++) a *= d[i];
return a;
}
template<typename Iterator, typename T = typename std::remove_reference<decltype(*std::declval<Iterator>())>::type>
T product(Iterator b, Iterator e) {
T a = T(1);
while (b != e) {
a *= *b;
++b;
}
return a;
}
template<typename InOutPlane>
void scalePlane(InOutPlane plane, typename InOutPlane::element scale)
{
const typename InOutPlane::index *base = plane.index_bases();
const typename InOutPlane::size_type *exts = plane.shape();
for (long i = base[0]; i < base[0] + exts[0]; i++) {
for (long j = base[1]; j < base[1] + exts[1]; j++) {
plane[i][j] *= scale;
}
}
}
template<typename InOutLine>
void scaleLine(InOutLine line, typename InOutLine::element scale)
{
const typename InOutLine::index *base = line.index_bases();
const typename InOutLine::size_type *exts = line.shape();
for (long i = base[0]; i < base[0] + exts[0]; i++) {
line[i] *= scale;
}
}
template<typename InArray, typename OutArray>
void scaleAndCopyArray3d_rv(OutArray out, const InArray& in, typename OutArray::element scale)
{
using boost::format;
long N0 = out.shape()[0],
N1 = out.shape()[1],
N2 = out.shape()[2];
Console& cons = Console::instance();
N0 = std::min(N0,long(in.shape()[0]));
N1 = std::min(N1,long(in.shape()[1]));
N2 = std::min(N2,long(in.shape()[2]));
long s0 = out.index_bases()[0],
s1 = out.index_bases()[1],
s2 = out.index_bases()[2];
long i_s0 = in.index_bases()[0],
i_s1 = in.index_bases()[1],
i_s2 = in.index_bases()[2];
cons.print<LOG_DEBUG>(format("Copying (%d-%d, %d-%d, %d-%d) -> (%d-%d, %d-%d, %d-%d)")
% i_s0 % (i_s0+N0) % i_s1 % (i_s1+N1) % i_s2 % (i_s2+N2) %
s0 % (s0+N0) % s1 % (s1+N1) % s2 % (s2+N2));
#pragma omp parallel for
for (long n0 = 0; n0 < N0; n0++) {
typename OutArray::reference out0 = out[s0+n0];
typename InArray::const_reference in0 = in[i_s0+n0];
cons.print<LOG_DEBUG>(format("Line %d") % n0);
for (long n1 = 0; n1 < N1; n1++) {
typename OutArray::reference::reference out1 = out0[s1+n1];
typename InArray::const_reference::const_reference in1 = in0[i_s1+n1];
for (long n2 = 0; n2 < N2; n2++) {
out1[s2+n2] = in1[i_s2+n2]*scale;
}
}
}
cons.print<LOG_DEBUG>("Done copy");
}
template<typename InArray, typename OutArray>
void scaleAndCopyArray3d(OutArray& out, const InArray& in, typename OutArray::element scale, bool in_padded = false)
{
using boost::format;
size_t N0 = out.shape()[0],
N1 = out.shape()[1],
N2 = out.shape()[2];
if (!in_padded && (N0 < in.shape()[0] ||
N1 < in.shape()[1] ||
N2 < in.shape()[2])) {
error_helper<ErrorBadState>("Invalid copy shape in scaleAndcopyArray3d");
}
N0 = std::min(N0,in.shape()[0]);
N1 = std::min(N1,in.shape()[1]);
N2 = std::min(N2,in.shape()[2]);
ssize_t s0 = out.index_bases()[0],
s1 = out.index_bases()[1],
s2 = out.index_bases()[2];
Console::instance().print<LOG_DEBUG>(format("Copying (%d-%d, %d-%d, %d-%d)") % s0 % (s0+N0) % s1 % (s1+N1) % s2 % (s2+N2));
#pragma omp parallel for
for (size_t n0 = s0; n0 < s0+N0; n0++) {
auto out_0 = out[n0];
auto in_0 = in[n0];
for (size_t n1 = s1; n1 < s1+N1; n1++) {
auto out_1 = out_0[n1];
auto in_1 = in_0[n1];
for (size_t n2 = s2; n2 < s2+N2; n2++) {
out_1[n2] = in_1[n2]*scale;
}
}
}
Console::instance().print<LOG_DEBUG>("Done copy");
}
template<typename T>
auto generate_slice(T x[6]) {
typedef boost::multi_array_types::index_range i_range;
return boost::indices[i_range(x[0], x[1])][i_range(x[2],x[3])][i_range(x[4],x[5])];
}
template<typename A, typename RangeList>
auto slice_array(A&& a, const RangeList& rlist)
-> decltype(a[rlist])
{
auto v = a[rlist];
typedef typename std::remove_reference<A>::type A_t;
boost::array<size_t, A_t::dimensionality> ids;
size_t i = 0 ;
for (auto v: rlist.ranges_) {
ids[i] = v.get_start(a.index_bases()[i]);
i++;
}
v.reindex(ids);
return v;
}
namespace details {
typedef boost::multi_array_types::extent_range range;
template<size_t Nd>
struct make_extent {
template<typename E, typename IB, typename S>
static inline auto make(E e, IB ib, S s)
{
return make_extent<Nd-1>::make(e[range(*ib, *ib+*s)], ib+1, s+1);
}
};
template<>
struct make_extent<0> {
template<typename E, typename IB, typename S>
static inline auto make(E e, IB ib, S s) { return e; }
};
}
namespace star_index_detail {
template<typename I>
auto _make_star_indices(I indices, std::integer_sequence<size_t>) {
return indices;
}
template<typename I, size_t N0, size_t... N1>
auto _make_star_indices(I indices, std::integer_sequence<size_t,N0,N1...>) {
typedef boost::multi_array_types::index_range i_range;
return _make_star_indices(indices, std::integer_sequence<size_t,N1...>())[i_range()];
}
template<size_t N, typename I>
auto make_star_indices(I indices) {
return _make_star_indices(indices, std::make_integer_sequence<size_t,N>());
}
}
using star_index_detail::make_star_indices;
template<size_t N, typename IB, typename S, typename E = boost::multi_array_types::extent_gen>
auto make_extent(IB bases, S shape, E e = E()) {
return
details::make_extent<N>::make(
e, bases, shape
);
}
template<typename... I>
auto extent(I... i) {
std::array<size_t, sizeof...(I)> s{i...};
std::array<size_t, sizeof...(I)> b;
std::fill(b.begin(), b.end(), 0);
return make_extent<sizeof...(I)>(b.data(), s.data());
}
/**
* @brief build a new extent object from the dimensions of an existing array-like type.
*
* @return an extent
*/
template<typename A, typename E = boost::multi_array_types::extent_gen>
auto make_extent_from(A&& a, E e = E())
{
return make_extent<std::remove_reference<A>::type::dimensionality>(a.index_bases(), a.shape(), e);
}
template<typename IndexArray, typename SwapFunc>
void reorder(const IndexArray& part_idx, SwapFunc func)
{
size_t numPart = part_idx.shape()[0], i = 0;
boost::multi_array<typename IndexArray::element,1> sorter(boost::extents[numPart]);
LibLSS::copy_array(sorter, part_idx);
// Now that partices have been gathered back, we have to reorder them
// to "unsort".
while (i < numPart) {
typename IndexArray::reference swapper = sorter[i];
if (swapper == i) {
i++;
continue;
}
func(swapper, i);
std::swap(sorter[i], sorter[swapper]);
}
}
};
};
#endif

View file

@ -0,0 +1,128 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/auto_interpolator.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_TOOLS_AUTO_INTERP_HPP
#define __LIBLSS_TOOLS_AUTO_INTERP_HPP
#include <boost/multi_array.hpp>
#include <cmath>
#include "libLSS/tools/errors.hpp"
namespace LibLSS {
namespace internal_auto_interp {
template <typename T>
class auto_interpolator {
public:
typedef T bare_t;
typedef boost::multi_array<T, 1> array_type;
private:
array_type *array_vals;
size_t N;
T start, end, step, overflow, underflow;
bool throwOnOverflow;
public:
explicit auto_interpolator(
const T &_start, const T &_end, const T &_step, const T &_under,
const T &_over, array_type *value)
: array_vals(value), start(_start), end(_end), step(_step),
underflow(_under), overflow(_over), N(value->size()),
throwOnOverflow(false) {}
auto_interpolator(auto_interpolator<T> &&other)
: array_vals(other.array_vals), start(other.start), end(other.end),
step(other.step), underflow(other.underflow),
overflow(other.overflow), N(other.N), throwOnOverflow(false) {
other.array_vals = 0;
}
explicit auto_interpolator()
: array_vals(0), start(0), end(0), step(0), underflow(0), overflow(0),
N(0), throwOnOverflow(false) {}
auto_interpolator(auto_interpolator<T> const &other) {
array_vals = 0;
operator=(other);
}
auto_interpolator<T> &operator=(auto_interpolator<T> const &other) {
if (array_vals)
delete array_vals;
array_vals = new array_type(boost::extents[other.N]);
start = other.start;
end = other.end;
step = other.step;
underflow = other.underflow;
overflow = other.overflow;
N = other.N;
throwOnOverflow = other.throwOnOverflow;
for (size_t i = 0; i < N; i++)
(*array_vals)[i] = (*other.array_vals)[i];
return *this;
}
~auto_interpolator() {
if (array_vals)
delete array_vals;
}
auto_interpolator<T> &setThrowOnOverflow() {
throwOnOverflow = true;
return *this;
}
T operator()(const T &a) const {
T normed_pos = (a - start) / step;
T f_pos = std::floor(normed_pos);
ssize_t i_pos = ssize_t(f_pos);
T r = normed_pos - f_pos;
if (i_pos < 0)
return underflow;
if (i_pos == (N - 1) && std::abs(r) < 1e-5) {
return (*array_vals)[N - 1];
}
if (i_pos >= (N - 1)) {
if (throwOnOverflow)
throw ErrorParams("overflow in interpolation");
return overflow;
}
return (1 - r) * (*array_vals)[i_pos] + r * (*array_vals)[i_pos + 1];
}
};
template <typename T, typename Functor>
auto_interpolator<T> build_auto_interpolator(
const Functor &f, const T &start, const T &end, const T &step,
const T &underflow, const T &overflow) {
typedef auto_interpolator<T> a_interp;
typedef typename a_interp::array_type array_type;
size_t N = size_t(round((end - start) / step));
array_type *vals = new array_type(boost::extents[N]);
#pragma omp parallel
for (size_t i = 0; i < N; i++) {
T x = start + i * step;
(*vals)[i] = f(x);
}
return a_interp(start, end, step, underflow, overflow, vals);
}
} // namespace internal_auto_interp
using internal_auto_interp::auto_interpolator;
using internal_auto_interp::build_auto_interpolator;
} // namespace LibLSS
#endif

View file

@ -0,0 +1,56 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/bisection.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_BISECTION_HPP
#define __LIBLSS_BISECTION_HPP
#include <cmath>
namespace LibLSS {
template <typename Function>
double
bisection(double a, double b, double feps, double ftarget, Function f) {
double c = 0;
// Bisection Method
double epsilon = 1e-6;
double f_a = f(a);
double f_b = f(b);
if ((f_a < f_b && f_b < ftarget) || (f_a > f_b && f_b > ftarget)) {
return b;
}
if ((f_a < f_b && f_a > ftarget) || (f_a > f_b && f_a < ftarget)) {
return a;
}
while (std::abs(a - b) > 2 * epsilon) {
// Calculate midpoint of domain
c = (a + b) / 2.;
double f_c = f(c);
double fc = ftarget - f_c;
double fa = ftarget - f_a;
if (std::abs(fc) < feps)
break; //break if tolerance has been reached
if ((fa * fc) < 0.)
b = c;
else {
a = c;
f_a = f_c;
}
}
return c;
}
} // namespace LibLSS
#endif

29
libLSS/tools/checkmem.hpp Normal file
View file

@ -0,0 +1,29 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/checkmem.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_CHECKMEM_HPP
#define __LIBLSS_CHECKMEM_HPP
/* Stackoverflow: http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence
*/
#define HAS_MEM_FUNC(func, name) \
template<typename T, typename Sign> \
struct name { \
typedef char yes[1]; \
typedef char no [2]; \
template <typename U, U> struct type_check; \
template <typename _1> static yes &chk(type_check<Sign, &_1::func > *); \
template <typename > static no &chk(...); \
static bool const value = sizeof(chk<T>(0)) == sizeof(yes); \
}
#endif

View file

@ -0,0 +1,54 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/color_mod.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_COLOR_MOD_HPP
#define __LIBLSS_COLOR_MOD_HPP
#include <string>
#include <boost/format.hpp>
namespace LibLSS {
namespace Color {
enum ColorValue {
BLACK = 0,
RED = 1,
GREEN = 2,
YELLOW = 3,
BLUE = 4,
MAGENTA = 5,
CYAN = 6,
WHITE = 7
};
enum ColorIntensity {
NORMAL = 0,
BRIGHT = 1
};
inline std::string fg(ColorValue c, const std::string& text, ColorIntensity i = NORMAL, bool is_console = true) {
if (is_console)
return boost::str(boost::format("\x1b[%d;%dm%s\x1b[39;0m") % ((int)c + 30) % (int)i % text);
else
return text;
}
inline std::string bg(ColorValue c, const std::string& text, ColorIntensity i = NORMAL, bool is_console = true) {
if (is_console)
return boost::str(boost::format("\x1b[%d;%dm%s\x1b[49;0m") % ((int)c + 40) % (int)i % text);
else
return text;
}
}
}
#endif

View file

@ -0,0 +1,9 @@
#pragma once
#define DISABLE_WARN_DIV_BY_ZERO \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wdiv-by-zero\"")
#define ENABLE_WARN_DIV_BY_ZERO \
_Pragma("GCC diagnostic pop")

253
libLSS/tools/console.cpp Normal file
View file

@ -0,0 +1,253 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/console.cpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#include "libLSS/cconfig.h"
#include <cstdlib>
#include <boost/stacktrace.hpp>
#include <boost/assert.hpp>
#include <boost/chrono.hpp>
#include "libLSS/tools/string_tools.hpp"
#include <fstream>
#include <iostream>
#include <map>
#include "libLSS/tools/static_init.hpp"
#include "console.hpp"
#include <CosmoTool/hdf5_array.hpp>
#include "libLSS/tools/hdf5_type.hpp"
#include "libLSS/tools/timing_db.hpp"
using boost::format;
using boost::str;
using boost::chrono::duration;
using boost::chrono::duration_cast;
using boost::chrono::system_clock;
using LibLSS::Console;
using LibLSS::details::ProgressBase;
static std::string format_duration(duration<double> &d) {
typedef boost::chrono::hours hours;
typedef boost::chrono::minutes minutes;
typedef boost::chrono::seconds seconds;
duration<long> d_int = duration_cast<duration<long>>(d);
return str(
format("%02d:%02d:%02d") % duration_cast<hours>(d_int).count() %
duration_cast<minutes>(d_int % hours(1)).count() %
duration_cast<seconds>(d_int % minutes(1)).count());
}
#ifndef NDEBUG
void boost::assertion_failed(
char const *expr, char const *function, char const *file, long line) {
auto &cons = Console::instance();
std::string msg = "ASSERTION FAILED: " + std::string(expr) + " in " +
std::string(function) + " (" + std::string(file) + ")";
cons.print<LibLSS::LOG_ERROR>(msg);
::abort();
LibLSS::MPI_Communication::instance()->abort();
}
void boost::assertion_failed_msg(
char const *expr, char const *msg, char const *function, char const *file,
long line) {
auto &cons = Console::instance();
std::string m = "ASSERTION FAILED: " + std::string(expr) +
", msg = " + std::string(msg) + " in " +
std::string(function) + " (" + std::string(file) + ")";
cons.print<LibLSS::LOG_ERROR>(m);
::abort();
LibLSS::MPI_Communication::instance()->abort();
}
#endif
void Console::print_stack_trace() {
std::string s = boost::stacktrace::to_string(boost::stacktrace::stacktrace());
print<LOG_ERROR>(LibLSS::tokenize(s, "\n"));
}
ProgressBase::ProgressBase(Console *c, const std::string &m, int num, int step)
: numElements(num), percent(0), iLevel(c->indentLevel), step(step), msg(m),
start(system_clock::now()), console(c) {}
void ProgressBase::destroy() {
update(numElements);
console->cleanProgress(this);
}
void ProgressBase::update(int i) {
int new_percent;
if (numElements == 0)
return;
current = i;
new_percent = current * 100L / numElements;
if (new_percent > (percent + step - 1)) {
duration<double> elapsed = system_clock::now() - start, sec;
int oldLevel = console->indentLevel;
sec = elapsed * (100. - new_percent) / new_percent;
percent = new_percent;
console->setIndentLevel(iLevel);
print(
str(format("%s %d %% (ETA %s, elapsed %s)") % msg % new_percent %
format_duration(sec) % format_duration(elapsed)));
console->setIndentLevel(oldLevel);
}
}
struct StatInfo {
size_t count;
double total_time;
StatInfo() : count(0), total_time(0) {}
};
struct TimingInfoStore {
CosmoTool::CosmoString name;
StatInfo info;
};
CTOOL_STRUCT_TYPE(
StatInfo, HDF5T_StatInfo, ((size_t, count))((double, total_time)));
CTOOL_STRUCT_TYPE(
TimingInfoStore, HDF5T_TimingInfoStore,
((CosmoTool::CosmoString, name))((StatInfo, info)));
static std::map<std::string, StatInfo> timing_stats;
static bool report_timing_done = true;
namespace LibLSS {
namespace timings {
void record(std::string const &n, double t) {
#ifdef LIBLSS_TIMED_CONTEXT
if (report_timing_done)
return;
auto &info = timing_stats[n];
info.count++;
info.total_time += t;
#endif
}
} // namespace timings
} // namespace LibLSS
#include "libLSS/ares_version.hpp"
namespace LibLSS {
namespace details {
namespace {
ConsoleContextBase baseContext;
}
thread_local ConsoleContextBase *currentContext = &baseContext;
} // namespace details
} // namespace LibLSS
namespace {
using namespace LibLSS;
static std::string g_time_file_pattern = "timing_stats_%d.txt";
static void record_init() {
Console::instance().print<LOG_INFO>(
"libLSS version " + ARES_GIT_VERSION + " built-in modules " +
ARES_BUILTIN_MODULES);
report_timing_done = false;
}
static void dump_time_records(bool close) {
if (report_timing_done)
return;
report_timing_done = close;
std::string s =
str(format(g_time_file_pattern) %
LibLSS::MPI_Communication::instance()->rank());
std::ofstream f(s);
f << "ARES version " << ARES_GIT_VERSION << " modules "
<< ARES_BUILTIN_MODULES << std::endl
<< std::endl;
f << "Cumulative timing spent in different context" << std::endl
<< "--------------------------------------------" << std::endl
<< "Context, Total time (seconds)" << std::endl
<< std::endl;
std::vector<std::pair<std::string, StatInfo>> sorted_timings;
std::copy(
timing_stats.begin(), timing_stats.end(),
std::back_inserter(sorted_timings));
std::sort(
sorted_timings.begin(), sorted_timings.end(),
[](std::pair<std::string, StatInfo> const &p1,
std::pair<std::string, StatInfo> const &p2) {
return p1.second.total_time > p2.second.total_time;
});
for (auto &n : sorted_timings) {
f << format("% 40s\t%d\t%g") % n.first % n.second.count %
n.second.total_time
<< std::endl;
}
}
static void record_fini() { dump_time_records(true); }
// Highest priority after console.
LibLSS::RegisterStaticInit
reg_record_init(record_init, record_fini, 1, "Timing database");
} // namespace
namespace LibLSS {
namespace timings {
void set_file_pattern(std::string const &pattern) {
g_time_file_pattern = pattern;
}
void trigger_dump() { dump_time_records(false); }
void reset() { timing_stats.clear(); }
void load(H5_CommonFileGroup &g) {
boost::multi_array<TimingInfoStore, 1> store;
try {
CosmoTool::hdf5_read_array(g, "timings", store, true);
for (size_t i = 0; i < store.num_elements(); i++) {
TimingInfoStore const &t = store[i];
auto &entry = timing_stats[std::string(t.name.s)];
entry.count += t.info.count;
entry.total_time += t.info.total_time;
}
} catch (H5::FileIException const &e) {
Console::instance().print<LOG_WARNING>(
"No timing database in this file.");
}
}
void save(H5_CommonFileGroup &g) {
boost::multi_array<TimingInfoStore, 1> store;
ssize_t idx = 0;
store.resize(boost::extents[timing_stats.size()]);
for (auto &n : timing_stats) {
store[idx].name.s = n.first.c_str();
store[idx].info = n.second;
idx++;
}
CosmoTool::hdf5_write_array(g, "timings", store);
}
} // namespace timings
} // namespace LibLSS
AUTO_REGISTRATOR_IMPL(console_timing);

506
libLSS/tools/console.hpp Normal file
View file

@ -0,0 +1,506 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/console.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_CONSOLE_HPP
#define __LIBLSS_CONSOLE_HPP
// Log traits automatically available when console is loaded
#include <boost/format.hpp>
#include <boost/chrono.hpp>
#include <iostream>
#include <fstream>
#include <list>
#include <boost/chrono.hpp>
#include "libLSS/mpi/generic_mpi.hpp"
#include "libLSS/tools/log_traits.hpp"
#include "libLSS/tools/static_auto.hpp"
#include "libLSS/tools/function_name.hpp"
namespace LibLSS {
class Console;
namespace details {
class ProgressBase {
private:
int numElements;
int current;
int percent;
int iLevel;
int step;
std::string msg;
boost::chrono::system_clock::time_point start;
friend class Console;
protected:
Console *console;
ProgressBase(Console *c, const std::string &m, int num, int _step);
virtual ~ProgressBase() {}
public:
void destroy();
virtual void print(const std::string &s) = 0;
void update(int i);
};
}; // namespace details
template <typename T>
class Progress : public details::ProgressBase {
private:
Progress(Console *c, const std::string &m, int num, int _step)
: ProgressBase(c, m, num, _step) {}
friend class Console;
public:
virtual void print(const std::string &s);
};
class Console;
namespace timings {
void record(std::string const &n, double t);
void trigger_dump();
void reset();
void set_file_pattern(std::string const &pattern);
} // namespace timings
namespace details {
class ConsoleContextBase;
extern thread_local ConsoleContextBase *currentContext;
class ConsoleContextBase {
protected:
ConsoleContextBase *previousContext;
#ifdef LIBLSS_TIMED_CONTEXT
boost::chrono::system_clock::time_point start_context;
std::string ctx_msg, short_msg;
void record_timing(boost::chrono::duration<double> const &timing) {
timings::record(ctx_msg, timing.count());
}
void record_self_timing(
boost::chrono::system_clock::time_point const &timing) {}
#else
void record_timing(boost::chrono::duration<double> const &) {}
void record_self_timing(boost::chrono::duration<double> const &) {}
#endif
public:
#ifdef LIBLSS_TIMED_CONTEXT
std::string const &getMessage() const { return ctx_msg; }
#else
std::string const &getMessage() const {
static std::string s = "";
return s;
}
#endif
static inline ConsoleContextBase &current() { return *currentContext; }
ConsoleContextBase();
~ConsoleContextBase();
};
class ConsoleContextDummy {
public:
std::string getMessage() const { return std::string(); }
static inline ConsoleContextBase &current() { return *currentContext; }
template <typename... Args>
void print(const Args &... args) {}
template <typename T2, typename... Args>
void print2(const Args &... args) {}
template <typename... Args>
void format(Args &&... args) {}
template <typename T2, typename... Args>
void format2(Args &&... args) {}
ConsoleContextDummy() {}
~ConsoleContextDummy() {}
};
template <typename T>
class ConsoleContext : public ConsoleContextBase {
private:
ConsoleContext(ConsoleContext const &) {}
public:
ConsoleContext(std::string const &code_name);
ConsoleContext(
std::string const &code_name, std::string const &short_name);
~ConsoleContext();
template <typename... Args>
void format(Args &&... args);
template <typename T2, typename... Args>
void format2(Args &&... args);
template <typename... Args>
void print(const Args &... args);
template <typename T2, typename... Args>
void print2(const Args &... args);
};
} // namespace details
using details::ConsoleContext;
class Console {
protected:
typedef std::list<details::ProgressBase *> PList;
std::ofstream outputFile;
int verboseLevel;
int logfileVerboseLevel;
int indentLevel;
bool noColor;
typedef std::function<void(int, std::string const &)> OutputHijacker;
OutputHijacker hijackOutput;
std::string indentString;
PList all_progress;
friend class details::ProgressBase;
template <typename T>
friend class ConsoleContext;
Console(int verbose = DEFAULT_LOG_LEVEL::verboseLevel, int indent = 0)
: verboseLevel(verbose), logfileVerboseLevel(LOG_DEBUG::verboseLevel),
indentLevel(indent), noColor(false) {}
private:
void cleanProgress(details::ProgressBase *b) {
PList::iterator i = all_progress.begin();
while (i != all_progress.end()) {
if ((*i) == b)
i = all_progress.erase(i);
else
++i;
}
}
public:
static Console &instance() {
static Console singleton;
return singleton;
}
void setSeparateStream(OutputHijacker f) { hijackOutput = f; }
void setNoColor(bool nc) { noColor = nc; }
void outputToFile(const std::string &fname) {
outputFile.close();
outputFile.open(fname.c_str(), std::ofstream::app);
}
void indent() { setIndentLevel(indentLevel + 2); }
void unindent() { setIndentLevel(indentLevel - 2); }
void setIndentLevel(int indent) {
indentString = "";
for (int i = 0; i < indent / 2; i++)
indentString += "| ";
indentLevel = indent;
}
void setVerboseLevel(int verbose) { verboseLevel = verbose; }
void setLogfileVerboseLevel(int verbose) { logfileVerboseLevel = verbose; }
int getVerboseLevel() const {return verboseLevel;}
template <typename T>
void setVerboseLevel() {
setVerboseLevel(T::verboseLevel);
}
template <typename T>
void setLogfileVerboseLevel() {
setLogfileVerboseLevel(T::verboseLevel);
}
template <typename T>
bool willPrint() const {
return verboseLevel >= T::verboseLevel;
}
// Fast track debug. Reduced functionality but less performance loss.
void debug(const std::string &s) {
#ifndef LIBLSS_CONSOLE_NO_DEBUG_SUPPORT
if (willPrint<LOG_DEBUG>())
print<LOG_DEBUG>(s);
#endif
}
template <typename T>
void print(const boost::format &fmt) {
#ifdef LIBLSS_CONSOLE_NO_DEBUG_SUPPORT
if (T::verboseLevel >= LOG_DEBUG::verboseLevel)
return;
#endif
print<T>(fmt.str());
}
template <typename T>
void print(std::vector<std::string> const &msg_v) {
#ifdef LIBLSS_CONSOLE_NO_DEBUG_SUPPORT
if (T::verboseLevel >= LOG_DEBUG::verboseLevel)
return;
#endif
for (auto const &s : msg_v)
print<T>(s);
}
static void _format_expansion(boost::format &f) {}
template <typename A, typename... U>
static void _format_expansion(boost::format &f, A &&a, U &&... u) {
_format_expansion(f % a, u...);
}
template <typename T, typename... U>
void format(std::string const &s, U &&... args) {
#ifdef LIBLSS_CONSOLE_NO_DEBUG_SUPPORT
if (T::verboseLevel >= LOG_DEBUG::verboseLevel)
return;
#endif
boost::format f(s);
_format_expansion(f, std::forward<U>(args)...);
print<T>(f);
}
template <typename T>
void print(const std::string &msg) {
#ifdef LIBLSS_CONSOLE_NO_DEBUG_SUPPORT
if (T::verboseLevel >= LOG_DEBUG::verboseLevel)
return;
#endif
MPI_Communication *world = MPI_Communication::instance();
bool notMainRank = ((T::mainRankOnly) && (world->rank() != 0));
if (outputFile && T::verboseLevel <= logfileVerboseLevel) {
std::string fullMessage = T::prefix + indentString + msg;
outputFile << fullMessage << std::endl;
}
if (hijackOutput) {
std::string fullMessage = T::prefix + indentString + msg;
hijackOutput(T::verboseLevel, fullMessage);
}
if (verboseLevel < T::verboseLevel)
return;
std::string fullMessage_c =
(noColor ? T::prefix : T::prefix_c) + indentString + msg;
if (!T::mainRankOnly) {
fullMessage_c = boost::str(
boost::format("[% 3d/% 3d] %s") % world->rank() % world->size() %
fullMessage_c);
} else if (notMainRank)
return;
else {
fullMessage_c = "[---/---] " + fullMessage_c;
}
for (int i = 0; i < T::numOutput; i++)
if (*T::os[i])
(*T::os[i]) << fullMessage_c << std::endl;
}
template <typename T>
void print_memory(size_t n) {
if (n < 1024L)
print<T>(boost::format("Requesting %ld bytes") % n);
else if (n < 1024L * 1024L)
print<T>(boost::format("Requesting %lg kbytes") % (n / 1024.));
else if (n < 1024L * 1024L * 1024L)
print<T>(boost::format("Requesting %lg Mbytes") % (n / 1024. / 1024.));
else
print<T>(
boost::format("Requesting %lg Gbytes") %
(n / 1024. / 1024. / 1024.));
}
template <typename T>
Progress<T> &
start_progress(const std::string &msg, int numElements, int step = 10) {
Progress<T> *p = new Progress<T>(this, msg, numElements, step);
all_progress.push_back(p);
return *p;
}
void print_stack_trace();
void c_assert(bool c, const std::string &msg) {
if (!c) {
print<LOG_ERROR>(msg);
print_stack_trace();
MPI_Communication::instance()->abort();
}
}
template <typename U, typename... T>
void c_assert(bool c, const std::string &msg, U &&u, T &&... t) {
if (!c) {
boost::format f(msg);
_format_expansion(f, std::forward<U>(u), std::forward<T>(t)...);
print<LOG_ERROR>(f.str());
MPI_Communication::instance()->abort();
}
}
};
inline details::ConsoleContextBase::ConsoleContextBase() {
previousContext = currentContext;
currentContext = this;
}
inline details::ConsoleContextBase::~ConsoleContextBase() {
currentContext = previousContext;
}
template <typename T>
details::ConsoleContext<T>::ConsoleContext(std::string const &msg)
: ConsoleContextBase() {
#ifdef LIBLSS_TIMED_CONTEXT
start_context = boost::chrono::system_clock::now();
short_msg = ctx_msg = msg;
#endif
Console &c = Console::instance();
c.print<T>("Entering " + msg);
c.indent();
}
template <typename T>
details::ConsoleContext<T>::ConsoleContext(
std::string const &msg, std::string const &short_msg_)
: ConsoleContextBase() {
#ifdef LIBLSS_TIMED_CONTEXT
start_context = boost::chrono::system_clock::now();
ctx_msg = msg;
short_msg = short_msg_;
record_self_timing(start_context);
#endif
Console &c = Console::instance();
c.print<T>("Entering " + short_msg_);
c.indent();
}
template <typename T>
details::ConsoleContext<T>::~ConsoleContext() {
Console &c = Console::instance();
c.unindent();
#ifdef LIBLSS_TIMED_CONTEXT
boost::chrono::duration<double> ctx_time =
boost::chrono::system_clock::now() - start_context;
c.print<T>(boost::format("Done (in %s) (ctx='%s')") % ctx_time % short_msg);
record_timing(ctx_time);
#else
c.print<T>("Done");
#endif
}
template <typename T>
template <typename... Args>
void details::ConsoleContext<T>::print(const Args &... args) {
#ifdef LIBLSS_CONSOLE_NO_DEBUG_SUPPORT
if (T::verboseLevel >= LOG_DEBUG::verboseLevel)
return;
#endif
Console::instance().print<T>(args...);
}
template <typename T>
template <typename... U>
void details::ConsoleContext<T>::format(U &&... u) {
Console::instance().format<T>(std::forward<U>(u)...);
}
template <typename T>
template <typename T2, typename... U>
void details::ConsoleContext<T>::format2(U &&... u) {
#ifdef LIBLSS_CONSOLE_NO_DEBUG_SUPPORT
if (T::verboseLevel >= LOG_DEBUG::verboseLevel)
return;
#endif
Console::instance().format<T2>(std::forward<U>(u)...);
}
template <typename T>
template <typename T2, typename... Args>
void details::ConsoleContext<T>::print2(const Args &... args) {
/* if (T2::verboseLevel < T::verboseLevel)
return;*/
#ifdef LIBLSS_CONSOLE_NO_DEBUG_SUPPORT
if (T::verboseLevel >= LOG_DEBUG::verboseLevel)
return;
#endif
Console::instance().print<T2>(args...);
}
template <typename T>
void Progress<T>::print(const std::string &m) {
Console::instance().print<T>(m);
}
}; // namespace LibLSS
AUTO_REGISTRATOR_DECL(console_timing);
namespace LibLSS {
namespace fileTools {
namespace {
constexpr const char *str_end(const char *str) {
return *str ? str_end(str + 1) : str;
}
constexpr bool str_slant(const char *str) {
return *str == '/' ? true : (*str ? str_slant(str + 1) : false);
}
constexpr const char *r_slant(const char *str) {
return *str == '/' ? (str + 1) : r_slant(str - 1);
}
constexpr const char *file_name(const char *str) {
return str_slant(str) ? r_slant(str_end(str)) : str;
}
} // namespace
} // namespace fileTools
} // namespace LibLSS
#define LIBLSS_AUTO_CONTEXT(level, name) \
LibLSS::ConsoleContext<level> name( \
std::string("[" __FILE__ "]") + LIBLSS_FUNCTION)
#define LIBLSS_AUTO_CONTEXT2(level, name, short) \
LibLSS::ConsoleContext<level> name( \
std::string("[" __FILE__ "]") + LIBLSS_FUNCTION, short)
#ifdef LIBLSS_CONSOLE_NO_DEBUG_SUPPORT
# define LIBLSS_AUTO_DEBUG_CONTEXT(name) \
LibLSS::details::ConsoleContextDummy name
# define LIBLSS_AUTO_DEBUG_CONTEXT2(name, short) \
LibLSS::details::ConsoleContextDummy name
#else
# define LIBLSS_AUTO_DEBUG_CONTEXT(name) \
LibLSS::ConsoleContext<LOG_DEBUG> name( \
std::string("[" __FILE__ "]") + LIBLSS_FUNCTION)
# define LIBLSS_AUTO_DEBUG_CONTEXT2(name, short) \
LibLSS::ConsoleContext<LOG_DEBUG> name( \
std::string("[" __FILE__ "]") + LIBLSS_FUNCTION, short)
#endif
#endif

View file

@ -0,0 +1,19 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/cpu/feature_check.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_TOOLS_CPUCHECK_HPP
#define __LIBLSS_TOOLS_CPUCHECK_HPP
#if defined(__GNUC__) && !defined(__INTEL_COMPILER)
# include "feature_check_gnuc.hpp"
#else
# include "feature_check_other.hpp"
#endif
#endif

View file

@ -0,0 +1,90 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/cpu/feature_check_gnuc.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#include <string>
namespace LibLSS {
static inline bool check_compatibility(std::string &features) {
__builtin_cpu_init();
features = "";
#ifdef __MMX__
if (!__builtin_cpu_supports("mmx"))
return false;
features += "MMX ";
#else
if (__builtin_cpu_supports("mmx"))
features += "[!MMX] ";
#endif
#ifdef __AVX__
if (!__builtin_cpu_supports("avx"))
return false;
features += "AVX ";
#else
if (__builtin_cpu_supports("avx"))
features += "[!AVX] ";
#endif
#ifdef __AVX2__
if (!__builtin_cpu_supports("avx2"))
return false;
features += "AVX2 ";
#else
if (__builtin_cpu_supports("avx2"))
features += "[!AVX2] ";
#endif
#ifdef __AVX512F__
if (!__builtin_cpu_supports("avx512f"))
return false;
features += "AVX512F ";
#else
if (__builtin_cpu_supports("avx512f"))
features += "[!AVX512F] ";
#endif
#ifdef __SSE__
if (!__builtin_cpu_supports("sse"))
return false;
features += "SSE ";
#else
if (__builtin_cpu_supports("sse"))
features += "[!SSE] ";
#endif
#ifdef __SSE2__
if (!__builtin_cpu_supports("sse2"))
return false;
features += "SSE2 ";
#else
if (__builtin_cpu_supports("sse2"))
features += "[!SSE2] ";
#endif
#ifdef __SSE3__
if (!__builtin_cpu_supports("sse3"))
return false;
features += "SSE3 ";
#else
if (__builtin_cpu_supports("sse3"))
features += "[!SSE3] ";
#endif
#ifdef __SSE4_1__
if (!__builtin_cpu_supports("sse4.1"))
return false;
features += "SSE4.1 ";
#else
if (__builtin_cpu_supports("sse4.1"))
features += "[!SSE4.1] ";
#endif
#ifdef __SSE4_2__
if (!__builtin_cpu_supports("sse4.2"))
return false;
features += "SSE4.2 ";
#else
if (__builtin_cpu_supports("sse4.2"))
features += "[!SSE4.2] ";
#endif
return true;
}
} // namespace LibLSS

View file

@ -0,0 +1,15 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/cpu/feature_check_other.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
namespace LibLSS {
static inline bool check_compatibility(std::string &features) {
features = "*UNKNOWN*";
return true;
}
} // namespace LibLSS

149
libLSS/tools/defer.hpp Normal file
View file

@ -0,0 +1,149 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/defer.hpp
Copyright (C) 2018-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_TOOLS_DEFER_HPP
# define __LIBLSS_TOOLS_DEFER_HPP
# include <functional>
# include <vector>
# include "libLSS/tools/console.hpp"
namespace LibLSS {
struct DeferTypes {
enum State { WAIT, READY, ERROR };
typedef std::function<void()> Function;
};
struct DeferState : DeferTypes {
std::vector<Function> ready_f, error_f;
State state;
DeferState(State s) : state(s) {}
};
/**
* @brief Add Future like concept for ARES.
* If some state is not guaranteed to exist at a given time, a Defer can be
* added to a class. Other class may subscribe to this Defer to receive asynchronously
* an information on the change of status of the Defer.
*/
class Defer : public DeferTypes {
protected:
std::shared_ptr<DeferState> state_p;
public:
/**
* @brief Construct a new Defer object
*
*/
Defer() : state_p(std::make_shared<DeferState>(WAIT)) {}
/**
* @brief Destroy the Defer object
*
*/
~Defer() {}
/**
* @brief Check whether the defer is still is waiting status.
*
* @return true if it is still waiting.
* @return false if it is READY or FAIL.
*/
bool isWaiting() const { return state_p->state == WAIT; }
/**
* @brief Subscribe a new functor to be called when state is becoming ready.
*
* @param f Functor to be called
*/
void ready(Function f) {
if (state_p->state == READY) {
f();
return;
} else if (state_p->state == WAIT) {
state_p->ready_f.push_back(f);
}
}
/**
* @brief Subscribe a new functor to be called when state is becoming fail.
*
* @param f Functor to be called.
*/
void fail(Function f) {
if (state_p->state == ERROR) {
f();
return;
} else if (state_p->state == WAIT) {
state_p->error_f.push_back(f);
}
}
/**
* @brief Submit a state change to READY.
*
*/
void submit_ready() {
if (state_p->state == READY)
return;
Console::instance().c_assert(
state_p->state == WAIT, "State has already changed (in submit_ready).");
state_p->state = READY;
for (auto &f : state_p->ready_f) {
f();
}
state_p->ready_f.clear();
}
/**
* @brief Submit a state change to FAIL.
*
*/
void submit_error() {
if (state_p->state == ERROR)
return;
Console::instance().c_assert(
state_p->state == WAIT, "State has already changed (in submit_error).");
state_p->state = ERROR;
for (auto &f : state_p->error_f) {
f();
}
state_p->error_f.clear();
}
};
template <typename T>
class PromisePointer {
private:
typedef std::shared_ptr<T> Pointer_t;
Pointer_t ptr;
public:
Defer defer;
Pointer_t get() { return ptr; }
PromisePointer(Pointer_t starter) : ptr(starter) {}
};
template <typename T>
auto make_promise_pointer(std::shared_ptr<T> p) {
return PromisePointer<T>(p);
}
} // namespace LibLSS
#endif
// ARES TAG: num_authors = 1
// ARES TAG: name(0) = Guilhem Lavaux
// ARES TAG: email(0) = guilhem.lavaux@iap.fr
// ARES TAG: year(0) = 2018-2020

226
libLSS/tools/domains.cpp Normal file
View file

@ -0,0 +1,226 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/domains.cpp
Copyright (C) 2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#include "libLSS/cconfig.h"
#include <vector>
#include <memory>
#include "libLSS/tools/console.hpp"
#include "libLSS/mpi/generic_mpi.hpp"
#include "libLSS/tools/domains.hpp"
#include "libLSS/tools/array_tools.hpp"
#include "libLSS/tools/uninitialized_type.hpp"
using namespace LibLSS;
template <typename T, size_t N>
using TemporarySlice = LibLSS::U_Array<T, N>;
template <typename T, size_t N>
auto makeTempSlice(typename DomainSpec<N>::DomainLimit_t &slice) {
std::array<ssize_t, N> bases, shapes;
for (unsigned int i = 0; i < N; i++) {
bases[i] = slice[2 * i];
shapes[i] = slice[2 * i + 1];
}
auto ext = array::make_extent<N>(bases, shapes);
return std::make_shared<TemporarySlice<T, N>>(ext);
}
template <typename T, size_t N>
std::shared_ptr<TemporarySlice<T, N>> extractSlice(
Domain<T, N> const &input, typename DomainSpec<N>::DomainLimit_t &slice) {
auto ret = makeTempSlice(slice);
fwrap(ret->get_array()) = input;
return ret;
}
template <typename T, size_t N>
void pushSlice(
std::shared_ptr<TemporarySlice<T, N>> tmp_slice, Domain<T, N> &output,
typename DomainSpec<N>::DomainLimit_t &slice) {
//fwrap(ret->get_array()) = input;
//return ret;
}
template <size_t N>
boost::optional<DomainSpec<N>>
DomainSpec<N>::intersect(DomainSpec<N> const &other) const {
Console::instance().c_assert(
domains.size() == 1,
"Only intersect of single hypercube are supported at the moment");
DomainSpec<N> result;
std::array<ssize_t, N> start, end;
for (int i = 0; i < N; i++) {
start[i] = domains[0][2 * i];
end[i] = domains[0][2 * i + 1];
auto other_start = other.domains[0][2 * i];
auto other_end = other.domains[0][2 * i + 1];
if (end[i] < other_start || other_end < start[i])
return boost::optional<DomainSpec<N>>();
start[i] = std::max(start[i], other_start);
end[i] = std::min(end[i], other_end);
result.domains[0][2 * i] = start[i];
result.domains[0][2 * i + 1] = end[i];
}
return result;
}
template <size_t N>
void LibLSS::computeCompleteDomainSpec(
MPI_Communication *comm, CompleteDomainSpec<N> &complete,
DomainSpec<N> const &inputSpec) {
size_t commSize = comm->size();
size_t rank = comm->rank();
std::unique_ptr<int[]> domainPerNodes(new int[commSize]),
displs(new int[commSize]);
std::unique_ptr<ssize_t[]> tmp_domain(new ssize_t[2 * N * commSize]);
RequestArray requests(boost::extents[commSize]);
complete.domainOnRank.resize(commSize);
domainPerNodes[rank] = inputSpec.domains.size();
comm->all_gather_t(&domainPerNodes[comm->rank()], 1, domainPerNodes.get(), 1);
// We now have the size of each input domain
//
// Now each node must broadcast its exact domain spec to everybody.
{
size_t previous = 0;
for (size_t i = 0; i < commSize; i++) {
complete.domainOnRank[i].domains.resize(domainPerNodes[i]);
// Now domainPerNodes contain the number of elements for the descriptor.
domainPerNodes[i] *= 2 * N;
// Add to the displacement.
displs[i] = previous + domainPerNodes[i];
}
}
// Do a vector gather over the communicator
comm->all_gatherv_t(
&inputSpec.domains[0][0], 2 * N * domainPerNodes[rank], tmp_domain.get(),
domainPerNodes.get(), displs.get());
// Copy the result back in place
for (size_t i = 0; i < commSize; i++) {
for (int j = 0; j < domainPerNodes[i] / (2 * N); j++) {
std::copy(
&tmp_domain[displs[i]], &tmp_domain[displs[i] + domainPerNodes[i]],
complete.domainOnRank[i].domains[j].begin());
}
}
}
template <size_t N>
void LibLSS::mpiDomainComputeTodo(
MPI_Communication *comm, CompleteDomainSpec<N> const &inputSpec,
CompleteDomainSpec<N> const &outputSpec, DomainTodo<N> &todo) {
// Now that all nodes know everything. We may compute the I/O operations to achieve.
// i.e. which nodes are peers for the current one and which slices
// Clear up the todo list
todo.tasks.clear();
// We will build the tasks to execute between this node and the others based on the description.
// First which pieces to send
{
auto &current_domain = inputSpec.domainOnRank[comm->rank()];
for (int r = 0; r < comm->size(); r++) {
//An intersection of two hypercube is still a single hybercube
DomainTask<N> task;
auto result = current_domain.intersect(outputSpec.domainOnRank[r]);
if (!result)
continue;
task.slice = *result;
task.recv = false;
task.rankIO = r;
todo.tasks.push_back(task);
}
}
{
auto &current_domain = outputSpec.domainOnRank[comm->rank()];
for (int r = 0; r < comm->size(); r++) {
//An intersection of two hypercube is still a single hybercube
DomainTask<N> task;
auto result = current_domain.intersect(inputSpec.domainOnRank[r]);
if (!result)
continue;
task.slice = *result;
task.recv = true;
task.rankIO = r;
todo.tasks.push_back(task);
}
}
}
template <typename T, size_t N>
void mpiDomainRun(
MPI_Communication *comm, Domain<T, N> const &input_domains,
Domain<T, N> &output_domains, DomainTodo<N> const &todo) {
size_t numTasks = todo.tasks.size();
std::vector<MPICC_Request> requestList(numTasks);
std::vector<MPI_Status> statusList(numTasks);
std::vector<std::shared_ptr<TemporarySlice<T, N>>> slices(numTasks);
// Schedule all exchanges
for (int t = 0; t < todo.tasks; t++) {
auto &task = todo.tasks[t];
if (!task.recv) {
slices[t] = extractSlice(input_domains, task.slice);
requestList[t] = comm->IsendT(
slices[t]->get_array()->data(), slices[t]->get_array()->size(),
task.rankIO, 0);
} else {
slices[t] = makeTempSlice(task.slice);
requestList[t] = comm->IrecvT(
slices[t]->get_array()->data(), slices[t]->get_array()->size(),
task.rankIO, 0);
}
}
for (int t = 0; t < todo.tasks; t++) {
auto &task = todo.tasks[t];
if (task.recv) {
requestList[t].wait();
pushSlice(output_domains, task.slice);
}
}
for (int t = 0; t < todo.tasks; t++) {
if (!todo.tasks[t].recv)
requestList[t].wait();
}
}
#define FORCE(N) \
template void LibLSS::mpiDomainComputeTodo<>( \
MPI_Communication * comm, DomainSpec<N> const &inputSpec, \
DomainSpec<N> const &outputSpec, DomainTodo<N> &todo); \
template void LibLSS::computeCompleteDomainSpec<>( \
MPI_Communication *, CompleteDomainSpec<N> & complete, \
DomainSpec<N> const &inputSpec);
//FORCE(3);
// ARES TAG: num_authors = 1
// ARES TAG: name(0) = Guilhem Lavaux
// ARES TAG: year(0) = 2020
// ARES TAG: email(0) = guilhem.lavaux@iap.fr

146
libLSS/tools/domains.hpp Normal file
View file

@ -0,0 +1,146 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/domains.hpp
Copyright (C) 2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_TOOLS_MPI_DOMAIN_HPP
# define __LIBLSS_TOOLS_MPI_DOMAIN_HPP
# include <boost/multi_array.hpp>
# include <cstdint>
# include <list>
# include "libLSS/mpi/generic_mpi.hpp"
# include <boost/optional.hpp>
# include "libLSS/samplers/core/types_samplers.hpp"
namespace LibLSS {
/**
* @brief Specifies the boundary of domains
*
* @tparam N number of dimensions
*/
template <size_t N>
struct DomainSpec {
/**
* @brief Type defining an hypercube boundary.
*
* The hypercube is defined by two corners defined in an N-d space.
* Thus we need 2 * Nd elements in that array.
*/
typedef std::array<ssize_t, 2 * N> DomainLimit_t;
/**
* @brief Boundaries
*
* A domain is defined by a set of hypercube boundaries.
* The assumption is that the hypercubes are disjoint.
*/
std::vector<DomainLimit_t> domains;
boost::optional<DomainSpec<N>> intersect(DomainSpec<N> const &other) const;
};
/**
* @brief Hold the complete description of the domain on all nodes.
*
* Any sane description should cover completely the hypercube domain.
*
* @tparam N number of dimensions of the hypercube
*/
template <size_t N>
struct CompleteDomainSpec {
std::vector<DomainSpec<N>> domainOnRank;
};
/**
* @brief Specific task to exchange data on domains
*
* @tparam N
*/
template <size_t N>
struct DomainTask {
typename DomainSpec<N>::DomainLimit_t slice;
int rankIO;
bool recv;
};
/**
* @brief Todo list of tasks to complete to redistribute domain data on nodes
*
* @tparam N
*/
template <size_t N>
struct DomainTodo {
std::list<DomainTask<N>> tasks;
};
/**
* @brief Array of data for the domain
*
* @tparam T
* @tparam N
*/
template <typename T, size_t N>
using Domain = LibLSS::multi_array_ref<T, N>;
/**
* @brief Gather the complete domain specification.
*
* This function gather the description from each tasks about their domain
* specification and store the result in `complete`.
* @tparam N
* @param comm
* @param complete
* @param inputSpec
*/
template <size_t N>
void computeCompleteDomainSpec(
MPI_Communication *comm, CompleteDomainSpec<N> &complete,
DomainSpec<N> const &inputSpec);
/**
* @brief Compute the list of tasks to do to transform from one spec to another
*
* The algorithm must complete the list of tasks to go from `inputSpec` to `outputSpec`
* on each node. Provided the domain does not change the list of tasks must be
* invariant w.r.t held data.
*
* @tparam N
* @param comm
* @param inputSpec
* @param outputSpec
* @param todo
*/
template <size_t N>
void mpiDomainComputeTodo(
MPI_Communication *comm, CompleteDomainSpec<N> const &inputSpec,
CompleteDomainSpec<N> const &outputSpec, DomainTodo<N> &todo);
/**
* @brief Execute a domain redecomposition
*
* @tparam T
* @tparam N
* @param comm
* @param inputSpec
* @param inputDomain
* @param outputSpec
* @param outputDomain
*/
template <typename T, size_t N>
void mpiDomainRun(
MPI_Communication *comm, Domain<T, N> const &input_domains,
Domain<T, N> &output_domains, DomainTodo<N> const &todo);
} // namespace LibLSS
#endif
// ARES TAG: num_authors = 1
// ARES TAG: name(0) = Guilhem Lavaux
// ARES TAG: year(0) = 2020
// ARES TAG: email(0) = guilhem.lavaux@iap.fr

64
libLSS/tools/errors.hpp Normal file
View file

@ -0,0 +1,64 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/errors.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_TOOLS_ERRORS_HPP
#define __LIBLSS_TOOLS_ERRORS_HPP
#include "libLSS/tools/console.hpp"
#include <exception>
#include <string>
namespace LibLSS {
class ErrorBase: virtual public std::exception {
private:
std::string message;
ErrorBase() {}
public:
ErrorBase(const std::string& m) : message(m) {}
ErrorBase(const boost::format& m) : message(m.str()) {}
virtual ~ErrorBase() throw () {}
virtual const char *what() const throw() { return message.c_str(); }
};
#define LIBLSS_NEW_ERROR(TNAME) \
class TNAME: virtual public ErrorBase { \
public: \
TNAME(const std::string& m): ErrorBase(m) {} \
TNAME(const boost::format& m): ErrorBase(m) {} \
virtual ~TNAME() throw () {} \
};
LIBLSS_NEW_ERROR(ErrorIO);
LIBLSS_NEW_ERROR(ErrorBadState);
LIBLSS_NEW_ERROR(ErrorMemory);
LIBLSS_NEW_ERROR(ErrorParams);
LIBLSS_NEW_ERROR(ErrorBadCast);
LIBLSS_NEW_ERROR(ErrorNotImplemented);
LIBLSS_NEW_ERROR(ErrorGSL);
LIBLSS_NEW_ERROR(ErrorLoadBalance);
template<typename Error>
[[ noreturn ]] void error_helper(const std::string& msg) {
Console::instance().print<LOG_ERROR>(msg);
Console::instance().print_stack_trace();
throw Error(msg);
}
template<typename Error>
[[ noreturn ]] void error_helper(const boost::format& msg) {
Console::instance().print<LOG_ERROR>(msg.str());
Console::instance().print_stack_trace();
throw Error(msg);
}
};
#endif

View file

@ -0,0 +1,18 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/fftw_allocator.cpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#include "libLSS/tools/fftw_allocator.hpp"
using LibLSS::FFTW_Allocator;
/*#define TYPE_IMPL(T) template<> FFTW_Allocator<T>::size_type FFTW_Allocator<T>::minAllocSize = 0
TYPE_IMPL(float);
TYPE_IMPL(double);
*/

View file

@ -0,0 +1,79 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/fftw_allocator.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_FFTW_ALLOCATOR_HPP
#define __LIBLSS_FFTW_ALLOCATOR_HPP
#include "libLSS/tools/align_helper.hpp"
#include "libLSS/tools/errors.hpp"
#include <CosmoTool/fourier/fft/fftw_calls.hpp>
#include "libLSS/tools/memusage.hpp"
namespace LibLSS {
template<typename T>
class FFTW_Allocator {
public:
typedef T value_type;
typedef T *pointer;
typedef T& reference;
typedef const T* const_pointer;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
size_type minAllocSize;
template<class U> struct rebind { typedef FFTW_Allocator<U> other; };
pointer address(reference x) const { return &x; }
const_pointer address(const_reference x) const { return &x; }
FFTW_Allocator() : minAllocSize(0) {}
pointer allocate(size_type n, const void *p = 0) {
if (n > this->max_size()) {
error_helper<ErrorMemory>("Failed allocation");
}
n = std::max(n, minAllocSize) * sizeof(T);
// Console::instance().print_memory<LOG_DEBUG>(n);
pointer ret = (pointer)fftw_malloc(n);
if (ret == 0)
error_helper<ErrorMemory>(boost::format("FFTW malloc failed to allocate %d elements") % n);
report_allocation(n, ret);
return ret;
}
void deallocate(pointer p, size_type n) {
fftw_free(p);
report_free(n*sizeof(T), p);
}
size_t max_size() const throw() {
return size_t(-1) / sizeof(T);
}
void construct(pointer p, const_reference val) {
::new((void *)p) T(val);
}
void destroy(pointer p) {
p->~T();
}
};
template<typename T> inline bool operator==(const FFTW_Allocator<T>&, const FFTW_Allocator<T>&) { return true; }
template<typename T> inline bool operator!=(const FFTW_Allocator<T>&, const FFTW_Allocator<T>&) { return false; }
template<typename T>
struct DetectAlignment<FFTW_Allocator<T> > {
enum { Align = Eigen::Aligned };
};
}
#endif

View file

@ -0,0 +1,17 @@
#ifndef __LIBLSS_FUNCTION_NAME_HPP
#define __LIBLSS_FUNCTION_NAME_HPP
#include "libLSS/cconfig.h"
#ifdef __LIBLSS_PRETTY_FUNCTION_AVAILABLE
#define LIBLSS_FUNCTION __PRETTY_FUNCTION__
#else
#define LIBLSS_FUNCTION __func__
#endif
#endif
// ARES TAG: authors_num = 1
// ARES TAG: name(0) = Guilhem Lavaux
// ARES TAG: year(0) = 2018
// ARES TAG: email(0) = guilhem.lavaux@iap.fr

View file

@ -0,0 +1,28 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/fuse/healpix.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#pragma once
#ifndef __LIBLSS_TOOLS_FUSE_HEALPIX_HPP
# define __LIBLSS_TOOLS_FUSE_HEALPIX_HPP
# include <healpix_cxx/healpix_map.h>
namespace LibLSS {
namespace FuseWrapper_detail {
template <typename T>
auto fwrap(Healpix_Map<T> &a) {
boost::const_multi_array_ref<T, 1> b(&a.Map()[0], boost::extents[a.Npix()]);
return fwrap_(b, std::true_type());
}
} // namespace FuseWrapper_detail
using FuseWrapper_detail::fwrap;
} // namespace LibLSS
#endif

View file

@ -0,0 +1,241 @@
#pragma once
namespace LibLSS {
namespace FuseWrapper_detail {
template <typename T>
struct is_wrapper : std::false_type {};
template <typename Array, bool copy>
struct is_wrapper<Wrapper<Array, copy>> : std::true_type {};
// Here we builds recursively lazy binary expressions using boost::phoenix capabilities.
//
template <typename T, typename U>
using DisableIf = typename std::enable_if<!T::value, U>::type;
template <typename T, typename U>
using EnableIf = typename std::enable_if<T::value, U>::type;
template <typename A, typename = int>
struct _DetectElement;
template <typename A>
struct _DetectElement<
A, typename std::enable_if<
!is_array_like<A>::value && !is_wrapper<A>::value, int>::type> {
typedef typename std::remove_reference<A>::type element;
};
template <typename A>
struct _DetectElement<A, EnableIf<is_array_like<A>, int>> {
typedef typename std::remove_reference<A>::type::element element;
};
template <typename Array, bool copy>
struct _DetectElement<Wrapper<Array, copy>> {
typedef typename Array::element element;
};
template <typename A>
using DetectElement = typename _DetectElement<
typename std::remove_reference<A>::type>::element;
#define FWRAPPER_BUILD_UNARY_OPERATOR(op) \
template <typename Array, bool copy> \
inline auto operator op(Wrapper<Array, copy> const &self) { \
return fwrap( \
b_va_fused<typename Array::element>(op _p1, self.forward_wrap())); \
}
FWRAPPER_BUILD_UNARY_OPERATOR(-)
FWRAPPER_BUILD_UNARY_OPERATOR(!)
#undef FWRAPPER_BUILD_UNARY_OPERATOR
#define FWRAPPER_BUILD_BINARY_OPERATOR(op) \
template <typename Array, bool copy, typename ToWrap2> \
inline auto operator op( \
Wrapper<Array, copy> const &self, ToWrap2 const &other) { \
typedef typename Array::element A_t; \
typedef DetectElement<ToWrap2> O_t; \
typedef decltype(::std::declval<A_t>() op ::std::declval<O_t>()) AO_t; \
return fwrap(b_va_fused<AO_t>( \
_p1 op _p2, self.forward_wrap(), \
Wrapper<Array, copy>::fautowrap(other))); \
} \
\
template < \
typename Array, bool copy, typename NotWrap2, \
typename T = DisableIf<is_wrapper<NotWrap2>, void>> \
inline auto operator op( \
NotWrap2 const &other, Wrapper<Array, copy> const &self) { \
typedef typename Array::element A_t; \
typedef DetectElement<NotWrap2> O_t; \
typedef decltype(::std::declval<A_t>() op ::std::declval<O_t>()) AO_t; \
return fwrap(b_va_fused<AO_t>( \
_p2 op _p1, self.forward_wrap(), \
Wrapper<Array, copy>::fautowrap(other))); \
}
FWRAPPER_BUILD_BINARY_OPERATOR(+);
FWRAPPER_BUILD_BINARY_OPERATOR(-);
FWRAPPER_BUILD_BINARY_OPERATOR(/);
FWRAPPER_BUILD_BINARY_OPERATOR(*);
FWRAPPER_BUILD_BINARY_OPERATOR(^);
#undef FWRAPPER_BUILD_BINARY_OPERATOR
#define FWRAPPER_BUILD_COMPARATOR(op) \
template <typename Array, bool copy, typename Wrap2> \
inline auto operator op( \
Wrapper<Array, copy> const &self, Wrap2 const &other) \
->decltype(fwrap(b_va_fused<bool>( \
_p1 op _p2, self.forward_wrap(), \
Wrapper<Array, copy>::fautowrap(other)))) { \
return fwrap(b_va_fused<bool>( \
_p1 op _p2, self.forward_wrap(), \
Wrapper<Array, copy>::fautowrap(other))); \
} \
template <typename Array, bool copy, typename NotWrap2> \
inline auto operator op( \
NotWrap2 const &other, Wrapper<Array, copy> const &self) \
->DisableIf< \
is_wrapper<NotWrap2>, \
decltype(fwrap(b_va_fused<bool>( \
_p2 op _p1, self.forward_wrap(), \
Wrapper<Array, copy>::fautowrap(other))))> { \
return fwrap(b_va_fused<bool>( \
_p2 op _p1, self.forward_wrap(), \
Wrapper<Array, copy>::fautowrap(other))); \
}
FWRAPPER_BUILD_COMPARATOR(==)
FWRAPPER_BUILD_COMPARATOR(!=)
FWRAPPER_BUILD_COMPARATOR(>)
FWRAPPER_BUILD_COMPARATOR(<)
FWRAPPER_BUILD_COMPARATOR(<=)
FWRAPPER_BUILD_COMPARATOR(>=)
FWRAPPER_BUILD_COMPARATOR(&&)
FWRAPPER_BUILD_COMPARATOR(||)
#undef FWRAPPER_BUILD_COMPARATOR
// template<typename Array >
// Wrapper<Array const,false> fwrap(Array const& a) {
// return Wrapper<Array const,false>(a);
// }
//
template <typename Array, typename U>
Wrapper<U, true> fwrap_(Array &&a, std::true_type) {
// lvalue refs, copy mandatorily
return Wrapper<U, true>(a);
}
template <typename Array, typename U>
Wrapper<U, false> fwrap_(Array &&a, std::false_type) {
// rvalue refs, do not copy
return Wrapper<U, false>(a);
}
} // namespace FuseWrapper_detail
} // namespace LibLSS
#define FUSE_MATH_UNARY_OPERATOR(mathfunc) \
namespace LibLSS { \
namespace FuseWrapper_detail { \
template <typename T> \
struct mathfunc##_functor { \
auto operator()(T const &val) const -> decltype(std::mathfunc(val)) { \
return std::mathfunc(val); \
} \
}; \
template <typename T> \
using result_##mathfunc = \
typename std::result_of<mathfunc##_functor<T>(T)>::type; \
} \
} \
\
namespace std { \
template <typename Array, bool copy> \
auto mathfunc(LibLSS::FuseWrapper_detail::Wrapper<Array, copy> wrap) { \
typedef LibLSS::FuseWrapper_detail::result_##mathfunc< \
typename Array::element> \
Return; \
typedef LibLSS::FuseWrapper_detail::mathfunc##_functor< \
typename Array::element> \
Functor; \
return LibLSS::FuseWrapper_detail::fwrap( \
LibLSS::b_va_fused<Return>(Functor(), wrap.forward_wrap())); \
} \
}
//// std::cout << "Functor=" #mathfunc << " type = " << typeid(Return).name() << std::endl;\
//
//
#define FUSE_MATH_RESULT(mathfunc) result_##mathfunc
#define FUSE_MATH_BINARY_OPERATOR(mathfunc) \
namespace LibLSS { \
namespace FuseWrapper_detail { \
template <typename T, typename T2> \
struct mathfunc##_functor { \
T2 val2; \
\
mathfunc##_functor(T2 val2_) : val2(val2_) {} \
auto operator()(T const &val) const \
-> decltype(std::mathfunc(val, val2)) { \
return std::mathfunc(val, val2); \
} \
}; \
template <typename T, typename T2> \
using FUSE_MATH_RESULT(mathfunc) = \
typename std::result_of<mathfunc##_functor<T, T2>(T)>::type; \
} \
} \
\
namespace std { \
template < \
typename Array, bool copy, typename Other, \
typename = \
typename std::enable_if<std::is_scalar<Other>::value, void>::type> \
auto mathfunc( \
LibLSS::FuseWrapper_detail::Wrapper<Array, copy> wrap, Other other) { \
return LibLSS::FuseWrapper_detail::fwrap( \
LibLSS::b_va_fused< \
LibLSS::FuseWrapper_detail::FUSE_MATH_RESULT(mathfunc) < \
typename Array::element, \
Other>> \
(LibLSS::FuseWrapper_detail::mathfunc##_functor< \
typename Array::element, Other>(other), \
wrap.forward_wrap())); \
} \
}
FUSE_MATH_UNARY_OPERATOR(real);
FUSE_MATH_UNARY_OPERATOR(imag);
FUSE_MATH_UNARY_OPERATOR(norm);
FUSE_MATH_UNARY_OPERATOR(conj);
FUSE_MATH_UNARY_OPERATOR(exp);
FUSE_MATH_UNARY_OPERATOR(sinh);
FUSE_MATH_UNARY_OPERATOR(cosh);
FUSE_MATH_UNARY_OPERATOR(tanh);
FUSE_MATH_UNARY_OPERATOR(sin);
FUSE_MATH_UNARY_OPERATOR(cos);
FUSE_MATH_UNARY_OPERATOR(tan);
FUSE_MATH_UNARY_OPERATOR(atan);
FUSE_MATH_UNARY_OPERATOR(acos);
FUSE_MATH_UNARY_OPERATOR(asin);
FUSE_MATH_UNARY_OPERATOR(sqrt);
FUSE_MATH_UNARY_OPERATOR(log);
FUSE_MATH_UNARY_OPERATOR(log10);
FUSE_MATH_UNARY_OPERATOR(floor);
FUSE_MATH_UNARY_OPERATOR(ceil);
FUSE_MATH_UNARY_OPERATOR(abs);
FUSE_MATH_UNARY_OPERATOR(erf);
FUSE_MATH_UNARY_OPERATOR(erfc);
FUSE_MATH_BINARY_OPERATOR(pow);
FUSE_MATH_BINARY_OPERATOR(modf);
FUSE_MATH_BINARY_OPERATOR(fmod);

View file

@ -0,0 +1,488 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/fused_array.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_FUSED_ARRAY_HPP
#define __LIBLSS_FUSED_ARRAY_HPP
#include <boost/multi_array.hpp>
#include "libLSS/tools/nary_arrays.hpp"
#include "libLSS/tools/fused_assign.hpp"
#include "libLSS/tools/ref_tools.hpp"
namespace LibLSS {
namespace FUSE_detail {
namespace FusedArrayTypes {
typedef boost::multi_array_types::size_type size_type;
typedef boost::multi_array_types::index index;
} // namespace FusedArrayTypes
template <typename T, std::size_t ND, typename Allocator>
struct BoostArray {
enum { NumDims = ND };
typedef boost::multi_array<T, ND, Allocator> type;
typedef T element;
};
template <typename T, std::size_t ND>
struct BoostRefArray {
enum { NumDims = ND };
typedef boost::const_multi_array_ref<T, ND> type;
typedef T element;
};
template <typename T>
struct ConstantArray {
enum { NumDims = 0 };
typedef T type;
typedef T element;
};
struct FusedArray_base {
typedef FusedArrayTypes::size_type size_type;
typedef FusedArrayTypes::index index;
};
template <
typename ArrayTuple, typename Operation, std::size_t N,
std::size_t order, bool shaped>
struct FusedArray_view;
template <typename ArrayTuple, typename Operation, std::size_t N>
struct FusedArray_view_scalar : FusedArray_base {
static constexpr bool Shaped = ArrayTuple::Shaped;
Operation op;
const ArrayTuple &tuple;
typedef typename ArrayTuple::element element;
typedef FusedArrayTypes::size_type size_type;
typedef boost::array<index, N> subindex;
subindex idx;
inline FusedArray_view_scalar(
Operation _op, const ArrayTuple &_tuple, const subindex &i)
: op(_op), tuple(_tuple), idx(i) {}
inline const element &apply() const {
return details::apply_op(op, tuple, idx);
};
inline operator element() const {
return details::apply_op(op, tuple, idx);
}
};
// *************************** Begin of view with no shape support ****************
template <
typename ArrayTuple, typename Operation, std::size_t N,
std::size_t order>
struct FusedArray_view<ArrayTuple, Operation, N, order, false>
: FusedArray_base {
static constexpr bool Shaped = ArrayTuple::Shaped;
Operation op;
ArrayTuple tuple;
typedef boost::array<index, N> subindex;
typedef FusedArray_view<ArrayTuple, Operation, N, order - 1, false>
subview;
typedef FusedArray_view<ArrayTuple, Operation, N, 0, false> subview0;
subindex idx;
inline FusedArray_view(
Operation _op, const ArrayTuple &_tuple, const subindex &i)
: op(_op), tuple(_tuple), idx(i) {}
template <typename T_sub>
inline typename std::enable_if<
subindex::static_size == T_sub::static_size, subview0>::type
operator()(T_sub const &i) const {
return subview0(op, tuple, i);
}
template <typename T_sub>
inline typename std::enable_if<
order != N &&
(N - order + T_sub::static_size == subindex::static_size),
subview0>::type
operator()(T_sub const &i) const {
subindex j;
for (unsigned int k = 0; k < N - order; k++)
j[k] = idx[k];
for (unsigned int k = N - order; k < N; k++)
j[k] = i[k - (N - order)];
return subview0(op, tuple, j);
}
inline subview operator[](size_type i) const {
subindex idx0 = idx;
idx0[N - order] = i;
return subview(op, tuple, idx0);
}
};
template <typename ArrayTuple, typename Operation, std::size_t N>
struct FusedArray_view<ArrayTuple, Operation, N, 0, false>
: FusedArray_view_scalar<ArrayTuple, Operation, N> {
static constexpr bool Shaped = ArrayTuple::Shaped;
typedef FusedArray_view_scalar<ArrayTuple, Operation, N> super;
typedef typename super::subindex subindex;
inline FusedArray_view(
Operation _op, const ArrayTuple &_tuple, const subindex &i)
: FusedArray_view_scalar<ArrayTuple, Operation, N>(_op, _tuple, i) {}
};
// a (0,0) is never shaped.
template <typename ArrayTuple, typename Operation>
struct FusedArray_view<ArrayTuple, Operation, 0, 0, false>
: FusedArray_base {
static constexpr bool Shaped = ArrayTuple::Shaped;
typedef typename ArrayTuple::element element;
Operation op;
const ArrayTuple &tuple;
const index *indexes;
const size_type *shapes;
typedef boost::array<size_type, 0> subindex;
typedef FusedArray_view<ArrayTuple, Operation, 0, 0, false> subview;
inline FusedArray_view(
Operation _op, const ArrayTuple &_tuple, const subindex &i)
: op(_op), tuple(_tuple) {}
inline const element &apply() const {
return details::apply_op(op, tuple, subindex());
};
inline operator element() const {
return details::apply_op(op, tuple, subindex());
}
inline subview operator[](size_type i) const {
// Ignore everything
return subview(op, tuple, subindex());
}
};
// *************************** End of view with no shape support ****************
// *************************** Begin of view with shape support ****************
template <
typename ArrayTuple, typename Operation, std::size_t N,
std::size_t order>
struct FusedArray_view<ArrayTuple, Operation, N, order, true>
: FusedArray_base {
typedef typename ArrayTuple::element element;
Operation op;
ArrayTuple tuple;
static constexpr bool Shaped = ArrayTuple::Shaped;
typedef boost::array<index, N> subindex_root;
typedef boost::array<index, order> subindex;
typedef FusedArray_view<ArrayTuple, Operation, N, order - 1, true>
subview;
typedef FusedArray_view<ArrayTuple, Operation, N, 0, true> subview0;
subindex_root idx;
const index *v_index;
const size_type *v_size;
inline FusedArray_view(
Operation _op, const ArrayTuple &_tuple, const subindex_root &i,
const index *_index, const size_type *_size)
: op(_op), tuple(_tuple), idx(i), v_index(_index), v_size(_size) {}
template <typename T = element>
inline typename std::enable_if<order == 1, T>::type
operator[](size_type i) const {
subindex_root idx0 = idx;
idx0[N - order] = i;
return details::apply_op(op, tuple, idx0);
}
template <typename T = subview>
inline typename std::enable_if<order != 1, T>::type
operator[](size_type i) const {
subindex_root idx0 = idx;
idx0[N - order] = i;
return subview(op, tuple, idx0, v_index + 1, v_size + 1);
}
inline subview0 operator()(subindex_root const &i) const {
return subview0(op, tuple, i, 0, 0);
}
template <typename T = subview0>
inline typename std::enable_if<order != N, T>::type
operator()(subindex const &i) const {
subindex_root idx0 = idx;
for (unsigned int k = 0; k < order; k++)
idx0[N - order + k] = i[k];
return subview0(op, tuple, idx0, 0, 0);
}
inline const size_type *shape() const { return v_size; }
inline const index *index_bases() const { return v_index; }
};
template <typename ArrayTuple, typename Operation, std::size_t N>
struct FusedArray_view<ArrayTuple, Operation, N, 0, true>
: FusedArray_view_scalar<ArrayTuple, Operation, N> {
typedef FusedArray_view_scalar<ArrayTuple, Operation, N> super;
static constexpr bool Shaped = ArrayTuple::Shaped;
typedef typename super::subindex subindex;
typedef typename super::index index;
typedef typename super::size_type size_type;
inline FusedArray_view(
Operation _op, const ArrayTuple &_tuple, const subindex &i,
const index *_index, const size_type *_size)
: FusedArray_view_scalar<ArrayTuple, Operation, N>(_op, _tuple, i) {}
// Here we discard _index and _size as we have just a zero-dim array.
};
// *************************** End of view with shape support ****************
template <std::size_t N>
struct NumDimDecrement {
enum { value = N - 1 };
};
template <>
struct NumDimDecrement<0> {
enum { value = 0 };
};
template <
typename ArrayTuple, typename Operation,
bool ShapeTuple = ArrayTuple::Shaped>
struct FusedArray;
template <typename ArrayTuple, typename Operation>
struct FusedArray<ArrayTuple, Operation, true> : FusedArray_base {
enum { NumDims = ArrayTuple::NumDims };
enum { dimensionality = ArrayTuple::NumDims };
typedef typename ArrayTuple::element element;
static constexpr bool Shaped = true;
ArrayTuple const a;
Operation const op;
typedef boost::array<index, NumDims> subindex;
typedef FusedArray_view<
ArrayTuple, Operation, NumDims, NumDimDecrement<NumDims>::value,
ArrayTuple::Shaped>
subview;
inline FusedArray(ArrayTuple p_a, Operation p_op) : a(p_a), op(p_op) {}
inline subview operator[](FusedArrayTypes::size_type i) const {
subindex idx;
if (NumDims >= 1)
idx[0] = i;
return subview(op, a, idx, a.index_bases() + 1, a.shape() + 1);
}
inline element operator()(const subindex &idx) const {
return details::apply_op(op, a, idx);
}
inline const size_type *shape() const { return a.shape(); }
inline const index *index_bases() const { return a.index_bases(); }
inline size_type num_elements() const { return a.num_elements(); }
};
// No shape then strip all the shape related functions.
template <typename ArrayTuple, typename Operation>
struct FusedArray<ArrayTuple, Operation, false> : FusedArray_base {
enum { NumDims = ArrayTuple::NumDims };
enum { dimensionality = ArrayTuple::NumDims };
typedef typename ArrayTuple::element element;
static constexpr bool Shaped = false;
ArrayTuple const a;
Operation const op;
typedef boost::array<index, NumDims> subindex;
typedef FusedArray_view<
ArrayTuple, Operation, NumDims, NumDimDecrement<NumDims>::value,
ArrayTuple::Shaped>
subview;
inline FusedArray(ArrayTuple p_a, Operation p_op) : a(p_a), op(p_op) {}
inline subview operator[](FusedArrayTypes::size_type i) const {
subindex idx;
if (NumDims >= 1)
idx[0] = i;
return subview(op, a, idx);
}
inline element operator()(const subindex &idx) const {
return details::apply_op(op, a, idx);
}
};
// This is a new API using Variadic templates. The interface is incompatible with b_fused
// thus a new name. The operator has to be put at the front of the list to
// support an arbitrary number of arrays in the list.
// This variant can only be used if there is at least one array
// in the list, thus this specialization.
// The no-array variant is as usual using the ArrayNullTuple variant.
// This function is compatible both with temporary objects and stable objects.
// The universal reference mechanism (Array1&& etc) grabs everything and produce
// references, even to rvalues. However reference to rvalues is unsafe and likely produce
// segv in most cases for our specific use. So we strip the rvalue reference and convert
// it back to a normal copy in that case. That allows slicing, intricated fused calls etc.
template <typename A>
using remove_const_ref = typename boost::remove_const<
typename boost::remove_reference<A>::type>::type;
template <
typename Return, typename Operation, typename Array1,
typename... Arrays>
inline auto b_va_fused(Operation op, Array1 &&a1, Arrays &&... arrays) {
typedef typename boost::remove_reference<Array1>::type BareArray1;
typedef std::tuple<
typename strip_rvalue_ref<decltype(a1)>::type,
typename strip_rvalue_ref<decltype(arrays)>::type...>
btuple;
typedef ArrayTuple<
BareArray1::dimensionality, Return, btuple,
DetectShaped<typename boost::remove_const<BareArray1>::type>::Shaped>
RealTuple;
return FusedArray<RealTuple, Operation>(
RealTuple(btuple(a1, arrays...)), op);
}
template <typename Return, std::size_t N, typename Operation>
inline auto b_va_fused(Operation op) {
typedef ArrayNullTuple<N, Return, N> RealTuple;
return FusedArray<RealTuple, Operation>(RealTuple(), op);
}
template <
typename Return, typename Array1, typename Array2, typename Array3,
typename Array4, typename Operation>
inline auto b_fused(
const Array1 &a1, const Array2 &a2, const Array3 &a3, const Array4 &a4,
Operation op) {
typedef typename std::tuple<
const Array1 &, const Array2 &, const Array3 &, const Array4 &>
tuple;
typedef ArrayTuple<Array1::dimensionality, Return, tuple, true> RealTuple;
return FusedArray<RealTuple, Operation>(
RealTuple(std::forward_as_tuple(a1, a2, a3, a4)), op);
}
template <
typename Return, typename Array1, typename Array2, typename Array3,
typename Operation>
inline auto b_fused(
const Array1 &a1, const Array2 &a2, const Array3 &a3, Operation op) {
typedef
typename std::tuple<const Array1 &, const Array2 &, const Array3 &>
tuple;
typedef ArrayTuple<Array1::dimensionality, Return, tuple, true> RealTuple;
return FusedArray<RealTuple, Operation>(
RealTuple(std::forward_as_tuple(a1, a2, a3)), op);
}
template <
typename Return, typename Array1, typename Array2, typename Operation>
inline auto b_fused(const Array1 &a1, const Array2 &a2, Operation op) {
typedef typename std::tuple<const Array1 &, const Array2 &> tuple;
typedef ArrayTuple<Array1::dimensionality, Return, tuple, true> RealTuple;
return FusedArray<RealTuple, Operation>(
RealTuple(std::forward_as_tuple(a1, a2)), op);
}
template <typename Return, typename Array1, typename Operation>
inline auto b_fused(const Array1 &a1, Operation op) {
typedef typename std::tuple<const Array1 &> tuple;
typedef ArrayTuple<Array1::dimensionality, Return, tuple, true> RealTuple;
return FusedArray<RealTuple, Operation>(
RealTuple(std::forward_as_tuple(a1)), op);
}
template <typename Return, std::size_t N, typename Operation>
inline auto b_fused(Operation op) {
typedef ArrayNullTuple<N, Return> RealTuple;
return FusedArray<RealTuple, Operation>(RealTuple(), op);
}
/**
* This builds a virtual array with N-dimensions, whose elements
* are of type Return. Each access to the element call the provided
* operator, with the corresponding requested index.
* The signature should thus be "op(size_t, ...)->Return" with number of arguments
* corresponding to the number of dimensions.
* This array is "infinite", i.e. it does not have any specified bound, which may be
* required for some assignment operation.
*
* @param op a functor that must be Copy-Constructible
* @return a Copy-Constructible expression
*/
template <typename Return, std::size_t N, typename Operation>
inline auto b_fused_idx(Operation op) {
typedef ArrayNullTuple<N, Return, N> RealTuple;
return FusedArray<RealTuple, Operation>(RealTuple(), op);
}
/**
* This builds a virtual array with N-dimensions, whose elements
* are of type Return. Each access to the element call the provided
* operator, with the corresponding requested index.
* The signature should thus be "op(size_t, ...)->Return" with number of arguments
* corresponding to the number of dimensions.
* This array is finite with extents indicated by "f_extents".
*
* @param op a functor that must be Copy-Constructible.
* @param f_extents extent of the new virtual array.
* @return a Copy-Constructible expression
*/
template <
typename Return, std::size_t N, typename ExtentType, typename Operation>
inline auto b_fused_idx(Operation op, const ExtentType &f_extents) {
typedef ArrayNullTupleExtent<N, Return, N> RealTuple;
return FusedArray<RealTuple, Operation>(RealTuple(f_extents), op);
}
/**
* Same as the above, but with extents split into two lists for easier management.
*/
template <
typename Return, std::size_t N, typename ShapeList, typename IndexList,
typename Operation>
inline auto b_fused_idx(
Operation op, const ShapeList &_shapes, const IndexList &_indexes) {
typedef ArrayNullTupleExtent<N, Return, N> RealTuple;
return FusedArray<RealTuple, Operation>(RealTuple(_shapes, _indexes), op);
}
} // namespace FUSE_detail
using FUSE_detail::b_fused;
using FUSE_detail::b_fused_idx;
using FUSE_detail::b_va_fused;
} // namespace LibLSS
#endif

View file

@ -0,0 +1,186 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/fused_assign.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_FUSED_ASSIGNMENT_HPP
#define __LIBLSS_FUSED_ASSIGNMENT_HPP
#include <iostream>
#include <boost/type_traits/has_trivial_constructor.hpp>
#include "libLSS/tools/console.hpp"
// When we can get rid of AssignFunctor
//#include "libLSS/tools/phoenix_vars.hpp"
//#include <boost/phoenix/operator.hpp>
namespace LibLSS {
namespace FUSE_details {
template<std::size_t N, typename BiFunctor, bool parallel>
struct OperatorAssignment {};
template<std::size_t N, typename BiFunctor>
struct OperatorAssignment<N,BiFunctor,false> {
template<typename A, typename B>
static inline void apply(BiFunctor f, A&& a, const B& b) {
std::size_t s = a.index_bases()[0], e = a.shape()[0];
for (std::size_t i = s; i < s+e; i++) {
OperatorAssignment<N-1,BiFunctor,false> op;
op.apply(f, a[i], b[i]);
}
}
};
template<typename BiFunctor>
struct OperatorAssignment<3,BiFunctor,false> {
template<typename A, typename B>
static inline void apply(BiFunctor f, A&& a, const B& b) {
auto ib = a.index_bases();
auto sh = a.shape();
std::size_t s0 = ib[0], e0 = s0+sh[0];
std::size_t s1 = ib[1], e1 = s1+sh[1];
std::size_t s2 = ib[2], e2 = s2+sh[2];
boost::array<ssize_t, 3> i;
for (i[0] = s0; i[0] < e0; i[0]++) {
for (i[1] = s1; i[1] < e1; i[1]++) {
for (i[2] = s2; i[2] < e2; i[2]++) {
f(a(i), b(i));
}
}
}
}
};
template<std::size_t N, typename BiFunctor>
struct OperatorAssignment<N,BiFunctor,true> {
template<typename A, typename B>
static inline void apply(BiFunctor f, A&& a, const B& b) {
std::size_t s = a.index_bases()[0], e = a.shape()[0];
typename boost::remove_reference<A>::type *a_ptr = &a;
const B *b_ptr = &b;
#pragma omp parallel for schedule(static)
for (std::size_t i = s; i < s+e; i++) {
OperatorAssignment<N-1,BiFunctor,false> op;
op.apply(f, (*a_ptr)[i], (*b_ptr)[i]);
}
}
};
template<typename BiFunctor>
struct OperatorAssignment<3,BiFunctor,true> {
template<typename A, typename B>
static inline void apply(BiFunctor f, A&& a, const B& b) {
auto ib = a.index_bases();
auto sh = a.shape();
std::size_t s0 = ib[0], e0 = s0+sh[0];
std::size_t s1 = ib[1], e1 = s1+sh[1];
std::size_t s2 = ib[2], e2 = s2+sh[2];
// Console::instance().print<LOG_DEBUG>("Using optimized 3-loop collapsed omp");
Console::instance().format<LOG_DEBUG>("Using optimized 3-loop collapsed omp, %dx%dx%d -- %dx%dx%d", s0,s1,s2,e0,e1,e2);
#pragma omp parallel for collapse(3)
for (size_t i = s0; i < e0; i++) {
for (size_t j = s1; j < e1; j++) {
{
/* boost::array<ssize_t, 3> idx;
idx[0] = i;
idx[1] = j;
for (idx[2] = s2; idx[2] < e2; idx[2]++) {
f( a(idx), b(idx));
}
*/
for (size_t k = s2; k < e2; k++) {
f(a[i][j][k], b[i][j][k]);
}
}
}
}
}
};
template<typename BiFunctor>
struct OperatorAssignment<2,BiFunctor,true> {
template<typename A, typename B>
static inline void apply(BiFunctor f, A&& a, const B& b) {
std::size_t s0 = a.index_bases()[0], e0 = a.shape()[0];
std::size_t s1 = a.index_bases()[1], e1 = a.shape()[1];
#pragma omp parallel for collapse(1) schedule(static)
for (std::size_t i = s0; i < s0+e0; i++) {
// Factorize memory access for the last index.
auto stripe_a = a[i];
auto stripe_b = b[i];
for (std::size_t j = s1; j < s1+e1; j++) {
f( a[i][j], b[i][j]);
}
}
}
};
template<typename BiFunctor>
struct OperatorAssignment<0, BiFunctor, false> {
template<typename A, typename B>
static inline void apply(BiFunctor f, A& a, const B& b) {
f(a,b);
}
};
template<typename BiFunctor>
struct OperatorAssignment<0, BiFunctor, true> {
template<typename A, typename B>
static inline void apply(BiFunctor f, A& a, const B& b) {
f(a,b);
}
};
struct AssignFunctor {
template<typename T0,typename T1>
inline void operator()(T0& a, const T1& b) {
a = b;
}
};
template<typename OutArray, typename BiFunctor, typename InArray>
inline void apply_array(BiFunctor f, OutArray A, const InArray& B, bool openmp = true) {
typedef typename boost::remove_reference<OutArray>::type PureArray;
if (openmp) {
OperatorAssignment<PureArray::dimensionality,BiFunctor,true> op;
op.template apply(f, A, B);
} else {
OperatorAssignment<PureArray::dimensionality,BiFunctor,false> op;
op.template apply(f, A, B);
}
}
template<typename OutArray, typename InArray>
inline void copy_array_rv(OutArray A, const InArray& B, bool openmp=true) {
// GCC is not yet sufficiently clever for that one. The code is suboptimal (test_fused_array timing)
// auto assigner = (boost::phoenix::ref(_p1)=boost::phoenix::cref(_p2));
AssignFunctor assigner;
apply_array<OutArray, decltype(assigner), InArray>(assigner, A, B, openmp);
}
template<typename OutArray, typename InArray>
inline void copy_array(OutArray& A, const InArray& B, bool openmp=true) {
copy_array_rv<OutArray&>(A, B, openmp);
}
}
using FUSE_details::apply_array;
using FUSE_details::copy_array;
using FUSE_details::copy_array_rv;
};
#endif

148
libLSS/tools/fused_cond.hpp Normal file
View file

@ -0,0 +1,148 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/fused_cond.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef _LIBLSS_FUSED_CONDITIONAL_HPP
#define _LIBLSS_FUSED_CONDITIONAL_HPP
#include <functional>
#include "libLSS/tools/fused_array.hpp"
#include "libLSS/tools/ref_tools.hpp"
namespace LibLSS {
namespace FusedCondDetails {
using size_type = FUSE_detail::FusedArrayTypes::size_type;
using index = FUSE_detail::FusedArrayTypes::index;
template<typename T>
struct ArrayDim {
typedef typename std::remove_reference<T>::type Array;
static constexpr size_t D = Array::dimensionality;
};
template<typename T>
using ArrayIndex = boost::array<index, ArrayDim<T>::D>;
// This functor allows invokes another functor with the indexes wrapped in a boost::array
template<typename Return, typename Tuple>
struct MakeSubIndexOp {
Tuple const t;
inline MakeSubIndexOp(Tuple const&& _t) : t(_t) {}
inline MakeSubIndexOp(Tuple const& _t) : t(_t) {}
template<typename... Indexes>
inline Return operator()(Indexes... idx) const {
boost::array<index, sizeof...(Indexes)> i = { idx... };
if (std::get<0>(t)(i)) {
return std::get<1>(t)(i);
} else {
return std::get<2>(t)(i);
}
}
};
// This is an auxiliary function to make life easier
template<typename Return, typename Tuple>
inline MakeSubIndexOp<Return, Tuple>
make_subindex_op(const Tuple&& t) {
return MakeSubIndexOp<Return, Tuple>(std::forward<Tuple const>(t));
}
// Again this is an auxiliary struct that includes a fake begin/end calls.
template<typename _T>
struct wrap_ptr {
typedef typename std::remove_reference<_T>::type T;
T const *s, *e;
inline wrap_ptr(T const *_start, T const *_end)
: s(_start), e(_end) { }
inline T const *begin() const { return s; }
inline T const *end() const { return e; }
};
// This is the main instrumentation to build the lazy branching on a pair of arrays
template<typename Return, typename CondArray, typename Array1, typename Array2>
struct CondHelper {
// If we have rvalue ref we strip the reference to ensure
// the value is copied in the tuple. If we are provided
// with a ref just keep it that way to avoid copying the dense
// array.
typedef std::tuple<
typename strip_rvalue_ref<CondArray>::type,
typename strip_rvalue_ref<Array1>::type,
typename strip_rvalue_ref<Array2>::type
> btuple;
typedef typename std::remove_reference<CondArray>::type PureCond;
typedef typename std::remove_reference<Array1>::type PureArray1;
typedef typename std::remove_reference<Array2>::type PureArray2;
// This create a functor on the 3 array
static inline auto make_op(CondArray cond, Array1 a1, Array2 a2)
-> decltype(FusedCondDetails::make_subindex_op<Return, btuple>(
btuple(std::forward<CondArray>(cond), std::forward<Array1>(a1), std::forward<Array2>(a2))
)) {
return FusedCondDetails::make_subindex_op<Return, btuple>(
btuple(std::forward<CondArray>(cond), std::forward<Array1>(a1), std::forward<Array2>(a2))
);
}
// This wraps the shapes
template<typename A>
static inline
auto build_shape(A a)
-> decltype(wrap_ptr<decltype(a.shape()[0])>(a.shape(), a.shape() + ArrayDim<A>::D)) {
return wrap_ptr<decltype(a.shape()[0])>(a.shape(), a.shape() + ArrayDim<A>::D);
}
// Similar for index_bases
template<typename A>
static inline
auto build_index_base(A a)
-> decltype(wrap_ptr<decltype(a.index_bases()[0])>(a.index_bases(), a.index_bases() + ArrayDim<A>::D)) {
return wrap_ptr<decltype(a.index_bases()[0])>(a.index_bases(), a.index_bases() + ArrayDim<A>::D);
}
// This is the main builder, creates a new virtual array out of the 3-array
static inline auto make(CondArray condition, Array1 array1, Array2 array2)
-> decltype( b_fused_idx<Return, ArrayDim<CondArray>::D>(
make_op(std::forward<CondArray>(condition), std::forward<Array1>(array1), std::forward<Array2>(array2)),
build_shape(condition),
build_index_base(condition))
) {
return b_fused_idx<Return, ArrayDim<CondArray>::D>(
make_op(std::forward<CondArray>(condition), std::forward<Array1>(array1), std::forward<Array2>(array2)),
build_shape(condition),
build_index_base(condition)
);
}
};
}
// Finally a helper function that makes life real easy to the caller.
// Universal references are probed: if they are rvalue ref, then a copy is
// made, otherwise we just keep the lvalue ref.
template<typename Return, typename CondArray, typename Array1, typename Array2>
inline auto b_cond_fused(CondArray&& condition, Array1&& array1, Array2&& array2)
{
return FusedCondDetails::CondHelper<
Return,
decltype(condition),
decltype(array1),
decltype(array2)
>::make(std::forward<CondArray>(condition),std::forward<Array1>(array1),std::forward<Array2>(array2));
}
}
#endif

View file

@ -0,0 +1,184 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/fused_masked_assign.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_FUSED_MASKED_ASSIGNMENT_HPP
#define __LIBLSS_FUSED_MASKED_ASSIGNMENT_HPP
#include <boost/type_traits/has_trivial_constructor.hpp>
// When we can get rid of AssignFunctor
//#include "libLSS/tools/phoenix_vars.hpp"
//#include <boost/phoenix/operator.hpp>
namespace LibLSS {
namespace FUSE_details {
template<std::size_t N, typename BiFunctor, bool parallel>
struct MaskedOperatorAssignment {};
template<std::size_t N, typename BiFunctor>
struct MaskedOperatorAssignment<N,BiFunctor,false> {
template<typename A, typename B, typename C, typename M>
static inline void apply(BiFunctor f, A a, const B& b, const C& c, const M& m) {
std::size_t s = a.index_bases()[0], e = a.shape()[0];
for (std::size_t i = s; i < s+e; i++) {
MaskedOperatorAssignment<N-1,BiFunctor,false> op;
op.apply(f, a[i], b[i], c[i], m[i]);
}
}
};
template<std::size_t N, typename BiFunctor>
struct MaskedOperatorAssignment<N,BiFunctor,true> {
template<typename A, typename B, typename C, typename M>
static inline void apply(BiFunctor f, A&& a, const B& b, const C& c, const M& m) {
std::size_t s = a.index_bases()[0], e = a.shape()[0];
typename boost::remove_reference<A>::type *a_ptr = &a;
const B *b_ptr = &b;
const M *m_ptr = &m;
const C *c_ptr = &c;
#pragma omp parallel for schedule(static)
for (std::size_t i = s; i < s+e; i++) {
MaskedOperatorAssignment<N-1,BiFunctor,false> op;
op.apply(f, (*a_ptr)[i], (*b_ptr)[i], (*c_ptr)[i], (*m_ptr)[i]);
}
}
};
template<typename BiFunctor>
struct MaskedOperatorAssignment<3,BiFunctor,true> {
template<typename A, typename B, typename C, typename M>
static inline void apply(BiFunctor f, A&& a, const B& b, const C& c, const M& m) {
std::size_t s0 = a.index_bases()[0], e0 = a.shape()[0];
std::size_t s1 = a.index_bases()[1], e1 = a.shape()[1];
std::size_t s2 = a.index_bases()[2], e2 = a.shape()[2];
#pragma omp parallel for collapse(2) schedule(static)
for (std::size_t i = s0; i < s0+e0; i++) {
for (std::size_t j = s1; j < s1+e1; j++) {
// Factorize memory access for the last index.
// This does not work in all cases. It would
// be necessary to exchange loops to order by
// order of memory access.
// This also means we cannot collapse the 3-loops
auto stripe_a = a[i][j];
auto stripe_b = b[i][j];
auto stripe_m = m[i][j];
auto stripe_c = c[i][j];
for (std::size_t k = s2; k < s2+e2; k++) {
if (stripe_m[k])
f( stripe_a[k], stripe_b[k] );
else
f( stripe_a[k], stripe_c[k] );
}
}
}
}
};
template<typename BiFunctor>
struct MaskedOperatorAssignment<2,BiFunctor,true> {
template<typename A, typename B, typename C, typename M>
static inline void apply(BiFunctor f, A&& a, const B& b, const C& c, const M& m) {
std::size_t s0 = a.index_bases()[0], e0 = a.shape()[0];
std::size_t s1 = a.index_bases()[1], e1 = a.shape()[1];
#pragma omp parallel for collapse(1) schedule(static)
for (std::size_t i = s0; i < s0+e0; i++) {
// Factorize memory access for the last index.
auto stripe_a = a[i];
auto stripe_b = b[i];
for (std::size_t j = s1; j < s1+e1; j++) {
if (m[i][j])
f( a[i][j], b[i][j]);
else
f( a[i][j], c[i][j]);
}
}
}
};
// Explicit specialization for one to avoid the evaluation of b if it is masked
// that saves an eventual computation for slightly dumber compiler.
template<typename BiFunctor>
struct _Sub_1_MaskedOperatorAssignment {
template<typename A, typename B, typename C, typename M>
static inline void apply(BiFunctor f, A a, const B& b, const C& c, const M& m) {
std::size_t s = a.index_bases()[0], e = a.shape()[0];
for (std::size_t i = s; i < s+e; i++) {
if (m[i])
f(a[i], b[i]);
else
f(a[i], c[i]);
}
}
};
template<typename BiFunctor>
struct MaskedOperatorAssignment<1, BiFunctor, false>: _Sub_1_MaskedOperatorAssignment<BiFunctor> {};
template<typename BiFunctor>
struct MaskedOperatorAssignment<1, BiFunctor, true>: _Sub_1_MaskedOperatorAssignment<BiFunctor> {};
template<typename BiFunctor>
struct _Sub_0_MaskedOperatorAssignment {
template<typename A, typename B, typename C, typename M>
static inline void apply(BiFunctor f, A& a, const B& b, const C& c, const M& m) {
if (m)
f(a,b);
else
f(a,c);
}
};
template<typename BiFunctor>
struct MaskedOperatorAssignment<0, BiFunctor, true>: _Sub_0_MaskedOperatorAssignment<BiFunctor> {};
template<typename BiFunctor>
struct MaskedOperatorAssignment<0, BiFunctor, false>: _Sub_0_MaskedOperatorAssignment<BiFunctor> {};
struct MaskedAssignFunctor {
template<typename T0,typename T1>
inline void operator()(T0& a, const T1& b) {
a = b;
}
};
template<typename OutArray, typename BiFunctor, typename InArray, typename InArray2, typename MaskArray>
inline void apply_array_masked(
BiFunctor f, OutArray&& A, const InArray& B, const InArray2& C,
const MaskArray& mask, bool openmp = true) {
typedef typename boost::remove_reference<OutArray>::type PureArray;
if (openmp) {
MaskedOperatorAssignment<PureArray::dimensionality,BiFunctor,true> op;
op.template apply(f, A, B, C, mask);
} else {
MaskedOperatorAssignment<PureArray::dimensionality,BiFunctor,false> op;
op.template apply(f, A, B, C, mask);
}
}
template<typename OutArray, typename InArray, typename InArray2, typename MaskArray>
inline void copy_array_masked(OutArray&& A, const InArray& B, const InArray2& C,
const MaskArray& mask, bool openmp=true) {
MaskedAssignFunctor assigner;
apply_array_masked(assigner, A, B, C, mask, openmp);
}
}
using FUSE_details::apply_array_masked;
using FUSE_details::copy_array_masked;
};
#endif

View file

@ -0,0 +1,359 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/fused_reduce.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_FUSED_REDUCTION_HPP
#define __LIBLSS_FUSED_REDUCTION_HPP
#include <type_traits>
#include <boost/type_traits/has_trivial_constructor.hpp>
// This include file defines the reduction operation on
// virtual arrays as defined by fused_array.hpp
// The goal is to be able to combine virtual arrays and
// apply parallel reduction operation on it.
// A straightforward example is given in test_fuse_reduce.cpp
//
// r = LibLSS::reduce_sum(
// b_fused_idx (
// [](int i, int j)->int {return i*j;},
// extents[N][M] )
// );
//
// Which computes a \sum_{i=0,j=0}^{i=N-1,j=M-1} i*j, with openmp.
// However arrays can be folded in that.
//
// r = LibLSS::reduce_sum(
// b_fused_idx (
// [](int i, int j)->int {return i*j;},
// extents[N][M] )
// );
//
namespace LibLSS {
namespace FUSE_details {
template<std::size_t N, typename T, bool parallel>
struct OperatorReduction {};
// ======================
// MAX OPERATOR REDUCTION
template<std::size_t N, typename T, bool parallel>
struct MaxOperatorReduction {};
template<std::size_t N,typename T>
struct MaxOperatorReduction<N,T,false> {
template<typename A, typename M>
static T reduce(const A& a, const M& m) {
std::size_t s = a.index_bases()[0], e = a.shape()[0];
T r = -std::numeric_limits<T>::infinity();
for (std::size_t i = s; i < s+e; i++) {
MaxOperatorReduction<N-1,T,false> op;
r = std::max(r, op.reduce(a[i], m[i]));
}
return r;
}
};
template<std::size_t N,typename T>
struct MaxOperatorReduction<N,T,true> {
template<typename A, typename M>
static T reduce(const A& a, const M& m) {
std::size_t s = a.index_bases()[0], e = a.shape()[0];
typename boost::remove_reference<A>::type const *a_ptr = &a;
T r = -std::numeric_limits<T>::infinity();
#pragma omp parallel for reduction(max:r)
for (std::size_t i = s; i < s+e; i++) {
MaxOperatorReduction<N-1,T,false> op;
r = std::max(r, op.reduce((*a_ptr)[i], m[i]));
}
return r;
}
};
template<typename T>
struct MaxOperatorReduction<1,T,false> {
template<typename A,typename M>
static T reduce(const A& a, const M& m) {
std::size_t s = a.index_bases()[0], e = a.shape()[0];
T r = -std::numeric_limits<T>::infinity();
for (std::size_t i = s; i < s+e; i++) {
if (m[i])
r = std::max(r, T(a[i]));
}
return r;
}
};
template<typename T>
struct MaxOperatorReduction<1,T,true> {
template<typename A, typename M>
static T reduce(const A& a, const M& m) {
std::size_t s = a.index_bases()[0], e = a.shape()[0];
typename boost::remove_reference<A>::type const *a_ptr = &a;
T r = -std::numeric_limits<T>::infinity();
#pragma omp parallel for reduction(max:r)
for (std::size_t i = s; i < s+e; i++) {
if (m[i])
r = std::max(r, T((*a_ptr)[i]));
}
return r;
}
};
template<typename T, bool parallel>
struct MaxOperatorReduction<0,T,parallel> {
template<typename A>
static T reduce(const A& a) {
return a;
}
};
// ===============================
//
// ======================
// MIN OPERATOR REDUCTION
template<std::size_t N, typename T, bool parallel>
struct MinOperatorReduction {};
template<std::size_t N,typename T>
struct MinOperatorReduction<N,T,false> {
template<typename A, typename M>
static T reduce(const A& a, const M& m) {
std::size_t s = a.index_bases()[0], e = a.shape()[0];
T r = std::numeric_limits<T>::infinity();
for (std::size_t i = s; i < s+e; i++) {
MinOperatorReduction<N-1,T,false> op;
r = std::min(r, op.reduce(a[i], m[i]));
}
return r;
}
};
template<std::size_t N,typename T>
struct MinOperatorReduction<N,T,true> {
template<typename A, typename M>
static T reduce(const A& a, const M& m) {
std::size_t s = a.index_bases()[0], e = a.shape()[0];
typename boost::remove_reference<A>::type const *a_ptr = &a;
T r = std::numeric_limits<T>::infinity();
#pragma omp parallel for reduction(min:r)
for (std::size_t i = s; i < s+e; i++) {
MinOperatorReduction<N-1,T,false> op;
r = std::min(r, op.reduce((*a_ptr)[i], m[i]));
}
return r;
}
};
template<typename T>
struct MinOperatorReduction<1,T,false> {
template<typename A,typename M>
static T reduce(const A& a, const M& m) {
std::size_t s = a.index_bases()[0], e = a.shape()[0];
T r = std::numeric_limits<T>::infinity();
for (std::size_t i = s; i < s+e; i++) {
if (m[i])
r = std::min(r, T(a[i]));
}
return r;
}
};
template<typename T>
struct MinOperatorReduction<1,T,true> {
template<typename A, typename M>
static T reduce(const A& a, const M& m) {
std::size_t s = a.index_bases()[0], e = a.shape()[0];
typename boost::remove_reference<A>::type const *a_ptr = &a;
T r = std::numeric_limits<T>::infinity();
#pragma omp parallel for reduction(min:r)
for (std::size_t i = s; i < s+e; i++) {
if (m[i])
r = std::min(r, T((*a_ptr)[i]));
}
return r;
}
};
template<typename T, bool parallel>
struct MinOperatorReduction<0,T,parallel> {
template<typename A>
static T reduce(const A& a) {
return a;
}
};
// ===============================
template<std::size_t N,typename T>
struct OperatorReduction<N,T,false> {
template<typename A, typename M>
static T reduce(const A& a, const M& m) {
std::size_t s = a.index_bases()[0], e = a.shape()[0];
T r = 0;
for (std::size_t i = s; i < s+e; i++) {
OperatorReduction<N-1,T,false> op;
r += op.reduce(a[i], m[i]);
}
return r;
}
};
template<std::size_t N,typename T>
struct OperatorReduction<N,T,true> {
template<typename A, typename M>
static T reduce(const A& a, const M& m) {
std::size_t s = a.index_bases()[0], e = a.shape()[0];
typename boost::remove_reference<A>::type const *a_ptr = &a;
T r = 0;
#pragma omp parallel for reduction(+:r)
for (std::size_t i = s; i < s+e; i++) {
OperatorReduction<N-1,T,false> op;
r += op.reduce((*a_ptr)[i], m[i]);
}
return r;
}
};
template<typename T>
struct OperatorReduction<1,T,false> {
template<typename A,typename M>
static T reduce(const A& a, const M& m) {
std::size_t s = a.index_bases()[0], e = a.shape()[0];
T r = 0;
for (std::size_t i = s; i < s+e; i++) {
if (m[i])
r += a[i];
}
return r;
}
};
template<typename T>
struct OperatorReduction<1,T,true> {
template<typename A, typename M>
static T reduce(const A& a, const M& m) {
std::size_t s = a.index_bases()[0], e = a.shape()[0];
typename boost::remove_reference<A>::type const *a_ptr = &a;
T r = 0;
#pragma omp parallel for reduction(+:r)
for (std::size_t i = s; i < s+e; i++) {
if (m[i])
r += (*a_ptr)[i];
}
return r;
}
};
template<typename T>
struct OperatorReduction<0,T,false> {
template<typename A>
static T reduce(const A& a) {
return a;
}
};
template<typename T>
struct OperatorReduction<0,T,true> {
template<typename A>
static T reduce(const A& a) {
return a;
}
};
template<typename T,typename InArray,typename MaskArray>
typename std::enable_if<!std::is_same<MaskArray,bool>::value, T>::type
reduce_min(const InArray& A, const MaskArray& mask, bool openmp=true) {
typedef typename boost::remove_reference<InArray>::type PureArray;
if (openmp) {
MinOperatorReduction<InArray::dimensionality,T,true> op;
return op.template reduce(A, mask);
} else {
MinOperatorReduction<InArray::dimensionality,T,false> op;
return op.template reduce(A, mask);
}
}
template<typename T,typename InArray,typename MaskArray>
typename std::enable_if<!std::is_same<MaskArray,bool>::value, T>::type
reduce_max(const InArray& A, const MaskArray& mask, bool openmp=true) {
typedef typename boost::remove_reference<InArray>::type PureArray;
if (openmp) {
MaxOperatorReduction<InArray::dimensionality,T,true> op;
return op.template reduce(A, mask);
} else {
MaxOperatorReduction<InArray::dimensionality,T,false> op;
return op.template reduce(A, mask);
}
}
template<typename T,typename InArray,typename MaskArray>
typename std::enable_if<!std::is_same<MaskArray,bool>::value, T>::type
reduce_sum(const InArray& A, const MaskArray& mask, bool openmp=true) {
typedef typename boost::remove_reference<InArray>::type PureArray;
if (openmp) {
OperatorReduction<InArray::dimensionality,T,true> op;
return op.template reduce(A, mask);
} else {
OperatorReduction<InArray::dimensionality,T,false> op;
return op.template reduce(A, mask);
}
}
struct noMaskDummy {
template<typename... Args>
bool operator()(Args&&... t) const {
return true;
}
};
template<typename T,typename InArray>
T reduce_sum(const InArray& A, bool openmp=true) {
static_assert(DetectShaped<InArray>::Shaped, "Array has no shape");
return reduce_sum<T>(A, b_va_fused<bool,InArray::dimensionality>(noMaskDummy()), openmp);
}
template<typename T,typename InArray>
T reduce_min(const InArray& A, bool openmp=true) {
static_assert(DetectShaped<InArray>::Shaped, "Array has no shape");
return reduce_min<T>(A, b_va_fused<bool,InArray::dimensionality>(noMaskDummy()), openmp);
}
template<typename T,typename InArray>
T reduce_max(const InArray& A, bool openmp=true) {
static_assert(DetectShaped<InArray>::Shaped, "Array has no shape");
return reduce_max<T>(A, b_va_fused<bool,InArray::dimensionality>(noMaskDummy()), openmp);
}
}
using FUSE_details::reduce_sum;
using FUSE_details::reduce_min;
using FUSE_details::reduce_max;
};
#endif

View file

@ -0,0 +1,321 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/fusewrapper.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_FUSEWRAPPER_HPP
#define __LIBLSS_FUSEWRAPPER_HPP
#pragma once
#include <type_traits>
#include <utility>
#include <boost/core/enable_if.hpp>
#include <boost/type_traits/is_scalar.hpp>
#include "libLSS/tools/fused_array.hpp"
#include "libLSS/tools/fused_assign.hpp"
#include "libLSS/tools/phoenix_vars.hpp"
#include <boost/phoenix/operator.hpp>
#include "libLSS/tools/fused_reduce.hpp"
#include "libLSS/tools/fused_cond.hpp"
#include <boost/tti/has_type.hpp>
#include <boost/tti/has_member_function.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/logical.hpp>
#include "libLSS/tools/array_concepts.hpp"
#include <CosmoTool/algo.hpp>
#include "libLSS/tools/uninitialized_type.hpp"
namespace LibLSS {
namespace FuseWrapper_detail {
using LibLSS::array_concepts::is_array_like;
using LibLSS::array_concepts::is_array_storage;
using LibLSS::array_concepts::is_array_sub;
using LibLSS::array_concepts::is_array_view;
using LibLSS::array_concepts::is_callable;
template <typename Array, bool copy>
struct Wrapper;
template <
typename Array,
typename U = typename std::remove_reference<Array>::type>
Wrapper<U, true> fwrap_(Array &&a, std::true_type);
template <
typename Array,
typename U = typename std::remove_reference<Array>::type>
Wrapper<U, false> fwrap_(Array &&a, std::false_type);
template <typename T, size_t Nd, typename Allocator>
auto fwrap(UninitializedAllocation<T, Nd, Allocator> &a) {
return fwrap_(a.get_array(), std::false_type());
}
template <
typename Array,
typename = typename std::enable_if<
is_array_like<typename std::remove_reference<Array>::type>::value,
void>::type>
auto fwrap(Array &&a) {
return fwrap_(
std::forward<Array>(a), std::is_rvalue_reference<Array &&>());
}
template <typename A, bool copy>
struct CopyType;
template <typename A>
struct CopyType<A, true> {
typedef A type;
typedef A const_type;
};
template <typename A>
struct CopyType<A, false> {
typedef A &type;
typedef A const &const_type;
};
template <typename T>
struct constantFunctor {
T value;
constantFunctor(T v) : value(v) {}
template <typename... Args>
T const &operator()(Args &&... a) const {
return value;
}
};
template <typename F>
struct singleFunctor {
typedef std::result_of<F()> Result;
F f;
singleFunctor() {}
singleFunctor(F f_) : f(f_) {}
template <typename... Args>
Result operator()(Args &&... a) const {
return f();
}
};
template <typename Array, bool copy>
struct Wrapper {
typedef Array array_t;
typedef typename CopyType<Array, copy>::type WType;
typedef typename CopyType<Array, copy>::const_type WTypeConst;
WType a;
bool parallel;
explicit Wrapper(Array &a_) : a(a_), parallel(true) {}
explicit Wrapper(Array &&a_) : a(a_), parallel(true) {}
template <typename T, size_t Nd, typename Allocator>
static inline auto
fautowrap(UninitializedAllocation<T, Nd, Allocator> &a) {
return Wrapper(a.get_array());
}
Array &operator*() { return a; }
Array const &operator*() const { return a; }
Array *operator->() { return &a; }
Array const *operator->() const { return &a; }
Wrapper no_parallel() const {
auto b = *this;
b.parallel = false;
return b;
}
// This auxiliary function creates a perfect
// forwarding of the required encapsulation of the array.
// If a copy is needed to ensure the object is long lived
// enough, then WType is the full Array object.
// Otherwise, it will be a reference on the original object.
// Thus a receiving function will get either a lvalue-ref or an rvalue-ref
// depending on the need.
WType forward_wrap() { return a; }
WTypeConst forward_wrap() const { return a; }
template <typename Array2, bool B2>
static inline typename Wrapper<Array2, B2>::WType const
fautowrap(const Wrapper<Array2, B2> &other) {
return other.a;
}
template <typename Array2, typename U = Array2>
static inline
typename std::enable_if<is_array_like<Array2>::value, U>::type const &
fautowrap(Array2 const &other) {
return other;
}
// Intel 17.2 C++ compiler crashes without those
template <typename ValueType, size_t N>
struct wrapconst {
typedef decltype(b_va_fused<ValueType, N>(
constantFunctor<ValueType>(ValueType(0)))) Result;
static inline Result wrap(ValueType value) {
return (b_va_fused<ValueType, N>(constantFunctor<ValueType>(value)));
}
};
template <typename ValueType, size_t N, typename Operator>
struct wrapfunc {
typedef decltype(
b_va_fused<ValueType, N>(singleFunctor<Operator>())) Result;
static inline Result wrap(Operator op) {
return (b_va_fused<ValueType, N>(singleFunctor<Operator>(op)));
}
};
template <typename ValueType, typename U = ValueType>
static inline typename std::enable_if<
(boost::is_arithmetic<ValueType>::value ||
array_concepts::is_complex_type<ValueType>::value) &&
is_array_like<Array>::value,
wrapconst<U, Array::dimensionality>>::type::Result
fautowrap(ValueType other) {
return wrapconst<ValueType, Array::dimensionality>::wrap(other);
}
template <typename F, typename U = F>
static inline typename boost::enable_if<
is_callable<F>,
wrapfunc<typename Array::element, Array::dimensionality, U>>::type::
Result
fautowrap(F other) {
return wrapfunc<
typename Array::element, Array::dimensionality, F>::wrap(other);
}
typename Array::element sum() const {
return LibLSS::reduce_sum<typename Array::element>(a, parallel);
}
typename Array::element min() const {
return LibLSS::reduce_min<typename Array::element>(a, parallel);
}
typename Array::element max() const {
return LibLSS::reduce_max<typename Array::element>(a, parallel);
}
template <typename ArrayTo>
Wrapper<Array, copy> const &copy_to(ArrayTo &to) const {
LibLSS::copy_array(to, a, to.parallel);
return *this;
}
Wrapper<Array, copy> &operator=(Wrapper<Array, copy> &other) {
return operator=((Wrapper<Array, copy> const &)other);
}
template <typename Wrap2, typename A = Array>
inline Wrapper<Array, copy> &operator=(Wrap2 const &other) {
static_assert(
is_array_storage<A>::value || is_array_view<A>::value ||
is_array_sub<A>::value,
"The wrapped array is likely a pure expression. Impossible to "
"assign.");
LibLSS::copy_array(a, fautowrap(other), parallel);
return *this;
}
};
} // namespace FuseWrapper_detail
} // namespace LibLSS
#include "libLSS/tools/fuse/operators.hpp"
namespace LibLSS {
using FuseWrapper_detail::fwrap;
using FuseWrapper_detail::is_wrapper;
template <
size_t exponent, typename T,
typename = typename boost::enable_if<
boost::is_scalar<typename boost::remove_reference<T>::type>>::type>
auto ipow(T &&t) {
return CosmoTool::spower<
exponent, typename boost::remove_reference<T>::type>(t);
}
template <size_t N, typename T>
struct _spower_helper {
typedef decltype(CosmoTool::spower<N, T>(T(0))) Return;
inline Return operator()(T a) const { return CosmoTool::spower<N, T>(a); }
};
template <size_t exponent, typename Array, bool copy>
auto ipow(LibLSS::FuseWrapper_detail::Wrapper<Array, copy> wrap) {
return fwrap(
b_va_fused<
typename _spower_helper<exponent, typename Array::element>::Return>(
_spower_helper<exponent, typename Array::element>(),
std::forward<typename LibLSS::FuseWrapper_detail::Wrapper<
Array, copy>::WType>(wrap.a)));
}
template <typename T, size_t Nd, typename ExtentType>
auto ones(ExtentType e) {
return fwrap(b_fused_idx<T, Nd>([](auto... x) { return T(1); }, e));
}
template <typename T, size_t Nd, typename ExtentType>
auto zero(ExtentType e) {
return fwrap(b_fused_idx<T, Nd>([](auto... x) { return T(0); }, e));
}
template <typename T, size_t Nd, typename ExtentType>
auto constant(T value, ExtentType e) {
return fwrap(
b_fused_idx<T, Nd>([value](auto... x) { return T(value); }, e));
}
template <typename Array1, typename Array2, bool copy1, bool copy2>
auto make_complex(
LibLSS::FuseWrapper_detail::Wrapper<Array1, copy1> wrap_re,
LibLSS::FuseWrapper_detail::Wrapper<Array2, copy2> wrap_im) {
static_assert(
std::is_same<typename Array1::element, typename Array2::element>::value,
"The two array have different base type");
typedef typename Array1::element element;
return fwrap(b_va_fused<std::complex<element>>(
[](element const &a, element const &b) {
return std::complex<element>(a, b);
},
std::forward<
typename LibLSS::FuseWrapper_detail::Wrapper<Array1, copy1>::WType>(
wrap_re.a),
std::forward<
typename LibLSS::FuseWrapper_detail::Wrapper<Array2, copy2>::WType>(
wrap_im.a)));
}
template <
typename ArrayM, bool copyM, typename Array1, bool copy1, typename Array2,
bool copy2>
auto mask(
LibLSS::FuseWrapper_detail::Wrapper<ArrayM, copyM> wrap_mask,
LibLSS::FuseWrapper_detail::Wrapper<Array1, copy1> array1,
LibLSS::FuseWrapper_detail::Wrapper<Array2, copy2> array2) {
return fwrap(b_cond_fused<typename Array1::element>(
wrap_mask.forward_wrap(), array1.forward_wrap(),
array2.forward_wrap()));
}
} // namespace LibLSS
#endif

View file

@ -0,0 +1,49 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/gslIntegrate.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_INTEGRATE_HPP
#define __LIBLSS_INTEGRATE_HPP
#include <gsl/gsl_integration.h>
#include "libLSS/tools/gsl_error.hpp"
namespace LibLSS {
namespace details {
template<typename FunT>
double gslSpecialFunction(double x, void *param)
{
const FunT *f = (const FunT *)param;
return (*f)(x);
}
}
template<typename FunT>
double gslIntegrate(const FunT& v, double a, double b, double prec, int NPTS = 1024)
{
gsl_integration_workspace *w = gsl_integration_workspace_alloc(NPTS);
gsl_function f;
double result;
double abserr;
f.function = &details::gslSpecialFunction<FunT>;
f.params = (void*)&v;
gsl_integration_qag(&f, a, b, prec, 0, NPTS, GSL_INTEG_GAUSS61,
w, &result, &abserr);
gsl_integration_workspace_free(w);
return result;
}
}
#endif

View file

@ -0,0 +1,49 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/gsl_error.cpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#include "libLSS/tools/console.hpp"
#include "libLSS/tools/static_init.hpp"
#include <boost/format.hpp>
#include "libLSS/tools/gsl_error.hpp"
#include <gsl/gsl_errno.h>
using namespace LibLSS;
namespace {
bool s_gsl_error_fatal = true;
void console_errorPrinter(const char *reason, const char *file, int line, int gsl_errno)
{
ConsoleContext<LOG_ERROR> ctx("GSL error");
ctx.print(boost::format("An error has occurred at %1%:%2%, the given reason is \"%3%\"")
% file % line % reason);
if (s_gsl_error_fatal) {
ctx.print("Aborting run");
MPI_Communication::instance()->abort();
}
}
void initializeGSL_Error() {
Console::instance().print<LOG_DEBUG>("Initialize GSL error reporter");
gsl_set_error_handler (console_errorPrinter);
}
// After console initialization.
RegisterStaticInit reg(initializeGSL_Error, 1);
}
void LibLSS::setGSLFatality(bool on ) {
s_gsl_error_fatal = on;
}
AUTO_REGISTRATOR_IMPL(GSL_Error);

View file

@ -0,0 +1,21 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/gsl_error.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_GSL_ERROR_HPP
#define __LIBLSS_GSL_ERROR_HPP
#include "libLSS/tools/static_auto.hpp"
namespace LibLSS {
void setGSLFatality(bool on);
}
AUTO_REGISTRATOR_DECL(GSL_Error);
#endif

View file

@ -0,0 +1,133 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/hdf5_buffered_write.hpp
Copyright (C) 2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_HDF5_BUFFERED_WRITE_HPP
# define __LIBLSS_HDF5_BUFFERED_WRITE_HPP
# include <H5Cpp.h>
# include <functional>
# include <CosmoTool/hdf5_array.hpp>
# include "libLSS/tools/hdf5_type.hpp"
# include "libLSS/tools/array_tools.hpp"
# include "libLSS/tools/fusewrapper.hpp"
namespace LibLSS {
typedef std::function<void(size_t)> ProgressFunction;
namespace hdf5_buffer_detail {
template<typename B, typename D>
typename std::enable_if<B::dimensionality != 1,void>::type buffer_it(B& b, D& d, std::vector<hsize_t>& memdims_local, std::vector<hsize_t>& offsets_local) {
typedef boost::multi_array_types::index_range i_range;
auto inds1 = array::make_star_indices<B::dimensionality-1>(boost::indices[i_range(0, memdims_local[0])]);
auto inds2 = array::make_star_indices<B::dimensionality-1>(boost::indices[i_range(offsets_local[0], offsets_local[0]+memdims_local[0])]);
fwrap(b[inds1]) = fwrap(d[inds2]);
// for (int a = 0; a < memdims_local[0]; a++)
// fwrap(b[a]) = fwrap(d[a+offsets_local[0]]);
}
template<typename B, typename D>
typename std::enable_if<B::dimensionality == 1,void>::type buffer_it(B& b, D& d, std::vector<hsize_t>& memdims_local, std::vector<hsize_t>& offsets_local) {
size_t N0 = memdims_local[0];
#pragma omp parallel for
for (int a = 0; a < N0; a++)
b[a] = d[a+offsets_local[0]];
}
}
template <typename ArrayType, typename hdf5_data_type>
void hdf5_write_buffered_array(
H5_CommonFileGroup &fg, const std::string &data_set_name,
const ArrayType &data, const hdf5_data_type &datatype,
const std::vector<hsize_t> &dimensions, bool doCreate = true,
bool useBases = false, ProgressFunction progress = ProgressFunction()) {
LIBLSS_AUTO_DEBUG_CONTEXT(ctx);
typedef typename ArrayType::element element;
size_t const NUM_ROWS = 1024*1000*100;
std::vector<hsize_t> memdims(
data.shape(), data.shape() + data.num_dimensions());
std::vector<hsize_t> offsets(
data.index_bases(), data.index_bases() + data.num_dimensions());
H5::DataSpace dataspace(dimensions.size(), dimensions.data());
H5::DataSpace memspace(memdims.size(), memdims.data());
std::vector<hsize_t> offsets_zero;
hsize_t one_block, block_size;
H5::DataSet dataset;
if (doCreate)
dataset = fg.createDataSet(data_set_name, datatype, dataspace);
else
dataset = fg.openDataSet(data_set_name);
offsets_zero.resize(data.num_dimensions());
offsets_zero = offsets;
offsets_zero[0] = 0;
// Compute equivalent of a few MB of memory
one_block = array::product(memdims.begin() + 1, memdims.end());
block_size = one_block * NUM_ROWS;
std::unique_ptr<element[]> p_buffer(new element[block_size]);
boost::multi_array_ref<element, ArrayType::dimensionality> buffer(
p_buffer.get(),
array::make_extent<ArrayType::dimensionality - 1>(
offsets_zero.data() + 1, &memdims[1], boost::extents[NUM_ROWS]));
std::vector<hsize_t> offsets_local = offsets;
std::vector<hsize_t> memdims_local = memdims;
size_t block = 0;
while (block < memdims[0]) {
if (progress)
progress(block);
memdims_local[0] = std::min(NUM_ROWS, size_t(memdims[0] - block));
H5::DataSpace memspace_local(memdims_local.size(), memdims_local.data());
hdf5_buffer_detail::buffer_it(buffer, data, memdims_local, offsets_local);
dataspace.selectHyperslab(
H5S_SELECT_SET, memdims_local.data(), offsets_local.data());
dataset.write(p_buffer.get(), datatype, memspace_local, dataspace);
block += memdims_local[0];
offsets_local[0] += memdims_local[0];
}
}
template <typename ArrayType, typename hdf5_data_type>
void hdf5_write_buffered_array(
H5_CommonFileGroup &fg, const std::string &data_set_name,
const ArrayType &data, const hdf5_data_type &datatype,
bool doCreate = true, bool useBases = false,
ProgressFunction progress = ProgressFunction()) {
std::vector<hsize_t> dimensions(
data.shape(), data.shape() + data.num_dimensions());
hdf5_write_buffered_array(
fg, data_set_name, data, datatype, dimensions, doCreate, useBases,
progress);
}
template <typename ArrayType>
void hdf5_write_buffered_array(
H5_CommonFileGroup &fg, const std::string &data_set_name,
const ArrayType &data, bool doCreate = true, bool useBases = false,
ProgressFunction progress = ProgressFunction()) {
CosmoTool::get_hdf5_data_type<typename ArrayType::element> hdf_data_type;
hdf5_write_buffered_array(
fg, data_set_name, data, hdf_data_type.type(), doCreate, useBases,
progress);
}
}; // namespace LibLSS
#endif
// ARES TAG: authors_num = 1
// ARES TAG: name(0) = Guilhem Lavaux
// ARES TAG: year(0) = 2020
// ARES TAG: email(0) = guilhem.lavaux@iap.fr

View file

@ -0,0 +1,62 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/hdf5_error.cpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#include <H5Cpp.h>
#include "libLSS/tools/console.hpp"
#include "libLSS/tools/static_init.hpp"
#include <boost/format.hpp>
#include "libLSS/tools/hdf5_error.hpp"
using namespace LibLSS;
namespace {
herr_t console_h5e_walker(unsigned int n, const H5E_error_t *err_desc, void *client_data)
{
const char *maj_str = NULL;
const char *min_str = NULL;
const int indent = 2;
ConsoleContext<LOG_ERROR> *ctx = (ConsoleContext<LOG_ERROR> *)client_data;
/* Check arguments */
assert (err_desc);
/* Get descriptions for the major and minor error numbers */
maj_str = H5Eget_major (err_desc->maj_num);
min_str = H5Eget_minor (err_desc->min_num);
/* Print error message */
ctx->print(boost::format("#%03d: %s line %u in %s(): %s") % n
% err_desc->file_name
% err_desc->line
% err_desc->func_name
% err_desc->desc);
return 0;
}
herr_t console_errorPrinter(hid_t, void *cdata)
{
ConsoleContext<LOG_ERROR> ctx("HDF5 error");
H5::Exception::walkErrorStack (H5E_WALK_DOWNWARD, console_h5e_walker, &ctx);
return 0;
}
void initializeHDF5() {
H5E_auto2_t func = console_errorPrinter;
H5::Exception::setAutoPrint(func, 0);
}
// After console initialization.
RegisterStaticInit reg(initializeHDF5, 1);
}
AUTO_REGISTRATOR_IMPL(HDF5);

View file

@ -0,0 +1,17 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/hdf5_error.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_HDF5_ERROR_HPP
#define __LIBLSS_HDF5_ERROR_HPP
#include "libLSS/tools/static_auto.hpp"
AUTO_REGISTRATOR_DECL(HDF5);
#endif

View file

@ -0,0 +1,61 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/hdf5_scalar.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_HDF5_SCALAR_HPP
#define __LIBLSS_HDF5_SCALAR_HPP
#include <boost/format.hpp>
#include <CosmoTool/hdf5_array.hpp>
#include "libLSS/tools/console.hpp"
#include "libLSS/tools/errors.hpp"
#include "libLSS/tools/hdf5_type.hpp"
namespace LibLSS {
template<typename T>
void hdf5_save_scalar(H5_CommonFileGroup& fg, const std::string& name, const T& scalar) {
hsize_t d = 1;
using CosmoTool::get_hdf5_data_type;
H5::DataSpace dataspace(1, &d);
H5::DataSet dataset = fg.createDataSet(name, get_hdf5_data_type<T>::type(), dataspace);
dataset.write(&scalar, get_hdf5_data_type<T>::type());
}
namespace details {
namespace {
void scalar_error(const std::string& name) {
error_helper<ErrorIO>(boost::format("Scalar '%s' has wrong dimensions in file") % name);
}
}
}
template<typename T>
T hdf5_load_scalar(H5_CommonFileGroup& fg, const std::string& name) {
using CosmoTool::get_hdf5_data_type;
H5::DataSet dataset = fg.openDataSet(name);
H5::DataSpace dataspace = dataset.getSpace();
hsize_t d;
if (dataspace.getSimpleExtentNdims() != 1)
details::scalar_error(name);
dataspace.getSimpleExtentDims(&d);
if (d != 1)
details::scalar_error(name);
T data;
dataset.read(&data, get_hdf5_data_type<T>::type());
return data;
}
}
#endif

View file

@ -0,0 +1,25 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/hdf5_type.hpp
Copyright (C) 2019 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_HDF5_TYPE_HPP
#define __LIBLSS_HDF5_TYPE_HPP
#include <CosmoTool/hdf5_array.hpp>
namespace LibLSS
{
using CosmoTool::H5_CommonFileGroup;
}
#endif
// ARES TAG: authors_num = 2
// ARES TAG: name(0) = Guilhem Lavaux
// ARES TAG: email(0) = guilhem.lavaux@iap.fr
// ARES TAG: year(0) = 2019

View file

@ -0,0 +1,66 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/is_stl_container.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
/* Taken from
* https://stackoverflow.com/questions/9407367/determine-if-a-type-is-an-stl-container-at-compile-time
* Mike Kinghan (May 1st 2013)
*/
#ifndef IS_STL_CONTAINER_LIKE_HPP
#define IS_STL_CONTAINER_LIKE_HPP
#include <type_traits>
namespace LibLSS {
template<typename T>
struct is_stl_container_like
{
typedef typename std::remove_const<T>::type test_type;
template<typename A>
static constexpr bool test(
A * pt,
A const * cpt = nullptr,
decltype(pt->begin()) * = nullptr,
decltype(pt->end()) * = nullptr,
decltype(cpt->begin()) * = nullptr,
decltype(cpt->end()) * = nullptr,
typename A::iterator * pi = nullptr,
typename A::const_iterator * pci = nullptr,
typename A::value_type * pv = nullptr) {
typedef typename A::iterator iterator;
typedef typename A::const_iterator const_iterator;
typedef typename A::value_type value_type;
return std::is_same<decltype(pt->begin()),iterator>::value &&
std::is_same<decltype(pt->end()),iterator>::value &&
std::is_same<decltype(cpt->begin()),const_iterator>::value &&
std::is_same<decltype(cpt->end()),const_iterator>::value &&
(
std::is_same<decltype(**pi),value_type &>::value ||
std::is_same<decltype(**pi),value_type const &>::value
) &&
std::is_same<decltype(**pci),value_type const &>::value;
}
template<typename A>
static constexpr bool test(...) {
return false;
}
static const bool value = test<test_type>(nullptr);
};
}
#endif

View file

@ -0,0 +1,97 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/itertools.hpp
Copyright (C) 2019 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_ITERTOOLS_HPP
#define __LIBLSS_ITERTOOLS_HPP
#include <boost/iterator/counting_iterator.hpp>
#include <boost/iterator/zip_iterator.hpp>
#include <boost/tuple/tuple.hpp>
namespace LibLSS {
namespace itertools {
template <typename I>
auto enumerate_base(size_t i, I j) -> decltype(boost::make_zip_iterator(
boost::make_tuple(boost::counting_iterator<size_t>(i), j))) {
return boost::make_zip_iterator(
boost::make_tuple(boost::counting_iterator<size_t>(i), j));
}
template <typename T>
struct Enumerate {
public:
T const &t;
Enumerate(T const &t_) : t(t_) {}
auto begin() const -> decltype(enumerate_base(0, t.begin())) {
return enumerate_base(0, t.begin());
}
auto end() const -> decltype(enumerate_base(t.size(), t.end())) {
return enumerate_base(t.size(), t.end());
}
};
struct Range {
size_t i0, i1;
Range(size_t i0_, size_t i1_) : i0(i0_), i1(i1_) {}
auto begin() const -> decltype(boost::counting_iterator<size_t>(i0)) {
return boost::counting_iterator<size_t>(i0);
}
auto end() const -> decltype(boost::counting_iterator<size_t>(i1)) {
return boost::counting_iterator<size_t>(i1);
}
};
/**
* This function creates a pseudo container (range container) over
* which one can iterate upon.
* A typical use is:
* <code>
* for (size_t id: range(0, N)) { blabla; }
* </code>
*
* @param i0 start of the range
* @param i1 end of the range
* @return a container
*/
inline Range range(size_t i0, size_t i1) { return Range(i0, i1); }
/**
* This function creates a pseudo container made of a zip iterator binding
* a range and an iterator over the provided container.
* A typical use is:
* <code>
* for (auto x: enumerate(some_vector)) {
* size_t id = x.get<0>();
* // Some stuff
* the_type_in_vector const& v = x.get<1>();
* }
* </code>
*
* @param t a container
* @return a container with an enumeration
*/
template <typename T>
inline Enumerate<T> enumerate(T const &t) {
return Enumerate<T>(t);
}
} // namespace itertools
} // namespace LibLSS
#endif
// ARES TAG: authors_num = 1
// ARES TAG: name(0) = Guilhem Lavaux
// ARES TAG: email(0) = guilhem.lavaux@iap.fr
// ARES TAG: year(0) = 2019

View file

@ -0,0 +1,86 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/log_traits.cpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#include <boost/preprocessor/seq/for_each.hpp>
#include "log_traits.hpp"
#include "libLSS/tools/color_mod.hpp"
#include "libLSS/tools/static_init.hpp"
#include "libLSS/tools/static_auto.hpp"
#include <iostream>
using namespace LibLSS;
using namespace std;
#define IMPLEMENT_LOG_TRAIT(T) \
string LibLSS::T::prefix = ""; \
string LibLSS::T::prefix_c = ""; \
ostream *LibLSS::T::os[LibLSS::T::numOutput];
#define DO_IMPLEMENT(r, DATA, E) IMPLEMENT_LOG_TRAIT(E)
#if !defined(DOXYGEN_SHOULD_SKIP_THIS)
BOOST_PP_SEQ_FOR_EACH(DO_IMPLEMENT, DATA,
(LOG_STD)
(LOG_WARNING)
(LOG_ERROR)
(LOG_INFO)
(LOG_DEBUG)
(LOG_VERBOSE)
(LOG_INFO_SINGLE)
);
#endif
bool LibLSS::QUIET_CONSOLE_START = false;
namespace {
void initializeConsole()
{
using namespace LibLSS::Color;
if (!LibLSS::QUIET_CONSOLE_START)
cout << "Initializing console." << endl;
LOG_STD::prefix = "[STD ] ";
LOG_STD::prefix_c = "[STD ] ";
LOG_STD::os[0] = &std::cout;
LOG_WARNING::prefix = "[WARNING] ";
LOG_WARNING::prefix_c = "[" + fg(MAGENTA,"WARNING",BRIGHT) + "] ";
LOG_WARNING::os[0] = &std::cout;
LOG_WARNING::os[1] = &std::cerr;
LOG_ERROR::prefix = "[ERROR ] ";
LOG_ERROR::prefix_c = "[" + fg(RED, "ERROR",BRIGHT) +" ] ";
LOG_ERROR::os[0] = &std::cerr;
LOG_INFO::prefix = "[INFO ] ";
LOG_INFO::prefix_c= "[" + bg(BLACK,fg(YELLOW, "INFO", BRIGHT)) +" ] ";
LOG_INFO::os[0] = &std::cout;
LOG_INFO_SINGLE::prefix = "[INFO S ] ";
LOG_INFO_SINGLE::prefix_c= "[" + bg(BLACK,fg(YELLOW, "INFO S", BRIGHT)) +" ] ";
LOG_INFO_SINGLE::os[0] = &std::cout;
LOG_DEBUG::prefix = "[DEBUG ] ";
LOG_DEBUG::prefix_c= "[DEBUG ] ";
LOG_DEBUG::os[0] = &std::cout;
LOG_VERBOSE::prefix = "[VERBOSE] ";
LOG_VERBOSE::prefix_c= "[" + bg(BLACK,fg(CYAN, "VERBOSE", BRIGHT)) + "] ";
LOG_VERBOSE::os[0] = &std::cout;
if (LibLSS::QUIET_CONSOLE_START)
Console::instance().setVerboseLevel<LOG_ERROR>();
}
RegisterStaticInit reg(initializeConsole, 0);
}
AUTO_REGISTRATOR_IMPL(LogTraits);

View file

@ -0,0 +1,90 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/log_traits.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_LOG_TRAITS_HPP
#define __LIBLSS_LOG_TRAITS_HPP
#include <string>
#include <iostream>
#include "libLSS/tools/static_auto.hpp"
namespace LibLSS {
struct LOG_STD {
static const int verboseLevel = 1;
static const bool mainRankOnly = true;
static const int numOutput = 1;
static std::string prefix;
static std::string prefix_c;
static std::ostream *os[numOutput];
};
struct LOG_WARNING {
static const int verboseLevel = 1;
static const bool mainRankOnly = false;
static const int numOutput = 2;
static std::string prefix;
static std::string prefix_c;
static std::ostream *os[numOutput];
};
struct LOG_ERROR {
static const int verboseLevel = 0;
static const bool mainRankOnly = false;
static const int numOutput = 1;
static std::string prefix;
static std::string prefix_c;
static std::ostream *os[numOutput];
};
struct LOG_INFO {
static const int verboseLevel = 2;
static const bool mainRankOnly = false;
static const int numOutput = 1;
static std::string prefix;
static std::string prefix_c;
static std::ostream *os[numOutput];
};
struct LOG_INFO_SINGLE {
static const int verboseLevel = 2;
static const bool mainRankOnly = true;
static const int numOutput = 1;
static std::string prefix;
static std::string prefix_c;
static std::ostream *os[numOutput];
};
struct LOG_VERBOSE {
static const int verboseLevel = 3;
static const bool mainRankOnly = false;
static const int numOutput = 1;
static std::string prefix;
static std::string prefix_c;
static std::ostream *os[numOutput];
};
struct LOG_DEBUG {
static const int verboseLevel = 4;
static const bool mainRankOnly = false;
static const int numOutput = 1;
static std::string prefix;
static std::string prefix_c;
static std::ostream *os[numOutput];
};
typedef LOG_DEBUG DEFAULT_LOG_LEVEL;
extern bool QUIET_CONSOLE_START;
};
AUTO_REGISTRATOR_DECL(LogTraits);
#endif

106
libLSS/tools/memusage.cpp Normal file
View file

@ -0,0 +1,106 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/memusage.cpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#include <sys/types.h>
#include <iostream>
#include <string>
#include <map>
#include <fstream>
#include <iomanip>
#include "libLSS/tools/console.hpp"
#include "libLSS/tools/memusage.hpp"
#include "libLSS/tools/static_init.hpp"
#include "libLSS/tools/static_auto.hpp"
#include <boost/format.hpp>
using boost::format;
using namespace LibLSS;
static bool report_done = true;
namespace {
std::map<std::string, AllocationDetail> allocation_stats;
ssize_t totalAllocated = 0;
void memreport_ini() { report_done = false; }
void memreport_fini() {
Console::instance().print<LOG_DEBUG>("Writing memory report");
std::string s =
str(format("allocation_stats_%d.txt") %
LibLSS::MPI_Communication::instance()->rank());
std::ofstream f(s);
f << "Memory still allocated at the end: "
<< totalAllocated / 1024.0 / 1024.0 << " MB" << std::endl;
f << std::endl
<< "Statistics per context (name, allocated, freed, peak)" << std::endl
<< "======================" << std::endl
<< std::endl;
for (auto &s : allocation_stats) {
std::string name = s.first == "" ? "*none*" : s.first;
f << std::left << std::setw(40) << name << " "
<< s.second.allocated / (1024. * 1024.) << " "
<< s.second.freed / (1024. * 1024.) << " "
<< s.second.peak / (1024. * 1024.) << std::endl;
}
report_done = true;
}
// Highest priority after console.
LibLSS::RegisterStaticInit reg_record_init(
memreport_ini, memreport_fini, 1, "Memory allocated database");
} // namespace
AUTO_REGISTRATOR_IMPL(memory_alloc);
void LibLSS::report_allocation(size_t sz, const void *ptr) {
LibLSS::Console::instance().print<LOG_DEBUG>(
format("Allocated %d MB") % (sz / 1024. / 1024.));
auto &ctx = details::ConsoleContextBase::current();
std::string const &s = ctx.getMessage();
auto &state = allocation_stats[s];
#pragma omp critical
{
state.allocated += sz;
totalAllocated += sz;
if (totalAllocated > 0)
state.peak = std::max(state.peak, size_t(totalAllocated));
}
}
std::map<std::string, AllocationDetail> LibLSS::memoryReport() {
return allocation_stats;
}
void LibLSS::clearReport() { allocation_stats.clear(); }
void LibLSS::report_free(size_t sz, const void *ptr) {
if (report_done)
return;
auto &ctx = details::ConsoleContextBase::current();
assert(&ctx != 0);
auto const &s = ctx.getMessage();
auto &state = allocation_stats[s];
LibLSS::Console::instance().print<LOG_DEBUG>(
format("Freeing %d MB") % (sz / 1024. / 1024.));
#pragma omp critical
{
state.freed += sz;
totalAllocated -= sz;
}
}

55
libLSS/tools/memusage.hpp Normal file
View file

@ -0,0 +1,55 @@
#ifndef __LIBLSS_TOOLS_MEMUSAGE_HPP
#define __LIBLSS_TOOLS_MEMUSAGE_HPP
#include <sys/types.h>
#include "libLSS/tools/static_init.hpp"
#include "libLSS/tools/static_auto.hpp"
#include "libLSS/tools/errors.hpp"
#include <map>
#include <string>
namespace LibLSS {
struct AllocationDetail {
ssize_t allocated, freed;
size_t peak;
};
void report_allocation(size_t sz, const void *ptr);
void report_free(size_t sz, const void *ptr);
std::map<std::string, AllocationDetail> memoryReport();
void clearReport();
template<typename T>
struct track_allocator: public std::allocator<T> {
public:
typedef typename std::allocator<T> parent;
typedef typename parent::pointer pointer;
typedef typename parent::size_type size_type;
template<typename U> struct rebind { typedef track_allocator<U> other; };
track_allocator() throw(): std::allocator<T>() {}
track_allocator(const track_allocator& alloc) throw(): std::allocator<T>(alloc) {}
template<typename U>
track_allocator(const track_allocator<U>& alloc) throw(): std::allocator<T>(alloc) {}
pointer allocate(size_type _Count, const void *_Hint = 0) {
pointer p = std::allocator<T>::allocate(_Count, _Hint);
if (p) {
report_allocation(_Count*sizeof(T), _Hint);
} else {
error_helper<ErrorMemory>(boost::format("Memory allocation failed to allocate %d bytes") % (sizeof(T)*_Count));
}
return p;
}
void deallocate(pointer _Ptr, size_type _Count) {
std::allocator<T>::deallocate(_Ptr, _Count);
report_free(_Count*sizeof(T), _Ptr);
}
};
}
AUTO_REGISTRATOR_DECL(memory_alloc);
#endif

View file

@ -0,0 +1,14 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/mpi_fftw/copy_utils.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
template<bool upgrading, typename T> struct copy_utils{};
#include "copy_utils_upgrade.hpp"
#include "copy_utils_degrade.hpp"

View file

@ -0,0 +1,148 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/mpi_fftw/copy_utils_degrade.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
// target_mgr < init_mgr here
template<typename T>
struct copy_utils<false, T> {
typedef T element;
typedef FFTW_Manager<T,3> Mgr;
typedef std::complex<T> complex_element;
template<typename OutArray, typename InArray, typename Func >
static
void _copy_sub_2d_plane(const Mgr& init_mgr, const Mgr& target_mgr, OutArray out, const InArray& in_array, const Func& func)
{
long last_plane = target_mgr.N2_HC-1;
typedef typename OutArray::reference OutRef;
typedef typename InArray::const_reference InRef;
for (long i = 0; i < target_mgr.N1/2; i++) {
for (long j = 0; j < last_plane; j++) {
func(out[i][j], in_array[i][j], false, false);
}
func(out[i][last_plane], in_array[i][last_plane], false, true);
// There is missing half sum here. But the data are not necessarily here. (conjugate on the last plane).
// The final sum is delayed.
}
long base, base2;
long out1_half_N1, out2_half_N1;
long in1_half_N1, in2_half_N1;
base = 0;
base2 = init_mgr.N1 - target_mgr.N1;
out1_half_N1 = target_mgr.N1/2;
out2_half_N1 = target_mgr.N1/2;
in1_half_N1 = target_mgr.N1/2;
in2_half_N1 = init_mgr.N1-target_mgr.N1/2;
{
OutRef out1 = out[out1_half_N1];
OutRef out2 = out[out2_half_N1];
InRef in1 = in_array[in1_half_N1];
InRef in2 = in_array[in2_half_N1];
for (long j = 0; j < last_plane; j++) {
func(out1[j], in1[j], true, false);
func(out2[j], in2[j], true, false);
}
func(out1[last_plane], in1[last_plane], true, true);
}
for (long i = target_mgr.N1/2+1; i < target_mgr.N1; i++) {
OutRef out_i = out[base+i];
InRef in_i = in_array[base2+i];
for (long j = 0; j < last_plane; j++) {
func(out_i[j], in_i[j], false, false);
}
func(out_i[last_plane], in_i[last_plane], false, true);
// There is missing half sum here. But the data are not necessarily here. (conjugate on the last plane).
// The final sum is delayed.
}
}
// This function up/downgrades the input array to output array. It assumes
// the current manager object if the high resolution and init_mgr is the low
// resolution descriptor. It transfers then the two Fourier square ([0:N1/2, 0:N2_HC] and [N1/2:N1, 0:N2_HC] to their
// adequate position in the target array.
// The array must have a 1D flat topology.
template<typename OutArray, typename FlatPlane, typename Func >
static
void _copy_sub_2d_plane_flat(const Mgr& init_mgr, const Mgr& target_mgr,
OutArray out, const FlatPlane& flat,
const Func& func = Func())
{
typedef typename OutArray::reference OutRef;
ConsoleContext<LOG_DEBUG> ctx("_copy_sub_2d_plane_flat");
long h_N2 = target_mgr.N2_HC-1;
for (long i = 0; i < target_mgr.N1/2; i++) {
for (long j = 0; j < h_N2; j++) {
func(out[i][j], flat[i*init_mgr.N2_HC + j], false, false);
}
func(out[i][h_N2], flat[i*init_mgr.N2_HC + h_N2], false, true);
}
long half1 = target_mgr.N1/2;
long half2 = init_mgr.N1 - target_mgr.N1/2;
OutRef out_half = out[half1];
for (long j = 0; j < h_N2; j++) {
func(out_half[j], flat[half1*init_mgr.N2_HC + j], true, false);
func(out_half[j], flat[half2*init_mgr.N2_HC + j], true, false);
}
func(out_half[h_N2], flat[half1*init_mgr.N2_HC + h_N2], true, true);
func(out_half[h_N2], flat[half2*init_mgr.N2_HC + h_N2], true, true);
long base = init_mgr.N1-target_mgr.N1;
for (long i = target_mgr.N1/2+1; i < target_mgr.N1; i++) {
for (long j = 0; j < h_N2; j++) {
func(out[i][j], flat[(base+i)*init_mgr.N2_HC + j], false, false);
}
func(out[i][h_N2], flat[(base+i)*init_mgr.N2_HC + h_N2], false, true);
}
}
template<typename OutArray, typename InArray >
static
void _copy_sub_2d_plane(const Mgr& init_mgr, const Mgr& target_mgr,
OutArray out, const InArray& in_array)
{
_copy_sub_2d_plane(init_mgr, target_mgr, out, in_array, internal::AssignOperator<T,false>());
}
template<typename OutArray, typename FlatPlane >
static
void _copy_sub_2d_plane_flat(const Mgr& init_mgr, const Mgr& target_mgr,
OutArray out, const FlatPlane& flat)
{
_copy_sub_2d_plane_flat(init_mgr, target_mgr, out, flat, internal::AssignOperator<T,false>());
}
static inline
const Mgr& source(const Mgr& big_mgr, const Mgr& small_mgr) { return big_mgr; }
static inline
const Mgr& target(const Mgr& big_mgr, const Mgr& small_mgr) { return small_mgr; }
template<typename OutArray, typename InArray>
static
void _flat_copy_2d_array(const Mgr& init_mgr, const Mgr& target_mgr,
OutArray& out, const InArray& in)
{
ConsoleContext<LOG_DEBUG> ctx("_flat_copy_2d_array");
boost::multi_array_ref<complex_element, 2> out_ref(out.data(), boost::extents[init_mgr.N1][init_mgr.N2_HC]);
LibLSS::copy_array(out_ref, in);
}
};

View file

@ -0,0 +1,158 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/mpi_fftw/copy_utils_upgrade.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
template<typename T>
struct copy_utils<true, T> {
typedef T element;
typedef FFTW_Manager_3d<T> Mgr;
typedef std::complex<T> complex_element;
// This function upgrades the input array to output array. It assumes
// the current manager object if the high resolution and small_mgr is the low
// resolution descriptor. It transfers then the two Fourier square ([0:N1/2, 0:N2_HC] and [N1/2:N1, 0:N2_HC] to their
// adequate position in the target array.
// The array must have a 2D shape topology.
template<typename OutArray, typename InArray, typename Func >
static
void _copy_sub_2d_plane(Mgr const& target_mgr, Mgr const& init_mgr,
OutArray out, const InArray& in_array, const Func& func)
{
long last_plane = init_mgr.N2_HC-1;
typedef typename OutArray::reference OutRef;
typedef typename OutArray::const_reference InRef;
for (long i = 0; i < init_mgr.N1/2; i++) {
for (long j = 0; j < last_plane; j++) {
func(out[i][j], in_array[i][j], false, false);
}
func(out[i][last_plane], in_array[i][last_plane], false, true);
// There is missing half sum here. But the data are not necessarily here. (conjugate on the last plane).
// The final sum is delayed.
}
long base, base2;
long out1_half_N1, out2_half_N1;
long in1_half_N1, in2_half_N1;
base = target_mgr.N1-init_mgr.N1;
base2 = 0;
out1_half_N1 = init_mgr.N1/2;
out2_half_N1 = target_mgr.N1-init_mgr.N1/2;
in1_half_N1 = init_mgr.N1/2;
in2_half_N1 = init_mgr.N1/2;
{
OutRef out1 = out[out1_half_N1];
OutRef out2 = out[out2_half_N1];
InRef in1 = in_array[in1_half_N1];
InRef in2 = in_array[in2_half_N1];
for (long j = 0; j < last_plane; j++) {
func(out1[j], in_array[in1_half_N1][j], true, false);
func(out2[j], in_array[in2_half_N1][j], true, false);
}
func(out1[last_plane], in1[last_plane], true, true);
func(out2[last_plane], in2[last_plane], true, true);
}
for (long i = init_mgr.N1/2+1; i < init_mgr.N1; i++) {
OutRef out_i = out[base+i];
InRef in_i = in_array[base2+i];
for (long j = 0; j < last_plane; j++) {
func(out_i[j], in_i[j], false, false);
}
func(out_i[last_plane], in_i[last_plane], false, true);
// There is missing half sum here. But the data are not necessarily here. (conjugate on the last plane).
// The final sum is delayed.
}
}
// This function up/downgrades the input array to output array. It assumes
// the current manager object if the high resolution and small_mgr is the low
// resolution descriptor. It transfers then the two Fourier square ([0:N1/2, 0:N2_HC] and [N1/2:N1, 0:N2_HC] to their
// adequate position in the target array.
// The array must have a 1D flat topology.
template<typename OutArray, typename FlatPlane, typename Func >
static
void _copy_sub_2d_plane_flat(Mgr const& target_mgr, Mgr const& init_mgr,
OutArray out, const FlatPlane& flat,
const Func& func = Func())
{
typedef typename OutArray::reference OutRef;
ConsoleContext<LOG_DEBUG> ctx("_copy_sub_2d_plane_flat");
for (long i = 0; i < init_mgr.N1/2; i++) {
for (long j = 0; j < init_mgr.N2_HC; j++) {
func(out[i][j], flat[i*init_mgr.N2_HC + j], false, false);
}
}
long base = target_mgr.N1-init_mgr.N1;
long half1 = init_mgr.N1/2;
long half2 = target_mgr.N1 - init_mgr.N1/2;
OutRef out_half1 = out[half1];
OutRef out_half2 = out[half2];
for (long j = 0; j < init_mgr.N2_HC; j++) {
func(out_half1[j], flat[half1*init_mgr.N2_HC + j], true, false);
func(out_half2[j], flat[half1*init_mgr.N2_HC + j], true, false);
}
for (long i = init_mgr.N1/2+1; i < init_mgr.N1; i++) {
OutRef out_i = out[base+i];
for (long j = 0; j < init_mgr.N2_HC; j++) {
func(out_i[j], flat[i*init_mgr.N2_HC + j], false, false);
}
}
}
template<typename OutArray, typename InArray >
static
void _copy_sub_2d_plane(Mgr const& target_mgr, Mgr const& init_mgr,
OutArray out,
const InArray& in_array)
{
_copy_sub_2d_plane(target_mgr, init_mgr, out, in_array, internal::AssignOperator<T,true>());
}
template<typename OutArray, typename FlatPlane >
static
void _copy_sub_2d_plane_flat(Mgr const& target_mgr, const Mgr& init_mgr,
OutArray out, const FlatPlane& flat)
{
_copy_sub_2d_plane_flat(target_mgr, init_mgr, out, flat, internal::AssignOperator<T,true>());
}
// This function transforms 2D like array into a flattened 1D array.
// This assumes that the array has the correct shape (N1 x N2_HC)
// init_mgr is always the small one
template<typename OutArray, typename InArray, typename Func>
static
void _flat_copy_2d_array(const Mgr& target_mgr, const Mgr& init_mgr,
OutArray& out, const InArray& in, const Func& func)
{
boost::multi_array_ref<complex_element, 2> out_ref(out.data(), boost::extents[init_mgr.N1][init_mgr.N2_HC]);
LibLSS::copy_array(out_ref, in);
}
template<typename OutArray, typename InArray>
static
void _flat_copy_2d_array(const Mgr& target_mgr, const Mgr& init_mgr,
OutArray& out, const InArray& in)
{
_flat_copy_2d_array(target_mgr, init_mgr, out, in, internal::AssignOperator<T,true>());
}
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,76 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/mpi_fftw/nyquist_downgrade.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
template<typename T>
struct Nyquist_adjust<T, false> {
typedef FFTW_Manager<T,3> Mgr;
typedef typename Mgr::Plane Plane;
typedef typename Mgr::U_Plane U_Plane;
typedef typename Mgr::U_Array U_Array;
typedef internal::copy_utils<false, T> c_util;
template<typename InArray, typename OutArray>
static void handle(
const Mgr& small_mgr, const Mgr& big_mgr,
std::vector<U_Plane *>& request_planes,
std::vector<bool>& request_io,
RequestArray& request_array,
const InArray& in_modes, OutArray& out_modes) {
MPI_Status status;
long N0 = small_mgr.N0;
long N1 = small_mgr.N1;
long N2 = small_mgr.N2;
long half_N0 = small_mgr.N0/2;
long big_conjugate_plane = big_mgr.N0-half_N0;
Console& cons = Console::instance();
if (small_mgr.on_core(half_N0)) {
if(big_mgr.on_core(half_N0)) {
// both planes are here. push them into out_modes
c_util::_copy_sub_2d_plane(big_mgr, small_mgr, out_modes[half_N0], in_modes[half_N0], AccumOperator<T>());
} else {
// Hmm... we have to grab the request plane
assert(request_array[half_N0].is_active());
request_array[half_N0].wait(&status);
request_io[half_N0] = false;
c_util::_copy_sub_2d_plane_flat(big_mgr, small_mgr, out_modes[half_N0], request_planes[half_N0]->get_array(), AccumOperator<T>());
internal::safe_delete(request_planes[half_N0]);
}
if (big_mgr.on_core(big_conjugate_plane)) {
// both planes are here. push them into out_modes
c_util::_copy_sub_2d_plane(big_mgr, small_mgr, out_modes[half_N0], in_modes[big_conjugate_plane], AccumOperator<T>());
} else {
assert(request_array[N0].is_active());
request_array[N0].wait(&status);
request_io[N0] = false;
c_util::_copy_sub_2d_plane_flat(big_mgr, small_mgr, out_modes[half_N0], request_planes[N0]->get_array(), AccumOperator<T>());
internal::safe_delete(request_planes[N0]);
}
// Clear up imaginary parts
out_modes[half_N0][N1/2][0].imag(0);
out_modes[half_N0][N1/2][N2/2].imag(0);
out_modes[half_N0][0][0].imag(0);
out_modes[half_N0][0][N2/2].imag(0);
}
if (small_mgr.on_core(0)) {
out_modes[0][N1/2][0].imag(0);
out_modes[0][N1/2][N2/2].imag(0);
}
// There is no point for those two.
//out_modes[0][0][0].imag() = 0;
//out_modes[0][0][N2/2].imag() = 0;
}
};

View file

@ -0,0 +1,75 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/mpi_fftw/nyquist_upgrade.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
template<typename T> struct Nyquist_adjust<T, true> {
typedef FFTW_Manager<T,3> Mgr;
typedef typename Mgr::Plane Plane;
typedef typename Mgr::U_Plane U_Plane;
typedef typename Mgr::U_Array U_Array;
typedef internal::copy_utils<true, T> c_util;
template<typename InArray, typename OutArray>
static void handle(
const Mgr& small_mgr, const Mgr& big_mgr,
std::vector<U_Plane *>& request_planes,
std::vector<bool>& request_io,
RequestArray& request_array,
const InArray& in_modes, OutArray& out_modes) {
using boost::format;
Console& cons = Console::instance();
MPI_Status status;
MPI_Communication *comm = small_mgr.comm;
long half_N0 = small_mgr.N0/2;
long conjugate_big_plane = big_mgr.N0 - half_N0;
if (small_mgr.on_core(small_mgr.N0/2) && big_mgr.on_core(small_mgr.N0/2)) {
c_util::_copy_sub_2d_plane(big_mgr, small_mgr, out_modes[small_mgr.N0/2], in_modes[small_mgr.N0/2]);
}
if (small_mgr.on_core(small_mgr.N0/2) && big_mgr.on_core(conjugate_big_plane)) {
c_util::_copy_sub_2d_plane(big_mgr, small_mgr, out_modes[big_mgr.N0-small_mgr.N0/2], in_modes[small_mgr.N0/2]);
}
if (!small_mgr.on_core(half_N0) && big_mgr.on_core(half_N0)) {
U_Array& a_plane = request_planes[half_N0]->get_array();
cons.c_assert(request_planes[half_N0] != 0, "No half_N0 plane, though we need it here");
// Wait for the recv to complete
request_array[half_N0].wait(&status);
request_io[half_N0] = false;
cons.print<LOG_DEBUG>(format("Received plane %d (big is %d)") % half_N0 % half_N0);
// Copy the plane
c_util::_copy_sub_2d_plane_flat(big_mgr, small_mgr, out_modes[half_N0], a_plane);
// If the other plane is on this core, copy the data.
if (big_mgr.on_core(conjugate_big_plane)) {
c_util::_copy_sub_2d_plane_flat(big_mgr, small_mgr, out_modes[conjugate_big_plane], a_plane);
}
// Cleanup
internal::safe_delete(request_planes[half_N0]);
} else
if (!small_mgr.on_core(half_N0) && big_mgr.on_core(conjugate_big_plane)) {
// If we do not have the half_N0 plane and we are in the negative freq range
// just wait for the transfer to finish.
cons.print<LOG_DEBUG>(format("Half plane, big = %d") % conjugate_big_plane);
cons.c_assert(request_io[small_mgr.N0], "Invalid I/O state");
U_Array& a_plane = request_planes[small_mgr.N0]->get_array();
request_array[small_mgr.N0].wait(&status);
request_io[small_mgr.N0] = false;
cons.print<LOG_DEBUG>(format("Received plane %d (big is %d)") % half_N0 % conjugate_big_plane);
c_util::_copy_sub_2d_plane_flat(big_mgr, small_mgr, out_modes[conjugate_big_plane], a_plane);
internal::safe_delete(request_planes[half_N0]);
}
}
};

View file

@ -0,0 +1,180 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/mpi_fftw_helper.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_FFTW_HELPER_HPP
#define __LIBLSS_FFTW_HELPER_HPP
#include <boost/type_traits/remove_reference.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/multi_array.hpp>
#include "libLSS/samplers/core/types_samplers.hpp"
#include "libLSS/tools/array_tools.hpp"
#include "libLSS/tools/fused_array.hpp"
#include "libLSS/tools/uninitialized_type.hpp"
#define DEBUG_MPI_DEGRADE
#ifdef DEBUG_MPI_DEGRADE
#define CHECK_NYQ(q) Console::instance().c_assert(nyqCheck[q], "Plane not imported")
#endif
namespace LibLSS {
template<typename T, int Nd = 3> class FFTW_Manager;
template<typename T> using FFTW_Manager_3d = FFTW_Manager<T,3>;
namespace internal {
template<typename T> struct padding_multiplier {};
template<> struct padding_multiplier<double> {
enum { multiplier = 2 };
};
template<> struct padding_multiplier<float> {
enum { multiplier = 2 };
};
template<> struct padding_multiplier<std::complex<double> > {
enum { multiplier = 1 };
};
template<> struct padding_multiplier<std::complex<float> > {
enum { multiplier = 1 };
};
template<typename T>
void safe_delete(T *& p) {
if (p != 0) {
delete p;
p = 0;
}
}
template<typename T, bool upgrading>
struct AssignOperator {
void clear(std::complex<T>& a) const { }
// Natural degrade
void operator()(std::complex<T>& a, const std::complex<T>& b, bool nyq, bool nyq2) const {
if (upgrading) {
a = b;
} else {
T f = 1;
if (nyq) f *= 0.5;
if (nyq2) f *= 0.5;
a += f*b;
}
}
};
template<typename T>
struct AccumOperator {
void clear(std::complex<T>& a) const { }
// Natural degrade
void operator()(std::complex<T>& a, const std::complex<T>& b, bool nyq, bool nyq2) const {
T f = 0.5;
if (nyq) f *= 0.5;
if (nyq2) f *= 0.5;
a += f*b;
}
};
template<typename T, bool upgrading> struct Nyquist_adjust;
#include "mpi_fftw/copy_utils.hpp"
#include "mpi_fftw/nyquist_upgrade.hpp"
#include "mpi_fftw/nyquist_downgrade.hpp"
};
template<typename ArrayType>
inline bool copy_padded_data(
const ArrayType& a,
typename ArrayType::element *padded_a, bool only_mpi = false)
{
typedef typename ArrayType::element ElementType;
using internal::padding_multiplier;
long N0 = a.shape()[0], N1 = a.shape()[1], N2 = a.shape()[2];
long s_j = padding_multiplier<ElementType>::multiplier * (N2/2 + 1);
#ifdef ARES_MPI_FFTW
long s_i = N1 * s_j;
long s = a.index_bases()[0];
for (long i = 0; i < N0; i++)
for (long j = 0; j < N1; j++)
for (long k = 0 ; k < N2; k++)
padded_a[i * s_i + j * s_j + k] = a[i+s][j][k];
return true;
#else
if (!only_mpi)
memcpy(padded_a, a.data(), sizeof(typename ArrayType::element) * a.num_elements());
return false;
#endif
}
template<typename ArrayType>
inline bool copy_unpadded_data(
const typename ArrayType::element *padded_a,
ArrayType& a, bool only_mpi = false)
{
#ifdef ARES_MPI_FFTW
long N0 = a.shape()[0], N1 = a.shape()[1], N2 = a.shape()[2];
long s_j = 2 * (N2/2 + 1);
long s_i = N1 * s_j;
long s = a.index_bases()[0];
for (long i = 0; i < N0; i++)
for (long j = 0; j < N1; j++)
for (long k = 0 ; k < N2; k++)
a[i+s][j][k] = padded_a[i * s_i + j * s_j + k];
return true;
#else
if (!only_mpi)
memcpy(a.data(), padded_a, sizeof(typename ArrayType::element) * a.num_elements());
return false;
#endif
}
template<std::size_t NumDims>
boost::general_storage_order<NumDims> get_fftw_order() {
typedef boost::general_storage_order<NumDims> order;
#ifdef ARES_MPI_FFTW
typedef typename order::size_type size_type;
boost::array<size_type, NumDims> ordering;
boost::array<bool, NumDims> ascending;
if (NumDims >= 2) {
for (size_type i = 2; i != NumDims; i++) {
ordering[i] = NumDims - 1 - i;
ascending[i] = true;
}
ordering[0] = 1;
ordering[1] = 0;
ascending[0] = ascending[1] = true;
} else if (NumDims == 1) {
ordering[0] = 0;
ascending[0] = true;
}
return order(ordering.begin(), ascending.begin());
#else
return order(boost::c_storage_order());
#endif
}
#include "libLSS/tools/mpi_fftw/impl_3d.hpp"
}
#ifdef DEBUG_MPI_DEGRADE
#undef CHECK_NYQ
#endif
#endif

View file

@ -0,0 +1,248 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/nary_arrays.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_NARY_ARRAYS_HPP
#define __LIBLSS_NARY_ARRAYS_HPP
#include <algorithm>
#include <tuple>
#include "libLSS/tools/array_concepts.hpp"
namespace LibLSS {
struct ArrayTuple_base {
typedef boost::multi_array_types::size_type size_type;
typedef boost::multi_array_types::index index;
};
template<size_t Ndims, typename ReturnElement, typename TupleT, bool Shaped>
struct ArrayTuple;
template<size_t Ndims, typename ReturnElement, typename TupleT>
struct ArrayTuple<Ndims, ReturnElement,TupleT,false>: ArrayTuple_base {
enum { NumDims = Ndims };
enum { arity = std::tuple_size<TupleT>::value };
static constexpr bool Shaped = false;
typedef boost::array<index, Ndims> subindex;
typedef boost::array<size_type, Ndims> subshape;
typedef TupleT Tuple;
typedef ReturnElement element;
const Tuple tuple;
inline
ArrayTuple(Tuple const & t) : tuple(t) {}
inline bool vectorizable() const { return false; }
};
template<size_t Ndims, typename ReturnElement, typename TupleT>
struct ArrayTuple<Ndims, ReturnElement,TupleT,true>: ArrayTuple_base {
enum { NumDims = Ndims };
enum { arity = std::tuple_size<TupleT>::value };
static constexpr bool Shaped = true;
typedef TupleT Tuple;
typedef ReturnElement element;
const Tuple tuple;
inline
ArrayTuple(Tuple const & t) : tuple(t) {}
inline
const size_type *shape() const { return std::get<0>(tuple).shape(); }
inline
const index *index_bases() const { return std::get<0>(tuple).index_bases(); }
inline
size_type num_elements() const { return std::get<0>(tuple).num_elements(); }
inline bool vectorizable() const { return false; }
};
template<size_t Ndims, typename ReturnElement, size_t arity_par = 0>
struct ArrayNullTuple: ArrayTuple_base {
enum { NumDims = Ndims };
enum { arity = arity_par };
typedef ReturnElement element;
static constexpr bool Shaped = false;
// Special no-op tuple
struct Tuple {
// Type to access i-th position of the given index.
template<int i>
struct TupleElement {
template<typename Index>
inline auto operator()(const Index& j) const->
decltype(j[i]) { return j[i]; }
};
};
// Implicit accessor
const Tuple tuple;
inline
ArrayNullTuple() : tuple() {}
};
template<size_t Ndims, typename ReturnElement, size_t arity_par = 0>
struct ArrayNullTupleExtent: ArrayTuple_base {
enum { NumDims = Ndims };
enum { arity = arity_par };
typedef ReturnElement element;
static constexpr bool Shaped = true;
typedef boost::array<index, Ndims> subindex;
typedef boost::array<size_type, Ndims> subshape;
// Special no-op tuple
struct Tuple {
// Type to access i-th position of the given index.
template<int i>
struct TupleElement {
template<typename Index>
auto operator()(const Index& j) const -> decltype(j[i]) { return j[i]; }
};
};
// Implicit accessor
const Tuple tuple;
subindex indexes;
subshape shapes;
typedef boost::multi_array_types::extent_range extent_range;
size_t total_elts;
// ExtentGen is a boost::extents like type
template<typename ExtentType>
ArrayNullTupleExtent(const ExtentType& f_extents)
: tuple() {
using std::transform;
transform(f_extents.ranges_.begin(), f_extents.ranges_.end(),
indexes.begin(),
boost::mem_fun_ref(&extent_range::start));
transform(f_extents.ranges_.begin(), f_extents.ranges_.end(),
shapes.begin(),
boost::mem_fun_ref(&extent_range::size));
size_t _total = 1;
std::for_each(shapes.begin(), shapes.end(),
[&_total](size_type s) { _total *= s; });
total_elts = _total;
}
template<typename ShapeList, typename IndexList>
ArrayNullTupleExtent(const ShapeList& _shapes, const IndexList& _indexbase)
: tuple() {
using std::transform;
std::copy(_indexbase.begin(), _indexbase.end(), indexes.begin());
std::copy(_shapes.begin(), _shapes.end(), shapes.begin());
size_t _total = 1;
std::for_each(shapes.begin(), shapes.end(),
[&_total](size_type s) { _total *= s; });
total_elts = _total;
}
const size_type *shape() const { return &shapes[0];}
const index *index_bases() const { return &indexes[0]; }
size_type num_elements() const { return 0; }
};
// Special cases for which the tuple of array is degenerate to the empty set.\
// The arraytuple is always not shaped then.
template<size_t Ndims, typename ReturnElement, bool Shaped>
struct ArrayTuple<Ndims,ReturnElement,std::tuple<>,Shaped>: ArrayTuple_base, ArrayNullTuple<Ndims,ReturnElement> { };
template<size_t Ndims, typename ReturnElement, bool Shaped>
struct ArrayTuple<Ndims,ReturnElement,std::tuple<> const,Shaped>: ArrayTuple_base, ArrayNullTuple<Ndims,ReturnElement> { };
// Detect whether a type support shapes or not
// By default no.
template<typename T, typename = void>
struct DetectShaped {
static constexpr bool Shaped = false;
};
template<typename T>
struct DetectShaped<T, typename boost::enable_if<array_concepts::has_shape_info<T>>::type> {
static constexpr bool Shaped = T::Shaped;
};
template<typename T>
struct DetectShaped<T, typename boost::enable_if<array_concepts::is_array_storage<T>>::type> {
static constexpr bool Shaped = true;
};
template<typename T>
struct DetectShaped<T, typename std::enable_if<array_concepts::is_array_sub<T>::value && !array_concepts::is_array_view<T>::value && !array_concepts::is_array_storage<T>::value>::type> {
static constexpr bool Shaped = true;
};
template<typename T>
struct DetectShaped<T, typename boost::enable_if<array_concepts::is_array_view<T>>::type> {
static constexpr bool Shaped = true;
};
}
// Populate std with some additional getters to support NullTuples
namespace std {
template<size_t getter, typename NullTuple>
inline typename NullTuple::template TupleElement<getter>
get(NullTuple const& t) throw() {
return typename NullTuple::template TupleElement<getter>();
}
}
// Terminate the definition of a lazy/virtual array evaluation
namespace LibLSS {
namespace details {
template<int order>
struct array_apply_tuple {
template<typename Operation, typename ArrayTuple, typename Index, typename... Args>
static inline typename ArrayTuple::element
apply(Operation&& op, ArrayTuple& at, Index i, Args&&... args) {
return array_apply_tuple<order-1>::apply(op, at, i, std::get<order-1>(at.tuple)(i), args...);
}
};
template<>
struct array_apply_tuple<0> {
template<typename Operation, typename ArrayTuple, typename Index, typename... Args>
static inline typename ArrayTuple::element apply(Operation&& op, ArrayTuple& at, Index i, Args&&... args) {
return op(args...);
}
};
template<typename Operation, typename ArrayTuple, typename Index>
inline
typename ArrayTuple::element
apply_op(Operation&& op, ArrayTuple& t, Index i ) {
return array_apply_tuple<ArrayTuple::arity>::apply(op, t, i);
};
};
}
#endif

53
libLSS/tools/openmp.hpp Normal file
View file

@ -0,0 +1,53 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/openmp.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_OPENMP_HPP
#define __LIBLSS_OPENMP_HPP
#ifdef _OPENMP
#include <omp.h>
#endif
namespace LibLSS {
inline int smp_get_max_threads() {
#ifdef _OPENMP
return omp_get_max_threads();
#else
return 1;
#endif
}
inline int smp_get_thread_id() {
#ifdef _OPENMP
return omp_get_thread_num();
#else
return 0;
#endif
}
inline int smp_get_num_threads() {
#ifdef _OPENMP
return omp_get_num_threads();
#else
return 1;
#endif
}
inline void smp_set_nested(bool n) {
#ifdef _OPENMP
omp_set_nested(n ? 1 : 0);
#endif
}
};
#endif

View file

@ -0,0 +1,139 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/optimization/array_helper.hpp
Copyright (C) 2018-2019 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_TOOLS_OPTIMIZATION_ARRAY_HELPER_HPP
#define __LIBLSS_TOOLS_OPTIMIZATION_ARRAY_HELPER_HPP
#include <complex>
#include <boost/tti/has_type.hpp>
#include <boost/multi_array.hpp>
#include "libLSS/tools/array_tools.hpp"
#include "libLSS/tools/fusewrapper.hpp"
#include "libLSS/tools/uninitialized_type.hpp"
namespace LibLSS {
namespace Optimization {
BOOST_TTI_HAS_TYPE(array_t);
BOOST_TTI_HAS_TYPE(holder_array_t);
template<typename T> struct is_complex: public std::false_type {};
template<typename U> struct is_complex<std::complex<U>> : public std::true_type {};
template <typename Array1, typename Array2, typename U = typename Array1::array_t::element>
inline typename std::enable_if<!is_complex<U>::value, double>::type dotprod(Array1 const &a1, Array2 const &a2) {
return (a1*a2).sum();
}
template <typename Array1, typename Array2, typename U = typename Array1::array_t::element>
inline typename std::enable_if<is_complex<U>::value, double>::type dotprod(Array1 const &a1, Array2 const &a2) {
auto r = [](auto&& a) { return std::real(a); };
auto i = [](auto&& a) { return std::imag(a); };
auto r1 = r(a1);
auto r2 = r(a2);
auto i1 = i(a1);
auto i2 = i(a2);
return (r1*r2 + i1*i2).sum();
}
template <typename Array1, typename Array2>
inline double dotprod(MPI_Communication* comm, Array1 const &a1, Array2 const &a2) {
double r = dotprod(a1, a2);
comm->all_reduce_t(MPI_IN_PLACE, &r, 1, MPI_SUM);
return r;
}
template <typename Array>
struct array_holder {
public:
typedef Array array_t;
typedef UninitializedArray<array_t> u_array_t;
typedef std::shared_ptr<u_array_t> holder_array_t;
holder_array_t holder;
array_holder() : holder() {}
array_holder(array_holder<Array> &other) : holder(other.holder) {}
array_holder(array_holder<Array> &&other)
: holder(std::move(other.holder)) {}
array_holder(holder_array_t &&h) : holder(std::move(h)) {}
array_holder(holder_array_t& h) : holder(h) {}
array_holder<Array> const &operator=(array_holder<Array> &other) {
holder = other.holder;
return *this;
}
array_holder<Array> const &operator=(array_holder<Array> &&other) {
holder = std::move(other.holder);
return *this;
}
inline array_t &get() { return holder.get()->get_array(); }
inline array_t const &get() const { return holder.get()->get_array(); }
inline operator bool() const {
return holder.operator bool();
}
inline auto operator*() -> decltype(fwrap(get())) {
return fwrap(get());
}
inline auto operator*() const -> decltype(fwrap(get())) {
return fwrap(get());
}
};
template <typename T, typename = void>
struct is_holder : std::false_type {};
template <typename T>
struct is_holder<
T, typename std::enable_if<
has_type_array_t<T>::value &&
has_type_holder_array_t<T>::value>::type> : std::true_type {};
template <typename T, size_t N>
struct BoostArrayAllocator {
public:
typedef boost::multi_array_ref<T, N> array_t;
typedef array_holder<array_t> holder_t;
typedef typename holder_t::u_array_t new_array_t;
typedef decltype(fwrap(*(array_t *)0)) wrap_t;
inline auto wrapper(array_t const &a) const -> decltype(fwrap(a)) {
return fwrap(a);
}
inline auto wrapper(array_t &a) const -> decltype(fwrap(a)) {
return fwrap(a);
}
inline holder_t new_like(holder_t const &a) { return new_like(a.get()); }
template<typename ArrayLike>
inline holder_t new_like(ArrayLike const &a) {
return holder_t(std::unique_ptr<new_array_t>(
new new_array_t(LibLSS::array::make_extent_from(a)))
);
}
};
} // namespace Optimization
} // namespace LibLSS
// ARES TAG: num_authors = 1
// ARES TAG: name(0) = Guilhem Lavaux
// ARES TAG: email(0) = guilhem.lavaux@iap.fr
// ARES TAG: year(0) = 2018-2019
#endif

View file

@ -0,0 +1,265 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/optimization/cg.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_TOOLS_OPTIMIZATION_CG_HPP
#define __LIBLSS_TOOLS_OPTIMIZATION_CG_HPP
#include <algorithm>
#include <functional>
#include <boost/multi_array.hpp>
#include <boost/bind.hpp>
#include "libLSS/tools/console.hpp"
#include "libLSS/tools/array_tools.hpp"
#include "libLSS/tools/fused_array.hpp"
#include "libLSS/tools/fused_assign.hpp"
#include "libLSS/tools/optimization/array_helper.hpp"
namespace LibLSS {
namespace Optimization {
namespace details {
template <typename T>
T lazy_conj(T const &c) {
return c;
}
template <typename T>
std::complex<T> lazy_conj(std::complex<T> const &c) {
return std::conj(c);
}
} // namespace details
template <typename ArrayAllocator>
struct CG {
public:
unsigned int cg_refresh;
double epsilon;
unsigned int T;
typedef typename ArrayAllocator::array_t array_t;
typedef std::function<void(array_t& out, array_t const& in)> Matrix_function_t;
typedef std::function<void(array_t& out, array_t const& in, int s)> Precon_Matrix_function_t;
public:
CG(ArrayAllocator alloc_ = ArrayAllocator()):allocator(alloc_) {
cg_refresh = 1000; // sets refreshing rate for conjugating directions
epsilon = 1e-9; // sets convergence criterion
T = 40000000; // sets maximum number of cg steps
}
~CG() {}
void
run(Matrix_function_t A, array_t const &b,
array_t &x) {
ConsoleContext<LOG_VERBOSE> ctx("CG::run");
Console &cons = Console::instance();
auto r = allocator.new_like(x);
auto d = allocator.new_like(x);
auto q = allocator.new_like(x);
auto wx = allocator.wrapper(x);
auto wb = allocator.wrapper(b);
double dnew = 0.0;
double dinit = 0.0;
double dold = 0.0;
//apply matrix to x vector
A(q.get(), x);
//initialize values
*r = b - *q;
*d = *r;
dinit = dotprod(wb,wb);
dnew = dotprod(*r, *r);
dold = dnew;
int t = 0;
Progress<LOG_INFO_SINGLE> &progress =
cons.start_progress<LOG_INFO_SINGLE>(
"applying conjugate gradient", T, 10);
while ((t < T)) {
//apply matrix to d vector
A(q.get(), d.get());
double dq = dotprod(*d, *q);
cons.print<LOG_DEBUG>(boost::format("residue is dq=%g ") % dq);
//now determine alpha
double alpha = dnew / (dq+1e-40);
cons.print<LOG_DEBUG>(boost::format("alpha =%g ") % alpha);
//now update x vector
wx = wx + alpha * (*d);
bool exit= false;
if (t % cg_refresh == 0) {
A(q.get(), x);
*r = b -(*q);
exit=true;
cons.print<LOG_DEBUG>("Refresh! ");
} else {
*r = r.get() - alpha*(*q);
}
dold = dnew;
dnew = dotprod(*r, *r);
double const ratio = dnew/dinit;
cons.print<LOG_DEBUG>(boost::format("t=%g residue is dnew=%g / dinit=%g => ratio=%g") % t % dnew % dinit % ratio);
double beta = dnew / dold;
cons.print<LOG_DEBUG>(boost::format("beta - 1 =%g ") % (beta-1.));
*d = *r + beta*(*d);
t++;
progress.update(t);
double const dcheck = dotprod(*r, *r);
cons.print<LOG_DEBUG>(boost::format("residue is dnew=%g / dcheck=%g / dinit=%g") % dnew % dcheck % dinit);
if( (dcheck/dinit)<epsilon) { // or ( fabs(beta-1.) < epsilon ))
A(q.get(), x);
double const dcheck2 = dotprod(*q - b, *q - b);
cons.print<LOG_DEBUG>(boost::format("breaking at %g") % (dcheck/dinit));
cons.print<LOG_DEBUG>(boost::format("breaking at %g??") % (dcheck2/dinit));
if ((dcheck2/dinit)<epsilon)
break;
cons.print<LOG_DEBUG>("no");
}
}
cons.print<LOG_DEBUG>("Done with CG");
progress.destroy();
}
void
run(Matrix_function_t A, Precon_Matrix_function_t M_inv, typename ArrayAllocator::array_t const&b,
typename ArrayAllocator::array_t &x) {
ConsoleContext<LOG_VERBOSE> ctx("CG::run");
Console &cons = Console::instance();
auto r = allocator.new_like(x);
auto d = allocator.new_like(x);
auto q = allocator.new_like(x);
auto s = allocator.new_like(x);
double dnew = 0.0;
double dinit = 0.0;
double dold = 0.0;
//apply matrix to x vector
A(q.get(), x);
//initialize values
*r = b - *q;
//use preconditioner
M_inv(d.get(), r.get(),1);
dinit = dnew = dotprod(*r, *d);
dold = dnew;
int t = 0;
Progress<LOG_INFO_SINGLE> &progress =
cons.start_progress<LOG_INFO_SINGLE>(
"applying conjugate gradient", T, 10);
while ((t < T)) {// and (dinit > epsilon * epsilon)) {
//apply matrix to d vector
A(q.get(), d.get());
double dq = dotprod(*d, *q);
//now determine alpha
double alpha = dnew / (dq+1e-40);
//now update x vector
fwrap(x) = fwrap(x) + alpha*(*d);
bool exit=false;
if (t % cg_refresh == 0) {
A(q.get(), x);
*r = b -(*q);
exit=true;
} else {
*r = r.get() - alpha*(*q);
}
M_inv(s.get(), r.get(),1);
dold = dnew;
dnew = dotprod(*r, *s);
double beta = dnew / dold;
*d = *s + beta*(*d);
t++;
progress.update(t);
double const dcheck = dotprod(*r, *r);
cons.print<LOG_DEBUG>(boost::format("residue is dnew=%g / dcheck=%g / dinit=%g") % dnew % dcheck % dinit);
if( (dcheck/dinit)<epsilon) { // or ( fabs(beta-1.) < epsilon ))
cons.print<LOG_DEBUG>(boost::format("breaking at %g") % (dcheck/dinit));
break;
}
}
progress.destroy();
}
private:
ArrayAllocator allocator;
};
} // namespace Optimization
using Optimization::CG;
}; // namespace LibLSS
#endif

57
libLSS/tools/overload.hpp Normal file
View file

@ -0,0 +1,57 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/overload.hpp
Copyright (C) 2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
/* This particular trick has been taken from stackoverflow.
* It allows to have overloaded lambda operator in C++11
* https://stackoverflow.com/questions/32475576/c11-overloaded-lambda-with-variadic-template-and-variable-capture
* Author: Piotr Skotnicki
* MR FIXME: Is this still needed after ARES has changed to C++14?
*/
#pragma once
#ifndef __LIBLSS_TOOLS_OVERLOAD_HPP
# define __LIBLSS_TOOLS_OVERLOAD_HPP
namespace LibLSS {
namespace details_overload {
template <class... Fs>
struct _overload;
template <class F0, class... Frest>
struct _overload<F0, Frest...> : F0, _overload<Frest...> {
_overload(F0 f0, Frest... rest) : F0(f0), _overload<Frest...>(rest...) {}
using F0::operator();
using _overload<Frest...>::operator();
};
template <class F0>
struct _overload<F0> : F0 {
_overload(F0 f0) : F0(f0) {}
using F0::operator();
};
template <class... Fs>
auto overload(Fs... fs) {
return _overload<Fs...>(fs...);
}
} // namespace details_overload
using details_overload::overload;
} // namespace LibLSS
#endif
// ARES TAG: num_authors = 1
// ARES TAG: name(0) = Guilhem Lavaux
// ARES TAG: year(0) = 2020
// ARES TAG: email(0) = guilhem.lavaux@iap.fr

View file

@ -0,0 +1,33 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/phoenix_vars.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_PHOENIX_VARS_HPP
#define __LIBLSS_PHOENIX_VARS_HPP
#include <boost/phoenix/core/argument.hpp>
namespace LibLSS {
namespace PhoenixDetails {
using boost::phoenix::expression::argument;
argument<1>::type const _p1 = {};
argument<2>::type const _p2 = {};
argument<3>::type const _p3 = {};
argument<4>::type const _p4 = {};
}
using PhoenixDetails::_p1;
using PhoenixDetails::_p2;
using PhoenixDetails::_p3;
using PhoenixDetails::_p4;
}
#endif

View file

@ -0,0 +1,66 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/powerspectrum/measure.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_POWERSPECTRUM_MEASURE_HPP
#define __LIBLSS_POWERSPECTRUM_MEASURE_HPP
#include <CosmoTool/algo.hpp>
#include <CosmoTool/hdf5_array.hpp>
#include "libLSS/samplers/core/types_samplers.hpp"
#include "libLSS/samplers/core/powerspec_tools.hpp"
namespace LibLSS {
namespace PowerSpectrum {
template<typename CArray>
void computePower(ArrayType1d::ArrayType& power, const CArray& density, const IArrayType& a_keys,
const IArrayType& a_adjust, const IArrayType1d& a_nmode, double volume, bool clear = true) {
using CosmoTool::square;
const IArrayType::ArrayType& keys = *a_keys.array;
const IArrayType::ArrayType& adjust = *a_adjust.array;
const IArrayType1d::ArrayType& nmode = *a_nmode.array;
int begin0 = density.index_bases()[0];
int begin1 = density.index_bases()[1];
int begin2 = density.index_bases()[2];
int end0 = density.index_bases()[0] + density.shape()[0];
int end1 = density.index_bases()[1] + density.shape()[1];
int end2 = density.index_bases()[2] + density.shape()[2];
if (clear)
std::fill(power.begin(), power.end(), 0);
for (int i = begin0; i < end0; i++) {
for (int j = begin1; j < end1; j++) {
for (int k = begin2; k < end2; k++) {
double P = square(density[i][j][k].real()) + square(density[i][j][k].imag());
power[ keys[i][j][k] ] += P * adjust[i][j][k];
}
}
}
for (int i = 0; i < power.num_elements(); i++)
if (nmode[i] != 0)
power[i] /= (volume * nmode[i]);
}
template<typename F, typename CArray>
void savePower(F& f, const std::string& n, const CArray& density, const IArrayType& a_keys,
const IArrayType& a_adjust, const IArrayType1d& a_nmode, double volume) {
ArrayType1d::ArrayType P(boost::extents[a_nmode.array->num_elements()]);
computePower(P, density, a_keys, a_adjust, a_nmode, volume);
CosmoTool::hdf5_write_array(f, n, P);
}
}
};
#endif

View file

@ -0,0 +1,156 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/ptree_proxy.hpp
Copyright (C) 2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#pragma once
#ifndef __LIBLSS_PTREE_PROXY_HPP
# define __LIBLSS_PTREE_PROXY_HPP
# include <boost/variant.hpp>
# include <string>
# include "libLSS/tools/ptree_translators.hpp"
# include <typeindex>
# include <functional>
namespace LibLSS {
class PropertyProxy {
protected:
typedef boost::variant<int, double, bool, std::string> PropertyType;
virtual PropertyType
real_get(std::string const &n, std::type_index v) const = 0;
virtual PropertyType
real_get(std::string const &n, PropertyType v) const = 0;
virtual boost::optional<PropertyType>
real_get_optional(std::string const &n, std::type_index v) const = 0;
public:
PropertyProxy() {}
~PropertyProxy() {}
template <typename T>
inline T get(std::string const &n) const {
return boost::get<T>(real_get(n, typeid(T)));
}
template <typename T>
inline T get(std::string const &n, T def_value) const {
return boost::get<T>(real_get(n, PropertyType(def_value)));
}
template <typename T>
inline boost::optional<T> get_optional(std::string const &n) const {
auto ret = real_get_optional(n, typeid(T));
if (!ret)
return boost::optional<T>();
else
return boost::optional<T>(boost::get<T>(*ret));
}
};
template <typename ptree>
class PropertyProxyPtree : public PropertyProxy {
protected:
typedef ptree PropertyTree;
PropertyTree tree;
std::map<std::type_index, std::function<PropertyType(std::string const &)>>
getters;
std::map<
std::type_index,
std::function<boost::optional<PropertyType>(std::string const &)>>
optional_getters;
virtual boost::optional<PropertyType>
real_get_optional(std::string const &n, std::type_index ti) const {
return optional_getters.find(ti)->second(n);
}
virtual PropertyType
real_get(std::string const &n, std::type_index ti) const {
return getters.find(ti)->second(n);
}
virtual PropertyType real_get(std::string const &n, PropertyType v) const {
return boost::apply_visitor(
[this, &n, &v](auto v_hint) {
auto o = this->tree.template get_optional<decltype(v_hint)>(n);
if (o) return PropertyType(*o);
return v;
},
v);
}
template <typename U>
PropertyType implement_getter(std::string const &n) const {
return this->tree.template get<U>(n);
}
template <typename U>
boost::optional<PropertyType> implement_optional_getter(std::string const &n) const {
auto ret = this->tree.template get_optional<U>(n);
if (!ret)
return boost::optional<PropertyType>();
return boost::optional<PropertyType>(*ret);
}
template <typename U>
inline void setup_getters(boost::variant<U>) {
getters[typeid(U)] = std::bind(
&PropertyProxyPtree<ptree>::implement_getter<U>, this,
std::placeholders::_1);
optional_getters[typeid(U)] = std::bind(
&PropertyProxyPtree<ptree>::implement_optional_getter<U>, this,
std::placeholders::_1);
}
template <typename U, typename V, typename... T>
inline void setup_getters(boost::variant<U, V, T...>) {
getters[typeid(U)] = std::bind(
&PropertyProxyPtree<ptree>::implement_getter<U>, this,
std::placeholders::_1);
optional_getters[typeid(U)] = std::bind(
&PropertyProxyPtree<ptree>::implement_optional_getter<U>, this,
std::placeholders::_1);
setup_getters(boost::variant<V, T...>());
}
public:
PropertyProxyPtree(PropertyTree &tree_) : tree(tree_) {
setup_getters(PropertyType());
}
PropertyProxyPtree(boost::optional<PropertyTree &> tree_) {
if (tree_)
tree = *tree_;
setup_getters(PropertyType());
}
~PropertyProxyPtree() {}
};
template <typename T>
auto make_proxy_property_tree(T &tree_) {
return PropertyProxyPtree<T>(tree_);
}
template <typename T>
auto make_proxy_property_tree(boost::optional<T &> tree_) {
return PropertyProxyPtree<T>(tree_);
}
} // namespace LibLSS
#endif
// ARES TAG: num_authors = 1
// ARES TAG: name(0) = Guilhem Lavaux
// ARES TAG: year(0) = 2020
// ARES TAG: email(0) = guilhem.lavaux@iap.fr

View file

@ -0,0 +1,67 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/ptree_proxy_map.hpp
Copyright (C) 2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_PTREE_PROXY_MAP_HPP
# define __LIBLSS_PTREE_PROXY_MAP_HPP
# include "libLSS/tools/ptree_proxy.hpp"
namespace LibLSS {
/**
* @brief Class to holds a simple map that stores property fro PropertyProxy
*/
class PropertyFromMap : public LibLSS::PropertyProxy {
protected:
std::map<std::string, PropertyType> properties;
PropertyType
real_get(std::string const &n, std::type_index v) const override {
auto r = real_get_optional(n, v);
if (!r)
throw std::runtime_error("Missing entry");
return *r;
}
PropertyType real_get(std::string const &n, PropertyType v) const override {
auto i = properties.find(n);
if (i == properties.end())
return v;
return i->second;
}
boost::optional<PropertyType>
real_get_optional(std::string const &n, std::type_index v) const override {
auto i = properties.find(n);
if (i == properties.end())
return boost::optional<PropertyType>();
return boost::optional<PropertyType>(i->second);
}
public:
/**
* @brief Constructor
*/
PropertyFromMap() : PropertyProxy() {}
/**
* @brief assign a value to some property
* @params n name of the property
* @params prop value of the property, must be one of the supported variant
*/
void set(std::string const &n, PropertyType prop) { properties[n] = prop; }
};
} // namespace LibLSS
#endif
// ARES TAG: num_authors = 1
// ARES TAG: author(0) = Guilhem Lavaux
// ARES TAG: email(0) = guilhem.lavaux@iap.fr
// ARES TAG: year(0) = 2020

View file

@ -0,0 +1,130 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/ptree_translators.hpp
Copyright (C) 2016-2018 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_PTREE_TRANSLATORS_HPP
#define __LIBLSS_PTREE_TRANSLATORS_HPP
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include "libLSS/tools/errors.hpp"
#include "libLSS/tools/string_tools.hpp"
namespace LibLSS {
namespace PTreeTools {
// Custom translator for bool (only supports std::string)
struct BoolTranslator {
typedef std::string internal_type;
typedef bool external_type;
// Converts a string to bool
boost::optional<external_type> get_value(const internal_type &str) {
Console::instance().print<LOG_VERBOSE>("Translating " + str);
if (!str.empty()) {
using boost::algorithm::iequals;
if (iequals(str, "true") || iequals(str, "yes") || str == "1")
return boost::optional<external_type>(true);
else if (iequals(str, "false") || iequals(str, "no") || str == "0")
return boost::optional<external_type>(false);
else {
Console::instance().print<LOG_ERROR>(
"Error while translating to boolean.");
throw ErrorBadCast(
"String '" + str + "' cannot be cast to boolean");
}
} else {
Console::instance().print<LOG_VERBOSE>(" =+= String empty");
return boost::optional<external_type>(boost::none);
}
}
// Converts a bool to string
boost::optional<internal_type> put_value(const external_type &b) {
return boost::optional<internal_type>(b ? "true" : "false");
}
};
} // namespace PTreeTools
} // namespace LibLSS
/* Specialize translator_between so that it uses our custom translator for
bool value types. Specialization must be in boost::property_tree
namespace. */
namespace boost {
namespace property_tree {
template <typename Ch, typename Traits, typename Alloc>
struct translator_between<std::basic_string<Ch, Traits, Alloc>, bool> {
typedef LibLSS::PTreeTools::BoolTranslator type;
};
} // namespace property_tree
} // namespace boost
namespace LibLSS { namespace PTreeTools {
template <typename T>
using RangeType = std::tuple<T, T>;
template <typename T>
struct RangeConverter {
typedef std::string internal_type;
typedef RangeType<T> external_type;
boost::optional<external_type> get_value(const internal_type &str) {
auto &cons = Console::instance();
cons.print<LOG_VERBOSE>("Translating range string " + str);
if (!str.empty()) {
auto strRange = tokenize(str,"-");
if (strRange.size() != 2) {
cons.print<LOG_ERROR>("Invalid range");
return boost::optional<external_type>(boost::none);
}
auto translate =
boost::property_tree::translator_between<std::string, T>::type();
auto minRange = translate.get_value(strRange[0]);
auto maxRange = translate.get_value(strRange[1]);
return boost::optional<external_type>(RangeType<T>(minRange, maxRange));
} else {
cons.print<LOG_VERBOSE>("Empty string...");
return boost::optional<external_type>(boost::none);
}
}
};
} } // namespace LibLSS::PTreeTools
/* Specialize translator_between so that it uses our custom translator for
bool value types. Specialization must be in boost::property_tree
namespace. */
namespace boost {
namespace property_tree {
template <typename T, typename Ch, typename Traits, typename Alloc>
struct translator_between<
std::basic_string<Ch, Traits, Alloc>,
LibLSS::PTreeTools::RangeType<T>> {
typedef LibLSS::PTreeTools::RangeConverter<T> type;
};
} // namespace property_tree
} // namespace boost
#endif
// ARES TAG: authors_num = 2
// ARES TAG: name(0) = Guilhem Lavaux
// ARES TAG: email(0) = guilhem.lavaux@iap.fr
// ARES TAG: year(0) = 2016-2018

View file

@ -0,0 +1,31 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/ptree_vectors.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_PTREE_VECTORS_HPP
#define __LIBLSS_PTREE_VECTORS_HPP
#include <string>
#include <boost/lexical_cast.hpp>
#include <vector>
#include "libLSS/tools/string_tools.hpp"
namespace LibLSS {
template<typename T>
std::vector<T> string_as_vector(const std::string& s, std::string const& separator)
{
auto tokens = tokenize(s, separator);
std::vector<T> result;
std::transform(tokens.begin(), tokens.end(), std::back_inserter(result),
[](std::string const& s)->T { return boost::lexical_cast<T>(s); });
return result;
}
}
#endif

View file

@ -0,0 +1,44 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/push_operators.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_PUSH_OPERATORS
#define __LIBLSS_PUSH_OPERATORS
namespace LibLSS {
template<bool accum>
struct push_to {
template<typename T>
static void apply(T& ref, const T& value);
template<typename T>
void operator()(T& ref, const T& value) {
apply(ref, value);
}
};
template<>
struct push_to<true> {
template<typename T>
static void apply(T& ref, const T& value) {
ref += value;
}
};
template<>
struct push_to<false> {
template<typename T>
static void apply(T& ref, const T& value) {
ref = value;
}
};
}
#endif

View file

@ -0,0 +1,28 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/ref_tools.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef _LIBLSS_TOOLS_REF_TOOLS_HPP
#define _LIBLSS_TOOLS_REF_TOOLS_HPP
namespace LibLSS {
// This utility struct is used to remove rvalue references and use copy instead.
// We cannot allow ourselves to store references to temporary objects, only stable
// objects.
template<typename T> struct strip_rvalue_ref {
typedef T type;
};
template<typename T> struct strip_rvalue_ref<T&&> {
typedef T type;
};
}
#endif

100
libLSS/tools/sigcatcher.cpp Normal file
View file

@ -0,0 +1,100 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/sigcatcher.cpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#include <algorithm>
#include <cstring>
#include <unistd.h>
#include <signal.h>
#include "libLSS/tools/static_init.hpp"
#include "libLSS/tools/console.hpp"
#include "libLSS/tools/log_traits.hpp"
#include "libLSS/tools/sigcatcher.hpp"
AUTO_REGISTRATOR_IMPL(sigCatcher);
namespace LibLSS {
namespace {
template<typename iterator>
iterator int_to_str_safe(iterator start, iterator max_end, int value)
{
int dec_place = 0;
int save = value;
iterator i = start, end;
if (value == 0) {
if (i == max_end) return i;
*i++ = '0';
return i;
}
while (i < max_end && value > 0) {
*i = '0' + (value % 10);
value /= 10;
i++;
}
end = i;
if (i != max_end) i--;
while (start < i) {
std::swap(*start, *i);
start++;
i--;
}
return end;
}
static void signalCatcher(int s) {
static const int Nmax=1024;
static char staticBuffer[Nmax];
Console& cons = Console::instance();
int rank = MPI_Communication::instance()->rank();
int l;
char *end;
int rc;
static const char SEGV_msg[] = "****** Handling SEGV ******\n";
static const char SEGV_base[] = "SEGV on rank=";
rc = write(2, SEGV_msg, sizeof(SEGV_msg)-1);
strncpy(staticBuffer, SEGV_base, Nmax);
l = sizeof(SEGV_base)-1;
end = int_to_str_safe(staticBuffer + l, staticBuffer + Nmax, rank);
if (end-staticBuffer < sizeof(staticBuffer))
*end++ = '\n';
rc = write(2, staticBuffer, end - staticBuffer);
close(2);
abort();
}
struct sigaction old_act;
void initializeSigCatcher() {
struct sigaction act;
act.sa_handler = &signalCatcher;
sigfillset(&act.sa_mask);
act.sa_flags = SA_RESETHAND;
sigaction(SIGSEGV, &act, &old_act);
}
void doneSigCatcher() {
sigaction(SIGSEGV, &old_act, 0);
}
LibLSS::RegisterStaticInit reg(initializeSigCatcher, doneSigCatcher, 1, "SIGCATCHER"); // Just after console is initialized
}
}

View file

@ -0,0 +1,17 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/sigcatcher.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_SIGCATCHER_HPP
#define __LIBLSS_SIGCATCHER_HPP
#include "libLSS/tools/static_auto.hpp"
AUTO_REGISTRATOR_DECL(sigCatcher);
#endif

View file

@ -0,0 +1,29 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/static_auto.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_STATIC_AUTO_HPP
#define __LIBLSS_STATIC_AUTO_HPP
#define AUTO_CLASS_NAME(N) RegistratorHelper_##N
#define AUTO_REGISTRATOR_DECL(N) \
namespace LibLSS { \
namespace StaticInitDummy { \
struct AUTO_CLASS_NAME(N) { \
AUTO_CLASS_NAME(N)(); \
}; \
\
static AUTO_CLASS_NAME(N) helper_## N; \
} \
}
#define AUTO_REGISTRATOR_IMPL(N) LibLSS::StaticInitDummy::AUTO_CLASS_NAME(N)::AUTO_CLASS_NAME(N)() {}
#endif

View file

@ -0,0 +1,16 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/static_init.cpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#include "libLSS/tools/static_init.hpp"
LibLSS::StaticInit& LibLSS::StaticInit::instance() {
static StaticInit singleton;
return singleton;
}

View file

@ -0,0 +1,200 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/static_init.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_STATIC_INIT_HPP
#define __LIBLSS_STATIC_INIT_HPP
#include <vector>
#include <queue>
#include <boost/function.hpp>
#include <string>
#include "libLSS/tools/console.hpp"
#include "libLSS/tools/log_traits.hpp"
namespace LibLSS {
#if !defined(DOXYGEN_SHOULD_SKIP_THIS)
class RegisterStaticInitBase {
protected:
int priority;
std::string text;
friend struct CompareStaticInit;
friend struct CompareStaticFinal;
friend class StaticInit;
public:
virtual void executeStaticInit() = 0;
virtual void executeStaticFinal() = 0;
virtual ~RegisterStaticInitBase() {}
};
struct CompareStaticInit {
bool operator()(RegisterStaticInitBase *a, RegisterStaticInitBase *b) {
return a->priority >= b->priority;
}
};
struct CompareStaticFinal {
bool operator()(RegisterStaticInitBase *a, RegisterStaticInitBase *b) {
return a->priority <= b->priority;
}
};
#endif
/**
* Helper class to handle initialization of some global state.
* There is by design only one instance of this class. It may obtained
* through the static method StaticInit::instance().
* Nearly all test cases give an example of the use this API. Typically,
* this is the first line of code and the last line of code in the "main"
* function.
* @code
* int main() {
* StaticInit::initialize();
* // Do something
* StaticInit::finalize();
* return 0;
* }
* @endcode
* The initializers and finalizers are executed according to their priority code,
* provided at the registration.
*
* @see LibLSS::RegisterStaticInit
* @see LibLSS::Console
*/
class StaticInit {
private:
StaticInit() {}
void _execute() {
while (!all_initializers.empty()) {
RegisterStaticInitBase *i = all_initializers.top();
if (i->text.length() > 0) {
Console::instance().print<LOG_DEBUG>("INIT: " + i->text);
}
i->executeStaticInit();
all_initializers.pop();
}
}
void _finalize() {
while (!all_finalizers.empty()) {
RegisterStaticInitBase *i = all_finalizers.top();
if (i->text.length() > 0) {
Console::instance().print<LOG_DEBUG>("CLEANUP: " + i->text);
}
i->executeStaticFinal();
all_finalizers.pop();
}
}
public:
static const int MAX_PRIORITY = 0;
static const int MIN_PRIORITY = 99;
typedef std::priority_queue<RegisterStaticInitBase *, std::vector<RegisterStaticInitBase *>, CompareStaticInit> InitList;
typedef std::priority_queue<RegisterStaticInitBase *, std::vector<RegisterStaticInitBase *>, CompareStaticFinal> FinalList;
InitList all_initializers;
FinalList all_finalizers;
static StaticInit& instance();
/**
* @see LibLSS::RegisterStaticInit
*/
void add(RegisterStaticInitBase *b) {
all_initializers.push(b);
all_finalizers.push(b);
}
/**
* Run all initializers.
*/
static void execute() {
instance()._execute();
}
/**
* Run all finalizers.
*/
static void finalize() {
instance()._finalize();
}
};
/**
* Specific implementation of a registrator for static initializer .
* It introduces itself directly in the queue of the sole instance of the class LibLSS::StaticInit.
* They then call the adequate functors in initialization or finalization stage.
*/
class RegisterStaticInit: public RegisterStaticInitBase {
public:
/**
* Hold the initializer functor.
*/
std::function<void()> function;
/**
* Hold the finalizer functor.
*/
std::function<void()> function_final;
/**
* Constructor, without finalizer.
*
* @param f The functor corresponding to initialization. There is no finalizer in this case.
* @param _priority The execution priority.
* @param _text an information to print in debug mode.
*/
template<typename Derived>
RegisterStaticInit(Derived f, int _priority = StaticInit::MIN_PRIORITY, const std::string& _text = "") {
function = f;
this->priority = _priority;
this->text = _text;
StaticInit::instance().all_initializers.push(this);
}
/**
* Constructor.
* @param f The functor corresponding to initialization.
* @param f2 The functor corresponding to finalization.
* @param _priority The execution priority.
* @param _text an information to print in debug mode.
*/
template<typename Derived1, typename Derived2>
RegisterStaticInit(Derived1 f, Derived2 f2, int _priority = StaticInit::MIN_PRIORITY, const std::string& _text = "") {
function = f;
function_final = f2;
this->priority = _priority;
this->text = _text;
StaticInit::instance().all_initializers.push(this);
StaticInit::instance().all_finalizers.push(this);
}
/**
* Do not use. This is called by the internal machinery.
*/
virtual void executeStaticInit() {
function();
}
/**
* Do not use. This is called by the internal machinery.
*/
virtual void executeStaticFinal() {
if (function_final)
function_final();
}
private:
RegisterStaticInit() {
}
};
};
#endif

View file

@ -0,0 +1,14 @@
#include <string>
#include <vector>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include "libLSS/tools/string_tools.hpp"
std::vector<std::string>
LibLSS::tokenize(std::string const &in, std::string const &seps) {
using namespace boost::algorithm;
std::vector<std::string> result;
split(result, in, is_any_of(seps), token_compress_on);
return result;
}

View file

@ -0,0 +1,72 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/string_tools.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_STRING_TOOLS_HPP
#define __LIBLSS_STRING_TOOLS_HPP
#include <string>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <vector>
#include <boost/format.hpp>
#include <boost/multi_array.hpp>
#include "libLSS/tools/is_stl_container.hpp"
namespace LibLSS {
using std::to_string;
template <typename T>
typename std::enable_if<is_stl_container_like<T>::value, std::string>::type
to_string(T const &V) {
std::ostringstream s;
std::copy(
V.begin(), V.end(),
std::ostream_iterator<typename T::value_type>(s, ","));
return s.str();
}
std::vector<std::string>
tokenize(std::string const &in, std::string const &separator);
namespace lssfmt {
namespace format_detail {
static void _format_expansion(boost::format &f) {}
template <typename A, typename... U>
static void _format_expansion(boost::format &f, A &&a, U &&...u) {
_format_expansion(f % a, u...);
}
template <typename... U>
std::string format(std::string const &s, U &&...args) {
boost::format f(s);
_format_expansion(f, std::forward<U>(args)...);
return boost::str(f);
}
} // namespace format_detail
using format_detail::format;
} // namespace lssfmt
/* template<typename T>
std::string to_string(boost::multi_array_ref<T,1> const& V) {
std::ostringstream s;
std::copy(V.begin(), V.end(), std::ostream_iterator<T>(s, ","));
return s.str();
}*/
} // namespace LibLSS
#endif

View file

@ -0,0 +1,25 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/timing_db.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_TOOLS_TIMING_DB_HPP
#define __LIBLSS_TOOLS_TIMING_DB_HPP
#include <CosmoTool/hdf5_array.hpp>
#include "libLSS/tools/hdf5_type.hpp"
namespace LibLSS {
namespace timings {
void load(H5_CommonFileGroup& g);
void save(H5_CommonFileGroup& g);
}
}
#endif

View file

@ -0,0 +1,79 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/tuple_helper.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_TUPLE_HELPER_HPP
#define __LIBLSS_TUPLE_HELPER_HPP
#include <tuple>
#include <utility>
namespace LibLSS {
template<size_t start, size_t N, typename Tuple>
struct _tuple_last_helper
{
template<typename... Args>
static inline
auto convert(Tuple t, Args&&... args)
-> decltype(
_tuple_last_helper<start,N-1,Tuple>::convert(t,
std::get<start+N-1>(t),
std::forward<Args>(args)...))
{
return _tuple_last_helper<start,N-1,Tuple>::convert(t, std::get<start+N-1>(t), std::forward<Args>(args)...);
}
};
template<size_t start, typename Tuple>
struct _tuple_last_helper<start,0,Tuple>
{
template<typename... Args>
static inline
auto convert(Tuple t, Args&&... args)
-> decltype( std::make_tuple(std::forward<Args>(args)...) )
{
return std::make_tuple(std::forward<Args>(args)...);
}
};
template<size_t start, typename Tuple>
auto last_of_tuple(Tuple t)
-> decltype(_tuple_last_helper<start, std::tuple_size<Tuple>::value-start, Tuple>::convert(t))
{
return _tuple_last_helper<start, std::tuple_size<Tuple>::value-start, Tuple>::convert(t);
}
//https://stackoverflow.com/questions/1198260/how-can-you-iterate-over-the-elements-of-an-stdtuple
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
tuple_for_each(std::tuple<Tp...> const&, FuncT&&) // Unused arguments are given no names.
{ }
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
tuple_for_each(std::tuple<Tp...> const& t, FuncT&& f)
{
f(std::get<I>(t));
tuple_for_each<I + 1, FuncT, Tp...>(t, std::forward<FuncT>(f));
}
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
tuple_for_each(std::tuple<Tp...>&& t, FuncT&& f)
{
f(std::get<I>(t));
tuple_for_each<I + 1, FuncT, Tp...>(std::forward<std::tuple<Tp...>&&>(t), std::forward<FuncT>(f));
}
}
#endif

View file

@ -0,0 +1,132 @@
/*+
ARES/HADES/BORG Package -- ./libLSS/tools/uninitialized_type.hpp
Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
Additional contributions from:
Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
+*/
#ifndef __LIBLSS_TOOLS_UNINITIALIZED_HPP
#define __LIBLSS_TOOLS_UNINITIALIZED_HPP
#pragma once
#include <boost/multi_array.hpp>
#include "libLSS/tools/memusage.hpp"
namespace LibLSS {
template <
typename T, std::size_t NumDims,
typename Allocator = LibLSS::track_allocator<T>>
class UninitializedAllocation {
public:
typedef boost::multi_array_ref<T, NumDims> array_type;
private:
T *ptr;
Allocator allocator_;
size_t maxNumElements;
array_type *array_builder;
UninitializedAllocation(UninitializedAllocation const &) {}
UninitializedAllocation &operator=(UninitializedAllocation const &) {
return *this;
}
public:
// Implement a move constructor, but the copy constructor is disabled.
UninitializedAllocation(UninitializedAllocation &&other)
: ptr(other.ptr), array_builder(other.array_builder),
maxNumElements(other.maxNumElements) {
other.array_builder = 0;
other.ptr = 0;
other.maxNumElements = 0;
}
template <typename T2>
explicit UninitializedAllocation(T2 extents, Allocator const &alloc)
: allocator_(alloc), array_builder(new array_type(0, extents)) {
typename Allocator::const_pointer no_hint = 0;
ptr = allocator_.allocate(array_builder->num_elements(), no_hint);
delete array_builder;
array_builder = new array_type(ptr, extents);
maxNumElements = array_builder->num_elements();
}
template <typename T2, typename Order>
explicit UninitializedAllocation(
T2 extents, Allocator const &alloc, const Order &order)
: allocator_(alloc), array_builder(new array_type(0, extents, order)) {
typename Allocator::const_pointer no_hint = 0;
ptr = allocator_.allocate(array_builder->num_elements(), no_hint);
delete array_builder;
array_builder = new array_type(ptr, extents, order);
maxNumElements = array_builder->num_elements();
}
template <typename T2>
explicit UninitializedAllocation(T2 extents)
: array_builder(new boost::multi_array_ref<T, NumDims>(0, extents)) {
typename Allocator::const_pointer no_hint = 0;
ptr = allocator_.allocate(array_builder->num_elements(), no_hint);
delete array_builder;
array_builder = new array_type(ptr, extents);
maxNumElements = array_builder->num_elements();
}
template <typename T2>
void reshape(T2 extents) {
delete array_builder;
array_builder = new array_type(ptr, extents);
Console::instance().c_assert(
array_builder->num_elements() <= maxNumElements, "Invalid reshaping");
}
~UninitializedAllocation() {
if (ptr != 0)
allocator_.deallocate(ptr, array_builder->num_elements());
if (array_builder != 0)
delete array_builder;
}
T *get() { return ptr; }
array_type &get_array() { return *array_builder; }
operator array_type &() { return *array_builder; }
};
template <
typename Array,
typename Allocator = LibLSS::track_allocator<typename Array::element>>
class UninitializedArray
: public UninitializedAllocation<
typename Array::element, Array::dimensionality, Allocator> {
public:
typedef UninitializedAllocation<
typename Array::element, Array::dimensionality, Allocator>
super_type;
typedef typename super_type::array_type array_type;
UninitializedArray(UninitializedArray<Array, Allocator> &&other)
: super_type(std::forward<super_type>(other)) {}
template <typename T2>
explicit UninitializedArray(T2 extents, Allocator const &alloc)
: super_type(extents, alloc) {}
template <typename T2, typename Order>
explicit UninitializedArray(
T2 extents, Allocator const &alloc, const Order &order)
: super_type(extents, alloc, order) {}
template <typename T2>
explicit UninitializedArray(T2 extents) : super_type(extents) {}
};
template <
typename T, size_t N, typename Allocator = LibLSS::track_allocator<T>>
using U_Array = UninitializedArray<boost::multi_array_ref<T, N>, Allocator>;
} // namespace LibLSS
#endif