diff --git a/.gitignore b/.gitignore index 5f2023cb0f5..c50883aa413 100644 --- a/.gitignore +++ b/.gitignore @@ -61,4 +61,4 @@ doc/dg/background_errors.unsorted .ycm_extra_conf.py .ipynb_checkpoints -build*/ +/*build*/ diff --git a/src/core/accumulators/MeanVarianceCalculator.hpp b/src/core/accumulators/MeanVarianceCalculator.hpp index cfc9a8ae7f6..a07f2b7e98a 100644 --- a/src/core/accumulators/MeanVarianceCalculator.hpp +++ b/src/core/accumulators/MeanVarianceCalculator.hpp @@ -34,7 +34,7 @@ class MeanVarianceCalculator : public AccumulatorBase { public: // The accumulator struct has to be initialized with the correct vector size, // therefore the order of init is important. - MeanVarianceCalculator(std::shared_ptr const &obs, + MeanVarianceCalculator(std::shared_ptr obs, int delta_N) : AccumulatorBase(delta_N), m_obs(obs), m_acc(obs->n_values()) {} diff --git a/src/core/observables/CylindricalDensityProfile.hpp b/src/core/observables/CylindricalDensityProfile.hpp index a7d1de6e312..7aadc769588 100644 --- a/src/core/observables/CylindricalDensityProfile.hpp +++ b/src/core/observables/CylindricalDensityProfile.hpp @@ -37,7 +37,7 @@ class CylindricalDensityProfile : public CylindricalPidProfileObservable { std::vector evaluate(Utils::Span> particles, const ParticleObservables::traits &traits) const override { - Utils::CylindricalHistogram histogram(n_bins, 1, limits); + Utils::CylindricalHistogram histogram(n_bins(), 1, limits()); for (auto p : particles) { histogram.update(Utils::transform_coordinate_cartesian_to_cylinder( diff --git a/src/core/observables/CylindricalFluxDensityProfile.hpp b/src/core/observables/CylindricalFluxDensityProfile.hpp index 73b65f08c81..900453d08f0 100644 --- a/src/core/observables/CylindricalFluxDensityProfile.hpp +++ b/src/core/observables/CylindricalFluxDensityProfile.hpp @@ -39,7 +39,7 @@ class CylindricalFluxDensityProfile : public CylindricalPidProfileObservable { std::vector evaluate(Utils::Span> particles, const ParticleObservables::traits &traits) const override { - Utils::CylindricalHistogram histogram(n_bins, 3, limits); + Utils::CylindricalHistogram histogram(n_bins(), 3, limits()); // Write data to the histogram for (auto p : particles) { @@ -55,7 +55,8 @@ class CylindricalFluxDensityProfile : public CylindricalPidProfileObservable { return histogram.get_histogram(); } std::vector shape() const override { - return {n_bins[0], n_bins[1], n_bins[2], 3}; + auto const b = n_bins(); + return {b[0], b[1], b[2], 3}; } }; diff --git a/src/core/observables/CylindricalLBFluxDensityProfileAtParticlePositions.cpp b/src/core/observables/CylindricalLBFluxDensityProfileAtParticlePositions.cpp index 5ef211f40db..a8b36def080 100644 --- a/src/core/observables/CylindricalLBFluxDensityProfileAtParticlePositions.cpp +++ b/src/core/observables/CylindricalLBFluxDensityProfileAtParticlePositions.cpp @@ -33,8 +33,7 @@ std::vector CylindricalLBFluxDensityProfileAtParticlePositions::evaluate( Utils::Span> particles, const ParticleObservables::traits &traits) const { - - Utils::CylindricalHistogram histogram(n_bins, 3, limits); + Utils::CylindricalHistogram histogram(n_bins(), 3, limits()); // First collect all positions (since we want to call the LB function to // get the fluid velocities only once). diff --git a/src/core/observables/CylindricalLBFluxDensityProfileAtParticlePositions.hpp b/src/core/observables/CylindricalLBFluxDensityProfileAtParticlePositions.hpp index 319c65955b9..6c863397e03 100644 --- a/src/core/observables/CylindricalLBFluxDensityProfileAtParticlePositions.hpp +++ b/src/core/observables/CylindricalLBFluxDensityProfileAtParticlePositions.hpp @@ -41,7 +41,8 @@ class CylindricalLBFluxDensityProfileAtParticlePositions const ParticleObservables::traits &traits) const override; std::vector shape() const override { - return {n_bins[0], n_bins[1], n_bins[2], 3}; + auto const b = n_bins(); + return {b[0], b[1], b[2], 3}; } }; diff --git a/src/core/observables/CylindricalLBProfileObservable.hpp b/src/core/observables/CylindricalLBProfileObservable.hpp index df4e66e5936..b65d3b69cbf 100644 --- a/src/core/observables/CylindricalLBProfileObservable.hpp +++ b/src/core/observables/CylindricalLBProfileObservable.hpp @@ -44,9 +44,10 @@ class CylindricalLBProfileObservable : public CylindricalProfileObservable { calculate_sampling_positions(); } void calculate_sampling_positions() { + auto const lim = limits(); + auto const b = n_bins(); sampling_positions = Utils::get_cylindrical_sampling_positions( - limits[0], limits[1], limits[2], n_bins[0], n_bins[1], n_bins[2], - sampling_density); + lim[0], lim[1], lim[2], b[0], b[1], b[2], sampling_density); for (auto &p : sampling_positions) { auto p_cart = Utils::transform_coordinate_cylinder_to_cartesian(p); // We have to rotate the coordinates since the utils function assumes diff --git a/src/core/observables/CylindricalLBVelocityProfile.cpp b/src/core/observables/CylindricalLBVelocityProfile.cpp index 791417859b8..e0fab3f1b81 100644 --- a/src/core/observables/CylindricalLBVelocityProfile.cpp +++ b/src/core/observables/CylindricalLBVelocityProfile.cpp @@ -31,7 +31,7 @@ namespace Observables { std::vector CylindricalLBVelocityProfile::operator()() const { - Utils::CylindricalHistogram histogram(n_bins, 3, limits); + Utils::CylindricalHistogram histogram(n_bins(), 3, limits()); for (auto const &p : sampling_positions) { auto const velocity = lb_lbfluid_get_interpolated_velocity(p) * lb_lbfluid_get_lattice_speed(); diff --git a/src/core/observables/CylindricalLBVelocityProfile.hpp b/src/core/observables/CylindricalLBVelocityProfile.hpp index 4eb77cc6817..00a1de349ff 100644 --- a/src/core/observables/CylindricalLBVelocityProfile.hpp +++ b/src/core/observables/CylindricalLBVelocityProfile.hpp @@ -30,7 +30,8 @@ class CylindricalLBVelocityProfile : public CylindricalLBProfileObservable { using CylindricalLBProfileObservable::CylindricalLBProfileObservable; std::vector operator()() const override; std::vector shape() const override { - return {n_bins[0], n_bins[1], n_bins[2], 3}; + auto const b = n_bins(); + return {b[0], b[1], b[2], 3}; } }; diff --git a/src/core/observables/CylindricalLBVelocityProfileAtParticlePositions.cpp b/src/core/observables/CylindricalLBVelocityProfileAtParticlePositions.cpp index 9650c7dda6f..ab09f1fa1b0 100644 --- a/src/core/observables/CylindricalLBVelocityProfileAtParticlePositions.cpp +++ b/src/core/observables/CylindricalLBVelocityProfileAtParticlePositions.cpp @@ -33,7 +33,7 @@ namespace Observables { std::vector CylindricalLBVelocityProfileAtParticlePositions::evaluate( Utils::Span> particles, const ParticleObservables::traits &traits) const { - Utils::CylindricalHistogram histogram(n_bins, 3, limits); + Utils::CylindricalHistogram histogram(n_bins(), 3, limits()); for (auto p : particles) { auto const pos = folded_position(traits.position(p), box_geo); diff --git a/src/core/observables/CylindricalLBVelocityProfileAtParticlePositions.hpp b/src/core/observables/CylindricalLBVelocityProfileAtParticlePositions.hpp index 532731cc88c..b28fc05ebe7 100644 --- a/src/core/observables/CylindricalLBVelocityProfileAtParticlePositions.hpp +++ b/src/core/observables/CylindricalLBVelocityProfileAtParticlePositions.hpp @@ -41,7 +41,8 @@ class CylindricalLBVelocityProfileAtParticlePositions const ParticleObservables::traits &traits) const override; std::vector shape() const override { - return {n_bins[0], n_bins[1], n_bins[2], 3}; + auto const b = n_bins(); + return {b[0], b[1], b[2], 3}; } }; diff --git a/src/core/observables/CylindricalVelocityProfile.hpp b/src/core/observables/CylindricalVelocityProfile.hpp index 6b70dd2feaf..21f98e1397b 100644 --- a/src/core/observables/CylindricalVelocityProfile.hpp +++ b/src/core/observables/CylindricalVelocityProfile.hpp @@ -40,7 +40,7 @@ class CylindricalVelocityProfile : public CylindricalPidProfileObservable { std::vector evaluate(Utils::Span> particles, const ParticleObservables::traits &traits) const override { - Utils::CylindricalHistogram histogram(n_bins, 3, limits); + Utils::CylindricalHistogram histogram(n_bins(), 3, limits()); for (auto p : particles) { auto const pos = folded_position(traits.position(p), box_geo) - @@ -62,7 +62,8 @@ class CylindricalVelocityProfile : public CylindricalPidProfileObservable { return hist_tmp; } std::vector shape() const override { - return {n_bins[0], n_bins[1], n_bins[2], 3}; + auto const b = n_bins(); + return {b[0], b[1], b[2], 3}; } }; diff --git a/src/core/observables/DensityProfile.hpp b/src/core/observables/DensityProfile.hpp index 9b6ee672c52..256940e3b39 100644 --- a/src/core/observables/DensityProfile.hpp +++ b/src/core/observables/DensityProfile.hpp @@ -38,7 +38,7 @@ class DensityProfile : public PidProfileObservable { std::vector evaluate(Utils::Span> particles, const ParticleObservables::traits &traits) const override { - Utils::Histogram histogram(n_bins, 1, limits); + Utils::Histogram histogram(n_bins(), 1, limits()); for (auto p : particles) { histogram.update(folded_position(traits.position(p), box_geo)); diff --git a/src/core/observables/FluxDensityProfile.hpp b/src/core/observables/FluxDensityProfile.hpp index daa81c73774..574716e7388 100644 --- a/src/core/observables/FluxDensityProfile.hpp +++ b/src/core/observables/FluxDensityProfile.hpp @@ -35,13 +35,14 @@ class FluxDensityProfile : public PidProfileObservable { public: using PidProfileObservable::PidProfileObservable; std::vector shape() const override { - return {n_bins[0], n_bins[1], n_bins[2], 3}; + auto const b = n_bins(); + return {b[0], b[1], b[2], 3}; } std::vector evaluate(Utils::Span> particles, const ParticleObservables::traits &traits) const override { - Utils::Histogram histogram(n_bins, 3, limits); + Utils::Histogram histogram(n_bins(), 3, limits()); for (auto p : particles) { auto const ppos = folded_position(traits.position(p), box_geo); diff --git a/src/core/observables/ForceDensityProfile.hpp b/src/core/observables/ForceDensityProfile.hpp index a448de5f001..e56734cdfb0 100644 --- a/src/core/observables/ForceDensityProfile.hpp +++ b/src/core/observables/ForceDensityProfile.hpp @@ -36,13 +36,14 @@ class ForceDensityProfile : public PidProfileObservable { public: using PidProfileObservable::PidProfileObservable; std::vector shape() const override { - return {n_bins[0], n_bins[1], n_bins[2], 3}; + auto const b = n_bins(); + return {b[0], b[1], b[2], 3}; } std::vector evaluate(ParticleReferenceRange particles, const ParticleObservables::traits &traits) const override { - Utils::Histogram histogram(n_bins, 3, limits); + Utils::Histogram histogram(n_bins(), 3, limits()); for (auto p : particles) { histogram.update(folded_position(p.get().r.p, box_geo), p.get().f.f); } diff --git a/src/core/observables/LBProfileObservable.hpp b/src/core/observables/LBProfileObservable.hpp index 3dc96abd421..af96a997851 100644 --- a/src/core/observables/LBProfileObservable.hpp +++ b/src/core/observables/LBProfileObservable.hpp @@ -51,25 +51,26 @@ class LBProfileObservable : public ProfileObservable { bool allow_empty_bins; std::vector sampling_positions; void calculate_sampling_positions() { + auto const lim = limits(); sampling_positions.clear(); if (sampling_delta[0] == 0 or sampling_delta[1] == 0 or sampling_delta[2] == 0) throw std::runtime_error("Parameter delta_x/y/z must not be zero!"); const auto n_samples_x = static_cast( - std::rint((limits[0].second - limits[0].first) / sampling_delta[0])); + std::rint((lim[0].second - lim[0].first) / sampling_delta[0])); const auto n_samples_y = static_cast( - std::rint((limits[1].second - limits[1].first) / sampling_delta[1])); + std::rint((lim[1].second - lim[1].first) / sampling_delta[1])); const auto n_samples_z = static_cast( - std::rint((limits[2].second - limits[2].first) / sampling_delta[2])); + std::rint((lim[2].second - lim[2].first) / sampling_delta[2])); for (size_t x = 0; x < n_samples_x; ++x) { for (size_t y = 0; y < n_samples_y; ++y) { for (size_t z = 0; z < n_samples_z; ++z) { sampling_positions.push_back(Utils::Vector3d{ - {limits[0].first + sampling_offset[0] + + {lim[0].first + sampling_offset[0] + static_cast(x) * sampling_delta[0], - limits[1].first + sampling_offset[1] + + lim[1].first + sampling_offset[1] + static_cast(y) * sampling_delta[1], - limits[2].first + sampling_offset[2] + + lim[2].first + sampling_offset[2] + static_cast(z) * sampling_delta[2]}}); } } diff --git a/src/core/observables/LBVelocityProfile.cpp b/src/core/observables/LBVelocityProfile.cpp index a8a47240485..2ad5b02eb28 100644 --- a/src/core/observables/LBVelocityProfile.cpp +++ b/src/core/observables/LBVelocityProfile.cpp @@ -30,7 +30,7 @@ namespace Observables { std::vector LBVelocityProfile::operator()() const { - Utils::Histogram histogram(n_bins, 3, limits); + Utils::Histogram histogram(n_bins(), 3, limits()); for (auto const &p : sampling_positions) { const auto v = lb_lbfluid_get_interpolated_velocity(p) * lb_lbfluid_get_lattice_speed(); diff --git a/src/core/observables/LBVelocityProfile.hpp b/src/core/observables/LBVelocityProfile.hpp index b273cb7a190..c5738db3724 100644 --- a/src/core/observables/LBVelocityProfile.hpp +++ b/src/core/observables/LBVelocityProfile.hpp @@ -30,7 +30,8 @@ class LBVelocityProfile : public LBProfileObservable { public: using LBProfileObservable::LBProfileObservable; std::vector shape() const override { - return {n_bins[0], n_bins[1], n_bins[2], 3}; + auto const b = n_bins(); + return {b[0], b[1], b[2], 3}; } std::vector operator()() const override; }; diff --git a/src/core/observables/ProfileObservable.hpp b/src/core/observables/ProfileObservable.hpp index 4b7fc5cb66b..804b39bd0f2 100644 --- a/src/core/observables/ProfileObservable.hpp +++ b/src/core/observables/ProfileObservable.hpp @@ -34,37 +34,51 @@ namespace Observables { /** Cartesian profile observable */ class ProfileObservable : virtual public Observable { +private: + /** Range of the profile edges. */ + std::array, 3> m_limits; + /** Number of bins for each coordinate. */ + std::array m_n_bins; + public: ProfileObservable(int n_x_bins, int n_y_bins, int n_z_bins, double min_x, double max_x, double min_y, double max_y, double min_z, double max_z) - : limits{{std::make_pair(min_x, max_x), std::make_pair(min_y, max_y), - std::make_pair(min_z, max_z)}}, - n_bins{{static_cast(n_x_bins), static_cast(n_y_bins), - static_cast(n_z_bins)}} {} - /** Range of the profile edges. */ - std::array, 3> limits; - /** Number of bins for each coordinate. */ - std::array n_bins; + : m_limits{{std::make_pair(min_x, max_x), std::make_pair(min_y, max_y), + std::make_pair(min_z, max_z)}}, + m_n_bins{{static_cast(n_x_bins), static_cast(n_y_bins), + static_cast(n_z_bins)}} { + if (max_x <= min_x) + throw std::runtime_error("max_x has to be > min_x"); + if (max_y <= min_y) + throw std::runtime_error("max_y has to be > min_y"); + if (max_z <= min_z) + throw std::runtime_error("max_z has to be > min_z"); + } std::vector shape() const override { - return {n_bins[0], n_bins[1], n_bins[2]}; + return {m_n_bins[0], m_n_bins[1], m_n_bins[2]}; } + auto n_bins() const { return m_n_bins; } + + auto limits() const { return m_limits; } + /** Calculate the bin edges for each dimension */ - std::array, 3> edges() { + std::array, 3> edges() const { std::array, 3> profile_edges = { - {std::vector(n_bins[0] + 1), std::vector(n_bins[1] + 1), - std::vector(n_bins[2] + 1)}}; - boost::copy( - Utils::make_lin_space(limits[0].first, limits[0].second, n_bins[0] + 1), - profile_edges[0].begin()); - boost::copy( - Utils::make_lin_space(limits[1].first, limits[1].second, n_bins[1] + 1), - profile_edges[1].begin()); - boost::copy( - Utils::make_lin_space(limits[2].first, limits[2].second, n_bins[2] + 1), - profile_edges[2].begin()); + {std::vector(m_n_bins[0] + 1), + std::vector(m_n_bins[1] + 1), + std::vector(m_n_bins[2] + 1)}}; + boost::copy(Utils::make_lin_space(m_limits[0].first, m_limits[0].second, + m_n_bins[0] + 1), + profile_edges[0].begin()); + boost::copy(Utils::make_lin_space(m_limits[1].first, m_limits[1].second, + m_n_bins[1] + 1), + profile_edges[1].begin()); + boost::copy(Utils::make_lin_space(m_limits[2].first, m_limits[2].second, + m_n_bins[2] + 1), + profile_edges[2].begin()); return profile_edges; } }; diff --git a/src/script_interface/CylindricalTransformationParameters.hpp b/src/script_interface/CylindricalTransformationParameters.hpp index 0cd522b2ef2..92114a4f207 100644 --- a/src/script_interface/CylindricalTransformationParameters.hpp +++ b/src/script_interface/CylindricalTransformationParameters.hpp @@ -42,7 +42,7 @@ class CylindricalTransformationParameters [this]() { return m_transform_params->orientation(); }}}); } std::shared_ptr<::Utils::CylindricalTransformationParameters> - cyl_transform_params() { + cyl_transform_params() const { return m_transform_params; } void do_construct(VariantMap const ¶ms) override { diff --git a/src/script_interface/observables/CylindricalLBProfileObservable.hpp b/src/script_interface/observables/CylindricalLBProfileObservable.hpp index b0eafa3942a..52cf56a90d1 100644 --- a/src/script_interface/observables/CylindricalLBProfileObservable.hpp +++ b/src/script_interface/observables/CylindricalLBProfileObservable.hpp @@ -56,83 +56,46 @@ class CylindricalLBProfileObservable CylindricalLBProfileObservable() { this->add_parameters({ {"transform_params", m_transform_params}, - {"n_r_bins", - [this](const Variant &v) { - cylindrical_profile_observable()->n_bins[0] = - static_cast(get_value(v)); - }, + {"n_r_bins", AutoParameter::read_only, [this]() { - return static_cast(cylindrical_profile_observable()->n_bins[0]); + return static_cast( + cylindrical_profile_observable()->n_bins()[0]); }}, - {"n_phi_bins", - [this](const Variant &v) { - cylindrical_profile_observable()->n_bins[1] = - static_cast(get_value(v)); - }, + {"n_phi_bins", AutoParameter::read_only, [this]() { - return static_cast(cylindrical_profile_observable()->n_bins[1]); + return static_cast( + cylindrical_profile_observable()->n_bins()[1]); }}, - {"n_z_bins", - [this](const Variant &v) { - cylindrical_profile_observable()->n_bins[2] = - static_cast(get_value(v)); - }, + {"n_z_bins", AutoParameter::read_only, [this]() { - return static_cast(cylindrical_profile_observable()->n_bins[2]); + return static_cast( + cylindrical_profile_observable()->n_bins()[2]); }}, - {"min_r", - [this](const Variant &v) { - cylindrical_profile_observable()->limits[0].first = - get_value(v); - }, + {"min_r", AutoParameter::read_only, [this]() { - return cylindrical_profile_observable()->limits[0].first; + return cylindrical_profile_observable()->limits()[0].first; }}, - {"min_phi", - [this](const Variant &v) { - cylindrical_profile_observable()->limits[1].first = - get_value(v); - }, + {"min_phi", AutoParameter::read_only, [this]() { - return cylindrical_profile_observable()->limits[1].first; + return cylindrical_profile_observable()->limits()[1].first; }}, - {"min_z", - [this](const Variant &v) { - cylindrical_profile_observable()->limits[2].first = - get_value(v); - }, + {"min_z", AutoParameter::read_only, [this]() { - return cylindrical_profile_observable()->limits[2].first; + return cylindrical_profile_observable()->limits()[2].first; }}, - {"max_r", - [this](const Variant &v) { - cylindrical_profile_observable()->limits[0].second = - get_value(v); - }, + {"max_r", AutoParameter::read_only, [this]() { - return cylindrical_profile_observable()->limits[0].second; + return cylindrical_profile_observable()->limits()[0].second; }}, - {"max_phi", - [this](const Variant &v) { - cylindrical_profile_observable()->limits[1].second = - get_value(v); - }, + {"max_phi", AutoParameter::read_only, [this]() { - return cylindrical_profile_observable()->limits[1].second; + return cylindrical_profile_observable()->limits()[1].second; }}, - {"max_z", - [this](const Variant &v) { - cylindrical_profile_observable()->limits[2].second = - get_value(v); - }, + {"max_z", AutoParameter::read_only, [this]() { - return cylindrical_profile_observable()->limits[2].second; + return cylindrical_profile_observable()->limits()[2].second; }}, - {"sampling_density", - [this](const Variant &v) { - cylindrical_profile_observable()->sampling_density = - get_value(v); - }, + {"sampling_density", AutoParameter::read_only, [this]() { return cylindrical_profile_observable()->sampling_density; }}, diff --git a/src/script_interface/observables/CylindricalPidProfileObservable.hpp b/src/script_interface/observables/CylindricalPidProfileObservable.hpp index 6d7117e1002..19b55de4f41 100644 --- a/src/script_interface/observables/CylindricalPidProfileObservable.hpp +++ b/src/script_interface/observables/CylindricalPidProfileObservable.hpp @@ -58,80 +58,44 @@ class CylindricalPidProfileObservable {"ids", AutoParameter::read_only, [this]() { return cylindrical_pid_profile_observable()->ids(); }}, {"transform_params", m_transform_params}, - {"n_r_bins", - [this](const Variant &v) { - cylindrical_pid_profile_observable()->n_bins[0] = - static_cast(get_value(v)); - }, + {"n_r_bins", AutoParameter::read_only, [this]() { return static_cast( - cylindrical_pid_profile_observable()->n_bins[0]); + cylindrical_pid_profile_observable()->n_bins()[0]); }}, - {"n_phi_bins", - [this](const Variant &v) { - cylindrical_pid_profile_observable()->n_bins[1] = - static_cast(get_value(v)); - }, + {"n_phi_bins", AutoParameter::read_only, [this]() { return static_cast( - cylindrical_pid_profile_observable()->n_bins[1]); + cylindrical_pid_profile_observable()->n_bins()[1]); }}, - {"n_z_bins", - [this](const Variant &v) { - cylindrical_pid_profile_observable()->n_bins[2] = - static_cast(get_value(v)); - }, + {"n_z_bins", AutoParameter::read_only, [this]() { return static_cast( - cylindrical_pid_profile_observable()->n_bins[2]); + cylindrical_pid_profile_observable()->n_bins()[2]); }}, - {"min_r", - [this](const Variant &v) { - cylindrical_pid_profile_observable()->limits[0].first = - get_value(v); - }, + {"min_r", AutoParameter::read_only, [this]() { - return cylindrical_pid_profile_observable()->limits[0].first; + return cylindrical_pid_profile_observable()->limits()[0].first; }}, - {"min_phi", - [this](const Variant &v) { - cylindrical_pid_profile_observable()->limits[1].first = - get_value(v); - }, + {"min_phi", AutoParameter::read_only, [this]() { - return cylindrical_pid_profile_observable()->limits[1].first; + return cylindrical_pid_profile_observable()->limits()[1].first; }}, - {"min_z", - [this](const Variant &v) { - cylindrical_pid_profile_observable()->limits[2].first = - get_value(v); - }, + {"min_z", AutoParameter::read_only, [this]() { - return cylindrical_pid_profile_observable()->limits[2].first; + return cylindrical_pid_profile_observable()->limits()[2].first; }}, - {"max_r", - [this](const Variant &v) { - cylindrical_pid_profile_observable()->limits[0].second = - get_value(v); - }, + {"max_r", AutoParameter::read_only, [this]() { - return cylindrical_pid_profile_observable()->limits[0].second; + return cylindrical_pid_profile_observable()->limits()[0].second; }}, - {"max_phi", - [this](const Variant &v) { - cylindrical_pid_profile_observable()->limits[1].second = - get_value(v); - }, + {"max_phi", AutoParameter::read_only, [this]() { - return cylindrical_pid_profile_observable()->limits[1].second; + return cylindrical_pid_profile_observable()->limits()[1].second; }}, - {"max_z", - [this](const Variant &v) { - cylindrical_pid_profile_observable()->limits[2].second = - get_value(v); - }, + {"max_z", AutoParameter::read_only, [this]() { - return cylindrical_pid_profile_observable()->limits[2].second; + return cylindrical_pid_profile_observable()->limits()[2].second; }}, }); }; diff --git a/src/script_interface/observables/LBProfileObservable.hpp b/src/script_interface/observables/LBProfileObservable.hpp index 2f347479c0d..4116860f885 100644 --- a/src/script_interface/observables/LBProfileObservable.hpp +++ b/src/script_interface/observables/LBProfileObservable.hpp @@ -50,94 +50,43 @@ class LBProfileObservable using Base::Base; LBProfileObservable() { this->add_parameters( - {{"n_x_bins", - [this](const Variant &v) { - profile_observable()->n_bins[0] = - static_cast(get_value(v)); - }, + {{"n_x_bins", AutoParameter::read_only, [this]() { - return static_cast(profile_observable()->n_bins[0]); + return static_cast(profile_observable()->n_bins()[0]); }}, - {"n_y_bins", - [this](const Variant &v) { - profile_observable()->n_bins[1] = - static_cast(get_value(v)); - }, + {"n_y_bins", AutoParameter::read_only, [this]() { - return static_cast(profile_observable()->n_bins[1]); + return static_cast(profile_observable()->n_bins()[1]); }}, - {"n_z_bins", - [this](const Variant &v) { - profile_observable()->n_bins[2] = - static_cast(get_value(v)); - }, + {"n_z_bins", AutoParameter::read_only, [this]() { - return static_cast(profile_observable()->n_bins[2]); + return static_cast(profile_observable()->n_bins()[2]); }}, - {"min_x", - [this](const Variant &v) { - profile_observable()->limits[0].first = get_value(v); - }, - [this]() { return profile_observable()->limits[0].first; }}, - {"min_y", - [this](const Variant &v) { - profile_observable()->limits[1].first = get_value(v); - }, - [this]() { return profile_observable()->limits[1].first; }}, - {"min_z", - [this](const Variant &v) { - profile_observable()->limits[2].first = get_value(v); - }, - [this]() { return profile_observable()->limits[2].first; }}, - {"max_x", - [this](const Variant &v) { - profile_observable()->limits[0].second = get_value(v); - }, - [this]() { return profile_observable()->limits[0].second; }}, - {"max_y", - [this](const Variant &v) { - profile_observable()->limits[1].second = get_value(v); - }, - [this]() { return profile_observable()->limits[1].second; }}, - {"max_z", - [this](const Variant &v) { - profile_observable()->limits[2].second = get_value(v); - }, - [this]() { return profile_observable()->limits[2].second; }}, - {"sampling_delta_x", - [this](const Variant &v) { - profile_observable()->sampling_delta[0] = get_value(v); - }, + {"min_x", AutoParameter::read_only, + [this]() { return profile_observable()->limits()[0].first; }}, + {"min_y", AutoParameter::read_only, + [this]() { return profile_observable()->limits()[1].first; }}, + {"min_z", AutoParameter::read_only, + [this]() { return profile_observable()->limits()[2].first; }}, + {"max_x", AutoParameter::read_only, + [this]() { return profile_observable()->limits()[0].second; }}, + {"max_y", AutoParameter::read_only, + [this]() { return profile_observable()->limits()[1].second; }}, + {"max_z", AutoParameter::read_only, + [this]() { return profile_observable()->limits()[2].second; }}, + {"sampling_delta_x", AutoParameter::read_only, [this]() { return profile_observable()->sampling_delta[0]; }}, - {"sampling_delta_y", - [this](const Variant &v) { - profile_observable()->sampling_delta[1] = get_value(v); - }, + {"sampling_delta_y", AutoParameter::read_only, [this]() { return profile_observable()->sampling_delta[1]; }}, - {"sampling_delta_z", - [this](const Variant &v) { - profile_observable()->sampling_delta[2] = get_value(v); - }, + {"sampling_delta_z", AutoParameter::read_only, [this]() { return profile_observable()->sampling_delta[2]; }}, - {"sampling_offset_x", - [this](const Variant &v) { - profile_observable()->sampling_offset[0] = get_value(v); - }, + {"sampling_offset_x", AutoParameter::read_only, [this]() { return profile_observable()->sampling_offset[0]; }}, - {"sampling_offset_y", - [this](const Variant &v) { - profile_observable()->sampling_offset[1] = get_value(v); - }, + {"sampling_offset_y", AutoParameter::read_only, [this]() { return profile_observable()->sampling_offset[1]; }}, - {"sampling_offset_z", - [this](const Variant &v) { - profile_observable()->sampling_offset[2] = get_value(v); - }, + {"sampling_offset_z", AutoParameter::read_only, [this]() { return profile_observable()->sampling_offset[2]; }}, - {"allow_empty_bins", - [this](const Variant &v) { - profile_observable()->allow_empty_bins = get_value(v); - }, + {"allow_empty_bins", AutoParameter::read_only, [this]() { return profile_observable()->allow_empty_bins; }}}); } diff --git a/src/script_interface/observables/PidProfileObservable.hpp b/src/script_interface/observables/PidProfileObservable.hpp index b7ba0fbc6fa..b807ca63ca7 100644 --- a/src/script_interface/observables/PidProfileObservable.hpp +++ b/src/script_interface/observables/PidProfileObservable.hpp @@ -52,60 +52,30 @@ class PidProfileObservable this->add_parameters( {{"ids", AutoParameter::read_only, [this]() { return pid_profile_observable()->ids(); }}, - {"n_x_bins", - [this](const Variant &v) { - pid_profile_observable()->n_bins[0] = - static_cast(get_value(v)); - }, + {"n_x_bins", AutoParameter::read_only, [this]() { - return static_cast(pid_profile_observable()->n_bins[0]); + return static_cast(pid_profile_observable()->n_bins()[0]); }}, - {"n_y_bins", - [this](const Variant &v) { - pid_profile_observable()->n_bins[1] = - static_cast(get_value(v)); - }, + {"n_y_bins", AutoParameter::read_only, [this]() { - return static_cast(pid_profile_observable()->n_bins[1]); + return static_cast(pid_profile_observable()->n_bins()[1]); }}, - {"n_z_bins", - [this](const Variant &v) { - pid_profile_observable()->n_bins[2] = - static_cast(get_value(v)); - }, + {"n_z_bins", AutoParameter::read_only, [this]() { - return static_cast(pid_profile_observable()->n_bins[2]); + return static_cast(pid_profile_observable()->n_bins()[2]); }}, - {"min_x", - [this](const Variant &v) { - pid_profile_observable()->limits[0].first = get_value(v); - }, - [this]() { return pid_profile_observable()->limits[0].first; }}, - {"min_y", - [this](const Variant &v) { - pid_profile_observable()->limits[1].first = get_value(v); - }, - [this]() { return pid_profile_observable()->limits[1].first; }}, - {"min_z", - [this](const Variant &v) { - pid_profile_observable()->limits[2].first = get_value(v); - }, - [this]() { return pid_profile_observable()->limits[2].first; }}, - {"max_x", - [this](const Variant &v) { - pid_profile_observable()->limits[0].second = get_value(v); - }, - [this]() { return pid_profile_observable()->limits[0].second; }}, - {"max_y", - [this](const Variant &v) { - pid_profile_observable()->limits[1].second = get_value(v); - }, - [this]() { return pid_profile_observable()->limits[1].second; }}, - {"max_z", - [this](const Variant &v) { - pid_profile_observable()->limits[2].second = get_value(v); - }, - [this]() { return pid_profile_observable()->limits[2].second; }}}); + {"min_x", AutoParameter::read_only, + [this]() { return pid_profile_observable()->limits()[0].first; }}, + {"min_y", AutoParameter::read_only, + [this]() { return pid_profile_observable()->limits()[1].first; }}, + {"min_z", AutoParameter::read_only, + [this]() { return pid_profile_observable()->limits()[2].first; }}, + {"max_x", AutoParameter::read_only, + [this]() { return pid_profile_observable()->limits()[0].second; }}, + {"max_y", AutoParameter::read_only, + [this]() { return pid_profile_observable()->limits()[1].second; }}, + {"max_z", AutoParameter::read_only, + [this]() { return pid_profile_observable()->limits()[2].second; }}}); } void do_construct(VariantMap const ¶ms) override { diff --git a/src/script_interface/observables/RDF.hpp b/src/script_interface/observables/RDF.hpp index 070c3e2f66a..a56d3c7b273 100644 --- a/src/script_interface/observables/RDF.hpp +++ b/src/script_interface/observables/RDF.hpp @@ -42,30 +42,15 @@ class RDF : public AutoParameters { public: RDF() { this->add_parameters( - {{"ids1", - [this](const Variant &v) { - rdf_observable()->ids1() = get_value>(v); - }, + {{"ids1", AutoParameter::read_only, [this]() { return rdf_observable()->ids1(); }}, - {"ids2", - [this](const Variant &v) { - rdf_observable()->ids2() = get_value>(v); - }, + {"ids2", AutoParameter::read_only, [this]() { return rdf_observable()->ids2(); }}, - {"n_r_bins", - [this](const Variant &v) { - rdf_observable()->n_r_bins = static_cast(get_value(v)); - }, + {"n_r_bins", AutoParameter::read_only, [this]() { return static_cast(rdf_observable()->n_r_bins); }}, - {"min_r", - [this](const Variant &v) { - rdf_observable()->min_r = get_value(v); - }, + {"min_r", AutoParameter::read_only, [this]() { return rdf_observable()->min_r; }}, - {"max_r", - [this](const Variant &v) { - rdf_observable()->max_r = get_value(v); - }, + {"max_r", AutoParameter::read_only, [this]() { return rdf_observable()->max_r; }}}); } diff --git a/testsuite/python/observable_cylindrical.py b/testsuite/python/observable_cylindrical.py index 6aa80962544..f5d43ab0952 100644 --- a/testsuite/python/observable_cylindrical.py +++ b/testsuite/python/observable_cylindrical.py @@ -218,9 +218,8 @@ def test_cylindrical_pid_profile_interface(self): self.assertEqual(observable.n_z_bins, params['n_z_bins']) obs_data = observable.calculate() np.testing.assert_array_equal(obs_data.shape, [4, 6, 8]) - observable.n_r_bins = 1 - observable.n_phi_bins = 2 - observable.n_z_bins = 3 + observable = espressomd.observables.CylindricalDensityProfile( + **{**params, 'n_r_bins': 1, 'n_phi_bins': 2, 'n_z_bins': 3}) self.assertEqual(observable.n_r_bins, 1) self.assertEqual(observable.n_phi_bins, 2) self.assertEqual(observable.n_z_bins, 3) @@ -230,21 +229,19 @@ def test_cylindrical_pid_profile_interface(self): self.assertEqual(observable.min_r, params['min_r']) self.assertEqual(observable.min_phi, params['min_phi']) self.assertEqual(observable.min_z, params['min_z']) - observable.min_r = 4 - observable.min_phi = 5 - observable.min_z = 6 - self.assertEqual(observable.min_r, 4) - self.assertEqual(observable.min_phi, 5) - self.assertEqual(observable.min_z, 6) + observable = espressomd.observables.CylindricalDensityProfile( + **{**params, 'min_r': 1, 'min_phi': 2, 'min_z': 1}) + self.assertEqual(observable.min_r, 1) + self.assertEqual(observable.min_phi, 2) + self.assertEqual(observable.min_z, 1) obs_bin_edges = observable.bin_edges() - np.testing.assert_array_equal(obs_bin_edges[0, 0, 0], [4, 5, 6]) + np.testing.assert_array_almost_equal(obs_bin_edges[0, 0, 0], [1, 2, 1]) # check edges upper corner self.assertEqual(observable.max_r, params['max_r']) self.assertEqual(observable.max_phi, params['max_phi']) self.assertEqual(observable.max_z, params['max_z']) - observable.max_r = 7 - observable.max_phi = 8 - observable.max_z = 9 + observable = espressomd.observables.CylindricalDensityProfile( + **{**params, 'max_r': 7, 'max_phi': 8, 'max_z': 9}) self.assertEqual(observable.max_r, 7) self.assertEqual(observable.max_phi, 8) self.assertEqual(observable.max_z, 9) @@ -253,6 +250,8 @@ def test_cylindrical_pid_profile_interface(self): # check center, axis, orientation ctp = espressomd.math.CylindricalTransformationParameters( center=[1, 2, 3], axis=[0, 1, 0], orientation=[0, 0, 1]) + observable = espressomd.observables.CylindricalDensityProfile( + **{**params, 'transform_params': ctp}) observable.transform_params = ctp for attr_name in ['center', 'axis', 'orientation']: diff --git a/testsuite/python/observable_cylindricalLB.py b/testsuite/python/observable_cylindricalLB.py index 0d1f72edfc8..6ae37e93d39 100644 --- a/testsuite/python/observable_cylindricalLB.py +++ b/testsuite/python/observable_cylindricalLB.py @@ -185,9 +185,8 @@ def test_cylindrical_lb_profile_interface(self): self.assertEqual(observable.n_z_bins, params['n_z_bins']) obs_data = observable.calculate() np.testing.assert_array_equal(obs_data.shape, [4, 6, 8, 3]) - observable.n_r_bins = 1 - observable.n_phi_bins = 2 - observable.n_z_bins = 3 + observable = espressomd.observables.CylindricalLBVelocityProfile( + **{**params, 'n_r_bins': 1, 'n_phi_bins': 2, 'n_z_bins': 3}) self.assertEqual(observable.n_r_bins, 1) self.assertEqual(observable.n_phi_bins, 2) self.assertEqual(observable.n_z_bins, 3) @@ -197,21 +196,19 @@ def test_cylindrical_lb_profile_interface(self): self.assertEqual(observable.min_r, params['min_r']) self.assertEqual(observable.min_phi, params['min_phi']) self.assertEqual(observable.min_z, params['min_z']) - observable.min_r = 4 - observable.min_phi = 5 - observable.min_z = 6 - self.assertEqual(observable.min_r, 4) - self.assertEqual(observable.min_phi, 5) - self.assertEqual(observable.min_z, 6) + observable = espressomd.observables.CylindricalLBVelocityProfile( + **{**params, 'min_r': 4.0, 'min_phi': 1.0, 'min_z': 1.0}) + self.assertEqual(observable.min_r, 4.0) + self.assertEqual(observable.min_phi, 1.0) + self.assertEqual(observable.min_z, 1.0) obs_bin_edges = observable.bin_edges() - np.testing.assert_array_equal(obs_bin_edges[0, 0, 0], [4, 5, 6]) + np.testing.assert_array_equal(obs_bin_edges[0, 0, 0], [4, 1, 1]) # check edges upper corner self.assertEqual(observable.max_r, params['max_r']) self.assertEqual(observable.max_phi, params['max_phi']) self.assertEqual(observable.max_z, params['max_z']) - observable.max_r = 7 - observable.max_phi = 8 - observable.max_z = 9 + observable = espressomd.observables.CylindricalLBVelocityProfile( + **{**params, 'max_r': 7, 'max_phi': 8, 'max_z': 9}) self.assertEqual(observable.max_r, 7) self.assertEqual(observable.max_phi, 8) self.assertEqual(observable.max_z, 9) @@ -220,6 +217,8 @@ def test_cylindrical_lb_profile_interface(self): # check center, axis, orientation ctp = espressomd.math.CylindricalTransformationParameters( center=[1, 2, 3], axis=[0, 1, 0], orientation=[0, 0, 1]) + observable = espressomd.observables.CylindricalLBVelocityProfile( + **{**params, 'transform_params': ctp}) observable.transform_params = ctp for attr_name in ['center', 'axis', 'orientation']: diff --git a/testsuite/python/observable_profile.py b/testsuite/python/observable_profile.py index f050acc8544..4cd2a2bfd6b 100644 --- a/testsuite/python/observable_profile.py +++ b/testsuite/python/observable_profile.py @@ -112,9 +112,8 @@ def test_pid_profile_interface(self): self.assertEqual(observable.n_z_bins, params['n_z_bins']) obs_data = observable.calculate() np.testing.assert_array_equal(obs_data.shape, [4, 6, 8]) - observable.n_x_bins = 1 - observable.n_y_bins = 2 - observable.n_z_bins = 3 + observable = espressomd.observables.DensityProfile( + **{**params, 'n_x_bins': 1, 'n_y_bins': 2, 'n_z_bins': 3}) self.assertEqual(observable.n_x_bins, 1) self.assertEqual(observable.n_y_bins, 2) self.assertEqual(observable.n_z_bins, 3) @@ -124,21 +123,20 @@ def test_pid_profile_interface(self): self.assertEqual(observable.min_x, params['min_x']) self.assertEqual(observable.min_y, params['min_y']) self.assertEqual(observable.min_z, params['min_z']) - observable.min_x = 4 - observable.min_y = 5 - observable.min_z = 6 - self.assertEqual(observable.min_x, 4) - self.assertEqual(observable.min_y, 5) - self.assertEqual(observable.min_z, 6) + observable = espressomd.observables.DensityProfile( + **{**params, 'min_x': 0.5, 'min_y': 2, 'min_z': 1.12}) + self.assertEqual(observable.min_x, 0.5) + self.assertEqual(observable.min_y, 2) + self.assertEqual(observable.min_z, 1.12) obs_bin_edges = observable.bin_edges() - np.testing.assert_array_equal(obs_bin_edges[0, 0, 0], [4, 5, 6]) + np.testing.assert_array_almost_equal( + obs_bin_edges[0, 0, 0], [0.5, 2.0, 1.12]) # check edges upper corner self.assertEqual(observable.max_x, params['max_x']) self.assertEqual(observable.max_y, params['max_y']) self.assertEqual(observable.max_z, params['max_z']) - observable.max_x = 7 - observable.max_y = 8 - observable.max_z = 9 + observable = espressomd.observables.DensityProfile( + **{**params, 'max_x': 7, 'max_y': 8, 'max_z': 9}) self.assertEqual(observable.max_x, 7) self.assertEqual(observable.max_y, 8) self.assertEqual(observable.max_z, 9) diff --git a/testsuite/python/observable_profileLB.py b/testsuite/python/observable_profileLB.py index e7866f9880f..1fb02ab8f9d 100644 --- a/testsuite/python/observable_profileLB.py +++ b/testsuite/python/observable_profileLB.py @@ -128,7 +128,8 @@ def test_lb_profile_interface(self): obs = espressomd.observables.LBVelocityProfile(**params) # check flag self.assertFalse(obs.allow_empty_bins) - obs.allow_empty_bins = True + obs = espressomd.observables.LBVelocityProfile( + **{**params, 'allow_empty_bins': True}) self.assertTrue(obs.allow_empty_bins) # check bins self.assertEqual(obs.n_x_bins, 4) @@ -136,18 +137,16 @@ def test_lb_profile_interface(self): self.assertEqual(obs.n_z_bins, 8) obs_data = obs.calculate() np.testing.assert_array_equal(obs_data.shape, [4, 6, 8, 3]) - obs.n_x_bins = 1 - obs.n_y_bins = 2 - obs.n_z_bins = 3 + obs = espressomd.observables.LBVelocityProfile( + **{**params, 'n_x_bins': 1, 'n_y_bins': 2, 'n_z_bins': 3}) obs_data = obs.calculate() np.testing.assert_array_equal(obs_data.shape, [1, 2, 3, 3]) # check edges lower corner self.assertEqual(obs.min_x, params['min_x']) self.assertEqual(obs.min_y, params['min_y']) self.assertEqual(obs.min_z, params['min_z']) - obs.min_x = 4 - obs.min_y = 5 - obs.min_z = 6 + obs = espressomd.observables.LBVelocityProfile( + **{**params, 'min_x': 4, 'min_y': 5, 'min_z': 6}) self.assertEqual(obs.min_x, 4) self.assertEqual(obs.min_y, 5) self.assertEqual(obs.min_z, 6) @@ -157,9 +156,8 @@ def test_lb_profile_interface(self): self.assertEqual(obs.max_x, params['max_x']) self.assertEqual(obs.max_y, params['max_y']) self.assertEqual(obs.max_z, params['max_z']) - obs.max_x = 7 - obs.max_y = 8 - obs.max_z = 9 + obs = espressomd.observables.LBVelocityProfile( + **{**params, 'max_x': 7, 'max_y': 8, 'max_z': 9}) self.assertEqual(obs.max_x, 7) self.assertEqual(obs.max_y, 8) self.assertEqual(obs.max_z, 9) @@ -169,9 +167,8 @@ def test_lb_profile_interface(self): self.assertEqual(obs.sampling_delta_x, params['sampling_delta_x']) self.assertEqual(obs.sampling_delta_y, params['sampling_delta_y']) self.assertEqual(obs.sampling_delta_z, params['sampling_delta_z']) - obs.sampling_delta_x = 10 - obs.sampling_delta_y = 11 - obs.sampling_delta_z = 12 + obs = espressomd.observables.LBVelocityProfile( + **{**params, 'sampling_delta_x': 10, 'sampling_delta_y': 11, 'sampling_delta_z': 12}) self.assertEqual(obs.sampling_delta_x, 10) self.assertEqual(obs.sampling_delta_y, 11) self.assertEqual(obs.sampling_delta_z, 12) @@ -179,9 +176,8 @@ def test_lb_profile_interface(self): self.assertEqual(obs.sampling_offset_x, params['sampling_offset_x']) self.assertEqual(obs.sampling_offset_y, params['sampling_offset_y']) self.assertEqual(obs.sampling_offset_z, params['sampling_offset_z']) - obs.sampling_offset_x = 13 - obs.sampling_offset_y = 14 - obs.sampling_offset_z = 15 + obs = espressomd.observables.LBVelocityProfile( + **{**params, 'sampling_offset_x': 13, 'sampling_offset_y': 14, 'sampling_offset_z': 15}) self.assertEqual(obs.sampling_offset_x, 13) self.assertEqual(obs.sampling_offset_y, 14) self.assertEqual(obs.sampling_offset_z, 15) diff --git a/testsuite/python/rdf.py b/testsuite/python/rdf.py index e2f43b2a6dc..46ec9d2b013 100644 --- a/testsuite/python/rdf.py +++ b/testsuite/python/rdf.py @@ -110,34 +110,35 @@ def test_rdf_interface(self): s.part.add(pos=4 * [(0, 0, 0)], type=[0, 1, 0, 1]) pids1 = s.part[:].id[0::2] pids2 = s.part[:].id[1::2] - observable = espressomd.observables.RDF(ids1=pids1, ids2=pids2, - min_r=1, max_r=2, n_r_bins=3) + params = { + 'ids1': pids1, + 'ids2': pids2, + 'min_r': 1, + 'max_r': 2, + 'n_r_bins': 3} + observable = espressomd.observables.RDF(**params) # check pids np.testing.assert_array_equal(np.copy(observable.ids1), pids1) np.testing.assert_array_equal(np.copy(observable.ids2), pids2) new_pids1 = [s.part[:].id[0]] new_pids2 = [s.part[:].id[1]] - observable.ids1 = new_pids1 - observable.ids2 = new_pids2 + observable = espressomd.observables.RDF( + **{**params, 'ids1': new_pids1, 'ids2': new_pids2}) np.testing.assert_array_equal(np.copy(observable.ids1), new_pids1) np.testing.assert_array_equal(np.copy(observable.ids2), new_pids2) # check bins self.assertEqual(observable.n_r_bins, 3) - observable.n_r_bins = 2 + observable = espressomd.observables.RDF(**{**params, 'n_r_bins': 2}) self.assertEqual(observable.n_r_bins, 2) obs_data = observable.calculate() np.testing.assert_array_equal(obs_data.shape, [2]) # check edges lower corner self.assertEqual(observable.min_r, 1) - observable.min_r = 0 - self.assertEqual(observable.min_r, 0) # check edges upper corner self.assertEqual(observable.max_r, 2) - observable.max_r = 4 - self.assertEqual(observable.max_r, 4) # check bin centers obs_bin_centers = observable.bin_centers() - np.testing.assert_array_equal(obs_bin_centers, [1, 3]) + np.testing.assert_array_almost_equal(obs_bin_centers, [1.25, 1.75]) if __name__ == "__main__":