mupp: add x-axis error bar support to all plot outputs.

Previously only y-errors were rendered. The IPC data file format now
includes x-errors per point (x, xPosErr, xNegErr, y, yPosErr, yNegErr,
...). Both the live canvas (PMuppCanvas) and the ROOT macro exports
(GUI and script path) propagate x-errors to TGraphAsymmErrors;
x-errors default to 0.0 when the parameter carries none.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-26 20:43:05 +02:00
parent 9d859950d2
commit 6a6de42308
4 changed files with 98 additions and 36 deletions
+29 -12
View File
@@ -1955,8 +1955,9 @@ void PmuppGui::createMacro()
fout << " gStyle->SetCanvasColor(TColor::GetColor(255,255,255)); // canvas bkg to white" << Qt::endl;
fout << Qt::endl;
fout << " Int_t nn=0, i=0;" << Qt::endl;
fout << " Double_t null[512];" << Qt::endl;
fout << " Double_t xx[512];" << Qt::endl;
fout << " Double_t xxPosErr[512];" << Qt::endl;
fout << " Double_t xxNegErr[512];" << Qt::endl;
fout << " Double_t yy[512];" << Qt::endl;
fout << " Double_t yyPosErr[512];" << Qt::endl;
fout << " Double_t yyNegErr[512];" << Qt::endl;
@@ -1966,7 +1967,7 @@ void PmuppGui::createMacro()
QString collName("");
QString xLabel(""), yLabel("");
char gLabel[128];
QVector<double> xx, yy, yyPosErr, yyNegErr;
QVector<double> xx, xxPosErr, xxNegErr, yy, yyPosErr, yyNegErr;
double xMin=1.0e10, xMax=-1.0e10, yMin=1.0e10, yMax=-1.0e10;
bool ok;
for (int i=0; i<fXY.size(); i++) {
@@ -1976,13 +1977,19 @@ void PmuppGui::createMacro()
xLabel = fXY[i].getXlabel();
pos = xLabel.indexOf(" (-");
xLabel.remove(pos, xLabel.length()-pos);
// get x-vector
// get x-vector and x-errors (x-errors are optional; use 0.0 if not available)
xx = getValues(collName, xLabel, ok);
if (!ok) {
QString msg = QString("Couldn't get x-axis data from '%1', coll: '%2'").arg(xLabel).arg(collName);
QMessageBox::critical(this, "ERROR", msg);
return;
}
xxPosErr = getPosErr(collName, xLabel, ok);
if (!ok)
xxPosErr = QVector<double>(xx.size(), 0.0);
xxNegErr = getNegErr(collName, xLabel, ok);
if (!ok)
xxNegErr = QVector<double>(xx.size(), 0.0);
getMinMax(xx, xMin, xMax);
// a couple of x-vector specifics
if ((xLabel == "dataT") || (xLabel == "dataE"))
@@ -2019,14 +2026,18 @@ void PmuppGui::createMacro()
snprintf(gLabel, sizeof(gLabel), "g_%d_%d", i, j);
fout << " nn = " << xx.size() << ";" << Qt::endl;
fout << Qt::endl;
fout << " // null value vector" << Qt::endl;
for (int k=0; k<xx.size(); k++) {
fout << " null[" << k << "]=0.0;" << Qt::endl;
}
fout << " // xx" << Qt::endl;
for (int k=0; k<xx.size(); k++) {
fout << " xx[" << k << "]=" << xx[k] << ";" << Qt::endl;
}
fout << " // xxPosErr" << Qt::endl;
for (int k=0; k<xx.size(); k++) {
fout << " xxPosErr[" << k << "]=" << xxPosErr[k] << ";" << Qt::endl;
}
fout << " // xxNegErr" << Qt::endl;
for (int k=0; k<xx.size(); k++) {
fout << " xxNegErr[" << k << "]=" << fabs(xxNegErr[k]) << ";" << Qt::endl;
}
fout << " // yy" << Qt::endl;
for (int k=0; k<xx.size(); k++) {
fout << " yy[" << k << "]=" << yy[k] << ";" << Qt::endl;
@@ -2040,7 +2051,7 @@ void PmuppGui::createMacro()
fout << " yyNegErr[" << k << "]=" << fabs(yyNegErr[k]) << ";" << Qt::endl;
}
fout << Qt::endl;
fout << " TGraphAsymmErrors *" << gLabel << " = new TGraphAsymmErrors(nn, xx, yy, null, null, yyNegErr, yyPosErr);" << Qt::endl;
fout << " TGraphAsymmErrors *" << gLabel << " = new TGraphAsymmErrors(nn, xx, yy, xxNegErr, xxPosErr, yyNegErr, yyPosErr);" << Qt::endl;
}
}
fout << Qt::endl;
@@ -2138,7 +2149,7 @@ void PmuppGui::plot()
int collTag = -1, pos;
QString collName("");
QString xLabel(""), yLabel("");
QVector<double> xx, yy, yyPosErr, yyNegErr;
QVector<double> xx, xxPosErr, xxNegErr, yy, yyPosErr, yyNegErr;
QVector< QVector<double> > yyy, yyyPosErr, yyyNegErr;
bool ok;
@@ -2165,13 +2176,19 @@ void PmuppGui::plot()
xLabel = fXY[i].getXlabel();
pos = xLabel.indexOf(" (-");
xLabel.remove(pos, xLabel.length()-pos);
// get x-vector
// get x-vector and x-errors (x-errors are optional; use 0.0 if not available)
xx = getValues(collName, xLabel, ok);
if (!ok) {
QString msg = QString("Couldn't get x-axis data from '%1', coll: '%2'").arg(xLabel).arg(collName);
QMessageBox::critical(this, "ERROR", msg);
return;
}
xxPosErr = getPosErr(collName, xLabel, ok);
if (!ok)
xxPosErr = QVector<double>(xx.size(), 0.0);
xxNegErr = getNegErr(collName, xLabel, ok);
if (!ok)
xxNegErr = QVector<double>(xx.size(), 0.0);
xLabel = substituteDefaultLabels(xLabel);
fout << "xLabel: " << xLabel << ", ";
@@ -2253,9 +2270,9 @@ void PmuppGui::plot()
}
}
// data
// data: x, xPosErr, xNegErr, y1, y1PosErr, y1NegErr, ...
for (int j=0; j<xx.size(); j++) {
fout << xx[j] << ", ";
fout << xx[j] << ", " << xxPosErr[j] << ", " << xxNegErr[j] << ", ";
for (int k=0; k<yyy.size()-1; k++) {
fout << yyy[k][j] << ", " << yyyPosErr[k][j] << ", " << yyyNegErr[k][j] << ", ";
}
+25 -7
View File
@@ -784,15 +784,16 @@ int PmuppScript::macro(const QString str, const QString plotFln)
fout << " gStyle->SetCanvasColor(TColor::GetColor(255,255,255)); // canvas bkg to white" << Qt::endl;
fout << Qt::endl;
fout << " Int_t nn=0, i=0;" << Qt::endl;
fout << " Double_t null[512];" << Qt::endl;
fout << " Double_t xx[512];" << Qt::endl;
fout << " Double_t xxPosErr[512];" << Qt::endl;
fout << " Double_t xxNegErr[512];" << Qt::endl;
fout << " Double_t yy[512];" << Qt::endl;
fout << " Double_t yyPosErr[512];" << Qt::endl;
fout << " Double_t yyNegErr[512];" << Qt::endl;
fout << Qt::endl;
// write data
QVector<double> xx, yy, yyPosErr, yyNegErr;
QVector<double> xx, xxPosErr, xxNegErr, yy, yyPosErr, yyNegErr;
QString collName;
int count=0;
double x_min=0.0, x_max=0.0, x_min_new=0.0, x_max_new=0.0, y_min=0.0, y_max=0.0, y_min_new=0.0, y_max_new=0.0;
@@ -812,6 +813,19 @@ int PmuppScript::macro(const QString str, const QString plotFln)
std::vector<double> xVal = fVarHandler[idx].getValues();
QVector<double> qvec(xVal.begin(), xVal.end());
xx = qvec;
// use variable errors for x
std::vector<double> xErr = fVarHandler[idx].getErrors();
QVector<double> qvecE(xErr.begin(), xErr.end());
xxPosErr = qvecE;
xxNegErr = qvecE;
} else {
// x-errors from parameter data handler (0.0 if not available)
xxPosErr = fParamDataHandler->GetPosErr(collName, fPlotInfo[i].xLabel);
if (xxPosErr.size() == 0)
xxPosErr = QVector<double>(xx.size(), 0.0);
xxNegErr = fParamDataHandler->GetNegErr(collName, fPlotInfo[i].xLabel);
if (xxNegErr.size() == 0)
xxNegErr = QVector<double>(xx.size(), 0.0);
}
// get x-axis min/max
minMax(xx, x_min, x_max);
@@ -858,14 +872,18 @@ int PmuppScript::macro(const QString str, const QString plotFln)
}
fout << " // " << ++count << ". data set" << Qt::endl;
fout << " nn = " << xx.size() << ";" << Qt::endl;
fout << " // null-values" << Qt::endl;
for (int k=0; k<xx.size(); k++) {
fout << " null[" << k << "]=0.0;" << Qt::endl;
}
fout << " // x-values" << Qt::endl;
for (int k=0; k<xx.size(); k++) {
fout << " xx[" << k << "]=" << xx[k] << ";" << Qt::endl;
}
fout << " // xxPosErr-values" << Qt::endl;
for (int k=0; k<xxPosErr.size(); k++) {
fout << " xxPosErr[" << k << "]=" << fabs(xxPosErr[k]) << ";" << Qt::endl;
}
fout << " // xxNegErr-values" << Qt::endl;
for (int k=0; k<xxNegErr.size(); k++) {
fout << " xxNegErr[" << k << "]=" << fabs(xxNegErr[k]) << ";" << Qt::endl;
}
fout << " // y-values" << Qt::endl;
for (int k=0; k<yy.size(); k++) {
dval = yy[k];
@@ -891,7 +909,7 @@ int PmuppScript::macro(const QString str, const QString plotFln)
fout << " yyPosErr[" << k << "]=" << dval << ";" << Qt::endl;
}
fout << Qt::endl;
fout << " TGraphAsymmErrors *g_" << i << "_" << j << " = new TGraphAsymmErrors(nn, xx, yy, null, null, yyNegErr, yyPosErr);" << Qt::endl;
fout << " TGraphAsymmErrors *g_" << i << "_" << j << " = new TGraphAsymmErrors(nn, xx, yy, xxNegErr, xxPosErr, yyNegErr, yyPosErr);" << Qt::endl;
fout << Qt::endl;
}
}
+42 -17
View File
@@ -26,7 +26,7 @@
- Tab-separated values
- First line: "PLOT_DATA" header
- Collection blocks with name, x-label, y-labels, and data points
- Each data point: x-value, y-value, positive error, negative error
- Each data point: x-value, x-pos-error, x-neg-error, y-value, y-pos-error, y-neg-error, ...
***************************************************************************/
@@ -421,7 +421,7 @@ int PMuppCanvas::ReadPlotData(const Char_t *fln)
fin.close();
return -4;
}
if (tok->GetEntries() != (noOfDataTokens-1)*3+1) {
if (tok->GetEntries() != noOfDataTokens*3) {
std::cerr << "**ERROR** number of token in data line (" << tok->GetEntries() << ") is inconsistent with no of labels (" << noOfDataTokens << ")." << std::endl;
fin.close();
return -4;
@@ -431,7 +431,7 @@ int PMuppCanvas::ReadPlotData(const Char_t *fln)
data.yValue.resize(noOfDataTokens-1);
// raw data point
// x-value
// x-value (token 0)
ostr = dynamic_cast<TObjString*>(tok->At(0));
str = ostr->GetString();
if (!str.IsFloat()) {
@@ -441,9 +441,29 @@ int PMuppCanvas::ReadPlotData(const Char_t *fln)
}
data.xValue.push_back(str.Atof());
// y-value(s)
// pos x error-value (token 1)
ostr = dynamic_cast<TObjString*>(tok->At(1));
str = ostr->GetString();
if (!str.IsFloat()) {
std::cerr << "**ERROR** token found in data line is not a number (" << str << ")." << std::endl;
fin.close();
return -5;
}
data.xErrPos.push_back(str.Atof());
// neg x error-value (token 2)
ostr = dynamic_cast<TObjString*>(tok->At(2));
str = ostr->GetString();
if (!str.IsFloat()) {
std::cerr << "**ERROR** token found in data line is not a number (" << str << ")." << std::endl;
fin.close();
return -5;
}
data.xErrNeg.push_back(str.Atof());
// y-value(s): starting at token 3, each group of 3 = y, yposErr, ynegErr
Int_t idx=0;
for (Int_t i=1; i<tok->GetEntries(); i+=3) {
for (Int_t i=3; i<tok->GetEntries(); i+=3) {
// y-value
ostr = dynamic_cast<TObjString*>(tok->At(i));
str = ostr->GetString();
@@ -525,6 +545,8 @@ void PMuppCanvas::InitDataCollection(PDataCollection &coll)
coll.xLabel = TString("");
coll.yLabel.clear();
coll.xValue.clear();
coll.xErrPos.clear();
coll.xErrNeg.clear();
coll.yValue.clear();
}
@@ -553,7 +575,8 @@ void PMuppCanvas::UpdateGraphs()
gg = new TGraphAsymmErrors(fPlotData[i].xValue.size());
for (UInt_t k=0; k<fPlotData[i].yValue[j].size(); k++) {
gg->SetPoint(k, fPlotData[i].xValue[k], fPlotData[i].yValue[j][k].y);
gg->SetPointError(k, 0.0, 0.0, fabs(fPlotData[i].yValue[j][k].eYneg), fPlotData[i].yValue[j][k].eYpos);
gg->SetPointError(k, fabs(fPlotData[i].xErrNeg[k]), fPlotData[i].xErrPos[k],
fabs(fPlotData[i].yValue[j][k].eYneg), fPlotData[i].yValue[j][k].eYpos);
// set marker style and size
if (idxS < fMarkerStyleList.size()) {
gg->SetMarkerStyle(fMarkerStyleList[idxS]);
@@ -666,12 +689,14 @@ void PMuppCanvas::ExportData()
std::ofstream fout(fi.fFilename, std::ios_base::out);
// write header
for (int i=0; i<fPlotData.size(); i++) {
fout << fPlotData[i].xLabel.Strip(TString::kLeading).Data() << ", ";
TString xl = fPlotData[i].xLabel.Strip(TString::kLeading);
fout << xl.Data() << ", " << xl.Data() << "ErrPos, " << xl.Data() << "ErrNeg, ";
for (int j=0; j<fPlotData[i].yLabel.size(); j++) {
if ((i == fPlotData.size()-1) && (j == fPlotData[i].yLabel.size()-1))
fout << fPlotData[i].yLabel[j].Strip(TString::kLeading).Data() << ", " << fPlotData[i].yLabel[j].Strip(TString::kLeading).Data() << "ErrPos, " << fPlotData[i].yLabel[j].Strip(TString::kLeading).Data() << "ErrNeg";
TString yl = fPlotData[i].yLabel[j].Strip(TString::kLeading);
if ((i == (int)fPlotData.size()-1) && (j == (int)fPlotData[i].yLabel.size()-1))
fout << yl.Data() << ", " << yl.Data() << "ErrPos, " << yl.Data() << "ErrNeg";
else
fout << fPlotData[i].yLabel[j].Strip(TString::kLeading).Data() << ", " << fPlotData[i].yLabel[j].Strip(TString::kLeading).Data() << "ErrPos, " << fPlotData[i].yLabel[j].Strip(TString::kLeading).Data() << "ErrNeg, ";
fout << yl.Data() << ", " << yl.Data() << "ErrPos, " << yl.Data() << "ErrNeg, ";
}
}
fout << std::endl;
@@ -679,21 +704,21 @@ void PMuppCanvas::ExportData()
// search the longest data set
Int_t maxLength=0;
for (int i=0; i<fPlotData.size(); i++) {
if (maxLength < fPlotData[i].xValue.size())
if (maxLength < (Int_t)fPlotData[i].xValue.size())
maxLength = fPlotData[i].xValue.size();
}
// write data
for (int i=0; i<maxLength; i++) { // maximal data set length
for (int j=0; j<fPlotData.size(); j++) { // number of x-data sets
// write x-value
if (i < fPlotData[j].xValue.size()) // make sure that the entry exists
fout << fPlotData[j].xValue[i] << ", ";
// write x-value and x-errors
if (i < (int)fPlotData[j].xValue.size())
fout << fPlotData[j].xValue[i] << ", " << fPlotData[j].xErrPos[i] << ", " << fPlotData[j].xErrNeg[i] << ", ";
else
fout << " , ";
fout << " , , , ";
// write y-value and y-value error
for (int k=0; k<fPlotData[j].yValue.size(); k++) { // number of y-data sets
if ((j == fPlotData.size()-1) && (k == fPlotData[j].yValue.size()-1))
if (i < fPlotData[j].yValue[k].size())
if ((j == (int)fPlotData.size()-1) && (k == (int)fPlotData[j].yValue.size()-1))
if (i < (int)fPlotData[j].yValue[k].size())
fout << fPlotData[j].yValue[k][i].y << ", " << fPlotData[j].yValue[k][i].eYpos << ", " << fPlotData[j].yValue[k][i].eYneg;
else
fout << ", , , ";
@@ -93,6 +93,8 @@ struct PDataCollection {
TString xLabel; ///< label for the x-axis
PStringVector yLabel; ///< labels for each y-dataset
PDoubleVector xValue; ///< x-values (common to all y-datasets)
PDoubleVector xErrPos; ///< positive x-errors (upward)
PDoubleVector xErrNeg; ///< negative x-errors (downward)
std::vector< std::vector<PDataPoint> > yValue; ///< y-values with errors for each dataset
};