From 5acf706b78fc747a73a7907d027f831852d98682 Mon Sep 17 00:00:00 2001
From: Markus Frank <Markus.Frank@cern.ch>
Date: Wed, 5 Dec 2018 16:57:49 +0100
Subject: [PATCH] Fix issue #465 and add example on how to do a volume scan.

---
 DDCore/src/Shapes.cpp                         |  47 +++++--
 DDG4/src/Geant4Converter.cpp                  |   9 ++
 examples/ClientTests/CMakeLists.txt           |  10 ++
 .../compact/Check_Shape_PseudoTrap.xml        |   2 +-
 .../compact/Check_Shape_PseudoTrapCMS.xml     |  38 ++++++
 .../compact/Check_Shape_TubeDivision.xml      |  32 +++++
 .../src/PlacedVolumeScannerTest.cpp           | 122 ++++++++++++++++++
 .../src/TestConstantMultiplier.cpp            |   2 +-
 8 files changed, 252 insertions(+), 10 deletions(-)
 create mode 100644 examples/ClientTests/compact/Check_Shape_PseudoTrapCMS.xml
 create mode 100644 examples/ClientTests/compact/Check_Shape_TubeDivision.xml
 create mode 100644 examples/ClientTests/src/PlacedVolumeScannerTest.cpp

diff --git a/DDCore/src/Shapes.cpp b/DDCore/src/Shapes.cpp
index 2ee5c7431..f3ae09840 100644
--- a/DDCore/src/Shapes.cpp
+++ b/DDCore/src/Shapes.cpp
@@ -646,40 +646,71 @@ void PseudoTrap::make(double x1, double x2, double y1, double y2, double z, doub
   double startPhi     = 0;
   double halfZ        = z;
   double halfOpeningAngle = std::asin( x / std::abs( r ))/units::deg;
-  
+
   /// calculate the displacement of the tubs w.r.t. to the trap, determine the opening angle of the tubs
   double delta        = std::sqrt( r * r - x * x );
  
+#if 0
   if( r < 0 && std::abs(r) >= x )  {
-    intersec = true; // intersection solid
+    intersec = true;       // intersection solid
     h = y1 < y2 ? y2 : y1; // tubs half height
-    h += h/20.; // enlarge a bit - for subtraction solid
+    h += h/20.;            // enlarge a bit - for subtraction solid
+    if( atMinusZ )    {
+      displacement = -halfZ - delta; 
+      startPhi     =  270.0 - halfOpeningAngle;
+    }
+    else    {
+      displacement =  halfZ + delta;
+      startPhi     =  90.0  - halfOpeningAngle;
+    }
+  }
+  else if( r > 0 && std::abs(r) >= x )  {
+    if( atMinusZ )    {
+      displacement = -halfZ + delta;
+      startPhi     =  90.0  - halfOpeningAngle;
+      h = y1;
+    }
+    else
+    {
+      displacement =  halfZ - delta; 
+      startPhi     =  270.0 - halfOpeningAngle;
+      h = y2;
+    }    
+  }
+  else  {
+    except("PseudoTrap","Check parameters of the PseudoTrap!");   
+  }
+#endif 
+  if( r < 0 && std::abs(r) >= x )  {
+    intersec = true;       // intersection solid
+    h = y1 < y2 ? y2 : y1; // tubs half height
+    h += h/20.;            // enlarge a bit - for subtraction solid
     if( atMinusZ )    {
       displacement = - halfZ - delta; 
-      startPhi     = 270.0 - halfOpeningAngle;
+      startPhi     =  90.0 - halfOpeningAngle;
     }
     else    {
       displacement =   halfZ + delta;
-      startPhi     = 90.0 - halfOpeningAngle;
+      startPhi     = -90.0 - halfOpeningAngle;
     }
   }
   else if( r > 0 && std::abs(r) >= x )  {
     if( atMinusZ )    {
       displacement = - halfZ + delta;
-      startPhi     = 90.0 - halfOpeningAngle;
+      startPhi     = 270.0 - halfOpeningAngle;
       h = y1;
     }
     else
     {
       displacement =   halfZ - delta; 
-      startPhi     = 270.0 - halfOpeningAngle;
+      startPhi     =  90.0 - halfOpeningAngle;
       h = y2;
     }    
   }
   else  {
     except("PseudoTrap","Check parameters of the PseudoTrap!");   
   }
- 
+
   Solid trap(new TGeoTrd2(x1, x2, y1, y2, halfZ));
   Solid tubs(new TGeoTubeSeg(0.,std::abs(r),h,startPhi,startPhi + halfOpeningAngle*2.));
   TGeoCompositeShape* solid = 0;
diff --git a/DDG4/src/Geant4Converter.cpp b/DDG4/src/Geant4Converter.cpp
index 0d0172ac7..b5ff59e34 100644
--- a/DDG4/src/Geant4Converter.cpp
+++ b/DDG4/src/Geant4Converter.cpp
@@ -70,6 +70,7 @@
 #include "G4UnionSolid.hh"
 #include "G4Paraboloid.hh"
 #include "G4Ellipsoid.hh"
+#include "G4GenericTrap.hh"
 #include "G4ExtrudedSolid.hh"
 #include "G4EllipticalTube.hh"
 #include "G4SubtractionSolid.hh"
@@ -527,6 +528,14 @@ void* Geant4Converter::handleSolid(const string& name, const TGeoShape* shape) c
                          sh->GetH1() * CM_2_MM, sh->GetBl1() * CM_2_MM, sh->GetTl1() * CM_2_MM, sh->GetAlpha1() * DEGREE_2_RAD,
                          sh->GetH2() * CM_2_MM, sh->GetBl2() * CM_2_MM, sh->GetTl2() * CM_2_MM, sh->GetAlpha2() * DEGREE_2_RAD);
     }
+    else if (shape->IsA() == TGeoArb8::Class())  {
+      vector<G4TwoVector> vertices;
+      TGeoTrap* sh = (TGeoTrap*) shape;
+      Double_t* vtx_xy = sh->GetVertices();
+      for ( size_t i=0; i<8; ++i, vtx_xy +=2 )
+        vertices.push_back(G4TwoVector(vtx_xy[0] * CM_2_MM,vtx_xy[1] * CM_2_MM));
+      solid = new G4GenericTrap(name, sh->GetDz() * CM_2_MM, vertices);
+    }
     else if (shape->IsA() == TGeoCompositeShape::Class()) {
       const TGeoCompositeShape* sh = (const TGeoCompositeShape*) shape;
       const TGeoBoolNode* boolean = sh->GetBoolNode();
diff --git a/examples/ClientTests/CMakeLists.txt b/examples/ClientTests/CMakeLists.txt
index fde1de970..aa66ae54e 100644
--- a/examples/ClientTests/CMakeLists.txt
+++ b/examples/ClientTests/CMakeLists.txt
@@ -36,6 +36,16 @@ dd4hep_install_dir( compact scripts ref DESTINATION ${ClientTestsEx_INSTALL} )
 dd4hep_configure_scripts( ClientTests DEFAULT_SETUP WITH_TESTS)
 #---  Testing  ------------------------------------------------------------
 #
+#  Test Volume scanner for CMS
+dd4hep_add_test_reg( ClientTests_volume_scanner
+  COMMAND    "${CMAKE_INSTALL_PREFIX}/bin/run_test_ClientTests.sh"
+  EXEC_ARGS  geoPluginRun -input ${ClientTestsEx_INSTALL}/compact/MiniTel.xml
+  -destroy -plugin DD4hep_PlacedVolumeScannerTest -detector /world
+  REGEX_PASS "Visited a total of 11 placed volumes"
+  REGEX_FAIL "Exception"
+  REGEX_FAIL "FAILED"
+  )
+#
 #  Test namespaces for constants 
 dd4hep_add_test_reg( ClientTests_namespace_constants
   COMMAND    "${CMAKE_INSTALL_PREFIX}/bin/run_test_ClientTests.sh"
diff --git a/examples/ClientTests/compact/Check_Shape_PseudoTrap.xml b/examples/ClientTests/compact/Check_Shape_PseudoTrap.xml
index 79ff75fff..d9038c978 100644
--- a/examples/ClientTests/compact/Check_Shape_PseudoTrap.xml
+++ b/examples/ClientTests/compact/Check_Shape_PseudoTrap.xml
@@ -29,7 +29,7 @@
       </check>
       -->
 
-      <test  type="DD4hep_Mesh_Verifier" ref="${DD4hepExamplesINSTALL}/examples/ClientTests/ref/Ref_PseudoTrap.txt" create="CheckShape_create"/>
+      <test11  type="DD4hep_Mesh_Verifier" ref="${DD4hepExamplesINSTALL}/examples/ClientTests/ref/Ref_PseudoTrap.txt" create="CheckShape_create"/>
     </detector>
   </detectors>
 </lccdd>
diff --git a/examples/ClientTests/compact/Check_Shape_PseudoTrapCMS.xml b/examples/ClientTests/compact/Check_Shape_PseudoTrapCMS.xml
new file mode 100644
index 000000000..3343f639e
--- /dev/null
+++ b/examples/ClientTests/compact/Check_Shape_PseudoTrapCMS.xml
@@ -0,0 +1,38 @@
+<lccdd>
+  <includes>
+    <gdmlFile ref="CheckShape.xml"/>
+  </includes>
+
+  <detectors>
+    <detector id="1" name="Shape_PseudoTrap" type="DD4hep_TestShape_Creator">
+      <!-- Union pseudo-trap:          -->
+      <check vis="Shape1_vis">
+        <shape type="PseudoTrap" name="YE1_b" 
+               x1="0.5*m" x2="1.86356*m" 
+               y1="0.3000*m"   y2="0.3000*m" 
+               z="0.92934*m"   radius="-0.91350*m" minusZ="true"/>
+        <position x="0*cm"  y="0*cm" z="0*cm"/>
+        <rotation x="0"     y="0"    z="0"/>
+      </check>
+      <check vis="Shape2_vis">
+        <shape type="PseudoTrap" name="YE1_c" 
+               x1="0.293734*m" x2="0.86356*m" 
+               y1="0.3000*m"   y2="0.9000*m" 
+               z="0.92934*m"   radius="-1.1350*m" minusZ="false"/>
+        <position x="400*cm"  y="180*cm" z="0*cm"/>
+        <rotation x="pi/2"     y="0"    z="0"/>
+      </check>
+      <check vis="Shape3_vis">
+        <shape type="PseudoTrap" name="YE1_d" 
+               x1="0.293734*m" x2="0.86356*m" 
+               y1="0.3000*m"   y2="0.9000*m" 
+               z="0.92934*m"   radius="1.1350*m" minusZ="false"/>
+        <position x="-300*cm"  y="-180*cm" z="0*cm"/>
+        <rotation x="0"       y="pi/2"    z="pi"/>
+      </check>
+      <comment>
+        <test  type="DD4hep_Mesh_Verifier" ref="${DD4hepExamplesINSTALL}/examples/ClientTests/ref/Ref_PseudoTrap.txt" create="CheckShape_create"/>
+      </comment>
+    </detector>
+  </detectors>
+</lccdd>
diff --git a/examples/ClientTests/compact/Check_Shape_TubeDivision.xml b/examples/ClientTests/compact/Check_Shape_TubeDivision.xml
new file mode 100644
index 000000000..551f0cc6a
--- /dev/null
+++ b/examples/ClientTests/compact/Check_Shape_TubeDivision.xml
@@ -0,0 +1,32 @@
+<lccdd>
+  <includes>
+    <gdmlFile ref="CheckShape.xml"/>
+  </includes>
+
+  <detectors>
+    <detector id="1" name="Shape_TubeDivision" type="DD4hep_TestShape_Creator">
+      <check vis="Shape1_vis_20">
+        <shape type="Tube" rmin="4.69*m" rmax="4.95*m" dz="31.9*cm" startphi="346.25*deg" deltaphi="360*deg">
+          <position x="0"  y="0" z="50"/>
+          <rotation x="0"  y="0" z="0"/>
+        </shape>
+      </check>
+      <check vis="Shape2_vis">
+        <shape type="Tube" rmin="4.694*m" rmax="4.955*m" dz="32*cm" startphi="346.25*deg" deltaphi="3.099999*deg">
+          <position x="0"  y="30" z="50"/>
+          <rotation x="0"  y="0"  z="0"/>
+        </shape>
+      </check>
+      <check vis="Shape3_vis">
+        <shape type="Tube" rmin="4.694*m" rmax="4.955*m" dz="32*cm" startphi="10.75*deg" deltaphi="3.099999*deg">
+          <position x="0"  y="0" z="0"/>
+          <rotation x="0"  y="0" z="0"/>
+        </shape>
+      </check>
+
+<!--
+      <test  type="DD4hep_Mesh_Verifier" ref="${DD4hepExamplesINSTALL}/examples/ClientTests/ref/Ref_TubeDivision.txt" create="CheckShape_create"/>
+-->
+    </detector>
+  </detectors>
+</lccdd>
diff --git a/examples/ClientTests/src/PlacedVolumeScannerTest.cpp b/examples/ClientTests/src/PlacedVolumeScannerTest.cpp
new file mode 100644
index 000000000..7800e7b1c
--- /dev/null
+++ b/examples/ClientTests/src/PlacedVolumeScannerTest.cpp
@@ -0,0 +1,122 @@
+//==========================================================================
+//  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
+//
+//==========================================================================
+/* 
+   Plugin invocation:
+   ==================
+   This plugin behaves like a main program.
+   Invoke the plugin with something like this:
+
+   geoPluginRun -destroy -plugin DD4hep_VolumeScannerTest -opt [-opt]
+
+*/
+// Framework include files
+#include "DD4hep/Volumes.h"
+#include "DD4hep/Detector.h"
+#include "DD4hep/Printout.h"
+#include "DD4hep/Factories.h"
+#include "DD4hep/DetectorTools.h"
+#include "DD4hep/VolumeProcessor.h"
+#include "TClass.h"
+#include <iomanip>
+
+namespace   {
+  class MyVolumeProcessor : public dd4hep::PlacedVolumeProcessor   {
+  public:
+    int count = 0;
+    std::vector<std::string> volume_stack;
+  public:
+    /// Default constructor
+    MyVolumeProcessor() = default;
+    /// Default destructir
+    virtual ~MyVolumeProcessor() = default;
+    /// Callback to output PlacedVolume information of an entire Placement
+    virtual int process(dd4hep::PlacedVolume pv, int level, bool recursive)    {
+      volume_stack.push_back(pv.name());
+      int ret = this->dd4hep::PlacedVolumeProcessor::process(pv, level, recursive);
+      volume_stack.pop_back();
+      return ret;
+    }
+    /// Volume callback
+    virtual int operator()(dd4hep::PlacedVolume pv, int level)   {
+      dd4hep::Volume vol = pv.volume();
+      std::cout << "Hierarchical level:" << level << "   Placement:";
+      for(const auto& i : volume_stack ) std::cout << "/" << i;
+      std::cout << std::endl
+                << "\tMaterial:" << vol.material().name() 
+                << "\tSolid:   " << vol.solid().name()
+                << " [" << vol.solid()->IsA()->GetName() << "]" << std::endl;
+      ++count;
+      return 1;
+    }
+  };
+}
+
+using namespace std;
+using namespace dd4hep;
+
+/// Plugin function: Test example of the volume scanner using a customized callback functor
+/**
+ *
+ *  \author  M.Frank
+ *  \version 1.0
+ *  \date    20/01/2018
+ */
+static int scan_volumes (Detector& detector, int argc, char** argv)  {
+  bool help = false;
+  string det_element_path, placed_vol_path;
+  for(int i=0; i<argc && argv[i]; ++i)  {
+    if ( 0 == ::strncmp("-help",argv[i],4) )
+      help = true;
+    else if ( 0 == ::strncmp("-path",argv[i],4) )
+      placed_vol_path = argv[++i];
+    else if ( 0 == ::strncmp("-detector",argv[i],4) )
+      det_element_path = argv[++i];
+    else
+      help = true;
+  }
+  if ( help )   {
+    /// Help printout describing the basic command line interface
+    cout <<
+      "Usage: -plugin <name> -arg [-arg]                                                  \n"
+      "     name:   factory name     DD4hep_PlacedVolumeScannerTest                       \n"
+      "     -detector <name>         Path to the detector element where to start the scan.\n"
+      "     -path     <name>         Alternatively specify the physical volume path.      \n"
+      "     -help                    Ahow this help.                                      \n"
+      "\tArguments given: " << arguments(argc,argv) << endl << flush;
+    ::exit(EINVAL);
+  }
+
+  // Detectine the proper placed volume for the start (default=/world_volume)
+  PlacedVolume start_pv;
+  DetElement   start_de, de = detector.world();
+  if ( !det_element_path.empty() )
+    start_de = detail::tools::findElement(detector, det_element_path);
+  else if ( !placed_vol_path.empty() )
+    start_pv = detail::tools::findNode(de.placement(),placed_vol_path);
+
+  if ( !start_pv.isValid() )   {
+    if ( !start_de.isValid() )   {      
+      except("VolumeScanner","Failed to find start conditions for the volume scan");
+    }
+    start_pv = start_de.placement();
+  }
+
+  // Let's scan
+  MyVolumeProcessor proc;
+  PlacedVolumeScanner().scanPlacements(proc, start_pv, 0, true);
+  
+  printout(ALWAYS,"VolumeScanner","+++ Visited a total of %d placed volumes.",proc.count);
+  return 1;
+}
+
+DECLARE_APPLY(DD4hep_PlacedVolumeScannerTest,scan_volumes)
diff --git a/examples/ClientTests/src/TestConstantMultiplier.cpp b/examples/ClientTests/src/TestConstantMultiplier.cpp
index f2af38dc2..44e588e01 100644
--- a/examples/ClientTests/src/TestConstantMultiplier.cpp
+++ b/examples/ClientTests/src/TestConstantMultiplier.cpp
@@ -16,7 +16,7 @@
  This plugin behaves like a main program.
  Invoke the plugin with something like this:
 
- geoPluginRun -destroy -plugin DD4hep_XML-In-Memory -input <file name>
+ geoPluginRun -destroy -plugin DD4hep_TestConstantsMultiplier -opt [-opt]
 
 */
 // Framework include files
-- 
GitLab