diff --git a/include/InvariantMassBinning.h b/include/InvariantMassBinning.h new file mode 100644 index 00000000..3e63dd29 --- /dev/null +++ b/include/InvariantMassBinning.h @@ -0,0 +1,98 @@ +/* YAP - Yet another PWA toolkit + Copyright 2015, Technische Universitaet Muenchen, + Authors: Daniel Greenwald, Johannes Rauch + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/// \file + + +#ifndef yap_InvariantMassBinning_h +#define yap_InvariantMassBinning_h + +#include "fwd/InvariantMassBinning.h" + +#include "fwd/CachedValue.h" +#include "fwd/DataPoint.h" +#include "fwd/Model.h" +#include "fwd/StatusManager.h" + +#include "StaticDataAccessor.h" + +#include +#include + +namespace yap { + +/// \brief Partitions the invariant-mass range in \f$n\f$ bins, whose (constant) lower +/// edges are stored in `BinLowEdges_`. +/// \details `BinLowEdges_` is a \f$(n+1)\f$-element vector whose entries partition the +/// axis in an closed-open fashion as follows: +/// \f[ +/// (-\infty, m_0) \cup [m_0,m_1) \cup [m_1,m_2) \dots [m_{n-1}, m_n) \cup [m_n, +\infty). +/// \f] +/// The `0`-th value of `BinLowEdges_` corresponds to \f$m_0\f$; the `n`-th to \f$m_n\f$. +/// InvariantMassBinning has a function to calculate which bin the invariant mass of a +/// ParticleCombination lies in: +/// * In case of underflow (i.e. \f$m < m_0\f$), the bin will be set to `-1`. +/// * In case of overflow (i.e. \f$m > m_n\f$), the bin will be set to `n`. +/// +/// \author Paolo Di Giglio. +class InvariantMassBinning : public StaticDataAccessor +{ +public: + /// Constructor. + /// \param m The owning Model. + /// \param low_edges Low edges of the bins; the last element is the upper edge of the last bin. + explicit InvariantMassBinning(Model& m, const std::vector& low_edges); + + /// \brief Calculate which bin the invatiant mass of the + /// ParticleCombination's belong to. + /// \details The class description explains which bin value is used + /// in case of overflow or underflow. + /// \param d The DataPoint to calculate into. + /// \param sm The StatusManager to update. + virtual void calculate(DataPoint& d, StatusManager& sm) const override; + + /// Access the partition. + const std::vector& lowEdges() const + { return BinLowEdges_; } + + /// Return the bin number. + double bin(const DataPoint& d, const std::shared_ptr& pc) const; + +private: + + /// Lower edges of the bins. + const std::vector BinLowEdges_; + + /// The bin the masses of the ParticleCombination's belong to. + std::shared_ptr Bin_; +}; + +/// Check if n corresponds to an underflow value. +/// \param n The index to check agains the bin underflow value. +inline const bool is_underflow(int n) +{ return n < 0; } + +/// Check if n corresponds to an overflow value. +/// \param n The index to check agains the bin overflow value. +/// \param B The partition the bin overflow value has to be evaluated from. +inline const bool is_overflow(int n, const InvariantMassBinning& B) +{ return n >= static_cast(B.lowEdges().size()) - 1; } + +} + +#endif diff --git a/include/fwd/InvariantMassBinning.h b/include/fwd/InvariantMassBinning.h new file mode 100644 index 00000000..e299f4ba --- /dev/null +++ b/include/fwd/InvariantMassBinning.h @@ -0,0 +1,32 @@ +/* YAP - Yet another PWA toolkit + Copyright 2015, Technische Universitaet Muenchen, + Authors: Daniel Greenwald, Johannes Rauch + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/// \file +/// \author Paolo Di Giglio. +/// Contains forward declarations only. + +#ifndef yap_InvariantMassBinningFwd_h +#define yap_InvariantMassBinningFwd_h + +namespace yap { + +class InvariantMassBinning; + +} + +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 40258c21..b6b2b610 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,52 +1,53 @@ set(INCLUDE_DIRECTORIES - ${YAP_SOURCE_DIR}/include) + ${YAP_SOURCE_DIR}/include) include_directories(${INCLUDE_DIRECTORIES}) set(YAP_SOURCES - BlattWeisskopf.cxx - BreitWigner.cxx - CachedValue.cxx - ClebschGordan.cxx - DataAccessor.cxx - DataPartition.cxx - DataPoint.cxx - DataSet.cxx - DecayChannel.cxx - DecayingParticle.cxx - DecayTree.cxx - DecayTreeVectorIntegral.cxx - Filters.cxx - FinalStateParticle.cxx - Flatte.cxx - FreeAmplitude.cxx - FourMomenta.cxx - HelicityAngles.cxx - HelicityFormalism.cxx - ImportanceSampler.cxx - Integrator.cxx - MassRange.cxx - MassShape.cxx - MassShapeWithNominalMass.cxx - MeasuredBreakupMomenta.cxx - Model.cxx - ModelIntegral.cxx - Parameter.cxx - ParticleCombination.cxx - ParticleCombinationCache.cxx - Particle.cxx - ParticleFactory.cxx - PDL.cxx - PoleMass.cxx - QuantumNumbers.cxx - RecalculableDataAccessor.cxx - RelativisticBreitWigner.cxx - Resonance.cxx - SpinAmplitude.cxx - SpinAmplitudeCache.cxx - StaticDataAccessor.cxx - StatusManager.cxx - WignerD.cxx - ZemachFormalism.cxx + BlattWeisskopf.cxx + BreitWigner.cxx + CachedValue.cxx + ClebschGordan.cxx + DataAccessor.cxx + DataPartition.cxx + DataPoint.cxx + DataSet.cxx + DecayChannel.cxx + DecayTree.cxx + DecayTreeVectorIntegral.cxx + DecayingParticle.cxx + Filters.cxx + FinalStateParticle.cxx + Flatte.cxx + FourMomenta.cxx + FreeAmplitude.cxx + HelicityAngles.cxx + HelicityFormalism.cxx + ImportanceSampler.cxx + Integrator.cxx + InvariantMassBinning.cxx + MassRange.cxx + MassShape.cxx + MassShapeWithNominalMass.cxx + MeasuredBreakupMomenta.cxx + Model.cxx + ModelIntegral.cxx + PDL.cxx + Parameter.cxx + Particle.cxx + ParticleCombination.cxx + ParticleCombinationCache.cxx + ParticleFactory.cxx + PoleMass.cxx + QuantumNumbers.cxx + RecalculableDataAccessor.cxx + RelativisticBreitWigner.cxx + Resonance.cxx + SpinAmplitude.cxx + SpinAmplitudeCache.cxx + StaticDataAccessor.cxx + StatusManager.cxx + WignerD.cxx + ZemachFormalism.cxx ) add_library(YAP SHARED ${YAP_SOURCES}) @@ -55,21 +56,21 @@ add_library(YAP SHARED ${YAP_SOURCES}) # cmake -DLIBRARY_OUTPUT_DIRECTORY:PATH= # otherwise, default LD_LIBRARY_PATH if(NOT DEFINED LIBRARY_OUTPUT_DIRECTORY) - set(LIBRARY_OUTPUT_DIRECTORY ${YAP_SOURCE_DIR}/lib/${CMAKE_BUILD_TYPE}) + set(LIBRARY_OUTPUT_DIRECTORY ${YAP_SOURCE_DIR}/lib/${CMAKE_BUILD_TYPE}) endif() if(NOT DEFINED INCLUDE_OUTPUT_DIRECTORY) - set(INCLUDE_OUTPUT_DIRECTORY ${YAP_SOURCE_DIR}/include/YAP) + set(INCLUDE_OUTPUT_DIRECTORY ${YAP_SOURCE_DIR}/include/YAP) endif() install(TARGETS YAP LIBRARY DESTINATION ${LIBRARY_OUTPUT_DIRECTORY}) # Matches all the headers in ${YAPDID}/include and its subdirs file(GLOB_RECURSE - INSTALL_INCLUDES ${YAP_SOURCE_DIR}/include/*.h) + INSTALL_INCLUDES ${YAP_SOURCE_DIR}/include/*.h) #message(STATUS "${INSTALL_INCLUDES}") #install(FILE ${INSTALL_INCLUDES} -# DESTINATION ${INCLUDE_OUTPUT_DIRECTORY} -# ) +# DESTINATION ${INCLUDE_OUTPUT_DIRECTORY} +# ) diff --git a/src/InvariantMassBinning.cxx b/src/InvariantMassBinning.cxx new file mode 100644 index 00000000..89e93c6e --- /dev/null +++ b/src/InvariantMassBinning.cxx @@ -0,0 +1,66 @@ +#include "InvariantMassBinning.h" + +#include "CachedValue.h" +#include "CalculationStatus.h" +#include "DataPoint.h" +#include "Exceptions.h" +#include "FourMomenta.h" +#include "Model.h" +#include "ParticleCombination.h" +#include "StatusManager.h" + +#include +#include +#include + +namespace yap { + +//------------------------- +InvariantMassBinning::InvariantMassBinning(Model& m, const std::vector& bins) : + StaticDataAccessor(m, equal_by_orderless_content), + BinLowEdges_(bins), + Bin_(RealCachedValue::create(*this)) +{ + if (BinLowEdges_.size() < 2) + throw exceptions::Exception("At least two bin edges must be specified", + "InvariantMassBinning::InvariantMassBinning"); + + // use std::less_equal so that if two elements are the same, std::is_sorted + // will return false + if (!std::is_sorted(BinLowEdges_.cbegin(), BinLowEdges_.cend(), std::less_equal())) + throw exceptions::Exception("Elements of the vector are not monotonically increasing", + "InvariantMassBinning::InvariantMassBinning"); + + registerWithModel(); +} + +//------------------------- +void InvariantMassBinning::calculate(DataPoint& d, StatusManager& sm) const +{ + // set all bins to uncalculated + sm.set(*this, CalculationStatus::uncalculated); + + // loop over particle combinations -> indices + for (auto& pc_i : symmetrizationIndices()) { + // check if calculation is necessary + if (sm.status(*Bin_, pc_i.second ) == CalculationStatus::uncalculated) { + auto invariant_mass = model()->fourMomenta()->m(d, pc_i.first); + + // get the first lower bound that is greater than the invariant mass + auto bin = std::upper_bound(BinLowEdges_.cbegin(), BinLowEdges_.cend(), invariant_mass); + + // set the value into the DataPoint + // NOTE: std::distance() - 1 accounts for the fact that the actual bin value + // is the one preceding the one that is found above + Bin_->setValue(std::distance(BinLowEdges_.cbegin(), bin) - 1, d, pc_i.second, sm); + } + } +} + +//------------------------- +double InvariantMassBinning::bin(const DataPoint& d, const std::shared_ptr& pc) const +{ + return Bin_->value(d, symmetrizationIndex(pc)); +} + +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9b608244..c8d74103 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -25,6 +25,7 @@ set(YAP_TEST_SOURCES test_HelicityAngles.cxx test_HelicityAngles_boostRotate.cxx test_integration.cxx + test_InvariantMassBinning.cxx test_Matrix.cxx test_swapDalitzAxes.cxx test_swapFinalStates.cxx diff --git a/test/test_InvariantMassBinning.cxx b/test/test_InvariantMassBinning.cxx new file mode 100644 index 00000000..f7cbc880 --- /dev/null +++ b/test/test_InvariantMassBinning.cxx @@ -0,0 +1,115 @@ +/// \file test_InvariantMassBinning.cxx +/// \brief Test the binning of the mass axis. +/// \author Paolo Di Giglio + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "helperFunctions.h" +#include +#include +#include + +const std::vector monospaced_partition(const yap::MassRange& range, std::size_t bins) +{ + const double bin_width = (range[1] - range[0]) / bins; + // Reserve the space for the partition + std::vector p; + p.reserve(bins + 1); + + for (std::size_t i = 0; i < bins + 1; ++i) + p.push_back(i * bin_width); + + return p; +} + +inline const std::vector model_mass_range(std::shared_ptr model) +{ + return yap::mass_range( + model->massAxes(), + model->initialStateParticles().begin()->first, + model->finalStateParticles()); +} + +inline const bool mass_belongs_to_bin(double mass, int bin, const std::vector& bin_low_edges) +{ + if (bin == static_cast(bin_low_edges.size()) - 1) + return bin_low_edges[bin] <= mass; + if (bin == -1) + return mass < bin_low_edges[0]; + + return bin_low_edges[bin] <= mass && mass < bin_low_edges[bin + 1]; +} + +class InvariantMassBinningTest : public yap::InvariantMassBinning +{ +public: + InvariantMassBinningTest(yap::Model& m, const std::vector& bins) : + yap::InvariantMassBinning(m, bins) + {} + + void addPC(std::shared_ptr pc) + { yap::InvariantMassBinning::addParticleCombination(pc); } +}; + +TEST_CASE( "InvariantMassBinning" ) +{ + // disable debug logs in test + yap::disableLogs(el::Level::Debug); + //yap::plainLogs(el::Level::Debug); + + // Create the model + auto model = create_model(); + REQUIRE(model->consistent()); + + // Get the allowed mass range for the model mass axes + auto m_range = model_mass_range(model); +// for (const auto& it : m_range) +// std::cerr << it[0] << " " << it[1] << std::endl; + + // Number of bins + const unsigned int nBins = 10; + // Vector of the low edges of the partition + auto bin_low_edges = monospaced_partition(*m_range.begin(), nBins); + // Create the binning + InvariantMassBinningTest binning(*model, bin_low_edges); + + // Loop over particle combinations -> indices + for (const auto& pc_i : model->fourMomenta()->symmetrizationIndices()) { + // Add particle combinations to the binning + binning.addPC(pc_i.first); + } + + // Generate the DataSet + const unsigned int nPoints = 1000; + auto data_set = generate_data(model, nPoints); + + // Calculate and check the bins + for (const auto& pc_i : model->fourMomenta()->symmetrizationIndices()) { + for (auto& dp : data_set) { + // Calculate the bin + binning.calculate(dp, data_set); + // Check the bin + auto mass = model->fourMomenta()->m(dp, pc_i.first); + auto bin = binning.bin(dp, pc_i.first); + auto bin_is_correct = mass_belongs_to_bin(mass, bin, bin_low_edges); + if (!bin_is_correct) + std::cout << "bin = " << bin << " " + << bin_low_edges[bin] << " <= " + << mass << " < " + << bin_low_edges[bin + 1] << std::endl; + + REQUIRE(bin_is_correct); + } + } +}