ROOT   Reference Guide
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 *
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 "RooAbsReal.h"
51#include "RooHelpers.h"
52#include "TError.h"
53
54#include "ROOT/RMakeUnique.hxx"
55
56#include <cmath>
57#include <limits>
58#include <utility>
59
61
62////////////////////////////////////////////////////////////////////////////////
63/// Creates the fully parametrized crystal ball shape with asymmetric Gaussian core and asymmetric tails.
64///
65/// \param name Name that identifies the PDF in computations.
66/// \param title Title for plotting.
67/// \param x The variable of the PDF.
68/// \param x0 Location parameter of the Gaussian component.
69/// \param sigmaL Width parameter of the left side of the Gaussian component.
70/// \param sigmaR Width parameter of the right side of the Gaussian component.
71/// \param alphaL Location of transition to a power law on the left, in standard deviations away from the mean.
72/// \param nL Exponent of power-law tail on the left.
73/// \param alphaR Location of transition to a power law on the right, in standard deviations away from the mean.
74/// \param nR Exponent of power-law tail on the right.
75RooCrystalBall::RooCrystalBall(const char *name, const char *title, RooAbsReal &x, RooAbsReal &x0, RooAbsReal &sigmaL,
76 RooAbsReal &sigmaR, RooAbsReal &alphaL, RooAbsReal &nL, RooAbsReal &alphaR,
77 RooAbsReal &nR)
78 : RooAbsPdf(name, title), x_("x", "Dependent", this, x), x0_("x0", "X0", this, x0),
79 sigmaL_("sigmaL", "Left Sigma", this, sigmaL), sigmaR_("sigmaR", "Right Sigma", this, sigmaR),
80 alphaL_{"alphaL", "Left Alpha", this, alphaL}, nL_{"nL", "Left Order", this, nL},
81 alphaR_{std::make_unique<RooRealProxy>("alphaR", "Right Alpha", this, alphaR)},
82 nR_{std::make_unique<RooRealProxy>("nR", "Right Order", this, nR)}
83{
84 RooHelpers::checkRangeOfParameters(this, {&sigmaL}, 0.0);
85 RooHelpers::checkRangeOfParameters(this, {&sigmaR}, 0.0);
86 RooHelpers::checkRangeOfParameters(this, {&alphaL}, 0.0);
87 RooHelpers::checkRangeOfParameters(this, {&alphaR}, 0.0);
88 RooHelpers::checkRangeOfParameters(this, {&nL}, 0.0);
89 RooHelpers::checkRangeOfParameters(this, {&nR}, 0.0);
90}
91
92////////////////////////////////////////////////////////////////////////////////
93/// Create a crystal ball shape with symmetric Gaussian core and asymmetric tails (just like RooDSCBShape).
94///
95/// \param name Name that identifies the PDF in computations.
96/// \param title Title for plotting.
97/// \param x The variable of the PDF.
98/// \param x0 Location parameter of the Gaussian component.
99/// \param sigma Width parameter of the Gaussian component.
100/// \param alphaL Location of transition to a power law on the left, in standard deviations away from the mean.
101/// \param nL Exponent of power-law tail on the left.
102/// \param alphaR Location of transition to a power law on the right, in standard deviations away from the mean.
103/// \param nR Exponent of power-law tail on the right.
104RooCrystalBall::RooCrystalBall(const char *name, const char *title, RooAbsReal &x, RooAbsReal &x0, RooAbsReal &sigmaLR,
105 RooAbsReal &alphaL, RooAbsReal &nL, RooAbsReal &alphaR, RooAbsReal &nR)
106 : RooAbsPdf(name, title), x_("x", "Dependent", this, x), x0_("x0", "X0", this, x0),
107 sigmaL_("sigmaL", "Left Sigma", this, sigmaLR), sigmaR_("sigmaR", "Right Sigma", this, sigmaLR),
108 alphaL_{"alphaL", "Left Alpha", this, alphaL}, nL_{"nL", "Left Order", this, nL},
109 alphaR_{std::make_unique<RooRealProxy>("alphaR", "Right Alpha", this, alphaR)},
110 nR_{std::make_unique<RooRealProxy>("nR", "Right Order", this, nR)}
111{
112 RooHelpers::checkRangeOfParameters(this, {&sigmaLR}, 0.0);
113 RooHelpers::checkRangeOfParameters(this, {&alphaL}, 0.0);
114 RooHelpers::checkRangeOfParameters(this, {&alphaR}, 0.0);
115 RooHelpers::checkRangeOfParameters(this, {&nL}, 0.0);
116 RooHelpers::checkRangeOfParameters(this, {&nR}, 0.0);
117}
118
119////////////////////////////////////////////////////////////////////////////////
120/// Create a crystal ball shape with symmetric Gaussian core and only a tail on
121/// one side (just like RooCBShape) or two symmetric tails (like RooSDSCBShape).
122///
123/// \param name Name that identifies the PDF in computations.
124/// \param title Title for plotting.
125/// \param x The variable of the PDF.
126/// \param x0 Location parameter of the Gaussian component.
127/// \param sigma Width parameter of the Gaussian component.
128/// \param alpha Location of transition to a power law, in standard deviations away from the mean.
129/// \param n Exponent of power-law tail.
130/// \param doubleSided Whether the tail is only on one side or on both sides
131RooCrystalBall::RooCrystalBall(const char *name, const char *title, RooAbsReal &x, RooAbsReal &x0, RooAbsReal &sigmaLR,
132 RooAbsReal &alpha, RooAbsReal &n, bool doubleSided)
133 : RooAbsPdf(name, title), x_("x", "Dependent", this, x), x0_("x0", "X0", this, x0),
134 sigmaL_{"sigmaL", "Left Sigma", this, sigmaLR}, sigmaR_{"sigmaR", "Right Sigma", this, sigmaLR},
135 alphaL_{"alphaL", "Left Alpha", this, alpha},
136 nL_{"nL", "Left Order", this, n}
137{
138 if (doubleSided) {
139 alphaR_ = std::make_unique<RooRealProxy>("alphaR", "Right Alpha", this, alpha);
140 nR_ = std::make_unique<RooRealProxy>("nR", "Right Order", this, n);
141 }
142
143 RooHelpers::checkRangeOfParameters(this, {&sigmaLR}, 0.0);
145 if (doubleSided) {
146 RooHelpers::checkRangeOfParameters(this, {&alpha}, 0.0);
147 }
148}
149
150////////////////////////////////////////////////////////////////////////////////
151/// Copy a RooCrystalBall.
153 : RooAbsPdf(other, name), x_("x", this, other.x_), x0_("x0", this, other.x0_),
154 sigmaL_("sigmaL", this, other.sigmaL_),
155 sigmaR_("sigmaR", this, other.sigmaR_), alphaL_{"alphaL", this, other.alphaL_},
156 nL_{"nL", this, other.nL_},
157 alphaR_{other.alphaR_ ? std::make_unique<RooRealProxy>("alphaR", this, *other.alphaR_) : nullptr},
158 nR_{other.nR_ ? std::make_unique<RooRealProxy>("nR", this, *other.nR_) : nullptr}
159{
160}
161
162////////////////////////////////////////////////////////////////////////////////
163
164namespace {
165
166inline double evaluateCrystalBallTail(double t, double alpha, double n)
167{
168 double a = std::pow(n / alpha, n) * std::exp(-0.5 * alpha * alpha);
169 double b = n / alpha - alpha;
170
171 return a / std::pow(b - t, n);
172}
173
174inline double integrateGaussian(double sigmaL, double sigmaR, double tmin, double tmax)
175{
176 constexpr double sqrtPiOver2 = 1.2533141373;
177 constexpr double sqrt2 = 1.4142135624;
178
179 const double sigmaMin = tmin < 0 ? sigmaL : sigmaR;
180 const double sigmaMax = tmax < 0 ? sigmaL : sigmaR;
181
182 return sqrtPiOver2 * (sigmaMax * std::erf(tmax / sqrt2) - sigmaMin * std::erf(tmin / sqrt2));
183}
184
185inline double integrateTailLogVersion(double sigma, double alpha, double n, double tmin, double tmax)
186{
187 double a = std::pow(n / alpha, n) * exp(-0.5 * alpha * alpha);
188 double b = n / alpha - alpha;
189
190 return a * sigma * (log(b - tmin) - log(b - tmax));
191}
192
193inline double integrateTailRegular(double sigma, double alpha, double n, double tmin, double tmax)
194{
195 double a = std::pow(n / alpha, n) * exp(-0.5 * alpha * alpha);
196 double b = n / alpha - alpha;
197
198 return a * sigma / (1.0 - n) * (1.0 / (std::pow(b - tmin, n - 1.0)) - 1.0 / (std::pow(b - tmax, n - 1.0)));
199}
200
201} // namespace
202
203////////////////////////////////////////////////////////////////////////////////
204
206{
207 const double x = x_;
208 const double x0 = x0_;
209 const double sigmaL = std::abs(sigmaL_);
210 const double sigmaR = std::abs(sigmaR_);
211 double alphaL = std::abs(alphaL_);
212 double nL = nL_;
213 double alphaR = alphaR_ ? std::abs(*alphaR_) : std::numeric_limits<double>::infinity();
214 double nR = nR_ ? *nR_ : 0.0;
215
216 // If alphaL is negative, then the tail will be on the right side.
217 // Like this, we follow the convention established by RooCBShape.
218 if(!alphaR_ && alphaL_ < 0.0) {
219 std::swap(alphaL, alphaR);
220 std::swap(nL, nR);
221 }
222
223 const double t = (x - x0) / (x < x0 ? sigmaL : sigmaR);
224
225 if (t < -alphaL) {
226 return evaluateCrystalBallTail(t, alphaL, nL);
227 } else if (t <= alphaR) {
228 return std::exp(-0.5 * t * t);
229 } else {
230 return evaluateCrystalBallTail(-t, alphaR, nR);
231 }
232}
233
234////////////////////////////////////////////////////////////////////////////////
235
236Int_t RooCrystalBall::getAnalyticalIntegral(RooArgSet &allVars, RooArgSet &analVars, const char * /*rangeName*/) const
237{
238 return matchArgs(allVars, analVars, x_) ? 1 : 0;
239}
240
241////////////////////////////////////////////////////////////////////////////////
242
243Double_t RooCrystalBall::analyticalIntegral(Int_t code, const char *rangeName) const
244{
245 R__ASSERT(code == 1);
246
247 const double x0 = x0_;
248 const double sigmaL = std::abs(sigmaL_);
249 const double sigmaR = std::abs(sigmaR_);
250 double alphaL = std::abs(alphaL_);
251 double nL = nL_;
252 double alphaR = alphaR_ ? std::abs(*alphaR_) : std::numeric_limits<double>::infinity();
253 double nR = nR_ ? *nR_ : 0.0;
254
255 // If alphaL is negative, then the tail will be on the right side.
256 // Like this, we follow the convention established by RooCBShape.
257 if(!alphaR_ && alphaL_ < 0.0) {
258 std::swap(alphaL, alphaR);
259 std::swap(nL, nR);
260 }
261
262 constexpr double switchToLogThreshold = 1.0e-05;
263
264 const double xmin = x_.min(rangeName);
265 const double xmax = x_.max(rangeName);
266 const double tmin = (xmin - x0) / (xmin < x0 ? sigmaL : sigmaR);
267 const double tmax = (xmax - x0) / (xmax < x0 ? sigmaL : sigmaR);
268
269 double result = 0.0;
270
271 if (tmin < -alphaL) {
272 auto integrateTailL = std::abs(nL - 1.0) < switchToLogThreshold ? integrateTailLogVersion : integrateTailRegular;
273 result += integrateTailL(sigmaL, alphaL, nL, tmin, std::min(tmax, -alphaL));
274 }
275 if (tmax > alphaR) {
276 auto integrateTailR = std::abs(nR - 1.0) < switchToLogThreshold ? integrateTailLogVersion : integrateTailRegular;
277 result += integrateTailR(sigmaR, alphaR, nR, -tmax, std::min(-tmin, -alphaR));
278 }
279 if (tmin < alphaR && tmax > -alphaL) {
280 result += integrateGaussian(sigmaL, sigmaR, std::max(tmin, -alphaL), std::min(tmax, alphaR));
281 }
282
283 return result;
284}
285
286////////////////////////////////////////////////////////////////////////////////
287/// Advertise that we know the maximum of self for given (m0,alpha,n,sigma).
288
290{
291 RooArgSet dummy;
292 return matchArgs(vars, dummy, x_) ? 1 : 0;
293}
294
295////////////////////////////////////////////////////////////////////////////////
296
298{
299 R__ASSERT(code == 1);
300
301 // The maximum value for given (m0,alpha,n,sigma) is 1./ Integral in the variable range
302 // For the crystal ball, the maximum is 1.0 in the current implementation,
303 // but it's maybe better to keep this general in case the implementation changes.
304 return 1.0 / analyticalIntegral(code);
305}
#define b(i)
Definition RSha256.hxx:100
#define a(i)
Definition RSha256.hxx:99
double Double_t
Definition RtypesCore.h:59
#define ClassImp(name)
Definition Rtypes.h:364
#define R__ASSERT(e)
Definition TError.h:120
char name[80]
Definition TGX11.cxx:110
float xmin
float xmax
double exp(double)
double log(double)
RooAbsReal is the common abstract base class for objects that represent a real value and implements f...
Definition RooAbsReal.h:61
Bool_t 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:29
PDF implementing the generalized Asymmetrical Double-Sided Crystall Ball line shape.
std::unique_ptr< RooRealProxy > nR_
virtual Double_t analyticalIntegral(Int_t code, const char *rangeName=0) const
Implements the actual analytical integral(s) advertised by getAnalyticalIntegral.
Double_t evaluate() const
Evaluate this PDF / function / constant. Needs to be overridden by all derived classes.
virtual Double_t maxVal(Int_t code) const
Return maximum value for set of observables identified by code assigned in getMaxVal.
RooRealProxy sigmaR_
RooRealProxy nL_
RooRealProxy x_
virtual Int_t getAnalyticalIntegral(RooArgSet &allVars, RooArgSet &analVars, const char *rangeName=0) const
Interface function getAnalyticalIntergral advertises the analytical integrals that are supported.
std::unique_ptr< RooRealProxy > alphaR_
RooRealProxy x0_
RooRealProxy alphaL_
RooRealProxy sigmaL_
virtual Int_t getMaxVal(const RooArgSet &vars) const
Advertise that we know the maximum of self for given (m0,alpha,n,sigma).
double min(const char *rname=0) const
Query lower limit of range. This requires the payload to be RooAbsRealLValue or derived.
double max(const char *rname=0) const
Query upper limit of range. This requires the payload to be RooAbsRealLValue or derived.
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 extraMessage="")
Check if the parameters have a range, and warn if the range extends below / above the set limits.