diff --git a/src/pymatlib/core/cpp/double_lookup_interpolation.cpp b/src/pymatlib/core/cpp/double_lookup_interpolation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fab595dba5b86d93d79b04b9ede300b3e65394c3 --- /dev/null +++ b/src/pymatlib/core/cpp/double_lookup_interpolation.cpp @@ -0,0 +1,149 @@ +#include "include/double_lookup_interpolation.h" +#include <cmath> + + +double interpolate_double_lookup( + double E_target, + const py::array_t<double>& T_eq, + const py::array_t<double>& E_neq, + const py::array_t<double>& E_eq, + const py::array_t<int32_t>& idx_map) { + + // Cache all buffer requests at start + const auto T_eq_buf = T_eq.request(); + const auto E_neq_buf = E_neq.request(); + const auto E_eq_buf = E_eq.request(); + const auto idx_map_buf = idx_map.request(); + + // Get all pointers + const double* __restrict__ T_eq_ptr = static_cast<double*>(T_eq_buf.ptr); + const double* __restrict__ E_neq_ptr = static_cast<double*>(E_neq_buf.ptr); + const double* __restrict__ E_eq_ptr = static_cast<double*>(E_eq_buf.ptr); + const int32_t* __restrict__ idx_map_ptr = static_cast<int32_t*>(idx_map_buf.ptr); + + // Quick boundary checks + if (E_target <= E_neq_ptr[0]) { + return T_eq_ptr[0]; + } + + const size_t last_idx = E_neq_buf.size - 1; + if (E_target >= E_neq_ptr[last_idx]) { + return T_eq_ptr[last_idx]; + } + + // Cache delta_E to avoid recomputation + const double delta_E = E_eq_ptr[1] - E_eq_ptr[0]; + const double E_eq_start = E_eq_ptr[0]; + + // Compute indices + const int idx_E_eq = std::min( + static_cast<int>((E_target - E_eq_start) / delta_E), + static_cast<int>(idx_map_buf.size - 1) + ); + + int idx_E_neq = idx_map_ptr[idx_E_eq]; + if (E_neq_ptr[idx_E_neq + 1] < E_target) { + ++idx_E_neq; + } + + // Linear interpolation with minimal temporaries + const double E1 = E_neq_ptr[idx_E_neq]; + const double dE = E_neq_ptr[idx_E_neq + 1] - E1; + const double T1 = T_eq_ptr[idx_E_neq]; + + return T1 + (T_eq_ptr[idx_E_neq + 1] - T1) * (E_target - E1) / dE; +} + +/* +double interpolate_double_lookup( + double E_target, + const py::array_t<double>& T_eq, + const py::array_t<double>& E_neq, + const py::array_t<double>& E_eq, + const py::array_t<int32_t>& idx_map) { + + auto T_eq_buf = T_eq.request(); + auto E_neq_buf = E_neq.request(); + auto E_eq_buf = E_eq.request(); + auto idx_map_buf = idx_map.request(); + + const double* T_eq_ptr = static_cast<double*>(T_eq_buf.ptr); + const double* E_neq_ptr = static_cast<double*>(E_neq_buf.ptr); + const double* E_eq_ptr = static_cast<double*>(E_eq_buf.ptr); + const int32_t* idx_map_ptr = static_cast<int32_t*>(idx_map_buf.ptr); + + if (E_target <= E_neq_ptr[0]) { + return T_eq_ptr[0]; + } + if (E_target >= E_neq_ptr[E_neq_buf.size - 1]) { + return T_eq_ptr[T_eq_buf.size - 1]; + } + + int idx_E_eq = static_cast<int>((E_target - E_eq_ptr[0]) / (E_eq_ptr[1] - E_eq_ptr[0])); + idx_E_eq = std::min(idx_E_eq, static_cast<int>(idx_map_buf.size - 1)); + + int idx_E_neq = idx_map_ptr[idx_E_eq]; + + if (E_neq_ptr[idx_E_neq + 1] < E_target) { + ++idx_E_neq; + } + + double E1 = E_neq_ptr[idx_E_neq]; + double E2 = E_neq_ptr[idx_E_neq + 1]; + double T1 = T_eq_ptr[idx_E_neq]; + double T2 = T_eq_ptr[idx_E_neq + 1]; + + return T1 + (T2 - T1) * (E_target - E1) / (E2 - E1); +} +*/ +/* +double interpolate_double_lookup( + double E_target, + const py::array_t<double>& T_eq, + const py::array_t<double>& E_neq, + const py::array_t<double>& E_eq, + const py::array_t<int32_t>& idx_map) { + + // Get buffer info once and cache sizes + const auto& E_neq_buf = E_neq.request(); + const auto E_neq_size = E_neq_buf.size; + const double* const E_neq_ptr = static_cast<double*>(E_neq_buf.ptr); + + // Quick boundary checks + if (E_target <= E_neq_ptr[0]) { + return static_cast<double*>(T_eq.request().ptr)[0]; + } + if (E_target >= E_neq_ptr[E_neq_size - 1]) { + return static_cast<double*>(T_eq.request().ptr)[E_neq_size - 1]; + } + + // Cache frequently used values + const double* const E_eq_ptr = static_cast<double*>(E_eq.request().ptr); + const double delta_E = E_eq_ptr[1] - E_eq_ptr[0]; + + // Calculate index in equidistant grid + const int idx_E_eq = std::min( + static_cast<int>((E_target - E_eq_ptr[0]) / delta_E), + static_cast<int>(idx_map.request().size - 1) + ); + + // Get mapped index and data pointers + int idx_E_neq = static_cast<int32_t*>(idx_map.request().ptr)[idx_E_eq]; + const double* const T_eq_ptr = static_cast<double*>(T_eq.request().ptr); + + // Adjust index if needed + if (E_neq_ptr[idx_E_neq + 1] < E_target) { + ++idx_E_neq; + } + + // Linear interpolation using direct array access + const double E1 = E_neq_ptr[idx_E_neq]; + const double E2 = E_neq_ptr[idx_E_neq + 1]; + const double T1 = T_eq_ptr[idx_E_neq]; + const double T2 = T_eq_ptr[idx_E_neq + 1]; + + return T1 + ((T2 - T1) * (E_target - E1)) / (E2 - E1); +} +*/ + + diff --git a/src/pymatlib/core/cpp/include/double_lookup_interpolation.h b/src/pymatlib/core/cpp/include/double_lookup_interpolation.h new file mode 100644 index 0000000000000000000000000000000000000000..936097b2c832dd3b09863ab9ef1e6a9d961704be --- /dev/null +++ b/src/pymatlib/core/cpp/include/double_lookup_interpolation.h @@ -0,0 +1,17 @@ +#ifndef DOUBLE_LOOKUP_INTERPOLATION_H +#define DOUBLE_LOOKUP_INTERPOLATION_H + +#include <pybind11/pybind11.h> +#include <pybind11/numpy.h> +#include "interpolate_double_lookup_cpp.h" + +namespace py = pybind11; + +double interpolate_double_lookup( + double E_target, + const py::array_t<double>& T_eq, + const py::array_t<double>& E_neq, + const py::array_t<double>& E_eq, + const py::array_t<int32_t>& idx_map); + +#endif // DOUBLE_LOOKUP_INTERPOLATION_H