GRSISort "v4.0.0.5"
An extension of the ROOT analysis Framework
Loading...
Searching...
No Matches
TMultiPeak.cxx
Go to the documentation of this file.
1#include "TMultiPeak.h"
2
3#include "Math/Minimizer.h"
4#include "Math/Factory.h"
5#include "Math/Functor.h"
6
7#include <algorithm>
8
10
11TMultiPeak::TMultiPeak(Double_t xlow, Double_t xhigh, const std::vector<Double_t>& centroids, Option_t*)
12 : TGRSIFit("multipeakbg", this, &TMultiPeak::MultiPhotoPeakBG, xlow, xhigh, centroids.size() * 6 + 5, "TMultiPeak", "MultiPhotoPeakBG")
13{
14 std::cout << "Warning, the class TMultiPeak is deprecated (use TPeakFitter instead)!" << std::endl;
15 Clear();
16 // We make the background first so we can send it to the TPeaks.
17 fBackground = new TF1(Form("MPbackground_%d_to_%d", static_cast<Int_t>(xlow), static_cast<Int_t>(xhigh)), this, &TMultiPeak::MultiStepBG, xlow, xhigh, centroids.size() * 6 + 5, "TMuliPeak", "MultiStepBG");
18 fBackground->SetNpx(1000);
19 fBackground->SetLineStyle(2);
20 fBackground->SetLineColor(kBlack);
22
23 for(double cent : centroids) {
24 Bool_t out_of_range_flag = false;
25 if(cent > xhigh) {
26 std::cout << "centroid " << cent << " is higher than range" << std::endl;
27 out_of_range_flag = true;
28 } else if(cent < xlow) {
29 std::cout << "centroid " << cent << " is lower than range" << std::endl;
30 out_of_range_flag = true;
31 }
32 if(out_of_range_flag) {
33 std::cout << "ignoring peak at " << cent << ", make a new multi peak with the corrected energy" << std::endl;
34 } else {
35 auto* peak = new TPeak(cent, xlow, xhigh, fBackground);
36 peak->AddToGlobalList(kFALSE);
37 fPeakVec.push_back(peak);
38 }
39 }
40 SetRange(xlow, xhigh);
41
42 SetName(Form("MultiPeak_%d_to_%d", static_cast<Int_t>(xlow),
43 static_cast<Int_t>(xhigh))); // Gives a default name to the peak
44 SortPeaks(); // Defaults to sorting by TPeak::CompareEnergy
45 InitNames();
46}
47
48TMultiPeak::TMultiPeak() : TGRSIFit("multipeakbg", this, &TMultiPeak::MultiPhotoPeakBG, 0, 1000, 10, "TMultiPeak", "MultiPhotoPeakBG")
49{
50 std::cout << "Warning, the class TMultiPeak is deprecated (use TPeakFitter instead)!" << std::endl;
51 // I don't think this constructor should be used, RD.
52 InitNames();
53 fBackground = new TF1("background", this, &TMultiPeak::MultiStepBG, 1000, 10, 10, "TMultiPeak", "MultiStepBG"); // This is a weird nonsense line.
54 fBackground->SetNpx(1000);
55 fBackground->SetLineStyle(2);
56 fBackground->SetLineColor(kBlack);
58}
59
61{
62 delete fBackground;
63
64 for(auto& peak : fPeakVec) {
65 delete peak;
66 }
67}
68
69void TMultiPeak::SortPeaks(Bool_t (*SortFunction)(const TPeak*, const TPeak*))
70{
71 std::sort(fPeakVec.begin(), fPeakVec.end(), SortFunction);
72}
73
75{
76 SetParName(0, "N_Peaks");
77 SetParName(1, "A");
78 SetParName(2, "B");
79 SetParName(3, "C");
80 SetParName(4, "bg_offset");
81
82 for(int i = 0; i < static_cast<int>(fPeakVec.size()); ++i) {
83 SetParName(6 * i + 5, Form("Height_%i", i));
84 SetParName(6 * i + 6, Form("Centroid_%i", i));
85 SetParName(6 * i + 7, Form("Sigma_%i", i));
86 SetParName(6 * i + 8, Form("Beta_%i", i));
87 SetParName(6 * i + 9, Form("R_%i", i));
88 SetParName(6 * i + 10, Form("Step_%i", i));
89 }
90 FixParameter(0, static_cast<double>(fPeakVec.size()));
91}
92
94{
95 copy.Copy(*this);
96}
97
98void TMultiPeak::Copy(TObject& obj) const
99{
100 TGRSIFit::Copy(obj);
101 auto* mpobj = static_cast<TMultiPeak*>(&obj);
102 if((mpobj->fBackground) == nullptr) {
103 mpobj->fBackground = new TF1(*(fBackground));
104 } else {
105 *(mpobj->fBackground) = *fBackground;
106 }
107
109
110 // Copy all of the TPeaks.
111 for(auto* i : fPeakVec) {
112 auto* peak = new TPeak(*i);
113 peak->AddToGlobalList(kFALSE);
114 mpobj->fPeakVec.push_back(peak);
115 }
116}
117
118Bool_t TMultiPeak::InitParams(TH1* fithist)
119{
120 // Makes initial guesses at parameters for the fit. Uses the histogram to make the initial guesses
121 if((fithist == nullptr) && (GetHist() != nullptr)) {
122 fithist = GetHist();
123 }
124
125 if(fithist == nullptr) {
126 std::cout << "No histogram is associated yet, no initial guesses made" << std::endl;
127 return false;
128 }
129
130 FixParameter(0, static_cast<double>(fPeakVec.size()));
131 // This is the range for the fit.
132 Double_t xlow = 0;
133 Double_t xhigh = 0;
134 GetRange(xlow, xhigh);
135 Int_t binlow = fithist->GetXaxis()->FindBin(xlow);
136 Int_t binhigh = fithist->GetXaxis()->FindBin(xhigh);
137
138 // Initialize background
139 SetParLimits(1, 0.0, fithist->GetBinContent(binlow) * 100.0);
140 SetParameter("A", fithist->GetBinContent(binlow));
141 SetParameter("B", (fithist->GetBinContent(binlow) - fithist->GetBinContent(binhigh)) / (xlow - xhigh));
142 SetParameter("C", 0.0000);
143 SetParameter("bg_offset", (xhigh + xlow) / 2.0);
144
145 FixParameter(3, 0);
146
147 // We need to initialize parameters for every peak in the fit
148 for(int i = 0; i < static_cast<int>(fPeakVec.size()); ++i) {
149 Double_t centroid = fPeakVec.at(i)->GetCentroid();
150 Int_t bin = fithist->GetXaxis()->FindBin(centroid);
151 SetParLimits(6 * i + 5, 0, fithist->GetBinContent(bin) * 5.);
152 SetParLimits(6 * i + 6, centroid - 4, centroid + 4);
153 // SetParLimits(6*i+7,0.1,xhigh-xlow);//This will be linked to other peaks eventually.
154 SetParLimits(6 * i + 7, 0.1, 1.5); // This will be linked to other peaks eventually.
155 SetParLimits(6 * i + 8, 0.000001, 10);
156 SetParLimits(6 * i + 9, 0.000001, 100);
157 SetParLimits(6 * i + 10, 0.0, 1.0E2);
158 // Step size is allow to vary to anything. If it goes below 0, the code will fix it to 0
159
160 // Now set the actual paramter to start the fit from these points
161 SetParameter(Form("Height_%i", i), fithist->GetBinContent(bin));
162 SetParameter(Form("Centroid_%i", i), centroid);
163 // SetParameter("sigma",(xhigh-xlow)*0.5); // slightly more robust starting value for sigma -JKS
164 // SetParameter("sigma",1.0/binWidth); // slightly more robust starting value for sigma -JKS
165 SetParameter(Form("Sigma_%i", i), TMath::Sqrt(9.0 + 4. * GetParameter(Form("Centroid_%i", i)) / 1000.));
166 SetParameter(Form("Beta_%i", i), GetParameter(Form("Sigma_%i", i)) / 2.0);
167 SetParameter(Form("R_%i", i), 1.0);
168 SetParameter(Form("Step_%i", i), 1.0);
169
170 // Fix beta and R. These will be released if they are needed (or can be asked to be released).
171 FixParameter(6 * i + 8, GetParameter(Form("Beta_%i", i)));
172 FixParameter(6 * i + 9, 0.00);
173 }
174
176 return true;
177}
178
179Bool_t TMultiPeak::Fit(TH1* fithist, Option_t* opt)
180{
181 TString optstr = opt;
182 if((fithist == nullptr) && (GetHist() == nullptr)) {
183 std::cout << "No hist passed, trying something...";
184 fithist = fHistogram;
185 }
186 if(fithist == nullptr) {
187 std::cout << "No histogram associated with Peak" << std::endl;
188 return false;
189 }
190 if(!IsInitialized()) {
191 InitParams(fithist);
192 }
193
194 TVirtualFitter::SetMaxIterations(100000);
195 ROOT::Math::MinimizerOptions::SetDefaultMinimizer("Minuit2", "Combination");
196 SetHist(fithist);
197
198 TString options(opt);
199 bool print_flag = true;
200 if(options.Contains("Q")) {
201 print_flag = false;
202 }
203
204 // Now that it is initialized, let's fit it.
205
206 TFitResultPtr fitres;
207 // Log likelihood is the proper fitting technique UNLESS the data is a result of an addition or subtraction.
209 fitres = fithist->Fit(this, Form("%sLRSQ", opt)); // The RS needs to always be there
210 } else {
211 fitres = fithist->Fit(this, Form("%sRSQ", opt)); // The RS needs to always be there
212 }
213
214 std::vector<Double_t> sigma_list(static_cast<int>(GetParameter(0) + 0.5));
215 for(int i = 0; i < static_cast<int>(GetParameter(0) + 0.5); ++i) {
216 // Get Median sigma
217 sigma_list[i] = GetParameter(6 * i + 7);
218 }
219 std::sort(sigma_list.begin(), sigma_list.end(), std::greater<>());
220 Double_t median = sigma_list.at(sigma_list.size() / 2);
221
222 Double_t range_low = 0.;
223 Double_t range_high = 0.;
224 for(int i = 0; i < static_cast<int>(GetParameter(0) + 0.5); ++i) {
225 GetParLimits(6 * i + 7, range_low, range_high);
226 if(range_low != range_high) {
227 SetParLimits(6 * i + 7, median * .95, median * 1.05);
228 }
229 }
230
231 // Refit
233 fitres = fithist->Fit(this, Form("%sLRS", opt)); // The RS needs to always be there
234 } else {
235 fitres = fithist->Fit(this, Form("%sRS", opt)); // The RS needs to always be there
236 }
237
238 // After performing this fit I want to put something here that takes the fit result (good,bad,etc)
239 // for printing out. RD
240
241 // This removes the background parts of the fit form the integral error, while maintaining the covariance between the
242 // fits and the background.
243 TMatrixDSym CovMat = fitres->GetCovarianceMatrix();
244 CovMat(0, 0) = 0.0;
245 CovMat(1, 1) = 0.0;
246 CovMat(2, 2) = 0.0;
247 CovMat(3, 3) = 0.0;
248 CovMat(4, 4) = 0.0;
249
250 // This copies the parameters background but the background function doesn't have peaks
252 // We now make a copy of the covariance matrix that has completel 0 diagonals so that we can remove the other peaks
253 // form the integral error.
254 // This is done by adding back the peak of interest on the diagonal when it is integrated.
255 TMatrixDSym emptyCovMat = CovMat;
256 for(size_t i = 0; i < fPeakVec.size(); ++i) {
257 emptyCovMat(6 * i + 5, 6 * i + 5) = 0.0;
258 emptyCovMat(6 * i + 6, 6 * i + 6) = 0.0;
259 emptyCovMat(6 * i + 7, 6 * i + 7) = 0.0;
260 emptyCovMat(6 * i + 8, 6 * i + 8) = 0.0;
261 emptyCovMat(6 * i + 9, 6 * i + 9) = 0.0;
262 emptyCovMat(6 * i + 10, 6 * i + 10) = 0.0;
263 }
264
265 if(print_flag) {
266 std::cout << "Chi^2/NDF = " << fitres->Chi2() / fitres->Ndf() << std::endl;
267 }
268 // We will now set the parameters of each of the peaks based on the fits.
269 for(int i = 0; i < static_cast<int>(fPeakVec.size()); ++i) {
270 auto* tmpMp = new TMultiPeak(*this);
271 tmpMp->ClearParameters(); // We need to clear all of the parameters so that we can add the ones we want back in
272 Double_t binWidth = fithist->GetBinWidth(1); // This assumes all bins are the same width, so we can just take the first bin instead of the bin of the centroid.
273 TPeak* peak = fPeakVec.at(i);
274 TMatrixDSym tmpCovMat = emptyCovMat;
275 peak->SetParameter("Height", GetParameter(Form("Height_%i", i)));
276 peak->SetParameter("centroid", GetParameter(Form("Centroid_%i", i)));
277 peak->SetParameter("sigma", GetParameter(Form("Sigma_%i", i)));
278 peak->SetParameter("beta", GetParameter(Form("Beta_%i", i)));
279 peak->SetParameter("R", GetParameter(Form("R_%i", i)));
280 peak->SetParameter("step", GetParameter(Form("Step_%i", i)));
281 peak->SetParameter("A", 0.0);
282 peak->SetParameter("B", 0.0);
283 peak->SetParameter("C", 0.0);
284 peak->SetParameter("bg_offset", 0.0);
285 peak->SetChi2(fitres->Chi2());
286 peak->SetNdf(fitres->Ndf());
287
288 // Set the important diagonals for the integral of the covariance matrix
289 tmpCovMat(6 * i + 5, 6 * i + 5) = CovMat(6 * i + 5, 6 * i + 5);
290 tmpCovMat(6 * i + 6, 6 * i + 6) = CovMat(6 * i + 6, 6 * i + 6);
291 tmpCovMat(6 * i + 7, 6 * i + 7) = CovMat(6 * i + 7, 6 * i + 7);
292 tmpCovMat(6 * i + 8, 6 * i + 8) = CovMat(6 * i + 8, 6 * i + 8);
293 tmpCovMat(6 * i + 9, 6 * i + 9) = CovMat(6 * i + 9, 6 * i + 9);
294
295 tmpMp->SetParameter("N_Peaks", GetParameter("N_Peaks"));
296 tmpMp->SetParameter(Form("Height_%i", i), GetParameter(Form("Height_%i", i)));
297 tmpMp->SetParameter(Form("Centroid_%i", i), GetParameter(Form("Centroid_%i", i)));
298 tmpMp->SetParameter(Form("Sigma_%i", i), GetParameter(Form("Sigma_%i", i)));
299 tmpMp->SetParameter(Form("Beta_%i", i), GetParameter(Form("Beta_%i", i)));
300 tmpMp->SetParameter(Form("R_%i", i), GetParameter(Form("R_%i", i)));
301
302 Double_t width = GetParameter(Form("Sigma_%i", i));
303 Double_t xlow = 0.;
304 Double_t xhigh = 0.;
305 GetRange(xlow, xhigh);
306 Double_t int_low = xlow - 10. * width; // making the integration bounds a bit smaller, but still large enough. -JKS
307 Double_t int_high = xhigh + 10. * width;
308
309 // Make a function that does not include the background
310 // Intgrate the background.
311 tmpMp->SetRange(int_low, int_high); // This will help get the true area of the gaussian 200 ~ infinity in a gaus
312 // peak->SetName("tmppeak");
313
314 // This is where we will do integrals and stuff.
315 peak->SetArea((tmpMp->Integral(int_low, int_high)) / binWidth);
316 peak->SetAreaErr((tmpMp->IntegralError(int_low, int_high, tmpMp->GetParameters(), tmpCovMat.GetMatrixArray())) /
317 binWidth);
318 peak->SetParameter("centroid", GetParameter(Form("Centroid_%i", i)));
319 peak->SetParError(peak->GetParNumber("centroid"), GetParError(GetParNumber(Form("Centroid_%i", i))));
320 peak->SetParameter("sigma", GetParameter(GetParNumber(Form("Sigma_%i", i))));
321 peak->SetParError(peak->GetParNumber("sigma"), GetParError(GetParNumber(Form("Sigma_%i", i))));
322 if(print_flag) {
323 std::cout << "Integral: " << peak->GetArea() << " +- " << peak->GetAreaErr() << std::endl;
324 }
325 }
326
327 return true;
328}
329
330void TMultiPeak::Clear(Option_t* opt)
331{
332 TGRSIFit::Clear(opt);
333 for(auto& peak : fPeakVec) {
334 delete peak;
335 peak = nullptr;
336 }
337 fPeakVec.clear();
338}
339
340void TMultiPeak::Print(Option_t* opt) const
341{
342 /// Prints TMultiPeak properties. To see More properties use the option "+"
343 std::cout << "Name: " << GetName() << std::endl;
344 std::cout << "Number of Peaks: " << fPeakVec.size() << std::endl;
345 TF1::Print();
346 for(int i = 0; i < static_cast<int>(fPeakVec.size()); ++i) {
347 std::cout << "Peak: " << i << std::endl;
348 fPeakVec.at(i)->Print(opt);
349 std::cout << std::endl;
350 }
351}
352
353Double_t TMultiPeak::MultiPhotoPeakBG(Double_t* dim, Double_t* par)
354{
355 // Limits need to be imposed or error states may occour.
356 //
357 // General background.
358 int npeaks = static_cast<int>(par[0] + 0.5);
359 double result = TGRSIFunctions::PolyBg(dim, &par[1], 2); // polynomial background. uses par[1->4]
360 for(int i = 0; i < npeaks; ++i) { // par[0] is number of peaks
361 std::array<Double_t, 6> tmp_par;
362 tmp_par[0] = par[6 * i + 5]; // height of photopeak
363 tmp_par[1] = par[6 * i + 6]; // Peak Centroid of non skew gaus
364 tmp_par[2] = par[6 * i + 7]; // standard deviation of gaussian
365 tmp_par[3] = par[6 * i + 8]; //"skewedness" of the skewed gaussian
366 tmp_par[4] = par[6 * i + 9]; // relative height of skewed gaussian
367 tmp_par[5] = par[6 * i + 10]; // Size of step in background
368 result += TGRSIFunctions::PhotoPeak(dim, tmp_par.data()) + TGRSIFunctions::StepFunction(dim, tmp_par.data());
369 }
370 return result;
371}
372
373Double_t TMultiPeak::MultiStepBG(Double_t* dim, Double_t* par)
374{
375 // Limits need to be imposed or error states may occour.
376 //
377 // General background.
378 int npeaks = static_cast<int>(par[0] + 0.5);
379 double result = TGRSIFunctions::PolyBg(dim, &par[1], 2); // polynomial background. uses par[1->4]
380 for(int i = 0; i < npeaks; i++) { // par[0] is number of peaks
381 std::array<Double_t, 6> tmp_par;
382 tmp_par[0] = par[6 * i + 5]; // height of photopeak
383 tmp_par[1] = par[6 * i + 6]; // Peak Centroid of non skew gaus
384 tmp_par[2] = par[6 * i + 7]; // standard deviation of gaussian
385 tmp_par[3] = par[6 * i + 8]; //"skewedness" of the skewed gaussian
386 tmp_par[4] = par[6 * i + 9]; // relative height of skewed gaussian
387 tmp_par[5] = par[6 * i + 10]; // Size of step in background
388 result += TGRSIFunctions::StepFunction(dim, tmp_par.data());
389 }
390 return result;
391}
392
393Double_t TMultiPeak::SinglePeakBG(Double_t* dim, Double_t* par)
394{
395 // Limits need to be imposed or error states may occour.
396 //
397 // General background.
398
399 int npeaks = static_cast<int>(par[0] + 0.5);
400 double result = TGRSIFunctions::PolyBg(dim, &par[1], 2); // polynomial background. uses par[1->4]
401 for(int i = 0; i < npeaks; i++) { // par[0] is number of peaks
402 std::array<Double_t, 6> tmp_par;
403 tmp_par[0] = par[6 * i + 5]; // height of photopeak
404 tmp_par[1] = par[6 * i + 6]; // Peak Centroid of non skew gaus
405 tmp_par[2] = par[6 * i + 7]; // standard deviation of gaussian
406 tmp_par[3] = par[6 * i + 8]; //"skewedness" of the skewed gaussian
407 tmp_par[4] = par[6 * i + 9]; // relative height of skewed gaussian
408 tmp_par[5] = par[6 * i + 10]; // Size of step in background
409 result += TGRSIFunctions::StepFunction(dim, tmp_par.data());
410 }
411 result += TGRSIFunctions::PhotoPeak(dim, &par[npeaks * 6 + 5]);
412 return result;
413}
414
416{
417 if(idx < fPeakVec.size()) {
418 return fPeakVec.at(idx);
419 }
420 std::cout << "No matching peak at index " << idx << std::endl;
421
422 return nullptr;
423}
424
426{
427 size_t closest_idx = 0;
428 Double_t closest_so_far = 1000000.;
429 for(size_t i = 0; i < fPeakVec.size(); ++i) {
430 if(std::abs(energy - fPeakVec.at(i)->GetCentroid()) < closest_so_far) {
431 closest_so_far = std::abs(energy - fPeakVec.at(i)->GetCentroid());
432 closest_idx = i;
433 }
434 }
435
436 return GetPeak(closest_idx);
437}
438
440{
441 // Draws the individual TPeaks that make up the TMultiPeak. ROOT makes this a complicated process. The result on the
442 // histogram might have memory issues.
443 Double_t xlow = 0.;
444 Double_t xhigh = 0.;
445 GetRange(xlow, xhigh);
446 int npeaks = fPeakVec.size();
447 for(auto* peak : fPeakVec) {
448 // Should be good enough to draw between -2 and +2 fwhm
449 Double_t centroid = peak->GetCentroid();
450 Double_t range = 2. * peak->GetFWHM();
451
452 auto* sum = new TF1(Form("tmp%s", peak->GetName()), this, &TMultiPeak::SinglePeakBG, centroid - range, centroid + range,
453 fPeakVec.size() * 6 + 11, "TMultiPeak", "SinglePeakBG");
454
455 for(int j = 0; j < GetNpar(); ++j) {
456 sum->SetParameter(j, GetParameter(j));
457 }
458 for(int j = 0; j < 5; ++j) {
459 sum->SetParameter(npeaks * 6 + 5 + j, peak->GetParameter(j));
460 }
461
462 sum->SetNpx(1000);
463 sum->SetLineStyle(2);
464 sum->SetLineColor(kMagenta);
465
466 sum->SetRange(centroid - range, centroid + range);
467 sum->DrawCopy("SAME");
468 delete sum;
469 }
470}
Bool_t AddToGlobalList(Bool_t yes=kTRUE) override
Definition TGRSIFit.cxx:62
virtual void SetHist(TH1 *hist)
Definition TGRSIFit.h:53
void Clear(Option_t *opt="") override
Definition TGRSIFit.cxx:36
virtual void CopyParameters(TF1 *copy) const
Definition TGRSIFit.cxx:50
Bool_t IsInitialized() const
Definition TGRSIFit.h:67
void SetInitialized(Bool_t flag=true)
Definition TGRSIFit.h:68
void Copy(TObject &obj) const override
Definition TGRSIFit.cxx:20
virtual TH1 * GetHist() const
Definition TGRSIFit.h:57
TF1 * fBackground
Definition TMultiPeak.h:66
void InitNames()
TPeak * GetPeakClosestTo(Double_t energy)
static bool fLogLikelihoodFlag
!
Definition TMultiPeak.h:64
void Copy(TObject &obj) const override
Double_t MultiPhotoPeakBG(Double_t *dim, Double_t *par)
Double_t SinglePeakBG(Double_t *dim, Double_t *par)
Bool_t Fit(TH1 *fithist, Option_t *opt="")
TPeak * GetPeak(UInt_t idx)
std::vector< TPeak * > fPeakVec
Definition TMultiPeak.h:65
static bool GetLogLikelihoodFlag()
Definition TMultiPeak.h:54
void Print(Option_t *opt="") const override
bool InitParams(TH1 *fithist) override
void DrawPeaks()
void SortPeaks(Bool_t(*SortFunction)(const TPeak *, const TPeak *)=TPeak::CompareEnergy)
void Clear(Option_t *opt="") override
Double_t MultiStepBG(Double_t *dim, Double_t *par)
Definition TPeak.h:28
void SetNdf(Double_t Ndf)
Definition TPeak.h:92
void SetArea(Double_t area)
Definition TPeak.h:84
Double_t GetAreaErr() const
Definition TPeak.h:54
void SetAreaErr(Double_t areaErr)
Definition TPeak.h:85
void SetChi2(Double_t chi2)
Definition TPeak.h:91
Double_t GetArea() const
Definition TPeak.h:53
Double_t StepFunction(Double_t *dim, Double_t *par)
Double_t PhotoPeak(Double_t *dim, Double_t *par)
Double_t PolyBg(Double_t *x, Double_t *par, Int_t order)