Skip to content

File derivatives.hpp

File List > include > derivatives.hpp

Go to the documentation of this file

#pragma once

#include <cstddef>
#include <tuple>
#include <utility>
#include <mdspan/mdspan.hpp>


namespace cip {


template <std::size_t N, std::size_t D, std::size_t current = 0, typename Func, typename XiArray, typename... Indices>
void iterate_over_indices(Func&& func, XiArray const& xi, Indices... indices) {
    if constexpr (current == N) {
        func(indices...);
    } else {
        if constexpr (current == D) {
            iterate_over_indices<N, D, current + 1>(std::forward<Func>(func), xi, indices...);
        } else {
            for (std::size_t i = 0; i < xi[current].size(); ++i)
            {
                iterate_over_indices<N, D, current + 1>(std::forward<Func>(func), xi, indices..., i);
            }
        }
    }
}

template <std::size_t I, std::size_t N, std::size_t D, typename Tuple, typename Accum>
constexpr auto build_coordinate_indices_impl(const Tuple& t, Accum acc) {
    if constexpr (I == N) {
        return acc;
    } else {
        if constexpr (I == D) {
            return build_coordinate_indices_impl<I + 1, N, D>(
                t, std::tuple_cat(acc, std::make_tuple(std::full_extent))
            );
        } else if constexpr (I < D) {
            return build_coordinate_indices_impl<I + 1, N, D>(
                t, std::tuple_cat(acc, std::make_tuple(std::get<I>(t)))
            );
        } else {
            return build_coordinate_indices_impl<I + 1, N, D>(
                t, std::tuple_cat(acc, std::make_tuple(std::get<I - 1>(t)))
            );
        }
    }
}

template <std::size_t N, std::size_t D, typename Tuple>
constexpr auto build_coordinate_indices(const Tuple& t) {
    return build_coordinate_indices_impl<0, N, D>(t, std::tuple<>());
}

template <typename FType, typename CoordTuple, typename DerivTuple>
auto call_submdspan_1d(FType& F, const CoordTuple& coord, const DerivTuple& deriv) {
    return std::apply([&F](const auto&... args) {
        return F.submdspan_1d(args...);
    }, std::tuple_cat(coord, deriv));
}

template <typename FType, typename SlopeType, typename CoordTuple, typename DerivTuple>
void call_move_into_submdspan(FType& F, SlopeType&& slopes, const CoordTuple& coord, const DerivTuple& deriv) {
    std::apply([&F, &slopes](const auto&... args) {
        F.move_into_submdspan(std::forward<SlopeType>(slopes),args...);
    }, std::tuple_cat(coord, deriv));
}

constexpr std::size_t SIZE_T_ZERO = 0;

template <std::size_t N>
constexpr auto make_zero_tuple() {
    return []<std::size_t... I>(std::index_sequence<I...>) {
        return std::make_tuple(((void)I, SIZE_T_ZERO)...);
    }(std::make_index_sequence<N>{});
}

template <std::size_t D, typename Tuple, typename T>
constexpr auto update_tuple_element(const Tuple& tup, T new_value) {
    constexpr std::size_t N = std::tuple_size_v<Tuple>;
    return []<std::size_t... Is>(const Tuple& tup, T new_value, std::index_sequence<Is...>) {
        return std::make_tuple((Is == D ? new_value : std::get<Is>(tup))...);
    }(tup, new_value, std::make_index_sequence<N>{});
}

template <std::size_t N, typename Tuple, typename Func, std::size_t... Is>
constexpr void for_each_dimension_impl(const Tuple& tup, Func&& func, std::index_sequence<Is...>) {
    ((std::get<Is>(tup) == 0 ? (func(std::integral_constant<std::size_t, Is>{}), 0) : 0), ...);
}

template <std::size_t N, typename Tuple, typename Func>
constexpr void for_each_dimension(const Tuple& tup, Func&& func) {
    for_each_dimension_impl<N>(tup, std::forward<Func>(func), std::make_index_sequence<N>{});
}

template <std::size_t N, typename FType, typename XiArray, typename DerivTuple, typename CalcSlopesFunctor>
void compute_mixed_derivatives_impl(FType& F, XiArray const& xi, const DerivTuple& currentDeriv, CalcSlopesFunctor calcSlopes, std::size_t start = 0) {
    for_each_dimension<N>(currentDeriv, [&](const auto d_const) {
        constexpr std::size_t D = d_const.value;
        if (D < start) {
            return;
        }
        iterate_over_indices<N, D>([&, d_const](auto... loopIndices) {
            constexpr std::size_t D = d_const.value;
            auto indicesTuple = std::make_tuple(loopIndices...);
            auto coord = build_coordinate_indices<N, D>(indicesTuple);
            auto extractionPattern = update_tuple_element<D>(currentDeriv, 0);
            auto f_slice = call_submdspan_1d(F, coord, extractionPattern);
            auto newDeriv = update_tuple_element<D>(currentDeriv, 1);
            call_move_into_submdspan(F, calcSlopes(xi[D], f_slice), coord, newDeriv);
        }, xi);
        auto newDeriv = update_tuple_element<D>(currentDeriv, 1);
        compute_mixed_derivatives_impl<N>(F, xi, newDeriv, calcSlopes, D+1);
    });
}

template <std::size_t N, typename FType, typename XiArray, typename CalcSlopesFunctor>
void compute_mixed_derivatives(FType& F, XiArray const& xi, CalcSlopesFunctor calcSlopes) {
    auto basePattern = make_zero_tuple<N>();
    compute_mixed_derivatives_impl<N>(F, xi, basePattern, calcSlopes, 0);
}


}  // namespace cip