diff --git a/inc/LauDecayTimePhysicsModel.hh b/inc/LauDecayTimePhysicsModel.hh index b6354fd..e127f1e 100644 --- a/inc/LauDecayTimePhysicsModel.hh +++ b/inc/LauDecayTimePhysicsModel.hh @@ -1,270 +1,329 @@ /* Copyright 2021 University of Warwick Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* Laura++ package authors: John Back Paul Harrison Thomas Latham */ /*! \file LauDecayTimePhysicsModel.hh \brief File containing declaration of LauDecayTimePhysicsModel class. */ #ifndef LAU_DECAYTIME_PHYSICSMODEL #define LAU_DECAYTIME_PHYSICSMODEL #include "LauDecayTime.hh" +#include "LauJsonTools.hh" #include "Rtypes.h" #include "TString.h" -#include +#include +#include #include #include #include class LauAbsRValue; /*! \class LauDecayTimePhysicsModel \brief Class for defining the physics and associated parameters for the decay time model */ class LauDecayTimePhysicsModel final { public: //! Constructor /*! \param type the type of the physics model \param parameters the parameters of the physics model */ LauDecayTimePhysicsModel( const LauDecayTime::FuncType type, std::vector> parameters ); //! Retrieve the type of the physics function /*! \return the function type */ LauDecayTime::FuncType getFunctionType() const { return type_; } //! Retrieve the parameters of the physics model so that they can be loaded into a fit /*! \return the parameters of the physics model */ std::vector getParameters() { return params_; } //! Retrieve the up-to-date value of the lifetime /*! \return the lifetime value */ const Double_t& getLifetimeValue() const { return tauVal_; } //! Retrieve the up-to-date value of the inverse lifetime /*! \return the inverse lifetime value */ const Double_t& getGammaValue() const { return gammaVal_; } //! Retrieve the up-to-date value of the mass difference /*! \return the mass difference value */ const Double_t& getDeltaMValue() const { return deltaMVal_; } //! Retrieve the up-to-date value of the width difference /*! \return the width difference value */ const Double_t& getDeltaGammaValue() const { return deltaGammaVal_; } //! Retrieve the up-to-date value of the prompt fraction /*! \return the prompt fraction value */ const Double_t& getFracPromptValue() const { return fracPromptVal_; } //! Retrieve whether any of the parameter values have changed in the last fit iteration /*! \return the any param changed flag */ const bool& anythingChanged() const { return anythingChanged_; } //! Retrieve whether the lifetime value has changed in the last fit iteration /*! \return the lifetime changed flag */ const bool& lifetimeChanged() const { return tauChanged_; } //! Retrieve the up-to-date value of the mass difference /*! \return the mass difference value */ const bool& deltaMChanged() const { return deltaMChanged_; } //! Retrieve the up-to-date value of the width difference /*! \return the width difference value */ const bool& deltaGammaChanged() const { return deltaGammaChanged_; } //! Retrieve the up-to-date value of the prompt fraction /*! \return the prompt fraction value */ const bool& fracPromptChanged() const { return fracPromptChanged_; } //! Initialise the parameter cache /*! Must be called prior to starting fitting or generation */ void initialise(); //! Propagate any updates to parameters and recalculate information as neeeded /*! Should be called at each fit iteration */ void propagateParUpdates(); //! Construct a collection of physics model objects based on values read from a JSON value /*! The JSON value should be an Object, which must contain the following elements: - "parameters", an Array, which contains an Object for each parameter of the models to be constructed - "signalModel", an Object, which contains: + "functionType", a String to specify the function type (must match a state of LauDecayTime::FuncType) + "parameters", an Array of Strings that specifies the parameters from the top-level "parameters" Array to be used to build the signal model - "backgroundModels", an Array, which contains Objects for each background component, each of which contain: + "name", a String specifying the background component name + "model", an Object to define the model (definition as per the "signalModel" entry above) \param [in] j the JSON value to deserialise \return the collection of newly constructed models */ static std::map> readFromJson( const nlohmann::json& j ); //! Construct a collection of physics model objects based on values read from a JSON file /*! The JSON value structure is as defined in readFromJson(nlohmann::json) \param [in] fileName the name of the file from which the JSON should be read \param [in] elementName the optional name of the JSON element that contains the definitions (defaults to using the root record) \return the collection of newly constructed models */ static std::map> readFromJson( const TString& fileName, const TString& elementName = "" ); + //! Write a collection of physics model objects to a JSON file + /*! + \tparam PhysModelPtr a pointer-like type that points to LauDecayTimePhysicsModel objects + \param [in] models the collection of models to be written out + \param [in] fileName the name of the file to which the JSON should be written + \param [in] elementName the (optional) name of the JSON element to which the coefficients should be written + \param [in] append if true, append the spline to the existing JSON within the file - if using this option it is then mandatory to provide elementName + \param [in] indent the indentation level to use in the output in number of spaces (defaults to 4) + */ + template + static void writeToJson( const std::map& models, const TString& fileName, const TString& elementName = "", const bool append = false, const int indent = 4 ); + //! Retrieve the specified parameter /*! \param [in] parName the parameter to retrieve */ const LauAbsRValue* findParameter(const TString& parName) const; //! Retrieve the specified parameter /*! \param [in] parName the parameter to retrieve */ LauAbsRValue* findParameter(const TString& parName); private: //! Update the cached parameter values void updateParameterCache(); //! Read the signal model part of the JSON serialisation /*! \param [in] j the JSON value from which to read the model \param [in] parameters the collection of parameters, some of which should be used to construct the model \return the newly constructed signal model */ static std::unique_ptr readSignalModelFromJson( const nlohmann::json& j, std::vector>& parameters ); //! Read the background models part of the JSON serialisation /*! \param [in] j the JSON value from which to read the model \param [in] parameters the collection of parameters, which should be used to construct the models \return the newly constructed background models */ static std::map> readBackgroundModelsFromJson( const nlohmann::json& j, std::vector>& parameters ); //! Which type of decay time function is this? const LauDecayTime::FuncType type_; //! Store of all parameters of the physics model std::vector> paramsOwned_; //! Store of all parameters of the physics model std::vector params_; //! Lifetime parameter LauAbsRValue* tau_{nullptr}; //! Mass difference parameter LauAbsRValue* deltaM_{nullptr}; //! Width difference parameter LauAbsRValue* deltaGamma_{nullptr}; //! Parameter for the fraction of prompt events in DeltaExp LauAbsRValue* fracPrompt_{nullptr}; //! Cached value of lifetime Double_t tauVal_{0.0}; //! Cached value of 1/lifetime Double_t gammaVal_{0.0}; //! Cached value of mass difference Double_t deltaMVal_{0.0}; //! Cached value of width difference Double_t deltaGammaVal_{0.0}; //! Cached value of prompt fraction Double_t fracPromptVal_{0.0}; //! Is the lifetime floating in the fit? bool tauFloating_{false}; //! Is the mass difference floating in the fit? bool deltaMFloating_{false}; //! Is the width difference floating in the fit? bool deltaGammaFloating_{false}; //! Is the prompt fraction floating in the fit? bool fracPromptFloating_{false}; //! Are any of the physics parameters floating in the fit? bool anythingFloating_{false}; //! Has the lifetime parameter changed in the last fit iteration? bool tauChanged_{false}; //! Has the mass difference parameter changed in the last fit iteration? bool deltaMChanged_{false}; //! Has the width difference parameter changed in the last fit iteration? bool deltaGammaChanged_{false}; //! Has the prompt fraction parameter changed in the last fit iteration? bool fracPromptChanged_{false}; //! Have any of the physics parameters changed in the last fit iteration? bool anythingChanged_{false}; ClassDef(LauDecayTimePhysicsModel, 0) }; +template +void LauDecayTimePhysicsModel::writeToJson( const std::map& models, const TString& fileName, const TString& elementName, const bool append, const int indent ) +{ + using nlohmann::json; + + // Check for a signal model + auto iter { models.find("signal") }; + if ( iter == models.end() ) { + std::cerr << "ERROR in LauDecayTimePhysicsModel::writeToJson : cannot locate the signal model in the collection, aborting" << std::endl; + return; + } + + json j; + + json& parametersJson = j["parameters"] = json::array(); + json& signalModelJson = j["signalModel"] = json::object(); + json& backgroundModelsJson = j["backgroundModels"] = json::array(); + + for ( const auto& [ name, model ] : models ) { + if ( name == "signal" ) { + signalModelJson["functionType"] = model->getFunctionType(); + json& sigPars = signalModelJson["parameters"] = json::array(); + for ( auto par : model->getParameters() ) { + sigPars.push_back( par->name() ); + parametersJson.push_back( *par ); + } + } else { + json obj = json::object(); + obj["name"] = name; + json& bkgndModel = obj["model"] = json::object(); + bkgndModel["functionType"] = model->getFunctionType(); + json& bkgndPars = bkgndModel["parameters"] = json::array(); + for ( auto par : model->getParameters() ) { + bkgndPars.push_back( par->name() ); + parametersJson.push_back( *par ); + } + backgroundModelsJson.push_back( obj ); + } + } + + const bool writeOK { LauJsonTools::writeJsonFile( j, fileName.Data(), elementName.Data(), append, indent ) }; + if ( ! writeOK ) { + std::cerr << "ERROR in LauDecayTimePhysicsModel::writeToJson : could not successfully write to file \"" << fileName << std::endl; + } +} #endif diff --git a/test/TestReadDecayTimePhysicsModelFromJson.cc b/test/TestReadDecayTimePhysicsModelFromJson.cc index 81563e1..df8993d 100644 --- a/test/TestReadDecayTimePhysicsModelFromJson.cc +++ b/test/TestReadDecayTimePhysicsModelFromJson.cc @@ -1,63 +1,71 @@ #include "LauDecayTimePhysicsModel.hh" #include "LauParameter.hh" #include #include #include #include int main() { using nlohmann::json; json j = json::object(); json& parameters = j["parameters"] = json::array(); json& signalModel = j["signalModel"] = json::object(); json& backgroundModels = j["backgroundModels"] = json::array(); // signal parameters auto tauBd = std::make_unique("dt_sig_tau_Bd", 1.519, 0.5, 5.0, kTRUE); auto deltaMd = std::make_unique("dt_sig_deltaM_Bd", 0.5065, 0.0, 1.0, kFALSE); // Bd2DKpi background parameters auto tauBd_Bkgnd = std::unique_ptr(tauBd->createClone("dt_bkgnd_tau_Bd")); auto deltaMd_Bkgnd = std::unique_ptr(deltaMd->createClone("dt_bkgnd_deltaM_Bd")); // Bs2DKpi background parameters auto tauBs = std::make_unique("dt_tau_Bs", 1.527, 1.526, 1.528, kTRUE); auto deltaMs = std::make_unique("dt_deltaM_Bs", 17.765, 17.60, 17.80, kTRUE); auto deltaGs = std::make_unique("dt_deltaGamma_Bs", 0.084, 0.080, 0.090, kTRUE); // Lb2Dppi background parameters auto tauLb = std::make_unique("dt_tau_Lb", 1.464, 1.450, 1.470, kTRUE); std::vector> params; params.emplace_back( std::move(tauBd) ); params.emplace_back( std::move(deltaMd) ); params.emplace_back( std::move(tauBd_Bkgnd) ); params.emplace_back( std::move(deltaMd_Bkgnd) ); params.emplace_back( std::move(tauBs) ); params.emplace_back( std::move(deltaMs) ); params.emplace_back( std::move(deltaGs) ); params.emplace_back( std::move(tauLb) ); for ( auto& par : params ) { parameters.push_back( *par ); } signalModel["functionType"] = LauDecayTime::FuncType::ExpTrig; signalModel["parameters"] = { "dt_sig_tau_Bd", "dt_sig_deltaM_Bd" }; backgroundModels.push_back( json::object( { {"name", "Bd2DKpi"}, {"model", json::object( { {"functionType", LauDecayTime::FuncType::ExpTrig}, {"parameters", json::array({"dt_bkgnd_tau_Bd","dt_bkgnd_deltaM_Bd"})} } )} } ) ); backgroundModels.push_back( json::object( { {"name", "Bs2DKpi"}, {"model", json::object( { {"functionType", LauDecayTime::FuncType::ExpHypTrig}, {"parameters", json::array({"dt_tau_Bs","dt_deltaM_Bs","dt_deltaGamma_Bs"})} } )} } ) ); backgroundModels.push_back( json::object( { {"name", "Lb2Dppi"}, {"model", json::object( { {"functionType", LauDecayTime::FuncType::Exp}, {"parameters", json::array({"dt_tau_Lb"})} } )} } ) ); - std::cout << j.dump(4) << std::endl; + { + std::ofstream fout("dt-physics-test1.json"); + fout << j.dump(4) << std::endl; + } + + //std::map myMap; auto models = LauDecayTimePhysicsModel::readFromJson( j ); for ( auto& [ name, model ] : models ) { std::cout << "Loaded LauDecayTimePhysicsModel for component: " << name << " with type " << static_cast(model->getFunctionType()) << std::endl; + //myMap.insert( std::make_pair(name, model.get()) ); } + + LauDecayTimePhysicsModel::writeToJson( models, "dt-physics-test2.json" ); }