Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RooCrystalBall.cxx
Go to the documentation of this file.
1/*****************************************************************************
2 * Project: RooFit *
3 * Authors: *
4 * WV, Wouter Verkerke, UC Santa Barbara, verkerke@slac.stanford.edu *
5 * DK, David Kirkby, UC Irvine, dkirkby@uci.edu *
6 * *
7 * Copyright (c) 2000-2019, Regents of the University of California *
8 * and Stanford University. All rights reserved. *
9 * *
10 * Redistribution and use in source and binary forms, *
11 * with or without modification, are permitted according to the terms *
12 * listed in LICENSE (http://roofit.sourceforge.net/license.txt) *
13 *****************************************************************************/
14
15// Authors of this class:
16// T. Skwarnicki:
17// - modify RooCBShape to Asymmetrical Double-Sided CB
18// Michael Wilkinson
19// - add to RooFit source
20// Jonas Rembser, CERN 02/2021:
21// - merging RooDSCBShape with RooSDSCBShape to RooCrystalBall
22// - implement possibility to have asymmetrical Gaussian core
23// - complete rewrite of evaluation and integral code to reduce code
24// duplication
25
26/** \class RooCrystalBall
27 \ingroup Roofit
28
29PDF implementing the generalized Asymmetrical Double-Sided Crystall Ball line shape.
30\f[
31 f(m;m_0,\sigma,\alpha_L,n_L,\alpha_R,n_R) =
32 \begin{cases}
33 A_L \cdot (B_L - \frac{m - m_0}{\sigma_L})^{-n_L}, & \mbox{for }\frac{m - m_0}{\sigma_L} < -\alpha_L \\
34 \exp \left( - \frac{1}{2} \cdot \left[ \frac{m - m_0}{\sigma_L} \right]^2 \right), & \mbox{for }\frac{m - m_0}{\sigma_L} \leq 0 \\
35 \exp \left( - \frac{1}{2} \cdot \left[ \frac{m - m_0}{\sigma_R} \right]^2 \right), & \mbox{for }\frac{m - m_0}{\sigma_R} \leq \alpha_R \\
36 A_R \cdot (B_R + \frac{m - m_0}{\sigma_R})^{-n_R}, & \mbox{otherwise}, \\
37 \end{cases}
38\f]
39times some normalization factor,
40where
41\f[
42 \begin{align}
43 A_i &= \left(\frac{n_i}{\left| \alpha_i \right|}\right)^{n_i} \cdot \exp\left(- \frac {\left| \alpha_i \right|^2}{2}\right) \\
44 B_i &= \frac{n_i}{\left| \alpha_i \right|} - \left| \alpha_i \right| \\
45 \end{align}
46\f]
47**/
48
49#include "RooCrystalBall.h"
50#include "RooHelpers.h"
51#include "TError.h"
52
53#include <cmath>
54#include <limits>
55#include <memory>
56#include <utility>
57
59
60////////////////////////////////////////////////////////////////////////////////
61/// Creates the fully parametrized crystal ball shape with asymmetric Gaussian core and asymmetric tails.
62///
63/// \param name Name that identifies the PDF in computations.
64/// \param title Title for plotting.
65/// \param x The variable of the PDF.
66/// \param x0 Location parameter of the Gaussian component.
67/// \param sigmaL Width parameter of the left side of the Gaussian component.
68/// \param sigmaR Width parameter of the right side of the Gaussian component.
69/// \param alphaL Location of transition to a power law on the left, in standard deviations away from the mean.
70/// \param nL Exponent of power-law tail on the left.
71/// \param alphaR Location of transition to a power law on the right, in standard deviations away from the mean.
72/// \param nR Exponent of power-law tail on the right.
73RooCrystalBall::RooCrystalBall(const char *name, const char *title, RooAbsReal &x, RooAbsReal &x0, RooAbsReal &sigmaL,
74 RooAbsReal &sigmaR, RooAbsReal &alphaL, RooAbsReal &nL, RooAbsReal &alphaR,
75 RooAbsReal &nR)
76 : RooAbsPdf(name, title), x_("x", "Dependent", this, x), x0_("x0", "X0", this, x0),
77 sigmaL_("sigmaL", "Left Sigma", this, sigmaL), sigmaR_("sigmaR", "Right Sigma", this, sigmaR),
78 alphaL_{"alphaL", "Left Alpha", this, alphaL}, nL_{"nL", "Left Order", this, nL},
79 alphaR_{std::make_unique<RooRealProxy>("alphaR", "Right Alpha", this, alphaR)},
80 nR_{std::make_unique<RooRealProxy>("nR", "Right Order", this, nR)}
81{
82 RooHelpers::checkRangeOfParameters(this, {&sigmaL}, 0.0);
83 RooHelpers::checkRangeOfParameters(this, {&sigmaR}, 0.0);
84 RooHelpers::checkRangeOfParameters(this, {&alphaL}, 0.0);
85 RooHelpers::checkRangeOfParameters(this, {&alphaR}, 0.0);
86 RooHelpers::checkRangeOfParameters(this, {&nL}, 0.0);
87 RooHelpers::checkRangeOfParameters(this, {&nR}, 0.0);
88}
89
90////////////////////////////////////////////////////////////////////////////////
91/// Create a crystal ball shape with symmetric Gaussian core and asymmetric tails (just like `RooDSCBShape`).
92///
93/// \param name Name that identifies the PDF in computations.
94/// \param title Title for plotting.
95/// \param x The variable of the PDF.
96/// \param x0 Location parameter of the Gaussian component.
97/// \param sigmaLR Width parameter of the Gaussian component.
98/// \param alphaL Location of transition to a power law on the left, in standard deviations away from the mean.
99/// \param nL Exponent of power-law tail on the left.
100/// \param alphaR Location of transition to a power law on the right, in standard deviations away from the mean.
101/// \param nR Exponent of power-law tail on the right.
102RooCrystalBall::RooCrystalBall(const char *name, const char *title, RooAbsReal &x, RooAbsReal &x0, RooAbsReal &sigmaLR,
103 RooAbsReal &alphaL, RooAbsReal &nL, RooAbsReal &alphaR, RooAbsReal &nR)
104 : RooAbsPdf(name, title), x_("x", "Dependent", this, x), x0_("x0", "X0", this, x0),
105 sigmaL_("sigmaL", "Left Sigma", this, sigmaLR), sigmaR_("sigmaR", "Right Sigma", this, sigmaLR),
106 alphaL_{"alphaL", "Left Alpha", this, alphaL}, nL_{"nL", "Left Order", this, nL},
107 alphaR_{std::make_unique<RooRealProxy>("alphaR", "Right Alpha", this, alphaR)},
108 nR_{std::make_unique<RooRealProxy>("nR", "Right Order", this, nR)}
109{
110 RooHelpers::checkRangeOfParameters(this, {&sigmaLR}, 0.0);
111 RooHelpers::checkRangeOfParameters(this, {&alphaL}, 0.0);
112 RooHelpers::checkRangeOfParameters(this, {&alphaR}, 0.0);
113 RooHelpers::checkRangeOfParameters(this, {&nL}, 0.0);
114 RooHelpers::checkRangeOfParameters(this, {&nR}, 0.0);
115}
116
117////////////////////////////////////////////////////////////////////////////////
118/// Create a crystal ball shape with symmetric Gaussian core and only a tail on
119/// one side (just like `RooCBShape`) or two symmetric tails (like `RooSDSCBShape`).
120///
121/// \param name Name that identifies the PDF in computations.
122/// \param title Title for plotting.
123/// \param x The variable of the PDF.
124/// \param x0 Location parameter of the Gaussian component.
125/// \param sigmaLR Width parameter of the Gaussian component.
126/// \param alpha Location of transition to a power law, in standard deviations away from the mean.
127/// \param n Exponent of power-law tail.
128/// \param doubleSided Whether the tail is only on one side or on both sides
129RooCrystalBall::RooCrystalBall(const char *name, const char *title, RooAbsReal &x, RooAbsReal &x0, RooAbsReal &sigmaLR,
130 RooAbsReal &alpha, RooAbsReal &n, bool doubleSided)
131 : RooAbsPdf(name, title), x_("x", "Dependent", this, x), x0_("x0", "X0", this, x0),
132 sigmaL_{"sigmaL", "Left Sigma", this, sigmaLR}, sigmaR_{"sigmaR", "Right Sigma", this, sigmaLR},
133 alphaL_{"alphaL", "Left Alpha", this, alpha},
134 nL_{"nL", "Left Order", this, n}
135{
136 if (doubleSided) {
137 alphaR_ = std::make_unique<RooRealProxy>("alphaR", "Right Alpha", this, alpha);
138 nR_ = std::make_unique<RooRealProxy>("nR", "Right Order", this, n);
139 }
140
141 RooHelpers::checkRangeOfParameters(this, {&sigmaLR}, 0.0);
143 if (doubleSided) {
144 RooHelpers::checkRangeOfParameters(this, {&alpha}, 0.0);
145 }
146}
147
148////////////////////////////////////////////////////////////////////////////////
149/// Copy a RooCrystalBall.
151 : RooAbsPdf(other, name), x_("x", this, other.x_), x0_("x0", this, other.x0_),
152 sigmaL_("sigmaL", this, other.sigmaL_),
153 sigmaR_("sigmaR", this, other.sigmaR_), alphaL_{"alphaL", this, other.alphaL_},
154 nL_{"nL", this, other.nL_},
155 alphaR_{other.alphaR_ ? std::make_unique<RooRealProxy>("alphaR", this, *other.alphaR_) : nullptr},
156 nR_{other.nR_ ? std::make_unique<RooRealProxy>("nR", this, *other.nR_) : nullptr}
157{
158}
159
160////////////////////////////////////////////////////////////////////////////////
161
162namespace {
163
164inline double evaluateCrystalBallTail(double t, double alpha, double n)
165{
166 double a = std::pow(n / alpha, n) * std::exp(-0.5 * alpha * alpha);
167 double b = n / alpha - alpha;
168
169 return a / std::pow(b - t, n);
170}
171
172inline double integrateGaussian(double sigmaL, double sigmaR, double tmin, double tmax)
173{
174 constexpr double sqrtPiOver2 = 1.2533141373;
175 constexpr double sqrt2 = 1.4142135624;
176
177 const double sigmaMin = tmin < 0 ? sigmaL : sigmaR;
178 const double sigmaMax = tmax < 0 ? sigmaL : sigmaR;
179
180 return sqrtPiOver2 * (sigmaMax * std::erf(tmax / sqrt2) - sigmaMin * std::erf(tmin / sqrt2));
181}
182
183inline double integrateTailLogVersion(double sigma, double alpha, double n, double tmin, double tmax)
184{
185 double a = std::pow(n / alpha, n) * exp(-0.5 * alpha * alpha);
186 double b = n / alpha - alpha;
187
188 return a * sigma * (log(b - tmin) - log(b - tmax));
189}
190
191inline double integrateTailRegular(double sigma, double alpha, double n, double tmin, double tmax)
192{
193 double a = std::pow(n / alpha, n) * exp(-0.5 * alpha * alpha);
194 double b = n / alpha - alpha;
195
196 return a * sigma / (1.0 - n) * (1.0 / (std::pow(b - tmin, n - 1.0)) - 1.0 / (std::pow(b - tmax, n - 1.0)));
197}
198
199} // namespace
200
201////////////////////////////////////////////////////////////////////////////////
202
204{
205 const double x = x_;
206 const double x0 = x0_;
207 const double sigmaL = std::abs(sigmaL_);
208 const double sigmaR = std::abs(sigmaR_);
209 double alphaL = std::abs(alphaL_);
210 double nL = nL_;
211 double alphaR = alphaR_ ? std::abs(*alphaR_) : std::numeric_limits<double>::infinity();
212 double nR = nR_ ? *nR_ : 0.0;
213
214 // If alphaL is negative, then the tail will be on the right side.
215 // Like this, we follow the convention established by RooCBShape.
216 if(!alphaR_ && alphaL_ < 0.0) {
217 std::swap(alphaL, alphaR);
218 std::swap(nL, nR);
219 }
220
221 const double t = (x - x0) / (x < x0 ? sigmaL : sigmaR);
222
223 if (t < -alphaL) {
224 return evaluateCrystalBallTail(t, alphaL, nL);
225 } else if (t <= alphaR) {
226 return std::exp(-0.5 * t * t);
227 } else {
228 return evaluateCrystalBallTail(-t, alphaR, nR);
229 }
230}
231
232////////////////////////////////////////////////////////////////////////////////
233
234Int_t RooCrystalBall::getAnalyticalIntegral(RooArgSet &allVars, RooArgSet &analVars, const char * /*rangeName*/) const
235{
236 return matchArgs(allVars, analVars, x_) ? 1 : 0;
237}
238
239////////////////////////////////////////////////////////////////////////////////
240
241double RooCrystalBall::analyticalIntegral(Int_t code, const char *rangeName) const
242{
243 R__ASSERT(code == 1);
244
245 const double x0 = x0_;
246 const double sigmaL = std::abs(sigmaL_);
247 const double sigmaR = std::abs(sigmaR_);
248 double alphaL = std::abs(alphaL_);
249 double nL = nL_;
250 double alphaR = alphaR_ ? std::abs(*alphaR_) : std::numeric_limits<double>::infinity();
251 double nR = nR_ ? *nR_ : 0.0;
252
253 // If alphaL is negative, then the tail will be on the right side.
254 // Like this, we follow the convention established by RooCBShape.
255 if(!alphaR_ && alphaL_ < 0.0) {
256 std::swap(alphaL, alphaR);
257 std::swap(nL, nR);
258 }
259
260 constexpr double switchToLogThreshold = 1.0e-05;
261
262 const double xmin = x_.min(rangeName);
263 const double xmax = x_.max(rangeName);
264 const double tmin = (xmin - x0) / (xmin < x0 ? sigmaL : sigmaR);
265 const double tmax = (xmax - x0) / (xmax < x0 ? sigmaL : sigmaR);
266
267 double result = 0.0;
268
269 if (tmin < -alphaL) {
270 auto integrateTailL = std::abs(nL - 1.0) < switchToLogThreshold ? integrateTailLogVersion : integrateTailRegular;
271 result += integrateTailL(sigmaL, alphaL, nL, tmin, std::min(tmax, -alphaL));
272 }
273 if (tmax > alphaR) {
274 auto integrateTailR = std::abs(nR - 1.0) < switchToLogThreshold ? integrateTailLogVersion : integrateTailRegular;
275 result += integrateTailR(sigmaR, alphaR, nR, -tmax, std::min(-tmin, -alphaR));
276 }
277 if (tmin < alphaR && tmax > -alphaL) {
278 result += integrateGaussian(sigmaL, sigmaR, std::max(tmin, -alphaL), std::min(tmax, alphaR));
279 }
280
281 return result;
282}
283
284////////////////////////////////////////////////////////////////////////////////
285/// Advertise that we know the maximum of self for given (m0,alpha,n,sigma).
286
288{
289 RooArgSet dummy;
290 return matchArgs(vars, dummy, x_) ? 1 : 0;
291}
292
293////////////////////////////////////////////////////////////////////////////////
294
296{
297 R__ASSERT(code == 1);
298
299 // The maximum value for given (m0,alpha,n,sigma) is 1./ Integral in the variable range
300 // For the crystal ball, the maximum is 1.0 in the current implementation,
301 // but it's maybe better to keep this general in case the implementation changes.
302 return 1.0 / analyticalIntegral(code);
303}
#define b(i)
Definition RSha256.hxx:100
#define a(i)
Definition RSha256.hxx:99
#define ClassImp(name)
Definition Rtypes.h:377
#define R__ASSERT(e)
Definition TError.h:118
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
char name[80]
Definition TGX11.cxx:110
float xmin
float xmax
Abstract interface for all probability density functions.
Definition RooAbsPdf.h:40
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:59
bool matchArgs(const RooArgSet &allDeps, RooArgSet &numDeps, const RooArgProxy &a) const
Utility function for use in getAnalyticalIntegral().
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:55
PDF implementing the generalized Asymmetrical Double-Sided Crystall Ball line shape.
std::unique_ptr< RooRealProxy > nR_
Int_t getAnalyticalIntegral(RooArgSet &allVars, RooArgSet &analVars, const char *rangeName=nullptr) const override
Interface function getAnalyticalIntergral advertises the analytical integrals that are supported.
RooRealProxy sigmaR_
RooRealProxy nL_
RooRealProxy x_
std::unique_ptr< RooRealProxy > alphaR_
RooRealProxy x0_
RooRealProxy alphaL_
double maxVal(Int_t code) const override
Return maximum value for set of observables identified by code assigned in getMaxVal.
double evaluate() const override
Evaluate this PDF / function / constant. Needs to be overridden by all derived classes.
RooRealProxy sigmaL_
Int_t getMaxVal(const RooArgSet &vars) const override
Advertise that we know the maximum of self for given (m0,alpha,n,sigma).
double analyticalIntegral(Int_t code, const char *rangeName=nullptr) const override
Implements the actual analytical integral(s) advertised by getAnalyticalIntegral.
double max(const char *rname=nullptr) const
Query upper limit of range. This requires the payload to be RooAbsRealLValue or derived.
double min(const char *rname=nullptr) const
Query lower limit of range. This requires the payload to be RooAbsRealLValue or derived.
RVec< PromoteType< T > > log(const RVec< T > &v)
Definition RVec.hxx:1804
RVec< PromoteType< T > > exp(const RVec< T > &v)
Definition RVec.hxx:1800
const Double_t sigma
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
void checkRangeOfParameters(const RooAbsReal *callingClass, std::initializer_list< const RooAbsReal * > pars, double min=-std::numeric_limits< double >::max(), double max=std::numeric_limits< double >::max(), bool limitsInAllowedRange=false, std::string const &extraMessage="")
Check if the parameters have a range, and warn if the range extends below / above the set limits.