int main(int argc, char * argv[]){ TApplication app("app", &argc, argv); plottingEngine.SetDefaultStyle(); // Load the Magboltz gas file MediumMagboltz* gas = new MediumMagboltz(); gas->LoadGasFile("ar_90_c4h10_10_B2T_90deg.gas"); // // Structure of Micromegas cell (from top to bottom): //------------------------ // | Drift electrode (Vdrift = -2500 V) // | Drift gap (Hdriftgap = 3 mm) // | Mesh electrode (Vmesh = -500 V) // | Amplification gap (Hampgap = 100 um) // | Strips (Vstrip = 0 V) //------------------------ // // // We place 3 strips in z direction in order to // be able to resolve the avalanche signal. // Strips can be parametrized by width and pitch // // Units given in [cm] const double Hdriftgap = 0.3; // Drift gap height const double Hampgap = 0.01; // Amplification gap height const double Htot = Hdriftgap + Hampgap; // Total height const double Pitch = 0.07; // Pitch const double Interpitch = 0.01; // Distance between two strips const double Wstrip = Pitch - Interpitch; // Electrode potentials const double Vdrift = -3000.; const double Vamp = -500.; const double Vstrip = 0.; // Magnetic field const double MagX = 0.; const double MagY = 0.; const double MagZ = 2.; // Geometry of the structure // Parallel planes, SolidBox, height [cm] // Width [cm] const double w = 3.0; // Length [cm] const double l = 3.0; GeometrySimple* geo = new GeometrySimple(); SolidBox* box = new SolidBox(0.,Htot/2.0, 0., w, Htot/2.0, l); geo->AddSolid(box, gas); // // The Component requires a description of the geometry, that is a list of volumes // and associated media. // Here we construct 2 ComponentAnalyticalFields, to treat the // arift region and the amplification separately // ComponentAnalyticField* cmpDrift = new ComponentAnalyticField(); ComponentAnalyticField* cmpAmp = new ComponentAnalyticField(); // Pass a pointer of the geometry class to the components. cmpDrift->SetGeometry(geo); cmpAmp->SetGeometry(geo); // Now we add the planes for the electrodes with labels cmpDrift->AddPlaneY(Htot, Vdrift, "DriftPlane"); cmpDrift->AddPlaneY(Hampgap, Vamp, "AmpPlane"); cmpAmp->AddPlaneY(Hampgap, Vamp, "AmpPlane"); cmpAmp->AddPlaneY(0.0, Vstrip, "StripPlane"); //Next we construct the Strips for readout of te signal, also with labels double Xoffset = 0.; double Xstrip1, Xstrip2, Xstrip3; // Store the center of the strips Xstrip1 = Xoffset + Wstrip/2.0; cmpAmp->AddStripOnPlaneY('z', 0.0, Xoffset, Xoffset + Wstrip, "Strip1"); Xoffset += (Wstrip + Interpitch); Xstrip2 = Xoffset + Wstrip/2.0; cmpAmp->AddStripOnPlaneY('z', 0.0, Xoffset, Xoffset + Wstrip, "Strip2"); Xoffset += (Wstrip + Interpitch); Xstrip3 = Xoffset + Wstrip/2.0; cmpAmp->AddStripOnPlaneY('z', 0.0, Xoffset, Xoffset + Wstrip, "Strip3"); //We want to calculate the signal induced on the strip. //We have to tell this to the ComponentAnalyticalField cmpAmp->AddReadout("Strip1"); cmpAmp->AddReadout("Strip2"); cmpAmp->AddReadout("Strip3"); // Set constant magnetic field in [Tesla] cmpDrift->SetMagneticField(MagX, MagY, MagZ); //Finally we assemble a Sensor object Sensor* sensor = new Sensor(); // Calculate the electric field using the Component object cmp sensor->AddComponent(cmpDrift); sensor->AddComponent(cmpAmp); // Request signal calculation for the electrode named with labels above, // using the weighting field provided by the Component object cmp. sensor->AddElectrode(cmpAmp, "Strip1"); sensor->AddElectrode(cmpAmp, "Strip2"); sensor->AddElectrode(cmpAmp, "Strip3"); // Set Time window for signal integration, units in [ns] const double tMin = 0.; const double tMax = 100.; const double tStep = 0.05; const int nTimeBins = int((tMax - tMin) / tStep); sensor->SetTimeWindow(0., tStep, nTimeBins); // This canvas will be used to display the drift lines and the field TCanvas * c = new TCanvas("c", "c", 10, 10, 1000, 700); c->Divide(2,1); // Construct object to visualise drift lines ViewDrift* viewdrift = new ViewDrift(); viewdrift->SetArea(0.0, 0.0, -0.1, 0.2, Htot,0.1 ); viewdrift->SetClusterMarkerSize(0.1); viewdrift->SetCollisionMarkerSize(0.5); viewdrift->SetCanvas((TCanvas*)c->cd(1)); //For simulating the electron avalanche we use the class AvalancheMicroscopic AvalancheMicroscopic* aval = new AvalancheMicroscopic(); const int aval_size = 10; aval->SetSensor(sensor); // Switch on signal calculation. aval->EnableSignalCalculation(); aval->SetTimeWindow(tMin,tMax ); // aval->EnableAvalancheSizeLimit(aval_size); aval->EnablePlotting(viewdrift); aval->EnableDriftLines(); aval->EnableMagneticField(); // Additional optional switches //aval->EnableExcitationMarkers(); //aval->EnableIonisationMarkers(); //Add ionizing particle using Heed // Here we add a negative pion with some momentum, units in [eV/c] const double momentum = 300.e+06; // eV/c TrackHeed* track = new TrackHeed(); track->SetParticle("pi-"); track->SetMomentum(momentum); track->SetSensor(sensor); track->EnableMagneticField(); track->EnableElectricField(); track->EnablePlotting(viewdrift); // Cluster info double xcls, ycls, zcls, tcls, e, extra; xcls = ycls = zcls = tcls = e = extra = -999.; // Electron info double xele, yele, zele, tele, eele, dxele, dyele, dzele; // Electron start and endpoints, momentum direction and status double x0ele, y0ele, z0ele, t0ele, e0ele;// start point double x1ele, y1ele, z1ele, t1ele, e1ele;// endpoint double dx1ele, dy1ele, dz1ele; // momentum direction int status1ele; // status int n = 0; // number of electrons in cluster bool cls_ret;// return OK if cluster is OK // The initial impact position of the incoming ionising track double track_x = 0.15;// [cm] double track_y = 0.3; double track_z = 0.0; // Momentum direction of incoming track double track_dx = 0.0; double track_dy = -1.0; // Track coming downstream double track_dz = 0.0; // Now create a single track track->NewTrack(track_x, track_y, track_z, tMin, track_dx, track_dy, track_dz); cls_ret = track->GetCluster(xcls, ycls, zcls, tcls, n, e, extra); // To accumulate the electron signal for each strip separately double Qstrip1 = 0.0; double Qstrip2 = 0.0; double Qstrip3 = 0.0; // Now loop over electrons in cluster for(int j = 1; j <= n; j++){ track->GetElectron(j-1, xele, yele, zele, tele, eele, dxele, dyele, dzele); // Simulate an avalanche for the current electron aval->AvalancheElectron(xele, yele, zele, tele, eele, dxele, dyele, dzele); } // To get Signal from each strip we get the it from each sensor // by integrating over time for(int i = 0; i < nTimeBins; i++){ Qstrip1 += fabs(sensor->GetElectronSignal("Strip1", i)); Qstrip2 += fabs(sensor->GetElectronSignal("Strip2", i)); Qstrip3 += fabs(sensor->GetElectronSignal("Strip3", i)); } // Now calculate the reconstructed pion position in the Micromegas // using the weighted average of the Strip signals double mean = 0.0; mean = (Qstrip1*Xstrip1 + Qstrip2*Xstrip2 + Qstrip3*Xstrip3)/(Qstrip1+Qstrip2+Qstrip3); std::cout << "---------------------------" << std::endl; std::cout << "XMean: " << mean << " cm, XTrue: " << track_x << " cm" << std::endl; std::cout << "XTrue - XMean: " << fabs(track_x - mean)*10000.0 << " um" << std::endl; std::cout << "---------------------------" << std::endl; // -- Plotting -- // Now plot the drift lines viewdrift->Plot(); // View the Field ViewField * viewfield = new ViewField(); viewfield->SetComponent(cmpAmp); viewfield->SetSensor(sensor); viewfield->SetCanvas((TCanvas*)c->cd(2)); viewfield->SetWeightingFieldRange(0.0, 10000.0); c->cd(2); viewfield->PlotContour(); // View the strip signals TCanvas * c2 = new TCanvas("c2", "c2", 1000, 20, 700, 500); ViewSignal* signalView = new ViewSignal(); signalView->SetSensor(sensor); signalView->SetCanvas(c2); if(Qstrip1 > 0)signalView->PlotSignal("Strip1"); if(Qstrip2 > 0)signalView->PlotSignal("Strip2"); if(Qstrip3 > 0)signalView->PlotSignal("Strip3"); app.Run(); return 0; }
int main(int argc, char * argv[]) { TApplication app("app", &argc, argv); plottingEngine.SetDefaultStyle(); const bool debug = true; // Load the field map. ComponentAnsys123* fm = new ComponentAnsys123(); const std::string efile = "ELIST.lis"; const std::string nfile = "NLIST.lis"; const std::string mfile = "MPLIST.lis"; const std::string sfile = "PRNSOL.lis"; fm->Initialise(efile, nfile, mfile, sfile, "mm"); fm->EnableMirrorPeriodicityX(); fm->EnableMirrorPeriodicityY(); fm->PrintRange(); // Dimensions of the GEM const double pitch = 0.014; const double kapton = 50.e-4; const double metal = 5.e-4; const double outdia = 70.e-4; const double middia = 50.e-4; const bool plotField = false; if (plotField) { ViewField* fieldView = new ViewField(); fieldView->SetComponent(fm); fieldView->SetPlane(0., -1., 0., 0., 0., 0.); fieldView->SetArea(-pitch / 2., -0.02, pitch / 2., 0.02); fieldView->SetVoltageRange(-160., 160.); TCanvas* cF = new TCanvas(); fieldView->SetCanvas(cF); fieldView->PlotContour(); } // Setup the gas. MediumMagboltz* gas = new MediumMagboltz(); gas->SetComposition("ar", 70., "co2", 30.); gas->SetTemperature(293.15); gas->SetPressure(760.); gas->EnableDebugging(); gas->Initialise(); gas->DisableDebugging(); // Set the Penning transfer efficiency. const double rPenning = 0.57; const double lambdaPenning = 0.; gas->EnablePenningTransfer(rPenning, lambdaPenning, "ar"); // Load the ion mobilities. gas->LoadIonMobility("IonMobility_Ar+_Ar.txt"); // Associate the gas with the corresponding field map material. const int nMaterials = fm->GetNumberOfMaterials(); for (int i = 0; i < nMaterials; ++i) { const double eps = fm->GetPermittivity(i); if (fabs(eps - 1.) < 1.e-3) fm->SetMedium(i, gas); } fm->PrintMaterials(); // Create the sensor. Sensor* sensor = new Sensor(); sensor->AddComponent(fm); sensor->SetArea(-5 * pitch, -5 * pitch, -0.03, 5 * pitch, 5 * pitch, 0.03); AvalancheMicroscopic* aval = new AvalancheMicroscopic(); aval->SetSensor(sensor); AvalancheMC* drift = new AvalancheMC(); drift->SetSensor(sensor); drift->SetDistanceSteps(2.e-4); const bool plotDrift = true; ViewDrift* driftView = new ViewDrift(); if (plotDrift) { driftView->SetArea(-2 * pitch, -2 * pitch, -0.02, 2 * pitch, 2 * pitch, 0.02); // Plot every 10 collisions (in microscopic tracking). aval->SetCollisionSteps(10); aval->EnablePlotting(driftView); drift->EnablePlotting(driftView); } // Histograms int nBinsGain = 100; double gmin = 0.; double gmax = 100.; TH1F* hElectrons = new TH1F("hElectrons", "Number of electrons", nBinsGain, gmin, gmax); TH1F* hIons = new TH1F("hIons", "Number of ions", nBinsGain, gmin, gmax); int nBinsChrg = 100; TH1F* hChrgE = new TH1F("hChrgE", "Electrons on plastic", nBinsChrg, -0.5e4 * kapton, 0.5e4 * kapton); TH1F* hChrgI = new TH1F("hChrgI", "Ions on plastic", nBinsChrg, -0.5e4 * kapton, 0.5e4 * kapton); double sumIonsTotal = 0.; double sumIonsDrift = 0.; double sumIonsPlastic = 0.; double sumElectronsTotal = 0.; double sumElectronsPlastic = 0.; double sumElectronsUpperMetal = 0.; double sumElectronsLowerMetal = 0.; double sumElectronsTransfer = 0.; double sumElectronsOther = 0.; const int nEvents = 10; for (int i = nEvents; i--;) { if (debug || i % 10 == 0) std::cout << i << "/" << nEvents << "\n"; // Randomize the initial position. const double smear = pitch / 2.; double x0 = -smear + RndmUniform() * smear; double y0 = -smear + RndmUniform() * smear; double z0 = 0.025; double t0 = 0.; double e0 = 0.1; aval->AvalancheElectron(x0, y0, z0, t0, e0, 0., 0., 0.); int ne = 0, ni = 0; aval->GetAvalancheSize(ne, ni); hElectrons->Fill(ne); hIons->Fill(ni); const int np = aval->GetNumberOfElectronEndpoints(); double xe1, ye1, ze1, te1, e1; double xe2, ye2, ze2, te2, e2; double xi1, yi1, zi1, ti1; double xi2, yi2, zi2, ti2; int status; for (int j = np; j--;) { aval->GetElectronEndpoint(j, xe1, ye1, ze1, te1, e1, xe2, ye2, ze2, te2, e2, status); sumElectronsTotal += 1.; if (ze2 > -kapton / 2. && ze2 < kapton / 2.) { hChrgE->Fill(ze2 * 1.e4); sumElectronsPlastic += 1.; } else if (ze2 >= kapton / 2. && ze2 <= kapton / 2. + metal) { sumElectronsUpperMetal += 1.; } else if (ze2 <= -kapton / 2. && ze2 >= -kapton / 2. - metal) { sumElectronsLowerMetal += 1.; } else if (ze2 < -kapton / 2. - metal) { sumElectronsTransfer += 1.; } else { sumElectronsOther += 1.; } drift->DriftIon(xe1, ye1, ze1, te1); drift->GetIonEndpoint(0, xi1, yi1, zi1, ti1, xi2, yi2, zi2, ti2, status); if (zi1 < 0.01) { sumIonsTotal += 1.; if (zi2 > 0.01) sumIonsDrift += 1.; } if (zi2 > -kapton / 2. && zi2 < kapton / 2.) { hChrgI->Fill(zi2 * 1.e4); sumIonsPlastic += 1.; } } } double fFeedback = 0.; if (sumIonsTotal > 0.) fFeedback = sumIonsDrift / sumIonsTotal; std::cout << "Fraction of ions drifting back: " << fFeedback << "\n"; const double neMean = hElectrons->GetMean(); std::cout << "Mean number of electrons: " << neMean << "\n"; const double niMean = hIons->GetMean(); std::cout << "Mean number of ions: " << niMean << "\n"; std::cout << "Mean number of electrons on plastic: " << sumElectronsPlastic / nEvents << "\n"; std::cout << "Mean number of ions on plastic: " << sumIonsPlastic / nEvents << "\n"; std::cout << "Electron endpoints:\n"; const double fUpperMetal = sumElectronsUpperMetal / sumElectronsTotal; const double fPlastic = sumElectronsPlastic / sumElectronsTotal; const double fLowerMetal = sumElectronsLowerMetal / sumElectronsTotal; const double fTransfer = sumElectronsTransfer / sumElectronsTotal; const double fOther = sumElectronsOther / sumElectronsTotal; std::cout << " upper metal: " << fUpperMetal * 100. << "%\n"; std::cout << " plastic: " << fPlastic * 100. << "%\n"; std::cout << " lower metal: " << fLowerMetal * 100. << "%\n"; std::cout << " transfer: " << fTransfer * 100. << "%\n"; std::cout << " other: " << fOther * 100. << "%\n"; TCanvas* cD = new TCanvas(); const bool plotGeo = true; if (plotGeo && plotDrift) { // Build the geometry in Root. TGeoManager* geoman = new TGeoManager("world", "geometry"); TGeoMaterial* matVacuum = new TGeoMaterial("Vacuum", 0, 0, 0); TGeoMedium* medVacuum = new TGeoMedium("Vacuum", 1, matVacuum); TGeoMaterial* matKapton = new TGeoMaterial("Kapton", 12, 6, 1.42); TGeoMedium* medKapton = new TGeoMedium("Kapton", 2, matKapton); TGeoMaterial* matCopper = new TGeoMaterial("Copper", 63, 29, 8.94); TGeoMedium* medCopper = new TGeoMedium("Copper", 3, matCopper); TGeoVolume* volTop = geoman->MakeBox("TOP", medVacuum, pitch, pitch, 0.02); volTop->SetVisibility(0); TGeoBBox* shpKapton = new TGeoBBox("K", pitch / 2., pitch / 2., kapton / 2.); TGeoPcon* shpHole = new TGeoPcon("H", 0., 360., 3); shpHole->DefineSection(0, -kapton / 2., 0., outdia / 2.); shpHole->DefineSection(1, 0., 0., middia / 2.); shpHole->DefineSection(2, kapton / 2., 0., outdia / 2.); TGeoCompositeShape* shpGem = new TGeoCompositeShape("G", "K - H"); TGeoVolume* volKapton = new TGeoVolume("Kapton", shpGem, medKapton); volKapton->SetLineColor(kGreen); volKapton->SetTransparency(50); TGeoBBox* shpMetal = new TGeoBBox("M", pitch / 2., pitch / 2., metal / 2.); TGeoTube* shpTube = new TGeoTube("T", 0., outdia / 2., metal / 2.); TGeoCompositeShape* shpElectrode = new TGeoCompositeShape("E", "M - T"); TGeoVolume* volElectrode = new TGeoVolume("Electrode", shpElectrode, medCopper); volElectrode->SetLineColor(kBlue); volElectrode->SetTransparency(50); TGeoVolumeAssembly* volGem = new TGeoVolumeAssembly("Gem"); const double shift = 0.5 * (metal + kapton); volGem->AddNode(volKapton, 1); volGem->AddNode(volElectrode, 2, new TGeoTranslation(0., 0., shift)); volGem->AddNode(volElectrode, 3, new TGeoTranslation(0., 0., -shift)); volTop->AddNode(volGem, 1); volTop->AddNode(volGem, 2, new TGeoTranslation(-pitch, 0., 0.)); volTop->AddNode(volGem, 3, new TGeoTranslation(+pitch, 0., 0.)); volTop->AddNode(volGem, 4, new TGeoTranslation(-pitch / 2., sqrt(3) * pitch / 2., 0.)); volTop->AddNode(volGem, 5, new TGeoTranslation(+pitch / 2., sqrt(3) * pitch / 2., 0.)); volTop->AddNode(volGem, 6, new TGeoTranslation(-pitch / 2., -sqrt(3) * pitch / 2., 0.)); volTop->AddNode(volGem, 7, new TGeoTranslation(+pitch / 2., -sqrt(3) * pitch / 2., 0.)); geoman->SetVerboseLevel(0); geoman->SetTopVolume(volTop); geoman->CloseGeometry(); geoman->CheckOverlaps(0.1e-4); geoman->SetNmeshPoints(100000); cD->cd(); geoman->GetTopVolume()->Draw("ogl"); } if (plotDrift) { driftView->SetCanvas(cD); driftView->Plot(); } const bool plotHistogram = true; if (plotHistogram) { TCanvas* cH = new TCanvas("cH", "Histograms", 800, 700); cH->Divide(2, 2); cH->cd(1); hElectrons->Draw(); cH->cd(2); hIons->Draw(); cH->cd(3); hChrgE->Draw(); cH->cd(4); hChrgI->Draw(); } app.Run(kTRUE); }