Initial import
This commit is contained in:
commit
56a50eead3
820 changed files with 192077 additions and 0 deletions
23
libLSS/tools/align_helper.hpp
Normal file
23
libLSS/tools/align_helper.hpp
Normal 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
|
22
libLSS/tools/allocator_policy.hpp
Normal file
22
libLSS/tools/allocator_policy.hpp
Normal 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
|
102
libLSS/tools/array_concepts.hpp
Normal file
102
libLSS/tools/array_concepts.hpp
Normal 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
|
315
libLSS/tools/array_tools.hpp
Normal file
315
libLSS/tools/array_tools.hpp
Normal 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
|
128
libLSS/tools/auto_interpolator.hpp
Normal file
128
libLSS/tools/auto_interpolator.hpp
Normal 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
|
56
libLSS/tools/bisection.hpp
Normal file
56
libLSS/tools/bisection.hpp
Normal 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
29
libLSS/tools/checkmem.hpp
Normal 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
|
54
libLSS/tools/color_mod.hpp
Normal file
54
libLSS/tools/color_mod.hpp
Normal 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
|
9
libLSS/tools/compiler_tools.hpp
Normal file
9
libLSS/tools/compiler_tools.hpp
Normal 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
253
libLSS/tools/console.cpp
Normal 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
506
libLSS/tools/console.hpp
Normal 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 ¤t() { return *currentContext; }
|
||||
|
||||
ConsoleContextBase();
|
||||
~ConsoleContextBase();
|
||||
};
|
||||
|
||||
class ConsoleContextDummy {
|
||||
public:
|
||||
std::string getMessage() const { return std::string(); }
|
||||
static inline ConsoleContextBase ¤t() { 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
|
19
libLSS/tools/cpu/feature_check.hpp
Normal file
19
libLSS/tools/cpu/feature_check.hpp
Normal 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
|
90
libLSS/tools/cpu/feature_check_gnuc.hpp
Normal file
90
libLSS/tools/cpu/feature_check_gnuc.hpp
Normal 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
|
15
libLSS/tools/cpu/feature_check_other.hpp
Normal file
15
libLSS/tools/cpu/feature_check_other.hpp
Normal 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
149
libLSS/tools/defer.hpp
Normal 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
226
libLSS/tools/domains.cpp
Normal 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 ¤t_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 ¤t_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
146
libLSS/tools/domains.hpp
Normal 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
64
libLSS/tools/errors.hpp
Normal 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
|
18
libLSS/tools/fftw_allocator.cpp
Normal file
18
libLSS/tools/fftw_allocator.cpp
Normal 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);
|
||||
*/
|
79
libLSS/tools/fftw_allocator.hpp
Normal file
79
libLSS/tools/fftw_allocator.hpp
Normal 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
|
17
libLSS/tools/function_name.hpp
Normal file
17
libLSS/tools/function_name.hpp
Normal 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
|
28
libLSS/tools/fuse/healpix.hpp
Normal file
28
libLSS/tools/fuse/healpix.hpp
Normal 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
|
241
libLSS/tools/fuse/operators.hpp
Normal file
241
libLSS/tools/fuse/operators.hpp
Normal 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);
|
488
libLSS/tools/fused_array.hpp
Normal file
488
libLSS/tools/fused_array.hpp
Normal 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
|
186
libLSS/tools/fused_assign.hpp
Normal file
186
libLSS/tools/fused_assign.hpp
Normal 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
148
libLSS/tools/fused_cond.hpp
Normal 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
|
184
libLSS/tools/fused_masked_assign.hpp
Normal file
184
libLSS/tools/fused_masked_assign.hpp
Normal 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
|
359
libLSS/tools/fused_reduce.hpp
Normal file
359
libLSS/tools/fused_reduce.hpp
Normal 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
|
321
libLSS/tools/fusewrapper.hpp
Normal file
321
libLSS/tools/fusewrapper.hpp
Normal 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 ©_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
|
49
libLSS/tools/gslIntegrate.hpp
Normal file
49
libLSS/tools/gslIntegrate.hpp
Normal 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
|
49
libLSS/tools/gsl_error.cpp
Normal file
49
libLSS/tools/gsl_error.cpp
Normal 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);
|
21
libLSS/tools/gsl_error.hpp
Normal file
21
libLSS/tools/gsl_error.hpp
Normal 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
|
133
libLSS/tools/hdf5_buffered_write.hpp
Normal file
133
libLSS/tools/hdf5_buffered_write.hpp
Normal 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
|
62
libLSS/tools/hdf5_error.cpp
Normal file
62
libLSS/tools/hdf5_error.cpp
Normal 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);
|
17
libLSS/tools/hdf5_error.hpp
Normal file
17
libLSS/tools/hdf5_error.hpp
Normal 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
|
61
libLSS/tools/hdf5_scalar.hpp
Normal file
61
libLSS/tools/hdf5_scalar.hpp
Normal 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
|
25
libLSS/tools/hdf5_type.hpp
Normal file
25
libLSS/tools/hdf5_type.hpp
Normal 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
|
66
libLSS/tools/is_stl_container.hpp
Normal file
66
libLSS/tools/is_stl_container.hpp
Normal 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
|
97
libLSS/tools/itertools.hpp
Normal file
97
libLSS/tools/itertools.hpp
Normal 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
|
86
libLSS/tools/log_traits.cpp
Normal file
86
libLSS/tools/log_traits.cpp
Normal 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);
|
90
libLSS/tools/log_traits.hpp
Normal file
90
libLSS/tools/log_traits.hpp
Normal 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
106
libLSS/tools/memusage.cpp
Normal 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
55
libLSS/tools/memusage.hpp
Normal 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
|
14
libLSS/tools/mpi_fftw/copy_utils.hpp
Normal file
14
libLSS/tools/mpi_fftw/copy_utils.hpp
Normal 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"
|
||||
|
148
libLSS/tools/mpi_fftw/copy_utils_degrade.hpp
Normal file
148
libLSS/tools/mpi_fftw/copy_utils_degrade.hpp
Normal 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);
|
||||
}
|
||||
};
|
158
libLSS/tools/mpi_fftw/copy_utils_upgrade.hpp
Normal file
158
libLSS/tools/mpi_fftw/copy_utils_upgrade.hpp
Normal 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>());
|
||||
}
|
||||
|
||||
|
||||
};
|
1020
libLSS/tools/mpi_fftw/impl_3d.hpp
Normal file
1020
libLSS/tools/mpi_fftw/impl_3d.hpp
Normal file
File diff suppressed because it is too large
Load diff
76
libLSS/tools/mpi_fftw/nyquist_downgrade.hpp
Normal file
76
libLSS/tools/mpi_fftw/nyquist_downgrade.hpp
Normal 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;
|
||||
}
|
||||
|
||||
};
|
75
libLSS/tools/mpi_fftw/nyquist_upgrade.hpp
Normal file
75
libLSS/tools/mpi_fftw/nyquist_upgrade.hpp
Normal 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]);
|
||||
}
|
||||
}
|
||||
};
|
180
libLSS/tools/mpi_fftw_helper.hpp
Normal file
180
libLSS/tools/mpi_fftw_helper.hpp
Normal 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
|
248
libLSS/tools/nary_arrays.hpp
Normal file
248
libLSS/tools/nary_arrays.hpp
Normal 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
53
libLSS/tools/openmp.hpp
Normal 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
|
139
libLSS/tools/optimization/array_helper.hpp
Normal file
139
libLSS/tools/optimization/array_helper.hpp
Normal 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
|
265
libLSS/tools/optimization/cg.hpp
Normal file
265
libLSS/tools/optimization/cg.hpp
Normal 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
57
libLSS/tools/overload.hpp
Normal 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
|
33
libLSS/tools/phoenix_vars.hpp
Normal file
33
libLSS/tools/phoenix_vars.hpp
Normal 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
|
66
libLSS/tools/powerspectrum/measure.hpp
Normal file
66
libLSS/tools/powerspectrum/measure.hpp
Normal 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
|
156
libLSS/tools/ptree_proxy.hpp
Normal file
156
libLSS/tools/ptree_proxy.hpp
Normal 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
|
67
libLSS/tools/ptree_proxy_map.hpp
Normal file
67
libLSS/tools/ptree_proxy_map.hpp
Normal 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
|
130
libLSS/tools/ptree_translators.hpp
Normal file
130
libLSS/tools/ptree_translators.hpp
Normal 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
|
31
libLSS/tools/ptree_vectors.hpp
Normal file
31
libLSS/tools/ptree_vectors.hpp
Normal 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
|
44
libLSS/tools/push_operators.hpp
Normal file
44
libLSS/tools/push_operators.hpp
Normal 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
|
28
libLSS/tools/ref_tools.hpp
Normal file
28
libLSS/tools/ref_tools.hpp
Normal 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
100
libLSS/tools/sigcatcher.cpp
Normal 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
|
||||
|
||||
}
|
||||
|
||||
}
|
17
libLSS/tools/sigcatcher.hpp
Normal file
17
libLSS/tools/sigcatcher.hpp
Normal 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
|
29
libLSS/tools/static_auto.hpp
Normal file
29
libLSS/tools/static_auto.hpp
Normal 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
|
16
libLSS/tools/static_init.cpp
Normal file
16
libLSS/tools/static_init.cpp
Normal 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;
|
||||
}
|
200
libLSS/tools/static_init.hpp
Normal file
200
libLSS/tools/static_init.hpp
Normal 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
|
14
libLSS/tools/string_tools.cpp
Normal file
14
libLSS/tools/string_tools.cpp
Normal 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;
|
||||
}
|
72
libLSS/tools/string_tools.hpp
Normal file
72
libLSS/tools/string_tools.hpp
Normal 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
|
25
libLSS/tools/timing_db.hpp
Normal file
25
libLSS/tools/timing_db.hpp
Normal 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
|
79
libLSS/tools/tuple_helper.hpp
Normal file
79
libLSS/tools/tuple_helper.hpp
Normal 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
|
132
libLSS/tools/uninitialized_type.hpp
Normal file
132
libLSS/tools/uninitialized_type.hpp
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue