TMultiGraph with two Y-axis

Dear All,

I’m trying to plot a total of 8 TGraphs in a multigraph with two Y-axes, and that is where the problems started.
I managed to draw the two Y-axes, but the axes range of the left Y-axis is wrong (should be 4 - 10], the labels of the right Y-axis are bold-face, but I want them normal like the other axes. And I would like to have a legend, some dotted lines with labels over the plot, but they are not drawn anymore. While in the version where I used only one Y-axis I had all of those. Also, I don’t find the data from mg1 in the plot.

I hope you can figure it out from this code snippet, because the minimum working example might be not so minimum…
I tried making this based on other examples, and I feel like I’m almost there but not quite :slight_smile:

TGraph* myHist = RunningAverageNormalized(myHistTemp, pWindow, normalize);

    if (count % 2 == 0) # all of this is inside a long for loop where it loops over the 8 data sets
      mg->Add(myHist, "l");
    else
      mg1->Add(myHist, "l");

    myHist->SetTitle(pLegends.operator[](count));  
    myHist->SetLineColor(cColors.operator[](count));
    myHist->SetLineWidth(cLineThickness);

    ++count;
  }

  TCanvas *cMult = new TCanvas();
  cMult->SetBottomMargin(0.15);
  gStyle->SetTitleFontSize(0.025);

  TPad *cPad = new TPad("pad", "", 0, 0, 1, 1);
  cPad->Draw();
  cPad->cd();
  cPad->SetBottomMargin(0.15);

  mg->GetXaxis()->SetLabelOffset(0.025);
  mg->GetXaxis()->SetNdivisions(505);
  mg->GetXaxis()->SetTitleOffset(1.9);
  mg->GetXaxis()->SetLimits(pStart.Convert(), pStop.Convert());
  mg->GetXaxis()->SetTimeDisplay(1);
  mg->GetXaxis()->SetTimeFormat("#splitline{%d/%m/%y}{   %H:%M}");
  mg->GetXaxis()->SetTimeOffset(0,"local");
  mg->SetMinimum(4);
  mg->SetMaximum(10);
  mg->GetYaxis()->SetNdivisions(505);
  mg->GetYaxis()->SetLabelOffset(0.01);
  mg->GetYaxis()->SetTitleOffset(1.2);

  mg->Draw("a");

  cMult->cd();
  TPad *cOverlay = new TPad("overlay", "", 0, 0, 1, 1); # this is for the second axis
  cOverlay->Draw();
  cOverlay->cd();
  cOverlay->SetBottomMargin(0.15);

  mg1->GetXaxis()->SetLabelOffset(0.025);
  mg1->GetXaxis()->SetNdivisions(505);
  mg1->GetXaxis()->SetTitleOffset(1.9);
  mg1->GetXaxis()->SetLimits(pStart.Convert(), pStop.Convert());
  mg1->GetXaxis()->SetTimeDisplay(1);
  mg1->GetXaxis()->SetTimeFormat("#splitline{%d/%m/%y}{   %H:%M}");
  mg1->GetXaxis()->SetTimeOffset(0,"local");
  mg1->SetMinimum(yLow1);
  mg1->SetMaximum(yHigh1);
  mg1->GetYaxis()->SetNdivisions(505);
  mg1->GetYaxis()->SetLabelOffset(0.01);
  mg1->GetYaxis()->SetTitleOffset(1.2);
  // mg1->GetYaxis()->SetFontSize(0.025);

  mg1->Draw("a");

  Double_t cXMin = cPad->GetUxmin();
  Double_t cXMax = cPad->GetUxmax();

  TGaxis *cAxis = new TGaxis(cXMax, yLow1, cXMax, yHigh1, yLow1, yHigh1, 505, "+L");
  cAxis->SetLineColor(kRed);
  cAxis->SetLabelColor(kRed);
  cAxis->SetTitle("Resistance [#Omega]");
  cAxis->SetTitleOffset(1.2);
  cAxis->Draw();

  cMult->cd();

  // Make a nice legend for whatever number of entries
  double cLegendSizeY = cColors.size()/nColumns;
  double cLegendSizeX = nColumns;
  if (doubleLegendX)
    cLegendSizeX = nColumns*1.7;
  if (doubleLegendY)
    cLegendSizeY = nColumns;

  double lX1 = 0.89, lX0 = lX1-0.085*cLegendSizeX, lY1 = 0.89, lY0 = lY1-0.03*cLegendSizeY;

  TLegend* l0 = NULL;
  l0 = cMult->BuildLegend(lX0, lY0, lX1, lY1, "", "l");
  Float_t symbolMargin = 0.02*cLegendSizeX / (lX1 - lX0);
  l0->SetMargin(symbolMargin);
  l0->SetNColumns(nColumns);

  // Draw the lines with interesting events
  TLine* myLine = new TLine(0.4, 0.4, 1.4, 1.4);
  myLine->SetLineWidth(cLineThickness);
  myLine->SetLineStyle(2);
  myLine->SetLineColor(12);
  int countL = 0;
  for (auto it : pLines)
  {
    if (it > 0 && it > pStart.Convert() && it < pStop.Convert())
    {
      myLine->DrawLine(it, yLow, it, yHigh);
      // auto *myText = new TText(it, (TMath::Abs(yHigh)-TMath::Abs(yLow))/5.+yLow, pLineTitles.operator[](countL));
      auto *myText = new TText(it, yHigh, pLineTitles.operator[](countL));
      myText->SetTextAngle(45);
      myText->SetTextAlign(11);
      myText->SetTextSize(0.011);
      myText->SetTextColor(12);
      myText->Draw();
    }
    ++countL;
  }

  l0->Draw();
  cMult->Update();

This is what the plot looks like now:

Thanks in advance for your help :slight_smile:

_ROOT Version: 6.26/04
_Platform: Linux Manjaro
_Compiler: ROOT


Can you provide an example we can run?
I suppose mg is your TMultigraph ? but it is not defined in what you sent.

Dear @couet ,

Thanks for your response.

Allright, it’s quite big, but I tried to remove the things that don’t matter. Here is a cpp script, and you need to download two data files in order to run it:

atlasData.cpp (11.8 KB)

https://cernbox.cern.ch/index.php/s/rw5TvOTe13lP03d
https://cernbox.cern.ch/index.php/s/r4GXpBK9xJkdkso

To make the code run faster, I would eventually just like to reopen the created TTree that contains the data (see line 80 where I save the tree) but when I tried making a code that opens the file and retreives the tree from it I did not manage to get that far yet :slight_smile:

Cheers,
Nikkie

The two multigraphs overwrite each other.
You need two canvas. Here is your macro modified:

#include <iostream>
#include <map>
#include <vector>
#include <fstream>

#include "TCanvas.h"
#include "TPad.h"
#include "TGraph.h"
#include "TMultiGraph.h"
#include "TTree.h"
#include "TList.h"
#include "TDatime.h"
#include "TAxis.h"
#include "TGaxis.h"
#include "TLegend.h"
#include "TStyle.h"
#include "TMath.h"
#include "TLine.h"
#include "TText.h"

// Colors
const int cRed = 632;
const int cOrange = 800;
const int cBlue = 860;
const std::vector<int> cColorsTwelve {cRed-9, cOrange, cBlue+6, cRed-4, cOrange-3, cBlue+5, cRed+1, cOrange+8, cBlue-5, cRed+3, cOrange+4, cBlue-6};
const std::vector<int> cColorsFifteen {cRed-9, cOrange, cBlue+6, cRed-4, cOrange-3, cBlue+5, cRed+1, cOrange+8, cBlue-5, cRed+3, cOrange+4, cBlue-6, cRed+4, cOrange+3, cBlue+3};
const vector<int> cColorsEight {cBlue+6, cOrange-4, cBlue+5, cOrange+6, cBlue-5, cRed-7, cBlue-6, cRed+1};
const vector<int> cColorsFour {cRed-7, cOrange+6, cOrange-4, cBlue+6};
const std::vector<int> cColorsTwo {cOrange-4, cBlue+6};

const int cLineThickness = 2;

bool checkFile (const char* fileName)
{
    bool fileExists = false;
    std::ifstream file(fileName);
    if(file.is_open())
      fileExists = true;

    return fileExists;
}

TTree* makeTree (const char* pFileName, const char* pSaveFileName)
{
  TTree* myTree = new TTree();
  myTree->ReadFile(Form("%s", pFileName), "", ',');
  myTree->SetName("t1");

  TDatime time;
  Int_t dateTime;
  char Date[100];

  TBranch *intTime = myTree->Branch("dateTime", &dateTime);
  myTree->SetBranchAddress("Timestamp (LOCAL_TIME)", &Date[0]);

  int tempCount = 0;
  for (Long64_t i=0; i<myTree->GetEntries(); i++)
  //for (Long64_t i=0; i<200; i++)
  {
    myTree->GetEntry(i);

    // Get the date and make it into a string, cut into pieces
    std::string sDate(Date);

    time.Set(std::stoi(sDate.substr(0,4)),
              std::stoi(sDate.substr(5,2)),
              std::stoi(sDate.substr(8,2)),
              std::stoi(sDate.substr(11,2)),
              std::stoi(sDate.substr(14,2)),
              std::stoi(sDate.substr(17,2)));

    dateTime = time.Convert();
    intTime->Fill();
  }

  myTree->ResetBranchAddresses(); // Disconnect from local variables
  // myTree->Print();

  // Save the TTree to a file
  myTree->SaveAs(pSaveFileName);

  return myTree;
}

TGraph* RunningAverageNormalized(TGraph* inputGraph, const int pWindow, const int pNorm)
{
  TGraph* outputGraphtemp = new TGraph();
  TGraph* outputGraph = new TGraph();

  if (pWindow <= 0 && pNorm <= 0)
    return inputGraph;

  for (int i=0; i<inputGraph->GetN()-pWindow; i++)
  {
    double averageX = 0;
    double averageY = 0;
    for (int j=0; j<pWindow; j++)
    {
      averageX += inputGraph->GetPointX(i+j);
      averageY += inputGraph->GetPointY(i+j);
    }
    outputGraphtemp->SetPoint(i,averageX/pWindow, averageY/pWindow);
  }

  if (pNorm > 0)
  {
    double cNorm = 0.0;
    for(int i=0; i<25; i++)
      cNorm += outputGraphtemp->GetPointY(i);
    cNorm = cNorm/25;

    for(int i=0; i<outputGraphtemp->GetN(); i++)
      outputGraph->SetPoint(i, outputGraphtemp->GetPointX(i), outputGraphtemp->GetPointY(i)-cNorm);
      // outputGraph->SetPoint(i, outputGraphtemp->GetPointX(i), outputGraphtemp->GetPointY(i)-cNorm+pNorm);
  }
  else
    outputGraph = outputGraphtemp;

  return outputGraph;
}

void doubleYAxis (double unit, int normalize, int nColumns, bool temperature,
      std::vector<TTree*> pTree,
      std::vector<const char*> pSensorList,
      std::vector<const char*> pLegends,
      const char* pTitles,
      const char* pSaveFileName,
      const vector<int> cColors,
      TDatime pStart, TDatime pStop,
      const double yLow, const double yHigh, const double yLow1, const double yHigh1, const double pWindow,
      std::vector<int> pLines, std::vector<const char*> pLineTitles, bool IVcurve = false, bool bbGraphs = false, bool doubleLegendX = false, bool doubleLegendY = false)
{
  // Make a nice multigraph with the correct titles
  TMultiGraph *mg = new TMultiGraph();
  TMultiGraph *mg1 = new TMultiGraph();

  mg->SetTitle(pTitles);
  mg1->SetTitle(pTitles);

  int count = 0;
  TCanvas* c1 = new TCanvas();
  for ( auto it : pSensorList )
  {
    c1->Clear();
    if (it == std::string("NULL"))
    {
        std::cout <<"\x1b[31mSensor not found:" << it << "\e[0m" << std::endl;
        Double_t x[1] = {double(pStart.Convert())};
        Double_t y[1] = {-99.00};
        TGraph *myNullHist = new TGraph(1, x, y);
        mg->Add(myNullHist, ""); myNullHist->SetTitle(" "); myNullHist->SetLineColor(0);
        ++count;
        continue;
    }

    TTree* myTree = NULL;
    for ( auto jt : pTree )
      if (jt->GetBranch(it) != 0) myTree = jt;

    // Check if sensor exists in any tree
    if (myTree == NULL)
    {
      std::cout <<"\x1b[31mSensor not found in tree:" << it << "\e[0m" << std::endl;
      Double_t x[1] = {double(pStart.Convert())};
      Double_t y[1] = {-99.00};
      TGraph *myNullHist = new TGraph(1, x, y);
      mg->Add(myNullHist, ""); myNullHist->SetTitle(" "); myNullHist->SetLineColor(0);
      ++count;
      continue;
    }

    // Draw the data for the selected sensor
    if (temperature)
      myTree->Draw(Form("%s*%f:dateTime", it, unit), Form("dateTime>=%d&&dateTime<=%d&&%s>0", pStart.Convert()-3600, pStop.Convert()+3600, it));

    // Access the graphs
    TGraph *myHistTemp = (TGraph*)gPad->GetPrimitive("Graph")->Clone();
    TGraph* myHist = RunningAverageNormalized(myHistTemp, pWindow, normalize);

    if (count % 2 == 0)
      mg1->Add(myHist, "l");
    else
      mg->Add(myHist, "l");

    myHist->SetTitle(pLegends.operator[](count));
    myHist->SetLineColor(cColors.operator[](count));
    myHist->SetLineWidth(cLineThickness);

    ++count;
  }
  delete c1;

  auto cMult = new TCanvas();
  cMult->SetBottomMargin(0.15);
  gStyle->SetTitleFontSize(0.025);

  TPad *cPad = new TPad("pad", "", 0, 0, 1, 1);
  cPad->Draw();
  cPad->cd();
  cPad->SetBottomMargin(0.15);
  gStyle->SetTitleFontSize(0.025);

  mg->GetXaxis()->SetLabelOffset(0.025);
  mg->GetXaxis()->SetNdivisions(505);
  mg->GetXaxis()->SetTitleOffset(1.9);
  mg->GetXaxis()->SetLimits(pStart.Convert(), pStop.Convert());
  mg->GetXaxis()->SetTimeDisplay(1);
  mg->GetXaxis()->SetTimeFormat("#splitline{%d/%m/%y}{   %H:%M}");
  mg->GetXaxis()->SetTimeOffset(0,"local");
  mg->SetMinimum(4);
  mg->SetMaximum(10);
  mg->GetYaxis()->SetNdivisions(505);
  mg->GetYaxis()->SetLabelOffset(0.01);
  mg->GetYaxis()->SetTitleOffset(1.2);

  mg->Draw("a");

  auto cMult1 = new TCanvas();
  TPad *cOverlay = new TPad("overlay", "", 0, 0, 1, 1);
  cOverlay->Draw();
  cOverlay->cd();
  cOverlay->SetBottomMargin(0.15);
  gStyle->SetTitleFontSize(0.025);

  mg1->GetXaxis()->SetLabelOffset(0.025);
  mg1->GetXaxis()->SetNdivisions(505);
  mg1->GetXaxis()->SetTitleOffset(1.9);
  mg1->GetXaxis()->SetLimits(pStart.Convert(), pStop.Convert());
  mg1->GetXaxis()->SetTimeDisplay(1);
  mg1->GetXaxis()->SetTimeFormat("#splitline{%d/%m/%y}{   %H:%M}");
  mg1->GetXaxis()->SetTimeOffset(0,"local");
  mg1->SetMinimum(yLow1);
  mg1->SetMaximum(yHigh1);
  mg1->GetYaxis()->SetNdivisions(505);
  mg1->GetYaxis()->SetLabelOffset(0.01);
  mg1->GetYaxis()->SetTitleOffset(1.2);
  // mg1->GetYaxis()->SetFontSize(0.025);

  mg1->Draw("a");

  Double_t cXMin = cPad->GetUxmin();
  Double_t cXMax = cPad->GetUxmax();

  TGaxis *cAxis = new TGaxis(cXMax, yLow1, cXMax, yHigh1, yLow1, yHigh1, 505, "+L");
  cAxis->SetLineColor(kRed);
  cAxis->SetLabelColor(kRed);
  cAxis->SetLabelFont(42);
  cAxis->SetTitle("Resistance [#Omega]");
  cAxis->SetTitleOffset(1.2);
  cAxis->Draw();

  // Make a nice legend for whatever number of entries
  double cLegendSizeY = cColors.size()/nColumns;
  double cLegendSizeX = nColumns;
  if (doubleLegendX)
    cLegendSizeX = nColumns*1.7;
  if (doubleLegendY)
    cLegendSizeY = nColumns;

  double lX1 = 0.89, lX0 = lX1-0.085*cLegendSizeX, lY1 = 0.89, lY0 = lY1-0.03*cLegendSizeY;

  TLegend* l0 = NULL;
  l0 = cMult->BuildLegend(lX0, lY0, lX1, lY1, "", "l");
  Float_t symbolMargin = 0.02*cLegendSizeX / (lX1 - lX0);
  l0->SetMargin(symbolMargin);
  l0->SetNColumns(nColumns);

  // Draw the lines with interesting events
  TLine* myLine = new TLine(0.4, 0.4, 1.4, 1.4);
  myLine->SetLineWidth(cLineThickness);
  myLine->SetLineStyle(2);
  myLine->SetLineColor(12);
  int countL = 0;
  for (auto it : pLines)
  {
    if (it > 0 && it > pStart.Convert() && it < pStop.Convert())
    {
      myLine->DrawLine(it, yLow, it, yHigh);
      // auto *myText = new TText(it, (TMath::Abs(yHigh)-TMath::Abs(yLow))/5.+yLow, pLineTitles.operator[](countL));
      auto *myText = new TText(it, yHigh, pLineTitles.operator[](countL));
      myText->SetTextAngle(45);
      myText->SetTextAlign(11);
      myText->SetTextSize(0.011);
      myText->SetTextColor(12);
      myText->Draw();
    }
    ++countL;
  }

  l0->Draw();
//   cMult->Update();
//
//   cMult->SaveAs(Form("%s_from_%d-%d-%d_%dh%dm%ds-to_%d-%d-%d_%dh%dm%ds.pdf", pSaveFileName,
//         pStart.GetYear(), pStart.GetMonth(), pStart.GetDay(), pStart.GetHour(), pStart.GetMinute(), pStart.GetSecond(),
//         pStop.GetYear(), pStop.GetMonth(), pStop.GetDay(), pStop.GetHour(), pStop.GetMinute(), pStop.GetSecond()));
}

void makePlots (TDatime plotsStart, TDatime plotStop, std::vector<int> lineTimes, std::vector<const char*> lineTitles, bool unZoom, std::vector<TTree*> temperatureTreeVec)
{
  bool temperatureYN = true;
  double unit = 1.0;
  int normalize = -99;
  double cWindow = 25;
  int nColumns = 3;
  double cYLow = 0;
  double cYHigh = 300;

  // ECTA // ////////////// Cold Mass - Coil and Keystone 1 //////////////
  const std::vector<const char*> cECTA1 {"1TE9118A", "1TE9119A",
                                         "1TE9118B", "1TE9119B",
                                         "1TE9128A", "1TE9129A",
                                         "1TE9128B", "1TE9129B"};
  const char* cECTA1Titles = "ECTA Cold Mass - Coil and Keyston 1 Temperature;Time [s];Temperature [K]";
  const char* cECTA1File = "./ECTA_CM1_Temperature";
  cYLow = 4; cYHigh = 10; nColumns = 2; cWindow = 25;
  doubleYAxis(unit, normalize, nColumns, temperatureYN, temperatureTreeVec, cECTA1, cECTA1, cECTA1Titles, cECTA1File, cColorsEight, plotsStart, plotStop, cYLow, cYHigh, 0, 50, cWindow, lineTimes, lineTitles, false);
}

void atlasData()
{

  // TIMES AND  DATES
  // Nominal current commissioning day 1
  TDatime cNCCDay1Start(2022, 4, 11, 8, 30, 0);   // Start NCC Day 1
  TDatime cNCCDay1Stop (2022, 4, 11, 17, 30, 0); // Day 1 Finisched
  TDatime cNcc1_1(2022, 4, 11, 9, 19, 38);
  int ncc1_1 = cNcc1_1.Convert();
  TDatime cNcc1_2(2022, 4, 11, 9, 31, 55);
  int ncc1_2 = cNcc1_2.Convert();
  TDatime cNcc1_3(2022, 4, 11, 9, 39, 5);
  int ncc1_3 = cNcc1_3.Convert();
  TDatime cNcc1_4(2022, 4, 11, 10, 7, 22);
  int ncc1_4 = cNcc1_4.Convert();
  TDatime cNcc1_5(2022, 4, 11, 10, 14, 16);
  int ncc1_5 = cNcc1_5.Convert();
  TDatime cNcc1_6(2022, 4, 11, 10, 30, 10);
  int ncc1_6 = cNcc1_6.Convert();
  TDatime cNcc1_7(2022, 4, 11, 10, 59, 44);
  int ncc1_7 = cNcc1_7.Convert();
  TDatime cNcc1_8(2022, 4, 11, 11, 3, 45);
  int ncc1_8 = cNcc1_8.Convert();
  TDatime cNcc1_9(2022, 4, 11, 11, 51, 00);
  int ncc1_9 = cNcc1_9.Convert();
  TDatime cNcc1_10(2022, 4, 11, 15, 23, 30);
  int ncc1_10 = cNcc1_10.Convert();


  // Lines and line titles
  std::vector<int> lineTimes          {ncc1_1, ncc1_2, ncc1_3, ncc1_4, ncc1_5, ncc1_6, ncc1_7, ncc1_8, ncc1_9, ncc1_10};
  std::vector<const char*> lineTitles {"1A/s to 200A", "1A/s to 500A", "SD", "1A/s to 200A", "1A/s to 1kA", "SD", "1A/s to 200A", "OL to 5kA", "SD", "Test OL ramping"};

  std::cout << "Making the temperature data trees ... " << std::endl;
  std::vector<TTree*> treeVec;

  TTree* tempECTASh = makeTree("./ecta_temp_0.csv", "./ectaT0.root");   treeVec.push_back(tempECTASh);
  TTree* tempECTAKS = makeTree("./ecta_temp_1_K.csv", "./ectaT1.root"); treeVec.push_back(tempECTAKS); std::cout << "Made tree ECTA ..." << std::endl;

  makePlots (cNCCDay1Start, cNCCDay1Stop, lineTimes, lineTitles, false, treeVec);
}


The output is now:

ok thank you, I did not see that!

Now I have the two plots again, but I would like to merge them into one, with two Y-axes, and the Y-axis of the “blue” graphs should be on the right, going from 0, 50 like it does now, and show “Resistance [Ohm]”. So I made the TGaxis on line 242 (on or about :slight_smile: ) and I tried to deal with pads like I saw in some example.

Any ideas how I can merge the two?

Cheers,
Nikkie

You should make only one TMultiGraph collecting all the TGraphs in mg and mg1.

ok, and how do I make sure that the second Y-axes is connected properly connected to the “blue” data points? Because I tried several options but I don’t see how I can do that in one MultiGraph. So that is why I played around with the two MultiGraphs and the TPads.

The 2nd axis is a TGaxis. It is not “connected” to anything. That’s up to you to define its limits when you create it.

ok, maybe I explained myself wrong, but luckily I found another answer from you with what I meant :slight_smile:

I’m almost there now, but the blue axes does not want to go to the right:

#include <iostream>
#include <map>
#include <vector>
#include <fstream>

#include "TCanvas.h"
#include "TPad.h"
#include "TGraph.h"
#include "TMultiGraph.h"
#include "TTree.h"
#include "TList.h"
#include "TDatime.h"
#include "TAxis.h"
#include "TGaxis.h"
#include "TLegend.h"
#include "TStyle.h"
#include "TMath.h"
#include "TLine.h"
#include "TText.h"

// Colors
const int cRed = 632;
const int cOrange = 800;
const int cBlue = 860;
const std::vector<int> cColorsTwelve {cRed-9, cOrange, cBlue+6, cRed-4, cOrange-3, cBlue+5, cRed+1, cOrange+8, cBlue-5, cRed+3, cOrange+4, cBlue-6};
const std::vector<int> cColorsFifteen {cRed-9, cOrange, cBlue+6, cRed-4, cOrange-3, cBlue+5, cRed+1, cOrange+8, cBlue-5, cRed+3, cOrange+4, cBlue-6, cRed+4, cOrange+3, cBlue+3};
const vector<int> cColorsEight {cBlue+6, cOrange-4, cBlue+5, cOrange+6, cBlue-5, cRed-7, cBlue-6, cRed+1};
const vector<int> cColorsFour {cRed-7, cOrange+6, cOrange-4, cBlue+6};
const std::vector<int> cColorsTwo {cOrange-4, cBlue+6};

const int cLineThickness = 2;

bool checkFile (const char* fileName)
{
    bool fileExists = false;
    std::ifstream file(fileName);
    if(file.is_open())
      fileExists = true;

    return fileExists;
}

TTree* makeTree (const char* pFileName, const char* pSaveFileName) 
{
  TTree* myTree = new TTree();
  myTree->ReadFile(Form("%s", pFileName), "", ',');
  myTree->SetName("t1");

  TDatime time;
  Int_t dateTime;
  char Date[100];

  TBranch *intTime = myTree->Branch("dateTime", &dateTime);
  myTree->SetBranchAddress("Timestamp (LOCAL_TIME)", &Date[0]);

  int tempCount = 0;
  for (Long64_t i=0; i<myTree->GetEntries(); i++)
  //for (Long64_t i=0; i<200; i++)
  {
    myTree->GetEntry(i);

    // Get the date and make it into a string, cut into pieces
    std::string sDate(Date);

    time.Set(std::stoi(sDate.substr(0,4)), 
              std::stoi(sDate.substr(5,2)), 
              std::stoi(sDate.substr(8,2)), 
              std::stoi(sDate.substr(11,2)),
              std::stoi(sDate.substr(14,2)),
              std::stoi(sDate.substr(17,2)));

    dateTime = time.Convert();
    intTime->Fill();
  }

  myTree->ResetBranchAddresses(); // Disconnect from local variables
  // myTree->Print();

  // Save the TTree to a file
  myTree->SaveAs(pSaveFileName);

  return myTree;
}

TGraph* RunningAverageNormalized(TGraph* inputGraph, const int pWindow, const int pNorm)
{
  TGraph* outputGraphtemp = new TGraph();
  TGraph* outputGraph = new TGraph();

  if (pWindow <= 0 && pNorm <= 0)
    return inputGraph;

  for (int i=0; i<inputGraph->GetN()-pWindow; i++)
  {
    double averageX = 0;
    double averageY = 0;
    for (int j=0; j<pWindow; j++)
    {
      averageX += inputGraph->GetPointX(i+j);
      averageY += inputGraph->GetPointY(i+j);
    }
    outputGraphtemp->SetPoint(i,averageX/pWindow, averageY/pWindow);
  }

  if (pNorm > 0)
  {
    double cNorm = 0.0;
    for(int i=0; i<25; i++)
      cNorm += outputGraphtemp->GetPointY(i);
    cNorm = cNorm/25;

    for(int i=0; i<outputGraphtemp->GetN(); i++)
      outputGraph->SetPoint(i, outputGraphtemp->GetPointX(i), outputGraphtemp->GetPointY(i)-cNorm);
      // outputGraph->SetPoint(i, outputGraphtemp->GetPointX(i), outputGraphtemp->GetPointY(i)-cNorm+pNorm);
  }
  else
    outputGraph = outputGraphtemp;

  return outputGraph;
}

void doubleYAxis (double unit, int normalize, int nColumns, bool temperature,
      std::vector<TTree*> pTree,
      std::vector<const char*> pSensorList,
      std::vector<const char*> pLegends,
      const char* pTitles,
      const char* pSaveFileName,
      const vector<int> cColors,
      TDatime pStart, TDatime pStop,
      const double yLow, const double yHigh, const double yLow1, const double yHigh1, const double pWindow,
      std::vector<int> pLines, std::vector<const char*> pLineTitles, bool IVcurve = false, bool bbGraphs = false, bool doubleLegendX = false, bool doubleLegendY = false)
{
  // Make a nice legend for whatever number of entries
  double cLegendSizeY = cColors.size()/nColumns;
  double cLegendSizeX = nColumns;
  if (doubleLegendX)
    cLegendSizeX = nColumns*1.7;
  if (doubleLegendY)
    cLegendSizeY = nColumns;

  double lX1 = 0.89, lX0 = lX1-0.085*cLegendSizeX, lY1 = 0.89, lY0 = lY1-0.03*cLegendSizeY;

  TLegend* l0 = new TLegend(lX0, lY0, lX1, lY1);
  Float_t symbolMargin = 0.02*cLegendSizeX / (lX1 - lX0);
  l0->SetMargin(symbolMargin);
  l0->SetNColumns(nColumns);
  
  // Make a nice multigraph with the correct titles
  TMultiGraph *mg = new TMultiGraph();
  TMultiGraph *mg1 = new TMultiGraph();

  mg->SetTitle(pTitles);
  // mg1->SetTitle(pTitles);

  int count = 0;
  TCanvas* c1 = new TCanvas();
  for ( auto it : pSensorList )
  {
    c1->Clear();
    if (it == std::string("NULL"))
    {
        std::cout <<"\x1b[31mSensor not found:" << it << "\e[0m" << std::endl;
        Double_t x[1] = {double(pStart.Convert())};
        Double_t y[1] = {-99.00};
        TGraph *myNullHist = new TGraph(1, x, y);
        mg->Add(myNullHist, ""); myNullHist->SetTitle(" "); myNullHist->SetLineColor(0);
        ++count;
        continue;
    }

    TTree* myTree = NULL;
    for ( auto jt : pTree )
      if (jt->GetBranch(it) != 0) myTree = jt;

    // Check if sensor exists in any tree
    if (myTree == NULL) 
    {
      std::cout <<"\x1b[31mSensor not found in tree:" << it << "\e[0m" << std::endl;
      Double_t x[1] = {double(pStart.Convert())};
      Double_t y[1] = {-99.00};
      TGraph *myNullHist = new TGraph(1, x, y);
      mg->Add(myNullHist, ""); myNullHist->SetTitle(" "); myNullHist->SetLineColor(0);
      ++count;
      continue;
    }

    // Draw the data for the selected sensor
    if (temperature)
      myTree->Draw(Form("%s*%f:dateTime", it, unit), Form("dateTime>=%d&&dateTime<=%d&&%s>0", pStart.Convert()-3600, pStop.Convert()+3600, it));

    // Access the graphs
    TGraph *myHistTemp = (TGraph*)gPad->GetPrimitive("Graph")->Clone();
    TGraph* myHist = RunningAverageNormalized(myHistTemp, pWindow, normalize);

    if (count % 2 == 0)
      mg1->Add(myHist, "Y+L");
    else
      mg->Add(myHist, "L");

    myHist->SetTitle(pLegends.operator[](count));  
    myHist->SetLineColor(cColors.operator[](count));
    myHist->SetLineWidth(cLineThickness);
    l0->AddEntry(myHist, pLegends.operator[](count), "l");

    ++count;
  }
  delete c1;

  TCanvas *cMult = new TCanvas("cMult", "Temperature of the ECTC", 0, 0, 800, 600);
  cMult->SetBottomMargin(0.15);
  gStyle->SetTitleFontSize(0.025);

  TPad *pad1 = new TPad("pad1", "", 0, 0, 1, 1);
  TPad *pad2 = new TPad("pad2", "", 0, 0, 1, 1);
  pad2->SetFillStyle(4000); // Makes Pad 2
  pad2->SetFrameFillStyle(0); // Transparant
  pad2->SetBottomMargin(0.15);
  pad1->SetBottomMargin(0.15);
  pad1->Draw();
  pad1->cd();

  mg->GetXaxis()->SetLabelOffset(0.025);
  mg->GetXaxis()->SetNdivisions(505);
  mg->GetXaxis()->SetTitleOffset(1.9);
  mg->GetXaxis()->SetLimits(pStart.Convert(), pStop.Convert());
  mg->GetXaxis()->SetTimeDisplay(1);
  mg->GetXaxis()->SetTimeFormat("#splitline{%d/%m/%y}{   %H:%M}");
  mg->GetXaxis()->SetTimeOffset(0,"local");
  mg->SetMinimum(yLow);
  mg->SetMaximum(yHigh);
  mg->GetYaxis()->SetNdivisions(505);
  mg->GetYaxis()->SetLabelOffset(0.01);
  mg->GetYaxis()->SetTitleOffset(1.2);

  mg->Draw("AL");

  Double_t cXMin = pad1->GetUxmin();
  Double_t cXMax = pad1->GetUxmax();

  TAxis *rightY = mg1->GetYaxis();
  rightY->SetRangeUser(yLow1, yHigh1);
  rightY->SetDrawOption("Y+");

  pad2->Draw();
  pad2->cd();
  mg1->GetXaxis()->SetLabelOffset(0.025);
  mg1->GetXaxis()->SetNdivisions(505);
  mg1->GetXaxis()->SetTitleOffset(1.9);
  mg1->GetXaxis()->SetLimits(pStart.Convert(), pStop.Convert());
  mg1->GetXaxis()->SetTimeDisplay(1);
  mg1->GetXaxis()->SetTimeFormat("#splitline{%d/%m/%y}{   %H:%M}");
  mg1->GetXaxis()->SetTimeOffset(0,"local");
  mg1->GetYaxis()->SetNdivisions(505);
  mg1->GetYaxis()->SetLabelOffset(0.01);
  mg1->GetYaxis()->SetTitleOffset(1.2);
  mg1->Draw("Y+ALY+");
  mg1->GetYaxis()->SetAxisColor(cBlue-5);
  mg1->GetYaxis()->SetLabelColor(cBlue-5);
  mg1->GetYaxis()->SetTitle("Resistance [#Omega]");
  mg1->GetYaxis()->SetDrawOption("Y+");
  pad2->Update();

  // Draw the lines with interesting events
  TLine* myLine = new TLine(0.4, 0.4, 1.4, 1.4);
  myLine->SetLineWidth(cLineThickness);
  myLine->SetLineStyle(2);
  myLine->SetLineColor(12);
  int countL = 0;
  for (auto it : pLines)
  {
    if (it > 0 && it > pStart.Convert() && it < pStop.Convert())
    {
      myLine->DrawLine(it, yLow1, it, yHigh1);
      auto *myText = new TText(it, yHigh1+0.1, pLineTitles.operator[](countL));
      myText->SetTextAngle(45);
      myText->SetTextAlign(11);
      myText->SetTextSize(0.011);
      myText->SetTextColor(12);
      myText->Draw();
    }
    ++countL;
  }

  l0->Draw();
  // cMult->Update();

  cMult->SaveAs(Form("%s_from_%d-%d-%d_%dh%dm%ds-to_%d-%d-%d_%dh%dm%ds.pdf", pSaveFileName,
        pStart.GetYear(), pStart.GetMonth(), pStart.GetDay(), pStart.GetHour(), pStart.GetMinute(), pStart.GetSecond(),
        pStop.GetYear(), pStop.GetMonth(), pStop.GetDay(), pStop.GetHour(), pStop.GetMinute(), pStop.GetSecond()));
}

void makePlots (TDatime plotsStart, TDatime plotStop, std::vector<int> lineTimes, std::vector<const char*> lineTitles, bool unZoom, std::vector<TTree*> temperatureTreeVec) 
{
  bool temperatureYN = true;
  double unit = 1.0;
  int normalize = -99;
  double cWindow = 25;
  int nColumns = 3;
  double cYLow = 0; 
  double cYHigh = 300;

  // ECTA // ////////////// Cold Mass - Coil and Keystone 1 //////////////
  const std::vector<const char*> cECTA1 {"1TE9118A", "1TE9119A", 
                                         "1TE9118B", "1TE9119B",
                                         "1TE9128A", "1TE9129A",
                                         "1TE9128B", "1TE9129B"};
  const char* cECTA1Titles = "ECTA Cold Mass - Coil and Keyston 1 Temperature;Time [s];Temperature [K]";
  const char* cECTA1File = "./ECTA_CM1_Temperature";  
  cYLow = 4; cYHigh = 10; nColumns = 2; cWindow = 25;
  doubleYAxis(unit, normalize, nColumns, temperatureYN, temperatureTreeVec, cECTA1, cECTA1, cECTA1Titles, cECTA1File, cColorsEight, plotsStart, plotStop, cYLow, cYHigh, 0, 50, cWindow, lineTimes, lineTitles, false);
}

void atlasData()
{  

  // TIMES AND  DATES
  // Nominal current commissioning day 1
  TDatime cNCCDay1Start(2022, 4, 11, 8, 30, 0);   // Start NCC Day 1
  TDatime cNCCDay1Stop (2022, 4, 11, 17, 30, 0); // Day 1 Finisched
  TDatime cNcc1_1(2022, 4, 11, 9, 19, 38);
  int ncc1_1 = cNcc1_1.Convert();
  TDatime cNcc1_2(2022, 4, 11, 9, 31, 55);
  int ncc1_2 = cNcc1_2.Convert();
  TDatime cNcc1_3(2022, 4, 11, 9, 39, 5);
  int ncc1_3 = cNcc1_3.Convert();
  TDatime cNcc1_4(2022, 4, 11, 10, 7, 22);
  int ncc1_4 = cNcc1_4.Convert();
  TDatime cNcc1_5(2022, 4, 11, 10, 14, 16);
  int ncc1_5 = cNcc1_5.Convert();
  TDatime cNcc1_6(2022, 4, 11, 10, 30, 10);
  int ncc1_6 = cNcc1_6.Convert();
  TDatime cNcc1_7(2022, 4, 11, 10, 59, 44);
  int ncc1_7 = cNcc1_7.Convert();
  TDatime cNcc1_8(2022, 4, 11, 11, 3, 45);
  int ncc1_8 = cNcc1_8.Convert();
  TDatime cNcc1_9(2022, 4, 11, 11, 51, 00);
  int ncc1_9 = cNcc1_9.Convert();
  TDatime cNcc1_10(2022, 4, 11, 15, 23, 30);
  int ncc1_10 = cNcc1_10.Convert();


  // Lines and line titles
  std::vector<int> lineTimes          {ncc1_1, ncc1_2, ncc1_3, ncc1_4, ncc1_5, ncc1_6, ncc1_7, ncc1_8, ncc1_9, ncc1_10};
  std::vector<const char*> lineTitles {"1A/s to 200A", "1A/s to 500A", "SD", "1A/s to 200A", "1A/s to 1kA", "SD", "1A/s to 200A", "OL to 5kA", "SD", "Test OL ramping"};

  std::cout << "Making the temperature data trees ... " << std::endl;
  std::vector<TTree*> treeVec;

  TTree* tempECTASh = makeTree("./ecta_temp_0.csv", "./ectaT0.root");   treeVec.push_back(tempECTASh);
  TTree* tempECTAKS = makeTree("./ecta_temp_1_K.csv", "./ectaT1.root"); treeVec.push_back(tempECTAKS); std::cout << "Made tree ECTA ..." << std::endl;

  makePlots (cNCCDay1Start, cNCCDay1Stop, lineTimes, lineTitles, false, treeVec);
}

ECTA_CM1_Temperature_from_2022-4-11_8h30m0s-to_2022-4-11_17h30m0s.pdf (68.9 KB)

For reference: this is the answer I was referring to :slight_smile:

I just don’t understand why my second y-axes stays stuck on the left…

It looks like in general “Y+” does not work for TMultiGraph (@couet maybe a bug in ROOT?).

{
  auto c0 = new TCanvas("c1","multigraph L3",200,10,700,500);

  auto mg1 = new TMultiGraph();
  auto gr1 = new TGraph(); gr1->SetLineColor(kBlue);
  auto gr2 = new TGraph(); gr2->SetLineColor(kRed);
  auto gr3 = new TGraph(); gr3->SetLineColor(kGreen);
  auto gr4 = new TGraph(); gr4->SetLineColor(kOrange);

  Double_t dx = 6.28/1000;
  Double_t x  = -3.14;
  for (int i=0; i<=1000; i++) {
     x = x+dx;
     gr1->SetPoint(i,x,2.*TMath::Sin(x));
     gr2->SetPoint(i,x,TMath::Cos(x));
     gr3->SetPoint(i,x,TMath::Cos(x*x));
     gr4->SetPoint(i,x,TMath::Cos(x*x*x));
  }
  mg1->Add(gr4); gr4->SetTitle("Cos(x*x*x)"); gr4->SetLineWidth(3);
  mg1->Add(gr3); gr3->SetTitle("Cos(x*x)")  ; gr3->SetLineWidth(3);
  mg1->Add(gr2); gr2->SetTitle("Cos(x)")    ; gr2->SetLineWidth(3);
  mg1->Add(gr1); gr1->SetTitle("2*Sin(x)")  ; gr1->SetLineWidth(3);
  mg1->SetTitle("Multi-graph Title; X-axis Title; Y-axis Title");

  mg1->Draw("alY+");

  mg1->GetHistogram()->SetMinimum(-2.4);
  mg1->GetHistogram()->SetMaximum(2.4);
  mg1->GetHistogram()->GetXaxis()->SetRangeUser(0.,2.5);
  gPad->Modified();
  gPad->Update();
}

gives

As alternative, as couet suggested, you can add all graphs from the 2 multigraphs to one multigraph, and then draw your own TAxis on the right side. But the “temperature” and “resistance” graphs have different y-axis scales, and when you set the max/min to 4-10, the resistance lines will be near the top edge (just below 10, from couet’s plots), i.e. near 50 according to the right-side scale; so you will need to scale and offset (in y) the resistance graphs before adding them to the multigraph, so that their 0-50 range corresponds to 4-10.

I am on it… give me some time. The macro needs a big clean up anyway.

2 Likes

Here we are.

#include <iostream>
#include <map>
#include <vector>
#include <fstream>

#include "TCanvas.h"
#include "TPad.h"
#include "TGraph.h"
#include "TMultiGraph.h"
#include "TTree.h"
#include "TList.h"
#include "TDatime.h"
#include "TAxis.h"
#include "TLegend.h"
#include "TStyle.h"
#include "TMath.h"
#include "TLine.h"
#include "TText.h"

// Colors
const int cRed    = 632;
const int cOrange = 800;
const int cBlue   = 860;
const std::vector<int> cColorsTwelve {cRed-9, cOrange, cBlue+6, cRed-4, cOrange-3, cBlue+5, cRed+1, cOrange+8, cBlue-5, cRed+3, cOrange+4, cBlue-6};
const std::vector<int> cColorsFifteen {cRed-9, cOrange, cBlue+6, cRed-4, cOrange-3, cBlue+5, cRed+1, cOrange+8, cBlue-5, cRed+3, cOrange+4, cBlue-6, cRed+4, cOrange+3, cBlue+3};
const vector<int> cColorsEight {cBlue+6, cOrange-4, cBlue+5, cOrange+6, cBlue-5, cRed-7, cBlue-6, cRed+1};
const vector<int> cColorsFour {cRed-7, cOrange+6, cOrange-4, cBlue+6};
const std::vector<int> cColorsTwo {cOrange-4, cBlue+6};

const int cLineThickness = 2;

bool checkFile (const char* fileName)
{
   bool fileExists = false;
   std::ifstream file(fileName);
   if (file.is_open()) fileExists = true;
   return fileExists;
}

TTree* makeTree (const char* pFileName, const char* pSaveFileName)
{
   auto myTree = new TTree();
   myTree->ReadFile(Form("%s", pFileName), "", ',');
   myTree->SetName("t1");

   TDatime time;
   Int_t dateTime;
   char Date[100];

   TBranch *intTime = myTree->Branch("dateTime", &dateTime);
   myTree->SetBranchAddress("Timestamp (LOCAL_TIME)", &Date[0]);

   int tempCount = 0;
   for (Long64_t i=0; i<myTree->GetEntries(); i++) {
      myTree->GetEntry(i);

      // Get the date and make it into a string, cut into pieces
      std::string sDate(Date);

      time.Set(std::stoi(sDate.substr(0,4)),
              std::stoi(sDate.substr(5,2)),
              std::stoi(sDate.substr(8,2)),
              std::stoi(sDate.substr(11,2)),
              std::stoi(sDate.substr(14,2)),
              std::stoi(sDate.substr(17,2)));

      dateTime = time.Convert();
      intTime->Fill();
   }

   myTree->ResetBranchAddresses(); // Disconnect from local variables

   // Save the TTree to a file
   myTree->SaveAs(pSaveFileName);

   return myTree;
}

TGraph* RunningAverageNormalized(TGraph* inputGraph, const int pWindow, const int pNorm)
{
   auto outputGraphtemp = new TGraph();
   auto outputGraph     = new TGraph();

   if (pWindow <= 0 && pNorm <= 0) return inputGraph;

   for (int i=0; i<inputGraph->GetN()-pWindow; i++) {
      double averageX = 0;
      double averageY = 0;
      for (int j=0; j<pWindow; j++) {
         averageX += inputGraph->GetPointX(i+j);
         averageY += inputGraph->GetPointY(i+j);
      }
      outputGraphtemp->SetPoint(i,averageX/pWindow, averageY/pWindow);
   }

   if (pNorm > 0) {
      double cNorm = 0.0;
      for(int i=0; i<25; i++)
      cNorm += outputGraphtemp->GetPointY(i);
      cNorm = cNorm/25;

      for(int i=0; i<outputGraphtemp->GetN(); i++)
      outputGraph->SetPoint(i, outputGraphtemp->GetPointX(i), outputGraphtemp->GetPointY(i)-cNorm);
   } else {
      outputGraph = outputGraphtemp;
   }
   return outputGraph;
}

void doubleYAxis (double unit, int normalize, int nColumns, bool temperature,
      std::vector<TTree*> pTree,
      std::vector<const char*> pSensorList,
      std::vector<const char*> pLegends,
      const char* pTitles,
      const char* pSaveFileName,
      const vector<int> cColors,
      TDatime pStart, TDatime pStop,
      const double yLow, const double yHigh, const double yLow1, const double yHigh1, const double pWindow,
      std::vector<int> pLines, std::vector<const char*> pLineTitles, bool IVcurve = false, bool bbGraphs = false, bool doubleLegendX = false, bool doubleLegendY = false)
{
   // Make a nice legend for whatever number of entries
   double cLegendSizeY = cColors.size()/nColumns;
   double cLegendSizeX = nColumns;
   if (doubleLegendX) cLegendSizeX = nColumns*1.7;
   if (doubleLegendY) cLegendSizeY = nColumns;

   double lX1 = 0.89, lX0 = lX1-0.085*cLegendSizeX, lY1 = 0.89, lY0 = lY1-0.03*cLegendSizeY;

   auto l0 = new TLegend(lX0, lY0, lX1, lY1);
   Float_t symbolMargin = 0.02*cLegendSizeX / (lX1 - lX0);
   l0->SetMargin(symbolMargin);
   l0->SetNColumns(nColumns);

   // Make a nice multigraph with the correct titles
   auto mg  = new TMultiGraph();
   auto mg1 = new TMultiGraph();

   mg->SetTitle(pTitles);

   int count = 0;

   for ( auto it : pSensorList ) {
      if (it == std::string("NULL")) {
         std::cout <<"\x1b[31mSensor not found:" << it << "\e[0m" << std::endl;
         Double_t x[1] = {double(pStart.Convert())};
         Double_t y[1] = {-99.00};
         TGraph *myNullHist = new TGraph(1, x, y);
         mg->Add(myNullHist, ""); myNullHist->SetTitle(" "); myNullHist->SetLineColor(0);
         ++count;
         continue;
      }

      TTree* myTree = NULL;
      for ( auto jt : pTree )
      if (jt->GetBranch(it) != 0) myTree = jt;

      // Check if sensor exists in any tree
         if (myTree == NULL) {
         std::cout <<"\x1b[31mSensor not found in tree:" << it << "\e[0m" << std::endl;
         Double_t x[1] = {double(pStart.Convert())};
         Double_t y[1] = {-99.00};
         TGraph *myNullHist = new TGraph(1, x, y);
         mg->Add(myNullHist, ""); myNullHist->SetTitle(" "); myNullHist->SetLineColor(0);
         ++count;
         continue;
      }

      // Draw the data for the selected sensor
      if (temperature)
      myTree->Draw(Form("%s*%f:dateTime", it, unit), Form("dateTime>=%d&&dateTime<=%d&&%s>0", pStart.Convert()-3600, pStop.Convert()+3600, it));

      // Access the graphs
      TGraph *myHistTemp = (TGraph*)gPad->GetPrimitive("Graph")->Clone();
      TGraph* myHist = RunningAverageNormalized(myHistTemp, pWindow, normalize);

      if (count % 2 == 0) mg1->Add(myHist);
      else                 mg->Add(myHist);

      myHist->SetTitle(pLegends.operator[](count));
      myHist->SetLineColor(cColors.operator[](count));
      myHist->SetLineWidth(cLineThickness);
      l0->AddEntry(myHist, pLegends.operator[](count), "l");

      ++count;
   }

   auto cMult = new TCanvas("cMult", "Temperature of the ECTC", 0, 0, 800, 600);
   cMult->SetBottomMargin(0.15);
   gStyle->SetTitleFontSize(0.025);

   auto pad1 = new TPad("pad1", "", 0, 0, 1, 1);
   auto pad2 = new TPad("pad2", "", 0, 0, 1, 1);
   pad2->SetFillStyle(4000); // Makes Pad 2
   pad2->SetFrameFillStyle(0); // Transparant
   pad2->SetBottomMargin(0.15);
   pad1->SetBottomMargin(0.15);
   pad1->Draw();
   pad1->cd();

   mg->GetXaxis()->SetLabelOffset(0.025);
   mg->GetXaxis()->SetNdivisions(505);
   mg->GetXaxis()->SetTitleOffset(1.9);
   mg->GetXaxis()->SetLimits(pStart.Convert(), pStop.Convert());
   mg->GetXaxis()->SetTimeDisplay(1);
   mg->GetXaxis()->SetTimeFormat("#splitline{%d/%m/%y}{   %H:%M}");
   mg->GetXaxis()->SetTimeOffset(0,"local");
   mg->SetMinimum(yLow);
   mg->SetMaximum(yHigh);
   mg->GetYaxis()->SetNdivisions(505);
   mg->GetYaxis()->SetLabelOffset(0.01);
   mg->GetYaxis()->SetTitleOffset(1.2);

   mg->Draw("AL");

   pad2->Draw();
   pad2->cd();
   mg1->GetYaxis()->SetRangeUser(yLow1, yHigh1);
   mg1->GetXaxis()->SetLabelOffset(0.025);
   mg1->GetXaxis()->SetNdivisions(505);
   mg1->GetXaxis()->SetTitleOffset(1.9);
   mg1->GetXaxis()->SetLimits(pStart.Convert(), pStop.Convert());
   mg1->GetXaxis()->SetTimeDisplay(1);
   mg1->GetXaxis()->SetTimeFormat("#splitline{%d/%m/%y}{   %H:%M}");
   mg1->GetXaxis()->SetTimeOffset(0,"local");
   mg1->GetYaxis()->SetLabelSize(0.);
   mg1->GetYaxis()->SetTickSize(0);
   mg1->Draw("A");
   pad2->Update();
   Double_t cXMin = pad2->GetUxmin();
   Double_t cXMax = pad2->GetUxmax();

   auto cAxis = new TGaxis(cXMax, yLow1, cXMax, yHigh1, yLow1, yHigh1, 505, "+L");
   cAxis->SetLineColor(cBlue-5);
   cAxis->SetLabelColor(cBlue-5);
   cAxis->SetLabelFont(42);
   cAxis->SetTitle("Resistance [#Omega]");
   cAxis->SetTitleOffset(1.2);
   cAxis->SetTitleFont(42);
   cAxis->Draw();

   // Draw the lines with interesting events
   auto myLine = new TLine(0.4, 0.4, 1.4, 1.4);
   myLine->SetLineWidth(cLineThickness);
   myLine->SetLineStyle(2);
   myLine->SetLineColor(12);
   int countL = 0;
   for (auto it : pLines) {
      if (it > 0 && it > pStart.Convert() && it < pStop.Convert()) {
         myLine->DrawLine(it, yLow1, it, yHigh1);
         auto *myText = new TText(it, yHigh1+0.1, pLineTitles.operator[](countL));
         myText->SetTextAngle(45);
         myText->SetTextAlign(11);
         myText->SetTextSize(0.011);
         myText->SetTextColor(12);
         myText->Draw();
      }
      ++countL;
   }

   l0->Draw();

   cMult->SaveAs(Form("%s_from_%d-%d-%d_%dh%dm%ds-to_%d-%d-%d_%dh%dm%ds.pdf", pSaveFileName,
        pStart.GetYear(), pStart.GetMonth(), pStart.GetDay(), pStart.GetHour(), pStart.GetMinute(), pStart.GetSecond(),
        pStop.GetYear(), pStop.GetMonth(), pStop.GetDay(), pStop.GetHour(), pStop.GetMinute(), pStop.GetSecond()));
}

void makePlots (TDatime plotsStart, TDatime plotStop, std::vector<int> lineTimes, std::vector<const char*> lineTitles, bool unZoom, std::vector<TTree*> temperatureTreeVec)
{
   bool   temperatureYN = true;
   double unit          = 1.0;
   int    normalize     = -99;
   double cWindow       = 25;
   int    nColumns      = 3;
   double cYLow         = 0;
   double cYHigh        = 300;

   // ECTA // ////////////// Cold Mass - Coil and Keystone 1 //////////////
   const std::vector<const char*> cECTA1 {"1TE9118A", "1TE9119A",
                                         "1TE9118B", "1TE9119B",
                                         "1TE9128A", "1TE9129A",
                                         "1TE9128B", "1TE9129B"};
   const char* cECTA1Titles = "ECTA Cold Mass - Coil and Keyston 1 Temperature;Time [s];Temperature [K]";
   const char* cECTA1File = "./ECTA_CM1_Temperature";
   cYLow = 4; cYHigh = 10; nColumns = 2; cWindow = 25;
   doubleYAxis(unit, normalize, nColumns, temperatureYN, temperatureTreeVec, cECTA1, cECTA1, cECTA1Titles, cECTA1File, cColorsEight, plotsStart, plotStop, cYLow, cYHigh, 0, 50, cWindow, lineTimes, lineTitles, false);
}

void atlasData()
{
   // TIMES AND  DATES
   // Nominal current commissioning day 1
   TDatime cNCCDay1Start(2022, 4, 11, 8, 30, 0);   // Start NCC Day 1
   TDatime cNCCDay1Stop (2022, 4, 11, 17, 30, 0); // Day 1 Finisched
   TDatime cNcc1_1(2022, 4, 11, 9, 19, 38);
   int ncc1_1 = cNcc1_1.Convert();
   TDatime cNcc1_2(2022, 4, 11, 9, 31, 55);
   int ncc1_2 = cNcc1_2.Convert();
   TDatime cNcc1_3(2022, 4, 11, 9, 39, 5);
   int ncc1_3 = cNcc1_3.Convert();
   TDatime cNcc1_4(2022, 4, 11, 10, 7, 22);
   int ncc1_4 = cNcc1_4.Convert();
   TDatime cNcc1_5(2022, 4, 11, 10, 14, 16);
   int ncc1_5 = cNcc1_5.Convert();
   TDatime cNcc1_6(2022, 4, 11, 10, 30, 10);
   int ncc1_6 = cNcc1_6.Convert();
   TDatime cNcc1_7(2022, 4, 11, 10, 59, 44);
   int ncc1_7 = cNcc1_7.Convert();
   TDatime cNcc1_8(2022, 4, 11, 11, 3, 45);
   int ncc1_8 = cNcc1_8.Convert();
   TDatime cNcc1_9(2022, 4, 11, 11, 51, 00);
   int ncc1_9 = cNcc1_9.Convert();
   TDatime cNcc1_10(2022, 4, 11, 15, 23, 30);
   int ncc1_10 = cNcc1_10.Convert();


   // Lines and line titles
   std::vector<int> lineTimes          {ncc1_1, ncc1_2, ncc1_3, ncc1_4, ncc1_5, ncc1_6, ncc1_7, ncc1_8, ncc1_9, ncc1_10};
   std::vector<const char*> lineTitles {"1A/s to 200A", "1A/s to 500A", "SD", "1A/s to 200A", "1A/s to 1kA", "SD", "1A/s to 200A", "OL to 5kA", "SD", "Test OL ramping"};

   std::cout << "Making the temperature data trees ... " << std::endl;
   std::vector<TTree*> treeVec;

   TTree* tempECTASh = makeTree("./ecta_temp_0.csv", "./ectaT0.root");   treeVec.push_back(tempECTASh);
   TTree* tempECTAKS = makeTree("./ecta_temp_1_K.csv", "./ectaT1.root"); treeVec.push_back(tempECTAKS); std::cout << "Made tree ECTA ..." << std::endl;

   makePlots (cNCCDay1Start, cNCCDay1Stop, lineTimes, lineTitles, false, treeVec);
}

1 Like

Ah great, thanks for your help!

1 Like