Heed/SRIM/TRIM Cross-Functionality

Howdy,

Me Again. This time is different, I promise. I actually feel pretty good about my code itself, at least from what I know about Garfield++. Just seeing if there’s something I’m missing about the interaction of TrackHeed.hh, TrackSrim.hh, and TrackTrim.hh.

So I’m working on a project to model Al-26(a,p) ionization and scattering in an isobutane filled chamber, so obviously TRIM is the main star. But I would also like to track electron drift from the interactions like there is in Heed, and to get output - or at least a plot - of the energy losses as a function of depth into the chamber - like can be done in SRIM.

So I’m having trouble getting them to all work together and/or at least talk to each other. Been tempted to try to just make a combined super subroutine of all 3 but that seems unlikely to work at best. Hilarious though.

Following is my code and the errors I get when I try to compile it if it’ll come in handy. Feels like this is more a concept question though so if it doesn’t, that’s alright.

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <ctime>

#include <math.h>

#include <TCanvas.h>
#include <TROOT.h>
#include <TApplication.h>

#include "Garfield/ViewCell.hh"
#include "Garfield/ViewDrift.hh"
#include "Garfield/ViewSignal.hh"
#include "Garfield/ViewField.hh"

#include "Garfield/ComponentAnalyticField.hh"
#include "Garfield/MediumMagboltz.hh"
#include "Garfield/Sensor.hh"
#include "Garfield/DriftLineRKF.hh"
#include "Garfield/TrackHeed.hh"
#include "Garfield/TrackSrim.hh"
#include "Garfield/TrackElectron.hh"
#include "Garfield/TrackTrim.hh"

using namespace Garfield;

int main(int argc, char* argv[]) {

    TApplication app("app", &argc, argv);

    srand(time(NULL));

    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Make a gas medium.
    MediumMagboltz gas;
    gas.LoadGasFile("isobutane.gas");

    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Make a component with analytic electric field.
    ComponentAnalyticField cmp;
    cmp.SetMedium(&gas);                  // Attach gas to cmp

    // Set parameters
    const int nElec = 19;                 // Number of electrodes - assumes CACACACA....             //(19)
    const int nWires = 50;                // Wires per electrode                                     //(50)
    const double dWire = 0.0007*2.54;     // Diameter of each wire [cm]
    const double elecspac = 0.497 * 2.54; // Spacing between electrodes [cm]
    const double wirespac = 0.2;          // Spacing between wires [cm]
    //Only have one of each Cathode and Anode not commented out at a time
    //const double vCathode = -250.;        // Potential of cathodes [V] 
    //const double vAnode = 250.;           // Potential of anodes [V] 
    const double vCathode = 0.;           // Potential of cathodes [V]
    const double vAnode = 500.;           // Potential of anodes [V]                                 //!! 500ish?
    const double chamberRad = (nWires * wirespac / 2.) +2.5;        // Radius of chamber [cm]                                  //!!
    double vWire;
    std::string label;

    // Create electrodes
    bool cathode = true;                  // First electrode is a cathode
    for (int j = 0; j < nElec; j++) {

        // Set y positions
        double ywire = elecspac * j;

        // Set potential and label
        if (cathode) {
            vWire = vCathode;
            label = "C";                  // Elements with same label are grouped together
        }
        else if (!cathode) {
            vWire = vAnode;
            label = "A";
        }

        // Create wires
        for (int i = -nWires / 2; i < nWires / 2; i++) {
            double xwire = wirespac * i + wirespac/2.;
            cmp.AddWire(xwire, ywire, dWire, vWire, label);
        }

        // Set 'cathode' for next electrode
        cathode = !cathode;               // Alternate CACACA...
    }

    // Define area (for sensor and outer casing)
    const double xmin = -chamberRad;
    const double xmax = chamberRad;
    const double ymin = -elecspac;                                                                   // !! Should actually be size of dead region
    const double ymax = nElec * elecspac;                                                            // !!?
    const double zmin = -chamberRad;
    const double zmax = chamberRad;
    const unsigned int nx = 1000;  //Should give point density, Default is 200
    const unsigned int ny = 1000;  //Should give point density, Default is 200

    // Create outer casing - model w 4 planes
    cmp.AddPlaneX(xmin, 0., "P1");
    cmp.AddPlaneY(ymin, 0., "P2");
    cmp.AddPlaneX(xmax, 0., "P3");
    cmp.AddPlaneY(ymax, 0., "P4");

    // Request calculation of the weighting field. 
    cmp.AddReadout("A");                  // Only anodes read out

    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Make a sensor.
    Sensor sensor;
    sensor.AddComponent(&cmp);                // Signal from cmp

    // Set area                               // Area the sensor considers
    sensor.SetArea(xmin, ymin, zmin, xmax, ymax, zmax);
    sensor.ClearSignal();

    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Set up TRIM
    TrackTrim Al26tr, Si29tr, H1tr;
    Al26tr.SetSensor(&sensor);
    Si29tr.SetSensor(&sensor);
    H1tr.SetSensor(&sensor);
    //const std::string file = "Aluminum-26 in No. 340 Isobutane (gas).txt";
    //tr.ReadFile(file);
    const unsigned int nIons = 10000;
    const unsigned int nSkip = 0;

    if ((!Al26tr.ReadFile("Al26EXYZ.txt", nIons, nSkip)) || (!Si29tr.ReadFile("Si29EXYZ.txt", nIons, nSkip)) || (!H1tr.ReadFile("H1EXYZ.txt", nIons, nSkip))) {
      std::cerr << "Reading TRIM EXYZ files failed.\n";
      return 1;
    }

    std::vector<double> AlSelect = { 15.696e6 , 13.362e6 , 34.725e6 , 32.583e6 };    
    /*
      Kinetic Energy -
      Ecm = 2.5 MeV , 0.72 MeV/u -> T = 18.73e6
      Ecm = 5 MeV , 1.441 MeV/u -> T = 37.46e6
      0.72 MeV/u, 1.12 um -> el = 0.162 -> AlEkin = 15.696e6
      1.441 MeV/u, 1.12 um -> el = 0.073 -> AlEkin = 34.725e6
      0.72 MeV/u, 2 um -> el = 0.287 -> AlEkin = 13.362e6
      1.441 MeV/u, 2 um -> el = 0.130 -> AlEkin = 32.583e6 
      This is a crude approach, but good enough for now.
     */

    int a, b, c;
    std::cout << "Select Beam Energy - 1.) 0.72 MeV/u or 2.) 1.441 MeV/u: ";
    std::cin >> a ;
    std::cout << "Select Mylar Thickness - 1.) 1.12 um or 2.) 2 um: ";
    std::cin >> b ;
    
    double AlEkin, SiEkin, HEkin;
    if ( (a == 1) && (b == 1) ) AlEkin = AlSelect[0];
    if ( (a == 1) && (b == 2) ) AlEkin = AlSelect[1];
    if ( (a == 2) && (b == 1) ) AlEkin = AlSelect[2];
    if ( (a == 2) && (b == 2) ) AlEkin = AlSelect[3];
    
    Al26tr.SetWorkFunction(30);                  // Work Function, just set to 30 eV
    Si29tr.SetWorkFunction(30);
    H1tr.SetWorkFunction(30);
    
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Set up drift lines
    std::cout << "RKF integration\n";
    DriftLineRKF drift;
    drift.SetMaximumStepSize(0.1 * wirespac);   // Setting Max Step Size; Experimental to get rid of "Hooks"
    drift.SetSensor(&sensor);                   // Attach to the sensor    
    
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Set canvas
    std::cout << "Set canvas for drift\n";
    TCanvas* cD = nullptr;                      // Canvas cD will be used to plot the electron drift lines
    cD = new TCanvas("cD", "", 600, 600);       // Sets stuff up but won't be plotted yet
    
    // Defined outside loop to allow it to be used after loop
    ViewCell cellView;                          // To visualise outline of cell i.e. tube, wire etc
    cellView.SetComponent(&cmp);                // Viewing cmp

    ViewDrift driftViewAl, driftViewSi, driftViewH;                        // To visualise electron drift lines

    driftViewAl.SetCanvas(cD);                    // Set canvas to cD
    driftViewAl.SetArea(xmin, ymin, xmax, ymax);  // Restrict area to be plotted
    driftViewSi.SetCanvas(cD);                    // Set canvas to cD
    driftViewSi.SetArea(xmin, ymin, xmax, ymax);  // Restrict area to be plotted
    driftViewH.SetCanvas(cD);
    driftViewH.SetArea(xmin, ymin, xmax, ymax);
    
    drift.EnablePlotting(&driftViewAl);             // Also need to specifically enable plotting drift
    drift.EnablePlotting(&driftViewSi);
    drift.EnablePlotting(&driftViewH);
    Al26tr.EnablePlotting(&driftViewAl);            // TRIM plotting 
    Si29tr.EnablePlotting(&driftViewSi);
    H1tr.EnablePlotting(&driftViewH);
    
    cellView.SetCanvas(cD);                     // cellView will be plotted on same canvas
    cellView.SetArea(xmin, ymin, xmax, ymax);
    
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Open output file
    std::ofstream output;
    output.open( "out.txt" );
    output << "# xi yi wire time \n";

    ///////////////////////////////////////////////////////////////////////////////////////////////

    std::cout << "Select Si-Excitation Energy - 1.) 0 MeV, 2.) 4 MeV, or 3.) 8 MeV: ";
    std::cin >> c ;

    std::vector<float> SiAngToE, HAngToE;

    //Silicon
    if ( (AlEkin == AlSelect[0]) && (c == 1) ) SiAngToE.insert( SiAngToE.end(), { 0.089, 0.181, 16.485, 7.02 });
    else if ( (AlEkin == AlSelect[0]) && (c == 2) ) SiAngToE.insert( SiAngToE.end(), { 0.116, 0.140, 15.15, 4.59 });
    else if ( (AlEkin == AlSelect[0]) && (c == 3) ){
      std::cout << "System Kinematics Not Possible" ;
      return 0;
    }
    else if ( (AlEkin == AlSelect[1]) && (c == 1) ) SiAngToE.insert( SiAngToE.end(), { 0.086, 0.171, 13.98, 7.02 });
    else if ( (AlEkin == AlSelect[1]) && (c == 2) ) SiAngToE.insert( SiAngToE.end(), { 0.111, 0.131, 12.67, 4.59 });
    else if ( (AlEkin == AlSelect[1]) && (c == 3) ){
      std::cout << "System Kinematics Not Possible" ;
      return 0;
    }
    else if ( (AlEkin == AlSelect[2]) && (c == 1) ) SiAngToE.insert( SiAngToE.end(), { 0.214, 0.342, 34.94, 5.69 });
    else if ( (AlEkin == AlSelect[2]) && (c == 2) ) SiAngToE.insert( SiAngToE.end(), { 0.251, 0.275, 33.35, 4.26 });
    else if ( (AlEkin == AlSelect[2]) && (c == 3) ) SiAngToE.insert( SiAngToE.end(), { 0.314, 0.072, 31.08, 1.86 });
    else if ( (AlEkin == AlSelect[3]) && (c == 1) ) SiAngToE.insert( SiAngToE.end(), { 0.216, 0.346, 32.51, 5.69 });
    else if ( (AlEkin == AlSelect[3]) && (c == 2) ) SiAngToE.insert( SiAngToE.end(), { 0.254, 0.278, 30.90, 4.26 });
    else if ( (AlEkin == AlSelect[3]) && (c == 3) ) SiAngToE.insert( SiAngToE.end(), { 0.317, 0.072, 28.60, 1.86 });
    else SiAngToE.insert( SiAngToE.end(), { 0.00, 0.00, 0.00, 0.00});

    //Proton
    if ( (AlEkin == AlSelect[0]) && (c == 1) ) HAngToE.insert( HAngToE.end(), { 0.000609, 0.00801, 8.416, 18.06 });
    else if ( (AlEkin == AlSelect[0]) && (c == 2) ) HAngToE.insert( HAngToE.end(), { 0.000498, 0.00232, 3.918, 18.06 });
    else if ( (AlEkin == AlSelect[0]) && (c == 3) ){
      std::cout << "System Kinematics Not Possible" ;
      return 0;
    }
    else if ( (AlEkin == AlSelect[1]) && (c == 1) ) HAngToE.insert( HAngToE.end(), { 0.000504, 0.00815, 6.142, 18.06 });
    else if ( (AlEkin == AlSelect[1]) && (c == 2) ) HAngToE.insert( HAngToE.end(), { 0.000363, 0.00193, 2.421, 18.06 });
    else if ( (AlEkin == AlSelect[1]) && (c == 3) ){
      std::cout << "System Kinematics Not Possible" ;
      return 0;
    }
    else if ( (AlEkin == AlSelect[2]) && (c == 1) ) HAngToE.insert( HAngToE.end(), { 0.00141, 0.00606, 13.567, 18.06 });
    else if ( (AlEkin == AlSelect[2]) && (c == 2) ) HAngToE.insert( HAngToE.end(), { 0.00110, 0.00694, 8.516, 18.06 });
    else if ( (AlEkin == AlSelect[2]) && (c == 3) ) HAngToE.insert( HAngToE.end(), { 0.000850, 0.00193, 3.314, 18.06 });
    else if ( (AlEkin == AlSelect[3]) && (c == 1) ) HAngToE.insert( HAngToE.end(), { 0.00133, 0.00649, 11.06, 18.06 });
    else if ( (AlEkin == AlSelect[3]) && (c == 2) ) HAngToE.insert( HAngToE.end(), { 0.000927, 0.00725, 6.230, 18.06 });
    else if ( (AlEkin == AlSelect[3]) && (c == 3) ) HAngToE.insert( HAngToE.end(), { 0.000606, 0.00159, 1.973, 18.06 });
    else HAngToE.insert( HAngToE.end(), { 0.00, 0.00, 0.00, 0.00});    
    
    double thetaSi, thetaH;

      ///////////////////////////////////////////////////////////////////                                                    
      //Silicon-29 Ion Angular Spread and Chamber Energy
      //SiEkin = c + b*theta - a*theta*theta, theta_limit = d
      //(a, b, c, d) calculated using LISE++ and Catkin
      //Chamber maxes at theta = 18.06 degrees
      //////////////////////////////////////////////////////////////////

      Al26tr.SetKineticEnergy(AlEkin);
      
      // Main loop
      int n1 = 100; //100 Electrons per row
      int n2 = 100; //100 rows
      for (int j = 0; j < n2; ++j) {                  // Each row                              //~20mins per row of 200 electrons
	for (int k = 0; k < n1+1; ++k) {              // Each electron in the row
	  
	  // Simulate a track.
	  const double xt = ((xmin + xmax) / 2) + (100*j + k) * (xmax - xmin) / 2500000; //y-track; 2500000 goes to 1 mm width
	  const double yt = ymin; //y-track
	  Al26tr.NewTrack(xt, yt, 0., 0., 0., 1., 0.); //y-track;
	  
	  thetaSi = -1 * SiAngToE[3] + (100*j + k) * SiAngToE[3] / 5000;
	  SiEkin = (SiAngToE[2] + SiAngToE[1]*abs(thetaSi) - SiAngToE[0]*thetaSi*thetaSi) * 1e6;
	  Si29tr.SetKineticEnergy(SiEkin);
	  Si29tr.NewTrack(xt + 23*tan( thetaSi * M_PI / 180), yt, 0., 0., sin( thetaSi * M_PI / 180),cos( thetaSi * M_PI / 180), 0.);

	  if ( (j % 10) == 1) {
	    thetaH = -1 * HAngToE[3] + (100*j) * HAngToE[3] / 5000; //got rid of k and added an if loop to clean up the view
	    HEkin = (HAngToE[2] - HAngToE[1]*abs(thetaH) - HAngToE[0]*thetaH*thetaH) * 1e6;
	    H1tr.SetKineticEnergy(HEkin);
	    H1tr.NewTrack(xt + 23*tan( thetaH * M_PI / 180), yt, 0., 0., sin( thetaH * M_PI / 180), cos( thetaH * M_PI / 180), 0.);
	  }

	  ////////////////////////////////////////////////////////////////////////////////
	  //Test of Track Output
	  //Loop over the clusters.               
	  double xc, yc, zc, tc, ec, extra;
	  int nc;

	  while (Al26tr.GetCluster(xc, yc, zc, tc, nc, ec, extra) && Si29tr.GetCluster(xc, yc, zc, tc, nc, ec, extra) && H1tr.GetCluster(xc, yc, zc, tc, nc, ec, extra)) {
	    for (int l = 0; l < nc; ++l) {
	      double xe, ye, ze, te, ee;
	      double dx, dy, dz;
	      Al26tr.GetElectron(l, xe, ye, ze, te, ee, dx, dy, dz);
	      Si29tr.GetElectron(l, xe, ye, ze, te, ee, dx, dy, dz);
	      H1tr.GetElectron(l, xe, ye, ze, te, ee, dx, dy, dz);
	      drift.DriftElectron(xe, ye, ze, te);
	    }
	  }
	  output << "\n \n";
	}
      }

      Al26tr.PlotEnergyLoss();
      Si29tr.PlotEnergyLoss();
      H1tr.PlotEnergyLoss();
      
      driftViewSi.SetColourTracks(kViolet);
      driftViewH.SetColourTracks(kBlue);
      ///////////////////////////////////////////////////////////////////////////////
      // Close output file
      output.close();

      // Plot drift
      std::cout << "Plot drift\n";
      cD->Clear();
      cellView.Plot2d();                        // Plot2d to plot cell (set up earlier)
      constexpr bool twod = true;
      constexpr bool drawaxis = false;
      driftViewAl.Plot(twod, drawaxis); // Plot to plot drift lines (set up earlier) - NOTE: when drawaxis is true, cell gets overwritten... FIX(?)
      driftViewSi.Plot(twod, drawaxis); 
      driftViewH.Plot(twod, drawaxis);

    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Set up fieldView to plot the electric potential
    std::cout << "Set up fieldView\n";
    TCanvas* cF = nullptr;                                            // Canvas cF will be used to plot potential/field
    ViewField fieldView;                                              // ViewField object manages this
    cF = new TCanvas("cF", "", 700, 600);                             // Doesn't rely on the main simulation loop so we can set up then plot immediately
    fieldView.SetCanvas(cF);
    fieldView.SetSensor(&sensor);                                     // ViewField gets the field/potentials from sensor (sensor is an interface between a lot of things)
    fieldView.SetPlane(0., 0., 1., 0., 0., 0.);                       // Set plane to plot - xy plane
    cellView.SetCanvas(cF);                                           // Also plot cell on same plot (this is why cellView was set up BEFORE main loop)
    cellView.SetPlane(0., 0., 1., 0., 0., 0.);                        // Set plane to plot - xy plane
    cellView.SetArea(xmin, ymin, xmax, ymax);                         // Restrict area (so that it lines up with area set by sensor)

    // Plot field
    std::cout << "Plot field\n";
    //fieldView.Plot("e","CONT2");     // ARR looks promising but too many cells
    fieldView.PlotContour("v");                                       // Plot "e" for field magnitude, "v" for potential (see table 4.2, pg55)
    cellView.Plot2d();                                                // Also plot cell

    ///////////////////////////////////////////////////////////////////////////////////////////////

    app.Run(kTRUE);
}

(base) jas@jason-VirtualBox:/home/jason/garfieldpp/Examples/IsobutaneGrid/build$ make
Scanning dependencies of target grid
[ 50%] Building CXX object CMakeFiles/grid.dir/grid.C.o
/home/jason/garfieldpp/Examples/IsobutaneGrid/grid.C: In function ‘int main(int, char**)’:
/home/jason/garfieldpp/Examples/IsobutaneGrid/grid.C:293:22: error: ‘class Garfield::TrackTrim’ has no member named ‘GetElectron’; did you mean ‘bool Garfield::Track::m_isElectron’? (not accessible from this context)
  293 |               Al26tr.GetElectron(l, xe, ye, ze, te, ee, dx, dy, dz);
      |                      ^~~~~~~~~~~
In file included from /home/jason/garfieldpp/install/include/Garfield/TrackHeed.hh:8,
                 from /home/jason/garfieldpp/Examples/IsobutaneGrid/grid.C:21:
/home/jason/garfieldpp/install/include/Garfield/Track.hh:109:8: note: declared protected here
  109 |   bool m_isElectron = false;
      |        ^~~~~~~~~~~~
/home/jason/garfieldpp/Examples/IsobutaneGrid/grid.C:294:22: error: ‘class Garfield::TrackTrim’ has no member named ‘GetElectron’; did you mean ‘bool Garfield::Track::m_isElectron’? (not accessible from this context)
  294 |               Si29tr.GetElectron(l, xe, ye, ze, te, ee, dx, dy, dz);
      |                      ^~~~~~~~~~~
In file included from /home/jason/garfieldpp/install/include/Garfield/TrackHeed.hh:8,
                 from /home/jason/garfieldpp/Examples/IsobutaneGrid/grid.C:21:
/home/jason/garfieldpp/install/include/Garfield/Track.hh:109:8: note: declared protected here
  109 |   bool m_isElectron = false;
      |        ^~~~~~~~~~~~
/home/jason/garfieldpp/Examples/IsobutaneGrid/grid.C:295:20: error: ‘class Garfield::TrackTrim’ has no member named ‘GetElectron’; did you mean ‘bool Garfield::Track::m_isElectron’? (not accessible from this context)
  295 |               H1tr.GetElectron(l, xe, ye, ze, te, ee, dx, dy, dz);
      |                    ^~~~~~~~~~~
In file included from /home/jason/garfieldpp/install/include/Garfield/TrackHeed.hh:8,
                 from /home/jason/garfieldpp/Examples/IsobutaneGrid/grid.C:21:
/home/jason/garfieldpp/install/include/Garfield/Track.hh:109:8: note: declared protected here
  109 |   bool m_isElectron = false;
      |        ^~~~~~~~~~~~
/home/jason/garfieldpp/Examples/IsobutaneGrid/grid.C:303:14: error: ‘class Garfield::TrackTrim’ has no member named ‘PlotEnergyLoss’
  303 |       Al26tr.PlotEnergyLoss();
      |              ^~~~~~~~~~~~~~
/home/jason/garfieldpp/Examples/IsobutaneGrid/grid.C:304:14: error: ‘class Garfield::TrackTrim’ has no member named ‘PlotEnergyLoss’
  304 |       Si29tr.PlotEnergyLoss();
      |              ^~~~~~~~~~~~~~
/home/jason/garfieldpp/Examples/IsobutaneGrid/grid.C:305:12: error: ‘class Garfield::TrackTrim’ has no member named ‘PlotEnergyLoss’
  305 |       H1tr.PlotEnergyLoss();
      |            ^~~~~~~~~~~~~~
make[2]: *** [CMakeFiles/grid.dir/build.make:82: CMakeFiles/grid.dir/grid.C.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:95: CMakeFiles/grid.dir/all] Error 2
make: *** [Makefile:103: all] Error 2

I guess @hschindl can help.

Hi Jason,

unlike TrackHeed, TrackTrim and TrackSrim don’t have a function GetElectron. All the nc electrons in a “cluster” are assumed to be created at the same position (the cluster position).
There is another, related simplification that you can make in your program. Since - with DriftLineRKF - electrons starting from the same initial position always follow the same path, you can call “DriftElectron” just once for every cluster and set the “signal scaling factor” to nc beforehand (such that you effectively transport nc electrons at the same time).

Does that help?

Real life stuff popped up, sorry for the delay.

Getting rid of the calls to GetElectrons and PlotEnergyLoss does seem to get the program working again but it takes much longer. I think this means the DriftElectron call does work but my “signal scaling factor” isn’t so it’s plotting everything. Naturally extending the run time.

I’ll keep you updated.

Edit:- Extending the run time by a lot.

Edit 2:- Alright, seems to work once I get used to the longer times. Having trouble actually getting the electrons to show up though. New Code Follows.

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <ctime>

#include <math.h>

#include <TCanvas.h>
#include <TROOT.h>
#include <TApplication.h>

#include "Garfield/ViewCell.hh"
#include "Garfield/ViewDrift.hh"
#include "Garfield/ViewSignal.hh"
#include "Garfield/ViewField.hh"

#include "Garfield/ComponentAnalyticField.hh"
#include "Garfield/MediumMagboltz.hh"
#include "Garfield/Sensor.hh"
#include "Garfield/DriftLineRKF.hh"
#include "Garfield/TrackHeed.hh"
#include "Garfield/TrackSrim.hh"
#include "Garfield/TrackElectron.hh"
#include "Garfield/TrackTrim.hh"

using namespace Garfield;

int main(int argc, char* argv[]) {

    TApplication app("app", &argc, argv);

    srand(time(NULL));

    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Make a gas medium.
    MediumMagboltz gas;
    gas.LoadGasFile("isobutane.gas");

    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Make a component with analytic electric field.
    ComponentAnalyticField cmp;
    cmp.SetMedium(&gas);                  // Attach gas to cmp

    // Set parameters
    const int nElec = 19;                 // Number of electrodes - assumes CACACACA....             //(19)
    const int nWires = 50;                // Wires per electrode                                     //(50)
    const double dWire = 0.0007*2.54;     // Diameter of each wire [cm]
    const double elecspac = 0.497 * 2.54; // Spacing between electrodes [cm]
    const double wirespac = 0.2;          // Spacing between wires [cm]
    //Only have one of each Cathode and Anode not commented out at a time
    //const double vCathode = -250.;        // Potential of cathodes [V] 
    //const double vAnode = 250.;           // Potential of anodes [V] 
    const double vCathode = 0.;           // Potential of cathodes [V]
    const double vAnode = 500.;           // Potential of anodes [V]                                 //!! 500ish?
    const double chamberRad = (nWires * wirespac / 2.) +2.5;        // Radius of chamber [cm]                                  //!!
    double vWire;
    std::string label;

    // Create electrodes
    bool cathode = true;                  // First electrode is a cathode
    for (int j = 0; j < nElec; j++) {

        // Set y positions
        double ywire = elecspac * j;

        // Set potential and label
        if (cathode) {
            vWire = vCathode;
            label = "C";                  // Elements with same label are grouped together
        }
        else if (!cathode) {
            vWire = vAnode;
            label = "A";
        }

        // Create wires
        for (int i = -nWires / 2; i < nWires / 2; i++) {
            double xwire = wirespac * i + wirespac/2.;
            cmp.AddWire(xwire, ywire, dWire, vWire, label);
        }

        // Set 'cathode' for next electrode
        cathode = !cathode;               // Alternate CACACA...
    }

    // Define area (for sensor and outer casing)
    const double xmin = -chamberRad;
    const double xmax = chamberRad;
    const double ymin = -elecspac;                                                                   // !! Should actually be size of dead region
    const double ymax = nElec * elecspac;                                                            // !!?
    const double zmin = -chamberRad;
    const double zmax = chamberRad;
    const unsigned int nx = 1000;  //Should give point density, Default is 200
    const unsigned int ny = 1000;  //Should give point density, Default is 200

    // Create outer casing - model w 4 planes
    cmp.AddPlaneX(xmin, 0., "P1");
    cmp.AddPlaneY(ymin, 0., "P2");
    cmp.AddPlaneX(xmax, 0., "P3");
    cmp.AddPlaneY(ymax, 0., "P4");

    // Request calculation of the weighting field. 
    cmp.AddReadout("A");                  // Only anodes read out

    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Make a sensor.
    Sensor sensor;
    sensor.AddComponent(&cmp);                // Signal from cmp

    // Set area                               // Area the sensor considers
    sensor.SetArea(xmin, ymin, zmin, xmax, ymax, zmax);
    sensor.ClearSignal();

    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Set up TRIM
    TrackTrim Al26tr, Si29tr, H1tr;
    Al26tr.SetSensor(&sensor);
    Si29tr.SetSensor(&sensor);
    H1tr.SetSensor(&sensor);
    //const std::string file = "Aluminum-26 in No. 340 Isobutane (gas).txt";
    //tr.ReadFile(file);
    const unsigned int nIons = 10000;
    const unsigned int nSkip = 0;

    if ((!Al26tr.ReadFile("Al26EXYZ.txt", nIons, nSkip)) || (!Si29tr.ReadFile("Si29EXYZ.txt", nIons, nSkip)) || (!H1tr.ReadFile("H1EXYZ.txt", nIons, nSkip))) {
      std::cerr << "Reading TRIM EXYZ files failed.\n";
      return 1;
    }

    std::vector<double> AlSelect = { 15.696e6 , 13.362e6 , 34.725e6 , 32.583e6 };    
    /*
      Kinetic Energy -
      Ecm = 2.5 MeV , 0.72 MeV/u -> T = 18.73e6
      Ecm = 5 MeV , 1.441 MeV/u -> T = 37.46e6
      0.72 MeV/u, 1.12 um -> el = 0.162 -> AlEkin = 15.696e6
      1.441 MeV/u, 1.12 um -> el = 0.073 -> AlEkin = 34.725e6
      0.72 MeV/u, 2 um -> el = 0.287 -> AlEkin = 13.362e6
      1.441 MeV/u, 2 um -> el = 0.130 -> AlEkin = 32.583e6 
      This is a crude approach, but good enough for now.
     */

    int a, b, c;
    std::cout << "Select Beam Energy - 1.) 0.72 MeV/u or 2.) 1.441 MeV/u: ";
    std::cin >> a ;
    std::cout << "Select Mylar Thickness - 1.) 1.12 um or 2.) 2 um: ";
    std::cin >> b ;
    
    double AlEkin, SiEkin, HEkin;
    if ( (a == 1) && (b == 1) ) AlEkin = AlSelect[0];
    if ( (a == 1) && (b == 2) ) AlEkin = AlSelect[1];
    if ( (a == 2) && (b == 1) ) AlEkin = AlSelect[2];
    if ( (a == 2) && (b == 2) ) AlEkin = AlSelect[3];
    
    Al26tr.SetWorkFunction(30);                  // Work Function, just set to 30 eV
    Si29tr.SetWorkFunction(30);
    H1tr.SetWorkFunction(30);
    
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Set up drift lines
    std::cout << "RKF integration\n";
    DriftLineRKF drift;
    drift.SetMaximumStepSize(0.1 * wirespac);   // Setting Max Step Size; Experimental to get rid of "Hooks"
    drift.SetSensor(&sensor);                   // Attach to the sensor    
    
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Set canvas
    std::cout << "Set canvas for drift\n";
    TCanvas* cD = nullptr;                      // Canvas cD will be used to plot the electron drift lines
    cD = new TCanvas("cD", "", 600, 600);       // Sets stuff up but won't be plotted yet
    
    // Defined outside loop to allow it to be used after loop
    ViewCell cellView;                          // To visualise outline of cell i.e. tube, wire etc
    cellView.SetComponent(&cmp);                // Viewing cmp

    ViewDrift driftViewAl, driftViewSi, driftViewH;                        // To visualise electron drift lines

    driftViewAl.SetCanvas(cD);                    // Set canvas to cD
    driftViewAl.SetArea(xmin, ymin, xmax, ymax);  // Restrict area to be plotted
    driftViewSi.SetCanvas(cD);                    // Set canvas to cD
    driftViewSi.SetArea(xmin, ymin, xmax, ymax);  // Restrict area to be plotted
    driftViewH.SetCanvas(cD);
    driftViewH.SetArea(xmin, ymin, xmax, ymax);
    
    drift.EnablePlotting(&driftViewAl);             // Also need to specifically enable plotting drift
    drift.EnablePlotting(&driftViewSi);
    drift.EnablePlotting(&driftViewH);
    Al26tr.EnablePlotting(&driftViewAl);            // TRIM plotting 
    Si29tr.EnablePlotting(&driftViewSi);
    H1tr.EnablePlotting(&driftViewH);
    
    cellView.SetCanvas(cD);                     // cellView will be plotted on same canvas
    cellView.SetArea(xmin, ymin, xmax, ymax);
    
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Open output file
    std::ofstream output;
    output.open( "out.txt" );
    output << "# xi yi wire time \n";

    ///////////////////////////////////////////////////////////////////////////////////////////////

    std::cout << "Select Si-Excitation Energy - 1.) 0 MeV, 2.) 4 MeV, or 3.) 8 MeV: ";
    std::cin >> c ;

    std::vector<float> SiAngToE, HAngToE;

    //Silicon
    if ( (AlEkin == AlSelect[0]) && (c == 1) ) SiAngToE.insert( SiAngToE.end(), { 0.089, 0.181, 16.485, 7.02 });
    else if ( (AlEkin == AlSelect[0]) && (c == 2) ) SiAngToE.insert( SiAngToE.end(), { 0.116, 0.140, 15.15, 4.59 });
    else if ( (AlEkin == AlSelect[0]) && (c == 3) ){
      std::cout << "System Kinematics Not Possible" ;
      return 0;
    }
    else if ( (AlEkin == AlSelect[1]) && (c == 1) ) SiAngToE.insert( SiAngToE.end(), { 0.086, 0.171, 13.98, 7.02 });
    else if ( (AlEkin == AlSelect[1]) && (c == 2) ) SiAngToE.insert( SiAngToE.end(), { 0.111, 0.131, 12.67, 4.59 });
    else if ( (AlEkin == AlSelect[1]) && (c == 3) ){
      std::cout << "System Kinematics Not Possible" ;
      return 0;
    }
    else if ( (AlEkin == AlSelect[2]) && (c == 1) ) SiAngToE.insert( SiAngToE.end(), { 0.214, 0.342, 34.94, 5.69 });
    else if ( (AlEkin == AlSelect[2]) && (c == 2) ) SiAngToE.insert( SiAngToE.end(), { 0.251, 0.275, 33.35, 4.26 });
    else if ( (AlEkin == AlSelect[2]) && (c == 3) ) SiAngToE.insert( SiAngToE.end(), { 0.314, 0.072, 31.08, 1.86 });
    else if ( (AlEkin == AlSelect[3]) && (c == 1) ) SiAngToE.insert( SiAngToE.end(), { 0.216, 0.346, 32.51, 5.69 });
    else if ( (AlEkin == AlSelect[3]) && (c == 2) ) SiAngToE.insert( SiAngToE.end(), { 0.254, 0.278, 30.90, 4.26 });
    else if ( (AlEkin == AlSelect[3]) && (c == 3) ) SiAngToE.insert( SiAngToE.end(), { 0.317, 0.072, 28.60, 1.86 });
    else SiAngToE.insert( SiAngToE.end(), { 0.00, 0.00, 0.00, 0.00});

    //Proton
    if ( (AlEkin == AlSelect[0]) && (c == 1) ) HAngToE.insert( HAngToE.end(), { 0.000609, 0.00801, 8.416, 18.06 });
    else if ( (AlEkin == AlSelect[0]) && (c == 2) ) HAngToE.insert( HAngToE.end(), { 0.000498, 0.00232, 3.918, 18.06 });
    else if ( (AlEkin == AlSelect[0]) && (c == 3) ){
      std::cout << "System Kinematics Not Possible" ;
      return 0;
    }
    else if ( (AlEkin == AlSelect[1]) && (c == 1) ) HAngToE.insert( HAngToE.end(), { 0.000504, 0.00815, 6.142, 18.06 });
    else if ( (AlEkin == AlSelect[1]) && (c == 2) ) HAngToE.insert( HAngToE.end(), { 0.000363, 0.00193, 2.421, 18.06 });
    else if ( (AlEkin == AlSelect[1]) && (c == 3) ){
      std::cout << "System Kinematics Not Possible" ;
      return 0;
    }
    else if ( (AlEkin == AlSelect[2]) && (c == 1) ) HAngToE.insert( HAngToE.end(), { 0.00141, 0.00606, 13.567, 18.06 });
    else if ( (AlEkin == AlSelect[2]) && (c == 2) ) HAngToE.insert( HAngToE.end(), { 0.00110, 0.00694, 8.516, 18.06 });
    else if ( (AlEkin == AlSelect[2]) && (c == 3) ) HAngToE.insert( HAngToE.end(), { 0.000850, 0.00193, 3.314, 18.06 });
    else if ( (AlEkin == AlSelect[3]) && (c == 1) ) HAngToE.insert( HAngToE.end(), { 0.00133, 0.00649, 11.06, 18.06 });
    else if ( (AlEkin == AlSelect[3]) && (c == 2) ) HAngToE.insert( HAngToE.end(), { 0.000927, 0.00725, 6.230, 18.06 });
    else if ( (AlEkin == AlSelect[3]) && (c == 3) ) HAngToE.insert( HAngToE.end(), { 0.000606, 0.00159, 1.973, 18.06 });
    else HAngToE.insert( HAngToE.end(), { 0.00, 0.00, 0.00, 0.00});    
    
    double thetaSi, thetaH;

      ///////////////////////////////////////////////////////////////////                                                    
      //Silicon-29 Ion Angular Spread and Chamber Energy
      //SiEkin = c + b*theta - a*theta*theta, theta_limit = d
      //(a, b, c, d) calculated using LISE++ and Catkin
      //Chamber maxes at theta = 18.06 degrees
      //////////////////////////////////////////////////////////////////

      Al26tr.SetKineticEnergy(AlEkin);
      
      // Main loop
      int n1 = 100; //100 Electrons per row
      int n2 = 100; //100 rows
      for (int j = 0; j < n2; ++j) {                  // Each row                              //~20mins per row of 200 electrons
	for (int k = 0; k < n1+1; ++k) {              // Each electron in the row
	  
	  // Simulate a track.
	  const double xt = ((xmin + xmax) / 2) + (100*j + k) * (xmax - xmin) / 2500000; //y-track; 2500000 goes to 1 mm width
	  const double yt = ymin; //y-track
	  Al26tr.NewTrack(xt, yt, 0., 0., 0., 1., 0.); //y-track;
	  
	  thetaSi = -1 * SiAngToE[3] + (100*j + k) * SiAngToE[3] / 5000;
	  SiEkin = (SiAngToE[2] + SiAngToE[1]*abs(thetaSi) - SiAngToE[0]*thetaSi*thetaSi) * 1e6;
	  Si29tr.SetKineticEnergy(SiEkin);
	  Si29tr.NewTrack(xt + 23*tan( thetaSi * M_PI / 180), yt, 0., 0., sin( thetaSi * M_PI / 180),cos( thetaSi * M_PI / 180), 0.);

	  if ( (j % 10) == 1) {
	    thetaH = -1 * HAngToE[3] + (100*j) * HAngToE[3] / 5000; //got rid of k and added an if loop to clean up the view
	    HEkin = (HAngToE[2] - HAngToE[1]*abs(thetaH) - HAngToE[0]*thetaH*thetaH) * 1e6;
	    H1tr.SetKineticEnergy(HEkin);
	    H1tr.NewTrack(xt + 23*tan( thetaH * M_PI / 180), yt, 0., 0., sin( thetaH * M_PI / 180), cos( thetaH * M_PI / 180), 0.);
	  }

	  ////////////////////////////////////////////////////////////////////////////////
	  //Test of Track Output
	  //Loop over the clusters.               
	  double xc, yc, zc, tc, ec, extra;
	  int nc;
	  
	  while (Al26tr.GetCluster(xc, yc, zc, tc, nc, ec, extra) && Si29tr.GetCluster(xc, yc, zc, tc, nc, ec, extra) && H1tr.GetCluster(xc, yc, zc, tc, nc, ec, extra)) {
	    double xe, ye, ze, te;
	    drift.DriftElectron(xe, ye, ze, te);
	  }
	  output << "\n \n";
	}
	std::cout << j << "%" << std::endl;
      }
      
      driftViewSi.SetColourTracks(kViolet);
      driftViewH.SetColourTracks(kBlue);
      ///////////////////////////////////////////////////////////////////////////////
      // Close output file
      output.close();

      // Plot drift
      std::cout << "Plot drift\n";
      cD->Clear();
      cellView.Plot2d();                        // Plot2d to plot cell (set up earlier)
      constexpr bool twod = true;
      constexpr bool drawaxis = false;
      driftViewAl.Plot(twod, drawaxis); // Plot to plot drift lines (set up earlier) - NOTE: when drawaxis is true, cell gets overwritten... FIX(?)
      driftViewSi.Plot(twod, drawaxis); 
      driftViewH.Plot(twod, drawaxis);

    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Set up fieldView to plot the electric potential
    std::cout << "Set up fieldView\n";
    TCanvas* cF = nullptr;                                            // Canvas cF will be used to plot potential/field
    ViewField fieldView;                                              // ViewField object manages this
    cF = new TCanvas("cF", "", 700, 600);                             // Doesn't rely on the main simulation loop so we can set up then plot immediately
    fieldView.SetCanvas(cF);
    fieldView.SetSensor(&sensor);                                     // ViewField gets the field/potentials from sensor (sensor is an interface between a lot of things)
    fieldView.SetPlane(0., 0., 1., 0., 0., 0.);                       // Set plane to plot - xy plane
    cellView.SetCanvas(cF);                                           // Also plot cell on same plot (this is why cellView was set up BEFORE main loop)
    cellView.SetPlane(0., 0., 1., 0., 0., 0.);                        // Set plane to plot - xy plane
    cellView.SetArea(xmin, ymin, xmax, ymax);                         // Restrict area (so that it lines up with area set by sensor)

    // Plot field
    std::cout << "Plot field\n";
    //fieldView.Plot("e","CONT2");     // ARR looks promising but too many cells
    fieldView.PlotContour("v");                                       // Plot "e" for field magnitude, "v" for potential (see table 4.2, pg55)
    cellView.Plot2d();                                                // Also plot cell

    ///////////////////////////////////////////////////////////////////////////////////////////////

    app.Run(kTRUE);
}

Hello Heinrich,

This is the dumbest thing but I think it’s all working, and it definitely acts like it’s calculating the electron drifts properly. It’s just not putting them up on my plot. I’m probably missing something really obvious. :stuck_out_tongue:

-Jason

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.