diff --git a/include/aare/Models.hpp b/include/aare/Models.hpp index eee84e2..04df428 100644 --- a/include/aare/Models.hpp +++ b/include/aare/Models.hpp @@ -600,8 +600,17 @@ struct GaussianChargeSharing { return par[3] > 0.0; } - static std::array estimate_par(NDView x, - NDView y) { + /** + * @brief Data-driven initial parameter estimates. + * @param x Independent variable values + * @param y Dependent variable values + * @param elastic_scattering If true, take into account elastic scattering + * effects + * @return Initial parameter estimates + */ + static std::array + estimate_par(NDView x, NDView y, + const bool elastic_scattering = true) { const ssize_t n = y.size(); const auto max_it = std::max_element(y.begin(), y.end()); @@ -609,7 +618,9 @@ struct GaussianChargeSharing { const double x_range = std::max(x[n - 1] - x[0], 1e-9); - const ssize_t tail = std::min(std::max(n / 10, 2), n); + const ssize_t tail = std::min( + std::max(n / 10, 2), + n); // TODO: why hardcoded to 10 % - doesnt it depend on x range?? double left_mean = 0.0; double right_mean = 0.0; @@ -618,18 +629,22 @@ struct GaussianChargeSharing { left_mean += y[i]; left_mean /= tail; - for (ssize_t i = n - tail; i < n; ++i) - right_mean += y[i]; - right_mean /= tail; + if (elastic_scattering) { + for (ssize_t i = n - tail; i < n; ++i) + right_mean += y[i]; + right_mean /= tail; + } const double p0 = right_mean; const double p1 = 0.0; - const double mu = x[i_max]; - const double N = std::max(*max_it - right_mean, 1e-9); + const double mu = x[i_max]; + const double N = + std::max(*max_it - right_mean, 1e-9); // amplitude K_\alpha // Left plateau excess is roughly N*C. - const double C = (left_mean - right_mean) / N; + const double C = + (left_mean - right_mean) / N; // charge-sharing amplitude ratio double sigma = 0.1 * x_range; const double half = right_mean + 0.5 * ((*max_it) - right_mean); @@ -811,9 +826,16 @@ struct GaussianChargeSharingKb { return par[3] > 0.0; } - static std::array estimate_par(NDView x, - NDView y) { - const auto base = GaussianChargeSharing::estimate_par(x, y); + /** + * @brief Data-driven initial parameter estimates. + * @param elastic_scattering If true, take into account elastic scattering + * effects + */ + static std::array + estimate_par(NDView x, NDView y, + const bool elastic_scattering = true) { + const auto base = + GaussianChargeSharing::estimate_par(x, y, elastic_scattering); const double p0 = base[0]; const double p1 = base[1]; diff --git a/python/src/fit.hpp b/python/src/fit.hpp index b07dadf..70cd1f8 100644 --- a/python/src/fit.hpp +++ b/python/src/fit.hpp @@ -21,10 +21,10 @@ fit_dispatch(const aare::FitModel &model, template void bind_fit_model(py::module &m, const char *name) { using FM = aare::FitModel; - py::class_(m, name) - .def(py::init(), - py::arg("strategy") = 0, py::arg("max_calls") = 100, - py::arg("tolerance") = 0.5, py::arg("compute_errors") = false) + auto cls = py::class_(m, name); + cls.def(py::init(), + py::arg("strategy") = 0, py::arg("max_calls") = 100, + py::arg("tolerance") = 0.5, py::arg("compute_errors") = false) .def("SetParLimits", py::overload_cast(&FM::SetParLimits), py::arg("idx"), py::arg("lo"), py::arg("hi")) @@ -101,6 +101,45 @@ template void bind_fit_model(py::module &m, const char *name) { )doc", py::arg("x"), py::arg("y"), py::arg("y_err") = py::none(), py::arg("n_threads") = 4); + + if constexpr (std::is_same_v or + std::is_same_v) { + cls.def( + "estimate_par", + [](const FM & /*self*/, + py::array_t x, + py::array_t y, + const bool elastic_scattering) { + auto x_view = make_view_1d(x); + auto y_view = make_view_1d(y); + + return Model::estimate_par(x_view, y_view, elastic_scattering); + }, + py::arg("x"), py::arg("y"), py::arg("elastic_scattering") = true, + R"doc( + Estimate starting parameters from 1D data. + Parameters + ---------- + elastic_scattering : bool + If true, take into account elastic scattering + effects. + )doc"); + } else { + cls.def( + "estimate_par", + [](const FM & /*self*/, + py::array_t x, + py::array_t + y) { + auto x_view = make_view_1d(x); + auto y_view = make_view_1d(y); + + return Model::estimate_par(x_view, y_view); + }, + py::arg("x"), py::arg("y"), R"doc( + Estimate starting parameters from 1D data. + )doc"); + } } template