Project

General

Profile

Writing a Geometry

NOTE: See utility functions for dealing with visual attributes.

See https://muon.npl.washington.edu/elog/g2/Software/49 for more information on the Geometry Service and writing your own Geometry class.

One of the problems with C++ is that there can be a lot of tedious typing. That's certainly true for a Geometry class where you have to write:

  • The member data declarations * The initialization list in the constructor * The print method * And finally the actual FCL file

If you have a lot of items in your geometry, this can lead to a lot of typing. To make your life easier, there is now a script in artg4/scripts that will do much of the work for you.

Example

You write the FCL file and the script will figure out the rest of the C++. For example, here is a FCL file for the Vacuum Chamber geometry

// VacuumChamber geometry
//
// Values are in mm unless otherwise specified

BEGIN_PROLOG
vac_geom : {

  // General values ---
  inflectorExtensionValue : 750
  topBottomWall: 78.65
  outerWallThickness: 9.65
  torus_rmin: 277 // in
  torus_rmax: [284.75, 284.37] // in
  torus_sphi: 0   // deg
  torus_dphi: 30  // deg

  // Outer scallop ---
  phi_a: 12.83  // deg
  phi_b: 2.68   // deg
  wallR: [275.542, 268.261, 276.624, 284.750] // in
  wallPhi: [0., 11.84, 11.96, 0]  // deg
  vacR: [275.916, 268.645, 276.707, 284.370] // in
  vacPhi: [0, 11.80, 11.92, 0.0] // deg

  // Inner scallop ---
  phi_q_subtractor: 15 // deg

  // If true then use phi_q; otherwise phi_q is a calculation
  set_phi_q: false
  phi_q:  0 // deg

  xz: [2.0, 2.0]  // in
  ext: [1.0, 1.0] // in

  // Tracker ---
  tracker_sphi:  0.1 // deg
  tracker_dphi: 0.01 // deg

  // Turn counter ---
  turn_sphi: 24   // deg
  turn_dphi: 0.01 // deg  
}
END_PROLOG

In this geometry you see that some items are in mm (the default), some have other units, like inches or degree, some are vectors of values (like vacR), and there's a boolean value too (set_phi_q).

If you run the script artg4/scripts/geomTyper.py, it will print out code snipits to put in to your geometry's hh and cc files.

mac-118277:vac lyon$ python $SRCS/artg4/scripts/geomTyper.py vac.fcl
-- CUT AND PASTE BELOW into class member data in header file --
const double inflectorExtensionValue;
const double topBottomWall;
const double outerWallThickness;
const double torus_rmin;
std::vector<double> torus_rmax;
const double torus_sphi;
const double torus_dphi;
const double phi_a;
const double phi_b;
std::vector<double> wallR;
std::vector<double> wallPhi;
std::vector<double> vacR;
std::vector<double> vacPhi;
const double phi_q_subtractor;
const bool set_phi_q;
const double phi_q;
std::vector<double> xz;
std::vector<double> ext;
const double tracker_sphi;
const double tracker_dphi;
const double turn_sphi;
const double turn_dphi;
-- CUT AND PASTE BELOW into constructor initialization list --
inflectorExtensionValue( p.get<double>("inflectorExtensionValue") * mm),
topBottomWall( p.get<double>("topBottomWall") * mm),
outerWallThickness( p.get<double>("outerWallThickness") * mm),
torus_rmin( p.get<double>("torus_rmin") * in),
torus_rmax( p.get<std::vector<double>>("torus_rmax") ),
torus_sphi( p.get<double>("torus_sphi") * deg),
torus_dphi( p.get<double>("torus_dphi") * deg),
phi_a( p.get<double>("phi_a") * deg),
phi_b( p.get<double>("phi_b") * deg),
wallR( p.get<std::vector<double>>("wallR") ),
wallPhi( p.get<std::vector<double>>("wallPhi") ),
vacR( p.get<std::vector<double>>("vacR") ),
vacPhi( p.get<std::vector<double>>("vacPhi") ),
phi_q_subtractor( p.get<double>("phi_q_subtractor") * deg),
set_phi_q( p.get<bool>("set_phi_q") ),
phi_q( p.get<double>("phi_q") * deg),
xz( p.get<std::vector<double>>("xz") ),
ext( p.get<std::vector<double>>("ext") ),
tracker_sphi( p.get<double>("tracker_sphi") * deg),
tracker_dphi( p.get<double>("tracker_dphi") * deg),
turn_sphi( p.get<double>("turn_sphi") * deg),
turn_dphi( p.get<double>("turn_dphi") * deg),
-- CUT AND PASTE BELOW into constructor body --
for (auto& entry : torus_rmax ) { entry *= in; }
for (auto& entry : wallR ) { entry *= in; }
for (auto& entry : wallPhi ) { entry *= deg; }
for (auto& entry : vacR ) { entry *= in; }
for (auto& entry : vacPhi ) { entry *= deg; }
for (auto& entry : xz ) { entry *= in; }
for (auto& entry : ext ) { entry *= in; }
-- CUT AND PASTE BELOW into the print method --
-- be sure to #include <sstream> --
std::ostringstream oss;
oss << "  inflectorExtensionValue=" << inflectorExtensionValue << "\n";
oss << "  topBottomWall=" << topBottomWall << "\n";
oss << "  outerWallThickness=" << outerWallThickness << "\n";
oss << "  torus_rmin=" << torus_rmin << "\n";
oss << "  torus_rmax= "; for (auto entry : torus_rmax) { oss << " " << entry; }; oss << "\n";
oss << "  torus_sphi=" << torus_sphi << "\n";
oss << "  torus_dphi=" << torus_dphi << "\n";
oss << "  phi_a=" << phi_a << "\n";
oss << "  phi_b=" << phi_b << "\n";
oss << "  wallR= "; for (auto entry : wallR) { oss << " " << entry; }; oss << "\n";
oss << "  wallPhi= "; for (auto entry : wallPhi) { oss << " " << entry; }; oss << "\n";
oss << "  vacR= "; for (auto entry : vacR) { oss << " " << entry; }; oss << "\n";
oss << "  vacPhi= "; for (auto entry : vacPhi) { oss << " " << entry; }; oss << "\n";
oss << "  phi_q_subtractor=" << phi_q_subtractor << "\n";
oss << "  set_phi_q=" << set_phi_q << "\n";
oss << "  phi_q=" << phi_q << "\n";
oss << "  xz= "; for (auto entry : xz) { oss << " " << entry; }; oss << "\n";
oss << "  ext= "; for (auto entry : ext) { oss << " " << entry; }; oss << "\n";
oss << "  tracker_sphi=" << tracker_sphi << "\n";
oss << "  tracker_dphi=" << tracker_dphi << "\n";
oss << "  turn_sphi=" << turn_sphi << "\n";
oss << "  turn_dphi=" << turn_dphi << "\n";
mf::LogInfo("CATEGORY") << oss.str();

The first part goes into the member data list of your class. For example, for VacGeometry.hh,

// Vac Geometry

#ifndef VACGEOMETRY_HH
#define VACGEOMETRY_HH

#include "artg4/Core/GeometryBase.hh" 

#include "Geant4/globals.hh" 

#include <iostream>

namespace gm2ringsim {

  struct VacGeometry : public artg4::GeometryBase {
    VacGeometry(std::string const &);

    double inflectorExtension(unsigned int) const;

    void print() const;

    const double in = 25.4 * mm;

    // ---> First part was pasted here
    const double inflectorExtensionValue;
    const double topBottomWall;
    const double outerWallThickness;
    const double torus_rmin;
    std::vector<double> torus_rmax;
    const double torus_sphi;
    const double torus_dphi;
    const double phi_a;
    const double phi_b;
    std::vector<double> wallR;
    std::vector<double> wallPhi;
    std::vector<double> vacR;
    std::vector<double> vacPhi;
    const double phi_q_subtractor;
    const bool set_phi_q;
    const double phi_q;
    std::vector<double> xz;
    std::vector<double> ext;
    const double tracker_sphi;
    const double tracker_dphi;
    const double turn_sphi;
    const double turn_dphi;
  };
}

#endif

The second part (marked onstructor initialization list) goes into your constructor (you may have to remove the last comma). The third part goes into the constructor body (if you don't have any vectors in your Geometry, then you won't have to do this third part).

// Geometry for the Vac

#include "gm2ringsim/vac/VacGeometry.hh" 
#include "messagefacility/MessageLogger/MessageLogger.h" 
#include "Geant4/globals.hh" 
#include <iostream>
#include <sstream>

gm2ringsim::VacGeometry::VacGeometry(std::string const & detName) :
  GeometryBase(detName),
  // ---> Second part was pasted here
  inflectorExtensionValue( p.get<double>("inflectorExtensionValue") * mm),
  topBottomWall( p.get<double>("topBottomWall") * mm),
  outerWallThickness( p.get<double>("outerWallThickness") * mm),
  torus_rmin( p.get<double>("torus_rmin") * in),
  torus_rmax( p.get<std::vector<double>>("torus_rmax") ),
  torus_sphi( p.get<double>("torus_sphi") * deg),
  torus_dphi( p.get<double>("torus_dphi") * deg),
  phi_a( p.get<double>("phi_a") * deg),
  phi_b( p.get<double>("phi_b") * deg),
  wallR( p.get<std::vector<double>>("wallR") ),
  wallPhi( p.get<std::vector<double>>("wallPhi") ),
  vacR( p.get<std::vector<double>>("vacR") ),
  vacPhi( p.get<std::vector<double>>("vacPhi") ),
  phi_q_subtractor( p.get<double>("phi_q_subtractor") * deg),
  set_phi_q( p.get<bool>("set_phi_q") ),
  phi_q( p.get<double>("phi_q") * deg),
  xz( p.get<std::vector<double>>("xz") ),
  ext( p.get<std::vector<double>>("ext") ),
  tracker_sphi( p.get<double>("tracker_sphi") * deg),
  tracker_dphi( p.get<double>("tracker_dphi") * deg),
  turn_sphi( p.get<double>("turn_sphi") * deg),
  turn_dphi( p.get<double>("turn_dphi") * deg)  // <-- Removed last comma
{
  // ---> Third part was pasted here
  for (auto& entry : torus_rmax ) { entry *= in; }
  for (auto& entry : wallR ) { entry *= in; }
  for (auto& entry : wallPhi ) { entry *= deg; }
  for (auto& entry : vacR ) { entry *= in; }
  for (auto& entry : vacPhi ) { entry *= deg; }
  for (auto& entry : xz ) { entry *= in; }
  for (auto& entry : ext ) { entry *= in; }
}

And finally, the fourth part gets pasted into your print method,

void gm2ringsim::VacGeometry::print() const {

  // Use the MessageLogger (see https://cdcvs.fnal.gov/redmine/projects/novaart/wiki/Using_the_Framework#MessageLogger )
  // ---> Fourth part was pasted here
  std::ostringstream oss;
  oss << "  inflectorExtensionValue=" << inflectorExtensionValue << "\n";
  oss << "  topBottomWall=" << topBottomWall << "\n";
  oss << "  outerWallThickness=" << outerWallThickness << "\n";
  oss << "  torus_rmin=" << torus_rmin << "\n";
  oss << "  torus_rmax= "; for (auto entry : torus_rmax) { oss << " " << entry; }; oss << "\n";
  oss << "  torus_sphi=" << torus_sphi << "\n";
  oss << "  torus_dphi=" << torus_dphi << "\n";
  oss << "  phi_a=" << phi_a << "\n";
  oss << "  phi_b=" << phi_b << "\n";
  oss << "  wallR= "; for (auto entry : wallR) { oss << " " << entry; }; oss << "\n";
  oss << "  wallPhi= "; for (auto entry : wallPhi) { oss << " " << entry; }; oss << "\n";
  oss << "  vacR= "; for (auto entry : vacR) { oss << " " << entry; }; oss << "\n";
  oss << "  vacPhi= "; for (auto entry : vacPhi) { oss << " " << entry; }; oss << "\n";
  oss << "  phi_q_subtractor=" << phi_q_subtractor << "\n";
  oss << "  set_phi_q=" << set_phi_q << "\n";
  oss << "  phi_q=" << phi_q << "\n";
  oss << "  xz= "; for (auto entry : xz) { oss << " " << entry; }; oss << "\n";
  oss << "  ext= "; for (auto entry : ext) { oss << " " << entry; }; oss << "\n";
  oss << "  tracker_sphi=" << tracker_sphi << "\n";
  oss << "  tracker_dphi=" << tracker_dphi << "\n";
  oss << "  turn_sphi=" << turn_sphi << "\n";
  oss << "  turn_dphi=" << turn_dphi << "\n";
  mf::LogInfo("VACGEOM") << "Vacuum Chamber Geometry\n" << oss.str();
}

Rules and limitations

As discussed above, geomTyper.py parses your fcl file. It does these things...

  • Blank lines and lines with only comments are ignored
  • A comment after a value specifies a unit, and will be multiplied by that unit in the initialization. If no unit is specified, then mm is assumed.
  • Numeric values are always treated as doubles
  • String values are not handled correctly
  • Single booleans set to true or false are handled correctly
  • Vector values are always treated as doubles

Visualization items are treated somewhat special. Any item that has "color" or "vis" (case insensitive) in the name will not have a unit applied.