Skip to content
Snippets Groups Projects
MaterialScan.cpp 10.5 KiB
Newer Older
//==========================================================================
//  AIDA Detector description implementation 
//--------------------------------------------------------------------------
// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
// All rights reserved.
//
// For the licensing terms see $DD4hepINSTALL/LICENSE.
// For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
//
// Author     : M.Frank
//
//==========================================================================
//
//  Simple program to print all the materials in a detector on
//  a straight line between two given points
// 
//  Author     : M.Frank, CERN
//
//==========================================================================
// Framework include files
#include <DDRec/MaterialScan.h>
#include <DD4hep/DD4hepUnits.h>
#include <DD4hep/Detector.h>
#include <DD4hep/Printout.h>
/// C/C++ include files
#include <cstdio>

using namespace dd4hep;
using namespace dd4hep::rec;

/// Default constructor
MaterialScan::MaterialScan()
  : m_detector(Detector::getInstance())
{
  m_materialMgr.reset(new MaterialManager(m_detector.world().volume()));
}

/// Default constructor
MaterialScan::MaterialScan(Detector& description)
  : m_detector(description)
{
  m_materialMgr.reset(new MaterialManager(m_detector.world().volume()));
}

/// Default destructor
MaterialScan::~MaterialScan()    {
}


/// Set a specific region to limit the scan (resets the subdetector selection)
void MaterialScan::setRegion(const char* region)   {
  Region reg;
  if ( region )   {
    reg = m_detector.region(region);
  }
  setRegion(reg);
}

/// Set a specific region to limit the scan (resets the subdetector selection)
void MaterialScan::setRegion(Region region)   {
  struct PvCollector  {
    Region rg;
    std::set<const TGeoNode*>& cont;
    PvCollector(Region r, std::set<const TGeoNode*>& c) : rg(r), cont(c) {}
    void collect(TGeoNode* pv)    {
      cont.insert(pv);
      for (Int_t idau = 0, ndau = pv->GetNdaughters(); idau < ndau; ++idau)
        collect(pv->GetDaughter(idau));
    }
    void operator()(TGeoNode* pv)    {
      Volume v = pv->GetVolume();
      Region r = v.region();
      if ( r.isValid() )  {
        collect(pv);
        return;
      }
      for (Int_t idau = 0, ndau = pv->GetNdaughters(); idau < ndau; ++idau)
        (*this)(pv->GetDaughter(idau));
    }
  };
  m_placements.clear();
  if ( region.isValid() )   {
    PvCollector coll(region, m_placements);
    coll(m_detector.world().placement().ptr());
    printout(ALWAYS,"MaterialScan","+++ Set new scanning region to: %s [%ld placements]",
             region.name(), m_placements.size());
  }
  else   {
    printout(ALWAYS,"MaterialScan","+++ No region restrictions set. Back to full scanning mode.");
  }
}

/// Set a specific detector volume to limit the scan
void MaterialScan::setDetector(const char* detector)   {
  DetElement det;
  if ( detector )   {
    det = m_detector.detector(detector);
  }
  setDetector(det);
}

/// Set a specific detector volume to limit the scan
void MaterialScan::setDetector(DetElement detector)   {
  struct PvCollector  {
    std::set<const TGeoNode*>& cont;
    PvCollector(std::set<const TGeoNode*>& c) : cont(c) {}
    void operator()(TGeoNode* pv)    {
      cont.insert(pv);
      for (Int_t idau = 0, ndau = pv->GetNdaughters(); idau < ndau; ++idau) {
        TGeoNode* daughter = pv->GetDaughter(idau);
        (*this)(daughter);
      }
    }
  };
    PlacedVolume pv = detector.placement();
    m_placements.clear();
    if ( pv.isValid() )  {
      PvCollector coll(m_placements);
      coll(pv.ptr());
    }
    printout(ALWAYS,"MaterialScan","+++ Set new scanning volume to: %s [%ld placements]",
             detector.path().c_str(), m_placements.size());
  }
  else   {
    printout(ALWAYS,"MaterialScan","+++ No subdetector restrictions set. Back to full scanning mode.");
    m_placements.clear();
  }
}

/// Set a specific volume material to limit the scan
void MaterialScan::setMaterial(const char* material)   {
  Material mat;
  if ( material )   {
    mat = m_detector.material(material);
  }
  setMaterial(mat);
}

/// Set a specific volume material to limit the scan
void MaterialScan::setMaterial(Material material)   {
  struct PvCollector  {
    Material material;
    std::set<const TGeoNode*>& cont;
Markus Frank's avatar
Markus Frank committed
    PvCollector(Material mat, std::set<const TGeoNode*>& c) : material(mat), cont(c) {}
    void operator()(TGeoNode* pv)    {
Markus Frank's avatar
Markus Frank committed
      Volume   vol = pv->GetVolume();
      Material mat = vol.material();
      if ( mat.ptr() == material.ptr() )  {
        cont.insert(pv);
      }
      for (Int_t idau = 0, ndau = pv->GetNdaughters(); idau < ndau; ++idau)
        (*this)(pv->GetDaughter(idau));
    }
  };
  m_placements.clear();
  if ( material.isValid() )   {
    PvCollector coll(material, m_placements);
    coll(m_detector.world().placement().ptr());
    printout(ALWAYS,"MaterialScan","+++ Set new scanning material to: %s [%ld placements]",
             material.name(), m_placements.size());
  }
  else   {
    printout(ALWAYS,"MaterialScan","+++ No material restrictions set. Back to full scanning mode.");
  }
}

/// Scan along a line and store the matrials internally
const MaterialVec& MaterialScan::scan(double x0, double y0, double z0, double x1, double y1, double z1, double epsilon)  const  {
  Vector3D p0(x0, y0, z0), p1(x1, y1, z1);
  return m_materialMgr->materialsBetween(p0, p1, epsilon);
}

/// Scan along a line and print the materials traversed
void MaterialScan::print(const Vector3D& p0, const Vector3D& p1, double epsilon)  const    {
  const auto& placements = m_materialMgr->placementsBetween(p0, p1, epsilon);
  auto& matMgr = *m_materialMgr;
  Vector3D end, direction;
  double sum_x0 = 0;
  double sum_lambda = 0;
  double path_length = 0, total_length = 0;
  const char* fmt1 = " | %7s %-25s %3.0f %8.3f %8.4f %11.4f  %11.4f %10.3f %8.2f %11.6f %11.6f  (%7.2f,%7.2f,%7.2f)\n";
  const char* fmt2 = " | %7s %-25s %3.0f %8.3f %8.4f %11.6g  %11.6g %10.3f %8.2f %11.6f %11.6f  (%7.2f,%7.2f,%7.2f)\n";
  const char* line = " +--------------------------------------------------------------------------------------------------------------------------------------------------\n";

  direction = (p1-p0).unit();
  
  ::printf("%s + Material scan between: x_0 = (%7.2f,%7.2f,%7.2f) [cm] and x_1 = (%7.2f,%7.2f,%7.2f) [cm] : \n%s",
           line,p0[0],p0[1],p0[2],p1[0],p1[1],p1[2],line);
  ::printf(" |       \\   %-16s          Atomic               Radiation   Interaction               Path   Integrated  Integrated    Material\n","Material");
  ::printf(" | Num.   \\  %-16s   Number/Z   Mass/A  Density    Length       Length    Thickness   Length      X0        Lambda      Endpoint/Startpoint\n","Name");
  ::printf(" | Layer   \\ %-16s            [g/mole]  [g/cm3]     [cm]        [cm]          [cm]      [cm]     [cm]        [cm]     (     cm,     cm,     cm)\n","");
  for( unsigned i=0, n=placements.size(); i<n; ++i){
    TGeoNode*  pv  = placements[i].first.ptr();
    double length  = placements[i].second;
    total_length  += length;
    end = p0 + total_length * direction;
    if ( !m_placements.empty() && m_placements.find(pv) == m_placements.end() )  {
#if 0
      ::printf("%p %s  %s  %s\n",
               placements[i].first.ptr(),
               placements[i].first->GetName(),
               placements[i].first->GetVolume()->GetName(),
               placements[i].first->GetMedium()->GetName());
#endif
      continue;
    }
    TGeoMaterial* curr_mat = placements[i].first->GetMedium()->GetMaterial();
    TGeoMaterial* next_mat = (i+1)<n ? placements[i+1].first->GetMedium()->GetMaterial() : nullptr;
    double nx0     = length / curr_mat->GetRadLen();
    double nLambda = length / curr_mat->GetIntLen();

    sum_x0        += nx0;
    sum_lambda    += nLambda;
    path_length   += length;
    materials.emplace_back(placements[i].first->GetMedium(),length);
    const char* fmt = curr_mat->GetRadLen() >= 1e5 ? fmt2 : fmt1;
    std::string mname = curr_mat->GetName();
    if ( next_mat && (i+1)<n )  {
      mname += " -> ";
      mname += next_mat->GetName();
    }
    Volume vol(placements[i].first->GetVolume());
    Solid  shape(vol.solid());
    if ( shape->IsA() == TGeoTessellated::Class() )  {
      tessellated_solids.insert(shape);
    }
    if ( 0 == i )  {
      ::printf(fmt, "(start)" , curr_mat->GetName(), curr_mat->GetZ(), curr_mat->GetA(),
	       curr_mat->GetDensity(), curr_mat->GetRadLen()/dd4hep::cm, curr_mat->GetIntLen()/dd4hep::cm,
	       0e0, 0e0, 0e0, 0e0,
	       p0[0]/dd4hep::cm, p0[1]/dd4hep::cm, p0[2]/dd4hep::cm);
    }
    // No else here!
    if ( (n-1) == i )  {
      ::printf(fmt, "(end)", curr_mat->GetName(), curr_mat->GetZ(), curr_mat->GetA(),
	       curr_mat->GetDensity(), curr_mat->GetRadLen()/dd4hep::cm, curr_mat->GetIntLen()/dd4hep::cm,
	       0e0, 0e0, 0e0, 0e0,
	       p0[0]/dd4hep::cm, p0[1]/dd4hep::cm, p0[2]/dd4hep::cm);
    }
    else   {
      ::printf(fmt, std::to_string(i+1).c_str(), mname.c_str(), next_mat->GetZ(), next_mat->GetA(),
	       next_mat->GetDensity(), next_mat->GetRadLen()/dd4hep::cm, next_mat->GetIntLen()/dd4hep::cm,
	       length/dd4hep::cm, path_length/dd4hep::cm, sum_x0, sum_lambda,
	       end[0]/dd4hep::cm, end[1]/dd4hep::cm, end[2]/dd4hep::cm);
    }
  const MaterialData& avg = matMgr.createAveragedMaterial(materials);
  const char* fmt = avg.radiationLength() >= 1e5 ? fmt2 : fmt1;
  ::printf(fmt,"","Average Material",avg.Z(),avg.A(),avg.density(), 
           avg.radiationLength()/dd4hep::cm, avg.interactionLength()/dd4hep::cm,
           path_length/dd4hep::cm, path_length/dd4hep::cm,
           path_length/avg.radiationLength(), 
           path_length/avg.interactionLength(),
           end[0]/dd4hep::cm, end[1]/dd4hep::cm, end[2]/dd4hep::cm);
  ::printf("%s",line);

  if ( !tessellated_solids.empty() )  {
    ::printf(" |  WARNING: %ld tessellated shape were encountered during the volume traversal:\n",
	     tessellated_solids.size());
    for(auto shape : tessellated_solids )
      ::printf(" |           \t\t %s\n", shape.name());
    ::printf(" |  WARNING: The results of this material scan are unreliable!\n");
    ::printf("%s",line);
  }
}

/// Scan along a line and print the materials traversed
void MaterialScan::print(double x0, double y0, double z0, double x1, double y1, double z1, double epsilon)  const  {
  Vector3D p0(x0, y0, z0), p1(x1, y1, z1);
  this->print(p0, p1, epsilon);