/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation; * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include //! @file fwdisp.C //! @author Craig Dowell (University of Washington) //! @mainpage //! This is a Root macro to analyze individual MDT chambers by looking at data //! in Athena Aware Ntuples generated by the MuonCommRecExample code. The //! name chambdisp stands for CHAMber DISPlayer. The primary output of this //! macro will be a drawing of a chamber that you define as seen from the //! chamber local coordinate system y-z plane. //! Root Canvas we'll use to draw the chamber on. TCanvas *gCanvas = 0; Double_t gzAxisLength = 0.; Double_t gyAxisLength = 0.; Double_t ghorzWirePitch = 0.; Double_t gvertWirePitch = 0.; Double_t gtubeOD = 0.; Double_t gtubeOR = 0.; Double_t gspacerHalfHeight = 0.; Double_t gmultilayerHeight = 0.; //! Do any required initialization for the cumulative data analysis. //! @returns Nothing. void init_cumulative() { // // What we want to do is to arrange the Root user coordinate system so that we // can just drop the y-z plane of the chamber local coordinate sysem on top of // the pad. // Y // ^ // | 1 2 3 4 5 6 7 8 // | // | 0 0 0 0 0 0 0 0 L3 // ML2 | 0 0 0 0 0 0 0 0 L2 // | 0 0 0 0 0 0 0 0 L1 // | // (0,0) ->+------------------------> Z // | // | 0 0 0 0 0 0 0 0 L3 // ML1 | 0 0 0 0 0 0 0 0 L2 // | 0 0 0 0 0 0 0 0 L1 // | // // So the y-axis remains the y-axis, but when we render along the z-axis in // the chamber local coordinate system, we'll be rendering along the x-axis // in Root. I'm going to naming all of the variables in the program as if // they were in the y-z plane. You will notice that variables named zSomething // will end up being passed to Root in parameters where you would expect to // see xSomething. // // It will be nice to arrange the units of the pad to be in millimeters. Then // no scaling is required. The tubes in the chambers have an outer diameter // of 30 mm. // Double_t tubeOD = 30.; // // There is some space between the tubes, and this results in a horizontal // wire pitch of 30.035 mm. // Double_t horzWirePitch = 30.035; // // To find the extent of one layer in the z-axis, get the number of spaces // between the wires and multiply that by the pitch, then add enough space // to account for the left side of tube one and the right side of the last // tube (one tube outside diameter). // Double_t zAxisLength = (giWires - 1) * horzWirePitch + tubeOD; // // Even layers are offset by half a horizontal wire pitch so we need to add // that space in. // zAxisLength += horzWirePitch / 2.; // // The y-axis is similar, but we have a spacer to account for. The origin of // the y-axis is dead center of the spacer. First, divide the spacer size in // half. Multilayer two will sit on the top of the spacer in the positive // y-axis. The situation is symmetrical for multilayer one in the negative // y-axis. // Double_t spacerHalfHeight = (Double_t)giSpacerWidthMM / 2.; Double_t yAxisLength = spacerHalfHeight; // // Given the horizontal wire pitch of 30.035 mm, you can calculate the vertical // wire pitch as 26.011 mm. To find the multilayer extent get the number of // spaces between the wires and multiply that by the pitch, then add enough // space to account for the bottom side of the tube at layer one and the top // side of the top (one tube outside diameter). // Double_t vertWirePitch = 26.011; Double_t multilayerHeight = (Double_t)(giLayers - 1) * vertWirePitch + tubeOD; yAxisLength += multilayerHeight; // // Now size the canvas according to all of the work we just did. The // user coordinate system matches the chamber coordinate system and runs in // millimeters. Now, we want the aspect ratio to be correct, so we need to // create the canvas similar to the chamber coordinates, but at a reasonable // size. The widest chambers are going to end up at 2177 mm along the z-axis // so dividing by two seems reasonable. // if (gCanvas) { delete gCanvas; gCanvas = 0; } assert(gCanvas == 0); gCanvas = new TCanvas("Canvas", "Chamber", zAxisLength / 2., yAxisLength); assert(gCanvas); gCanvas->Range(0., -yAxisLength, zAxisLength, yAxisLength); gCanvas->SetFixedAspectRatio(); gCanvas->Clear(); gCanvas->Update(); // // Stash away the variables we just so carefully calulated for use later. // gspacerHalfHeight = spacerHalfHeight; gzAxisLength = zAxisLength; gyAxisLength = yAxisLength; ghorzWirePitch = horzWirePitch; gvertWirePitch = vertWirePitch; gmultilayerHeight = multilayerHeight; gtubeOD = tubeOD; gtubeOR = tubeOD / 2.; } //! Do an analyzis on the cumulative data stored in the cumulative chamber //! array. Call out of this function to do your own analysis. //! @see giChamber //! @returns Nothing. void analyze_cumulative(void) { } //! Do any required initialization for the per-event data analysis. //! @returns Nothing. void init_event() { } //! Ask the user what to do between events. Typically used to implement a //! pause for the user to contemplate the output. The return value determines //! what happens next. //! @see PauseAction //! @param iEventNext The next event to run if jump is selected //! @returns PauseAction Action for program to take PauseAction event_pause(Int_t &iEventNext) { Int_t interesting = 0; for (Int_t m = 1; m <= giMultilayers; ++m) { for (Int_t l = 1; l <= giLayers; ++l) { for (Int_t w = 1; w <= giWires; ++w) { if (giEventChamber[m][l][w]) { ++interesting; } } } } if (!interesting) { return PauseAction::Next; } Char_t cmd; for (;;) { cout << "n)ext, p)rev, q)uit, j)ump: "; cin >> cmd; switch (cmd) { case 'n': return PauseAction::Next; case 'p': return PauseAction::Prev; case 'q': return PauseAction::Quit; case 'j': cout << " j)ump to event: "; cin >> iEventNext; return PauseAction::Jump; default: continue; } } return PauseAction::Next; } //! Do an analyzis on the per-event data stored in the per-event //! chamber array. Call out of this function to do your own analysis. //! @see giEventChamber //! @see giTdcChamber //! @see giAdcChamber //! @returns Nothing. void analyze_event(Int_t iEvent) { draw_chamber(iEvent); } void draw_chamber(Int_t iEvent) { gCanvas->Clear(); draw_multilayer(gspacerHalfHeight, 2); draw_spacer(); draw_multilayer(-gspacerHalfHeight - gmultilayerHeight, 1); gCanvas->Update(); } const Int_t kCOLOR_BLACK = 1; const Int_t kCOLOR_RED = 2; const Int_t kCOLOR_GREEN = 3; const Int_t kCOLOR_BLUE = 4; const Int_t kCOLOR_GRAY = 11; const Int_t kPATTERN_DIAGONAL_LINES_UP = 3004; const Int_t kPATTERN_DIAGONAL_LINES_DOWN = 3005; const Int_t kPATTERN_CROSS_HATCH = 3013; TBox *spacer = 0; void draw_spacer (void) { if (spacer) { delete spacer; spacer = 0; } spacer = new TBox(0, -gspacerHalfHeight, gzAxisLength, gspacerHalfHeight); spacer->SetFillColor(kCOLOR_GRAY); spacer->SetFillStyle(kPATTERN_CROSS_HATCH); spacer->Draw(); } void draw_multilayer(Double_t y, Int_t multilayer) { Double_t bottomWireCenter = y + gtubeOD / 2.; Double_t leftWireCenter = gtubeOD / 2.; for (Int_t i = 1; i <= giLayers; ++i) { Double_t layerY = bottomWireCenter + (i - 1) * gvertWirePitch; Double_t layerZ = leftWireCenter; layerZ += (i & 1) == 0 ? gtubeOD / 2. : 0.; draw_layer(layerY, layerZ, multilayer, i); } } void draw_layer(Double_t y, Double_t z, Int_t multilayer, Int_t layer) { for (Int_t i = 1; i <= giWires; ++i) { TEllipse *tube = new TEllipse(z, y, gtubeOR, gtubeOR); if (giEventChamber[multilayer][layer][i]) { tube->SetFillColor(kCOLOR_RED); } tube->Draw(); z += ghorzWirePitch; } }