diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index ead196becdbc4c8094ecc786bd0cad1a0a725f82..7939deeeeaa9765d1ea510f7f0f52a972119e453 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -27,6 +27,7 @@ variables:
   allow_failure: true
   timeout: 30 minutes
   before_script:
+    - brew tap davidchall/hep
     - brew bundle --file=CI/Brewfile # install third-party dependencies, if needed
     - export PATH="/usr/local/opt/expat/bin:$PATH" # to make CMake use expat from Homebrew instead of the old one shipped with the system in /usr/lib
     - export LDFLAGS="-L/usr/local/opt/expat/lib"
@@ -50,7 +51,7 @@ variables:
 .ubuntu-template-job-default: &ubuntu-job
   <<: *ubuntu-job-base
   before_script:
-    - apt-get update -qq && apt-get install -y -qq git wget unzip build-essential freeglut3-dev libboost-all-dev qtbase5-dev libqt5opengl5-dev mercurial libeigen3-dev libsqlite3-dev nlohmann-json3-dev libexpat1-dev libxerces-c-dev libhdf5-dev cmake
+    - apt-get update -qq && apt-get install -y -qq git wget unzip build-essential freeglut3-dev libboost-all-dev qtbase5-dev libqt5opengl5-dev mercurial libeigen3-dev libsqlite3-dev nlohmann-json3-dev libexpat1-dev libxerces-c-dev libhdf5-dev libhepmc3-dev cmake
 
 
 # .ubuntu-template-job-registry: &ubuntu-job-registry
@@ -253,6 +254,14 @@ mac-gm-fullsimlight:
     CMAKE_ARGS: ${CMAKE_BASE_ARGS}
     CMAKE_CONFIG_FLAGS: -DGEOMODEL_BUILD_FULLSIMLIGHT=TRUE
 
+mac-gm-atlasextras:
+  <<: *macos-job
+  <<: *geomodel-job
+  stage: step-C
+  variables:
+    CMAKE_ARGS: ${CMAKE_BASE_ARGS}
+    CMAKE_CONFIG_FLAGS: -DGEOMODEL_BUILD_ATLASEXTENSIONS=TRUE
+
 
 ### UBUNTU BUILD JOBS
 ubu-coin:
@@ -334,6 +343,15 @@ ubu-gm-fullsimlight:
     CMAKE_ARGS: ${CMAKE_BASE_ARGS}
     CMAKE_CONFIG_FLAGS: -DGEOMODEL_BUILD_FULLSIMLIGHT=TRUE
 
+ubu-gm-atlasextras:
+  <<: *ubuntu-job
+  <<: *geomodel-job
+  stage: step-C
+  needs: ["ubu-geant4"]
+  variables:
+    CMAKE_ARGS: ${CMAKE_BASE_ARGS}
+    CMAKE_CONFIG_FLAGS: -DGEOMODEL_BUILD_ATLASEXTENSIONS=TRUE
+
 
 #TODO: this does not work properly. Needs some work...
 # ubu-gm-fullsimlight-customxercesc-builtinjson:
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/CMakeLists.txt b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..dc42a307dfad2272e3f1fed980a1ab01227fd1ac
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/CMakeLists.txt
@@ -0,0 +1,83 @@
+# Set up the project.
+cmake_minimum_required( VERSION 3.1 )
+
+set(CMAKE_CXX_STANDARD 17)
+
+project( "ATLASMagneticFieldMapPlugin" )
+
+
+#Set up the project. Check if we build it with GeoModel or individually
+if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
+    # I am built as a top-level project.
+    # Make the root module directory visible to CMake.
+    list( APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake )
+    # get global GeoModel version
+    #include( GeoModelATLAS-version ) 
+    # set the project, with the version taken from the GeoModel parent project
+    project( "ATLASMagneticFieldMapPlugin" VERSION 1.0 LANGUAGES CXX )
+    # Define color codes for CMake messages
+    include( cmake_colors_defs )
+    # Use the GNU install directory names.
+    include( GNUInstallDirs )
+    # Set a default build type
+    include( BuildType )
+    # Set default build and C++ options
+    include( configure_cpp_options )
+    # Print Build Info on screen
+    include( PrintBuildInfo )
+    # Warn the users about what they are doing
+    message(STATUS "${BoldGreen}Building ${PROJECT_NAME} individually, as a top-level project.${ColourReset}")
+    # Set default build and C++ options
+    include( configure_cpp_options )
+    set( CMAKE_FIND_FRAMEWORK "LAST" CACHE STRING
+         "Framework finding behaviour on macOS" )
+    # GeoModel dependencies
+    find_package( GeoModelCore REQUIRED )
+else()
+    # I am called from other project with add_subdirectory().
+    message( STATUS "Building ${PROJECT_NAME} as part of the root project.")
+    # Set the project
+    project( "ATLASMagneticFieldMapPlugin" VERSION 1.0 LANGUAGES CXX )
+endif()
+
+
+
+# Find the header and source files.
+file( GLOB SOURCES src/*.cxx )
+file(GLOB HEADERS src/*.h)
+
+set(PROJECT_SOURCES ${SOURCES} ${HEADERS})
+
+# Set up the library.
+add_library(ATLASMagneticFieldMapPlugin SHARED ${SOURCES})
+
+find_package (Eigen3 REQUIRED)
+find_package(Geant4 REQUIRED)
+#find_package(FullSimLight)
+
+message( STATUS "Found Geant4: ${Geant4_INCLUDE_DIR}")
+#message("Geant4_USE_FILE: ${Geant4_USE_FILE}") # debug msg
+include(${Geant4_USE_FILE})
+
+# Use the GNU install directory names.
+include( GNUInstallDirs )
+
+target_include_directories( ATLASMagneticFieldMapPlugin PUBLIC ${CMAKE_SOURCE_DIR}/FullSimLight )
+
+
+target_link_libraries ( ATLASMagneticFieldMapPlugin PUBLIC ${CMAKE_DL_LIBS} ${Geant4_LIBRARIES} Eigen3::Eigen)
+
+
+set_target_properties( ATLASMagneticFieldMapPlugin PROPERTIES
+		       VERSION ${PROJECT_VERSION}
+		       SOVERSION ${PROJECT_VERSION_MAJOR} )
+
+
+
+install( TARGETS ATLASMagneticFieldMapPlugin
+	 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+	 COMPONENT Runtime
+	 NAMELINK_COMPONENT Development )
+
+
+
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/cmake/BuildType.cmake b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/cmake/BuildType.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..14a12a8ccc0c22511e31288d7ab3b4fea69fb561
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/cmake/BuildType.cmake
@@ -0,0 +1,28 @@
+
+# Author: Marcus D. Hanwell
+# Source: https://blog.kitware.com/cmake-and-the-default-build-type/
+
+# Set a default build type if none was specified
+set(default_build_type "Release")
+
+# TODO: at the moment, we want to build in Release mode by default,
+# even if we build from a Git clone, because that is the default mode
+# for our users to get the source code.
+# But maybe we will want to change this behavior, later?
+# if(EXISTS "${CMAKE_SOURCE_DIR}/.git")
+#   set(default_build_type "Debug")
+# endif()
+
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+  if( COLOR_DEFS )
+    message(STATUS "${Blue}INFO: Setting build type to '${default_build_type}' as none was specified.${ColourReset}")
+  else()
+    message(STATUS "INFO: Setting build type to '${default_build_type}' as none was specified.")
+  endif()
+  set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE
+      STRING "Choose the type of build." FORCE)
+  # Set the possible values of build type for cmake-gui
+  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+    "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
+endif()
+
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/cmake/PrintBuildInfo.cmake b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/cmake/PrintBuildInfo.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..862a34b45c7506c83a229d7ef71ff604d182acb6
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/cmake/PrintBuildInfo.cmake
@@ -0,0 +1,13 @@
+# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+
+if( COLOR_DEFS )
+  message(STATUS "-----")
+  message(STATUS "${BoldYellow}Building with type: ${CMAKE_BUILD_TYPE}${ColourReset}")
+  message(STATUS "${BoldYellow}Using C++ standard: ${CMAKE_CXX_STANDARD}${ColourReset}")
+  message(STATUS "-----")
+else()
+  message(STATUS "-----")
+  message(STATUS "Building with type: ${CMAKE_BUILD_TYPE}")
+  message(STATUS "Using C++ standard: ${CMAKE_CXX_STANDARD}")
+  message(STATUS "-----")
+endif()
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/cmake/cmake_colors_defs.cmake b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/cmake/cmake_colors_defs.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..b6eea59ba9e72a50754ac0afb36d25cfcac59e09
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/cmake/cmake_colors_defs.cmake
@@ -0,0 +1,25 @@
+
+# Copyright: "Fraser" (https://stackoverflow.com/users/2556117/fraser)
+# CC BY-SA 3.0
+# Source: https://stackoverflow.com/a/19578320/320369
+
+if(NOT WIN32)
+  set( COLOR_DEFS TRUE CACHE BOOL "Define color escape sequences to be used in CMake messages." )
+  string(ASCII 27 Esc)
+  set(ColourReset "${Esc}[m")
+  set(ColourBold  "${Esc}[1m")
+  set(Red         "${Esc}[31m")
+  set(Green       "${Esc}[32m")
+  set(Yellow      "${Esc}[33m")
+  set(Blue        "${Esc}[34m")
+  set(Magenta     "${Esc}[35m")
+  set(Cyan        "${Esc}[36m")
+  set(White       "${Esc}[37m")
+  set(BoldRed     "${Esc}[1;31m")
+  set(BoldGreen   "${Esc}[1;32m")
+  set(BoldYellow  "${Esc}[1;33m")
+  set(BoldBlue    "${Esc}[1;34m")
+  set(BoldMagenta "${Esc}[1;35m")
+  set(BoldCyan    "${Esc}[1;36m")
+  set(BoldWhite   "${Esc}[1;37m")
+endif()
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/cmake/configure_cpp_options.cmake b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/cmake/configure_cpp_options.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..9a6cc8a7902de8bd14d412f0ca4fc86a3bc116fc
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/cmake/configure_cpp_options.cmake
@@ -0,0 +1,34 @@
+
+#
+# Set build options and C++ standards and options
+#
+# This file sets up
+#
+#   CMAKE_BUILD_TYPE
+#   CMAKE_CXX_STANDARD
+#   CMAKE_CXX_EXTENSIONS
+#   CMAKE_CXX_STANDARD_REQUIRED
+#
+# The options can be overridden at configuration time by using, e.g.:
+#    `cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_STANDARD=14 ../GeoModelIO`
+# on the command line.
+#
+
+# Set default build options.
+set( CMAKE_BUILD_TYPE "Release" CACHE STRING "CMake build mode to use" )
+set( CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard used for the build" )
+set( CMAKE_CXX_EXTENSIONS FALSE CACHE BOOL "(Dis)allow using GNU extensions" )
+set( CMAKE_CXX_STANDARD_REQUIRED TRUE CACHE BOOL
+   "Require the specified C++ standard for the build" )
+
+# Setting CMAKE_CXX_FLAGS to avoid "deprecated" warnings
+set(CMAKE_CXX_FLAGS "-Wno-deprecated-declarations" ) # very basic
+#set(CMAKE_CXX_FLAGS "-Wall -Werror -pedantic-errors -Wno-deprecated-declarations" ) # good enough for a quick, better check
+#set(CMAKE_CXX_FLAGS "-Wall -Wextra -Werror -pedantic-errors -Wno-deprecated-declarations" ) # better for a thorough check
+#set(CMAKE_CXX_FLAGS "-Wall -Wextra -Werror -pedantic-errors" ) # better for an even more severe check
+#set(CMAKE_CXX_FLAGS "-Weverything -Werror -pedantic-errors" ) # not recommended, it warns for really EVERYTHING!
+
+
+# TODO: for Debug and with GCC, do we want to set the flags below by default?
+# set( CMAKE_BUILD_TYPE DEBUG )
+# set(CMAKE_CXX_FLAGS "-fPIC -O0 -g -gdwarf-2" )
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/ATLASMagneticFieldMapPlugin.cxx b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/ATLASMagneticFieldMapPlugin.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..911ff026683c03e5645fa1095a4ff2b893934e5f
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/ATLASMagneticFieldMapPlugin.cxx
@@ -0,0 +1,91 @@
+#include <iostream>
+#include "G4MagneticField.hh"
+#include "FullSimLight/MagFieldPlugin.h"
+#include "AtlasFieldSvc.h"
+#include "IMagFieldSvc.h"
+
+class AtlasField : public G4MagneticField
+{
+  public:
+
+    // Construct the field object from the IMagFieldSvc
+    AtlasField(MagField::IMagFieldSvc* m);
+
+    // Implementation of G4 method to retrieve field value
+    void GetFieldValue(const double *point, double *field) const
+    {
+      m_magFieldSvc_AtlasField->getField(point, field);
+    }
+
+  private:
+
+    // Pointer to the magnetic field service.
+    // We use a raw pointer here to avoid ServiceHandle overhead.
+    MagField::IMagFieldSvc* m_magFieldSvc_AtlasField;
+};
+
+
+AtlasField::AtlasField(MagField::IMagFieldSvc* mfield)
+  : m_magFieldSvc_AtlasField(mfield)
+{
+    std::cout<<"New instance of AtlasField, setting m_magFieldSvc_AtlasField: "<<m_magFieldSvc_AtlasField<<" to "<<mfield<<std::endl;
+
+}
+
+
+
+
+
+class ATLASMagneticFieldMapPlugin: public MagFieldPlugin
+
+{
+
+public:
+	//Constructor
+    ATLASMagneticFieldMapPlugin();
+
+	//Destructor
+	~ATLASMagneticFieldMapPlugin();
+
+protected:
+	//Overriding virtual function
+	G4MagneticField* getField(std::string map_path);
+
+private:
+	MagField::IMagFieldSvc* m_magFieldSvc;
+};
+
+ATLASMagneticFieldMapPlugin::ATLASMagneticFieldMapPlugin() {
+
+	std::cout << "HELLO from Atlas Magnetic Field Plugin" << std::endl;
+
+
+}
+
+ATLASMagneticFieldMapPlugin::~ATLASMagneticFieldMapPlugin() {
+
+std::cout << "GOODBYE from Atlas Magnetic Field Plugin" << std::endl;
+}               
+
+G4MagneticField* ATLASMagneticFieldMapPlugin::getField(std::string map_path)
+{ 
+  
+    MagField::AtlasFieldSvc * atlasFieldSvs = new MagField::AtlasFieldSvc(map_path,true);
+    atlasFieldSvs->handle();
+    m_magFieldSvc = atlasFieldSvs;
+
+
+  std::cout<<"AtlasFieldSvc::makeField with m_magFieldSvc "<<m_magFieldSvc<<std::endl;
+  return new AtlasField( &*m_magFieldSvc );
+}
+
+
+
+
+extern "C" ATLASMagneticFieldMapPlugin* createATLASMagneticFieldMapPlugin()
+{
+ return new ATLASMagneticFieldMapPlugin;
+}
+
+
+
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/AtlasFieldSvc.cxx b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/AtlasFieldSvc.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..e5b3c32363e7d8152f9d43829289b554c47ece6e
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/AtlasFieldSvc.cxx
@@ -0,0 +1,1856 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+///////////////////////////////////////////////////////////////////
+// AtlasFieldSvc.cxx, (c) ATLAS Detector software
+///////////////////////////////////////////////////////////////////
+
+#include <iostream>
+#include <fstream>
+
+// ISF_Services include
+#include "AtlasFieldSvc.h"
+
+// PathResolver
+//#include "PathResolver/PathResolver.h"
+
+// StoreGate
+//#include "StoreGate/StoreGateSvc.h"
+
+// Athena Pool
+//#include "AthenaPoolUtilities/AthenaAttributeList.h"
+//#include "AthenaPoolUtilities/CondAttrListCollection.h"
+
+// IncidentSvc
+//#include "GaudiKernel/IIncidentSvc.h"
+
+// CLHEP
+#include "CLHEP/Units/SystemOfUnits.h"
+// Units
+//#include "GeoModelKernel/Units.h"
+//#define SYSTEM_OF_UNITS GeoModelKernelUnits // so we will get, e.g., 'GeoModelKernelUnits::cm'
+// ****
+
+// ROOT
+//#include "TFile.h"
+//#include "TTree.h"
+
+#include "G4RootAnalysisReader.hh"
+
+/** Constructor **/
+MagField::AtlasFieldSvc::AtlasFieldSvc(bool isAscii) :
+    //base_class(name,svc),
+    //base_class(name),
+    m_fullMapAscii ("bmagatlas_09_fullAsym20400.data"),
+    m_isAscii (isAscii),
+    m_fullMapFilename("full_bfieldmap_7730_20400_14m_version5.root"),
+    m_soleMapFilename("solenoid_bfieldmap_7730_0_14m_version5.root"),
+    m_toroMapFilename("toroid_bfieldmap_0_20400_14m_version5.root"),
+    m_mapSoleCurrent(7730.),
+    m_mapToroCurrent(20400.),
+    m_soleMinCurrent(1.0),
+    m_toroMinCurrent(1.0),
+    //m_useDCS(false),
+    //m_coolCurrentsFolderName("/EXT/DCS/MAGNETS/SENSORDATA"),
+    //m_useMapsFromCOOL(true),
+    //m_coolMapsFolderName("/GLOBAL/BField/Maps"),
+    m_useSoleCurrent(7730.),
+    m_useToroCurrent(20400.),
+    m_lockMapCurrents(false),
+    //m_mapHandle(),
+    //m_currentHandle(),
+    m_zone(),
+    m_meshZR(nullptr),
+    m_edge(),
+    m_edgeLUT(),
+    m_invq(),
+    m_zoneLUT(),
+    m_zmin(0.),
+    m_zmax(0.),
+    m_nz(0),
+    m_rmax(0.),
+    m_nr(0),
+    m_nphi(0)
+    /* ,
+       m_doManipulation(false),
+       m_manipulator("undefined") */
+{
+    
+//    declareProperty("FullMapFile", m_fullMapFilename, "File storing the full magnetic field map");
+//    declareProperty("SoleMapFile", m_soleMapFilename, "File storing the solenoid-only magnetic field map");
+//    declareProperty("ToroMapFile", m_toroMapFilename, "File storing the toroid-only magnetic field map");
+//    declareProperty("MapSoleCurrent", m_mapSoleCurrent, "Nominal solenoid current (A)");
+//    declareProperty("MapToroCurrent", m_mapToroCurrent, "Nominal toroid current (A)");
+//    declareProperty("SoleMinCurrent", m_soleMinCurrent, "Minimum solenoid current (A) for which solenoid is considered ON");
+//    declareProperty("ToroMinCurrent", m_toroMinCurrent, "Minimum toroid current (A) for which toroid is considered ON");
+//    declareProperty("UseDCS", m_useDCS, "Get magnet currents from DCS through COOL");
+//    declareProperty("COOLCurrentsFolderName", m_coolCurrentsFolderName, "Name of the COOL folder containing magnet currents");
+//    declareProperty("UseMapsFromCOOL", m_useMapsFromCOOL, "Get magnetic field map filenames from COOL");
+//    declareProperty("COOLMapsFolderName", m_coolMapsFolderName, "Name of the COOL folder containing field maps");
+//    declareProperty("UseSoleCurrent", m_useSoleCurrent, "Set actual solenoid current (A)");
+//    declareProperty("UseToroCurrent", m_useToroCurrent, "Set actual toroid current (A)");
+//    declareProperty("LockMapCurrents", m_lockMapCurrents, "Do not rescale currents (use the map values)");
+    /* declareProperty("DoManipulation", m_doManipulation, "Apply field manipulation");
+       declareProperty("ManipulatorTool", m_manipulator, "Tool handle for field manipulation"); */
+}
+
+/** Constructor **/
+MagField::AtlasFieldSvc::AtlasFieldSvc( const std::string& name, bool isAscii, bool solenoidOFF, bool toroidsOFF):
+    m_isAscii(isAscii),
+    m_mapSoleCurrent(7730.),
+    m_mapToroCurrent(20400.),
+    m_soleMinCurrent(1.0),
+    m_toroMinCurrent(1.0),
+    m_useSoleCurrent(7730.),
+    m_useToroCurrent(20400.),
+    m_lockMapCurrents(false),
+    m_zone(),
+    m_meshZR(nullptr),
+    m_edge(),
+    m_edgeLUT(),
+    m_invq(),
+    m_zoneLUT(),
+    m_zmin(0.),
+    m_zmax(0.),
+    m_nz(0),
+    m_rmax(0.),
+    m_nr(0),
+    m_nphi(0)
+    {
+        if (m_isAscii) {
+            m_fullMapAscii = name;
+            std::cout<<"Magnetic field map file is ascii, will open "<<name<<std::endl;
+        }
+        else {m_fullMapFilename = name;
+            std::cout<<"File is root, will open "<<name<<std::endl;
+        }
+        if(solenoidOFF) {
+            setSolenoidCurrent(0.);
+            m_toroMapFilename = name;
+            
+        }
+        if(toroidsOFF) {
+            setToroidsCurrent (0.);
+            m_soleMapFilename = name;
+        }
+    }
+
+MagField::AtlasFieldSvc::~AtlasFieldSvc()
+{
+    delete m_meshZR;
+}
+
+/** framework methods */
+bool MagField::AtlasFieldSvc::initialize()
+{
+    std::cout<< "initialize() ..." << std::endl;
+
+//    // determine map location from COOL, if available
+//    if ( m_useMapsFromCOOL ) {
+//      // Register callback
+//      StoreGateSvc* detStore;
+//      if ( service( "DetectorStore", detStore ).isFailure() ) {
+//          //ATH_MSG_FATAL( "Could not get detector store" );
+//          return false;
+//      }
+//      std::string folder( m_coolMapsFolderName );
+//        std::cout<<"maps will be chosen reading COOL folder " << folder << std::endl;
+//      if ( detStore->regFcn( &MagField::AtlasFieldSvc::updateMapFilenames, this,
+//                                 m_mapHandle, folder ).isFailure() ) {
+//              //ATH_MSG_FATAL( "Could not book callback for " << folder );
+//              return false;
+//      }
+//    }
+
+    // are we going to get the magnet currents from DCS?
+//    if ( m_useDCS ) {
+//        // Register callback
+//        StoreGateSvc* detStore;
+//        if ( service( "DetectorStore", detStore ).isFailure() ) {
+//            //ATH_MSG_FATAL( "Could not get detector store" );
+//            return false;
+//        }
+//        std::string folder( m_coolCurrentsFolderName );
+//        std::cout<<"magnet currents will be read from COOL folder " << folder<< std::endl;
+//        if ( detStore->regFcn( &MagField::AtlasFieldSvc::updateCurrent, this,
+//                               m_currentHandle, folder ).isFailure() ) {
+//            //ATH_MSG_FATAL( "Could not book callback for " << folder );
+//            return false;
+//        }
+//        std::cout<< "Booked callback for " << folder << std::endl;
+//        // actual initalization has to wait for the fist callback
+//    } else {
+//        std::cout<< "Currents are set-up by jobOptions - delaying map initialization until BeginRun incident happens" << std::endl;
+//
+//        ServiceHandle<IIncidentSvc> incidentSvc("IncidentSvc", name());
+//        if (incidentSvc.retrieve().isFailure()) {
+//            //ATH_MSG_FATAL( "Unable to retrieve the IncidentSvc" );
+//            return false;
+//        } else {
+//            incidentSvc->addListener( this, IncidentType::BeginRun );
+//            std::cout<< "Added listener to BeginRun incident" << std::endl;
+//        }
+    //}
+
+    // retrieve thread-local storage
+    AtlasFieldSvcTLS &tls = getAtlasFieldSvcTLS();
+
+    // clear the map for zero field
+    clearMap(tls);
+    setSolenoidCurrent(0.0);
+    setToroidsCurrent(0.0);
+
+    /* // retrieve the manipulator tool
+       if (m_doManipulation) {
+       std::cout<< "field will be manipulated, retrieving tool" << std::endl;
+       if (m_manipulator.retrieve().isFailure()) {
+       //ATH_MSG_FATAL( "unable to retrieve manipulation tool" );
+       } else {
+       std::cout<< "manipulation tool retrieved" << std::endl;
+       getFieldActual = &MagField::AtlasFieldSvc::getFieldManipulated;
+       }
+       } else {
+       std::cout<< "no manipulation set up" << std::endl;
+       getFieldActual = &MagField::AtlasFieldSvc::getFieldStandard;
+       } */
+
+    std::cout<< "initialize() successful" << std::endl;
+    return true;
+}
+
+void MagField::AtlasFieldSvc::handle()
+{
+   // get thread-local storage
+   AtlasFieldSvcTLS &tls = getAtlasFieldSvcTLS();
+   if (! importCurrents(tls))
+   {
+       std::cout<< "Failure in setting of currents" <<std::endl;
+       
+   }
+}
+
+bool MagField::AtlasFieldSvc::importCurrents(AtlasFieldSvcTLS &tls)
+{
+    std::cout<< "\nImportCurrents() ..." << std::endl;
+
+    // take the current values from JobOptions
+    double solcur(m_useSoleCurrent);
+    double torcur(m_useToroCurrent);
+    if ( solcur < m_soleMinCurrent ) {
+        solcur = 0.0;
+        std::cout<< "Solenoid is off." << std::endl;
+    }
+    if ( torcur < m_toroMinCurrent) {
+        torcur = 0.0;
+        std::cout<< "Toroids are off." << std::endl;
+    }
+    setSolenoidCurrent(solcur);
+    setToroidsCurrent(torcur);
+    // read the map file
+    if ( !initializeMap(tls)) {
+        //ATH_MSG_FATAL( "Failed to initialize field map" );
+        std::cout<< "FATAL! Failed to initialize field map" << std::endl;
+        return false;
+    }
+
+    std::cout<< "Currents imported and map initialized successfully!" << std::endl;
+    return true;
+}
+
+/** callback for possible magnet current update **/
+//bool MagField::AtlasFieldSvc::updateCurrent(IOVSVC_CALLBACK_ARGS)
+//{
+//    // get magnet currents from DCS
+//    double solcur(0.);
+//    double torcur(0.);
+//    bool gotsol(false);
+//    bool gottor(false);
+//
+//    // due to inconsistencies between CONDBR2 and OFLP200/COMP200 (the former includes channel names
+//    // in the /EXT/DCS/MAGNETS/SENSORDATA folder, the latter don't), we try to read currents in
+//    // both ways
+//    bool hasChanNames(false);
+//
+//    std::cout<< "Attempt 1 at reading currents from DCS (using channel name)" << std::endl;
+//    for ( CondAttrListCollection::const_iterator itr = m_currentHandle->begin();
+//      itr != m_currentHandle->end(); ++itr ) {
+//
+//          std::string name = m_currentHandle->chanName(itr->first);
+//          std::cout<< "Trying to read from DCS: [channel name, index, value] " << name << " , " << itr->first << " , " << itr->second["value"].data<float>() << std::endl;
+//
+//      if (name.compare("") != 0) {
+//        hasChanNames = true;
+//      }
+//
+//          if ( name.compare("CentralSol_Current") == 0 ) {
+//              // channel 1 is solenoid current
+//              solcur = itr->second["value"].data<float>();
+//              gotsol = true;
+//          } else if ( name.compare("Toroids_Current") == 0 ) {
+//              // channel 3 is toroid current
+//              torcur = itr->second["value"].data<float>();
+//              gottor = true;
+//          }
+//    }
+//    if ( !hasChanNames ) {
+//        std::cout<< "Attempt 2 at reading currents from DCS (using channel index)" << std::endl;
+//        // in no channel is named, try again using channel index instead
+//        for ( CondAttrListCollection::const_iterator itr = m_currentHandle->begin();
+//              itr != m_currentHandle->end(); ++itr ) {
+//
+//              if ( itr->first == 1 ) {
+//                  // channel 1 is solenoid current
+//                  solcur = itr->second["value"].data<float>();
+//                  gotsol = true;
+//              } else if ( itr->first == 3 ) {
+//                  // channel 3 is toroid current
+//                  torcur = itr->second["value"].data<float>();
+//                  gottor = true;
+//              }
+//        }
+//    }
+//
+//    if ( !gotsol || !gottor ) {
+//        //if ( !gotsol ) ATH_MSG_ERROR( "Missing solenoid current in DCS information" );
+//        //if ( !gottor ) ATH_MSG_ERROR( "Missing toroid current in DCS information" );
+//        return false;
+//    }
+//    std::cout<< "Currents read from DCS: solenoid " << solcur << " toroid " << torcur << std::endl;
+//    // round to zero if close to zero
+//    if ( solcur < m_soleMinCurrent) {
+//        solcur = 0.0;
+//        std::cout<< "Solenoid is off" << std::endl;
+//    }
+//    if ( torcur < m_toroMinCurrent) {
+//        torcur = 0.0;
+//        std::cout<< "Toroids are off" << std::endl;
+//    }
+//    // did solenoid/toroid change status between on and off?
+//    bool solWasOn( solenoidOn() );
+//    bool torWasOn( toroidsOn() );
+//    setSolenoidCurrent( solcur );
+//    setToroidsCurrent( torcur );
+//    if ( solenoidOn() != solWasOn || toroidsOn() != torWasOn ) {
+//        // get thread-local storage
+//        AtlasFieldSvcTLS &tls = getAtlasFieldSvcTLS();
+//
+//        // map has changed. re-initialize the map
+//        if ( initializeMap(tls).isFailure() ) {
+//            //ATH_MSG_ERROR( "Failed to re-initialize field map" );
+//            return false;
+//        }
+//    } else {
+//        // map is still valid. just scale the currents
+//        if (!m_lockMapCurrents)
+//          scaleField();
+//        else
+//          std::cout<< "Currents are NOT scaled - using map values sole=" << m_mapSoleCurrent << " toro=" << m_mapToroCurrent << std::endl;
+//    }
+//
+//    return true;
+//}
+//
+///** callback for possible field map filenames update **/
+//bool MagField::AtlasFieldSvc::updateMapFilenames(IOVSVC_CALLBACK_ARGS)
+//{
+//    std::cout<< "reading magnetic field map filenames from COOL" << std::endl;
+//
+//    std::string fullMapFilename("");
+//    std::string soleMapFilename("");
+//    std::string toroMapFilename("");
+//
+//    for (CondAttrListCollection::const_iterator itr = m_mapHandle->begin(); itr != m_mapHandle->end(); ++itr) {
+//        const coral::AttributeList &attr = itr->second;
+//        const std::string &mapType = attr["FieldType"].data<std::string>();
+//        const std::string &mapFile = attr["MapFileName"].data<std::string>();
+//        const float soleCur = attr["SolenoidCurrent"].data<float>();
+//        const float toroCur = attr["ToroidCurrent"].data<float>();
+//
+//        std::cout<<"found map of type " << mapType << " with soleCur=" << soleCur << " toroCur=" << toroCur << " (path " << mapFile << ")")<< std::endl;
+//
+//    // first 5 letters are reserved (like "file:")
+//    const std::string mapFile_decoded = mapFile.substr(5);
+//    if (mapType == "GlobalMap") {
+//      fullMapFilename = mapFile_decoded;
+//      m_mapSoleCurrent = soleCur;
+//      m_mapToroCurrent = toroCur;
+//    } else if (mapType == "SolenoidMap") {
+//      soleMapFilename = mapFile_decoded;
+//    } else if (mapType == "ToroidMap") {
+//      toroMapFilename = mapFile_decoded;
+//    }
+//    // note: the idea is that the folder contains exactly three maps
+//    // (if it contains more than 3 maps, then this logic doesn't work perfectly)
+//    // nominal currents are read from the global map
+//    }
+//
+//    if (fullMapFilename == "" || soleMapFilename == "" || toroMapFilename == "") {
+//      //ATH_MSG_ERROR("unexpected content in COOL field map folder");
+//      return false;
+//    }
+//
+//    // check if maps really changed
+//    if (fullMapFilename != m_fullMapFilename || soleMapFilename != m_soleMapFilename || toroMapFilename != m_toroMapFilename) {
+//      std::cout<< "map set is new! reinitializing map"<< std::endl;
+//      m_fullMapFilename = fullMapFilename;
+//      m_soleMapFilename = soleMapFilename;
+//      m_toroMapFilename = toroMapFilename;
+//
+//      // retrieve the thread-local storage
+//      AtlasFieldSvcTLS &tls = getAtlasFieldSvcTLS();
+//
+//      // trigger map reinitialization
+//      if ( initializeMap(tls).isFailure() ) {
+//         //ATH_MSG_ERROR( "failed to re-initialize field map" );
+//         return false;
+//      }
+//    } else {
+//      std::cout<< "no need to update map set"<< std::endl;
+//    }
+//
+//    return true;
+//}
+
+//
+//  read and initialize map
+//
+bool MagField::AtlasFieldSvc::initializeMap(AtlasFieldSvcTLS &tls)
+{
+    std::cout<< "Initializing the field map (solenoidCurrent=" << getSolenoidCurrent() << " toroidCurrent=" << getToroidsCurrent() << ")" << std::endl;
+    // empty the current map first
+    clearMap(tls);
+
+    // determine the map to load
+    std::string mapFile("");
+    //ALL the Magnets are ON
+    if ( solenoidOn() && toroidsOn() ) {
+        if(m_isAscii) mapFile  = m_fullMapAscii;
+        else mapFile = m_fullMapFilename;
+        std::cout<<"mapFile::: "<<mapFile<<std::endl;
+    } else if ( solenoidOn() ) {
+        mapFile = m_soleMapFilename;
+    } else if ( toroidsOn() ) {
+        mapFile = m_toroMapFilename;
+    } else {
+        // all magnets OFF. no need to read map
+        return true;
+    }
+    // find the path to the map file
+    //std::string resolvedMapFile = PathResolver::find_file( mapFile.c_str(), "DATAPATH" );
+    std::string resolvedMapFile = mapFile.c_str();
+    if ( resolvedMapFile == "" ) {
+        std::cout<<"Field map file " << mapFile << " not found" <<std::endl;
+        return false;
+    }
+
+    if ( strstr(mapFile.c_str(), ".root") != 0 )
+    {
+        // read the ROOT map file
+        //if ( !readMap( resolvedMapFile.c_str() ) )
+        if ( !readMapRoot( resolvedMapFile ) )
+        {
+            std::cout<<"\nERROR! Magnetic field map cannot be read!"<<std::endl;
+            std::cout<<"Alternatively, you can set a constant Magnetic Field through the macro.g4 file."<<std::endl;
+            exit (-1);
+            return false;
+        }
+        
+    }else if ( strstr(mapFile.c_str(), ".data") != 0 )
+    {
+        std::filebuf fb;
+        if (fb.open (mapFile,std::ios::in))
+        {
+            std::istream is(&fb);
+            std::cout<<"Reading field map from " << mapFile  <<std::endl;
+            readMap(is);
+            fb.close();
+        }
+        else
+        {
+            std::cout<<"\nERROR! Magnetic field map file cannot be opened. Please make sure the file is available."<<std::endl;
+            std::cout<<"Alternatively, you can set a constant Magnetic Field through the macro.g4 file."<<std::endl;
+            
+            exit (-1);
+        }
+    }else
+    {
+        std::cout<<"ERROR: Sorry, Magnetic field map file extensions supported are .root or .data files!";
+        std::cout<<"Alternatively, you can set a constant Magnetic Field through the macro.g4 file."<<std::endl;
+        exit(-1);
+        
+    }
+    std::cout<< "Initialized the field map from " << resolvedMapFile << std::endl;
+    // scale magnet current as needed
+    if (!m_lockMapCurrents)
+      scaleField();
+    else
+      std::cout<< "Currents are NOT scaled - using map values sole=" << m_mapSoleCurrent << " toro=" << m_mapToroCurrent << std::endl;
+
+    return true;
+}
+
+void MagField::AtlasFieldSvc::scaleField()
+{
+    BFieldZone *solezone(0);
+    //
+    if ( solenoidOn() ) {
+        solezone = findZoneSlow( 0.0, 0.0, 0.0 );
+        if ( m_mapSoleCurrent > 0.0 &&
+             std::abs( getSolenoidCurrent()/m_mapSoleCurrent - 1.0 ) > 0.001 ) {
+            // scale the field in the solenoid zone
+            double factor = getSolenoidCurrent()/m_mapSoleCurrent;
+            solezone->scaleField( factor );
+            // remake the fast map
+            buildZR();
+            std::cout<< "Scaled the solenoid field by a factor " << factor << std::endl;
+        }
+    }
+    //
+    if ( toroidsOn() )
+    {
+        if ( m_mapToroCurrent > 0.0 &&
+             std::abs( getToroidsCurrent()/m_mapToroCurrent - 1.0 ) > 0.001 ) {
+            // scale the field in all zones except for the solenoid zone
+            double factor = getToroidsCurrent()/m_mapToroCurrent;
+            for ( unsigned i = 0; i < m_zone.size(); i++ ) {
+                if ( &(m_zone[i]) != solezone ) {
+                    m_zone[i].scaleField( factor );
+                }
+            }
+            std::cout<< "Scaled the toroid field by a factor " << factor << std::endl;
+        }
+    }
+}
+
+/** framework methods */
+bool MagField::AtlasFieldSvc::finalize()
+{
+    // finalization code would go here
+    //
+    std::cout<< "Finalize() AtlasFieldSvc successful!" << std::endl;
+    return true;
+}
+
+/* void MagField::AtlasFieldSvc::getFieldStandard(const double *xyz, double *bxyz, double *deriv) */
+void MagField::AtlasFieldSvc::getField(const double *xyz, double *bxyz, double *deriv) const
+{
+
+  const double &x(xyz[0]);
+  const double &y(xyz[1]);
+  const double &z(xyz[2]);
+  double r = std::sqrt(x * x + y * y);
+  double phi = std::atan2(y, x);
+
+  // retrieve the thread-local storage
+  AtlasFieldSvcTLS &tls = getAtlasFieldSvcTLS();
+  BFieldCache &cache = tls.cache;
+
+  // test if the TLS was initialized and the cache is valid
+  if ( !tls.isInitialized || !cache.inside(z, r, phi) ) {
+    // cache is invalid -> refresh cache
+    if (!fillFieldCache(z, r, phi, tls)) {
+
+      // caching failed -> outside the valid map volume
+      // return default field (0.1 gauss)
+      const double defaultB(0.1*CLHEP::gauss);
+      //std::cout<<"Cache failed, return default field !"<<std::sqrt(3*defaultB*defaultB)/CLHEP::tesla<<std::endl;
+      bxyz[0] = bxyz[1] = bxyz[2] = defaultB;
+      // return zero gradient if requested
+      if ( deriv ) {
+          for ( int i = 0; i < 9; i++ ) {
+            deriv[i] = 0.;
+          }
+      }
+      return;
+    }
+  }
+  // do interpolation
+  cache.getB(xyz, r, phi, bxyz, deriv);
+
+  // add biot savart component
+  if (tls.cond) {
+    const size_t condSize = tls.cond->size();
+    for (size_t i = 0; i < condSize; i++) {
+      (*tls.cond)[i].addBiotSavart(xyz, bxyz, deriv);
+    }
+  }
+} 
+
+/*
+void MagField::AtlasFieldSvc::getFieldManipulated(const double *xyz, double *bxyz, double *deriv)
+{
+  // this operation involves three steps:
+  //   - first we move the point at which the field is evaluated
+  //        ex1: solenoid translation by vector +a  =>  xyz -= a
+  //        ex2: solenoid rotation R by angle +phi  =>  rotate xyz by -phi (inverse rotation R_inv)
+  //   - then, we evaluate B in this new point
+  //        ex1: B(-a)
+  //        ex2: B(R_inv(xyz))
+  //   - then, we change the field
+  //        ex1: identity transformation 
+  //        ex2: rotate the field properly
+  
+  // step 1
+  double xyz_new[3];
+  m_manipulator->modifyPosition(xyz, xyz_new);
+
+  // step 2
+  getFieldStandard(xyz_new, bxyz, deriv);
+
+  // step 3
+  m_manipulator->modifyField(bxyz, deriv);
+}
+
+void MagField::AtlasFieldSvc::getField(const double *xyz, double *bxyz, double *deriv) {
+  (this->*this->getFieldActual)(xyz, bxyz, deriv);
+}
+*/
+
+void MagField::AtlasFieldSvc::getFieldZR(const double *xyz, double *bxyz, double *deriv) const
+{
+
+  const double &x(xyz[0]);
+  const double &y(xyz[1]);
+  const double &z(xyz[2]);
+  double r = sqrt(x * x + y * y);
+
+  // get thread-local storage
+  AtlasFieldSvcTLS &tls = getAtlasFieldSvcTLS();
+  BFieldCacheZR &cacheZR = tls.cacheZR;
+
+  // test if the TLS was initialized and the cache is valid
+  if ( !tls.isInitialized || !cacheZR.inside(z, r) ) {
+    // cache is invalid -> refresh cache
+    if (!fillFieldCacheZR(z, r, tls)) {
+      // caching failed -> outside the valid z-r map volume
+      // call the full version of getField()
+      getField(xyz, bxyz, deriv);
+      return;
+    }
+  }
+
+  // do interpolation
+  cacheZR.getB(xyz, r, bxyz, deriv);
+}
+
+//
+// Clear the map.
+// Subsequent call should return zero magnetic field.
+//
+void MagField::AtlasFieldSvc::clearMap(AtlasFieldSvcTLS &tls)
+{
+    tls.cache.invalidate();
+    tls.cacheZR.invalidate();
+
+    tls.cond = nullptr;
+    // Next lines clear m_zone, m_edge[3], m_edgeLUT[3], and m_zoneLUT and deallocate their memory.
+    std::vector<BFieldZone>().swap(m_zone);
+    for ( int i = 0; i < 3; i++ ) {
+        std::vector<double>().swap(m_edge[i]);
+        std::vector<int>().swap(m_edgeLUT[i]);
+    }
+    std::vector<const BFieldZone*>().swap(m_zoneLUT);
+    // Next lines ensure findZone() will fail
+    m_zmin = 0.0;
+    m_zmax = -1.0;
+    m_rmax = -1.0;
+    m_nz = m_nr = m_nphi = 0;
+    delete m_meshZR;
+    m_meshZR = nullptr;
+}
+
+//
+// Read the solenoid map from file.
+// The filename must end with ".root".
+//
+bool MagField::AtlasFieldSvc::readMap( const char* filename )
+{
+    if ( strstr(filename, ".root") != 0 ) {
+        std::cout<<"Sorry, ROOT map is not supported at the moment!"<<std::endl;
+        return false;
+    }
+    std::cout<<"This method should not be called, call readMapRoot!"<<std::endl;
+    return false;
+    
+//    std::cout<<"root: "<<filename<<std::endl;
+//    if ( strstr(filename, ".root") == 0 ) {
+//        std::cout<<"input file name '" << filename << "' does not end with .root"<< std::endl;
+//        //ATH_MSG_ERROR("input file name '" << filename << "' does not end with .root");
+//        return false;
+//    }
+//
+//    TFile* rootfile = new TFile(filename, "OLD");
+//    if ( ! rootfile ) {
+//        std::cout<<"failed to open " << filename<< std::endl;
+//        //ATH_MSG_ERROR("failed to open " << filename);
+//        return false;
+//    }
+//    std::cout<<"reading the map from " << filename<< std::endl;
+//    if ( !readMap(rootfile) ) {
+//        std::cout<<"something went wrong while trying to read the ROOT field map file"<<std::endl;
+//        //ATH_MSG_ERROR("something went wrong while trying to read the ROOT field map file");
+//        return false;
+//    }
+//
+//    rootfile->Close();
+//    delete rootfile;
+//    return true;
+}
+
+//
+// read an ASCII field map from istream
+// convert units m -> mm, and T -> kT
+//
+bool MagField::AtlasFieldSvc::readMap( std::istream& input )
+{
+    std::cout<<"Reading the map"<<std::endl;
+    const std::string myname("readMap()");
+    // first line contains version, date, time
+    std::string word;
+    int version;
+    int date;
+    int time;
+    input >> word >> version;
+    if ( word != "FORMAT-VERSION" ) {
+        std::cout<<"ERROR: "<< myname << ": found '" << word << "' instead of 'FORMAT-VERSION'"<<std::endl;
+        return false;
+    }
+    if ( version < 5 || version > 6) {
+        std::cout<<"ERROR: "<< myname << ": version number is " << version << " instead of 5 or 6"<<std::endl;
+        return false;
+    }
+    input >> word >> date;
+    if ( word != "DATE" ) {
+        std::cout<<"ERROR: "<< myname << ": found '" << word << "' instead of 'DATE'" <<std::endl;
+        return false;
+    }
+    input >> word >> time;
+    if ( word != "TIME" ) {
+        std::cout<<"ERROR: "<< myname << ": found '" << word << "' instead of 'TIME'" <<std::endl;
+        return false;
+    }
+
+    // read and skip header cards
+    int nheader;
+    input >> word >> nheader;
+    if ( word != "HEADERS" ) {
+        std::cout<<"ERROR: "<< myname << ": found '" << word << "' instead of 'HEADERS'" <<std::endl;
+        return false;
+    }
+    std::string restofline;
+    getline( input, restofline );
+    for ( int i = 0; i < nheader; i++ ) {
+        std::string header;
+        getline( input, header );
+    }
+
+    // read zone definitions
+    int nzone;
+    input >> word >> nzone;
+    if ( word != "ZONES" ) {
+        std::cout<<"ERROR: "<< myname << ": found '" << word << "' instead of 'ZONES'" <<std::endl;
+        return false;
+    }
+    std::vector<int> jz(nzone), nz(nzone);
+    std::vector<int> jr(nzone), nr(nzone);
+    std::vector<int> jphi(nzone), nphi(nzone);
+    std::vector<int> jbs(nzone), nbs(nzone);
+    std::vector<int> jcoil(nzone), ncoil(nzone);
+    std::vector<int> jfield(nzone), nfield(nzone);
+    std::vector<int> jaux(nzone), naux(nzone);
+    
+
+    for ( int i = 0; i < nzone; i++ )
+    {
+        int id;
+        int nrep, map; // unused
+        double z1, z2, r1, r2, phi1, phi2;
+        int nzrphi0; // unused
+        double tol; // unused
+        int mzn, mxsym, mrefl, mback; // unused
+        double qz, qr, qphi; // unused
+        double bscale;
+        input >> id >> nrep;
+	if ( version == 6 ) input >> map;
+        input >> z1 >> z2 >> nz[i]
+              >> r1 >> r2 >> nr[i]
+              >> phi1 >> phi2 >> nphi[i]
+              >> nzrphi0 >> tol
+              >> jbs[i] >> nbs[i]
+              >> jcoil[i] >> ncoil[i]
+              >> jz[i] >> jr[i] >> jphi[i]
+              >> jfield[i] >> nfield[i]
+              >> mzn;
+	if ( version == 6 ) input >> mxsym;
+        input >> mrefl >> mback
+              >> jaux[i] >> naux[i]
+              >> qz >> qr >> qphi >> bscale;
+        if ( id >= 0 ) { // remove dummy zone
+            z1 *= CLHEP::meter;
+            z2 *= CLHEP::meter;
+            r1 *= CLHEP::meter;
+            r2 *= CLHEP::meter;
+            phi1 *= CLHEP::degree;
+            phi2 *= CLHEP::degree;
+            bscale *= CLHEP::tesla;
+            BFieldZone zone( id, z1, z2, r1, r2, phi1, phi2, bscale );
+            m_zone.push_back(zone);
+        }
+    }
+
+    // read Biot-Savart data
+    int nbiot;
+    input >> word >> nbiot;
+    if ( word != "BIOT" ) {
+        std::cout<<"ERROR: "<< myname << ": found '" << word << "' instead of 'BIOT'" <<std::endl;
+        return false;
+    }
+    std::vector<BFieldCond> bslist;
+    for ( int i = 0; i < nbiot; i++ ) {
+        char dummy; // unused
+        char cfinite;
+        double xyz1[3], xyz2[3];
+        double phirot; // unused
+        double curr;
+        input >> dummy >> cfinite
+              >> xyz1[0] >> xyz1[1] >> xyz1[2]
+              >> xyz2[0] >> xyz2[1] >> xyz2[2]
+              >> phirot >> curr;
+        bool finite = ( cfinite == 'T' );
+        for ( int j = 0; j < 3; j++ ) {
+            xyz1[j] *= CLHEP::meter;
+            if ( finite ) xyz2[j] *= CLHEP::meter;
+        }
+        BFieldCond bs( finite, xyz1, xyz2, curr );
+        bslist.push_back(bs);
+    }
+    // attach them to the zones
+    for ( unsigned i = 0; i < m_zone.size(); i++ ) {
+        // copy the range that belongs to this zone
+        for ( int j = 0; j < nbs[i]; j++ ) {
+            // Fortran -> C conversion requires "-1"
+            m_zone[i].appendCond( bslist[jbs[i]+j-1] );
+        }
+    }
+
+    // read and skip coil data
+    int nc;
+    input >> word >> nc;
+    if ( word != "COIL" ) {
+        std::cout<<"ERROR: "<< myname << ": found '" << word << "' instead of 'COIL'" <<std::endl;
+        return false;
+    }
+    getline( input, restofline );
+    for ( int i = 0; i < nc; i++ ) {
+        std::string coildata;
+        getline( input, coildata );
+    }
+
+    // read and skip auxiliary array = list of subzones
+    int nauxarr;
+    input >> word >> nauxarr;
+    if ( word != "AUXARR" ) {
+        std::cout<<"ERROR: "<< myname << ": found '" << word << "' instead of 'AUXARR'" <<std::endl;
+        return false;
+    }
+    if ( version == 6 ) input >> word; // skip 'T'
+    for ( int i = 0; i < nauxarr; i++ ) {
+        int aux;
+        input >> aux;
+    }
+
+    // read mesh definition
+    int nmesh;
+    input >> word >> nmesh;
+    if ( word != "MESH" ) {
+        std::cout<<"ERROR: "<< myname << ": found '" << word << "' instead of 'MESH'" <<std::endl;
+        return false;
+    }
+    std::vector<double> meshlist;
+    for ( int i = 0; i < nmesh; i++ ) {
+        double mesh;
+        input >> mesh;
+        meshlist.push_back(mesh);
+    }
+    // attach them to the zones
+    for ( unsigned i = 0; i < m_zone.size(); i++ ) {
+        m_zone[i].reserve( nz[i], nr[i], nphi[i] );
+        for ( int j = 0; j < nz[i]; j++ ) {
+            m_zone[i].appendMesh( 0, meshlist[jz[i]+j-1]*CLHEP::meter );
+        }
+        for ( int j = 0; j < nr[i]; j++ ) {
+            m_zone[i].appendMesh( 1, meshlist[jr[i]+j-1]*CLHEP::meter );
+        }
+        for ( int j = 0; j < nphi[i]; j++ ) {
+            m_zone[i].appendMesh( 2, meshlist[jphi[i]+j-1] );
+        }
+    }
+
+    // read field values
+    int nf, nzlist;
+    std::string ftype, bytype;
+    input >> word >> nf >> nzlist >> ftype >> bytype;
+    if ( word != "FIELD" ) {
+        std::cout<<"ERROR: "<< myname << ": found '" << word << "' instead of 'FIELD'" <<std::endl;
+        return false;
+    }
+    if ( ftype != "I2PACK" ) {
+        std::cout<<"ERROR: "<< myname << ": found '" << ftype << "' instead of 'I2PACK'" <<std::endl;
+        return false;
+    }
+    if ( bytype != "FBYTE" ) {
+        std::cout<<"ERROR: "<< myname << ": found '" << bytype << "' instead of 'FBYTE'" <<std::endl;
+        return false;
+    }
+    // read zone by zone
+    for ( int i = 0; i < nzlist; i++ ) {
+        int izone, idzone, nfzone;
+        input >> izone >> idzone >> nfzone;
+        izone--; // fortran -> C++
+        if ( idzone != m_zone[izone].id() ) {
+            std::cout<<"ERROR: "<< myname << ": zone id " << idzone << " != " << m_zone[izone].id() <<std::endl;
+            //return StatusCode(2);
+            return true; // TO DO - it shoudl be recoverable - handle ! enum class ErrorCode : code_t { FAILURE = 0, SUCCESS = 1, RECOVERABLE = 2 };
+        }
+
+        std::vector<int> data[3];
+
+        // for field data in 2 bytes
+        for ( int j = 0; j < 3; j++ ) { // repeat z, r, phi
+            int ierr = read_packed_data( input, data[j] );
+            //if ( ierr != 0 ) return StatusCode(ierr);
+            if ( ierr != 0 ) return true; // TODO it could be 2 .. to handle!   enum class ErrorCode : code_t { FAILURE = 0, SUCCESS = 1, RECOVERABLE = 2 };
+            for ( int k = 0; k < nfzone; k++ ) {
+                // recover sign
+                data[j][k] = ( data[j][k]%2==0 ) ? data[j][k]/2 : -(data[j][k]+1)/2;
+                // second-order diff
+                if ( k >= 2 ) data[j][k] += 2*data[j][k-1] - data[j][k-2];
+            }
+        }
+        // store
+        for ( int k = 0; k < nfzone; k++ ) {
+            BFieldVector<short> B( data[0][k], data[1][k], data[2][k] );
+            m_zone[izone].appendField( B );
+        }
+
+        // skip fbyte
+        char c;
+        while ( input.good() ) {
+            input >> c;
+            if ( input.eof() || c == '}' ) break;
+        }
+    }
+
+    // build the LUTs and ZR zone
+    buildLUT();
+    buildZR();
+
+    return true;
+}
+
+//
+// write the map to a ROOT file
+//
+//void MagField::AtlasFieldSvc::writeMap( std::string filename ) const
+//{
+//    
+//    TFile* rootfile = new TFile(filename.c_str(), "RECREATE");
+//    //if ( rootfile == 0 ) return; // no file
+//    //if ( rootfile->cd() == false ) return; // could not make it current directory
+//    // define the tree
+//    TTree* tree = new TTree( "BFieldMap", "BFieldMap version 5" );
+//    TTree* tmax = new TTree( "BFieldMapSize", "Buffer size information" );
+//    int id;
+//    double zmin, zmax, rmin, rmax, phimin, phimax;
+//    double bscale;
+//    int ncond;
+//    //bool *finite;
+//    int *finite;
+//    double *p1x, *p1y, *p1z, *p2x, *p2y, *p2z;
+//    double *curr;
+//    int nmeshz, nmeshr, nmeshphi;
+//    double *meshz, *meshr, *meshphi;
+//    int nfield;
+//    double *fieldz, *fieldr, *fieldphi;
+//
+//    // prepare arrays - need to know the maximum sizes
+//    unsigned maxcond(0), maxmeshz(0), maxmeshr(0), maxmeshphi(0), maxfield(0);
+//    for ( unsigned i = 0; i < m_zone.size(); i++ ) {
+//        maxcond = std::max( maxcond, m_zone[i].ncond() );
+//        maxmeshz = std::max( maxmeshz, m_zone[i].nmesh(0) );
+//        maxmeshr = std::max( maxmeshr, m_zone[i].nmesh(1) );
+//        maxmeshphi = std::max( maxmeshphi, m_zone[i].nmesh(2) );
+//        maxfield = std::max( maxfield, m_zone[i].nfield() );
+//    }
+//    // store the maximum sizes
+//    tmax->Branch( "maxcond", &maxcond, "maxcond/i");
+//    tmax->Branch( "maxmeshz", &maxmeshz, "maxmeshz/i");
+//    tmax->Branch( "maxmeshr", &maxmeshr, "maxmeshr/i");
+//    tmax->Branch( "maxmeshphi", &maxmeshphi, "maxmeshphi/i");
+//    tmax->Branch( "maxfield", &maxfield, "maxfield/i");
+//    tmax->Fill();
+//    // prepare buffers
+//    //finite = new bool[maxcond];
+//    finite = new int[maxcond];
+//    p1x = new double[maxcond];
+//    p1y = new double[maxcond];
+//    p1z = new double[maxcond];
+//    p2x = new double[maxcond];
+//    p2y = new double[maxcond];
+//    p2z = new double[maxcond];
+//    curr = new double[maxcond];
+//    meshz = new double[maxmeshz];
+//    meshr = new double[maxmeshr];
+//    meshphi = new double[maxmeshphi];
+//    fieldz = new double[maxfield];
+//    fieldr = new double[maxfield];
+//    fieldphi = new double[maxfield];
+//    // define the tree branches
+//    tree->Branch( "id", &id, "id/I" );
+//    tree->Branch( "zmin", &zmin, "zmin/D" );
+//    tree->Branch( "zmax", &zmax, "zmax/D" );
+//    tree->Branch( "rmin", &rmin, "rmin/D" );
+//    tree->Branch( "rmax", &rmax, "rmax/D" );
+//    tree->Branch( "phimin", &phimin, "phimin/D" );
+//    tree->Branch( "phimax", &phimax, "phimax/D" );
+//    tree->Branch( "bscale", &bscale, "bscale/D" );
+//    tree->Branch( "ncond", &ncond, "ncond/I" );
+//    tree->Branch( "finite", finite, "finite[ncond]/I" );
+//    tree->Branch( "p1x", p1x, "p1x[ncond]/D" );
+//    tree->Branch( "p1y", p1y, "p1y[ncond]/D" );
+//    tree->Branch( "p1z", p1z, "p1z[ncond]/D" );
+//    tree->Branch( "p2x", p2x, "p2x[ncond]/D" );
+//    tree->Branch( "p2y", p2y, "p2y[ncond]/D" );
+//    tree->Branch( "p2z", p2z, "p2z[ncond]/D" );
+//    tree->Branch( "curr", curr, "curr[ncond]/D" );
+//    tree->Branch( "nmeshz", &nmeshz, "nmeshz/I" );
+//    tree->Branch( "meshz", meshz, "meshz[nmeshz]/D" );
+//    tree->Branch( "nmeshr", &nmeshr, "nmeshr/I" );
+//    tree->Branch( "meshr", meshr, "meshr[nmeshr]/D" );
+//    tree->Branch( "nmeshphi", &nmeshphi, "nmeshphi/I" );
+//    tree->Branch( "meshphi", meshphi, "meshphi[nmeshphi]/D" );
+//    tree->Branch( "nfield", &nfield, "nfield/I" );
+//    tree->Branch( "fieldz", fieldz, "fieldz[nfield]/D" );
+//    tree->Branch( "fieldr", fieldr, "fieldr[nfield]/D" );
+//    tree->Branch( "fieldphi", fieldphi, "fieldphi[nfield]/D" );
+//    // loop over zones to write
+//    for ( unsigned i = 0; i < m_zone.size(); i++ ) {
+//        const BFieldZone z = m_zone[i];
+//        id = z.id();
+//        zmin = z.zmin(); zmax = z.zmax();
+//        rmin = z.rmin(); rmax = z.rmax();
+//        phimin = z.phimin(); phimax = z.phimax();
+//        bscale = z.bscale();
+//        ncond = z.ncond();
+//        for ( int j = 0; j < ncond; j++ ) {
+//            const BFieldCond c = z.cond(j);
+//            finite[j] = c.finite();
+//            p1x[j] = c.p1(0);
+//            p1y[j] = c.p1(1);
+//            p1z[j] = c.p1(2);
+//            p2x[j] = c.p2(0);
+//            p2y[j] = c.p2(1);
+//            p2z[j] = c.p2(2);
+//            curr[j] = c.curr();
+//        }
+//        nmeshz = z.nmesh(0);
+//        for ( int j = 0; j < nmeshz; j++ ) {
+//            meshz[j] = z.mesh(0,j);
+//        }
+//        nmeshr = z.nmesh(1);
+//        for ( int j = 0; j < nmeshr; j++ ) {
+//            meshr[j] = z.mesh(1,j);
+//        }
+//        nmeshphi = z.nmesh(2);
+//        for ( int j = 0; j < nmeshphi; j++ ) {
+//            meshphi[j] = z.mesh(2,j);
+//        }
+//        nfield = z.nfield();
+//        for ( int j = 0; j < nfield; j++ ) {
+//            const BFieldVector<short> f = z.field(j);
+//            fieldz[j] = f.z();
+//            fieldr[j] = f.r();
+//            fieldphi[j] = f.phi();
+//        }
+//        tree->Fill();
+//    }
+//    rootfile->Write();
+//    // clean up
+//    delete[] finite;
+//    delete[] p1x;
+//    delete[] p1y;
+//    delete[] p1z;
+//    delete[] p2x;
+//    delete[] p2y;
+//    delete[] p2z;
+//    delete[] curr;
+//    delete[] meshz;
+//    delete[] meshr;
+//    delete[] meshphi;
+//    delete[] fieldz;
+//    delete[] fieldr;
+//    delete[] fieldphi;
+//}
+
+
+//bool MagField::AtlasFieldSvc::translateMap (std::string inFile,
+//                                            std::string outFile,
+//                                            std::string outTreeName,
+//                                            bool        rz,
+//                                            double      bScalor,
+//                                            double      lScalor) const{
+//
+//    std::cout << "Registering new ASCII FieldMap File : " << outFile << std::endl;
+//    TFile* rootFile = TFile::Open(inFile.c_str(), "RECREATE");
+//    if (!rootFile) std::cout << "Could not open '" << inFile << std::endl;
+//
+//    if ( rootFile->cd() == false ) {
+//        std::cout<<"Error, input file is not correct!!! "<<std::endl;
+//        return false;
+//    }
+//    // open the tree
+//    TTree* tree = (TTree*)rootFile->Get("BFieldMap");
+//    if ( tree == 0 ) {
+//        // no tree
+//        std::cout<<"Error translateMap(): TTree 'BFieldMap' does not exist in ROOT field map"<<std::endl;
+//        return false;
+//    }
+//    int id;
+//    double zmin, zmax, rmin, rmax, phimin, phimax;
+//    double bscale;
+//    int ncond;
+//    bool *finite;
+//    double *p1x, *p1y, *p1z, *p2x, *p2y, *p2z;
+//    double *curr;
+//    int nmeshz, nmeshr, nmeshphi;
+//    double *meshz, *meshr, *meshphi;
+//    int nfield;
+//    short *fieldz, *fieldr, *fieldphi;
+//    // define the fixed-sized branches first
+//    tree->SetBranchAddress( "id", &id );
+//    tree->SetBranchAddress( "zmin", &zmin );
+//    tree->SetBranchAddress( "zmax", &zmax );
+//    tree->SetBranchAddress( "rmin", &rmin );
+//    tree->SetBranchAddress( "rmax", &rmax );
+//    tree->SetBranchAddress( "phimin", &phimin );
+//    tree->SetBranchAddress( "phimax", &phimax );
+//    tree->SetBranchAddress( "bscale", &bscale );
+//    tree->SetBranchAddress( "ncond", &ncond );
+//    tree->SetBranchAddress( "nmeshz", &nmeshz );
+//    tree->SetBranchAddress( "nmeshr", &nmeshr );
+//    tree->SetBranchAddress( "nmeshphi", &nmeshphi );
+//    tree->SetBranchAddress( "nfield", &nfield );
+//    // prepare arrays - need to know the maximum sizes
+//    // open the tree of buffer sizes (may not exist in old maps)
+//    unsigned maxcond(0), maxmeshz(0), maxmeshr(0), maxmeshphi(0), maxfield(0);
+//    TTree* tmax = (TTree*)rootFile->Get("BFieldMapSize");
+//    if ( tmax != 0 ) {
+//        tmax->SetBranchAddress( "maxcond", &maxcond );
+//        tmax->SetBranchAddress( "maxmeshz", &maxmeshz );
+//        tmax->SetBranchAddress( "maxmeshr", &maxmeshr );
+//        tmax->SetBranchAddress( "maxmeshphi", &maxmeshphi );
+//        tmax->SetBranchAddress( "maxfield", &maxfield );
+//        tmax->GetEntry(0);
+//    } else { // "BFieldMapSize" tree does not exist
+//        for ( int i = 0; i < tree->GetEntries(); i++ ) {
+//            tree->GetEntry(i);
+//            maxcond = std::max( maxcond, (unsigned)ncond );
+//            maxmeshz = std::max( maxmeshz, (unsigned)nmeshz );
+//            maxmeshr = std::max( maxmeshr, (unsigned)nmeshr );
+//            maxmeshphi = std::max( maxmeshphi, (unsigned)nmeshphi );
+//            maxfield = std::max( maxfield, (unsigned)nfield );
+//        }
+//    }
+//    finite = new bool[maxcond];
+//    p1x = new double[maxcond];
+//    p1y = new double[maxcond];
+//    p1z = new double[maxcond];
+//    p2x = new double[maxcond];
+//    p2y = new double[maxcond];
+//    p2z = new double[maxcond];
+//    curr = new double[maxcond];
+//    meshz = new double[maxmeshz];
+//    meshr = new double[maxmeshr];
+//    meshphi = new double[maxmeshphi];
+//    fieldz = new short[maxfield];
+//    fieldr = new short[maxfield];
+//    fieldphi = new short[maxfield];
+//    // define the variable length branches
+//    tree->SetBranchAddress( "finite", finite );
+//    tree->SetBranchAddress( "p1x", p1x );
+//    tree->SetBranchAddress( "p1y", p1y );
+//    tree->SetBranchAddress( "p1z", p1z );
+//    tree->SetBranchAddress( "p2x", p2x );
+//    tree->SetBranchAddress( "p2y", p2y );
+//    tree->SetBranchAddress( "p2z", p2z );
+//    tree->SetBranchAddress( "curr", curr );
+//    tree->SetBranchAddress( "meshz", meshz );
+//    tree->SetBranchAddress( "meshr", meshr );
+//    tree->SetBranchAddress( "meshphi", meshphi );
+//    tree->SetBranchAddress( "fieldz", fieldz );
+//    tree->SetBranchAddress( "fieldr", fieldr );
+//    tree->SetBranchAddress( "fieldphi", fieldphi );
+//
+//    // reserve the space for m_zone so that it won't move as the vector grows
+////    m_zone.reserve( tree->GetEntries() );
+//    // read all tree and store
+////    for ( int i = 0; i < tree->GetEntries(); i++ )
+////    {
+////        tree->GetEntry(i);
+////        BFieldZone z( id, zmin, zmax, rmin, rmax, phimin, phimax, bscale );
+////        m_zone.push_back(z);
+////        m_zone.back().reserve( nmeshz, nmeshr, nmeshphi );
+////        for ( int j = 0; j < ncond; j++ ) {
+////            double p1[3], p2[3];
+////            p1[0] = p1x[j];
+////            p1[1] = p1y[j];
+////            p1[2] = p1z[j];
+////            p2[0] = p2x[j];
+////            p2[1] = p2y[j];
+////            p2[2] = p2z[j];
+////            BFieldCond cond( finite[j], p1, p2, curr[j] );
+////            m_zone.back().appendCond(cond);
+////        }
+////        for ( int j = 0; j < nmeshz; j++ ) {
+////            m_zone.back().appendMesh( 0, meshz[j] );
+////        }
+////        for ( int j = 0; j < nmeshr; j++ ) {
+////            m_zone.back().appendMesh( 1, meshr[j] );
+////        }
+////        for ( int j = 0; j < nmeshphi; j++ ) {
+////            m_zone.back().appendMesh( 2, meshphi[j] );
+////        }
+////        for ( int j = 0; j < nfield; j++ ) {
+////            BFieldVector<short> field( fieldz[j], fieldr[j], fieldphi[j] );
+////            m_zone.back().appendField( field );
+////        }
+////    }
+//    // clean up
+//    tree->Delete();
+//    delete[] finite;
+//    delete[] p1x;
+//    delete[] p1y;
+//    delete[] p1z;
+//    delete[] p2x;
+//    delete[] p2y;
+//    delete[] p2z;
+//    delete[] curr;
+//    delete[] meshz;
+//    delete[] meshr;
+//    delete[] meshphi;
+//    delete[] fieldz;
+//    delete[] fieldr;
+//    delete[] fieldphi;
+//
+//
+////    if (rz) {
+////
+////            /// [1] Read in field map file
+////            std::cout << "Opening new txt/csv input File : " << outFile << std::endl;
+////            std::ifstream map_file(inFile.c_str(), std::ios::in);
+////            // [1] Read in file and fill values
+////            std::string line;
+////            double      rpos = 0., zpos = 0.;
+////            double      br = 0., bz = 0.;
+////
+//
+//
+//}
+
+bool MagField::AtlasFieldSvc::readMapRoot (std::string filename)
+{
+    
+    // Create (or get) analysis reader
+    G4RootAnalysisReader* analysisReader = G4RootAnalysisReader::Instance();
+    analysisReader->SetVerboseLevel(-1);
+    
+    // Define a base file name
+    analysisReader->SetFileName(filename);
+    int ntupleId =analysisReader->GetNtuple("BFieldMap");
+    
+    if ( ntupleId < 0 ) {
+        // no tree
+        std::cout<<"readMap(): TTree 'BFieldMap' does not exist in ROOT field map"<<std::endl;
+        return false;
+    }
+    int id;
+    double zmin, zmax, rmin, rmax, phimin, phimax;
+    double bscale;
+    int ncond;
+    //int finite;
+    //double p1x, p1y, p1z, p2x, p2y, p2z;
+    //double curr;
+    int nmeshz, nmeshr, nmeshphi;
+    //double meshz, meshr, meshphi;
+    int nfield;
+    //double fieldz, fieldr, fieldphi;
+    
+    if ( ntupleId >= 0 ) {
+        
+        analysisReader->SetNtupleIColumn(ntupleId,"id", id);
+        analysisReader->SetNtupleDColumn(ntupleId,"zmin", zmin);
+        analysisReader->SetNtupleDColumn(ntupleId,"zmax", zmax);
+        analysisReader->SetNtupleDColumn(ntupleId,"rmin", rmin);
+        analysisReader->SetNtupleDColumn(ntupleId,"rmax",rmax);
+        analysisReader->SetNtupleDColumn(ntupleId,"phimin", phimin);
+        analysisReader->SetNtupleDColumn(ntupleId,"phimax", phimax);
+        analysisReader->SetNtupleDColumn(ntupleId,"bscale", bscale);
+        analysisReader->SetNtupleIColumn(ntupleId,"ncond", ncond);
+        analysisReader->SetNtupleIColumn(ntupleId,"nmeshz", nmeshz);
+        analysisReader->SetNtupleIColumn(ntupleId,"nmeshr", nmeshr);
+        analysisReader->SetNtupleIColumn(ntupleId,"nmeshphi", nmeshphi);
+        analysisReader->SetNtupleIColumn(ntupleId,"nfield", nfield);
+    }
+    
+    // prepare arrays - need to know the maximum sizes
+    // open the tree of buffer sizes (may not exist in old maps)
+    
+    int maxcond(0), maxmeshz(0), maxmeshr(0), maxmeshphi(0), maxfield(0);
+    
+    int ntupleId2 =analysisReader->GetNtuple("BFieldMapSize");
+    //TTree* tmax = (TTree*)rootfile->Get("BFieldMapSize");
+    if ( ntupleId2 >= 0 ) {
+        
+        analysisReader->SetNtupleIColumn(ntupleId2, "maxcond", maxcond);
+        analysisReader->SetNtupleIColumn(ntupleId2, "maxmeshz", maxmeshz);
+        analysisReader->SetNtupleIColumn(ntupleId2, "maxmeshr", maxmeshr );
+        analysisReader->SetNtupleIColumn(ntupleId2, "maxmeshphi", maxmeshphi);
+        analysisReader->SetNtupleIColumn(ntupleId2, "maxfield", maxfield);
+        
+        analysisReader->GetNtupleRow(ntupleId2); //Get one entry
+        std::cout<<"maxcond: "<<maxcond<<"\tmaxmeshz: "<<maxmeshz<<"\tmaxmeshr: "<<maxmeshr<<"\tmaxmeshphi: "<<maxmeshphi<<"\tmaxfield: "<<maxfield<<std::endl;
+    }
+    std::vector<int> finite(maxcond);
+    std::vector<double> p1x(maxcond);
+    std::vector<double> p1y(maxcond);
+    std::vector<double> p1z(maxcond);
+    std::vector<double> p2x(maxcond);
+    std::vector<double> p2y(maxcond);
+    std::vector<double> p2z(maxcond);
+    std::vector<double> curr(maxcond);
+    std::vector<double> meshz(maxmeshz);
+    std::vector<double> meshr (maxmeshr);
+    std::vector<double> meshphi(maxmeshphi);
+    std::vector<double> fieldz (maxfield);
+    std::vector<double> fieldr (maxfield);
+    std::vector<double> fieldphi (maxfield);
+    
+    analysisReader->SetNtupleIColumn(ntupleId,"finite", finite);
+    analysisReader->SetNtupleDColumn(ntupleId,"p1x", p1x);
+    analysisReader->SetNtupleDColumn(ntupleId,"p1y", p1y);
+    analysisReader->SetNtupleDColumn(ntupleId,"p1z", p1z);
+    analysisReader->SetNtupleDColumn(ntupleId,"p2x", p2x);
+    analysisReader->SetNtupleDColumn(ntupleId,"p2y", p2y);
+    analysisReader->SetNtupleDColumn(ntupleId,"p2z", p2z);
+    analysisReader->SetNtupleDColumn(ntupleId,"curr", curr );
+    analysisReader->SetNtupleDColumn(ntupleId,"meshz", meshz);
+    analysisReader->SetNtupleDColumn(ntupleId,"meshr", meshr);
+    analysisReader->SetNtupleDColumn(ntupleId,"meshphi", meshphi);
+    analysisReader->SetNtupleDColumn(ntupleId,"fieldz", fieldz);
+    analysisReader->SetNtupleDColumn(ntupleId,"fieldr", fieldr);
+    analysisReader->SetNtupleDColumn(ntupleId,"fieldphi", fieldphi);
+    
+    std::cout << "Ntuples reading" << std::endl;
+    // reserve the space for m_zone so that it won't move as the vector grows
+    m_zone.reserve( 406 );
+    //int counter = 0;
+    while ( analysisReader->GetNtupleRow(ntupleId) )
+    {
+//        std::cout << "  Entry: "<<counter<<std::endl;
+//        counter++;
+//        std::cout << "  id: " << id << "\tzmin: " << zmin <<"\tzmax: " << zmax <<std::endl;
+//        std::cout << "  rmin: " << rmin << "\trmax: " << rmax <<std::endl;
+//        std::cout << "  phimin: " << phimin << "\tphimax: " << phimax <<"\tbscale: "<<bscale<<std::endl;
+        
+        BFieldZone z( id, zmin, zmax, rmin, rmax, phimin, phimax, bscale );
+        m_zone.push_back(z);
+        m_zone.back().reserve( nmeshz, nmeshr, nmeshphi );
+        for ( int j = 0; j < ncond; j++ ) {
+            double p1[3], p2[3];
+            p1[0] = p1x[j];
+            p1[1] = p1y[j];
+            p1[2] = p1z[j];
+            p2[0] = p2x[j];
+            p2[1] = p2y[j];
+            p2[2] = p2z[j];
+            bool fini = (bool) finite[j];
+            BFieldCond cond( fini, p1, p2, curr[j] );
+            m_zone.back().appendCond(cond);
+        }
+        for ( int j = 0; j < nmeshz; j++ ) {
+            m_zone.back().appendMesh( 0, meshz[j] );
+        }
+        for ( int j = 0; j < nmeshr; j++ ) {
+            m_zone.back().appendMesh( 1, meshr[j] );
+        }
+        for ( int j = 0; j < nmeshphi; j++ ) {
+            m_zone.back().appendMesh( 2, meshphi[j] );
+        }
+        for ( int j = 0; j < nfield; j++ ) {
+            BFieldVector<short> field( fieldz[j], fieldr[j], fieldphi[j] );
+            m_zone.back().appendField( field );
+        }
+    }
+    
+    // build the LUTs
+    buildLUT();
+    buildZR();
+    return true;
+}
+
+//
+// read the map from a ROOT file.
+// returns 0 if successful.
+//
+//bool MagField::AtlasFieldSvc::readMap( TFile* rootfile )
+//{
+//
+//    if ( rootfile == 0 ) {
+//      // no file
+//      //ATH_MSG_ERROR("readMap(): unable to read field map, no TFile given");
+//      std::cout<<"readMap(): unable to read field map, no TFile given"<<std::endl;
+//      return false;
+//    }
+//    if ( rootfile->cd() == false ) {
+//      // could not make it current directory
+//      //ATH_MSG_ERROR("readMap(): unable to cd() into the ROOT field map TFile");
+//      std::cout<<"readMap(): unable to cd() into the ROOT field map TFile"<<std::endl;
+//      return false;
+//    }
+//    // open the tree
+//    TTree* tree = (TTree*)rootfile->Get("BFieldMap");
+//    if ( tree == 0 ) {
+//      // no tree
+//      //ATH_MSG_ERROR("readMap(): TTree 'BFieldMap' does not exist in ROOT field map");
+//      std::cout<<"readMap(): TTree 'BFieldMap' does not exist in ROOT field map"<<std::endl;
+//      return false;
+//    }
+//    int id;
+//    double zmin, zmax, rmin, rmax, phimin, phimax;
+//    double bscale;
+//    int ncond;
+//    bool *finite;
+//    double *p1x, *p1y, *p1z, *p2x, *p2y, *p2z;
+//    double *curr;
+//    int nmeshz, nmeshr, nmeshphi;
+//    double *meshz, *meshr, *meshphi;
+//    int nfield;
+//    short *fieldz, *fieldr, *fieldphi;
+//    // define the fixed-sized branches first
+//    tree->SetBranchAddress( "id", &id );
+//    tree->SetBranchAddress( "zmin", &zmin );
+//    tree->SetBranchAddress( "zmax", &zmax );
+//    tree->SetBranchAddress( "rmin", &rmin );
+//    tree->SetBranchAddress( "rmax", &rmax );
+//    tree->SetBranchAddress( "phimin", &phimin );
+//    tree->SetBranchAddress( "phimax", &phimax );
+//    tree->SetBranchAddress( "bscale", &bscale );
+//    tree->SetBranchAddress( "ncond", &ncond );
+//    tree->SetBranchAddress( "nmeshz", &nmeshz );
+//    tree->SetBranchAddress( "nmeshr", &nmeshr );
+//    tree->SetBranchAddress( "nmeshphi", &nmeshphi );
+//    tree->SetBranchAddress( "nfield", &nfield );
+//    // prepare arrays - need to know the maximum sizes
+//    // open the tree of buffer sizes (may not exist in old maps)
+//    unsigned maxcond(0), maxmeshz(0), maxmeshr(0), maxmeshphi(0), maxfield(0);
+//    TTree* tmax = (TTree*)rootfile->Get("BFieldMapSize");
+//    if ( tmax != 0 ) {
+//        tmax->SetBranchAddress( "maxcond", &maxcond );
+//        tmax->SetBranchAddress( "maxmeshz", &maxmeshz );
+//        tmax->SetBranchAddress( "maxmeshr", &maxmeshr );
+//        tmax->SetBranchAddress( "maxmeshphi", &maxmeshphi );
+//        tmax->SetBranchAddress( "maxfield", &maxfield );
+//        tmax->GetEntry(0);
+//    } else { // "BFieldMapSize" tree does not exist
+//        for ( int i = 0; i < tree->GetEntries(); i++ ) {
+//            tree->GetEntry(i);
+//            maxcond = std::max( maxcond, (unsigned)ncond );
+//            maxmeshz = std::max( maxmeshz, (unsigned)nmeshz );
+//            maxmeshr = std::max( maxmeshr, (unsigned)nmeshr );
+//            maxmeshphi = std::max( maxmeshphi, (unsigned)nmeshphi );
+//            maxfield = std::max( maxfield, (unsigned)nfield );
+//        }
+//    }
+//    finite = new bool[maxcond];
+//    p1x = new double[maxcond];
+//    p1y = new double[maxcond];
+//    p1z = new double[maxcond];
+//    p2x = new double[maxcond];
+//    p2y = new double[maxcond];
+//    p2z = new double[maxcond];
+//    curr = new double[maxcond];
+//    meshz = new double[maxmeshz];
+//    meshr = new double[maxmeshr];
+//    meshphi = new double[maxmeshphi];
+//    fieldz = new short[maxfield];
+//    fieldr = new short[maxfield];
+//    fieldphi = new short[maxfield];
+//    // define the variable length branches
+//    tree->SetBranchAddress( "finite", finite );
+//    tree->SetBranchAddress( "p1x", p1x );
+//    tree->SetBranchAddress( "p1y", p1y );
+//    tree->SetBranchAddress( "p1z", p1z );
+//    tree->SetBranchAddress( "p2x", p2x );
+//    tree->SetBranchAddress( "p2y", p2y );
+//    tree->SetBranchAddress( "p2z", p2z );
+//    tree->SetBranchAddress( "curr", curr );
+//    tree->SetBranchAddress( "meshz", meshz );
+//    tree->SetBranchAddress( "meshr", meshr );
+//    tree->SetBranchAddress( "meshphi", meshphi );
+//    tree->SetBranchAddress( "fieldz", fieldz );
+//    tree->SetBranchAddress( "fieldr", fieldr );
+//    tree->SetBranchAddress( "fieldphi", fieldphi );
+//
+//    // reserve the space for m_zone so that it won't move as the vector grows
+//    m_zone.reserve( tree->GetEntries() );
+//    // read all tree and store
+//    for ( int i = 0; i < tree->GetEntries(); i++ ) {
+//        tree->GetEntry(i);
+//        BFieldZone z( id, zmin, zmax, rmin, rmax, phimin, phimax, bscale );
+//        m_zone.push_back(z);
+//        m_zone.back().reserve( nmeshz, nmeshr, nmeshphi );
+//        for ( int j = 0; j < ncond; j++ ) {
+//            double p1[3], p2[3];
+//            p1[0] = p1x[j];
+//            p1[1] = p1y[j];
+//            p1[2] = p1z[j];
+//            p2[0] = p2x[j];
+//            p2[1] = p2y[j];
+//            p2[2] = p2z[j];
+//            BFieldCond cond( finite[j], p1, p2, curr[j] );
+//            m_zone.back().appendCond(cond);
+//        }
+//        for ( int j = 0; j < nmeshz; j++ ) {
+//            m_zone.back().appendMesh( 0, meshz[j] );
+//        }
+//        for ( int j = 0; j < nmeshr; j++ ) {
+//            m_zone.back().appendMesh( 1, meshr[j] );
+//        }
+//        for ( int j = 0; j < nmeshphi; j++ ) {
+//            m_zone.back().appendMesh( 2, meshphi[j] );
+//        }
+//        for ( int j = 0; j < nfield; j++ ) {
+//            BFieldVector<short> field( fieldz[j], fieldr[j], fieldphi[j] );
+//            m_zone.back().appendField( field );
+//        }
+//    }
+//    // clean up
+//    tree->Delete();
+//    delete[] finite;
+//    delete[] p1x;
+//    delete[] p1y;
+//    delete[] p1z;
+//    delete[] p2x;
+//    delete[] p2y;
+//    delete[] p2z;
+//    delete[] curr;
+//    delete[] meshz;
+//    delete[] meshr;
+//    delete[] meshphi;
+//    delete[] fieldz;
+//    delete[] fieldr;
+//    delete[] fieldphi;
+//    // build the LUTs
+//    buildLUT();
+//    buildZR();
+//
+//    return true;
+//}
+
+//
+// utility function used by readMap()
+//
+int MagField::AtlasFieldSvc::read_packed_data( std::istream& input, std::vector<int>& data ) const
+{
+    const std::string myname("BFieldMap::read_packed_data()");
+
+    data.resize(0);
+    char mode = 'u';
+    char c;
+    while ( input.good() ) {
+        input >> c;
+        if ( input.eof() ) return 0;
+        else if (c == '}') { // end of record
+            return 0;
+        }
+        else if (c == 'z') { // series of zeros
+            int n;
+            int ierr = read_packed_int( input, n );
+            if ( ierr != 0 ) return ierr;
+            for ( int i = 0; i < n; i++ ) {
+                data.push_back(0);
+            }
+        }
+        else if (c >= 'u' && c <= 'y') { // mode change
+            mode = c;
+        }
+        else if (c <= ' ' || c > 'z') {
+            //ATH_MSG_ERROR( myname << ": unexpected letter '" << c << "' in input" );
+            return 3;
+        }
+        else { // normal letter in the range '!' - 't'
+            switch (mode) {
+            case 'u':
+                {
+                    input.putback( c );
+                    int n;
+                    int ierr = read_packed_int( input, n );
+                    if ( ierr != 0 ) return ierr;
+                    data.push_back(n);
+                }
+                break;
+            case 'v':
+                {
+                    int n = c - '!';
+                    for ( int i = 0; i < 4; i++ ) {
+                        input >> c;
+                        data.push_back(c - '!' + 84*(n%3));
+                        n = n/3;
+                    }
+                }
+                break;
+            case 'w':
+                data.push_back(c - '!');
+                break;
+            case 'x':
+                {
+                    int n = c - '!';
+                    data.push_back(n/9);
+                    data.push_back(n%9);
+                }
+                break;
+            case 'y':
+                {
+                    int n = c - '!';
+                    data.push_back(n/27);
+                    data.push_back((n/9)%3);
+                    data.push_back((n/3)%3);
+                    data.push_back(n%3);
+                }
+                break;
+            }
+        }
+    }
+    return 0;
+}
+
+//
+// utility function used by read_packed_data()
+//
+int MagField::AtlasFieldSvc::read_packed_int( std::istream &input, int &n ) const
+{
+    const std::string myname("BFieldMap::read_packed_int()");
+    n = 0;
+    char c;
+    input >> c;
+    while ( c >= '!' && c <= 'J') {
+        n = 42*n + c - '!';
+        input >> c;
+    }
+    if ( c >= 'K' && c <= 't' ) {
+        n = 42*n + c - 'K';
+    } else {
+        //ATH_MSG_ERROR( myname << ": unexpected letter '" << c << "' in input" );
+        return 4;
+    }
+    return 0;
+}
+
+//
+// Search for the zone that contains a point (z, r, phi)
+// This is a linear-search version, used only to construct the LUT.
+//
+BFieldZone* MagField::AtlasFieldSvc::findZoneSlow( double z, double r, double phi )
+{
+    for ( int j = m_zone.size()-1; j >= 0; --j ) {
+        if ( m_zone[j].inside( z, r, phi ) ) return &m_zone[j];
+    }
+    return 0;
+}
+
+//
+// Build the look-up table used by FindZone().
+// Called by readMap()
+// It also calls buildLUT() for all zones.
+//
+void MagField::AtlasFieldSvc::buildLUT()
+{
+    // make lists of (z,r,phi) edges of all zones
+    for ( int j = 0; j < 3; j++ ) { // z, r, phi
+        for ( unsigned i = 0; i < m_zone.size(); i++ ) {
+            double e[2];
+            e[0] = m_zone[i].min(j);
+            e[1] = m_zone[i].max(j);
+            for ( int k = 0; k < 2; k++ ) {
+                // for the phi edge, fit into [-pi,pi]
+                if ( j==2 && e[k] > M_PI ) e[k] -= 2.0*M_PI;
+                m_edge[j].push_back(e[k]);
+            }
+        }
+        // add -pi and +pi to phi, just in case
+        m_edge[2].push_back(-M_PI);
+        m_edge[2].push_back(M_PI);
+        // sort
+        sort( m_edge[j].begin(), m_edge[j].end() );
+        // remove duplicates
+        // must do this by hand to allow small differences due to rounding in phi
+        int index = 0;
+        for ( unsigned k = 1; k < m_edge[j].size(); k++ ) {
+            if ( fabs(m_edge[j][index] - m_edge[j][k]) < 1.0e-6 ) continue;
+            m_edge[j][++index] = m_edge[j][k];
+        }
+        m_edge[j].resize( index+1 );
+        // because of the small differences allowed in the comparison above,
+        // m_edge[][] values do not exactly match the m_zone[] boundaries.
+        // we have to fix this up in order to avoid invalid field values
+        // very close to the zone boundaries.
+        for ( unsigned i = 0; i < m_zone.size(); i++ ) {
+            for ( unsigned k = 0; k < m_edge[j].size(); k++ ) {
+                if ( fabs(m_zone[i].min(j) - m_edge[j][k]) < 1.0e-6 ) {
+                    m_zone[i].adjustMin(j,m_edge[j][k]);
+                }
+                if ( fabs(m_zone[i].max(j) - m_edge[j][k]) < 1.0e-6 ) {
+                    m_zone[i].adjustMax(j,m_edge[j][k]);
+                }
+            }
+        }
+    }
+    // build LUT for edge finding
+    for ( int j = 0; j < 3; j++ ) { // z, r, phi
+        // find the size of the smallest interval
+        double width = m_edge[j].back() - m_edge[j].front();
+        double q(width);
+        for ( unsigned i = 0; i < m_edge[j].size()-1; i++ ) {
+            q = std::min( q, m_edge[j][i+1] - m_edge[j][i] );
+        }
+        // find the number of cells in the LUT
+        int n = int(width/q) + 1;
+        q = width/(n+0.5);
+        m_invq[j] = 1.0/q;
+        n++;
+        // fill the LUT
+        int m = 0;
+        for ( int i = 0; i < n; i++ ) { // index of LUT
+            if ( q*i+m_edge[j].front() > m_edge[j][m+1] ) m++;
+            m_edgeLUT[j].push_back(m);
+        }
+    }
+    // store min/max for speedup
+    m_zmin=m_edge[0].front();
+    m_zmax=m_edge[0].back();
+    m_rmax=m_edge[1].back();
+    
+//    std::cout<<" m_zmin: "<<m_zmin<<std::endl;
+//    std::cout<<" m_zmax: "<<m_zmax<<std::endl;
+//    std::cout<<" m_rmax: "<<m_rmax<<std::endl;
+    
+    // build LUT for zone finding
+    m_nz = m_edge[0].size() - 1;
+    m_nr = m_edge[1].size() - 1;
+    m_nphi = m_edge[2].size() - 1;
+    m_zoneLUT.reserve( m_nz*m_nr*m_nphi );
+    for ( int iz = 0; iz < m_nz; iz++ ) {
+        double z = 0.5*(m_edge[0][iz]+m_edge[0][iz+1]);
+        for ( int ir = 0; ir < m_nr; ir++ ) {
+            double r = 0.5*(m_edge[1][ir]+m_edge[1][ir+1]);
+            for ( int iphi = 0; iphi < m_nphi; iphi++ ) {
+                double phi = 0.5*(m_edge[2][iphi]+m_edge[2][iphi+1]);
+                const BFieldZone* zone = findZoneSlow( z, r, phi );
+                m_zoneLUT.push_back( zone );
+            }
+        }
+    }
+    // build LUT in each zone
+    for ( unsigned i = 0; i < m_zone.size(); i++ ) {
+        m_zone[i].buildLUT();
+    }
+}
+
+
+//
+// Build the z-r 2d map for fast solenoid field
+//
+void MagField::AtlasFieldSvc::buildZR()
+{
+    // delete if previously allocated
+    delete m_meshZR;
+
+    // find the solenoid zone
+    // solenoid zone always covers 100 < R < 1000, but not necessarily R < 100
+    // so we search for the zone that contains a point at R = 200, Z = 0
+    const BFieldZone *solezone = findZone( 0.0, 200.0, 0.0 );
+
+    // instantiate the new ZR map with the same external coverage as the solenoid zone
+    // make sure R = 0 is covered
+    m_meshZR = new BFieldMeshZR( solezone->zmin(), solezone->zmax(), 0.0, solezone->rmax() );
+
+    // reserve the right amount of memroy
+    unsigned nmeshz = solezone->nmesh(0);
+    unsigned nmeshr = solezone->nmesh(1);
+    if ( solezone->rmin() > 0.0 ) nmeshr++;
+    m_meshZR->reserve( nmeshz, nmeshr );
+
+    // copy the mesh structure in z/r
+    // take care of R = 0 first
+    if ( solezone->rmin() > 0.0 ) {
+        m_meshZR->appendMesh( 1, 0.0 );
+    }
+    // copy the rest
+    for ( int i = 0; i < 2; i++ ) { // z, r
+        for ( unsigned j = 0; j < solezone->nmesh(i); j++ ) { // loop over mesh points
+            m_meshZR->appendMesh( i, solezone->mesh( i, j ) );
+        }
+    }
+
+    // loop through the mesh and compute the phi-averaged field
+    for ( unsigned iz = 0; iz < m_meshZR->nmesh(0); iz++ ) { // loop over z
+        double z = m_meshZR->mesh( 0, iz );
+        for ( unsigned ir = 0; ir < m_meshZR->nmesh(1); ir++ ) { // loop over r
+            double r = m_meshZR->mesh( 1, ir );
+            const int nphi(200); // number of phi slices to average
+            double Br = 0.0;
+            double Bz = 0.0;
+            for ( int iphi = 0; iphi < nphi; iphi++ ) {
+                double phi = double(iphi)/double(nphi)*2.*M_PI;
+                double xyz[3];
+                xyz[0] = r*cos(phi);
+                xyz[1] = r*sin(phi);
+                xyz[2] = z;
+                double B[3];
+                solezone->getB( xyz, B, 0 );
+                Br += B[0]*cos(phi) + B[1]*sin(phi);
+                Bz += B[2];
+            }
+            Br *= 1.0/double(nphi);
+            Bz *= 1.0/double(nphi);
+            m_meshZR->appendField( BFieldVectorZR( Bz, Br ) );
+        }
+    }
+
+    // build the internal LUT
+    m_meshZR->buildLUT();
+}
+
+//
+// Approximate memory footprint
+//
+int MagField::AtlasFieldSvc::memSize() const
+{
+    int size = 0;
+    size += sizeof(BFieldCache);
+    size += sizeof(BFieldCacheZR);
+    for ( unsigned i = 0; i < m_zone.size(); i++ ) {
+        size += m_zone[i].memSize();
+    }
+    for ( int i = 0; i < 3; i++ ) {
+        size += sizeof(double)*m_edge[i].capacity();
+        size += sizeof(int)*m_edgeLUT[i].capacity();
+    }
+    size += sizeof(BFieldZone*)*m_zoneLUT.capacity();
+    if ( m_meshZR ) {
+        size += m_meshZR->memSize();
+    }
+    return size;
+}
+
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/AtlasFieldSvc.h b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/AtlasFieldSvc.h
new file mode 100644
index 0000000000000000000000000000000000000000..10648a456668e27e91ea23304c9905fa5a6b0853
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/AtlasFieldSvc.h
@@ -0,0 +1,285 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+///////////////////////////////////////////////////////////////////
+// AtlasFieldSvc.h, (c) ATLAS Detector software
+///////////////////////////////////////////////////////////////////
+
+#ifndef MAGFIELDSERVICES_ATLASFIELDSVC_H
+#define MAGFIELDSERVICES_ATLASFIELDSVC_H 1
+
+// FrameWork includes
+//#include "AthenaBaseComps/AthService.h"
+//#include "GaudiKernel/ToolHandle.h"
+//#include "GaudiKernel/IIncidentListener.h"
+
+// MagField includes
+#include "IMagFieldSvc.h"
+#include "AtlasFieldSvcTLS.h"
+#include "BFieldCache.h"
+#include "BFieldCacheZR.h"
+#include "BFieldCond.h"
+#include "BFieldZone.h"
+#include "BFieldMeshZR.h"
+#include "IMagFieldManipulator.h"
+
+// STL includes
+#include <vector>
+#include <iostream>
+
+// forward declarations
+//class CondAttrListCollection;
+class BFieldZone;
+//class TFile;
+//class Incident;
+
+namespace MagField {
+
+  /** @class AtlasFieldSvc
+      @author Elmar.Ritsch -at- cern.ch
+    */
+    class AtlasFieldSvc: public IMagFieldSvc {
+  //class AtlasFieldSvc : public extends<AthService, IMagFieldSvc, IIncidentListener> {
+    public:
+
+      /** Constructor with parameters */
+      AtlasFieldSvc(bool isAscii=true);
+    
+      /** Constructor with parameters */
+      AtlasFieldSvc( const std::string& name, bool isAscii=true, bool solenoidOFF = false, bool toroidsOFF = false);
+
+      /** Destructor */
+      virtual ~AtlasFieldSvc();
+
+      /** Athena algorithm's interface methods */
+//      virtual bool  initialize() override;
+//      virtual bool  finalize() override;
+        virtual bool  initialize() ;
+        virtual bool  finalize() ;
+
+      /** Read **/
+      virtual void handle();
+        
+      /** writeMap, can be enabled when fROOT dependency is on **/
+      //void writeMap( std::string filename ) const;
+
+      /** Call back for possible magnet current update **/
+      //StatusCode updateCurrent(IOVSVC_CALLBACK_ARGS);
+
+      /** Call back for possible magnet filename update **/
+      //StatusCode updateMapFilenames(IOVSVC_CALLBACK_ARGS);
+
+      /** get B field value at given position */
+      /** xyz[3] is in mm, bxyz[3] is in kT */
+      /** if deriv[9] is given, field derivatives are returned in kT/mm */
+      virtual void getField(const double *xyz, double *bxyz, double *deriv = nullptr) const override final;
+      virtual void getFieldZR(const double *xyz, double *bxyz, double *deriv = nullptr) const override final;
+        
+      double GetUseSolenoidCurrent(){return m_useSoleCurrent;}
+      void   SetUseSolenoidCurrent(double current){m_useSoleCurrent = current;}
+      double GetUseToroidsCurrent(){return m_useToroCurrent;}
+      void   SetUseToroidsCurrent(double current){m_useToroCurrent = current;}
+        
+    private:
+      /** Retrieve, initialize and return a thread-local storage object */
+      inline struct AtlasFieldSvcTLS &getAtlasFieldSvcTLS() const;
+        
+
+      /* // Methods called to get field
+         // pointer to actual function
+	 typedef void (AtlasFieldSvc::*FuncPtr)(const double *, double *, double *);
+	 FuncPtr getFieldActual;
+	 // standard calculation
+	 void getFieldStandard(const double *xyz, double *bxyz, double *deriv = 0);
+	 // manipulated field
+	 void getFieldManipulated(const double *xyz, double *bxyz, double *deriv = 0);
+      */
+
+      // Functions used by getField[ZR]
+      // search for a "zone" to which the point (z,r,phi) belongs
+      inline const BFieldZone* findZone( double z, double r, double phi ) const;
+      // slow search is used during initialization to build the LUT
+      BFieldZone* findZoneSlow( double z, double r, double phi );
+      // fill the cache. return true if successful
+      // return false if the position is outside the valid map volume
+      inline bool fillFieldCache(double z, double r, double phi, AtlasFieldSvcTLS &tls) const;
+      inline bool fillFieldCacheZR(double z, double r, AtlasFieldSvcTLS &tls) const;
+
+      // set currents from when DCS is not read
+      bool importCurrents(AtlasFieldSvcTLS &tls);
+      // initialize map
+      bool initializeMap(AtlasFieldSvcTLS &tls);
+      // read the field map from an ASCII or ROOT file
+      bool readMap( const char* filename );
+      bool readMap( std::istream& input );
+      /** readMap, can be enabled when fROOT dependency is on **/
+      //bool readMap( TFile* rootfile );
+      /** read the field map from a ROOT file, no fROOT dependency needed**/
+      bool readMapRoot (std::string filename);
+      
+      // translate the map from root file into ascii file and write it out
+      bool translateMap (std::string inFile,
+                           std::string outFile,
+                           std::string outTreeName,
+                           bool        rz,
+                           double      bScalor = 1.,
+                           double      lScalor = 1.) const;
+      // clear the field map
+      void clearMap(AtlasFieldSvcTLS &tls);
+
+      // utility functions used by readMap
+      int read_packed_data( std::istream& input, std::vector<int>& data ) const;
+      int read_packed_int( std::istream& input, int &n ) const;
+      void buildLUT();
+      void buildZR();
+
+      // scale field according to the current
+      void scaleField();
+
+      /** approximate memory footprint in bytes */
+      int memSize() const;
+
+      /** Data Members **/
+      std::string m_fullMapAscii;   // all magnets on
+      bool m_isAscii;               //read the map from ascii file if true
+      // field map names root
+      std::string m_fullMapFilename; // all magnets on
+      std::string m_soleMapFilename; // solenoid on / toroid off
+      std::string m_toroMapFilename; // toroid on / solenoid off
+      // current associated with the map
+      double m_mapSoleCurrent; // solenoid current in A
+      double m_mapToroCurrent; // toroid current in A
+      // threshold below which currents are considered zero
+      double m_soleMinCurrent; // minimum solenoid current to be considered ON
+      double m_toroMinCurrent; // minimum toroid current to be considered ON
+      // flag to use magnet current from DCS in COOL
+      //bool m_useDCS;
+      // COOL folder name containing current information
+      //std::string m_coolCurrentsFolderName;
+      // flag to read magnet map filenames from COOL
+      //bool m_useMapsFromCOOL;
+      // COOL folder name containing field maps
+      //std::string m_coolMapsFolderName;
+        // actual current if DCS is not in use
+      double m_useSoleCurrent; // solenoid current in A
+      double m_useToroCurrent; // toroid current in A
+      // flag to skip current rescale and use map currents as they are
+      bool m_lockMapCurrents;
+        
+
+      // handle for COOL field map filenames
+      //const DataHandle<CondAttrListCollection> m_mapHandle;
+      // handle for COOL currents
+      //const DataHandle<CondAttrListCollection> m_currentHandle;
+
+      // full 3d map (made of multiple zones)
+      std::vector<BFieldZone>        m_zone;
+
+      // fast 2d map (made of one zone)
+      BFieldMeshZR                  *m_meshZR;
+
+      // data members used in zone-finding
+      std::vector<double>            m_edge[3];    // zone boundaries in z, r, phi
+      std::vector<int>               m_edgeLUT[3]; // look-up table for zone edges
+      double                         m_invq[3];    // 1/stepsize in m_edgeLUT
+      std::vector<const BFieldZone*> m_zoneLUT; // look-up table for zones
+      // more data members to speed up zone-finding
+      double                         m_zmin;   // minimum z
+      double                         m_zmax;   // maximum z
+      int                            m_nz;     // number of z bins in zoneLUT
+      double                         m_rmax;   // maximum r
+      int                            m_nr;     // number of r bins in zoneLUT
+      int                            m_nphi;   // number of phi bins in zoneLUT
+
+      /* // handle for field manipulator, if any
+	 bool m_doManipulation;
+	 ToolHandle<IMagFieldManipulator> m_manipulator; */
+
+  };
+}
+
+// inline functions
+
+//
+// Initialize and return a thread-local storage object
+//
+struct MagField::AtlasFieldSvcTLS&
+MagField::AtlasFieldSvc::getAtlasFieldSvcTLS() const {
+  static thread_local AtlasFieldSvcTLS tls = AtlasFieldSvcTLS();
+  // return thread-local object
+  return tls;
+}
+
+//
+// Search for the zone that contains a point (z, r, phi)
+// Fast version utilizing the LUT.
+//
+const BFieldZone*
+MagField::AtlasFieldSvc::findZone( double z, double r, double phi ) const
+{
+  // make sure it's inside the largest zone
+  // NB: the sign of the logic is chosen in order to return 0 on NaN inputs
+  if ( z >= m_zmin && z <= m_zmax && r <= m_rmax ) {
+    // find the edges of the zone
+    // z
+    const std::vector<double>& edgez(m_edge[0]);
+    int iz = int(m_invq[0]*(z-m_zmin)); // index to LUT
+    iz = m_edgeLUT[0][iz]; // tentative index from LUT
+    if ( z > edgez[iz+1] ) iz++;
+    // r
+    const std::vector<double>& edger(m_edge[1]);
+    int ir = int(m_invq[1]*r); // index to LUT - note minimum r is always 0
+    ir = m_edgeLUT[1][ir]; // tentative index from LUT
+    if ( r > edger[ir+1] ) ir++;
+    // phi
+    const std::vector<double>& edgephi(m_edge[2]);
+    int iphi = int(m_invq[2]*(phi+M_PI)); // index to LUT - minimum phi is -pi
+    iphi = m_edgeLUT[2][iphi]; // tentative index from LUT
+    if ( phi > edgephi[iphi+1] ) iphi++;
+    // use LUT to get the zone
+    return m_zoneLUT[(iz*m_nr+ir)*m_nphi+iphi];
+  } else {
+     // std::cout<<"z is :"<<z<<" and should be >= "<<m_zmin<<"  && z <= "<<m_zmax<< "  && r is "<<r <<" and shoud be <= "<<m_rmax<<std::endl;
+    return 0;
+  }
+}
+
+/** fill given magnetic field zone */
+bool
+MagField::AtlasFieldSvc::fillFieldCache(double z, double r, double phi, AtlasFieldSvcTLS &tls) const
+{
+  // search for the zone
+  const BFieldZone* zone = findZone( z, r, phi );
+  if ( zone == 0 ) {
+      // outsize all zones
+      return false;
+  }
+  // fill the cache
+  zone->getCache( z, r, phi, tls.cache );
+
+  // pointer to the conductors in the zone
+  tls.cond = zone->condVector();
+
+  // set a flag that the thread-local storage is initialized
+  tls.isInitialized = true;
+
+  return true;
+}
+
+/** fill Z-R cache for solenoid */
+bool
+MagField::AtlasFieldSvc::fillFieldCacheZR(double z, double r, AtlasFieldSvcTLS &tls) const
+{
+  // is it inside the solenoid zone?
+  if ( m_meshZR && m_meshZR->inside( z, r ) ) {
+    // fill the cache
+    m_meshZR->getCache( z, r, tls.cacheZR );
+  } else {
+    // outside solenoid
+    return false;
+  }
+  return true;
+}
+
+#endif //> !MAGFIELDSERVICES_ATLASFIELDSVC_H
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/AtlasFieldSvcTLS.h b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/AtlasFieldSvcTLS.h
new file mode 100644
index 0000000000000000000000000000000000000000..314b6de9a1e88270860d10be6f98e44c2c5412d9
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/AtlasFieldSvcTLS.h
@@ -0,0 +1,48 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+ * @author Elmar.Ritsch -at- cern.ch
+ * @author Robert.Langenberg -at- cern.ch
+ * @date March 2017
+ * @brief Thread-local storage object used by MagField::AtlasFieldSvc
+ */
+
+#ifndef MAGFIELDSERVICES_ATLASFIELDSVCTLS_H
+#define MAGFIELDSERVICES_ATLASFIELDSVCTLS_H 1
+
+// MagField includes
+#include "BFieldCond.h"
+#include "BFieldZone.h"
+#include "BFieldMeshZR.h"
+
+namespace MagField {
+
+/** @class AtlasFieldSvcTLS
+ *
+ *  @brief Thread-local storage object used by MagField::AtlasFieldSvc
+ *
+ *  @author Elmar.Ritsch -at- cern.ch
+ *  @author Robert.Langenberg -at- cern.ch
+ */
+struct AtlasFieldSvcTLS {
+
+  /// Constructor
+  AtlasFieldSvcTLS() : isInitialized(false), cond(nullptr), cache(), cacheZR() { ; }
+
+  /// Is the current AtlasFieldSvcTLS object properly initialized
+  bool isInitialized;
+
+  /// Pointer to the conductors in the current field zone (to compute Biot-Savart component)
+  const std::vector<BFieldCond> *cond;
+
+  /// Full 3d field
+  BFieldCache cache;
+  /// Fast 2d field
+  BFieldCacheZR cacheZR;
+};
+
+}  // namespace MagField
+
+#endif  // MAGFIELDSERVICES_ATLASFIELDSVCTLS_H
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldCache.h b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldCache.h
new file mode 100644
index 0000000000000000000000000000000000000000..a60ab96c2b6600168477385e2b17d8a15494c9b8
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldCache.h
@@ -0,0 +1,127 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//
+// BFieldCache.h
+//
+// Cashe of one bin of the magnetic field map.
+// Defined by ranges in z, r, phi, and the B vectors at the 8 corners of the "bin".
+//
+// Masahiro Morii, Harvard University
+//
+#ifndef BFIELDCACHE_H
+#define BFIELDCACHE_H
+
+#include <iostream>
+#include <cmath>
+#include "BFieldVector.h"
+
+class BFieldCache {
+public:
+    // default constructor sets unphysical boundaries, so that inside() will fail
+    BFieldCache() : m_zmin(0.0), m_zmax(0.0), m_rmin(0.0), m_rmax(0.0), m_phimin(0.0), m_phimax(-1.0) {;}
+    // make this cache invalid, so that inside() will fail
+    void invalidate() { m_phimin = 0.0; m_phimax = -1.0; }
+    // set the z, r, phi range that defines the bin
+    void setRange( double zmin, double zmax, double rmin, double rmax, double phimin, double phimax )
+    { m_zmin = zmin; m_zmax = zmax; m_rmin = rmin; m_rmax = rmax; m_phimin = phimin; m_phimax = phimax;
+      m_invz = 1.0/(zmax-zmin); m_invr = 1.0/(rmax-rmin); m_invphi = 1.0/(phimax-phimin); }
+    // set the field values at each corner
+    void setField( int i, const BFieldVector<double> &field ) { for(int j=0; j<3; j++) m_field[j][i] = field[j]; }
+    void setField( int i, const BFieldVector<short> &field ) { for(int j=0; j<3; j++) m_field[j][i] = field[j]; }    // set the multiplicative factor for the field vectors
+    void setBscale( double bscale ) { m_scale = bscale; }
+    // test if (z, r, phi) is inside this bin
+    bool inside( double z, double r, double phi ) const
+    { if ( phi < m_phimin ) phi += 2.0*M_PI;
+      return ( phi >= m_phimin && phi <= m_phimax && z >= m_zmin && z <= m_zmax && r >= m_rmin && r <= m_rmax ); }
+    // interpolate the field and return B[3].
+    // also compute field derivatives if deriv[9] is given.
+    inline void getB( const double *xyz, double r, double phi, double *B, double *deriv=0 ) const;
+private:
+    double m_zmin, m_zmax; // bin range in z
+    double m_rmin, m_rmax; // bin range in r
+    double m_phimin, m_phimax; // bin range in phi
+    float m_invz, m_invr, m_invphi; // 1/(bin size) in z, r, phi
+    float m_field[3][8]; // (Bz,Br,Bphi) at 8 corners of the bin
+    float m_scale; // unit of m_field in kT
+};
+
+// inline methods
+
+void
+BFieldCache::getB( const double *xyz, double r, double phi, double *B, double *deriv ) const
+{
+    const double &x(xyz[0]);
+    const double &y(xyz[1]);
+    const double &z(xyz[2]);
+    // make sure phi is inside [m_phimin,m_phimax]
+    if ( phi < m_phimin ) phi += 2*M_PI;
+    // fractional position inside this bin
+    float fz = (z-m_zmin)*m_invz;
+    float gz = 1.0 - fz;
+    float fr = (r-m_rmin)*m_invr;
+    float gr = 1.0 - fr;
+    float fphi = (phi-m_phimin)*m_invphi;
+    float gphi = 1.0 - fphi;
+    // interpolate field values in z, r, phi
+    float Bzrphi[3];
+    for ( int i = 0; i < 3; i++ ) { // z, r, phi components
+        const float *field = m_field[i];
+        Bzrphi[i] = m_scale*( gz*( gr*( gphi*field[0] + fphi*field[1] ) +
+                                   fr*( gphi*field[2] + fphi*field[3] ) ) +
+                              fz*( gr*( gphi*field[4] + fphi*field[5] ) +
+                                   fr*( gphi*field[6] + fphi*field[7] ) ) );
+    }
+    // convert (Bz,Br,Bphi) to (Bx,By,Bz)
+    float invr, c, es;
+    if ( r > 0.0 ) {
+        invr = 1.0/r;
+        c = x*invr;
+        es = y*invr;
+    } else {
+        invr = 0.0;
+        c = cos(m_phimin);
+        es = sin(m_phimin);
+    }
+    B[0] = Bzrphi[1]*c - Bzrphi[2]*es;
+    B[1] = Bzrphi[1]*es + Bzrphi[2]*c;
+    B[2] = Bzrphi[0];
+
+    // compute field derivatives if requested
+    if ( deriv ) {
+        float sz = m_scale*m_invz;
+        float esr = m_scale*m_invr;
+        float sphi = m_scale*m_invphi;
+        float dBdz[3], dBdr[3], dBdphi[3];
+        for ( int j = 0; j < 3; j++ ) { // Bz, Br, Bphi components
+            const float *field = m_field[j];
+            dBdz[j]   = sz*( gr*( gphi*(field[4]-field[0]) + fphi*(field[5]-field[1]) ) +
+                             fr*( gphi*(field[6]-field[2]) + fphi*(field[7]-field[3]) ) );
+            dBdr[j]   = esr*( gz*( gphi*(field[2]-field[0]) + fphi*(field[3]-field[1]) ) +
+                             fz*( gphi*(field[6]-field[4]) + fphi*(field[7]-field[5]) ) );
+            dBdphi[j] = sphi*( gz*( gr*(field[1]-field[0]) + fr*(field[3]-field[2]) ) +
+                               fz*( gr*(field[5]-field[4]) + fr*(field[7]-field[6]) ) );
+        }
+        // convert to cartesian coordinates
+        float cc = c*c;
+        float cs = c*es;
+        float ss = es*es;
+        float ccinvr = cc*invr;
+        float csinvr = cs*invr;
+        float ssinvr = ss*invr;
+        float sinvr = es*invr;
+        float cinvr = c*invr;
+        deriv[0] = cc*dBdr[1] - cs*dBdr[2] - csinvr*dBdphi[1] + ssinvr*dBdphi[2] + sinvr*B[1];
+        deriv[1] = cs*dBdr[1] - ss*dBdr[2] + ccinvr*dBdphi[1] - csinvr*dBdphi[2] - cinvr*B[1];
+        deriv[2] = c*dBdz[1] - es*dBdz[2];
+        deriv[3] = cs*dBdr[1] + cc*dBdr[2] - ssinvr*dBdphi[1] - csinvr*dBdphi[2] - sinvr*B[0];
+        deriv[4] = ss*dBdr[1] + cs*dBdr[2] + csinvr*dBdphi[1] + ccinvr*dBdphi[2] + cinvr*B[0];
+        deriv[5] = es*dBdz[1] + c*dBdz[2];
+        deriv[6] = c*dBdr[0] - sinvr*dBdphi[0];
+        deriv[7] = es*dBdr[0] + cinvr*dBdphi[0];
+        deriv[8] = dBdz[0];
+    }
+}
+
+#endif
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldCacheZR.h b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldCacheZR.h
new file mode 100644
index 0000000000000000000000000000000000000000..a4fcc5369d9109e36587c7fd562acfd6d3e5116d
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldCacheZR.h
@@ -0,0 +1,105 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//
+// BFieldCacheZR.h
+//
+// Cashe of one bin of the magnetic field map.
+// Defined by ranges in z, r, and the (Bz, Br) vectors at the 4 corners of the "bin".
+//
+// Masahiro Morii, Harvard University
+//
+#ifndef BFIELDCACHEZR_H
+#define BFIELDCACHEZR_H
+
+#include "BFieldVectorZR.h"
+#include <iostream>
+#include <cmath>
+
+class BFieldCacheZR {
+public:
+    // default constructor sets unphysical boundaries, so that inside() will fail
+    BFieldCacheZR() : m_zmin(0.0), m_zmax(-1.0), m_rmin(0.0), m_rmax(-1.0) {;}
+    // invalidate this cache, so that inside() will fail
+    void invalidate() { m_rmin = 0.0; m_rmax = -1.0; }
+    // set the z, r range that defines the bin
+    void setRange( double zmin, double zmax, double rmin, double rmax )
+    { m_zmin = zmin; m_zmax = zmax; m_rmin = rmin; m_rmax = rmax;
+      m_invz = 1.0/(zmax-zmin); m_invr = 1.0/(rmax-rmin); }
+    // set the field values at each corner
+    void setField( int i, const BFieldVectorZR &field )
+    { m_field[0][i] = field[0]; m_field[1][i] = field[1]; }
+    // set the multiplicative factor for the field vectors
+    // test if (z, r) is inside this bin
+    bool inside( double z, double r ) const
+    { return ( z >= m_zmin && z <= m_zmax && r >= m_rmin && r <= m_rmax ); }
+    // interpolate the field and return B[3].
+    // also compute field derivatives if deriv[9] is given.
+    inline void getB( const double *xyz, double r, double *B, double *deriv=0 ) const;
+private:
+    double m_zmin, m_zmax; // bin range in z
+    double m_rmin, m_rmax; // bin range in r
+    float  m_invz, m_invr; // 1/(bin size) in z, r
+    float  m_field[2][4];  // (Bz,Br) at 4 corners of the bin
+};
+
+// inline methods
+
+void
+BFieldCacheZR::getB( const double *xyz, double r, double *B, double *deriv ) const
+{
+    const double &x(xyz[0]);
+    const double &y(xyz[1]);
+    const double &z(xyz[2]);
+    // fractional position inside this bin
+    float fz = (z-m_zmin)*m_invz;
+    float gz = 1.0 - fz;
+    float fr = (r-m_rmin)*m_invr;
+    float gr = 1.0 - fr;
+    // interpolate field values in z, r
+    float Bzr[2];
+    for ( int i = 0; i < 2; i++ ) { // z, r components
+        const float *field = m_field[i];
+        Bzr[i] = gz*( gr*field[0] + fr*field[1] ) + fz*( gr*field[2] + fr*field[3] );
+    }
+    // convert (Bz,Br) to (Bx,By,Bz)
+    float invr;
+    if ( r > 0.0 ) {
+        invr = 1.0/r;
+    } else {
+        invr = 0.0;
+    }
+    float c(x*invr);
+    float es(y*invr);
+    B[0] = Bzr[1]*c;
+    B[1] = Bzr[1]*es;
+    B[2] = Bzr[0];
+
+    // compute field derivatives if requested
+    if ( deriv ) {
+        float dBdz[2], dBdr[2];
+        for ( int j = 0; j < 2; j++ ) { // Bz, Br components
+            const float *field = m_field[j];
+            dBdz[j]   = m_invz*( gr*(field[2]-field[0]) + fr*(field[3]-field[1]) );
+            dBdr[j]   = m_invr*( gz*(field[1]-field[0]) + fz*(field[3]-field[2]) );
+        }
+        // convert to cartesian coordinates
+        float cc = c*c;
+        float cs = c*es;
+        float ss = es*es;
+        float sinvr = es*invr;
+        float cinvr = c*invr;
+        deriv[0] = cc*dBdr[1] + sinvr*B[1];
+        deriv[1] = cs*dBdr[1] - cinvr*B[1];
+        deriv[2] = c*dBdz[1];
+        deriv[3] = cs*dBdr[1] - sinvr*B[0];
+        deriv[4] = ss*dBdr[1] + cinvr*B[0];
+        deriv[5] = es*dBdz[1];
+        deriv[6] = c*dBdr[0];
+        deriv[7] = es*dBdr[0];
+        deriv[8] = dBdz[0];
+    }
+}
+
+#endif
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldCond.h b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldCond.h
new file mode 100644
index 0000000000000000000000000000000000000000..9857ef4a0bc6c993f1d7e0e0eeb47ffd85ecbe26
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldCond.h
@@ -0,0 +1,108 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//
+// BFieldCond.h
+//
+// Current conductors used in BFieldZone.
+// It may be finite : p1 to p2.
+// It may be infinite : passing p1 and direction parallel to p2.
+//
+// Masahiro Morii, Harvard University
+//
+#ifndef BFIELDCOND_H
+#define BFIELDCOND_H
+
+#include <Eigen/Dense>
+#include <cmath>
+
+class BFieldCond {
+public:
+    // constructor
+    BFieldCond( bool finite, const double *p1, const double *p2, double curr ):
+      m_finite(finite),
+      m_p1(Eigen::Map<const Eigen::Vector3d>(p1)),
+      m_p2(Eigen::Map<const Eigen::Vector3d>(p2)),
+      m_u(finite ? (m_p2 - m_p1).normalized() : m_p2),
+      m_curr(curr),
+      m_nomCurr(curr)
+    {}
+
+    // compute magnetic field, plus derivatives if requested, and add
+    inline void addBiotSavart( const double *xyz, double *B, double *deriv=0 ) const;
+    // scale the current wrt the nominal current
+    void scaleCurrent( double factor ) { m_curr = factor*m_nomCurr; }
+    // accessors
+    bool finite() const { return m_finite; }
+    double p1( int i ) const { return m_p1[i]; }
+    double p2( int i ) const { return m_p2[i]; }
+    double curr() const { return m_curr; }
+private:
+    bool m_finite;  // true if the conductor is finite in length
+
+    /*
+     * m_p1: One end of a finite conductor, or one point on an infinite conductor
+     * m_p2: The other end of a finite conductor, or the direction vector of an infinite conductor
+     * m_u : Direction vector (= m_p2 if infinite)
+     */
+    const Eigen::Vector3d m_p1, m_p2, m_u;
+    double m_curr;  // current (in A) flowing through the conductor
+    double m_nomCurr;  // nominal current (in A) read from the map file
+};
+
+//
+// Compute the Biot-Savart field due to this conductor
+// If deriv[9] is passed, also computes the field derivatives
+// The results are _added_ to B[] and deriv[].
+//
+void
+BFieldCond::addBiotSavart( const double *xyz, double *B_in, double *deriv ) const
+{
+    static const double mu04pi = 1.0e-7;  // mu_0/4pi
+    static const double minvsq = 10.*10.; // (1 cm)^2
+
+    const Eigen::Map<const Eigen::Vector3d> pos(xyz);
+    Eigen::Map<Eigen::Vector3d> B(B_in);
+
+    const Eigen::Vector3d r1 = pos - m_p1;
+    const Eigen::Vector3d r2 = pos - m_p2;
+    const Eigen::Vector3d v = m_u.cross(r1);
+    const double vsq = std::max(v.squaredNorm(), minvsq);
+
+    const double r1u = r1.dot(m_u), r2u = r2.dot(m_u);
+    const double r1n = r1.norm(), r2n = r2.norm();
+
+    const double f0 = mu04pi * m_curr / vsq;
+    const double f1 = f0 * (m_finite ? r1u / r1n - r2u / r2n : 2.0);
+    const double f2 = 2.0 * f1 / vsq;
+
+    B += f1 * v;
+
+    if (deriv) {
+        Eigen::Map<Eigen::Matrix<double, 3, 3, Eigen::RowMajor>> D(deriv);
+
+        D(0, 1) -= f1 * m_u(2);
+        D(0, 2) += f1 * m_u(1);
+        D(1, 0) += f1 * m_u(2);
+        D(1, 2) -= f1 * m_u(0);
+        D(2, 0) -= f1 * m_u(1);
+        D(2, 1) += f1 * m_u(0);
+
+        if (vsq > minvsq) {
+            Eigen::Vector3d w = f2 * (m_u * r1u - r1);
+
+            if (m_finite) {
+                // Finite conductor segment
+                w += f0 * (
+                    (m_u - r1 * r1u / (r1n * r1n)) / r1n -
+                    (m_u - r2 * r2u / (r2n * r2n)) / r2n
+                );
+            }
+
+            D += v * w.transpose();
+        }
+    }
+}
+
+#endif
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldH8Grid.cxx b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldH8Grid.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..9817715da6287ac398d58ae9a717b55aea3cd573
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldH8Grid.cxx
@@ -0,0 +1,143 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//
+// BFieldH8Grid.cxx
+//
+// Masahiro Morii, Harvard University
+//
+#include "BFieldH8Grid.h"
+
+BFieldH8Grid::BFieldH8Grid()
+{
+    for ( int i=0; i<3; i++ ) {
+        m_n[i] = 0;
+        m_min[i] = 0.0;
+        m_max[i] = 0.0;
+        m_d[i] = 0.0;
+    }
+}
+
+void
+BFieldH8Grid::readMap( std::istream& input )
+{
+    // read the magnet header line
+    char name[256];
+    double cor;
+    m_n[0] = 0;
+    input >> name;
+    for ( int i=0; i<3; i++ ) input >> m_n[i];
+    for ( int i=0; i<3; i++ ) input >> m_min[i] >> m_max[i];
+    for ( int i=0; i<3; i++ ) input >> m_d[i];
+    input >> cor;
+    // anything read?
+    if ( m_n[0] == 0 ) return;
+    // convert unit to mm
+    const double meter(1000.); // m in mm
+    for ( int i=0; i<3; i++ ) {
+        m_min[i] *= meter;
+        m_max[i] *= meter;
+        m_d[i] *= meter;
+    }
+    // prepare space for the field data
+    int nxyz = m_n[0]*m_n[1]*m_n[2];
+    for ( int i=0; i<3; i++ ) m_B[i].resize(nxyz);
+    // read the field data
+    double xyz[3];
+    double B[3];
+    for ( int k=0; k<nxyz; k++ ) {
+        input >> xyz[0] >> xyz[1] >> xyz[2] >> B[0] >> B[1] >> B[2];
+        // adjust unit and scale
+        const double cm(10.); // cm in mm
+        const double tesla(0.001); // T in kT
+        for ( int i=0; i<3; i++ ) {
+            xyz[i] *= cm;
+            B[i] *= -cor*tesla;
+        }
+        // find the grid number
+        int j[3];
+        for ( int i=0; i<3; i++ ) {
+            j[i] = int((xyz[i]-m_min[i])/(m_max[i]-m_min[i])*(m_n[i]-1)+0.5);
+        }
+        // store
+        int ixyz = j[0] + m_n[0]*(j[1] + m_n[1]*j[2]);
+        for ( int i=0; i<3; i++ ) {
+            m_B[i][ixyz] = B[i];
+        }
+    }
+    // apply offset to the range
+    for ( int i=0; i<3; i++ ) {
+        m_min[i] += m_d[i];
+        m_max[i] += m_d[i];
+    }
+}
+
+void
+BFieldH8Grid::getB( const double *xyz, double *B, double *deriv ) const
+{
+    if ( !defined() || !inside( xyz ) ) {
+        B[0] = B[1] = B[2] = 0.0;
+        if ( deriv != 0 ) {
+            for ( int j = 0; j < 9; j++ ) deriv[j] = 0.0;
+        }
+        return;
+    }
+    // find the grid index
+    double invunit[3];
+    int j[3];
+    double f[3];
+    double g[3];
+    for ( int i=0; i<3; i++ ) {
+        invunit[i] = (m_n[i]-1) / (m_max[i]-m_min[i]);
+        double a = (xyz[i]-m_min[i]) * invunit[i];
+        j[i] = int(a);
+        f[i] = a - j[i];
+        g[i] = 1.0 - f[i];
+    }
+    int ixyz[8];
+    ixyz[0] = j[0] + m_n[0]*(j[1] + m_n[1]*j[2]);
+    ixyz[1] = ixyz[0] + 1;
+    ixyz[2] = ixyz[0] + m_n[0];
+    ixyz[3] = ixyz[2] + 1;
+    ixyz[4] = ixyz[0] + m_n[0]*m_n[1];
+    ixyz[5] = ixyz[4] + 1;
+    ixyz[6] = ixyz[4] + m_n[0];
+    ixyz[7] = ixyz[6] + 1;
+    // interpolate field values
+    for ( int i=0; i<3; i++ ) { // Bx, By, Bz
+        B[i] = g[2]*( g[1]*( g[0]*m_B[i][ixyz[0]] + f[0]*m_B[i][ixyz[1]] ) +
+                      f[1]*( g[0]*m_B[i][ixyz[2]] + f[0]*m_B[i][ixyz[3]] ) ) +
+               f[2]*( g[1]*( g[0]*m_B[i][ixyz[4]] + f[0]*m_B[i][ixyz[5]] ) +
+                      f[1]*( g[0]*m_B[i][ixyz[6]] + f[0]*m_B[i][ixyz[7]] ) );
+    }
+    // derivatives
+    if ( deriv != 0 ) {
+        for ( int i=0; i<3; i++ ) { // Bx, By, Bz
+            deriv[i*3  ] = ( g[2]*( g[1]*(m_B[i][ixyz[1]] - m_B[i][ixyz[0]] ) +
+                                    f[1]*(m_B[i][ixyz[3]] - m_B[i][ixyz[2]] ) ) +
+                             f[2]*( g[1]*(m_B[i][ixyz[5]] - m_B[i][ixyz[4]] ) +
+                                    f[1]*(m_B[i][ixyz[7]] - m_B[i][ixyz[6]] ) ) ) * invunit[0];
+            deriv[i*3+1] = ( g[2]*( g[0]*(m_B[i][ixyz[2]] - m_B[i][ixyz[0]] ) +
+                                    f[0]*(m_B[i][ixyz[3]] - m_B[i][ixyz[1]] ) ) +
+                             f[2]*( g[0]*(m_B[i][ixyz[6]] - m_B[i][ixyz[4]] ) +
+                                    f[0]*(m_B[i][ixyz[7]] - m_B[i][ixyz[5]] ) ) ) * invunit[1];
+            deriv[i*3+2] = ( g[1]*( g[0]*(m_B[i][ixyz[4]] - m_B[i][ixyz[0]] ) +
+                                    f[0]*(m_B[i][ixyz[5]] - m_B[i][ixyz[1]] ) ) +
+                             f[1]*( g[0]*(m_B[i][ixyz[6]] - m_B[i][ixyz[2]] ) +
+                                    f[0]*(m_B[i][ixyz[7]] - m_B[i][ixyz[3]] ) ) ) * invunit[2];
+        }
+    }
+}
+
+void
+BFieldH8Grid::setOffset( const double *dxyz )
+{
+    // update the range by the change in offset
+    for ( int i=0; i<3; i++ ) {
+        m_min[i] += dxyz[i] - m_d[i];
+        m_max[i] += dxyz[i] - m_d[i];
+        m_d[i] = dxyz[i];
+    }
+}
+
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldH8Grid.h b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldH8Grid.h
new file mode 100644
index 0000000000000000000000000000000000000000..112a5983184f9d111a2131e53388fadb5076b513
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldH8Grid.h
@@ -0,0 +1,51 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//
+// BFieldH8Grid.h
+//
+// A regular x-y-z grid of field data for H8 field map
+//
+// Masahiro Morii, Harvard University
+//
+#ifndef BFIELDH8GRID_H
+#define BFIELDH8GRID_H
+
+#include <vector>
+#include <iostream>
+
+class BFieldH8Grid {
+public:
+    // constructor
+    BFieldH8Grid();
+    // read map data from a text file
+    void readMap( std::istream& input );
+    // compute magnetic field + derivatives
+    void getB( const double *xyz, double *B, double *deriv=0 ) const;
+    // true if the grid has been defined
+    bool defined() const { return ( m_n[0] > 0 ); }
+    // true if xyz[3] is inside this grid
+    bool inside( const double *xyz ) const
+    { return ( xyz[0]>=m_min[0] && xyz[0]<=m_max[0] &&
+               xyz[1]>=m_min[1] && xyz[1]<=m_max[1] &&
+               xyz[2]>=m_min[2] && xyz[2]<=m_max[2] ); }
+    // set x-y-z offset of the grid coordinates
+    void setOffset( const double *dxyz );
+    // get copies of size vectors
+    void getBounds(double *out_min, double *out_max, double *out_d) const
+    { for (unsigned i = 0; i < 3; i++) {
+        out_min[i] = m_min[i];
+        out_max[i] = m_max[i];
+        out_d[i] = m_d[i];
+      }
+      return;
+    }
+private:
+    int    m_n[3];              // number of grid points
+    double m_min[3], m_max[3];  // range in x,y,z (mm)
+    double m_d[3];              // offset in x,y,z (mm)
+    std::vector<double> m_B[3]; // Bz,By,Bz (kT)
+};
+
+#endif
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldMesh.h b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldMesh.h
new file mode 100644
index 0000000000000000000000000000000000000000..cc6a1d003f6716a41c0495c0ec3edd49598d5271
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldMesh.h
@@ -0,0 +1,294 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//
+// BFieldMesh.h
+//
+// Generic 3-d mesh representing a simple field map.
+// The field type is templated - it may be short (for the toroid) or double (for the solenoid)
+//
+// Masahiro Morii, Harvard University
+//
+#ifndef BFIELDMESH_H
+#define BFIELDMESH_H
+
+#include <iostream>
+#include <vector>
+#include <cmath>
+#include "BFieldVector.h"
+#include "BFieldCache.h"
+
+template <class T>
+class BFieldMesh {
+public:
+    // constructor
+    BFieldMesh() : m_scale(1.0) {;}
+    BFieldMesh( double zmin, double zmax, double rmin, double rmax, double phimin, double phimax,
+                double bscale )
+        : m_scale(bscale), m_nomScale(bscale)
+        { m_min[0] = zmin; m_max[0] = zmax; m_min[1] = rmin; m_max[1] = rmax; m_min[2] = phimin; m_max[2] = phimax; }
+    // set ranges
+    void setRange( double zmin, double zmax, double rmin, double rmax, double phimin, double phimax )
+        { m_min[0] = zmin; m_max[0] = zmax; m_min[1] = rmin; m_max[1] = rmax; m_min[2] = phimin; m_max[2] = phimax; }
+    // set bscale
+    void setBscale( double bscale ) { m_scale = m_nomScale = bscale; }
+    // scale bscale by a factor
+    void scaleBscale( double factor ) { m_scale = factor*m_nomScale; }
+    // allocate space to vectors
+    void reserve( int nz, int nr, int nphi, int nfield );
+    void reserve( int nz, int nr, int nphi ) { reserve( nz, nr, nphi, nz*nr*nphi ); }
+    // add elements to vectors
+    void appendMesh( int i, double mesh ) { m_mesh[i].push_back(mesh); }
+    void appendField( const BFieldVector<T> & field ) { m_field.push_back(field); }
+    // build LUT
+    void buildLUT();
+    // test if a point is inside this zone
+    inline bool inside( double z, double r, double phi ) const;
+    // find the bin
+    inline void getCache( double z, double r, double phi, BFieldCache & cache ) const;
+    // get the B field
+    void getB( const double *xyz, double *B, double* deriv=0 ) const;
+    // accessors
+    double min( int i ) const { return m_min[i]; }
+    double max( int i ) const { return m_max[i]; }
+    double zmin() const { return m_min[0]; }
+    double zmax() const { return m_max[0]; }
+    double rmin() const { return m_min[1]; }
+    double rmax() const { return m_max[1]; }
+    double phimin() const { return m_min[2]; }
+    double phimax() const { return m_max[2]; }
+    unsigned nmesh( int i ) const { return m_mesh[i].size(); }
+    double mesh( int i, int j ) const { return m_mesh[i][j]; }
+    unsigned nfield() const { return m_field.size(); }
+    const BFieldVector<T> & field( int i ) const { return m_field[i]; }
+    double bscale() const { return m_scale; }
+    int memSize() const;
+protected:
+    double m_min[3], m_max[3];
+    std::vector<double> m_mesh[3];
+private:
+    std::vector< BFieldVector<T> > m_field;
+    double m_scale;
+    double m_nomScale;  // nominal m_scale from the map
+    // look-up table and related variables
+    std::vector<int> m_LUT[3];
+    double m_invUnit[3];     // inverse unit size in the LUT
+    int m_roff, m_zoff;
+};
+
+//
+// Implemnetation follows
+//
+
+//
+// Reserve space in the vectors to avoid unnecessary memory re-allocations.
+//
+template <class T>
+void BFieldMesh<T>::reserve( int nz, int nr, int nphi, int nfield )
+{
+    m_mesh[0].reserve( nz );
+    m_mesh[1].reserve( nr );
+    m_mesh[2].reserve( nphi );
+    m_field.reserve( nfield );
+}
+
+//
+// Test if a point (z,r,phi) is inside this mesh region.
+//
+template <class T>
+bool BFieldMesh<T>::inside( double z, double r, double phi ) const
+{
+    // assume phi is in [-pi,pi].
+    // phimax() is in [0,2pi], but phimin() may be < 0 if the range crosses phi = 0.
+    // we have to test twice to get all possible cases.
+    if ( phi < phimin() ) phi += 2.0*M_PI;
+    return ( phi >= phimin() && phi <= phimax() &&
+             z >= zmin() && z <= zmax() && r >= rmin() && r <= rmax() );
+}
+
+//
+// Find and return the cache of the bin containing (z,r,phi)
+//
+template <class T>
+void BFieldMesh<T>::getCache( double z, double r, double phi, BFieldCache & cache ) const
+{
+    // make sure phi is inside this zone
+    if ( phi < phimin() ) phi += 2.0*M_PI;
+    // find the mesh, and relative location in the mesh
+    // z
+    const std::vector<double>& mz(m_mesh[0]);
+    int iz = int((z-zmin())*m_invUnit[0]); // index to LUT
+    iz = m_LUT[0][iz]; // tentative mesh index from LUT
+    if ( z > mz[iz+1] ) iz++;
+    // r
+    const std::vector<double>& mr(m_mesh[1]);
+    int ir = int((r-rmin())*m_invUnit[1]); // index to LUT
+    ir = m_LUT[1][ir]; // tentative mesh index from LUT
+    if ( r > mr[ir+1] ) ir++;
+    // phi
+    const std::vector<double>& mphi(m_mesh[2]);
+    int iphi = int((phi-phimin())*m_invUnit[2]); // index to LUT
+    iphi = m_LUT[2][iphi]; // tentative mesh index from LUT
+    if ( phi > mphi[iphi+1] ) iphi++;
+    // store the bin edges
+    cache.setRange( mz[iz], mz[iz+1], mr[ir], mr[ir+1], mphi[iphi], mphi[iphi+1] );
+    // store the B field at the 8 corners
+    int im0 = iz*m_zoff+ir*m_roff+iphi; // index of the first corner
+    cache.setField( 0, m_field[im0              ] );
+    cache.setField( 1, m_field[im0            +1] );
+    cache.setField( 2, m_field[im0      +m_roff  ] );
+    cache.setField( 3, m_field[im0      +m_roff+1] );
+    cache.setField( 4, m_field[im0+m_zoff        ] );
+    cache.setField( 5, m_field[im0+m_zoff      +1] );
+    cache.setField( 6, m_field[im0+m_zoff+m_roff  ] );
+    cache.setField( 7, m_field[im0+m_zoff+m_roff+1] );
+    // store the B scale
+    cache.setBscale( m_scale );
+    return;
+}
+
+//
+// Compute the magnetic field (and the derivatives) without caching
+//
+template <class T>
+void BFieldMesh<T>::getB( const double *xyz, double *B, double* deriv ) const
+{
+    // cylindrical coordinates
+    double x = xyz[0];
+    double y = xyz[1];
+    double z = xyz[2];
+    double r = sqrt( x*x + y*y );
+    double phi = atan2( y, x );
+    if ( phi < phimin() ) phi += 2.0*M_PI;
+    // is it inside this map?
+    if ( ! inside( z, r, phi ) ) { // no
+        B[0] = B[1] = B[2] = 0.0;
+        if ( deriv ) for ( int i = 0; i < 9; i++ ) deriv[i] = 0.0;
+        return;
+    }
+    // find the bin
+    // z
+    const std::vector<double>& mz(m_mesh[0]);
+    int iz = int((z-zmin())*m_invUnit[0]); // index to LUT
+    iz = m_LUT[0][iz]; // tentative mesh index from LUT
+    if ( z > mz[iz+1] ) iz++;
+    // r
+    const std::vector<double>& mr(m_mesh[1]);
+    int ir = int((r-rmin())*m_invUnit[1]); // index to LUT
+    ir = m_LUT[1][ir]; // tentative mesh index from LUT
+    if ( r > mr[ir+1] ) ir++;
+    // phi
+    const std::vector<double>& mphi(m_mesh[2]);
+    int iphi = int((phi-phimin())*m_invUnit[2]); // index to LUT
+    iphi = m_LUT[2][iphi]; // tentative mesh index from LUT
+    if ( phi > mphi[iphi+1] ) iphi++;
+    // get the B field at the 8 corners
+    int im0 = iz*m_zoff+ir*m_roff+iphi; // index of the first corner
+    BFieldVector<T> field[8];
+    field[0] = m_field[im0                ];
+    field[1] = m_field[im0              +1];
+    field[2] = m_field[im0       +m_roff  ];
+    field[3] = m_field[im0       +m_roff+1];
+    field[4] = m_field[im0+m_zoff         ];
+    field[5] = m_field[im0+m_zoff       +1];
+    field[6] = m_field[im0+m_zoff+m_roff  ];
+    field[7] = m_field[im0+m_zoff+m_roff+1];
+    // fractional position inside this mesh
+    double fz = (z-mz[iz]) / (mz[iz+1]-mz[iz]);
+    double gz = 1.0 - fz;
+    double fr = (r-mr[ir]) / (mr[ir+1]-mr[ir]);
+    double gr = 1.0 - fr;
+    double fphi = (phi-mphi[iphi]) / (mphi[iphi+1]-mphi[iphi]);
+    double gphi = 1.0 - fphi;
+    // interpolate field values in z, r, phi
+    double Bzrphi[3];
+    for ( int i = 0; i < 3; i++ ) { // z, r, phi
+        Bzrphi[i] = m_scale*( gz*( gr*( gphi*field[0][i] + fphi*field[1][i] ) +
+                                   fr*( gphi*field[2][i] + fphi*field[3][i] ) ) +
+                              fz*( gr*( gphi*field[4][i] + fphi*field[5][i] ) +
+                                   fr*( gphi*field[6][i] + fphi*field[7][i] ) ) );
+    }
+    // convert (Bz,Br,Bphi) to (Bx,By,Bz)
+    double c = (r>0.0) ? x/r : cos(mphi[iphi]);
+    double es = (r>0.0) ? y/r : sin(mphi[iphi]);
+    B[0] = Bzrphi[1]*c - Bzrphi[2]*es;
+    B[1] = Bzrphi[1]*es + Bzrphi[2]*c;
+    B[2] = Bzrphi[0];
+
+    // compute field derivatives if requested
+    if ( deriv ) {
+        double sz = m_scale/(mz[iz+1]-mz[iz]);
+        double esr = m_scale/(mr[ir+1]-mr[ir]);
+        double sphi = m_scale/(mphi[iphi+1]-mphi[iphi]);
+        double dBdz[3], dBdr[3], dBdphi[3];
+        for ( int j = 0; j < 3; j++ ) { // Bz, Br, Bphi components
+            dBdz[j]   = sz*( gr*( gphi*(field[4][j]-field[0][j]) + fphi*(field[5][j]-field[1][j]) ) +
+                             fr*( gphi*(field[6][j]-field[2][j]) + fphi*(field[7][j]-field[3][j]) ) );
+            dBdr[j]   = esr*( gz*( gphi*(field[2][j]-field[0][j]) + fphi*(field[3][j]-field[1][j]) ) +
+                             fz*( gphi*(field[6][j]-field[4][j]) + fphi*(field[7][j]-field[5][j]) ) );
+            dBdphi[j] = sphi*( gz*( gr*(field[1][j]-field[0][j]) + fr*(field[3][j]-field[2][j]) ) +
+                               fz*( gr*(field[5][j]-field[4][j]) + fr*(field[7][j]-field[6][j]) ) );
+        }
+        // convert to cartesian coordinates
+        double cc = c*c;
+        double cs = c*es;
+        double ss = es*es;
+        deriv[0] = cc*dBdr[1] - cs*dBdr[2] - cs*dBdphi[1]/r + ss*dBdphi[2]/r + es*B[1]/r;
+        deriv[1] = cs*dBdr[1] - ss*dBdr[2] + cc*dBdphi[1]/r - cs*dBdphi[2]/r - c*B[1]/r;
+        deriv[2] = c*dBdz[1] - es*dBdz[2];
+        deriv[3] = cs*dBdr[1] + cc*dBdr[2] - ss*dBdphi[1]/r - cs*dBdphi[2]/r - es*B[0]/r;
+        deriv[5] = es*dBdz[1] + c*dBdz[2];
+        deriv[6] = c*dBdr[0] - es*dBdphi[0]/r;
+        deriv[7] = es*dBdr[1] + c*dBdphi[0]/r;
+        deriv[8] = dBdz[0];
+    }
+    return;
+}
+
+//
+// Construct the look-up table to accelerate bin-finding.
+//
+template <class T>
+void BFieldMesh<T>::buildLUT()
+{
+    for ( int j = 0; j < 3; j++ ) { // z, r, phi
+        // align the m_mesh edges to m_min/m_max
+        m_mesh[j].front() = m_min[j];
+        m_mesh[j].back()  = m_max[j];
+        // determine the unit size, q, to be used in the LUTs
+        double width = m_mesh[j].back() - m_mesh[j].front();
+        double q(width);
+        for ( unsigned i = 0; i < m_mesh[j].size()-1; i++ ) {
+            q = std::min( q, m_mesh[j][i+1] - m_mesh[j][i] );
+        }
+        // find the number of units in the LUT
+        int n = int(width/q) + 1;
+        q = width/(n+0.5);
+        m_invUnit[j] = 1.0/q; // new unit size
+        n++;
+        int m_number = 0; // mesh number
+        for ( int i = 0; i < n; i++ ) { // LUT index
+            if ( i*q + m_mesh[j].front() > m_mesh[j][m_number+1] ) m_number++;
+            m_LUT[j].push_back(m_number);
+        }
+    }
+    m_roff = m_mesh[2].size();       // index offset for incrementing r by 1
+    m_zoff = m_roff*m_mesh[1].size(); // index offset for incrementing z by 1
+}
+
+template <class T>
+int BFieldMesh<T>::memSize() const
+{
+    int size = 0;
+    size += sizeof(double)*10;
+    size += sizeof(int)*2;
+    for ( int i = 0; i < 3; i++ ) {
+        size += sizeof(double)*m_mesh[i].capacity();
+        size += sizeof(int)*m_LUT[i].capacity();
+    }
+    size += sizeof(BFieldVector<T>)*m_field.capacity();
+    return size;
+}
+
+#endif
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldMeshZR.cxx b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldMeshZR.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..450e825a15af0841f49b9bb8e8cd8a4d6dbd56ea
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldMeshZR.cxx
@@ -0,0 +1,54 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//
+// BFieldMeshZR.cxx
+//
+// A 2-dim z-r mesh inside the solenoid field map
+//
+// Masahiro Morii, Harvard University
+//
+#include "BFieldMeshZR.h"
+
+//
+// Construct the look-up table to accelerate bin-finding.
+//
+void
+BFieldMeshZR::buildLUT()
+{
+    for ( int j = 0; j < 2; j++ ) { // z, r
+        // determine the unit size, q, to be used in the LUTs
+        double width = m_mesh[j].back() - m_mesh[j].front();
+        double q(width);
+        for ( unsigned i = 0; i < m_mesh[j].size()-1; i++ ) {
+            q = std::min( q, m_mesh[j][i+1] - m_mesh[j][i] );
+        }
+        // find the number of units in the LUT
+        int n = int(width/q) + 1;
+        q = width/(n+0.5);
+        m_invUnit[j] = 1.0/q; // new unit size
+        n++;
+        int m = 0; // mesh number
+        m_LUT[j].reserve(n);
+        for ( int i = 0; i < n; i++ ) { // LUT index
+            if ( i*q + m_mesh[j].front() > m_mesh[j][m+1] ) m++;
+            m_LUT[j].push_back(m);
+        }
+    }
+    m_zoff = m_mesh[1].size(); // index offset for incrementing z by 1
+}
+
+int BFieldMeshZR::memSize() const
+{
+    int size = 0;
+    size += sizeof(double)*6;
+    size += sizeof(int)*1;
+    for ( int i = 0; i < 2; i++ ) {
+        size += sizeof(double)*m_mesh[i].capacity();
+        size += sizeof(int)*m_LUT[i].capacity();
+    }
+    size += sizeof(BFieldVectorZR)*m_field.capacity();
+    return size;
+}
+
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldMeshZR.h b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldMeshZR.h
new file mode 100644
index 0000000000000000000000000000000000000000..65e0bada7da28ad203f7347cb69524754a35a0ba
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldMeshZR.h
@@ -0,0 +1,88 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//
+// BFieldMeshZR.h
+//
+// A 2-dim z-r mesh inside the solenoid field map
+//
+// Masahiro Morii, Harvard University
+//
+#ifndef BFIELDMESHZR_H
+#define BFIELDMESHZR_H
+
+#include <vector>
+#include <cmath>
+#include "BFieldVectorZR.h"
+#include "BFieldCacheZR.h"
+
+class BFieldMeshZR {
+public:
+    // constructor
+    BFieldMeshZR( double zmin, double zmax, double rmin, double rmax )
+        { m_min[0] = zmin; m_max[0] = zmax; m_min[1] = rmin; m_max[1] = rmax; }
+    // allocate space to vectors
+    void reserve( int nz, int nr )
+        { m_mesh[0].reserve( nz ); m_mesh[1].reserve( nr ); m_field.reserve( nz*nr ); }
+    // add elements to vectors
+    void appendMesh( int i, double mesh ) { m_mesh[i].push_back(mesh); }
+    void appendField( const BFieldVectorZR &field ) { m_field.push_back(field); }
+    // build LUT
+    void buildLUT();
+    // test if a point is inside this zone
+    inline bool inside( double z, double r ) const
+        { return ( z >= zmin() && z <= zmax() && r >= rmin() && r <= rmax() ); }
+    // find the bin
+    inline void getCache( double z, double r, BFieldCacheZR & cache ) const;
+    // accessors
+    double min( int i ) const { return m_min[i]; }
+    double max( int i ) const { return m_max[i]; }
+    double zmin() const { return m_min[0]; }
+    double zmax() const { return m_max[0]; }
+    double rmin() const { return m_min[1]; }
+    double rmax() const { return m_max[1]; }
+    unsigned nmesh( int i ) const { return m_mesh[i].size(); }
+    double mesh( int i, int j ) const { return m_mesh[i][j]; }
+    unsigned nfield() const { return m_field.size(); }
+    const BFieldVectorZR & field( int i ) const { return m_field[i]; }
+    int memSize() const;
+private:
+    double m_min[2], m_max[2];
+    std::vector<double> m_mesh[2];
+    std::vector<BFieldVectorZR> m_field;
+    // look-up table and related variables
+    std::vector<int> m_LUT[2];
+    double m_invUnit[2];     // inverse unit size in the LUT
+    int m_zoff;
+};
+
+//
+// Find and return the cache of the bin containing (z,r)
+//
+inline void
+BFieldMeshZR::getCache( double z, double r, BFieldCacheZR & cache ) const
+{
+    // find the mesh, and relative location in the mesh
+    // z
+    const std::vector<double>& mz(m_mesh[0]);
+    int iz = int((z-zmin())*m_invUnit[0]); // index to LUT
+    iz = m_LUT[0][iz]; // tentative mesh index from LUT
+    if ( z > mz[iz+1] ) iz++;
+    // r
+    const std::vector<double>& mr(m_mesh[1]);
+    int ir = int((r-rmin())*m_invUnit[1]); // index to LUT
+    ir = m_LUT[1][ir]; // tentative mesh index from LUT
+    if ( r > mr[ir+1] ) ir++;
+    // store the bin edges
+    cache.setRange( mz[iz], mz[iz+1], mr[ir], mr[ir+1] );
+    // store the B field at the 8 corners
+    int im0 = iz*m_zoff+ir; // index of the first corner
+    cache.setField( 0, m_field[im0         ] );
+    cache.setField( 1, m_field[im0       +1] );
+    cache.setField( 2, m_field[im0+m_zoff  ] );
+    cache.setField( 3, m_field[im0+m_zoff+1] );
+    return;
+}
+
+#endif
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldSolenoid.cxx b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldSolenoid.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..48d96575559e2886034a1e6d2890e9d77a677131
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldSolenoid.cxx
@@ -0,0 +1,344 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//
+// BFieldSolenoid.cxx
+//
+#include "BFieldSolenoid.h"
+#include <string>
+#include <cmath>
+#include <algorithm>
+//#include "TTree.h"
+using namespace std;
+
+//
+// read an ASCII field map from istream
+// return 0 if successful
+//
+int
+BFieldSolenoid::readMap( istream& input )
+{
+    const double meter(1000.0); // meter in mm
+    const double gauss(1.0e-7); // gauss in kT
+    const string myname("BFieldSolenoid::readMap()");
+    if ( m_orig == m_tilt ) delete m_orig;
+    else { delete m_orig; delete m_tilt; }
+    m_orig = m_tilt = new BFieldMesh<double>;
+    // first line contains version
+    int version;
+    input >> version;
+    if ( version != 4 ) {
+        cerr << myname << ": version number is " << version << " instead of 4" << endl;
+        return 1;
+    }
+    // second line contains number of bins
+    int nz, nr, nphi;
+    double unused;
+    input >> nz >> nr >> nphi >> unused >> unused >> unused;
+    m_orig->reserve( nz, nr, nphi+1 ); // extra phi at 2pi
+    // read mesh definitions
+    double x;
+    for ( int i = 0; i < nr; i++ ) { input >> x; m_orig->appendMesh(1,x*meter); }
+    for ( int i = 0; i < nz; i++ ) { input >> x; m_orig->appendMesh(0,x*meter); }
+    for ( int i = 0; i < nphi; i++ ) { input >> x; m_orig->appendMesh(2,x); }
+    m_orig->appendMesh(2,2*M_PI); // extra phi at 2pi
+    double zmin = m_orig->mesh(0,0);
+    double zmax = m_orig->mesh(0,nz-1);
+    double rmin = m_orig->mesh(1,0);
+    double rmax = m_orig->mesh(1,nr-1);
+    double phimin = m_orig->mesh(2,0);
+    double phimax = m_orig->mesh(2,nphi);
+    m_orig->setRange( zmin, zmax, rmin, rmax, phimin, phimax );
+    // skip one record
+    input >> unused >> unused >> unused >> unused >> unused;
+    // read field values
+    // the index order is opposite to the toroid - have to reorder
+    // also convert from gauss to Tesla here.
+    vector<double> Bz, Br, Bphi;
+    double b;
+    for ( int k = 0; k < nphi; k++ ) {
+        for ( int j = 0; j < nr; j++ ) {
+            for ( int i = 0; i < nz; i++ ) { input >> b; Bz.push_back(b*gauss); }
+        }
+        for ( int j = 0; j < nr; j++ ) {
+            for ( int i = 0; i < nz; i++ ) { input >> b; Br.push_back(b*gauss); }
+        }
+        for ( int j = 0; j < nr; j++ ) {
+            for ( int i = 0; i < nz; i++ ) { input >> b; Bphi.push_back(b*gauss); }
+        }
+    }
+    for ( int i = 0; i < nz; i++ ) {
+        for ( int j = 0; j < nr; j++ ) {
+            for ( int k = 0; k < nphi; k++ ) {
+                int index = i + nz*(j + nr*k);
+                BFieldVector<double> field( Bz[index], Br[index], Bphi[index] );
+                m_orig->appendField( field );
+            }
+            // close phi at 2pi
+            int index = i + nz*j;
+            BFieldVector<double> field( Bz[index], Br[index], Bphi[index] );
+            m_orig->appendField( field );
+        }
+    }
+    // build the LUT
+    m_orig->buildLUT();
+
+    return 0;
+}
+
+//
+// wrire the map to a ROOT file
+// if tilted = true, write the moved-and-tilted map.
+// ohterwise, write the original map.
+//
+//void
+//BFieldSolenoid::writeMap( TFile* rootfile, bool tilted )
+//{
+//    BFieldMesh<double> *map = tilted ? m_tilt : m_orig;
+//    if ( map == 0 ) return; // no map to write
+//    if ( rootfile == 0 ) return; // no file
+//    if ( rootfile->cd() == false ) return; // could not make it current directory
+//    // define the tree
+//    TTree* tree = new TTree( "BFieldSolenoid", "BFieldSolenoid version 4" );
+//    double zmin = map->zmin();
+//    double zmax = map->zmax();
+//    double rmin = map->rmin();
+//    double rmax = map->rmax();
+//    double phimin = map->phimin();
+//    double phimax = map->phimax();
+//    int nmeshz = map->nmesh(0);
+//    int nmeshr = map->nmesh(1);
+//    int nmeshphi = map->nmesh(2);
+//    double *meshz, *meshr, *meshphi;
+//    int nfield = nmeshz*nmeshr*nmeshphi;
+//    double *fieldz, *fieldr, *fieldphi;
+//    meshz = new double[nmeshz];
+//    meshr = new double[nmeshr];
+//    meshphi = new double[nmeshphi];
+//    fieldz = new double[nfield];
+//    fieldr = new double[nfield];
+//    fieldphi = new double[nfield];
+//    // define the tree branches
+//    tree->Branch( "zmin", &zmin, "zmin/D" );
+//    tree->Branch( "zmax", &zmax, "zmax/D" );
+//    tree->Branch( "rmin", &rmin, "rmin/D" );
+//    tree->Branch( "rmax", &rmax, "rmax/D" );
+//    tree->Branch( "phimin", &phimin, "phimin/D" );
+//    tree->Branch( "phimax", &phimax, "phimax/D" );
+//    tree->Branch( "nmeshz", &nmeshz, "nmeshz/I" );
+//    tree->Branch( "meshz", meshz, "meshz[nmeshz]/D" );
+//    tree->Branch( "nmeshr", &nmeshr, "nmeshr/I" );
+//    tree->Branch( "meshr", meshr, "meshr[nmeshr]/D" );
+//    tree->Branch( "nmeshphi", &nmeshphi, "nmeshphi/I" );
+//    tree->Branch( "meshphi", meshphi, "meshphi[nmeshphi]/D" );
+//    tree->Branch( "nfield", &nfield, "nfield/I" );
+//    tree->Branch( "fieldz", fieldz, "fieldz[nfield]/D" );
+//    tree->Branch( "fieldr", fieldr, "fieldr[nfield]/D" );
+//    tree->Branch( "fieldphi", fieldphi, "fieldphi[nfield]/D" );
+//    // fill the mesh and field arrays
+//    for ( int j = 0; j < nmeshz; j++ ) {
+//        meshz[j] = map->mesh(0,j);
+//    }
+//    for ( int j = 0; j < nmeshr; j++ ) {
+//        meshr[j] = map->mesh(1,j);
+//    }
+//    for ( int j = 0; j < nmeshphi; j++ ) {
+//        meshphi[j] = map->mesh(2,j);
+//    }
+//    for ( int j = 0; j < nfield; j++ ) {
+//        const BFieldVector<double> f = map->field(j);
+//        fieldz[j] = f.z();
+//        fieldr[j] = f.r();
+//        fieldphi[j] = f.phi();
+//    }
+//    // write
+//    tree->Fill();
+//    rootfile->Write();
+//    // clean up
+//    delete[] meshz;
+//    delete[] meshr;
+//    delete[] meshphi;
+//    delete[] fieldz;
+//    delete[] fieldr;
+//    delete[] fieldphi;
+//}
+
+//
+// read the map from a ROOT file.
+// returns 0 if successful.
+//
+//int
+//BFieldSolenoid::readMap( TFile* rootfile )
+//{
+//    if ( rootfile == 0 ) return 1; // no file
+//    if ( rootfile->cd() == false ) return 2; // could not make it current directory
+//    if ( m_orig == m_tilt ) delete m_orig;
+//    else { delete m_orig; delete m_tilt; }
+//    m_orig = m_tilt = new BFieldMesh<double>;
+//    // open the tree
+//    TTree* tree = (TTree*)rootfile->Get("BFieldSolenoid");
+//    if ( tree == 0 ) return 3; // no tree
+//    double zmin, zmax, rmin, rmax, phimin, phimax;
+//    int nmeshz, nmeshr, nmeshphi;
+//    double *meshz, *meshr, *meshphi;
+//    int nfield;
+//    double *fieldz, *fieldr, *fieldphi;
+//    //unsigned char *fbyte;
+//    // define the fixed-sized branches first
+//    tree->SetBranchAddress( "zmin", &zmin );
+//    tree->SetBranchAddress( "zmax", &zmax );
+//    tree->SetBranchAddress( "rmin", &rmin );
+//    tree->SetBranchAddress( "rmax", &rmax );
+//    tree->SetBranchAddress( "phimin", &phimin );
+//    tree->SetBranchAddress( "phimax", &phimax );
+//    tree->SetBranchAddress( "nmeshz", &nmeshz );
+//    tree->SetBranchAddress( "nmeshr", &nmeshr );
+//    tree->SetBranchAddress( "nmeshphi", &nmeshphi );
+//    tree->SetBranchAddress( "nfield", &nfield );
+//    // prepare arrays - need to know the maximum sizes
+//    tree->GetEntry(0);
+//    meshz = new double[nmeshz];
+//    meshr = new double[nmeshr];
+//    meshphi = new double[nmeshphi];
+//    fieldz = new double[nfield];
+//    fieldr = new double[nfield];
+//    fieldphi = new double[nfield];
+//    // define the variable length branches
+//    tree->SetBranchAddress( "meshz", meshz );
+//    tree->SetBranchAddress( "meshr", meshr );
+//    tree->SetBranchAddress( "meshphi", meshphi );
+//    tree->SetBranchAddress( "fieldz", fieldz );
+//    tree->SetBranchAddress( "fieldr", fieldr );
+//    tree->SetBranchAddress( "fieldphi", fieldphi );
+//    // read again, and copy data
+//    tree->GetEntry(0);
+//    m_orig->setRange( zmin, zmax, rmin, rmax, phimin, phimax );
+//    m_orig->reserve( nmeshz, nmeshr, nmeshphi );
+//    for ( int j = 0; j < nmeshz; j++ ) {
+//        m_orig->appendMesh( 0, meshz[j] );
+//    }
+//    for ( int j = 0; j < nmeshr; j++ ) {
+//        m_orig->appendMesh( 1, meshr[j] );
+//    }
+//    for ( int j = 0; j < nmeshphi; j++ ) {
+//        m_orig->appendMesh( 2, meshphi[j] );
+//    }
+//    for ( int j = 0; j < nfield; j++ ) {
+//        BFieldVector<double> field( fieldz[j], fieldr[j], fieldphi[j] );
+//        m_orig->appendField( field );
+//    }
+//    // clean up
+//    tree->Delete();
+//    delete[] meshz;
+//    delete[] meshr;
+//    delete[] meshphi;
+//    delete[] fieldz;
+//    delete[] fieldr;
+//    delete[] fieldphi;
+//    // build the LUTs
+//    m_orig->buildLUT();
+//
+//    return 0;
+//}
+
+//
+// Returns the magnetic field at any position.
+//
+void
+BFieldSolenoid::getB( const double *xyz, double *B, double *deriv ) const
+{
+    double z = xyz[2];
+    double r = sqrt(xyz[0]*xyz[0] + xyz[1]*xyz[1]);
+    double phi = atan2( xyz[1], xyz[0] );
+    if ( m_tilt && m_tilt->inside( z, r, phi ) ) {
+        m_tilt->getB( xyz, B, deriv );
+    } else {
+        B[0] = B[1] = B[2] = 0.0;
+        if ( deriv ) for ( int i = 0; i < 9; i++ ) deriv[i] = 0.0;
+    }
+}
+
+//
+// Move and tilt the solenoid.
+// Modify the m_tilt copy and keep the m_orig copy.
+//
+void
+BFieldSolenoid::moveMap( double dx, double dy, double dz, double ax, double ay )
+{
+    if ( m_orig==0 ) {
+        cerr << "BFieldSolenoid::moveMap() : original map has not been read" << endl;
+        return;
+    }
+    if ( m_tilt != m_orig ) delete m_tilt;
+    //
+    // copy the mesh and make it a bit smaller
+    //
+    const double zlim = 2820.; // mm
+    const double rlim = 1075.; // mm
+    m_tilt = new BFieldMesh<double>( -zlim, zlim, 0.0, rlim, 0.0, 2*M_PI, 1.0 );
+    // z
+    m_tilt->appendMesh( 0, -zlim );
+    for ( unsigned i = 0; i < m_orig->nmesh(0); i++ ) {
+        if ( abs(m_orig->mesh(0,i)) < zlim ) m_tilt->appendMesh( 0, m_orig->mesh(0,i) );
+    }
+    m_tilt->appendMesh( 0, zlim );
+    // r
+    for ( unsigned i = 0; i < m_orig->nmesh(1); i++ ) {
+        if ( m_orig->mesh(1,i) < rlim ) m_tilt->appendMesh( 1, m_orig->mesh(1,i) );
+    }
+    m_tilt->appendMesh( 1, rlim );
+    // phi (no change)
+    for ( unsigned i = 0; i < m_orig->nmesh(2); i++ ) {
+        m_tilt->appendMesh( 2, m_orig->mesh(2,i) );
+    }
+    //
+    // loop over the new mesh, and compute the field at the
+    // corresponding location in the original map
+    //
+    double sinax( sin(ax) );
+    double cosax( cos(ax) );
+    double sinay( sin(ay) );
+    double cosay( cos(ay) );
+    for ( unsigned i = 0; i < m_tilt->nmesh(0); i++ ) {
+        double z0( m_tilt->mesh(0,i) );
+        for ( unsigned j = 0; j < m_tilt->nmesh(1); j++ ) {
+            double r0( m_tilt->mesh(1,j) );
+            for ( unsigned k = 0; k < m_tilt->nmesh(2); k++ ) {
+                double phi0( m_tilt->mesh(2,k) );
+                double x0( r0*cos(phi0) );
+                double y0( r0*sin(phi0) );
+                // shift
+                double x1( x0 - dx );
+                double y1( y0 - dy );
+                double z1( z0 - dz );
+                // rotate around x by -ax
+                double x2( x1 );
+                double y2( y1*cosax + z1*sinax );
+                double z2( z1*cosax - y1*sinax );
+                // rotate around y by -ay
+                double xyz3[3] = { x2*cosay-z2*sinay, y2, z2*cosay + x2*sinay };
+                // get (Bx,By,Bz) in the original frame
+                double B[3];
+                m_orig->getB( xyz3, B );
+                // rotate around y by +ay
+                double Bx1( B[0]*cosay + B[2]*sinay );
+                double By1( B[1] );
+                double Bz1( B[2]*cosay - B[0]*sinay );
+                // rotate around x by +ax
+                double Bx2( Bx1 );
+                double By2( By1*cosax - Bz1*sinax );
+                double Bz2( Bz1*cosax + By1*sinax );
+                // convert to cylindrical
+                double cosphi0( cos(phi0) );
+                double sinphi0( sin(phi0) );
+                double Br( Bx2*cosphi0 + By2*sinphi0 );
+                double Bphi( -Bx2*sinphi0 + By2*cosphi0 );
+                BFieldVector<double> field( Bz2, Br, Bphi );
+                m_tilt->appendField( field );
+            }
+        }
+    }
+    m_tilt->buildLUT();
+}
+
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldSolenoid.h b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldSolenoid.h
new file mode 100644
index 0000000000000000000000000000000000000000..a996b259d4de848fe0e84f322c2d5d84d1ba402e
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldSolenoid.h
@@ -0,0 +1,44 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//
+// BFieldSolenoid.h
+//
+// Magnetic field map for the ATLAS solenoid
+//
+// Masahiro Morii, Harvard University
+//
+#ifndef BFIELDSOLENOID_H
+#define BFIELDSOLENOID_H
+
+#include <vector>
+#include <iostream>
+//#include "TFile.h"
+#include "BFieldZone.h"
+
+class BFieldSolenoid {
+public:
+    // constructor
+    BFieldSolenoid() : m_orig(0), m_tilt(0) {;}
+    // destructor
+    ~BFieldSolenoid() { delete m_orig; if (m_orig!=m_tilt) delete m_tilt; }
+    // read/write map from/to file
+    int readMap( std::istream& input );
+    //int readMap( TFile* rootfile );
+    //void writeMap( TFile* rootfile, bool tilted = false );
+    // move and tilt the map
+    void moveMap( double dx, double dy, double dz, double ax, double ay );
+    // compute magnetic field
+    void getB( const double *xyz, double *B, double *deriv=0 ) const;
+    // accessor
+    const BFieldMesh<double> *tiltedMap() const { return m_tilt; }
+private:
+    // data members
+    BFieldMesh<double> *m_orig; // original map as it was read from file
+    BFieldMesh<double> *m_tilt; // tilted and moved map
+    // cache for speed
+    mutable BFieldCache m_cache;
+};
+
+#endif
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldVector.h b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldVector.h
new file mode 100644
index 0000000000000000000000000000000000000000..db04ebbd4f3ff9e4ee9009359e88e5984e13946b
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldVector.h
@@ -0,0 +1,34 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//
+// BFieldVector.h
+//
+// Magnetic field value stored in the map.
+// It may be either short (toroid) or double (solenoid).
+//
+// Masahiro Morii, Harvard University
+//
+#ifndef BFIELDVECTOR_H
+#define BFIELDVECTOR_H
+
+template <class T>
+class BFieldVector {
+public:
+    // constructor
+    BFieldVector() {;}
+    BFieldVector( T Bz, T Br, T Bphi ) { m_B[0] = Bz; m_B[1] = Br; m_B[2] = Bphi; }
+    // setter
+    void set( T Bz, T Br, T Bphi ) { m_B[0] = Bz; m_B[1] = Br; m_B[2] = Bphi; }
+    // accessors
+    T z() const { return m_B[0]; }
+    T r() const { return m_B[1]; }
+    T phi() const { return m_B[2]; }
+    // array-like accessor
+    T operator[]( int i ) const { return m_B[i]; }
+private:
+    T m_B[3];
+};
+
+#endif
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldVectorZR.h b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldVectorZR.h
new file mode 100644
index 0000000000000000000000000000000000000000..45c519c2fbcf5648d2e8d2cae641c3484dd18e9e
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldVectorZR.h
@@ -0,0 +1,31 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//
+// BFieldVectorZR.h
+//
+// Magnetic field value (Bz,Br) stored in 2d map.
+//
+// Masahiro Morii, Harvard University
+//
+#ifndef BFIELDVECTORZR_H
+#define BFIELDVECTORZR_H
+
+class BFieldVectorZR {
+public:
+    // constructor
+    BFieldVectorZR() {;}
+    BFieldVectorZR( float Bz, float Br ) { m_B[0] = Bz; m_B[1] = Br; }
+    // setter
+    void set( float Bz, float Br ) { m_B[0] = Bz; m_B[1] = Br; }
+    // accessors
+    float z() const { return m_B[0]; }
+    float r() const { return m_B[1]; }
+    // array-like accessor
+    float operator[]( int i ) const { return m_B[i]; }
+private:
+    float m_B[2];
+};
+
+#endif
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldZone.h b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldZone.h
new file mode 100644
index 0000000000000000000000000000000000000000..4b15a3524bae00ffdefbfec4a4a625c3a83a033a
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/BFieldZone.h
@@ -0,0 +1,60 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//
+// BFieldZone.h
+//
+// A "zone" inside the toroid field map
+//
+// Masahiro Morii, Harvard University
+//
+#ifndef BFIELDZONE_H
+#define BFIELDZONE_H
+
+#include <vector>
+#include "BFieldMesh.h"
+#include "BFieldCond.h"
+
+class BFieldZone : public BFieldMesh<short> {
+public:
+    // constructor
+    BFieldZone( int id, double zmin, double zmax, double rmin, double rmax, double phimin, double phimax,
+                double scale )
+        : BFieldMesh<short>(zmin,zmax,rmin,rmax,phimin,phimax,scale), m_id(id) {;}
+    // add elements to vectors
+    void appendCond( const BFieldCond& cond ) { m_cond.push_back(cond); }
+    // compute Biot-Savart magnetic field and add to B[3]
+    inline void addBiotSavart( const double *xyz, double *B, double *deriv=0 ) const;
+    // scale B field by a multiplicative factor
+    void scaleField( double factor )
+    { scaleBscale(factor); for (unsigned i=0; i<ncond(); i++) { m_cond[i].scaleCurrent(factor); } }
+    // accessors
+    int id() const { return m_id; }
+    unsigned ncond() const { return m_cond.size(); }
+    const BFieldCond& cond(int i) const { return m_cond[i]; }
+    const std::vector<BFieldCond> *condVector() const { return &m_cond; }
+    int memSize() const
+    { return BFieldMesh<short>::memSize() + sizeof(int) + sizeof(BFieldCond)*m_cond.capacity(); }
+    // adjust the min/max edges to a new value
+    void adjustMin( int i, double x ) { m_min[i] = x; m_mesh[i].front() = x; }
+    void adjustMax( int i, double x ) { m_max[i] = x; m_mesh[i].back() = x; }
+private:
+    int m_id;          // zone ID number
+    std::vector<BFieldCond> m_cond;            // list of current conductors
+};
+
+// inline functions
+
+//
+// Compute magnetic field due to the conductors
+//
+void
+BFieldZone::addBiotSavart( const double *xyz, double *B, double *deriv ) const
+{
+    for ( unsigned i = 0; i < m_cond.size(); i++ ) {
+        m_cond[i].addBiotSavart( xyz, B, deriv );
+    }
+}
+
+#endif
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/H8FieldSvc.cxx b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/H8FieldSvc.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..37305d7e31eecb30a306f5af5230f199522d1ade
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/H8FieldSvc.cxx
@@ -0,0 +1,159 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//
+// H8FieldSvc.cxx
+//
+// Masahiro Morii, Harvard University
+//
+
+#include <iostream>
+#include <fstream>
+
+// ISF_Services include
+#include "H8FieldSvc.h"
+
+// PathResolver
+//#include "PathResolver/PathResolver.h"
+
+// StoreGate
+//#include "StoreGate/StoreGateSvc.h"
+
+// Athena Pool
+//#include "AthenaPoolUtilities/CondAttrListCollection.h"
+
+// CLHEP
+#include "CLHEP/Units/SystemOfUnits.h"
+
+/** Constructor **/
+MagField::H8FieldSvc::H8FieldSvc( const std::string& /* name,ISvcLocator* svc */) :
+    //base_class(name,svc),
+    m_H8MapFilename("MagneticFieldMaps/mbps1-all-id-800-mbps2-muons-800x4.data"),
+    m_dx1(0),
+    m_dy1(0),
+    m_dz1(0),
+    m_dx2(43830), // default H8MS2 x offset
+    m_dy2(0),
+    m_dz2(0)
+{
+//    declareProperty("H8MapFile", m_H8MapFilename, "File storing the H8 magnetic field map");
+//    declareProperty("dx1", m_dx1, "x component of magnet #1 shift (in mm)");
+//    declareProperty("dy1", m_dy1, "y component of magnet #1 shift (in mm)");
+//    declareProperty("dz1", m_dz1, "z component of magnet #1 shift (in mm)");
+//    declareProperty("dx2", m_dx2, "x component of magnet #2 shift (in mm)");
+//    declareProperty("dy2", m_dy2, "y component of magnet #2 shift (in mm)");
+//    declareProperty("dz2", m_dz2, "z component of magnet #2 shift (in mm)");
+}
+
+MagField::H8FieldSvc::~H8FieldSvc()
+{
+}
+
+/** framework methods */
+bool MagField::H8FieldSvc::initialize()
+{
+    //ATH_MSG_INFO( "initialize() ..." );
+    return true;
+}
+
+bool MagField::H8FieldSvc::start()
+{
+    //ATH_MSG_INFO( "start() ..." );
+    return readMap( m_H8MapFilename );
+}
+
+bool MagField::H8FieldSvc::finalize()
+{
+    //ATH_MSG_INFO( "finalize() ..." );
+    return true;
+}
+
+bool MagField::H8FieldSvc::readMap( const std::string mapFile )
+{
+    // find the path to the map file
+    //std::string resolvedMapFile = PathResolver::find_file( mapFile.c_str(), "DATAPATH" );
+    std::string resolvedMapFile = mapFile.c_str();
+    if ( resolvedMapFile == "" ) {
+        //ATH_MSG_ERROR( "Field map file " << mapFile << " not found" );
+        return false;
+    }
+    // opne the map file
+    std::ifstream input( resolvedMapFile.c_str() );
+    if ( ! input.good() ) {
+        //ATH_MSG_ERROR( "Failed to open the field map " << resolvedMapFile );
+        return false;
+    }
+    // skip the file header line
+    char line[256];
+    input.getline( line, 256 );
+    // read grids
+    unsigned igrid(0);
+    double offset[3];
+    while (1) {
+	// first determine offset
+	if (igrid == 0) {
+	  // magnet #1
+          offset[0] = m_dx1;
+          offset[1] = m_dy1;
+          offset[2] = m_dz1;
+	} else if (igrid == 1) {
+	  // magnet #2
+          offset[0] = m_dx2;
+          offset[1] = m_dy2;
+          offset[2] = m_dz2;
+	} else {
+	  // shift up to two magnets
+          offset[0] = 0;
+          offset[1] = 0;
+          offset[2] = 0;
+	}
+
+	// then, read the map and shift it
+        BFieldH8Grid grid;
+        grid.readMap( input );
+	grid.setOffset(offset);
+	
+        if ( grid.defined() ) {
+            //ATH_MSG_INFO("setting offset for magnet " << igrid << " to " << offset[0] << ", " << offset[1] << ", " << offset[2] << " mm");
+	    // save grid
+	    double its_min[3];
+	    double its_max[3];
+	    double its_d[3];
+	    grid.getBounds(its_min, its_max, its_d);
+            //ATH_MSG_INFO("new magnet grid #" << igrid << " found");
+            //ATH_MSG_INFO("  - min    (mm) " << its_min[0] << ", " << its_min[1] << ", " << its_min[2]);
+            //ATH_MSG_INFO("  - max    (mm) " << its_max[0] << ", " << its_max[1] << ", " << its_max[2]);
+            //ATH_MSG_INFO("  - offset (mm) " << its_d[0] << ", " << its_d[1] << ", " << its_d[2]);
+            m_grid.push_back( grid );
+	    igrid++;
+        } else {
+            break;
+        }
+    }
+    //ATH_MSG_INFO( "Initialized the field map from " << resolvedMapFile );
+    return true;
+}
+
+void MagField::H8FieldSvc::getField( const double *xyz, double *B, double *deriv ) const
+{
+    for ( unsigned i = 0; i < m_grid.size(); i++ ) {
+        // find the grid that contains xyz
+        if ( m_grid[i].inside( xyz ) ) {
+            m_grid[i].getB( xyz, B, deriv );
+            return;
+        }
+    }
+    // xyz is outside all grids
+    B[0] = B[1] = B[2] = 0.0;
+    if ( deriv != 0 ) {
+        for ( int j = 0; j < 9; j++ ) deriv[j] = 0.0;
+    }
+    return;
+}
+
+void MagField::H8FieldSvc::getFieldZR( const double *xyz, double *B, double *deriv ) const
+{
+    getField( xyz, B, deriv );
+    return;
+}
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/H8FieldSvc.h b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/H8FieldSvc.h
new file mode 100644
index 0000000000000000000000000000000000000000..cb47c812bc215e9d6371018d68f5a748d521b1fa
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/H8FieldSvc.h
@@ -0,0 +1,78 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+//
+// H8FieldSvc.h
+//
+// Magnetic field map of the H8 beam test
+//
+// Masahiro Morii, Harvard University
+//
+#ifndef H8FIELDSVC_H
+#define H8FIELDSVC_H
+
+// FrameWork includes
+//#include "AthenaBaseComps/AthService.h"
+
+// MagField includes
+#include "IMagFieldSvc.h"
+#include "BFieldH8Grid.h"
+
+// STL includes
+#include <vector>
+#include <string>
+
+// forward declarations
+class CondAttrListCollection;
+
+namespace MagField {
+//class H8FieldSvc{ : public extends<AthService, IMagFieldSvc> {
+  class H8FieldSvc: public IMagFieldSvc{
+    public:
+
+      //** Constructor with parameters */
+    H8FieldSvc( const std::string& name/*, ISvcLocator* pSvcLocator*/ );
+
+      /** Destructor */
+      virtual ~H8FieldSvc();
+
+      /** Athena algorithm's interface methods */
+      bool  initialize();
+      bool  start();
+      bool  finalize();
+
+      /** get B field value at given position */
+      /** xyz[3] is in mm, bxyz[3] is in kT */
+      /** if deriv[9] is given, field derivatives are returned in kT/mm */
+      virtual void getField( const double *xyz, double *bxyz, double *deriv = nullptr ) const;
+      /** getFieldZR simply calls getField **/
+      virtual void getFieldZR( const double *xyz, double *bxyz, double *deriv = nullptr ) const;
+
+    private:
+      // initialize map
+      bool initializeMap();
+      // read the field map
+      bool readMap( const std::string mapFile );
+
+      /** Data members **/
+
+      // field map name
+      std::string m_H8MapFilename;
+
+      // field data
+      std::vector<BFieldH8Grid> m_grid;
+
+      // first magnet offset
+      double m_dx1;
+      double m_dy1;
+      double m_dz1;
+      // second magnet offset
+      double m_dx2;
+      double m_dy2;
+      double m_dz2;
+
+  };
+}
+
+#endif
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/IMagFieldManipulator.h b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/IMagFieldManipulator.h
new file mode 100644
index 0000000000000000000000000000000000000000..da0b02457dd23d61a97fc28943a3039a273a42c5
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/IMagFieldManipulator.h
@@ -0,0 +1,57 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+///////////////////////////////////////////////////////////////////
+// IMagFieldManipulator.h, (c) ATLAS Detector software
+///////////////////////////////////////////////////////////////////
+#ifndef MAGFIELDINTERFACES_IMAGFIELDMANIPULATOR_H
+#define MAGFIELDINTERFACES_IMAGFIELDMANIPULATOR_H
+
+#include <cmath>
+#include <iostream>
+
+// Framework includes
+//#include "GaudiKernel/IAlgTool.h"
+
+// Amg classes
+//#include "GeoPrimitives/GeoPrimitives.h"
+
+/** Declaration of the interface ID ( interface id, major version, minor version) */
+//static const InterfaceID IID_IMagFieldManipulator("IMagFieldManipulator", 1, 0);
+
+namespace MagField {
+
+/** @ class IMagFieldManipulator
+ 
+ @ author Valerio.Ippolito -at- cern.ch
+ */
+    //class IMagFieldManipulator: virtual public IAlgTool {
+    
+    class IMagFieldManipulator{
+    ///////////////////////////////////////////////////////////////////
+    // Public methods:
+    ///////////////////////////////////////////////////////////////////
+    public:
+        /** constructor */
+        IMagFieldManipulator() {;}
+
+        /** Retrieve interface ID */
+//        static const InterfaceID& interfaceID() {
+//            return IID_IMagFieldManipulator;
+//        }
+
+        /** change the point where the field should be evaluated */
+        /** in AtlasFieldSvc, this is called before B field is evaluated and */
+	/** fed into modifyField **/
+        /** xyz_new[3] and xyz_old[3] are in mm */
+        virtual void modifyPosition(const double *xyz_old, double *xyz_new) = 0;
+
+        /** correct B field value at a position xyz */
+        /** bxyz[3] is in kT */
+        /** if deriv[9] is given, field derivatives are returned in kT/mm */
+        virtual void modifyField(double *bxyz, double *deriv = 0) = 0;
+    };
+}
+
+#endif //> !MAGFIELDINTERFACES_IMAGFIELDMANIPULATOR_H
diff --git a/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/IMagFieldSvc.h b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/IMagFieldSvc.h
new file mode 100644
index 0000000000000000000000000000000000000000..88c2f4f7ecb4c1eae1c8b72df131bf053f4baed9
--- /dev/null
+++ b/ATLAS-Extensions/ATLASMagneticFieldMapPlugin/src/IMagFieldSvc.h
@@ -0,0 +1,75 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+///////////////////////////////////////////////////////////////////
+// IMagFieldSvc.h, (c) ATLAS Detector software
+///////////////////////////////////////////////////////////////////
+#ifndef MAGFIELDINTERFACES_IMAGFIELDSVC_H
+#define MAGFIELDINTERFACES_IMAGFIELDSVC_H
+
+#include <cmath>
+#include <iostream>
+
+// Framework includes
+//#include "GaudiKernel/IInterface.h"
+
+// Amg classes
+//#include "GeoPrimitives/GeoPrimitives.h"
+
+namespace MagField {
+
+/** @ class IMagFieldSvc
+ 
+ @ author Elmar.Ritsch -at- cern.ch
+ */
+    //class IMagFieldSvc: virtual public IInterface {
+    class IMagFieldSvc{
+
+    ///////////////////////////////////////////////////////////////////
+    // Public methods:
+    ///////////////////////////////////////////////////////////////////
+    public:
+
+        /** Creates the InterfaceID and interfaceID() method */
+        //DeclareInterfaceID(IMagFieldSvc, 1, 0);
+
+        /** constructor */
+        IMagFieldSvc() : m_solenoidCurrent(0.0), m_toroidsCurrent(0.0) {;}
+
+        /** get B field value at given position */
+        /** xyz[3] is in mm, bxyz[3] is in kT */
+        /** if deriv[9] is given, field derivatives are returned in kT/mm */
+        virtual void getField(const double *xyz, double *bxyz, double *deriv = nullptr) const = 0;
+
+        /** a getField() wrapper for Amg classes */
+        //void getField(const Amg::Vector3D *xyz, Amg::Vector3D *bxyz) const {
+        //  getField( xyz->data(), bxyz->data(), nullptr );
+        //}
+        //void getField(const Amg::Vector3D *xyz, Amg::Vector3D *bxyz, Amg::RotationMatrix3D *deriv) const {
+        //  getField( xyz->data(), bxyz->data(), deriv->data() );
+        //}
+
+        /** get B field value on the z-r plane at given position */
+        /** works only inside the solenoid; otherwise calls getField() above */
+        /** xyz[3] is in mm, bxyz[3] is in kT */
+        /** if deriv[9] is given, field derivatives are returned in kT/mm */
+        virtual void getFieldZR(const double *xyz, double *bxyz, double *deriv = nullptr) const = 0;
+
+        /** status of the magnets */
+        bool solenoidOn() const  { return getSolenoidCurrent()> 0.0;}
+        bool toroidsOn () const  { return getToroidsCurrent() > 0.0; }
+        float getSolenoidCurrent() const  { return m_solenoidCurrent; }
+        float getToroidsCurrent () const  { return m_toroidsCurrent;  }
+
+    //protected:
+        void setSolenoidCurrent(float current) { m_solenoidCurrent = current; }
+        void setToroidsCurrent (float current) { m_toroidsCurrent  = current; }
+
+    private:
+        float m_solenoidCurrent; // solenoid current in ampere
+        float m_toroidsCurrent;   // toroid current in ampere
+    };
+}
+
+#endif //> !MAGFIELDINTERFACES_IMAGFIELDSVC_H
diff --git a/ATLAS-Extensions/CMakeLists.txt b/ATLAS-Extensions/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7156f4b96d484833d8a27d24c4b81489af9e3bb0
--- /dev/null
+++ b/ATLAS-Extensions/CMakeLists.txt
@@ -0,0 +1,51 @@
+# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+
+# CMake settings
+cmake_minimum_required( VERSION 3.14 )
+
+# Dummy call to 'project()', needed to set 'PROJECT_SOURCE_DIR'
+project( "ATLAS-Extensions" )
+
+#Set up the project. Check if we build it with GeoModel or individually
+if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
+    # I am built as a top-level project.
+    # Make the root module directory visible to CMake.
+    list( APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake )
+    # get global GeoModel version
+    include( GeoModel-version ) 
+    # set the project, with the version taken from the GeoModel parent project
+    project( "ATLAS-Extensions" VERSION ${GeoModel_VERSION} LANGUAGES CXX )
+    # Define color codes for CMake messages
+    include( cmake_colors_defs )
+    # Warn the users about what they are doing
+    message(STATUS "${BoldGreen}Building ${PROJECT_NAME} individually, as a top-level project.${ColourReset}")
+    # Set default build and C++ options
+    include( configure_cpp_options )
+    set( CMAKE_FIND_FRAMEWORK "LAST" CACHE STRING
+         "Framework finding behaviour on macOS" )
+    # Set up how the project handle some of its dependenices. Either by picking them
+    # up from the environment, or building them itself.
+
+    # Find the base GeoModel packages, which must be installed on the target system already
+    find_package( GeoModelCore REQUIRED )
+    # Set a flag to steer the build of the subpackages
+    #set( VISUALIZATION_INDIVIDUAL_BUILD ON )
+else()
+    # I am called from other project with add_subdirectory().
+    message( STATUS "Building ${PROJECT_NAME} as part of the root GeoModel project.")
+    # Set the project
+    project( "ATLAS-Extensions" VERSION ${GeoModel_VERSION} LANGUAGES CXX )
+endif()
+
+
+# Use the GNU install directory names.
+include( GNUInstallDirs )
+
+# Set up the build of the libraries of the project.
+add_subdirectory(HitsPlugin)
+add_subdirectory(ATLASMagneticFieldMapPlugin)
+add_subdirectory(LArCustomSolidExtension)
+
+
+
+
diff --git a/ATLAS-Extensions/HitsPlugin/CMakeLists.txt b/ATLAS-Extensions/HitsPlugin/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..01f39a0185ed71cb69b042153dbfa24e90540e03
--- /dev/null
+++ b/ATLAS-Extensions/HitsPlugin/CMakeLists.txt
@@ -0,0 +1,82 @@
+# Set up the project.
+cmake_minimum_required( VERSION 3.1 )
+
+set(CMAKE_CXX_STANDARD 17)
+
+project( "GenerateHitsPlugin" )
+
+#Set up the project. Check if we build it with GeoModel or individually
+if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
+    # I am built as a top-level project.
+    # Make the root module directory visible to CMake.
+    list( APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake )
+    # get global GeoModel version
+    #include( GeoModelATLAS-version ) 
+    # set the project, with the version taken from the GeoModel parent project
+    project( "GenerateHitsPlugin" VERSION 1.0 LANGUAGES CXX )
+    # Define color codes for CMake messages
+    include( cmake_colors_defs )
+    # Use the GNU install directory names.
+    include( GNUInstallDirs )
+    # Set a default build type
+    include( BuildType )
+    # Set default build and C++ options
+    include( configure_cpp_options )
+    # Print Build Info on screen
+    include( PrintBuildInfo )
+    # Warn the users about what they are doing
+    message(STATUS "${BoldGreen}Building ${PROJECT_NAME} individually, as a top-level project.${ColourReset}")
+    # Set default build and C++ options
+    include( configure_cpp_options )
+    set( CMAKE_FIND_FRAMEWORK "LAST" CACHE STRING
+         "Framework finding behaviour on macOS" )
+    # GeoModel dependencies
+    find_package( GeoModelCore REQUIRED )
+else()
+    # I am called from other project with add_subdirectory().
+    message( STATUS "Building ${PROJECT_NAME} as part of the root project.")
+    # Set the project
+    project( "GenerateHitsPlugin" VERSION 1.0 LANGUAGES CXX )
+endif()
+
+
+
+# Find the header and source files.
+file( GLOB SOURCES src/*.cxx )
+
+set(PROJECT_SOURCES ${SOURCES})
+
+# Set up the library.
+add_library(GenerateHitsPlugin SHARED ${SOURCES})
+
+#find_package (Eigen3 REQUIRED)
+find_package(Geant4 REQUIRED)
+#find_package(FullSimLight REQUIRED)
+find_package( HDF5 REQUIRED COMPONENTS CXX )
+
+message( STATUS "Found Geant4: ${Geant4_INCLUDE_DIR}")
+#message("Geant4_USE_FILE: ${Geant4_USE_FILE}") # debug msg
+include(${Geant4_USE_FILE})
+
+# Use the GNU install directory names.
+include( GNUInstallDirs )
+
+target_include_directories( GenerateHitsPlugin PUBLIC  ${CMAKE_SOURCE_DIR}/FullSimLight ${HDF5_CXX_INCLUDE_DIRS})
+
+target_link_libraries ( GenerateHitsPlugin PUBLIC ${CMAKE_DL_LIBS} ${Geant4_LIBRARIES} ${HDF5_CXX_LIBRARIES})
+
+
+
+set_target_properties( GenerateHitsPlugin PROPERTIES
+		       VERSION ${PROJECT_VERSION}
+		       SOVERSION ${PROJECT_VERSION_MAJOR} )
+
+
+
+install( TARGETS GenerateHitsPlugin
+	 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+	 COMPONENT Runtime
+	 NAMELINK_COMPONENT Development )
+
+
+
diff --git a/ATLAS-Extensions/HitsPlugin/cmake/BuildType.cmake b/ATLAS-Extensions/HitsPlugin/cmake/BuildType.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..14a12a8ccc0c22511e31288d7ab3b4fea69fb561
--- /dev/null
+++ b/ATLAS-Extensions/HitsPlugin/cmake/BuildType.cmake
@@ -0,0 +1,28 @@
+
+# Author: Marcus D. Hanwell
+# Source: https://blog.kitware.com/cmake-and-the-default-build-type/
+
+# Set a default build type if none was specified
+set(default_build_type "Release")
+
+# TODO: at the moment, we want to build in Release mode by default,
+# even if we build from a Git clone, because that is the default mode
+# for our users to get the source code.
+# But maybe we will want to change this behavior, later?
+# if(EXISTS "${CMAKE_SOURCE_DIR}/.git")
+#   set(default_build_type "Debug")
+# endif()
+
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+  if( COLOR_DEFS )
+    message(STATUS "${Blue}INFO: Setting build type to '${default_build_type}' as none was specified.${ColourReset}")
+  else()
+    message(STATUS "INFO: Setting build type to '${default_build_type}' as none was specified.")
+  endif()
+  set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE
+      STRING "Choose the type of build." FORCE)
+  # Set the possible values of build type for cmake-gui
+  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+    "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
+endif()
+
diff --git a/ATLAS-Extensions/HitsPlugin/cmake/PrintBuildInfo.cmake b/ATLAS-Extensions/HitsPlugin/cmake/PrintBuildInfo.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..862a34b45c7506c83a229d7ef71ff604d182acb6
--- /dev/null
+++ b/ATLAS-Extensions/HitsPlugin/cmake/PrintBuildInfo.cmake
@@ -0,0 +1,13 @@
+# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+
+if( COLOR_DEFS )
+  message(STATUS "-----")
+  message(STATUS "${BoldYellow}Building with type: ${CMAKE_BUILD_TYPE}${ColourReset}")
+  message(STATUS "${BoldYellow}Using C++ standard: ${CMAKE_CXX_STANDARD}${ColourReset}")
+  message(STATUS "-----")
+else()
+  message(STATUS "-----")
+  message(STATUS "Building with type: ${CMAKE_BUILD_TYPE}")
+  message(STATUS "Using C++ standard: ${CMAKE_CXX_STANDARD}")
+  message(STATUS "-----")
+endif()
diff --git a/ATLAS-Extensions/HitsPlugin/cmake/cmake_colors_defs.cmake b/ATLAS-Extensions/HitsPlugin/cmake/cmake_colors_defs.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..b6eea59ba9e72a50754ac0afb36d25cfcac59e09
--- /dev/null
+++ b/ATLAS-Extensions/HitsPlugin/cmake/cmake_colors_defs.cmake
@@ -0,0 +1,25 @@
+
+# Copyright: "Fraser" (https://stackoverflow.com/users/2556117/fraser)
+# CC BY-SA 3.0
+# Source: https://stackoverflow.com/a/19578320/320369
+
+if(NOT WIN32)
+  set( COLOR_DEFS TRUE CACHE BOOL "Define color escape sequences to be used in CMake messages." )
+  string(ASCII 27 Esc)
+  set(ColourReset "${Esc}[m")
+  set(ColourBold  "${Esc}[1m")
+  set(Red         "${Esc}[31m")
+  set(Green       "${Esc}[32m")
+  set(Yellow      "${Esc}[33m")
+  set(Blue        "${Esc}[34m")
+  set(Magenta     "${Esc}[35m")
+  set(Cyan        "${Esc}[36m")
+  set(White       "${Esc}[37m")
+  set(BoldRed     "${Esc}[1;31m")
+  set(BoldGreen   "${Esc}[1;32m")
+  set(BoldYellow  "${Esc}[1;33m")
+  set(BoldBlue    "${Esc}[1;34m")
+  set(BoldMagenta "${Esc}[1;35m")
+  set(BoldCyan    "${Esc}[1;36m")
+  set(BoldWhite   "${Esc}[1;37m")
+endif()
diff --git a/ATLAS-Extensions/HitsPlugin/cmake/configure_cpp_options.cmake b/ATLAS-Extensions/HitsPlugin/cmake/configure_cpp_options.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..9a6cc8a7902de8bd14d412f0ca4fc86a3bc116fc
--- /dev/null
+++ b/ATLAS-Extensions/HitsPlugin/cmake/configure_cpp_options.cmake
@@ -0,0 +1,34 @@
+
+#
+# Set build options and C++ standards and options
+#
+# This file sets up
+#
+#   CMAKE_BUILD_TYPE
+#   CMAKE_CXX_STANDARD
+#   CMAKE_CXX_EXTENSIONS
+#   CMAKE_CXX_STANDARD_REQUIRED
+#
+# The options can be overridden at configuration time by using, e.g.:
+#    `cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_STANDARD=14 ../GeoModelIO`
+# on the command line.
+#
+
+# Set default build options.
+set( CMAKE_BUILD_TYPE "Release" CACHE STRING "CMake build mode to use" )
+set( CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard used for the build" )
+set( CMAKE_CXX_EXTENSIONS FALSE CACHE BOOL "(Dis)allow using GNU extensions" )
+set( CMAKE_CXX_STANDARD_REQUIRED TRUE CACHE BOOL
+   "Require the specified C++ standard for the build" )
+
+# Setting CMAKE_CXX_FLAGS to avoid "deprecated" warnings
+set(CMAKE_CXX_FLAGS "-Wno-deprecated-declarations" ) # very basic
+#set(CMAKE_CXX_FLAGS "-Wall -Werror -pedantic-errors -Wno-deprecated-declarations" ) # good enough for a quick, better check
+#set(CMAKE_CXX_FLAGS "-Wall -Wextra -Werror -pedantic-errors -Wno-deprecated-declarations" ) # better for a thorough check
+#set(CMAKE_CXX_FLAGS "-Wall -Wextra -Werror -pedantic-errors" ) # better for an even more severe check
+#set(CMAKE_CXX_FLAGS "-Weverything -Werror -pedantic-errors" ) # not recommended, it warns for really EVERYTHING!
+
+
+# TODO: for Debug and with GCC, do we want to set the flags below by default?
+# set( CMAKE_BUILD_TYPE DEBUG )
+# set(CMAKE_CXX_FLAGS "-fPIC -O0 -g -gdwarf-2" )
diff --git a/ATLAS-Extensions/HitsPlugin/src/HitsPlugin.cxx b/ATLAS-Extensions/HitsPlugin/src/HitsPlugin.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..b32c8ed7325fcb8dd069a1143d7f3a58ad551815
--- /dev/null
+++ b/ATLAS-Extensions/HitsPlugin/src/HitsPlugin.cxx
@@ -0,0 +1,270 @@
+//
+// Plugin to Generate Hits
+//
+#include "H5Cpp.h"
+#include "FullSimLight/FSLUserActionPlugin.h"
+#include "G4UserSteppingAction.hh"
+#include "G4UserEventAction.hh"
+#include "G4UserRunAction.hh"
+#include <iostream>
+#include <fstream>
+#include "G4Step.hh"
+#include "G4Run.hh"
+#include <string>
+#include <G4Event.hh>
+#include <G4String.hh>
+#include <map>
+#include <mutex>
+
+//----------------------------------------------------------------------//
+
+struct Hit{
+  float x;
+  float y;
+  float z;
+  unsigned int  id;
+};
+
+std::map<G4String, unsigned int> particle_ids { {"gamma", 1}, {"e-", 2}, {"e+", 2}, {"mu-", 3}, {"mu+", 3}, };
+
+
+//----------------------------------------------------------------------//
+
+
+class GenerateHitsStep:
+public G4UserSteppingAction
+{
+public:
+    
+    
+    // Constructor:
+    GenerateHitsStep();
+     
+    // Destructor:
+    ~GenerateHitsStep();
+     
+    //Overriding Function
+    void UserSteppingAction(const G4Step* step) override;
+    
+    //Hits vector
+    std::vector<Hit> hits;
+    
+    //Clear Hits
+    void clearhits(){hits.clear();}
+    
+
+ };
+                            
+GenerateHitsStep::GenerateHitsStep(){}
+
+GenerateHitsStep::~GenerateHitsStep() {}
+
+
+void GenerateHitsStep::UserSteppingAction(const G4Step* step){
+ 
+    Hit hit_inst;
+    hit_inst.x = step->GetPreStepPoint()->GetPosition()[0];
+    hit_inst.y = step->GetPreStepPoint()->GetPosition()[1];
+    hit_inst.z = step->GetPreStepPoint()->GetPosition()[2];
+    
+    //Checking to see if particle is in particle map defined above
+    if (particle_ids.count(step->GetTrack()->GetParticleDefinition()->GetParticleName())>0)
+    {
+    hit_inst.id = particle_ids.find(step->GetTrack()->GetParticleDefinition()->GetParticleName())->second;
+    }
+    
+    //Otherwise assigning it a default particle id of 9 for now.
+    else
+    {
+    hit_inst.id = 9;
+    }
+    hits.push_back(hit_inst);
+
+}
+
+//----------------------------------------------------------------------//
+
+class GenerateHitsEvent:
+public G4UserEventAction
+ {
+public:
+    
+     // Constructor:
+     GenerateHitsEvent();
+     
+     // Destructor:
+    ~GenerateHitsEvent();
+     
+     //Overriding Function
+     void EndOfEventAction(const G4Event* evt) override;
+     
+     //Set Stepping Action
+     void SetSteppingAction(GenerateHitsStep* stepact){step = stepact;}
+     
+     //Set file
+     void assignfile(H5::H5File &filename){file = &filename;}
+     
+     //Set DataType
+     void assignDataType(H5::CompType &data_type){datatype = &data_type;}
+     
+     
+
+ private:
+     
+     GenerateHitsStep* step;
+     unsigned int event_ID;
+     std::vector<Hit> hits;
+     H5::H5File* file;
+     H5::CompType* datatype;
+     std::mutex mutex;
+     
+     
+     
+ };
+
+
+
+GenerateHitsEvent::GenerateHitsEvent(){}
+
+GenerateHitsEvent::~GenerateHitsEvent(){}
+
+void GenerateHitsEvent::EndOfEventAction(const G4Event* evt)
+{
+    event_ID = evt->GetEventID();
+    hits = step->hits;
+    mutex.lock();
+    hsize_t numberOfHits= hits.size();
+    H5::DataSpace dataspace(1,&numberOfHits,nullptr);
+    const std::string datasetName="EVENT-"+std::to_string(event_ID);
+    H5::DataSet dataset=file->createDataSet(datasetName, *datatype, dataspace);
+    dataset.write(hits.data(),*datatype);
+    step->clearhits();
+    mutex.unlock();
+}
+
+
+//----------------------------------------------------------------------//
+
+
+
+
+class GenerateHitsRun:
+public G4UserRunAction
+ {
+public:
+    
+     // Constructor:
+     GenerateHitsRun();
+     
+     // Destructor:
+    ~GenerateHitsRun();
+     
+     //Overriding Function
+     virtual void BeginOfRunAction(const G4Run*) override final;
+     virtual void EndOfRunAction(const G4Run*) override final;
+     
+     //Setting other User Actions
+     void SetEventAction(GenerateHitsEvent* evt) {event = evt;}
+     
+
+
+     
+ private:
+     
+     GenerateHitsEvent* event;
+     static H5::CompType datatype;
+     static std::string path;
+     static H5::H5File file;
+     
+
+ };
+
+std::string GenerateHitsRun::path = "hits.h5";
+H5::H5File GenerateHitsRun::file = H5::H5File(path, H5F_ACC_TRUNC);
+H5::CompType GenerateHitsRun::datatype = sizeof(Hit);
+
+GenerateHitsRun::GenerateHitsRun(){}
+GenerateHitsRun::~GenerateHitsRun(){}
+
+
+void GenerateHitsRun::BeginOfRunAction(const G4Run*)
+{
+    if(IsMaster())
+   {
+        
+        
+        datatype.insertMember("X", HOFFSET(Hit,x),H5::PredType::NATIVE_FLOAT);
+        datatype.insertMember("Y", HOFFSET(Hit,y),H5::PredType::NATIVE_FLOAT);
+        datatype.insertMember("Z", HOFFSET(Hit,z),H5::PredType::NATIVE_FLOAT);
+        datatype.insertMember("ID", HOFFSET(Hit,id),H5::PredType::NATIVE_UINT);
+
+    }
+    
+    event->assignfile(file);
+    event->assignDataType(datatype);
+    
+}
+
+void GenerateHitsRun::EndOfRunAction(const G4Run*)
+{
+    if(IsMaster())
+    {
+        std::cout << "Hits file written at: " << path << std::endl;
+    }
+    
+}
+
+
+
+
+
+
+
+
+
+
+
+//----------------------------------------------------------------------//
+
+
+class GenerateHitsPlugin:public FSLUserActionPlugin {
+    
+public:
+    
+    GenerateHitsPlugin();
+    virtual G4UserSteppingAction *getSteppingAction() const final override;
+    virtual G4UserEventAction *getEventAction() const final override;
+    virtual G4UserRunAction *getRunAction() const final override;
+    GenerateHitsEvent* eventaction = new GenerateHitsEvent();
+    GenerateHitsStep* stepaction = new GenerateHitsStep();
+    GenerateHitsRun*  runaction = new GenerateHitsRun();
+
+};
+
+GenerateHitsPlugin::GenerateHitsPlugin()
+{
+    
+eventaction->SetSteppingAction(stepaction);
+runaction->SetEventAction(eventaction);
+
+}
+
+G4UserSteppingAction *GenerateHitsPlugin::getSteppingAction() const {
+  return stepaction;
+}
+
+G4UserEventAction *GenerateHitsPlugin::getEventAction() const {
+    return eventaction;
+    
+}
+
+G4UserRunAction *GenerateHitsPlugin::getRunAction() const {
+    return runaction;
+    
+}
+
+
+extern "C" GenerateHitsPlugin *createGenerateHitsPlugin() {
+    return new GenerateHitsPlugin();
+}
+
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/CMakeLists.txt b/ATLAS-Extensions/LArCustomSolidExtension/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4951093a4dc2bb48567ccded2a94e713795cb7ef
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/CMakeLists.txt
@@ -0,0 +1,64 @@
+# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+
+cmake_minimum_required( VERSION 3.12 )
+project( "LArCustomShapeExtensionSolid" )
+
+
+#Set up the project. Check if we build it with GeoModel or individually
+if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
+    # I am built as a top-level project.
+    # Make the root module directory visible to CMake.
+    list( APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake )
+    # get global GeoModel version
+    #include( GeoModelATLAS-version ) 
+    # set the project, with the version taken from the GeoModel parent project
+    project( "LArCustomShapeExtensionSolid" VERSION 1.0 LANGUAGES CXX )
+    # Define color codes for CMake messages
+    include( cmake_colors_defs )
+    # Use the GNU install directory names.
+    include( GNUInstallDirs )
+    # Set a default build type
+    include( BuildType )
+    # Set default build and C++ options
+    include( configure_cpp_options )
+    # Print Build Info on screen
+    include( PrintBuildInfo )
+    # Warn the users about what they are doing
+    message(STATUS "${BoldGreen}Building ${PROJECT_NAME} individually, as a top-level project.${ColourReset}")
+    # Set default build and C++ options
+    include( configure_cpp_options )
+    set( CMAKE_FIND_FRAMEWORK "LAST" CACHE STRING
+         "Framework finding behaviour on macOS" )
+    # GeoModel dependencies
+    find_package( GeoModelCore REQUIRED )
+else()
+    # I am called from other project with add_subdirectory().
+    message( STATUS "Building ${PROJECT_NAME} as part of the root project.")
+    # Set the project
+    project( "LArCustomShapeExtensionSolid" VERSION 1.0 LANGUAGES CXX )
+endif()
+
+# Other project's dependencies.
+
+find_package( Geant4 REQUIRED )     
+
+include(${Geant4_USE_FILE})
+
+# Find the header and source files.
+file( GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/*.cxx ${CMAKE_CURRENT_SOURCE_DIR}/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/*.cxx ${CMAKE_CURRENT_SOURCE_DIR}/import/Simulation/G4Utilities/Geo2G4/src/*.cxx ${CMAKE_CURRENT_SOURCE_DIR}/LArCustomShapeExtensionSolid/*.cxx)
+
+# Define include directories
+include_directories(  ${CMAKE_CURRENT_SOURCE_DIR}/import/Control/CxxUtils ${CMAKE_CURRENT_SOURCE_DIR}/import/DetectorDescription/GeoModel/GeoSpecialShapes ${CMAKE_CURRENT_SOURCE_DIR}/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl  ${CMAKE_CURRENT_SOURCE_DIR}/import/Simulation/G4Utilities/Geo2G4 ${CMAKE_CURRENT_SOURCE_DIR}/import/Simulation/G4Utilities/Geo2G4/src ${GEANT4_INCLUDE_DIRS}  )
+
+add_definitions ( -DPORTABLE_LAR_SHAPE )
+
+add_library( LArCustomShapeExtensionSolid SHARED ${SOURCES} )
+target_link_libraries( LArCustomShapeExtensionSolid PUBLIC GeoModelCore::GeoModelKernel ${Geant4_LIBRARIES} ) 
+set_target_properties( LArCustomShapeExtensionSolid PROPERTIES
+		       VERSION ${PROJECT_VERSION}
+		       SOVERSION ${PROJECT_VERSION_MAJOR} )
+
+install( TARGETS LArCustomShapeExtensionSolid
+	 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+	 COMPONENT Runtime
+	 NAMELINK_COMPONENT Development )
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/LArCustomShapeExtensionSolid/LArCustomShapeExtensionSolid.cxx b/ATLAS-Extensions/LArCustomSolidExtension/LArCustomShapeExtensionSolid/LArCustomShapeExtensionSolid.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..4ff2f11041ddb2155d6b1099b5016a9c58c3a361
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/LArCustomShapeExtensionSolid/LArCustomShapeExtensionSolid.cxx
@@ -0,0 +1,196 @@
+#include "GeoModelKernel/GeoVG4ExtensionSolid.h"
+#include "GeoModelKernel/GeoUnidentifiedShape.h"
+#include "LArWheelSolid_type.h"
+#include "LArWheelSolid.h"
+#include "LArWheelSliceSolid.h"
+#include "LArWheelSolid_type.h"
+#include "GeoSpecialShapes/EMECData.h"
+#include <iostream>
+class LArCustomShapeExtensionSolid:public GeoVG4ExtensionSolid {
+
+public:
+
+  
+  //Constructor:
+  LArCustomShapeExtensionSolid();
+
+  //Destructor:
+  virtual ~LArCustomShapeExtensionSolid();
+
+  //Convert to G4
+  G4VSolid *newG4Solid(GeoUnidentifiedShape const*) const;
+
+private:
+
+  typedef std::map<std::string, G4VSolid*,std::less<std::string> > CustomSolidMap;
+  mutable CustomSolidMap customSolids;
+  
+};
+
+//Constructor:
+LArCustomShapeExtensionSolid::LArCustomShapeExtensionSolid() {
+  std::cout << "HELLO from LArCustomShapeExtensionSolid" << std::endl;
+}
+  //Destructor:
+LArCustomShapeExtensionSolid::~LArCustomShapeExtensionSolid() {
+  std::cout << "GOODBYE from LArCustomShapeExtensionSolid" << std::endl;
+}
+
+G4VSolid *LArCustomShapeExtensionSolid::newG4Solid(GeoUnidentifiedShape const* customShape) const{
+
+  typedef std::pair<LArWheelSolid_t, int> LArWheelSolidDef_t;
+  typedef std::map<std::string,  LArWheelSolidDef_t> TypeMap;
+  static const TypeMap types = {
+    /* entries w/o explicit Pos/Neg kept for backward compatibility */
+    { "LAr::EMEC::InnerWheel::Absorber",           {InnerAbsorberWheel, 1} },
+    { "LAr::EMEC::InnerWheel::Electrode",          {InnerElectrodWheel, 1} },
+    { "LAr::EMEC::InnerWheel::Glue",               {InnerGlueWheel, 1} },
+    { "LAr::EMEC::InnerWheel::Lead",               {InnerLeadWheel, 1} },
+    
+    { "LAr::EMEC::OuterWheel::Absorber",           {OuterAbsorberWheel, 1} },
+    { "LAr::EMEC::OuterWheel::Electrode",          {OuterElectrodWheel, 1} },
+    { "LAr::EMEC::OuterWheel::Glue",               {OuterGlueWheel, 1} },
+    { "LAr::EMEC::OuterWheel::Lead",               {OuterLeadWheel, 1} },
+    
+    { "LAr::EMEC::Pos::InnerWheel::Absorber",      {InnerAbsorberWheel, 1} },
+    { "LAr::EMEC::Pos::InnerWheel::Electrode",     {InnerElectrodWheel, 1} },
+    { "LAr::EMEC::Pos::InnerWheel::Glue",          {InnerGlueWheel, 1} },
+    { "LAr::EMEC::Pos::InnerWheel::Lead",          {InnerLeadWheel, 1} },
+    
+    { "LAr::EMEC::Pos::OuterWheel::Absorber",      {OuterAbsorberWheel, 1} },
+    { "LAr::EMEC::Pos::OuterWheel::Electrode",     {OuterElectrodWheel, 1} },
+    { "LAr::EMEC::Pos::OuterWheel::Glue",          {OuterGlueWheel, 1} },
+    { "LAr::EMEC::Pos::OuterWheel::Lead",          {OuterLeadWheel, 1} },
+    
+    { "LAr::EMEC::Neg::InnerWheel::Absorber",      {InnerAbsorberWheel, -1} },
+    { "LAr::EMEC::Neg::InnerWheel::Electrode",     {InnerElectrodWheel, -1} },
+    { "LAr::EMEC::Neg::InnerWheel::Glue",          {InnerGlueWheel, -1} },
+    { "LAr::EMEC::Neg::InnerWheel::Lead",          {InnerLeadWheel, -1} },
+    
+    { "LAr::EMEC::Neg::OuterWheel::Absorber",      {OuterAbsorberWheel, -1} },
+    { "LAr::EMEC::Neg::OuterWheel::Electrode",     {OuterElectrodWheel, -1} },
+    { "LAr::EMEC::Neg::OuterWheel::Glue",          {OuterGlueWheel, -1} },
+    { "LAr::EMEC::Neg::OuterWheel::Lead",          {OuterLeadWheel, -1} },
+    
+    { "LAr::EMEC::InnerModule::Absorber",          {InnerAbsorberModule, 1} },
+    { "LAr::EMEC::InnerModule::Electrode",         {InnerElectrodModule, 1} },
+    { "LAr::EMEC::OuterModule::Absorber",          {OuterAbsorberModule, 1} },
+    { "LAr::EMEC::OuterModule::Electrode",         {OuterElectrodModule, 1} },
+    
+    { "LAr::EMEC::Pos::InnerCone::Absorber",       {InnerAbsorberCone, 1} },
+    { "LAr::EMEC::Pos::InnerCone::Electrode",      {InnerElectrodCone, 1} },
+    { "LAr::EMEC::Pos::InnerCone::Glue",           {InnerGlueCone, 1} },
+    { "LAr::EMEC::Pos::InnerCone::Lead",           {InnerLeadCone, 1} },
+    
+    { "LAr::EMEC::Neg::InnerCone::Absorber",       {InnerAbsorberCone, -1} },
+    { "LAr::EMEC::Neg::InnerCone::Electrode",      {InnerElectrodCone, -1} },
+    { "LAr::EMEC::Neg::InnerCone::Glue",           {InnerGlueCone, -1} },
+    { "LAr::EMEC::Neg::InnerCone::Lead",           {InnerLeadCone, -1} },
+    
+    { "LAr::EMEC::Pos::OuterFrontCone::Absorber",  {OuterAbsorberFrontCone, 1} },
+    { "LAr::EMEC::Pos::OuterFrontCone::Electrode", {OuterElectrodFrontCone, 1} },
+    { "LAr::EMEC::Pos::OuterFrontCone::Glue",      {OuterGlueFrontCone, 1} },
+    { "LAr::EMEC::Pos::OuterFrontCone::Lead",      {OuterLeadFrontCone, 1} },
+    
+    { "LAr::EMEC::Neg::OuterFrontCone::Absorber",  {OuterAbsorberFrontCone, -1} },
+    { "LAr::EMEC::Neg::OuterFrontCone::Electrode", {OuterElectrodFrontCone, -1} },
+    { "LAr::EMEC::Neg::OuterFrontCone::Glue",      {OuterGlueFrontCone, -1} },
+    { "LAr::EMEC::Neg::OuterFrontCone::Lead",      {OuterLeadFrontCone, -1} },
+    
+    { "LAr::EMEC::Pos::OuterBackCone::Absorber",   {OuterAbsorberBackCone, 1} },
+    { "LAr::EMEC::Pos::OuterBackCone::Electrode",  {OuterElectrodBackCone, 1} },
+    { "LAr::EMEC::Pos::OuterBackCone::Glue",       {OuterGlueBackCone, 1} },
+    { "LAr::EMEC::Pos::OuterBackCone::Lead",       {OuterLeadBackCone, 1} },
+    
+    { "LAr::EMEC::Neg::OuterBackCone::Absorber",   {OuterAbsorberBackCone, -1} },
+    { "LAr::EMEC::Neg::OuterBackCone::Electrode",  {OuterElectrodBackCone, -1} },
+    { "LAr::EMEC::Neg::OuterBackCone::Glue",       {OuterGlueBackCone, -1} },
+    { "LAr::EMEC::Neg::OuterBackCone::Lead",       {OuterLeadBackCone, -1} }
+    
+  };
+
+ EMECData data;
+  EMECGEOMETRY emecgeometry;
+  EMECWHEELPARAMETERS emecwheelparameters[2];
+  EMECPARAMS emecparams;
+  EMECMAGICNUMBERS emecmagicnumbers;
+  EMECFAN emecfan;
+  COLDCONTRACTION  coldcontraction;
+
+  emecgeometry.Z0=368.95;
+  emecgeometry.Z1=369.1;
+  emecgeometry.DCF=368.9;
+  emecgeometry.DCRACK=.15;
+  emecgeometry.RLIMIT=203.4;
+  emecgeometry.ZSHIFT=4.5;
+
+  emecwheelparameters[0].ETAINT=3.2;
+  emecwheelparameters[0].ETAEXT=2.5;
+  emecwheelparameters[0].NABS=256;
+  emecwheelparameters[0].NACC=6;
+  emecwheelparameters[1].ETAEXT=1.375;
+  emecwheelparameters[1].NABS=768;
+  emecwheelparameters[1].NACC=9;
+
+  emecmagicnumbers.ACTIVELENGTH=510;
+  emecmagicnumbers.STRAIGHTSTARTSECTION=2;
+  emecmagicnumbers.REFTOACTIVE=11;
+
+  emecparams.PHIROTATION="off";
+  emecparams.SAGGING="off";
+  emecparams.INNERSLANTPARAM="default";
+  emecparams.OUTERSLANTPARAM="default";
+
+  coldcontraction.ABSORBERCONTRACTION=0.991;
+  coldcontraction.ELECTRODEINVCONTRACTION=1.0036256;
+
+  emecfan.LEADTHICKNESSINNER=2.2;
+  emecfan.LEADTHICKNESSOUTER=1.69;
+  emecfan.STEELTHICKNESS=0.2;
+  emecfan.GLUETHICKNESS=0.1;
+  emecfan.ELECTRODETOTALTHICKNESS=0.275;
+    
+  data.emecgeometry.push_back(emecgeometry);
+  data.emecwheelparameters.push_back(emecwheelparameters[0]);
+  data.emecwheelparameters.push_back(emecwheelparameters[1]);
+  data.emecmagicnumbers.push_back(emecmagicnumbers);
+  data.emecparams.push_back(emecparams);
+  data.emecfan.push_back(emecfan);
+  data.coldcontraction.push_back(coldcontraction);
+
+
+  
+
+  G4VSolid *theSolid=nullptr;
+  
+  if (customShape->name()=="LArCustomShape") {
+    std::string customName = customShape->asciiData();
+    CustomSolidMap::const_iterator it = customSolids.find(customName);
+    if(it!=customSolids.end())
+      theSolid = it->second;
+    else {
+      theSolid = nullptr;
+      
+      if(customName.find("Slice") != std::string::npos){
+	theSolid = new LArWheelSliceSolid(customShape->asciiData(),&data);
+      } else {
+	theSolid = new LArWheelSolid(customShape->asciiData(), types.at(customShape->asciiData()).first, types.at(customShape->asciiData()).second,nullptr,&data); 
+      }
+      if ( nullptr == theSolid ) {
+	std::string error = std::string("Can't create LArWheelSolid for name ") + customName + " in LArCustomShapeExtensionSolid";
+	throw std::runtime_error(error);
+      }
+      if(theSolid != nullptr) customSolids[customName] = theSolid;
+      
+    }
+    
+  }
+  
+  return theSolid;
+}
+
+
+extern "C" LArCustomShapeExtensionSolid *createLArCustomShapeExtensionSolid() {
+
+  return new LArCustomShapeExtensionSolid;
+}
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/README.md b/ATLAS-Extensions/LArCustomSolidExtension/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..0495343967a2ea9838725d5c51d0e6715783b910
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/README.md
@@ -0,0 +1,21 @@
+# LArCustomSolid Extension
+
+This repository contains the LArCustomSolid Extension that describe the ATLAS LAr EMEC custom shape. The LAr EMEC custom shape extension is build starting from a copy of the corresponding code in the [Athena](https://gitlab.cern.ch/atlas/athena) repository. 
+
+## How to build the LArCustomSolid Extension
+The LArCustomSolid Extension depends on GeoModel, so you need to install first GeoModel from this [repository](https://gitlab.cern.ch/GeoModelDev/GeoModel). 
+
+```bash
+git clone https://gitlab.cern.ch/SolidExtensions/LArCustomSolidExtension.git
+cd LArCustomSolidExtension
+mkdir build
+cd build
+cmake ../ -DCMAKE_INSTALL_PREFIX=<path_to_GeoModel_install_dir>
+make -j
+make install
+```
+This will produce a LArCustomSolidExtension.so/dylib library that can be used to build the EMEC detector.
+
+## How to update the LArCustomSolid Extension
+
+To keep the code of the LArCustomSolid Extention synchronized with the latest available version in the [Athena](https://gitlab.cern.ch/atlas/athena) repository, just run the autonomous-lar.sh script.
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/autonomous-lar.sh b/ATLAS-Extensions/LArCustomSolidExtension/autonomous-lar.sh
new file mode 100755
index 0000000000000000000000000000000000000000..1fab024b44cf9c14b78abd813bccd2612ef13a2e
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/autonomous-lar.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+mkdir -p import
+cd import
+
+git init
+git config core.sparsecheckout true
+
+echo Control/CxxUtils/CxxUtils/features.h                                                                                   >  .git/info/sparse-checkout
+echo Control/CxxUtils/CxxUtils/restrict.h                                                                                  >> .git/info/sparse-checkout
+echo Control/CxxUtils/CxxUtils/sincos.h                                                                                    >> .git/info/sparse-checkout
+echo Control/CxxUtils/CxxUtils/vec.h                                                                                       >> .git/info/sparse-checkout
+
+
+echo DetectorDescription/GeoModel/GeoSpecialShapes/GeoSpecialShapes/EMECData.h                                             >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/GeoSpecialShapes/toEMECData.h                                           >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/GeoSpecialShapes/LArWheelCalculatorEnums.h                              >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/GeoSpecialShapes/LArWheelCalculator.h                                   >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/GeoSpecialShapes/vec_parametrized_sincos.h                              >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/PortableMsgStream/PortableMsgStream.h                                   >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator.cxx                                              >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculatorGeometry.cxx                                      >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorFactory.cxx               >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorSaggingOff.cxx            >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorSaggingOn.cxx             >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/FanCalculatorFactory.cxx                    >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/ModuleFanCalculator.cxx                     >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/sincos_poly.cxx                             >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorFactory.h                 >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorSaggingOff.h              >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorSaggingOn.h               >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/FanCalculatorFactory.h                      >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/ModuleFanCalculator.h                       >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/WheelFanCalculator.h                        >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/IFanCalculator.h                            >> .git/info/sparse-checkout
+echo DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/IDistanceCalculator.h                       >> .git/info/sparse-checkout
+
+
+
+echo Simulation/G4Utilities/Geo2G4/src/LArFanSection.cxx                                                                   >> .git/info/sparse-checkout
+echo Simulation/G4Utilities/Geo2G4/src/LArWheelSliceSolid.cxx                                                              >> .git/info/sparse-checkout
+echo Simulation/G4Utilities/Geo2G4/src/LArWheelSliceSolidDisToIn.cxx                                                       >> .git/info/sparse-checkout
+echo Simulation/G4Utilities/Geo2G4/src/LArWheelSliceSolidDisToOut.cxx                                                      >> .git/info/sparse-checkout
+echo Simulation/G4Utilities/Geo2G4/src/LArWheelSliceSolidInit.cxx                                                          >> .git/info/sparse-checkout
+echo Simulation/G4Utilities/Geo2G4/src/LArWheelSolid.cxx                                                                   >> .git/info/sparse-checkout
+echo Simulation/G4Utilities/Geo2G4/src/LArWheelSolidDisToIn.cxx                                                            >> .git/info/sparse-checkout
+echo Simulation/G4Utilities/Geo2G4/src/LArWheelSolidDisToOut.cxx                                                           >> .git/info/sparse-checkout
+echo Simulation/G4Utilities/Geo2G4/src/LArWheelSolidInit.cxx                                                               >> .git/info/sparse-checkout
+echo Simulation/G4Utilities/Geo2G4/src/G4ShiftedCone.cxx                                                                   >> .git/info/sparse-checkout
+echo Simulation/G4Utilities/Geo2G4/src/LArFanSection.h                                                                     >> .git/info/sparse-checkout
+echo Simulation/G4Utilities/Geo2G4/src/LArWheelSliceSolid.h                                                                >> .git/info/sparse-checkout
+echo Simulation/G4Utilities/Geo2G4/src/LArWheelSolid_type.h                                                                >> .git/info/sparse-checkout
+echo Simulation/G4Utilities/Geo2G4/src/LArWheelSolid.h                                                                     >> .git/info/sparse-checkout
+echo Simulation/G4Utilities/Geo2G4/src/G4ShiftedCone.h                                                                     >> .git/info/sparse-checkout
+echo Simulation/G4Utilities/Geo2G4/src/G4ShiftedCone.icc                                                                   >> .git/info/sparse-checkout
+
+
+git remote add -f origin https://gitlab.cern.ch/atlas/athena.git
+git pull origin master
+rm -rf .git
+
+
+
+
+
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/cmake/BuildType.cmake b/ATLAS-Extensions/LArCustomSolidExtension/cmake/BuildType.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..14a12a8ccc0c22511e31288d7ab3b4fea69fb561
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/cmake/BuildType.cmake
@@ -0,0 +1,28 @@
+
+# Author: Marcus D. Hanwell
+# Source: https://blog.kitware.com/cmake-and-the-default-build-type/
+
+# Set a default build type if none was specified
+set(default_build_type "Release")
+
+# TODO: at the moment, we want to build in Release mode by default,
+# even if we build from a Git clone, because that is the default mode
+# for our users to get the source code.
+# But maybe we will want to change this behavior, later?
+# if(EXISTS "${CMAKE_SOURCE_DIR}/.git")
+#   set(default_build_type "Debug")
+# endif()
+
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+  if( COLOR_DEFS )
+    message(STATUS "${Blue}INFO: Setting build type to '${default_build_type}' as none was specified.${ColourReset}")
+  else()
+    message(STATUS "INFO: Setting build type to '${default_build_type}' as none was specified.")
+  endif()
+  set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE
+      STRING "Choose the type of build." FORCE)
+  # Set the possible values of build type for cmake-gui
+  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+    "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
+endif()
+
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/cmake/PrintBuildInfo.cmake b/ATLAS-Extensions/LArCustomSolidExtension/cmake/PrintBuildInfo.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..862a34b45c7506c83a229d7ef71ff604d182acb6
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/cmake/PrintBuildInfo.cmake
@@ -0,0 +1,13 @@
+# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+
+if( COLOR_DEFS )
+  message(STATUS "-----")
+  message(STATUS "${BoldYellow}Building with type: ${CMAKE_BUILD_TYPE}${ColourReset}")
+  message(STATUS "${BoldYellow}Using C++ standard: ${CMAKE_CXX_STANDARD}${ColourReset}")
+  message(STATUS "-----")
+else()
+  message(STATUS "-----")
+  message(STATUS "Building with type: ${CMAKE_BUILD_TYPE}")
+  message(STATUS "Using C++ standard: ${CMAKE_CXX_STANDARD}")
+  message(STATUS "-----")
+endif()
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/cmake/cmake_colors_defs.cmake b/ATLAS-Extensions/LArCustomSolidExtension/cmake/cmake_colors_defs.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..b6eea59ba9e72a50754ac0afb36d25cfcac59e09
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/cmake/cmake_colors_defs.cmake
@@ -0,0 +1,25 @@
+
+# Copyright: "Fraser" (https://stackoverflow.com/users/2556117/fraser)
+# CC BY-SA 3.0
+# Source: https://stackoverflow.com/a/19578320/320369
+
+if(NOT WIN32)
+  set( COLOR_DEFS TRUE CACHE BOOL "Define color escape sequences to be used in CMake messages." )
+  string(ASCII 27 Esc)
+  set(ColourReset "${Esc}[m")
+  set(ColourBold  "${Esc}[1m")
+  set(Red         "${Esc}[31m")
+  set(Green       "${Esc}[32m")
+  set(Yellow      "${Esc}[33m")
+  set(Blue        "${Esc}[34m")
+  set(Magenta     "${Esc}[35m")
+  set(Cyan        "${Esc}[36m")
+  set(White       "${Esc}[37m")
+  set(BoldRed     "${Esc}[1;31m")
+  set(BoldGreen   "${Esc}[1;32m")
+  set(BoldYellow  "${Esc}[1;33m")
+  set(BoldBlue    "${Esc}[1;34m")
+  set(BoldMagenta "${Esc}[1;35m")
+  set(BoldCyan    "${Esc}[1;36m")
+  set(BoldWhite   "${Esc}[1;37m")
+endif()
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/cmake/configure_cpp_options.cmake b/ATLAS-Extensions/LArCustomSolidExtension/cmake/configure_cpp_options.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..9a6cc8a7902de8bd14d412f0ca4fc86a3bc116fc
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/cmake/configure_cpp_options.cmake
@@ -0,0 +1,34 @@
+
+#
+# Set build options and C++ standards and options
+#
+# This file sets up
+#
+#   CMAKE_BUILD_TYPE
+#   CMAKE_CXX_STANDARD
+#   CMAKE_CXX_EXTENSIONS
+#   CMAKE_CXX_STANDARD_REQUIRED
+#
+# The options can be overridden at configuration time by using, e.g.:
+#    `cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_STANDARD=14 ../GeoModelIO`
+# on the command line.
+#
+
+# Set default build options.
+set( CMAKE_BUILD_TYPE "Release" CACHE STRING "CMake build mode to use" )
+set( CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard used for the build" )
+set( CMAKE_CXX_EXTENSIONS FALSE CACHE BOOL "(Dis)allow using GNU extensions" )
+set( CMAKE_CXX_STANDARD_REQUIRED TRUE CACHE BOOL
+   "Require the specified C++ standard for the build" )
+
+# Setting CMAKE_CXX_FLAGS to avoid "deprecated" warnings
+set(CMAKE_CXX_FLAGS "-Wno-deprecated-declarations" ) # very basic
+#set(CMAKE_CXX_FLAGS "-Wall -Werror -pedantic-errors -Wno-deprecated-declarations" ) # good enough for a quick, better check
+#set(CMAKE_CXX_FLAGS "-Wall -Wextra -Werror -pedantic-errors -Wno-deprecated-declarations" ) # better for a thorough check
+#set(CMAKE_CXX_FLAGS "-Wall -Wextra -Werror -pedantic-errors" ) # better for an even more severe check
+#set(CMAKE_CXX_FLAGS "-Weverything -Werror -pedantic-errors" ) # not recommended, it warns for really EVERYTHING!
+
+
+# TODO: for Debug and with GCC, do we want to set the flags below by default?
+# set( CMAKE_BUILD_TYPE DEBUG )
+# set(CMAKE_CXX_FLAGS "-fPIC -O0 -g -gdwarf-2" )
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/Control/CxxUtils/CxxUtils/features.h b/ATLAS-Extensions/LArCustomSolidExtension/import/Control/CxxUtils/CxxUtils/features.h
new file mode 100644
index 0000000000000000000000000000000000000000..e063f476568470b1fd6d0ed2b1d4a557e7870b5e
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/Control/CxxUtils/CxxUtils/features.h
@@ -0,0 +1,84 @@
+// This file's extension implies that it's C, but it's really -*- C++ -*-.
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+/**
+ * @file CxxUtils/features.h
+ * @author scott snyder <snyder@bnl.gov>
+ * @date May, 2018
+ * @brief Some additional feature test macros.
+ */
+
+#ifndef CXXUTILS_FEATURES_H
+#define CXXUTILS_FEATURES_H
+
+#include <features.h>
+
+/// Do we have function multiversioning?  GCC and clang > 7 support
+/// the target attribute
+#if ( defined(__i386__) || defined(__x86_64__) ) &&                            \
+  defined(__ELF__) && defined(__GNUC__) && !defined(__CLING__) &&              \
+  !defined(__ICC) && !defined(__COVERITY__) && !defined(__CUDACC__) &&         \
+  !defined(CL_SYCL_LANGUAGE_VERSION) && !defined(SYCL_LANGUAGE_VERSION) &&     \
+  !defined(__HIP__)
+# define HAVE_FUNCTION_MULTIVERSIONING 1
+#else
+# define HAVE_FUNCTION_MULTIVERSIONING 0
+#endif
+
+/// Do we have the target_clones attribute?  clang  does not support it
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) &&             \
+  !defined(__COVERITY__) && !defined(__CUDACC__)
+# define HAVE_TARGET_CLONES 1
+#else
+# define HAVE_TARGET_CLONES 0
+#endif
+
+/// Do we have support for all GCC intrinsics?
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) &&             \
+  !defined(__COVERITY__) && !defined(__CUDACC__)
+# define HAVE_GCC_INTRINSICS 1
+#else
+# define HAVE_GCC_INTRINSICS 0
+#endif
+
+/// Do we have the bit-counting intrinsics?
+// __builtin_ctz
+// __builtin_ctzl
+// __builtin_ctzll
+// __builtin_clz
+// __builtin_clzl
+// __builtin_clzll
+// __builtin_popcount
+// __builtin_popcountl
+// __builtin_popcountll
+#if defined(__GNUC__) || defined(__clang__)
+# define HAVE_BITCOUNT_INTRINSICS 1
+#else
+# define HAVE_BITCOUNT_INTRINSICS 0
+#endif
+
+// Do we have the vector_size attribute for writing explicitly
+// vectorized code?
+#if (defined(__GNUC__) || defined(__clang__)) && !defined(__CLING__) &&        \
+  !defined(__ICC) && !defined(__COVERITY__) && !defined(__CUDACC__)
+#define HAVE_VECTOR_SIZE_ATTRIBUTE 1
+#else
+# define HAVE_VECTOR_SIZE_ATTRIBUTE 0
+#endif
+
+// Do we additionally support the clang
+// __builtin_convertvector
+// GCC>=9 does
+#if HAVE_VECTOR_SIZE_ATTRIBUTE && (defined(__clang__) || (__GNUC__ >= 9))
+#define HAVE_CONVERT_VECTOR 1
+#else
+#define HAVE_CONVERT_VECTOR 0
+#endif
+
+// Do we have mallinfo2?  Present in glibc 2.33,
+// in which mallinfo is deprecated.
+#define HAVE_MALLINFO2 (__GLIBC_PREREQ(2, 33))
+
+
+#endif // not CXXUTILS_FEATURES_H
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/Control/CxxUtils/CxxUtils/restrict.h b/ATLAS-Extensions/LArCustomSolidExtension/import/Control/CxxUtils/CxxUtils/restrict.h
new file mode 100644
index 0000000000000000000000000000000000000000..da806b3aa758f62358a2f0c4eeeec3e9826538c4
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/Control/CxxUtils/CxxUtils/restrict.h
@@ -0,0 +1,35 @@
+// This file's extension implies that it's C, but it's really -*- C++ -*-.
+/*
+ * Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration.
+ */
+/**
+ * @file CxxUtils/restrict.h
+ * @author scott snyder <snyder@bnl.gov>
+ * @date May, 2019
+ * @brief Macro wrapping the nonstandard restrict keyword.
+ *
+ * Use the @c ATH_RESTRICT macro for the non-standard __restrict__ keyword.
+ */
+
+
+#ifndef CXXUTILS_RESTRICT_H
+#define CXXUTILS_RESTRICT_H
+
+
+#ifndef ATH_HAS_RESTRICT
+# if defined(__GNUC__) || defined(__clang__)
+#  define ATH_HAS_RESTRICT 1
+# else
+#  define ATH_HAS_RESTRICT 0
+# endif
+#endif
+
+
+#if ATH_HAS_RESTRICT
+# define ATH_RESTRICT __restrict__
+#else
+# define ATH_RESTRICT
+#endif
+
+
+#endif // not CXXUTILS_RESTRICT_H
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/Control/CxxUtils/CxxUtils/sincos.h b/ATLAS-Extensions/LArCustomSolidExtension/import/Control/CxxUtils/CxxUtils/sincos.h
new file mode 100644
index 0000000000000000000000000000000000000000..e6ba0026b44134ff1c4488195064af2ff38c254e
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/Control/CxxUtils/CxxUtils/sincos.h
@@ -0,0 +1,109 @@
+// This file's extension implies that it's C, but it's really -*- C++ -*-.
+
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+// $Id: sincos.h,v 1.1 2008-11-24 04:34:07 ssnyder Exp $
+
+/**
+ * @file CxxUtils/sincos.h
+ * @author scott snyder
+ * @date Nov 2008, from older D0 code.
+ * @brief Helper to simultaneously calculate sin and cos of the same angle.
+ */
+
+
+#ifndef CXXUTILS_SINCOS_H
+#define CXXUTILS_SINCOS_H
+
+
+#include <cmath>
+
+
+namespace CxxUtils {
+
+
+/**
+ * @brief Helper to simultaneously calculate sin and cos of the same angle.
+ *
+ * Instantiate an instance of this object, passing the angle to the
+ * constructor.  The sin and cos are then available as the sn and cs
+ * members.  In addition, the apply() method may be used to calculate
+ * a*sin(x) + b*cos(x).
+ *
+ * Implementation notes:
+ * 
+ * The i386/x87 architecture has an instruction to do this.
+ * So, on that platform, we use that instruction directly.
+ * It seems to be a win to use it even if we're using SSE math,
+ * so we'll use it for x86_64 too.
+ * Otherwise, we'll use sincos() if it's available (it's a GNU extension).
+ * Otherwise, just call sin() and cos() separately.
+ *
+ * Note that the fsincos instruction only works correctly
+ * if the input angle is in the range -2^63 ... 2^63.
+ * This is not likely to be an issue for us.
+ *
+ * Why prefer using the fsincos instruction directly to calling
+ * the sincos() library function?
+ *
+ * - It turns out to be a little difficult to ensure that
+ *   sincos()  actually gets inlined.  In general, one needs -ffast-math
+ *   (which isn't on for standard Atlas builds), but even that's not always
+ *   sufficient.
+ *
+ * - The library version includes extra code that we
+ *   don't really need to handle the case where
+ *   abs(angle) > 2^63.  (Runtime penalty, though, is
+ *   moving the FPU status word to $eax and one
+ *   taken branch.)
+ *
+ * - Most importantly, though, the library function
+ *   takes pointers into which the results are stored.
+ *   Playing with this, i was unable to prevent the
+ *   calculated values from being spilled to memory.
+ *   With the definition used below, we don't necessarily
+ *   spill to memory.  A sequence like
+ *
+ *      sincos sc (ang);
+ *      double a = sc.apply (x, y)
+ *
+ *   can be calculated entirely in the FPU register file,
+ *   with no spills.
+ */
+struct sincos
+{
+  // cppcheck-suppress uninitMemberVar  ; false positive
+  /// Calculate sine and cosine of x.
+  sincos (double x)
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+  // Inline assembly version.  Uses x87 FPU.
+  { __asm __volatile__ ("fsincos\n\t" : "=t" (cs), "=u" (sn) : "0" (x)); }
+#elif defined(__USE_GNU)
+  // Version using the GNU sincos() function.
+  { ::sincos(x, &sn, &cs); }
+#else
+  // Generic version.
+  : sn (std::sin (x)), cs (std::cos (x)) {}
+#endif
+
+  /// @f$\sin(x)@f$
+  double sn;
+
+  /// @f$\cos(x)@f$
+  double cs;
+
+  /// @f$a\sin(x) + b\cos(x)@f$
+  double apply (double a, double b) const { return a*sn + b*cs; }
+
+  /// @f$a\sin^2(x) + b\sin(x)\cos(x) + c\cos^2(x)@f$
+  double apply2 (double a, double b, double c) const
+  { return a*sn*sn + b*sn*cs + c*cs*cs; }
+};
+
+
+} // namespace CxxUtils
+
+
+#endif //not CXXUTILS_SINCOS_H
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/Control/CxxUtils/CxxUtils/vec.h b/ATLAS-Extensions/LArCustomSolidExtension/import/Control/CxxUtils/CxxUtils/vec.h
new file mode 100644
index 0000000000000000000000000000000000000000..b53500189ce2c6a8a77ce6538fc4b0fa83f8d9a1
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/Control/CxxUtils/CxxUtils/vec.h
@@ -0,0 +1,414 @@
+// This file's extension implies that it's C, but it's really -*- C++ -*-.
+/*
+ * Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration.
+ */
+/**
+ * @file CxxUtils/vec.h
+ * @author scott snyder <snyder@bnl.gov>
+ * @author Christos Anastopoulos (additional helper methods)
+ * @date Mar, 2020
+ * @brief Vectorization helpers.
+ *
+ * gcc and clang provide built-in types for writing vectorized code,
+ * using the vector_size attribute.  This usually results in code
+ * that is much easier to read and more portable than one would get
+ * using intrinsics directly.  However, it is still non-standard,
+ * and there are some operations which are kind of awkward.
+ *
+ * This file provides some helpers for writing vectorized code
+ * in C++.
+ *
+ * A vectorized type may be named as @c CxxUtils::vec<T, N>.  Here @c T is the
+ * element type, which should be an elementary integer or floating-point type.
+ * @c N is the number of elements in the vector; it should be a power of 2.
+ * This will either be a built-in vector type if the @c vector_size
+ * attribute is supported or a fallback C++ class intended to be
+ * (mostly) functionally equivalent (see vec_fb.h)
+ *
+ *
+ * The GCC, clang and fallback vector types support:
+ * ++, --, +,-,*,/,%, =, &,|,^,~, >>,<<, !, &&, ||,
+ * ==, !=, >, <, >=, <=, =, sizeof and Initialization from brace-enclosed lists
+ *
+ * Furthermore the GCC and clang vector types support the ternary operator.
+ *
+ * We also support some additional operations.
+ *
+ * Deducing useful types:
+ *
+ *  - @c CxxUtils::vec_type_t<VEC> is the element type of @c VEC.
+ *  - @c CxxUtils::vec_mask_type_t<VEC> is the vector type return by relational
+ *                                  operations.
+ *
+ * Deducing the num of elements in a vectorized type:
+ *
+ *  - @c CxxUtils::vec_size<VEC>() is the number of elements in @c VEC.
+ *  - @c CxxUtils::vec_size(const VEC&) is the number of elements in @c VEC.
+ *
+ * Additional Helpers for common SIMD operations:
+ *
+ *  - @c CxxUtils::vbroadcast (VEC& v, T x) initializes each element of
+ *                                          @c v with @c x.
+ *  - @c CxxUtils::vload (VEC& dst, const vec_type_t<VEC>* src)
+ *                                          loads elements from @c src
+ *                                          to @c dst
+ *  - @c CxxUtils::vstore (vec_type_t<VEC>* dst, const VEC& src)
+ *                                          stores elements from @c src
+ *                                          to @c dst
+ *  - @c CxxUtils::vselect (VEC& dst, const VEC& a, const VEC& b, const
+ *                          vec_mask_type_t<VEC>& mask) copies elements
+ *                          from @c a or @c b, depending
+ *                          on the value of @c  mask to @c dst.
+ *                          dst[i] = mask[i] ? a[i] : b[i]
+ *  - @c CxxUtils::vmin    (VEC& dst, const VEC& a, const VEC& b)
+ *                         copies to @c dst[i]  the min(a[i],b[i])
+ *  - @c CxxUtils::vmax    (VEC& dst, const VEC& a, const VEC& b)
+ *                         copies to @c dst[i]  the max(a[i],b[i])
+ *  - @c CxxUtils::vconvert (VEC1& dst, const VEC2& src)
+ *                          Fills @c dst with the result  of a
+ *                          static_cast of every element of @c src
+ *                          to the element type of dst.
+ *                          dst[i] = static_cast<vec_type_t<VEC1>>(src[i])
+ *
+ *  Functions that construct a permutation of elements from one or two vectors
+ *  and return a vector of the same type as the input vector(s).
+ *  The mask has the same element count  as the vectors.
+ *  Intentionally kept compatible with gcc's _builtin_shuffle.
+ *  If we move to gcc>=12 we could unify with clang's _builtin_shuffle_vector
+ *  and relax some of these requirements
+ *
+ *  - @c CxxUtils::vpermute<mask> (VEC& dst, const VEC& src)
+ *                          Fills dst with permutation of src
+ *                          according to mask.
+ *                          @c mask is a list of integers that specifies the elements
+ *                          that should be extracted and returned in @c src.
+ *                          dst[i] = src[mask[i]] where mask[i] is the ith integer
+ *                          in the @c mask.
+ *
+ *  - @c CxxUtils::vpermute2<mask> (VEC& dst, const VEC& src1,const VEC& src2)
+ *                          Fills @c dst with permutation of @c src1 and @c src2
+ *                          according to @c mask.
+ *                          @c mask is a list of integers that specifies the elements
+ *                          that should be extracted from @c src1 and @c src2.
+ *                          An index i in the interval [0,N) indicates that element number i
+ *                          from the first input vector should be placed in the
+ *                          corresponding position in the result vector.
+ *                          An index in the interval [N,2N)
+ *                          indicates that the element number i-N
+ *                          from the second input vector should be placed
+ *                          in the corresponding position in the result vector.
+ *
+ * In terms of expected performance it might be  advantageous to
+ * use vector types that fit the size of the ISA.
+ * e.g 128 bit wide for SSE, 256 wide for AVX.
+ *
+ * Specifying a combination that is not valid for the current architecture
+ * causes the compiler to synthesize the instructions using a narrower mode.
+ *
+ * Consider using Function Multiversioning (CxxUtils/features.h)
+ * if you really need to target efficiently multiple ISAs.
+ */
+
+#ifndef CXXUTILS_VEC_H
+#define CXXUTILS_VEC_H
+
+#include "CxxUtils/features.h"
+#include <cstdlib>
+#include <cstring>
+#include <type_traits>
+
+
+// Define @c WANT_VECTOR_FALLBACK prior to including this file to
+// make the fallback class @c vec_fb visible, even if we support the
+// built-in type.
+// Intended for testing.
+#ifndef WANT_VECTOR_FALLBACK
+# define WANT_VECTOR_FALLBACK 0
+#endif
+
+#if (!HAVE_VECTOR_SIZE_ATTRIBUTE) || WANT_VECTOR_FALLBACK!=0
+#include "CxxUtils/vec_fb.h"
+#endif // !HAVE_VECTOR_SIZE_ATTRIBUTE || WANT_VECTOR_FALLBACK
+
+namespace CxxUtils {
+
+
+/**
+ * @brief check the type and the size  of the vector.
+ * Choose between the built-in (if available or
+ * fallback type.
+ */
+template <typename T, size_t N>
+struct vec_typedef{
+   static_assert((N & (N-1)) == 0, "N must be a power of 2.");
+   static_assert(std::is_arithmetic_v<T>, "T not an arithmetic type");
+
+#if HAVE_VECTOR_SIZE_ATTRIBUTE
+
+   using type  __attribute__ ((vector_size(N*sizeof(T)))) = T;
+
+#else
+
+   using type vec_fb<T, N>;
+
+#endif
+};
+
+/**
+ * @brief Define a nice alias for the vectorized type
+ */
+template <typename T, size_t N>
+using vec = typename vec_typedef<T,N>::type;
+
+/**
+ * @brief Deduce the element type from a vectorized type.
+ */
+template <class VEC>
+struct vec_type
+{
+  // Requires c++20.
+  //typedef typename std::invoke_result< decltype([](const VEC& v){return v[0];}), VEC& >::type type;
+
+  // Works in c++17.
+  static auto elt (const VEC& v) -> decltype( v[0] );
+  typedef typename std::invoke_result< decltype(elt), const VEC& >::type type1;
+  typedef std::remove_cv_t<std::remove_reference_t<type1> > type;
+};
+
+/**
+ * @brief Define a nice alias for the element type of a vectorized type
+ */
+template<class VEC>
+using vec_type_t = typename vec_type<VEC>::type;
+
+/**
+ * @brief Deduce the type of the mask returned by relational operations,
+ * for a vectorized type.
+ */
+template<class VEC>
+struct vec_mask_type
+{
+  static auto maskt(const VEC& v1, const VEC& v2) -> decltype(v1 < v2);
+  typedef
+    typename std::invoke_result<decltype(maskt), const VEC&, const VEC&>::type type1;
+  typedef std::remove_cv_t<std::remove_reference_t<type1>> type;
+};
+
+/**
+ * @brief Define a nice alias for the mask type for a vectorized type.
+ */
+template<class VEC>
+using vec_mask_type_t = typename vec_mask_type<VEC>::type;
+
+/**
+ * @brief Return the number of elements in a vectorized type.
+ */
+template<class VEC>
+inline constexpr size_t
+vec_size()
+{
+  typedef vec_type_t<VEC> ELT;
+  return sizeof(VEC) / sizeof(ELT);
+}
+
+/**
+ * @brief Return the number of elements in a vectorized type.
+ */
+template<class VEC>
+inline constexpr size_t
+vec_size(const VEC&)
+{
+  typedef vec_type_t<VEC> ELT;
+  return sizeof(VEC) / sizeof(ELT);
+}
+
+/**
+ * @brief Copy a scalar to each element of a vectorized type.
+ */
+template<typename VEC, typename T>
+inline void
+vbroadcast(VEC& v, T x)
+{
+#if !HAVE_VECTOR_SIZE_ATTRIBUTE || WANT_VECTOR_FALLBACK
+  constexpr size_t N = CxxUtils::vec_size<VEC>();
+  for (size_t i = 0; i < N; ++i) {
+    v[i] = x;
+  }
+#else
+  // using  - to avoid sign conversions.
+  v = x - VEC{ 0 };
+#endif
+}
+
+/*
+ * @brief load elements from  memory address src (C-array)
+ * to a vectorized type dst.
+ * Used memcpy to avoid alignment issues
+ */
+template<typename VEC>
+inline void
+vload(VEC& dst, vec_type_t<VEC> const* src)
+{
+  std::memcpy(&dst, src, sizeof(VEC));
+}
+
+/*
+ * @brief store elements from a vectorized type src to
+ * to a memory address dst (C-array).
+ * Uses memcpy to avoid alignment issues
+ */
+template<typename VEC>
+inline void
+vstore(vec_type_t<VEC>* dst, const VEC& src)
+{
+  std::memcpy(dst, &src, sizeof(VEC));
+}
+
+/*
+ * @brief select elements based on a mask
+ * Fill dst according to
+ * dst[i] = mask[i] ? a[i] : b[i]
+ */
+template<typename VEC>
+inline void
+vselect(VEC& dst, const VEC& a, const VEC& b, const vec_mask_type_t<VEC>& mask)
+{
+#if !HAVE_VECTOR_SIZE_ATTRIBUTE || WANT_VECTOR_FALLBACK
+  constexpr size_t N = vec_size<VEC>();
+  for (size_t i = 0; i < N; ++i) {
+    dst[i] = mask[i] ? a[i] : b[i];
+  }
+#else
+  dst = mask ? a : b;
+#endif
+}
+
+/*
+ * @brief vectorized min.
+ * copies to @c dst[i]  the min(a[i],b[i])
+ */
+template<typename VEC>
+inline void
+vmin(VEC& dst, const VEC& a, const VEC& b)
+{
+#if !HAVE_VECTOR_SIZE_ATTRIBUTE || WANT_VECTOR_FALLBACK
+  constexpr size_t N = vec_size<VEC>();
+  for (size_t i = 0; i < N; ++i) {
+    dst[i] = a[i] < b[i] ? a[i] : b[i];
+  }
+#else
+  dst = a < b ? a : b;
+#endif
+}
+
+/*
+ * @brief vectorized max.
+ * copies to @c dst[i]  the max(a[i],b[i])
+ */
+template<typename VEC>
+inline void
+vmax(VEC& dst, const VEC& a, const VEC& b)
+{
+#if !HAVE_VECTOR_SIZE_ATTRIBUTE || WANT_VECTOR_FALLBACK
+  constexpr size_t N = vec_size<VEC>();
+  for (size_t i = 0; i < N; ++i) {
+    dst[i] = a[i] > b[i] ? a[i] : b[i];
+  }
+#else
+  dst = a > b ? a : b;
+#endif
+}
+
+template<typename VEC1, typename VEC2>
+inline void
+vconvert(VEC1& dst, const VEC2& src)
+{
+  static_assert((vec_size<VEC1>() == vec_size<VEC2>()),
+                "vconvert dst and src have different number of elements");
+
+#if !HAVE_CONVERT_VECTOR || WANT_VECTOR_FALLBACK
+  typedef vec_type_t<VEC1> ELT;
+  constexpr size_t N = vec_size<VEC1>();
+  for (size_t i = 0; i < N; ++i) {
+    dst[i] = static_cast<ELT>(src[i]);
+  }
+#else
+  dst = __builtin_convertvector(src, VEC1);
+#endif
+}
+
+/**
+ * @brief Helper for static asserts for argument packs
+ */
+namespace bool_pack_helper {
+template<bool...>
+struct bool_pack;
+template<bool... bs>
+using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>;
+}
+/**
+ * @brief vpermute function.
+ * move any element of a vector src
+ * into any or multiple position inside dst.
+ */
+template<size_t... Indices, typename VEC>
+inline void
+vpermute(VEC& dst, const VEC& src)
+{
+
+  constexpr size_t N = vec_size<VEC>();
+  static_assert((sizeof...(Indices) == N),
+                "vpermute number of indices different than vector size");
+  static_assert(
+    bool_pack_helper::all_true<(Indices >= 0 && Indices < N)...>::value,
+    "vpermute value of a mask index is outside the allowed range");
+
+#if !HAVE_VECTOR_SIZE_ATTRIBUTE || WANT_VECTOR_FALLBACK
+  dst = VEC{ src[Indices]... };
+#elif defined(__clang__)
+  dst = __builtin_shufflevector(src, src, Indices...);
+#else // gcc
+  dst = __builtin_shuffle(src, vec_mask_type_t<VEC>{ Indices... });
+#endif
+}
+
+/**
+ * @brief vpermute2 function.
+ * move any element of a vector src
+ * into any or multiple position inside dst.
+ */
+template<size_t... Indices, typename VEC>
+inline void
+vpermute2(VEC& dst, const VEC& src1, const VEC& src2)
+{
+  constexpr size_t N = vec_size<VEC>();
+  static_assert(
+    (sizeof...(Indices) == N),
+    "vpermute2 number of indices different than vector size");
+  static_assert(
+    bool_pack_helper::all_true<(Indices >= 0 && Indices < 2 * N)...>::value,
+    "vpermute2 value of a mask index is outside the allowed range");
+
+#if !HAVE_VECTOR_SIZE_ATTRIBUTE || WANT_VECTOR_FALLBACK
+  VEC tmp;
+  size_t pos{0};
+  for (auto index: { Indices... }) {
+    if (index < N) {
+      tmp[pos] = src1[index];
+    } else {
+      tmp[pos] = src2[index - N];
+    }
+    ++pos;
+  }
+  dst = tmp;
+#elif defined(__clang__)
+  dst = __builtin_shufflevector(src1, src2, Indices...);
+#else // gcc
+  dst = __builtin_shuffle(src1, src2, vec_mask_type_t<VEC>{ Indices... });
+#endif
+}
+
+
+} // namespace CxxUtils
+
+#endif // not CXXUTILS_VEC_H
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/GeoSpecialShapes/EMECData.h b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/GeoSpecialShapes/EMECData.h
new file mode 100644
index 0000000000000000000000000000000000000000..4daeff027b4b9506306271516ee8655de07428f7
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/GeoSpecialShapes/EMECData.h
@@ -0,0 +1,141 @@
+/*
+  Copyright (C) 2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+//---------------------------------------------------------------
+//                           
+// Data structures which determine properties (such as geometry)
+// of the EMEC detector in ATLAS.  May be filled by the database
+//
+//----------------------------------------------------------------
+
+#ifndef EMEC_DATA_H
+#define EMEC_DATA_H
+#include <string> 
+#include <vector> 
+
+
+struct EMECGEOMETRY {
+  double  EFIELD ;
+  double  MLGAP ;
+  double  SABL ;
+  double  SBAR ;
+  double  SBRN ;
+  double  SCRB ;
+  double  Z1 ;
+  double  Z0 ;
+  double  DCF ;
+  double  RLIMIT ;
+  double  EPS ;
+  double  RHOIN ;
+  double  RCKP ;
+  double  ETOT ;
+  double  EKAP ;
+  double  ECU ;
+  double  EPREP ;
+  double  EINOX ;
+  int     NLAYER;
+  double  RMIN ;
+  double  RMAX ;
+  double  DZENDE ;
+  double  ZORIG ;
+  double  DCRACK ;
+  double  DZPLA ;
+  double  DZDSE ;
+  double  DZDSM ;
+  double  DZDSI ;
+  int     NUREG ;
+  double  DETA_0 ;
+  double  DETA_1 ;
+  double  DETA_2 ;
+  double  DETA_3 ;
+  double  DETA_4 ;
+  double  DETA_5 ;
+  double  DETA_6 ;
+  double  DETA_7 ;
+  double  DETA_8 ;
+  double  DETA_9 ;
+  double  DETA_10 ;
+  double  DPHI_0 ;
+  double  DPHI_1 ;
+  double  DPHI_2 ;
+  double  DPHI_3 ;
+  double  DPHI_4 ;
+  double  DPHI_5 ;
+  double  DPHI_6 ;
+  double  DPHI_7 ;
+  double  DPHI_8 ;
+  double  DPHI_9 ;
+  double  DPHI_10 ;
+  double  ETASTR_0 ;
+  double  ETASTR_1 ;
+  double  ETASTR_2 ;
+  double  ETASTR_3 ;
+  double  ETASTR_4 ;
+  double  ETASTR_5 ;
+  double  ETASTR_6 ;
+  double  EMHIT ;
+  double  EMDIGI ;
+  double  GAP0 ;
+  double  ZSHIFT ;
+  
+};
+
+struct EMECPARAMS {
+  std::string  PHIROTATION;
+  std::string  SAGGING;
+  std::string  INNERSLANTPARAM;
+  std::string  OUTERSLANTPARAM;
+};
+
+
+struct EMECWHEELPARAMETERS {
+  int IWHEEL;
+  int NABS;
+  int NACC;
+  double ETAINT;
+  double ETAEXT;
+  int  NWSAMP;
+};
+
+
+struct EMECMAGICNUMBERS {
+  double STRAIGHTSTARTSECTION;
+  double FOCALTOREF ;
+  double REFTOACTIVE ;
+  double ACTIVELENGTH ;
+  double REFTOPRESAMPLER;
+  double PRESAMPLERLENGTH;
+
+};
+
+struct COLDCONTRACTION {
+  double ABSORBERCONTRACTION;
+  double G10RINGCONTRACTION;
+  double MOTHERBOARDCONTRACTION;
+  double CABLECONTRACTION;
+  double COLDWINDOWCONTRACTION;
+  double ELECTRODEINVCONTRACTION;
+};
+
+struct EMECFAN {
+  double LEADTHICKNESSINNER;
+  double LEADTHICKNESSOUTER; 
+  double STEELTHICKNESS;
+  double GLUETHICKNESS;
+  double ELECTRODETOTALTHICKNESS; 
+};
+
+
+struct EMECData {
+  std::vector<EMECGEOMETRY>        emecgeometry;
+  std::vector<EMECPARAMS>          emecparams;
+  std::vector<EMECWHEELPARAMETERS> emecwheelparameters;
+  std::vector<EMECMAGICNUMBERS>    emecmagicnumbers;
+  std::vector<COLDCONTRACTION>     coldcontraction;
+  std::vector<EMECFAN>             emecfan;
+};
+
+#endif
+
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/GeoSpecialShapes/LArWheelCalculator.h b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/GeoSpecialShapes/LArWheelCalculator.h
new file mode 100644
index 0000000000000000000000000000000000000000..906057ecf49909ee1008e4787971ee58f2b2506a
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/GeoSpecialShapes/LArWheelCalculator.h
@@ -0,0 +1,247 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef GEOSPECIALSHAPES_LARWHEELCALCULATOR_H
+#define GEOSPECIALSHAPES_LARWHEELCALCULATOR_H
+
+#include <vector>
+
+// FMV and other checks
+#ifndef PORTABLE_LAR_SHAPE
+// For athena
+  #include "CxxUtils/features.h"
+#else
+// When run outside of Athena
+  #define HAVE_VECTOR_SIZE_ATTRIBUTE 1
+  #ifdef __APPLE__
+    #define CXXUTILS_FEATURES_H 1
+  #endif
+#endif
+
+#include "CLHEP/Vector/ThreeVector.h"
+#if !defined(XAOD_STANDALONE) && !defined(PORTABLE_LAR_SHAPE)
+    #include "AthenaKernel/CLASS_DEF.h"
+#endif // XAOD_STANDALONE
+
+#if HAVE_VECTOR_SIZE_ATTRIBUTE
+    #include "vec_parametrized_sincos.h"
+#endif
+#include "GeoSpecialShapes/LArWheelCalculatorEnums.h"
+
+#define LARWC_SINCOS_POLY 5
+#define LARWC_DTNF_NEW
+
+struct EMECData;
+
+//#define HARDDEBUG
+
+// Forward declarations
+namespace LArWheelCalculator_Impl {
+  class IDistanceCalculator;
+  class DistanceCalculatorSaggingOff;
+  class DistanceCalculatorSaggingOn;
+
+  class IFanCalculator;
+  class ModuleFanCalculator;
+  template <typename SaggingType> class WheelFanCalculator;
+  template <typename SaggingType> class DistanceToTheNeutralFibre_OfFan;
+}
+
+/// @class LArWheelCalculator
+/// This class separates some of the geometry details of the LAr
+/// endcap.
+/// 26-May-2009 AMS: remove all previous comments from here as obsoleted
+///
+class LArWheelCalculator
+{
+
+    friend class LArWheelCalculator_Impl::DistanceCalculatorSaggingOff;
+    friend class LArWheelCalculator_Impl::DistanceCalculatorSaggingOn;
+    friend class LArWheelCalculator_Impl::ModuleFanCalculator;
+    template <typename SaggingType> friend class LArWheelCalculator_Impl::WheelFanCalculator;
+    template <typename SaggingType> friend class LArWheelCalculator_Impl::DistanceToTheNeutralFibre_OfFan;
+
+  public:
+
+  LArWheelCalculator(const EMECData & emecData, LArG4::LArWheelCalculator_t a_wheelType, int zside = 1);
+    virtual ~LArWheelCalculator();
+
+    LArWheelCalculator (const LArWheelCalculator&) = delete;
+    LArWheelCalculator& operator= (const LArWheelCalculator&) = delete;
+
+    static const char *LArWheelCalculatorTypeString(LArG4::LArWheelCalculator_t);
+    double GetFanHalfThickness(LArG4::LArWheelCalculator_t) const;
+
+    // "Get constant" methods:
+    double GetWheelThickness() const { return m_WheelThickness; }
+    double GetdWRPtoFrontFace() const { return m_dWRPtoFrontFace; }
+    double GetStraightStartSection() const { return m_StraightStartSection; }
+    virtual LArG4::LArWheelCalculator_t type() const { return m_type; }
+    // "zShift" is the z-distance (cm) that the EM endcap is shifted
+    // (due to cabling, etc.)
+    int GetAtlasZside() const { return m_AtlasZside; }
+    double zShift() const { return m_zShift; }
+    double GetFanFoldRadius() const { return m_FanFoldRadius; }
+    double GetZeroFanPhi() const { return m_ZeroFanPhi; }
+    int GetNumberOfWaves() const { return m_NumberOfWaves; }
+    int GetNumberOfHalfWaves() const { return m_NumberOfHalfWaves; }
+    int GetNumberOfFans() const { return m_NumberOfFans; }
+
+    double GetActiveLength() const { return m_ActiveLength; }
+    double GetFanStepOnPhi() const { return m_FanStepOnPhi; }
+    double GetHalfWaveLength() const { return m_HalfWaveLength; }
+    double GetQuarterWaveLength() const { return m_QuarterWaveLength; }
+    double GetWheelRefPoint() const { return m_zWheelRefPoint; }
+    double GetFanHalfThickness() const { return m_FanHalfThickness; }
+
+    bool GetisModule() const { return m_isModule; }
+    bool GetisElectrode() const { return m_isElectrode; }
+    bool GetisInner() const { return m_isInner; }
+    bool GetisBarrette() const { return m_isBarrette; }
+    bool GetisBarretteCalib() const { return m_isBarretteCalib; }
+
+    double GetWheelInnerRadius(double *) const;
+    void GetWheelOuterRadius(double *) const;
+
+    double GetElecFocaltoWRP() const { return m_dElecFocaltoWRP; }
+    // "set constant" method:
+
+    int GetFirstFan() const { return m_FirstFan; }
+    int GetLastFan() const { return m_LastFan; }
+
+    int GetStartGapNumber() const { return m_ZeroGapNumber; }
+    void SetStartGapNumber(int n) { m_ZeroGapNumber = n; }
+
+    /// @name geometry methods
+    /// @{
+
+    /// Determines the nearest to the input point fan.
+    /// Rotates point p to the localFan coordinates and returns the
+    /// fan number to out_fan_number parameter.
+    double DistanceToTheNearestFan(CLHEP::Hep3Vector &p, int & out_fan_number) const;
+
+    /// Calculates aproximate, probably underestimate, distance to the
+    /// neutral fibre of the vertical fan. Sign of return value means
+    /// side of the fan; negative - lower phi.
+    double DistanceToTheNeutralFibre(const CLHEP::Hep3Vector &p, int fan_number) const;
+
+    CLHEP::Hep3Vector NearestPointOnNeutralFibre(const CLHEP::Hep3Vector &p,
+                                                 int fan_number) const;
+    std::vector<double> NearestPointOnNeutralFibre_asVector(const CLHEP::Hep3Vector &p,
+                                                            int fan_number) const;
+    int GetPhiGap(const CLHEP::Hep3Vector &p) const { return GetPhiGapAndSide(p).first; }
+    int PhiGapNumberForWheel(int) const;
+    std::pair<int, int> GetPhiGapAndSide(const CLHEP::Hep3Vector &p) const;
+    double AmplitudeOfSurface(const CLHEP::Hep3Vector& P, int side, int fan_number) const;
+
+    /// @}
+
+  private:
+    LArG4::LArWheelCalculator_t m_type;
+
+    int m_AtlasZside;
+    bool m_SaggingOn; // !
+    bool m_phiRotation;
+    bool m_slant_use_default;
+    double m_slant_parametrization[5]; // pol4
+    double m_sin_parametrization[7]; // up to pol6
+    double m_cos_parametrization[7];
+    std::vector<std::vector<double> > m_sagging_parameter; // !
+
+    double m_ActiveLength;
+    double m_StraightStartSection;
+    double m_dWRPtoFrontFace;
+    double m_HalfGapBetweenWheels;
+    double m_zWheelRefPoint;
+    double m_dMechFocaltoWRP;
+    double m_dElecFocaltoWRP;
+    double m_rOuterCutoff;
+    double m_eta_hi, m_eta_mid, m_eta_low;
+    double m_zShift;
+
+    double m_WheelThickness;
+    double m_HalfWheelThickness;
+    double m_zWheelFrontFace, m_zWheelBackFace;
+
+    double m_QuarterWaveLength;
+    double m_HalfWaveLength;
+    double m_FanFoldRadius;
+    double m_ZeroFanPhi;
+    double m_ZeroFanPhi_ForDetNeaFan;
+    double m_FanStepOnPhi;
+    int m_NumberOfWaves;
+    int m_NumberOfHalfWaves;
+    int m_NumberOfFans;
+    //int m_HalfNumberOfFans; removed because unused. DM 2015-07-30
+    double m_FanHalfThickness;
+    int m_ZeroGapNumber;
+    int m_FirstFan;
+    int m_LastFan;
+
+    bool m_isModule;
+    bool m_isElectrode;
+    bool m_isInner;
+    bool m_isBarrette;
+    bool m_isBarretteCalib;
+
+
+    double m_leadThicknessInner;
+    double m_leadThicknessOuter;
+    double m_steelThickness;
+    double m_glueThickness;
+    double m_electrodeTotalThickness;
+    double m_coldContraction;
+    double m_electrodeInvContraction;
+
+
+    // int m_fan_number; // break thread-safety -> removed DM 2015-07-30
+
+    void outer_wheel_init(const EMECData &);
+    void inner_wheel_init(const EMECData &);
+    void module_init();
+
+  public:
+
+    /*void set_m_fan_number(const int &fan_number)
+    {
+      m_fan_number = fan_number;
+      if(m_fan_number < 0) m_fan_number += m_NumberOfFans;
+      m_fan_number += m_ZeroGapNumber;
+      if(m_fan_number >= m_NumberOfFans) m_fan_number -= m_NumberOfFans;
+    }*/
+    int adjust_fan_number(int fan_number) const {
+      int res_fan_number = fan_number;
+      if(res_fan_number < 0) res_fan_number += m_NumberOfFans;
+      res_fan_number += m_ZeroGapNumber;
+      if(res_fan_number >= m_NumberOfFans) res_fan_number -= m_NumberOfFans;
+      return res_fan_number;
+    }
+
+    /// Calculates wave slant angle using parametrization for current wheel
+    /// for given distance from calorimeter axis
+    double parameterized_slant_angle(double) const;
+
+  private:
+
+    void parameterized_sincos(const double, double &, double &) const;
+    void parameterized_sin(const double, double &, double &) const;
+
+  private:
+
+    LArWheelCalculator_Impl::IDistanceCalculator *m_distanceCalcImpl;
+    LArWheelCalculator_Impl::IFanCalculator *m_fanCalcImpl;
+    void fill_sincos_parameterization();
+#if HAVE_VECTOR_SIZE_ATTRIBUTE
+    vsincos_par m_vsincos_par{};
+#endif
+
+};
+
+#if !defined(XAOD_STANDALONE) && !defined(PORTABLE_LAR_SHAPE)
+    //using the macro below we can assign an identifier (and a version)
+    //This is required and checked at compile time when you try to record/retrieve
+    CLASS_DEF(LArWheelCalculator , 900345678 , 1)
+#endif // XAOD_STANDALONE
+
+#endif // GEOSPECIALSHAPES_LARWHEELCALCULATOR_H
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/GeoSpecialShapes/LArWheelCalculatorEnums.h b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/GeoSpecialShapes/LArWheelCalculatorEnums.h
new file mode 100644
index 0000000000000000000000000000000000000000..dcea707a7ec26374f55280cbec8a6dc8fa7d546f
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/GeoSpecialShapes/LArWheelCalculatorEnums.h
@@ -0,0 +1,25 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef GEOSPECIALSHAPES_LARWHEELCALCULATORENUMS_H
+#define GEOSPECIALSHAPES_LARWHEELCALCULATORENUMS_H
+
+namespace LArG4 {
+
+  enum LArWheelCalculator_t {
+    InnerAbsorberWheel,  OuterAbsorberWheel,
+    InnerElectrodWheel,  OuterElectrodWheel,
+    InnerAbsorberModule, OuterAbsorberModule,
+    InnerElectrodModule, OuterElectrodModule,
+    BackInnerBarretteWheel,       BackOuterBarretteWheel,
+    BackInnerBarretteWheelCalib,  BackOuterBarretteWheelCalib,
+    BackInnerBarretteModule,      BackOuterBarretteModule,
+    BackInnerBarretteModuleCalib, BackOuterBarretteModuleCalib,
+    InnerGlueWheel, OuterGlueWheel,
+    InnerLeadWheel, OuterLeadWheel
+  };
+
+  struct ROOT6_NamespaceAutoloadHook_WheelCalc{};
+}
+#endif
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/GeoSpecialShapes/toEMECData.h b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/GeoSpecialShapes/toEMECData.h
new file mode 100644
index 0000000000000000000000000000000000000000..f149078ad9da6673899d1cb831026e3c557c5d58
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/GeoSpecialShapes/toEMECData.h
@@ -0,0 +1,166 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+#ifndef _TO_EMEC_DATA_H
+#define _TO_EMEC_DATA_H
+#include "GeoSpecialShapes/EMECData.h"
+class IRDBAccessSvc;
+class DecodeVersionKey;
+#include <iostream> 
+// This code is inline in order to be inoffensive in environments 
+// that do not link to the full ATLAS software. Link dependencies
+// then only arise when the routine is actually needed. 
+
+inline EMECData toEMECData(IRDBAccessSvc *rdbAccess, const DecodeVersionKey  &larVersionKey) {
+
+  EMECData data;
+
+  // Little lambda function here: retreive recordset and fall back to default version if empty:
+  auto getRecordsetPtr=[rdbAccess] (const std::string& node, const std::string& tag, const std::string& tag2node,const std::string & fallbackTag2Node) {
+
+    // try with specified tag/node
+    IRDBRecordset_ptr ptr=rdbAccess->getRecordsetPtr(node,tag, tag2node);
+    // if failure, try with fallback tag/node
+    if (ptr->size()==0) {
+      ptr=rdbAccess->getRecordsetPtr(node,fallbackTag2Node);
+    }
+    return ptr;
+  };
+
+
+  IRDBRecordset_ptr emecGeometry        = getRecordsetPtr("EmecGeometry",        larVersionKey.tag(),larVersionKey.node(),"EmecGeometry-00");
+  IRDBRecordset_ptr emecMagicNumbers    = getRecordsetPtr("EmecMagicNumbers",    larVersionKey.tag(), larVersionKey.node(),"EmecMagicNumbers-00");
+  IRDBRecordset_ptr emecParams          = getRecordsetPtr("EmecParams",          larVersionKey.tag(), larVersionKey.node(),"EMECParams-00");
+  IRDBRecordset_ptr emecWheelParameters = getRecordsetPtr("EmecWheelParameters", larVersionKey.tag(), larVersionKey.node(),"EmecWheelParameters-00");
+  IRDBRecordset_ptr emecFan             = getRecordsetPtr("EmecFan",             larVersionKey.tag(), larVersionKey.node(),"EmecFan-00");
+  IRDBRecordset_ptr coldContraction     = getRecordsetPtr("ColdContraction",     larVersionKey.tag(), larVersionKey.node(),"ColdContraction-00");
+  
+
+  for (size_t i=0;i<emecParams->size();i++) {
+    EMECPARAMS emecparams;
+    emecparams.PHIROTATION=(*emecParams)[i]->getString("PHIROTATION");
+    emecparams.SAGGING=(*emecParams)[i]->getString("SAGGING");
+    emecparams.INNERSLANTPARAM=(*emecParams)[i]->getString("INNERSLANTPARAM");
+    emecparams.OUTERSLANTPARAM=(*emecParams)[i]->getString("OUTERSLANTPARAM");
+    data.emecparams.push_back(emecparams);
+  }
+
+
+  for  (size_t i=0;i<emecWheelParameters->size();i++) {
+    EMECWHEELPARAMETERS emecwheelparameters;
+    emecwheelparameters.IWHEEL=(*emecWheelParameters)[i]->getInt("IWHEEL");
+    emecwheelparameters.NABS=(*emecWheelParameters)[i]->getInt("NABS");
+    emecwheelparameters.NACC=(*emecWheelParameters)[i]->getInt("NACC");
+    emecwheelparameters.ETAINT=(*emecWheelParameters)[i]->getDouble("ETAINT");
+    emecwheelparameters.ETAEXT=(*emecWheelParameters)[i]->getDouble("ETAEXT");
+    emecwheelparameters.NWSAMP=(*emecWheelParameters)[i]->getInt("NWSAMP");
+    data.emecwheelparameters.push_back(emecwheelparameters);
+  }
+
+  for (size_t i=0;i<emecMagicNumbers->size();i++) {
+    EMECMAGICNUMBERS emecmagicnumbers;
+    emecmagicnumbers.STRAIGHTSTARTSECTION=(*emecMagicNumbers)[i]->getDouble("STRAIGHTSTARTSECTION");
+    emecmagicnumbers.FOCALTOREF=(*emecMagicNumbers)[i]->getDouble("FOCALTOREF");
+    emecmagicnumbers.REFTOACTIVE=(*emecMagicNumbers)[i]->getDouble("REFTOACTIVE");
+    emecmagicnumbers.ACTIVELENGTH=(*emecMagicNumbers)[i]->getDouble("ACTIVELENGTH");
+    emecmagicnumbers.REFTOPRESAMPLER=(*emecMagicNumbers)[i]->getDouble("REFTOPRESAMPLER");
+    emecmagicnumbers.PRESAMPLERLENGTH=(*emecMagicNumbers)[i]->getDouble("PRESAMPLERLENGTH");
+    data.emecmagicnumbers.push_back(emecmagicnumbers);
+  }
+
+  for (size_t i=0;i<emecFan->size();i++) {
+    EMECFAN emecfan;
+    emecfan.LEADTHICKNESSINNER=(*emecFan)[i]->getDouble("LEADTHICKNESSINNER");
+    emecfan.LEADTHICKNESSOUTER=(*emecFan)[i]->getDouble("LEADTHICKNESSOUTER");
+    emecfan.STEELTHICKNESS=(*emecFan)[i]->getDouble("STEELTHICKNESS");
+    emecfan.GLUETHICKNESS=(*emecFan)[i]->getDouble("GLUETHICKNESS");
+    emecfan.ELECTRODETOTALTHICKNESS=(*emecFan)[i]->getDouble("ELECTRODETOTALTHICKNESS");
+    data.emecfan.push_back(emecfan);
+  }
+
+
+  for (size_t i=0;i<coldContraction->size();i++) {
+    COLDCONTRACTION coldcontraction;
+    coldcontraction.ABSORBERCONTRACTION=(*coldContraction)[i]->getDouble("ABSORBERCONTRACTION");
+    coldcontraction.G10RINGCONTRACTION=(*coldContraction)[i]->getDouble("G10RINGCONTRACTION");
+    coldcontraction.MOTHERBOARDCONTRACTION=(*coldContraction)[i]->getDouble("MOTHERBOARDCONTRACTION");
+    coldcontraction.CABLECONTRACTION=(*coldContraction)[i]->getDouble("CABLECONTRACTION");
+    coldcontraction.COLDWINDOWCONTRACTION=(*coldContraction)[i]->getDouble("COLDWINDOWCONTRACTION");
+    coldcontraction.ELECTRODEINVCONTRACTION=(*coldContraction)[i]->getDouble("ELECTRODEINVCONTRACTION");
+    data.coldcontraction.push_back(coldcontraction);
+  }
+
+
+  for (size_t i=0;i<emecGeometry->size();i++) {
+    EMECGEOMETRY emecgeometry;
+    emecgeometry.EFIELD=(*emecGeometry)[i]->getDouble("EFIELD");
+    emecgeometry.MLGAP=(*emecGeometry)[i]->getDouble("MLGAP");
+    emecgeometry.SABL=(*emecGeometry)[i]->getDouble("SABL");
+    emecgeometry.SBAR=(*emecGeometry)[i]->getDouble("SBAR");
+    emecgeometry.SBRN=(*emecGeometry)[i]->getDouble("SBRN");
+    emecgeometry.SCRB=(*emecGeometry)[i]->getDouble("SCRB");
+    emecgeometry.Z1=(*emecGeometry)[i]->getDouble("Z1");
+    emecgeometry.Z0=(*emecGeometry)[i]->getDouble("Z0");
+    emecgeometry.DCF=(*emecGeometry)[i]->getDouble("DCF");
+    emecgeometry.RLIMIT=(*emecGeometry)[i]->getDouble("RLIMIT");
+    emecgeometry.EPS=(*emecGeometry)[i]->getDouble("EPS");
+    emecgeometry.RHOIN=(*emecGeometry)[i]->getDouble("RHOIN");
+    emecgeometry.RCKP=(*emecGeometry)[i]->getDouble("RCKP");
+    emecgeometry.ETOT=(*emecGeometry)[i]->getDouble("ETOT");
+    emecgeometry.EKAP=(*emecGeometry)[i]->getDouble("EKAP");
+    emecgeometry.ECU=(*emecGeometry)[i]->getDouble("ECU");
+    emecgeometry.EPREP=(*emecGeometry)[i]->getDouble("EPREP");
+    emecgeometry.EINOX=(*emecGeometry)[i]->getDouble("EINOX");
+    emecgeometry.NLAYER=(*emecGeometry)[i]->getInt("NLAYER");
+    emecgeometry.RMIN=(*emecGeometry)[i]->getDouble("RMIN");
+    emecgeometry.RMAX=(*emecGeometry)[i]->getDouble("RMAX");
+    emecgeometry.DZENDE=(*emecGeometry)[i]->getDouble("DZENDE");
+    emecgeometry.ZORIG=(*emecGeometry)[i]->getDouble("ZORIG");
+    emecgeometry.DCRACK=(*emecGeometry)[i]->getDouble("DCRACK");
+    emecgeometry.DZPLA=(*emecGeometry)[i]->getDouble("DZPLA");
+    emecgeometry.DZDSE=(*emecGeometry)[i]->getDouble("DZDSE");
+    emecgeometry.DZDSM=(*emecGeometry)[i]->getDouble("DZDSM");
+    emecgeometry.DZDSI=(*emecGeometry)[i]->getDouble("DZDSI");
+    emecgeometry.NUREG=(*emecGeometry)[i]->getInt("NUREG");
+    emecgeometry.DETA_0=(*emecGeometry)[i]->getDouble("DETA_0");
+    emecgeometry.DETA_1=(*emecGeometry)[i]->getDouble("DETA_1");
+    emecgeometry.DETA_2=(*emecGeometry)[i]->getDouble("DETA_2");
+    emecgeometry.DETA_3=(*emecGeometry)[i]->getDouble("DETA_3");
+    emecgeometry.DETA_4=(*emecGeometry)[i]->getDouble("DETA_4");
+    emecgeometry.DETA_5=(*emecGeometry)[i]->getDouble("DETA_5");
+    emecgeometry.DETA_6=(*emecGeometry)[i]->getDouble("DETA_6");
+    emecgeometry.DETA_7=(*emecGeometry)[i]->getDouble("DETA_7");
+    emecgeometry.DETA_8=(*emecGeometry)[i]->getDouble("DETA_8");
+    emecgeometry.DETA_9=(*emecGeometry)[i]->getDouble("DETA_9");
+    emecgeometry.DETA_10=(*emecGeometry)[i]->getDouble("DETA_10");
+    emecgeometry.DPHI_0=(*emecGeometry)[i]->getDouble("DPHI_0");
+    emecgeometry.DPHI_1=(*emecGeometry)[i]->getDouble("DPHI_1");
+    emecgeometry.DPHI_2=(*emecGeometry)[i]->getDouble("DPHI_2");
+    emecgeometry.DPHI_3=(*emecGeometry)[i]->getDouble("DPHI_3");
+    emecgeometry.DPHI_4=(*emecGeometry)[i]->getDouble("DPHI_4");
+    emecgeometry.DPHI_5=(*emecGeometry)[i]->getDouble("DPHI_5");
+    emecgeometry.DPHI_6=(*emecGeometry)[i]->getDouble("DPHI_6");
+    emecgeometry.DPHI_7=(*emecGeometry)[i]->getDouble("DPHI_7");
+    emecgeometry.DPHI_8=(*emecGeometry)[i]->getDouble("DPHI_8");
+    emecgeometry.DPHI_9=(*emecGeometry)[i]->getDouble("DPHI_9");
+    emecgeometry.DPHI_10=(*emecGeometry)[i]->getDouble("DPHI_10");
+    emecgeometry.ETASTR_0=(*emecGeometry)[i]->getDouble("ETASTR_0");
+    emecgeometry.ETASTR_1=(*emecGeometry)[i]->getDouble("ETASTR_1");
+    emecgeometry.ETASTR_2=(*emecGeometry)[i]->getDouble("ETASTR_2");
+    emecgeometry.ETASTR_3=(*emecGeometry)[i]->getDouble("ETASTR_3");
+    emecgeometry.ETASTR_4=(*emecGeometry)[i]->getDouble("ETASTR_4");
+    emecgeometry.ETASTR_5=(*emecGeometry)[i]->getDouble("ETASTR_5");
+    emecgeometry.ETASTR_6=(*emecGeometry)[i]->getDouble("ETASTR_6");
+    emecgeometry.EMHIT=(*emecGeometry)[i]->getDouble("EMHIT");
+    emecgeometry.EMDIGI=(*emecGeometry)[i]->getDouble("EMDIGI");
+    emecgeometry.GAP0=(*emecGeometry)[i]->getDouble("GAP0");
+    emecgeometry.ZSHIFT=(*emecGeometry)[i]->getDouble("ZSHIFT");
+    data.emecgeometry.push_back(emecgeometry);
+  }
+
+
+  return data;
+}
+
+
+#endif
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/GeoSpecialShapes/vec_parametrized_sincos.h b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/GeoSpecialShapes/vec_parametrized_sincos.h
new file mode 100644
index 0000000000000000000000000000000000000000..f662fbf1799365a40b320832b6b27166e89259b8
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/GeoSpecialShapes/vec_parametrized_sincos.h
@@ -0,0 +1,75 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+  * @brief vectorized version of parametrized sincos
+  * see ATLASSIM-4753 for details
+  * @author Miha Muskinja, Chistos Anastopoulos
+*/
+#ifndef VEC_PARAMETRIZED_SINCOS_H
+#define VEC_PARAMETRIZED_SINCOS_H
+
+#include "CxxUtils/restrict.h"
+#include "CxxUtils/vec.h"
+struct vsincos_par
+{
+
+    CxxUtils::vec<double, 4> param_0 = {};
+    CxxUtils::vec<double, 4> param_1 = {};
+    CxxUtils::vec<double, 4> param_2 = {};
+
+#if HAVE_FUNCTION_MULTIVERSIONING
+#if defined(__x86_64__)
+
+    __attribute__((target("avx2,fma")))
+
+    void
+    evaluate(const double r,
+             double &ATH_RESTRICT sin_a,
+             double &ATH_RESTRICT cos_a) const ATH_RESTRICT
+    {
+        const double r2 = r * r;
+        CxxUtils::vec<double, 4> P = r2 * param_0 + param_1;
+        P = r2 * P + param_2;
+        CxxUtils::vec<double, 4> P2 = {P[1], P[0], P[3], P[2]};
+        CxxUtils::vec<double, 4> res = r * P2 + P;
+        sin_a = res[0];
+        cos_a = res[2];
+    }
+
+    __attribute__((target("avx2")))
+
+    void
+    eval(const double r,
+         double &ATH_RESTRICT sin_a,
+         double &ATH_RESTRICT cos_a) const ATH_RESTRICT
+    {
+        const double r2 = r * r;
+        CxxUtils::vec<double, 4> P = r2 * param_0 + param_1;
+        P = r2 * P + param_2;
+        CxxUtils::vec<double, 4> P2 = {P[1], P[0], P[3], P[2]};
+        CxxUtils::vec<double, 4> res = r * P2 + P;
+        sin_a = res[0];
+        cos_a = res[2];
+    }
+
+    __attribute__((target("default")))
+
+#endif // x86
+#endif // FMV
+
+    void
+    eval(const double r,
+         double &ATH_RESTRICT sin_a,
+         double &ATH_RESTRICT cos_a) const ATH_RESTRICT
+    {
+        const double r2 = r * r;
+        CxxUtils::vec<double, 4> P = r2 * param_0 + param_1;
+        P = r2 * P + param_2;
+        sin_a = r * P[1] + P[0];
+        cos_a = r * P[3] + P[2];
+    }
+};
+
+#endif
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/PortableMsgStream/PortableMsgStream.h b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/PortableMsgStream/PortableMsgStream.h
new file mode 100644
index 0000000000000000000000000000000000000000..9017f810a6b0718babd9bc1f7c93ffc47d8f6e95
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/PortableMsgStream/PortableMsgStream.h
@@ -0,0 +1,106 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+// JFB Feb 2022
+// This is an ersatz message stream class. It directs messages to cout.
+// This is not used within Athena. It is for portability (e.g. to FullSimLight).
+// 
+
+#ifndef __LArWheelCalculator_Impl_PortableMsgStream_H__
+#define __LArWheelCalculator_Impl_PortableMsgStream_H__
+#ifdef PORTABLE_LAR_SHAPE
+#include <iostream>
+#include <sstream>
+#include <string>
+
+
+namespace MSG {
+  enum Level {VERBOSE, DEBUG, INFO, WARNING, ERROR, FATAL};
+}
+
+class PortableMsgStream;
+PortableMsgStream& endmsg( PortableMsgStream& s ); 
+
+
+class PortableMsgStream {
+public:
+
+  PortableMsgStream(const std::string  label="",MSG::Level threshold=MSG::INFO,std::ostream & stream=std::cout) :
+    m_ostream(stream),
+    m_label(label),
+    m_threshold(threshold)
+  {
+  }
+
+  template<typename T> PortableMsgStream & operator << (const T & t) {
+    if (m_level>=m_threshold) m_sstream << t;
+    return *this;
+  }
+
+  PortableMsgStream& operator<<( PortableMsgStream& ( *t )(PortableMsgStream&)) {
+    if (t==endmsg) {
+      if (m_level>=m_threshold) {
+	return doOutput();
+      }
+      else {
+	m_ostream.clear();
+	return *this;
+      }
+    }
+    return *this;
+  }
+  
+  PortableMsgStream& operator << (MSG::Level level) {
+    m_level=level;
+    return *this;
+  }
+
+  PortableMsgStream & doOutput() {
+    
+    switch (m_level) {
+    case MSG::VERBOSE:
+      m_ostream << "VERBOSE: ";
+      break;
+    case MSG::DEBUG:
+      m_ostream << "DEBUG  : ";
+      break;
+    case MSG::INFO:
+      m_ostream << "INFO   : ";
+      break;
+    case MSG::WARNING:
+      m_ostream << "WARNING: ";
+      break;
+    case MSG::ERROR:
+      m_ostream << "ERROR  : ";
+      break;
+    case MSG::FATAL:
+      m_ostream << "FATAL  : ";
+      break;
+    };
+    m_ostream << m_label;
+    m_ostream << m_sstream.str() << std::endl;
+    m_sstream.str("");
+    m_sstream.clear();
+    return *this;
+  }
+
+  
+private:
+
+  PortableMsgStream (const PortableMsgStream &) = delete;
+  PortableMsgStream & operator=(const PortableMsgStream &) = delete;
+  
+  std::ostringstream   m_sstream;
+  std::ostream       & m_ostream;
+  const std::string    m_label;
+  MSG::Level           m_level;
+  MSG::Level           m_threshold;
+};
+
+inline PortableMsgStream& endmsg( PortableMsgStream& s ) { return s.doOutput(); }
+
+
+#endif //LAR_PORTABLE_SHAPE
+#endif  // __LArWheelCalculator_Impl_PortableMsgStream_H__
+
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator.cxx b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..41a4cdb44d01bb17150497cb3e7891288d45dfd5
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator.cxx
@@ -0,0 +1,534 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+// LArWheelCalculator 19-Apr-2001 Bill Seligman
+// 26-May-2009 AMS: remove all previous comments from here as obsoleted
+
+#include <cmath>
+#include <climits>
+#include <cassert>
+#ifndef PORTABLE_LAR_SHAPE
+#include "GaudiKernel/Bootstrap.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/MsgStream.h"
+#else
+#include "PortableMsgStream/PortableMsgStream.h"
+#endif
+
+#include "GeoSpecialShapes/LArWheelCalculator.h"
+#include "GeoSpecialShapes/EMECData.h"
+
+#include "./LArWheelCalculator_Impl/DistanceCalculatorFactory.h"
+#include "./LArWheelCalculator_Impl/FanCalculatorFactory.h"
+
+
+#include "GeoModelKernel/Units.h"
+
+using namespace GeoModelKernelUnits;
+
+
+// these numbers are taken from DB in constructor,
+// hardcoded values here are just for reference
+/*
+  double LArWheelCalculator::zWheelRefPoint       = 3689.5*mm;  //=endg_z0
+  double LArWheelCalculator::dMechFocaltoWRP      = 3691. *mm;  //=endg_z1
+  double LArWheelCalculator::dElecFocaltoWRP      = 3689. *mm;  //=endg_dcf
+  double LArWheelCalculator::rOuterCutoff                 = 2034. *mm;  //=endg_rlimit
+  double LArWheelCalculator::HalfGapBetweenWheels = 0.15*cm;  // In DB EMECGEOMETRY.DCRACK
+  float LArWheelCalculator::m_zShift = 4. *cm; // endg_zshift
+  double LArWheelCalculator::eta_hi  = 3.2; // from EmecWheelParameters
+  double LArWheelCalculator::eta_mid = 2.5;
+  double LArWheelCalculator::eta_low = 1.375;
+*/
+
+// these values are taken from "EMECParams" DB
+/*
+  bool LArWheelCalculator::SaggingOn = false;
+  bool LArWheelCalculator::phiRotation = false;
+*/
+
+// these were internal constants, now everything in DB
+//const double LArWheelCalculator::s_dWRPtoFrontFace = 11.*mm;
+//const double LArWheelCalculator::s_WheelThickness = 514.*mm;
+// 2x2mm are to be substracted from value of wheel thickness
+// - for straight 2-mm domains at start and finish of absorber
+//const double LArWheelCalculator::s_StraightStartSection = 2.*mm;
+//const double LArWheelCalculator::s_HalfWheelThickness = s_WheelThickness * 0.5;
+//const double LArWheelCalculator::s_WheelThickness_wo_2sss = s_WheelThickness - 2.*s_StraightStartSection;
+
+static const double default_slant_parametrization[2][5] = {
+  { -50.069, 0.50073, -0.10127E-02, 0.10390E-05, -0.42176E-09 }, // inner
+  { -34.254, 0.15528, -0.11670E-03, 0.45018E-07, -0.68473E-11 }  //outer
+};
+
+const char *LArWheelCalculator::LArWheelCalculatorTypeString(LArG4::LArWheelCalculator_t type)
+{
+  switch(type){
+     case LArG4::InnerAbsorberWheel:           return("InnerAbsorberWheel");
+     case LArG4::OuterAbsorberWheel:           return("OuterAbsorberWheel");
+     case LArG4::InnerElectrodWheel:           return("InnerElectrodWheel");
+     case LArG4::OuterElectrodWheel:           return("OuterElectrodWheel");
+     case LArG4::InnerAbsorberModule:          return("InnerAbsorberModule");
+     case LArG4::OuterAbsorberModule:          return("OuterAbsorberModule");
+     case LArG4::InnerElectrodModule:          return("InnerElectrodModule");
+     case LArG4::OuterElectrodModule:          return("OuterElectrodModule");
+     case LArG4::BackOuterBarretteWheel:       return("BackOuterBarretteWheel");
+     case LArG4::BackInnerBarretteWheel:       return("BackInnerBarretteWheel");
+     case LArG4::BackOuterBarretteModule:      return("BackOuterBarretteModule");
+     case LArG4::BackInnerBarretteModule:      return("BackInnerBarretteModule");
+     case LArG4::BackOuterBarretteWheelCalib:  return("BackOuterBarretteWheelCalib");
+     case LArG4::BackInnerBarretteWheelCalib:  return("BackInnerBarretteWheelCalib");
+     case LArG4::BackOuterBarretteModuleCalib: return("BackOuterBarretteModuleCalib");
+     case LArG4::BackInnerBarretteModuleCalib: return("BackInnerBarretteModuleCalib");
+     case LArG4::InnerGlueWheel:               return("InnerGlueWheel");
+     case LArG4::InnerLeadWheel:               return("InnerLeadWheel");
+     case LArG4::OuterGlueWheel:               return("OuterGlueWheel");
+     case LArG4::OuterLeadWheel:               return("OuterLeadWheel");
+  }
+  return("unknown");
+}
+
+LArWheelCalculator::~LArWheelCalculator() {
+  delete m_distanceCalcImpl;
+  m_distanceCalcImpl = 0;
+  delete m_fanCalcImpl;
+  m_fanCalcImpl = 0;
+}
+
+LArWheelCalculator::LArWheelCalculator(const EMECData & emecData, LArG4::LArWheelCalculator_t a_wheelType, int zside) :
+  m_type(a_wheelType),
+  m_AtlasZside(zside),
+  m_distanceCalcImpl(0),
+  m_fanCalcImpl(0)
+{
+
+#ifndef PORTABLE_LAR_SHAPE
+  // Get pointer to the message service
+  ISvcLocator* svcLocator = Gaudi::svcLocator();
+  IMessageSvc* msgSvc;
+  StatusCode status = svcLocator->service("MessageSvc", msgSvc);
+  if(status.isFailure()){
+    throw std::runtime_error("LArWheelCalculator constructor: cannot initialze message service");
+  }
+  MsgStream msg(msgSvc, "LArWheelCalculator");
+#else
+  PortableMsgStream msg("LArWheelCalculator");
+#endif
+  msg << MSG::VERBOSE << "LArWheelCalculator constructor at " << this
+      << " (type " << LArWheelCalculatorTypeString(m_type)
+      << "):" << endmsg;
+
+
+  msg << MSG::VERBOSE << "LArWheelCalculator constructor at " << this
+      << " (type " << LArWheelCalculatorTypeString(m_type)
+      << "):" << endmsg;
+
+#ifdef LARWC_DTNF_NEW
+  msg << MSG::VERBOSE << "compiled with new DTNF" << endmsg;
+#endif
+
+  // Access source of detector parameters.
+  msg << MSG::VERBOSE
+      << "initializing data members from DB..." << endmsg;
+
+  m_zWheelRefPoint       =emecData.emecgeometry[0].Z0*cm;
+  m_dMechFocaltoWRP      =emecData.emecgeometry[0].Z1*cm;
+  m_dElecFocaltoWRP      =emecData.emecgeometry[0].DCF*cm;
+  m_HalfGapBetweenWheels =emecData.emecgeometry[0].DCRACK*cm;
+  m_rOuterCutoff         =emecData.emecgeometry[0].RLIMIT*cm;
+  m_zShift               =emecData.emecgeometry[0].ZSHIFT*cm;
+  
+  
+  m_eta_hi  =emecData.emecwheelparameters[0].ETAINT;  
+  m_eta_mid =emecData.emecwheelparameters[0].ETAEXT;  
+  m_eta_low =emecData.emecwheelparameters[1].ETAEXT;  
+
+
+
+  m_leadThicknessInner=emecData.emecfan[0].LEADTHICKNESSINNER*mm; 
+  m_leadThicknessOuter=emecData.emecfan[0].LEADTHICKNESSOUTER*mm;
+  m_steelThickness=emecData.emecfan[0].STEELTHICKNESS*mm;
+  m_glueThickness=emecData.emecfan[0].GLUETHICKNESS*mm;
+  m_electrodeTotalThickness=emecData.emecfan[0].ELECTRODETOTALTHICKNESS*mm;
+  m_coldContraction=emecData.coldcontraction[0].ABSORBERCONTRACTION;
+  m_electrodeInvContraction=emecData.coldcontraction[0].ELECTRODEINVCONTRACTION;
+
+
+    
+  m_ActiveLength         =emecData.emecmagicnumbers[0].ACTIVELENGTH*mm;
+  m_StraightStartSection =emecData.emecmagicnumbers[0].STRAIGHTSTARTSECTION*mm;
+  m_dWRPtoFrontFace      =emecData.emecmagicnumbers[0].REFTOACTIVE*mm;
+
+
+  m_WheelThickness = m_ActiveLength + 2.*m_StraightStartSection;
+  m_HalfWheelThickness = m_WheelThickness * 0.5;
+
+  std::string pr_opt_value=emecData.emecparams[0].PHIROTATION;
+  std::string sagging_opt_value=emecData.emecparams[0].SAGGING;
+
+  m_phiRotation =  pr_opt_value == "g3"? true: false;
+
+  m_zWheelFrontFace = m_dMechFocaltoWRP + m_dWRPtoFrontFace;
+  m_zWheelBackFace = m_zWheelFrontFace + m_WheelThickness;
+
+  msg << MSG::DEBUG << "... got these values:" << endmsg 
+      << "m_zWheelRefPoint       : " << m_zWheelRefPoint / cm << " [cm]" << endmsg 
+      << "m_dMechFocaltoWRP      : " << m_dMechFocaltoWRP / cm << " [cm]" << endmsg 
+      << "m_dElecFocaltoWRP      : " << m_dElecFocaltoWRP / cm << " [cm]" << endmsg 
+      << "m_HalfGapBetweenWheels : " << m_HalfGapBetweenWheels / cm << " [cm]" << endmsg
+      << "m_rOuterCutoff         : " << m_rOuterCutoff / cm << " [cm]" << endmsg
+      << "m_zWheelFrontFace      : " << m_zWheelFrontFace / cm << " [cm]" << endmsg
+      << "m_zWheelBackFace       : " << m_zWheelBackFace / cm << " [cm]" << endmsg
+      << "m_zShift               : " << m_zShift / cm << " [cm]" << endmsg
+      << "Phi rotation           : " << (m_phiRotation? "true": "false") << "" << endmsg
+      << "eta wheels limits      : " << m_eta_low << ", " << m_eta_mid << ", " << m_eta_hi
+      << endmsg;
+  msg << MSG::VERBOSE << "hardcoded constants: " << endmsg 
+      << "m_WheelThickness       : " << m_WheelThickness / cm << " [cm]" << endmsg 
+      << "m_dWRPtoFrontFace      : " << m_dWRPtoFrontFace / cm << " [cm]"
+      << endmsg;
+
+
+  // Constructor initializes the geometry.
+
+  m_isBarrette      = false;
+  m_isBarretteCalib = false;
+  m_isModule        = false;
+  m_isElectrode     = false;
+  m_isInner         = false;
+  m_FirstFan        = 0;
+  m_LastFan         = 0;
+
+  switch(m_type){
+    case LArG4::BackInnerBarretteWheelCalib:
+      m_isBarretteCalib = true;
+      /* FALLTHROUGH */
+    case LArG4::BackInnerBarretteWheel:
+      m_isBarrette = true;
+      m_type = LArG4::InnerAbsorberWheel;
+      /* FALLTHROUGH */
+    case LArG4::InnerAbsorberWheel:
+    case LArG4::InnerGlueWheel:
+    case LArG4::InnerLeadWheel:
+      inner_wheel_init(emecData);
+      m_ZeroFanPhi = m_FanStepOnPhi * 0.5;
+      if(m_phiRotation) m_ZeroFanPhi += m_FanStepOnPhi * 0.5;
+      break;
+    case LArG4::BackOuterBarretteWheelCalib:
+      m_isBarretteCalib = true;
+      /* FALLTHROUGH */
+    case LArG4::BackOuterBarretteWheel:
+      m_isBarrette = true;
+      m_type = LArG4::OuterAbsorberWheel;
+      /* FALLTHROUGH */
+    case LArG4::OuterAbsorberWheel:
+    case LArG4::OuterGlueWheel:
+    case LArG4::OuterLeadWheel:
+      outer_wheel_init(emecData);
+      m_ZeroFanPhi = m_FanStepOnPhi * 0.5;
+      if(m_phiRotation) m_ZeroFanPhi += m_FanStepOnPhi * 0.5;
+      break;
+    case LArG4::InnerElectrodWheel:
+      inner_wheel_init(emecData);
+      m_ZeroFanPhi = 0;
+      if(m_phiRotation) m_ZeroFanPhi += m_FanStepOnPhi * 0.5;
+      m_isElectrode = true;
+      break;
+    case LArG4::OuterElectrodWheel:
+      outer_wheel_init(emecData);
+      m_ZeroFanPhi = 0;
+      if(m_phiRotation) m_ZeroFanPhi += m_FanStepOnPhi * 0.5;
+      m_isElectrode = true;
+      break;
+    case LArG4::BackInnerBarretteModuleCalib:
+      m_isBarretteCalib = true;
+      /* FALLTHROUGH */
+    case LArG4::BackInnerBarretteModule:
+      m_isBarrette = true;
+      m_type = LArG4::InnerAbsorberModule;
+      /* FALLTHROUGH */
+    case LArG4::InnerAbsorberModule:
+      inner_wheel_init(emecData);
+      module_init();
+      m_ZeroFanPhi += m_FanStepOnPhi * 0.5;
+      // later for all? m_ZeroFanPhi_ForDetNeaFan = m_ZeroFanPhi - m_FanStepOnPhi * 0.5;
+      break;
+    case LArG4::BackOuterBarretteModuleCalib:
+      m_isBarretteCalib = true;
+      /* FALLTHROUGH */
+    case LArG4::BackOuterBarretteModule:
+      m_isBarrette = true;
+      m_type = LArG4::OuterAbsorberModule;
+      /* FALLTHROUGH */
+    case LArG4::OuterAbsorberModule:
+      outer_wheel_init(emecData);
+      module_init();
+      m_ZeroFanPhi += m_FanStepOnPhi * 0.5;
+      // later for all? m_ZeroFanPhi_ForDetNeaFan = m_ZeroFanPhi - m_FanStepOnPhi * 0.5;
+      break;
+    case LArG4::InnerElectrodModule:
+      inner_wheel_init(emecData);
+      module_init();
+      m_FirstFan ++;
+      m_isElectrode = true;
+      break;
+    case LArG4::OuterElectrodModule:
+      outer_wheel_init(emecData);
+      module_init();
+      m_FirstFan ++;
+      m_isElectrode = true;
+      break;
+    default:
+      throw std::runtime_error("LArWheelCalculator constructor:unknown LArWheelCalculator_t");
+  }
+  m_ZeroFanPhi_ForDetNeaFan = m_ZeroFanPhi - m_FanStepOnPhi * 0.5;
+  m_NumberOfHalfWaves = m_NumberOfWaves * 2;
+  m_HalfWaveLength = m_ActiveLength / m_NumberOfHalfWaves;
+  m_QuarterWaveLength = m_HalfWaveLength * 0.5;
+  //m_HalfNumberOfFans = m_NumberOfFans / 2;
+  m_FanHalfThickness = GetFanHalfThickness(m_type);
+
+  // Init sagging
+  // value read above
+  // std::string sagging_opt_value = (*DB_EMECParams)[0]->getString("SAGGING");
+
+  msg << MSG::VERBOSE << "SAGGING value = " << sagging_opt_value << endmsg;
+
+  // the same condition is in DistanceCalculatorFactory::Create
+  m_SaggingOn = (sagging_opt_value != "" && sagging_opt_value != "off")? true: false;
+
+  m_distanceCalcImpl = LArWheelCalculator_Impl::DistanceCalculatorFactory::Create(
+										  sagging_opt_value, this);
+  if (m_SaggingOn) {
+    msg << MSG::VERBOSE << "Creating DistanceCalculatorSaggingOn = "  << this
+        << ',' << m_distanceCalcImpl << endmsg;
+  } else {
+    msg << MSG::VERBOSE << "Creating DistanceCalculatorSaggingOff = " << this
+        << ',' << m_distanceCalcImpl << endmsg;
+  }
+
+  m_fanCalcImpl = LArWheelCalculator_Impl::FanCalculatorFactory::Create(
+      m_SaggingOn, m_isModule, this);
+
+  //--------------------------
+  // At this place there was the loading of sagging parameters
+  // Transfered to DistanceCalculatorSaggingOn
+  //--------------------------
+
+  // Get option: Slant params.
+  msg << MSG::VERBOSE << "Loading SlantAngle parameters ...";
+  std::string slant_params;
+
+  if (m_isInner) {
+    slant_params=emecData.emecparams[0].INNERSLANTPARAM;
+  } else {
+    slant_params=emecData.emecparams[0].OUTERSLANTPARAM;
+  }
+
+  msg << (m_isInner?" InnerWheel ":" OuterWheel ") << slant_params << endmsg;
+
+  if(slant_params != "" && slant_params != "default"){
+    double a, b, c, d, e;
+    if(sscanf(slant_params.c_str(), "%80le %80le %80le %80le %80le", &a, &b, &c, &d, &e) != 5){
+      msg << MSG::ERROR
+          << "LArWheelCalculator: ERROR: wrong value(s) "
+          << "for EMEC slant angle parameters: "
+          << slant_params << ", "
+          << "defaults are used" << endmsg;
+    } else {
+      m_slant_parametrization[0] = a;
+      m_slant_parametrization[1] = b;
+      m_slant_parametrization[2] = c;
+      m_slant_parametrization[3] = d;
+      m_slant_parametrization[4] = e;
+      m_slant_use_default = false;
+    }
+  } // else already initialized in inner/outer_wheel_init()
+
+  fill_sincos_parameterization(); // initialize sin&cos parameterization
+
+  msg << MSG::VERBOSE << "All params initialized. Print some internal variables" << endmsg;
+
+  msg << MSG::VERBOSE << "Data members:" << endmsg 
+      << "m_AtlasZside              = " << m_AtlasZside << "" << endmsg
+      << "m_NumberOfFans            = " << m_NumberOfFans << "" << endmsg
+      << "m_ZeroFanPhi              = " << m_ZeroFanPhi << "" << endmsg
+      << "m_ZeroFanPhi_ForDetNeaFan = " << m_ZeroFanPhi_ForDetNeaFan << "" << endmsg
+      << "m_FanStepOnPhi            = " << m_FanStepOnPhi << "" << endmsg
+      << "m_FanHalfThickness        = " << m_FanHalfThickness << "" << endmsg
+      //<< "Sagging parameters        : " << m_sagging_parameter[0][0] << " " << m_sagging_parameter[0][1] << "" << endmsg
+      //<< "Sagging parameters        : " << m_sagging_parameter[1][0] << " " << m_sagging_parameter[1][1] << "" << endmsg
+      << "slant_params              = " << slant_params << "" << endmsg
+      << "Sagging option            = " << sagging_opt_value << "" << endmsg
+      << "SaggingOn                 = " << (m_SaggingOn? "true": "false") << "" << endmsg
+      << "Slant parameters          : ";
+  for(int i = 0; i < 5; i ++) msg << " " << m_slant_parametrization[i];
+  msg << endmsg;
+
+  if(m_isModule){
+    msg << MSG::VERBOSE
+        << "module_init: FirstFan = " << m_FirstFan
+        << ", LastFan = " << m_LastFan
+        << ", ZeroFanPhi = " << m_ZeroFanPhi
+        << endmsg;
+  }
+
+  //m_fan_number = -1000;
+
+  // Is the following code fragment obsoleted? DM 2015-03-13
+  /* to compare various methods of slant angle computation:
+     if(isInner) return;
+     FILE *O = fopen("slant_stat.table1.txt", "w");
+     if(O == 0) abort();
+     struct timeval t1, t2;
+     struct timezone tz;
+     std::vector<double> alpha;
+     gettimeofday(&t1, &tz);
+     for(double r = 600.; r < 2100.; r += .01){
+     alpha.push_back(parameterized_slant_angle(r));
+     }
+     gettimeofday(&t2, &tz);
+
+     fprintf(O, "%d.%06d %d.%06d" << endmsg, t1.tv_sec, t1.tv_usec, t2.tv_sec, t2.tv_usec);
+     int i = 0;
+     for(double r = 600.; r < 2100.; r += .01, i ++){
+     fprintf(O, "%f %f\n", r, alpha[i]);
+     }
+
+     fclose(O);
+     exit(0);
+  */
+}
+
+/* converts module gap number into wheel gap number */
+int LArWheelCalculator::PhiGapNumberForWheel(int i) const
+{
+  return m_fanCalcImpl->PhiGapNumberForWheel(i);
+}
+
+void LArWheelCalculator::inner_wheel_init(const EMECData & emecData)
+{
+  for(int i = 0; i < 5; ++ i) {
+    m_slant_parametrization[i] = default_slant_parametrization[0][i];
+  }
+  m_slant_use_default = true;
+
+  m_NumberOfFans=emecData.emecwheelparameters[0].NABS;
+  m_NumberOfWaves=emecData.emecwheelparameters[0].NACC;
+
+  m_FanFoldRadius = 3.25*mm;
+  m_ZeroGapNumber = 64; // internal constant, should not be taken from DB
+  m_FanStepOnPhi = 2*M_PI / m_NumberOfFans;
+  m_isInner = true;
+}
+
+void LArWheelCalculator::outer_wheel_init(const EMECData & emecData)
+{
+  for(int i = 0; i < 5; ++ i) {
+    m_slant_parametrization[i] = default_slant_parametrization[1][i];
+  }
+  m_slant_use_default = true;
+
+  m_NumberOfFans=emecData.emecwheelparameters[1].NABS;
+  m_NumberOfWaves=emecData.emecwheelparameters[1].NACC;
+
+
+  m_FanFoldRadius = 3.0*mm;
+  m_ZeroGapNumber = 192; // internal constant, should not be taken from DB
+  m_FanStepOnPhi = 2*M_PI / m_NumberOfFans;
+  m_isInner = false;
+}
+
+double LArWheelCalculator::GetFanHalfThickness(LArG4::LArWheelCalculator_t t) const
+{
+
+  switch(t){
+    case LArG4::BackInnerBarretteWheelCalib:
+    case LArG4::BackInnerBarretteModuleCalib:
+    case LArG4::BackInnerBarretteWheel:
+    case LArG4::BackInnerBarretteModule:
+    case LArG4::InnerAbsorberWheel:
+    case LArG4::InnerAbsorberModule:
+       return (m_leadThicknessInner / 2 + m_steelThickness + m_glueThickness)*m_coldContraction; // new values, 02.11.06 J.T. with contraction in cold
+      // lead / 2 + steel + glue
+    case LArG4::InnerGlueWheel:
+      return (m_leadThicknessInner / 2 + m_glueThickness)*m_coldContraction;
+    case LArG4::InnerLeadWheel:
+      return m_leadThicknessInner / 2 * m_coldContraction;
+
+    case LArG4::BackOuterBarretteWheelCalib:
+    case LArG4::BackOuterBarretteModuleCalib:
+    case LArG4::BackOuterBarretteWheel:
+    case LArG4::BackOuterBarretteModule:
+    case LArG4::OuterAbsorberWheel:
+    case LArG4::OuterAbsorberModule:
+       return (m_leadThicknessOuter / 2 + m_steelThickness + m_glueThickness)*m_coldContraction;  // new values, 02.11.06 J.T.
+    case LArG4::OuterGlueWheel:
+      return (m_leadThicknessOuter / 2 + m_glueThickness)*m_coldContraction;
+    case LArG4::OuterLeadWheel:
+      return m_leadThicknessOuter / 2 * m_coldContraction;
+
+    case LArG4::InnerElectrodWheel:
+    case LArG4::OuterElectrodWheel:
+    case LArG4::InnerElectrodModule:
+    case LArG4::OuterElectrodModule:
+      return m_electrodeTotalThickness/m_electrodeInvContraction * 0.5;  //new values, 02.11.06 J.T
+  }
+  throw std::runtime_error("LArWheelCalculator::GetFanHalfThickness: wrong wheel type");
+}
+
+void LArWheelCalculator::module_init()
+{
+  m_isModule = true;
+  m_LastFan = m_NumberOfFans / 8;
+  m_FirstFan = 0;
+  m_ZeroFanPhi = - m_LastFan / 2 * m_FanStepOnPhi;
+}
+
+/*
+  array of r is filled with:
+  for inner wheel - 2 elements { r_front, r_back }
+  for outer wheel - 3 elements { r_front, r_middle, r_back }
+  return value - delta_z of middle point in case of outer wheel
+*/
+double LArWheelCalculator::GetWheelInnerRadius(double *r) const
+{
+  double zMid = 0.;
+  if(m_isInner){
+    double tanThetaInner = 2. * exp(-m_eta_hi ) / (1. - exp(-2.*m_eta_hi ));
+    r[0] = m_zWheelFrontFace * tanThetaInner;
+    r[1] = m_zWheelBackFace  * tanThetaInner;
+  } else {
+    double tanThetaMid   = 2. * exp(-m_eta_mid) / (1. - exp(-2.*m_eta_mid));
+    double inv_tanThetaOuter = (1. - exp(-2.*m_eta_low)) / (2. * exp(-m_eta_low));
+    // Note that there is a 3mm gap between the outer surface of the
+    // inner wheel and the inner surface of the outer wheel.
+    r[0] = m_zWheelFrontFace * tanThetaMid + m_HalfGapBetweenWheels;
+    r[1] = m_rOuterCutoff * inv_tanThetaOuter * tanThetaMid + m_HalfGapBetweenWheels;
+    r[2] = m_zWheelBackFace  * tanThetaMid + m_HalfGapBetweenWheels;
+    zMid = m_rOuterCutoff * inv_tanThetaOuter - m_zWheelFrontFace;
+  }
+  return zMid;
+}
+
+/*
+  array of r is filled with:
+  for inner wheel - 2 elements { r_front, r_back }
+  for outer wheel - 3 elements { r_front, r_middle, r_back }
+*/
+void LArWheelCalculator::GetWheelOuterRadius(double *r) const
+{
+  if(m_isInner){
+    double tanThetaMid   = 2. * exp(-m_eta_mid) / (1. - exp(-2.*m_eta_mid));
+    // Note that there is a 3mm gap between the outer surface of the
+    // inner wheel and the inner surface of the outer wheel.
+    r[0] = m_zWheelFrontFace * tanThetaMid - m_HalfGapBetweenWheels;
+    r[1] = m_zWheelBackFace  * tanThetaMid - m_HalfGapBetweenWheels;
+  } else {
+    double tanThetaOuter = 2. * exp(-m_eta_low) / (1. - exp(-2.*m_eta_low));
+    r[0] = m_zWheelFrontFace * tanThetaOuter;
+    r[1] = m_rOuterCutoff;
+    r[2] = m_rOuterCutoff;
+  }
+}
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculatorGeometry.cxx b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculatorGeometry.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..b985a3efe37c64e1adacfb649f4796116f1193bd
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculatorGeometry.cxx
@@ -0,0 +1,133 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "CxxUtils/sincos.h"
+//#include <cmath>
+#include <climits>
+#include <cassert>
+#include <iostream>
+
+#include "GeoSpecialShapes/LArWheelCalculator.h"
+#include "./LArWheelCalculator_Impl/IDistanceCalculator.h"
+#include "./LArWheelCalculator_Impl/IFanCalculator.h"
+#include "GeoModelKernel/Units.h"
+#ifdef HARDDEBUG
+#include<stdio.h>
+#endif
+
+using namespace GeoModelKernelUnits;
+
+void LArWheelCalculator::parameterized_sin(const double r, double &sin_a, double &cos_a) const
+{
+  const double r2 = r*r;
+  const double r3 = r2*r;
+  const double r4 = r2*r2;
+#if LARWC_SINCOS_POLY > 4
+  const double r5 = r4*r;
+#endif
+  sin_a = m_sin_parametrization[0]
+        + m_sin_parametrization[1]*r
+        + m_sin_parametrization[2]*r2
+        + m_sin_parametrization[3]*r3
+        + m_sin_parametrization[4]*r4
+#if LARWC_SINCOS_POLY > 4
+        + m_sin_parametrization[5]*r5
+#endif
+  ;
+  cos_a = sqrt(1. - sin_a*sin_a);
+}
+
+void LArWheelCalculator::parameterized_sincos(const double r, double &sin_a, double &cos_a) const
+{
+  const double r2 = r*r;
+  const double r3 = r2*r;
+  const double r4 = r2*r2;
+#if LARWC_SINCOS_POLY > 4
+  const double r5 = r4*r;
+#endif
+  sin_a = m_sin_parametrization[0]
+        + m_sin_parametrization[1]*r
+        + m_sin_parametrization[2]*r2
+        + m_sin_parametrization[3]*r3
+        + m_sin_parametrization[4]*r4
+#if LARWC_SINCOS_POLY > 4
+        + m_sin_parametrization[5]*r5
+#endif
+  ;
+  cos_a = m_cos_parametrization[0]
+        + m_cos_parametrization[1]*r
+        + m_cos_parametrization[2]*r2
+        + m_cos_parametrization[3]*r3
+        + m_cos_parametrization[4]*r4
+#if LARWC_SINCOS_POLY > 4
+        + m_cos_parametrization[5]*r5
+#endif
+  ;
+}
+
+// calculates wave slant angle using parametrization for current wheel
+// for given distance from calorimeter axis
+double LArWheelCalculator::parameterized_slant_angle(double r) const
+{
+  const double r2 = r*r;
+  const double r3 = r2*r;
+  const double r4 = r2*r2;
+  const double result = m_slant_parametrization[0] +
+                        r*m_slant_parametrization[1] +
+                        r2*m_slant_parametrization[2] +
+                        r3*m_slant_parametrization[3] +
+                        r4*m_slant_parametrization[4];
+  return result*deg;
+}
+
+// Determines the nearest to the input point fan.
+// Relays on the fact that each two fans have a fan of a different type between
+// them.
+// Returns distance to the nearest fan. Vector p is set to nearest fan coord.
+// system.
+// m_fan_number is set to nearest fan number
+double LArWheelCalculator::DistanceToTheNearestFan(CLHEP::Hep3Vector &p, int & out_fan_number) const
+{
+  return m_fanCalcImpl->DistanceToTheNearestFan(p, out_fan_number);
+}
+
+// Relays on the fact that each two fans have a fan of a different type between
+// them.
+// Affects m_fan_number.
+std::pair<int, int> LArWheelCalculator::GetPhiGapAndSide(const CLHEP::Hep3Vector &p) const
+{
+  return m_fanCalcImpl->GetPhiGapAndSide(p);
+}
+
+// Represents aproximate, probably underestimate, distance to the
+// neutral fibre of the vertical fan. Sign of return value means
+// side of the fan; negative - lower phi.
+//
+// Uses m_fan_number to compute sagging.
+double LArWheelCalculator::DistanceToTheNeutralFibre(const CLHEP::Hep3Vector& P, int fan_number) const
+{
+  return m_distanceCalcImpl->DistanceToTheNeutralFibre(P, fan_number);
+}
+
+CLHEP::Hep3Vector LArWheelCalculator::NearestPointOnNeutralFibre(const CLHEP::Hep3Vector &P, int fan_number) const
+{
+  return m_distanceCalcImpl->NearestPointOnNeutralFibre(P, fan_number);
+}
+
+std::vector<double> LArWheelCalculator::NearestPointOnNeutralFibre_asVector(const CLHEP::Hep3Vector &p, int fan_number) const
+{
+  CLHEP::Hep3Vector np = NearestPointOnNeutralFibre(p, fan_number);
+  return std::vector<double> { np.x(), np.y(), np.z() };
+}
+
+/*
+input is in local fan's coordinate system
+side: < 0 - lower phi
+      > 0 - greater phi
+      = 0 - neutral fibre
+*/
+double LArWheelCalculator::AmplitudeOfSurface(const CLHEP::Hep3Vector& P, int side, int fan_number) const
+{
+  return m_distanceCalcImpl->AmplitudeOfSurface(P, side, fan_number);
+}
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorFactory.cxx b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorFactory.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..cb130827c5888647cace5b119230915b85bcf614
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorFactory.cxx
@@ -0,0 +1,28 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "DistanceCalculatorFactory.h"
+
+#include "DistanceCalculatorSaggingOff.h"
+#include "DistanceCalculatorSaggingOn.h"
+
+namespace LArWheelCalculator_Impl
+{
+
+  IDistanceCalculator* DistanceCalculatorFactory::Create(const std::string & sagging_opt,
+                                                         LArWheelCalculator* lwc)
+
+  {
+    // the same condition is in LArWheelCalculator constructor
+    bool SaggingOn = (sagging_opt != "" && sagging_opt != "off")? true: false;
+
+    if (SaggingOn) {
+      return new DistanceCalculatorSaggingOn(sagging_opt, lwc);
+    } else {
+      return new DistanceCalculatorSaggingOff(lwc);
+    }
+  }
+
+}
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorFactory.h b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorFactory.h
new file mode 100644
index 0000000000000000000000000000000000000000..397e44c8e076dee08fa050673cd2720c2a0e0bed
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorFactory.h
@@ -0,0 +1,26 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef __LArWheelCalculator_Impl_DistanceCalculatorFactory_H__
+#define __LArWheelCalculator_Impl_DistanceCalculatorFactory_H__
+
+// DistanceCalculator factory
+// calculator creation depends on sagging mode
+
+#include <string>
+#include "IDistanceCalculator.h"
+class LArWheelCalculator;
+namespace LArWheelCalculator_Impl
+{
+  /// @todo Why is this a class???
+  class DistanceCalculatorFactory
+  {
+    public:
+      static IDistanceCalculator* Create(const std::string & sagging_opt,
+                                         LArWheelCalculator* lwc);
+  };
+
+}
+
+#endif // __LArWheelCalculator_Impl_DistanceCalculatorFactory_H__
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorSaggingOff.cxx b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorSaggingOff.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..fe4edc65fc5cc0e9ce33ad72406cbfb969885c6a
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorSaggingOff.cxx
@@ -0,0 +1,517 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "DistanceCalculatorSaggingOff.h"
+#include "GeoSpecialShapes/LArWheelCalculator.h"
+
+#include<cassert>
+
+//#define LWC_PARAM_ANGLE
+
+#ifdef LWC_PARAM_ANGLE
+#include <CxxUtils/sincos.h>
+#endif
+
+#include<signal.h>
+
+
+namespace LArWheelCalculator_Impl
+{
+
+  DistanceCalculatorSaggingOff::DistanceCalculatorSaggingOff(LArWheelCalculator* c)
+    : m_lwc(c)
+  {
+    m_EndQuarterWave = lwc()->m_ActiveLength - lwc()->m_QuarterWaveLength;
+  }
+
+#ifndef LARWC_DTNF_NEW
+  double DistanceCalculatorSaggingOff::DistanceToTheNeutralFibre(const CLHEP::Hep3Vector& P, int /*fan_number*/) const
+#else
+  double DistanceCalculatorSaggingOff::DistanceToTheNeutralFibre_ref(const CLHEP::Hep3Vector& P, int /*fan_number*/) const
+#endif
+  {
+    assert(P.y() > 0.);
+    double distance = 0.;
+    double z = P.z() - lwc()->m_StraightStartSection;
+    double x = P.x();
+
+#ifdef LWC_PARAM_ANGLE //old variant
+    const double alpha = lwc()->parameterized_slant_angle(P.y());
+    //double cos_a, sin_a;
+    //::sincos(alpha, &sin_a, &cos_a);
+    CxxUtils::sincos scalpha(alpha);
+    const double cos_a = scalpha.cs, sin_a = scalpha.sn;
+#else // parameterized sine
+    double cos_a, sin_a;
+    lwc()->m_vsincos_par.eval(P.y(), sin_a, cos_a);
+#endif
+    // determination of the nearest quarter-wave number
+    int nqwave = (z < 0.) ? 0 : int(z / lwc()->m_QuarterWaveLength);
+    //if(z < 0.) nqwave = 0;
+    //else nqwave = int(z / lwc()->m_QuarterWaveLength);
+
+    bool begin_qw = false;
+    if((nqwave % 2) != 0){
+      nqwave ++;
+      begin_qw = true;
+    }
+
+    nqwave /= 2;
+
+    // now nqwave is not the number of quarter-wave but the number of half-wave
+    // half-waves with last and zero numbers are not really half-waves but start
+    // and finish quarter-waves
+    // It means that half-wave number 1 begins after starting quarter-wave
+    if(nqwave != 0 && nqwave != lwc()->m_NumberOfHalfWaves){ // regular half-waves
+      z -= nqwave * lwc()->m_HalfWaveLength;
+      // there are some symmetries, so use them
+      if((nqwave % 2) == 0) x = -x;
+      if(begin_qw){
+        z = -z;
+        x = -x;
+      }
+      // certain situation: rising slope of wave, z is positive
+      // rotate to prime-coordinate system (see description)
+      const double z_prime = z * cos_a + x * sin_a;
+      const double x_prime = x * cos_a - z * sin_a;
+      const double straight_part = (lwc()->m_QuarterWaveLength - lwc()->m_FanFoldRadius * sin_a) / cos_a;
+      if(z_prime > straight_part){// fold region
+        const double dz = straight_part - z_prime;
+        const double dx = x_prime + lwc()->m_FanFoldRadius;
+        distance = sqrt(dz*dz + dx*dx) - lwc()->m_FanFoldRadius;
+      } else if(z_prime > -straight_part){
+        distance = x_prime; // straight part of the quarter-wave
+      } else {// fold region
+        const double dz = straight_part + z_prime;
+        const double dx = x_prime - lwc()->m_FanFoldRadius;
+        distance = lwc()->m_FanFoldRadius - sqrt(dz*dz + dx*dx);
+      }
+      // set correct sign for result
+      if(!begin_qw) distance = -distance;
+      if((nqwave % 2) == 0) distance = -distance;
+
+    } else { // start and finish quarter-waves
+      if(nqwave == 0)	{ // start quarter-wave
+        x = - x;
+      } else { // finish quarter-wave
+        z = lwc()->m_ActiveLength - z;
+      }
+
+      const double tan_beta = sin_a/(1.0 + cos_a); //tan(alpha * 0.5);
+      const double local_straight_section = lwc()->m_FanFoldRadius * tan_beta;
+      if( z < - local_straight_section &&
+          ( x < lwc()->m_FanFoldRadius ||
+            x < - lwc()->m_StraightStartSection * z / local_straight_section / tan_beta ) )
+      {
+        distance = - x;
+      }
+      else {
+        const double z_prime = z * cos_a + x * sin_a;
+        const double x_prime = x * cos_a - z * sin_a;
+        if (z_prime < local_straight_section) { // start fold region
+          const double dz = local_straight_section - z_prime;
+          const double dx = x_prime - lwc()->m_FanFoldRadius;
+          distance = sqrt(dz*dz + dx*dx) - lwc()->m_FanFoldRadius;
+        } else {
+          const double straight_part = (lwc()->m_QuarterWaveLength - lwc()->m_FanFoldRadius * sin_a) / cos_a;
+          if (z_prime <= straight_part) { // straight part of quarter-wave
+            distance = - x_prime;
+          } else { // regular fold region of the quarter-wave
+            const double dz = straight_part - z_prime;
+            const double dx = x_prime + lwc()->m_FanFoldRadius;
+            distance = lwc()->m_FanFoldRadius - sqrt(dz*dz + dx*dx);
+          }
+        }
+      }
+      // set correct sign
+      if (nqwave == 0) distance = -distance;
+    }
+#ifdef HARDDEBUG
+    double dd = DistanceToTheNeutralFibre_ref(P);
+    if(fabs(dd - distance) > 0.000001){
+      //static int cnt = 0;
+      std::cout << "DTNF MISMATCH " << this << " " << P << " "
+                << dd << " vs " << distance << std::endl;
+      //cnt ++;
+      //if(cnt > 100) exit(0);
+    }
+#endif
+    return distance;
+  }
+
+  // IMPROVED PERFORMANCE
+#ifdef LARWC_DTNF_NEW
+  double DistanceCalculatorSaggingOff::DistanceToTheNeutralFibre(const CLHEP::Hep3Vector& P, int /*fan_number*/) const
+#else
+  double DistanceCalculatorSaggingOff::DistanceToTheNeutralFibre_ref(const CLHEP::Hep3Vector& P, int /*fan_number*/) const
+#endif
+  {
+    double z = P.z() - lwc()->m_StraightStartSection;
+    double x = P.x();
+
+#ifdef LWC_PARAM_ANGLE //old variant
+    const double alpha = lwc()->parameterized_slant_angle(P.y());
+    CxxUtils::sincos scalpha(alpha);
+    double cos_a = scalpha.cs, sin_a = scalpha.sn;
+#else // parameterized sine
+    double cos_a, sin_a;
+    lwc()->m_vsincos_par.eval(P.y(), sin_a, cos_a);
+#endif
+
+    bool sqw = false;
+    if(z > lwc()->m_QuarterWaveLength){
+      if(z < m_EndQuarterWave){ // regular half-waves
+        unsigned int nhwave = (unsigned int)(z / lwc()->m_HalfWaveLength + 0.5);
+        z -= lwc()->m_HalfWaveLength * nhwave;
+        const double straight_part = (lwc()->m_QuarterWaveLength - lwc()->m_FanFoldRadius * sin_a) / cos_a;
+        nhwave &= 1U;
+        if(nhwave == 0) sin_a = - sin_a;
+        double z_prime = z * cos_a + x * sin_a;
+        const double x_prime = z * sin_a - x * cos_a;
+        if(z_prime > straight_part){ // up fold region
+          const double dz = z_prime - straight_part;
+          if(nhwave == 0){
+            const double dx = x_prime + lwc()->m_FanFoldRadius;
+            return sqrt(dz*dz + dx*dx) - lwc()->m_FanFoldRadius;
+          } else {
+            const double dx = x_prime - lwc()->m_FanFoldRadius;
+            return lwc()->m_FanFoldRadius - sqrt(dz*dz + dx*dx);
+          }
+        }
+        z_prime += straight_part;
+        if(z_prime > 0){
+          return x_prime; // straight part of the quarter-wave
+        } else { // low fold region
+          const double &dz = z_prime;
+          if(nhwave == 0){
+            const double dx = x_prime - lwc()->m_FanFoldRadius;
+            return lwc()->m_FanFoldRadius - sqrt(dz*dz + dx*dx);
+          } else {
+            const double dx = x_prime + lwc()->m_FanFoldRadius;
+            return sqrt(dz*dz + dx*dx) - lwc()->m_FanFoldRadius;
+          }
+        }
+      } else { // ending quarter-wave
+        z = lwc()->m_ActiveLength - z;
+      }
+    } else { // starting quarter-wave
+      x = - x;
+      sqw = true;
+    }
+
+    // start and finish quarter-waves
+    const double tan_beta = sin_a/(1.0 + cos_a); //tan(alpha * 0.5);
+    const double local_straight_section = lwc()->m_FanFoldRadius * tan_beta;
+    if(z < - local_straight_section &&
+       ( x < lwc()->m_FanFoldRadius ||
+         x < - lwc()->m_StraightStartSection * z / local_straight_section / tan_beta ))
+    {
+      return sqw? x: (-x);
+    }
+    else {
+      const double z_prime = z * cos_a + x * sin_a;
+      const double x_prime = x * cos_a - z * sin_a;
+      if (z_prime < local_straight_section) { // start fold region
+        const double dz = local_straight_section - z_prime;
+        const double dx = x_prime - lwc()->m_FanFoldRadius;
+        if(sqw) return lwc()->m_FanFoldRadius - sqrt(dz*dz + dx*dx);
+        else    return sqrt(dz*dz + dx*dx) - lwc()->m_FanFoldRadius;
+      } else {
+        const double straight_part =
+          (lwc()->m_QuarterWaveLength - lwc()->m_FanFoldRadius * sin_a) / cos_a;
+        if (z_prime <= straight_part) { // straight part of quarter-wave
+          return sqw? x_prime: (-x_prime);
+        } else { // regular fold region of the quarter-wave
+          const double dz = straight_part - z_prime;
+          const double dx = x_prime + lwc()->m_FanFoldRadius;
+          if(sqw) return sqrt(dz*dz + dx*dx) - lwc()->m_FanFoldRadius;
+          else    return lwc()->m_FanFoldRadius - sqrt(dz*dz + dx*dx);
+        }
+      }
+    }
+    // ???
+    std::abort();
+  }
+
+  CLHEP::Hep3Vector DistanceCalculatorSaggingOff::NearestPointOnNeutralFibre(const CLHEP::Hep3Vector &P, int /*fan_number*/) const
+  {
+    CLHEP::Hep3Vector result;
+    double z = P.z() - lwc()->m_StraightStartSection;
+    double x = P.x();
+    double y = P.y();
+
+#ifdef LWC_PARAM_ANGLE //old variant
+    const double alpha = lwc()->parameterized_slant_angle(P.y());
+    CxxUtils::sincos scalpha(alpha);
+    const double cos_a = scalpha.cs, sin_a = scalpha.sn;
+#else // parameterized sine
+    double cos_a, sin_a;
+    lwc()->m_vsincos_par.eval(P.y(), sin_a, cos_a);
+#endif
+
+    int nqwave;
+    if(z < 0.) nqwave = 0;
+    else nqwave = int(z / lwc()->m_QuarterWaveLength);
+    bool begin_qw = false;
+    if((nqwave % 2) != 0){
+      nqwave ++;
+      begin_qw = true;
+    }
+    nqwave /= 2;
+    if(nqwave != 0 && nqwave != lwc()->m_NumberOfHalfWaves){
+      z -= nqwave * lwc()->m_HalfWaveLength;
+      if((nqwave % 2) == 0) x = -x;
+      if(begin_qw){
+        z = -z;
+        x = -x;
+      }
+      const double z_prime = z * cos_a + x * sin_a;
+      const double x_prime = x * cos_a - z * sin_a;
+      const double straight_part = (lwc()->m_QuarterWaveLength - lwc()->m_FanFoldRadius * sin_a) / cos_a;
+      const double dz = straight_part - z_prime;
+      if (dz > 0) result.set(0., y, z_prime);
+      else {
+        double a = atan(fabs(dz / (x_prime + lwc()->m_FanFoldRadius)));
+        result.set(lwc()->m_FanFoldRadius * (cos(a) - 1), y, straight_part + lwc()->m_FanFoldRadius * sin(a));
+      }
+      result.rotateY(asin(sin_a));
+      if(begin_qw){
+        result.setX(-result.x());
+        result.setZ(-result.z());
+      }
+      if((nqwave % 2) == 0) result.setX(-result.x());
+      result.setZ(result.z() + nqwave * lwc()->m_HalfWaveLength);
+    } else {
+      if(nqwave == 0) x = -x;
+      else z = lwc()->m_ActiveLength - z;
+      const double tan_beta = sin_a/(1.0+cos_a); //tan(alpha * 0.5);
+      const double local_straight_section = lwc()->m_FanFoldRadius * tan_beta;
+      if(z < - local_straight_section &&
+         ( x < lwc()->m_FanFoldRadius ||
+           x < - lwc()->m_StraightStartSection * z / local_straight_section / tan_beta))
+      {
+        result.set(0., y, z);
+      }
+      else {
+        const double z_prime = z * cos_a + x * sin_a;
+        const double x_prime = x * cos_a - z * sin_a;
+        if(z_prime < local_straight_section) {
+          double a = fabs(atan((local_straight_section - z_prime) / (x_prime - lwc()->m_FanFoldRadius)));
+
+          result.set(lwc()->m_FanFoldRadius * (1 - cos(a)), y, local_straight_section - lwc()->m_FanFoldRadius * sin(a));
+        } else {
+          double straight_part = (lwc()->m_QuarterWaveLength - lwc()->m_FanFoldRadius * sin_a) / cos_a;
+          if(z_prime <= straight_part) {
+            result.set(0., y, z_prime);
+          } else {
+            double a = fabs(atan((straight_part - z_prime) /  (x_prime + lwc()->m_FanFoldRadius)) );
+            result.set(lwc()->m_FanFoldRadius * (cos(a) - 1), y, straight_part + lwc()->m_FanFoldRadius * sin(a));
+          }
+        }
+        result.rotateY(asin(sin_a));
+      }
+      if(nqwave != 0){
+        result.setZ(lwc()->m_ActiveLength - result.z());
+      } else {
+        result.setX(-result.x());
+      }
+    }
+    result.setZ(result.z() + lwc()->m_StraightStartSection);
+    return result;
+  }
+
+  // IMPROVED VERSION
+  CLHEP::Hep3Vector DistanceCalculatorSaggingOff::NearestPointOnNeutralFibre_ref(const CLHEP::Hep3Vector &P, int /*fan_number*/) const
+  {
+    CLHEP::Hep3Vector result;
+    double z = P.z() - lwc()->m_StraightStartSection;
+    double x = P.x();
+    double y = P.y();
+
+#ifdef LWC_PARAM_ANGLE //old variant
+    const double alpha = lwc()->parameterized_slant_angle(P.y());
+    CxxUtils::sincos scalpha(alpha);
+    double cos_a = scalpha.cs, sin_a = scalpha.sn;
+#else // parameterized sine
+    double cos_a, sin_a;
+    lwc()->m_vsincos_par.eval(P.y(), sin_a, cos_a);
+#endif
+
+    bool sqw = false;
+    if(z > lwc()->m_QuarterWaveLength){
+      if(z < m_EndQuarterWave){ // regular half-waves
+        unsigned int nhwave = (unsigned int)(z / lwc()->m_HalfWaveLength + 0.5);
+        const double zshift = lwc()->m_HalfWaveLength * nhwave;
+        z -= zshift;
+        const double straight_part =
+          (lwc()->m_QuarterWaveLength - lwc()->m_FanFoldRadius * sin_a) / cos_a;
+        nhwave &= 1U;
+        if(nhwave == 0) sin_a = - sin_a;
+        const double z_prime = z * cos_a + x * sin_a;
+        if(z_prime > straight_part){ // up fold
+          const double x_prime = x * cos_a - z * sin_a;
+          const double dz = straight_part - z_prime;
+          double a1 = atan(fabs(dz / (x_prime + lwc()->m_FanFoldRadius)));
+          const double x1 = lwc()->m_FanFoldRadius * (cos(a1) - 1.);
+          const double z1 = straight_part + lwc()->m_FanFoldRadius * sin(a1);
+          result.set(x1*cos_a - z1*sin_a, y, z1*cos_a + z1*sin_a);
+          return result;
+        } else if(z_prime > -straight_part){ // straight part
+          result.set(-z_prime * sin_a, y, z_prime*cos_a + zshift);
+          return result;
+        } else { // low fold
+          const double x_prime = x * cos_a - z * sin_a;
+          const double dz = straight_part + z_prime;
+          double a1 = atan(fabs(dz / (x_prime + lwc()->m_FanFoldRadius)));
+          const double x1 = lwc()->m_FanFoldRadius * (cos(a1) - 1.);
+          const double z1 = straight_part + lwc()->m_FanFoldRadius * sin(a1);
+          result.set(x1*cos_a - z1*sin_a, y, z1*cos_a + z1*sin_a);
+          return result;
+        }
+      } else { // ending quarter-wave
+        z = lwc()->m_ActiveLength - z;
+      }
+    } else { // starting quarter-wave
+      x = - x;
+      sqw = true;
+    }
+
+    // start and finish quarter-waves
+    const double tan_beta = sin_a / (1.0 + cos_a);
+    const double local_straight_section = lwc()->m_FanFoldRadius * tan_beta;
+    if(z < - local_straight_section &&
+       (x < lwc()->m_FanFoldRadius ||
+        x < - lwc()->m_StraightStartSection * z / local_straight_section / tan_beta))
+    {
+      result.set(0., y, z);
+    }
+    else {
+      const double z_prime = z * cos_a + x * sin_a;
+      const double x_prime = x * cos_a - z * sin_a;
+      if(z_prime < local_straight_section) {
+        double a = fabs(atan((local_straight_section - z_prime) / (x_prime - lwc()->m_FanFoldRadius)));
+        result.set(lwc()->m_FanFoldRadius * (1 - cos(a)), y, local_straight_section - lwc()->m_FanFoldRadius * sin(a));
+      } else {
+        double straight_part = (lwc()->m_QuarterWaveLength - lwc()->m_FanFoldRadius * sin_a) / cos_a;
+        if(z_prime <= straight_part) {
+          result.set(0., y, z_prime);
+        } else {
+          double a = fabs(atan((straight_part - z_prime) /  (x_prime + lwc()->m_FanFoldRadius)) );
+          result.set(lwc()->m_FanFoldRadius * (cos(a) - 1), y, straight_part + lwc()->m_FanFoldRadius * sin(a));
+        }
+      }
+      result.rotateY(asin(sin_a));
+    }
+    if(sqw) result.setX(-result.x());
+    else result.setZ(lwc()->m_ActiveLength - result.z());
+    result.setZ(result.z() + lwc()->m_StraightStartSection);
+    return result;
+  }
+
+  /*
+  input is in local fan's coordinate system
+  side: < 0 - lower phi
+        > 0 - greater phi
+        = 0 - neutral fibre
+  */
+  double DistanceCalculatorSaggingOff::AmplitudeOfSurface(const CLHEP::Hep3Vector& P, int side, int /*fan_number*/) const
+  {
+    double result = 0.;
+    double rho = lwc()->m_FanFoldRadius;
+    double z = P.z() - lwc()->m_StraightStartSection;
+
+#ifdef LWC_PARAM_ANGLE //old variant
+    const double alpha = lwc()->parameterized_slant_angle(P.y());
+    //double cos_a, sin_a;
+    //::sincos(alpha, &sin_a, &cos_a);
+    CxxUtils::sincos scalpha(alpha);
+    const double cos_a = scalpha.cs, sin_a = scalpha.sn;
+    // parameterized sine
+#else
+    double cos_a, sin_a;
+    lwc()->m_vsincos_par.eval(P.y(), sin_a, cos_a);
+#endif
+
+    // determination of the nearest quarter-wave number
+    int nqwave;
+    if(z < 0.) nqwave = 0;
+    else nqwave = int(z / lwc()->m_QuarterWaveLength);
+    bool begin_qw = false;
+    if((nqwave % 2) != 0){
+      nqwave ++;
+      begin_qw = true;
+    }
+    nqwave /= 2;
+    // now nqwave is not the number of quarter-wave but the number of half-wave
+    // half-waves with last and zero numbers are not really half-waves but start
+    // and finish quarter-waves
+    // It means that half-wave number 1 begins after starting quarter-wave
+    if(nqwave != 0 && nqwave != lwc()->m_NumberOfHalfWaves){ // regular half-waves
+      z -= nqwave * lwc()->m_HalfWaveLength;
+      if(begin_qw) z = -z;
+      double dz = lwc()->m_QuarterWaveLength - z;
+
+      int local_side = 1;
+      if((nqwave % 2) == 0){
+        if(begin_qw) local_side = -1;
+      } else {
+        if(!begin_qw) local_side = -1;
+      }
+
+      rho += lwc()->m_FanHalfThickness * local_side * side;
+
+      if(dz >= rho * sin_a){
+        result = z * sin_a / cos_a; // straight part of the quarter-wave
+      } else { // fold region
+        result = (lwc()->m_QuarterWaveLength * sin_a - rho) / cos_a
+          + sqrt(rho * rho - dz * dz);
+      }
+      result *= -local_side;
+      if(side < 0) result += lwc()->m_FanHalfThickness / cos_a;
+      else if(side > 0) result -= lwc()->m_FanHalfThickness / cos_a;
+
+    } else { // start and finish quarter-waves
+      int local_side = 1;
+      if(nqwave == 0) { // start quarter-wave
+        local_side = -1;
+      } else { // finish quarter-wave
+        z = lwc()->m_ActiveLength - z;
+      }
+
+      const double rho1i = lwc()->m_FanFoldRadius;
+      const double tan_beta = sin_a/(1.0+cos_a); //tan(alpha * 0.5);
+      const double min_local_fold_region = rho1i * tan_beta;
+
+      if(z <= - min_local_fold_region){
+        result = - side * lwc()->m_FanHalfThickness;
+      } else {
+        const double rho1 = rho1i + lwc()->m_FanHalfThickness * side * local_side;
+
+        const double max_local_fold_region = rho1 * sin_a - min_local_fold_region;
+        //const double max_local_fold_region = rho1 * tan_beta * (1. + cos_a) - min_local_fold_region;
+        if(z < max_local_fold_region){ // start fold region
+          z += min_local_fold_region;
+          result = rho1 - sqrt(rho1 * rho1 - z * z);
+          if(nqwave == 0) result = -result;
+          if(side < 0) result += lwc()->m_FanHalfThickness;
+          else if(side > 0) result -= lwc()->m_FanHalfThickness;
+        } else {
+          rho -= lwc()->m_FanHalfThickness * local_side * side;
+          const double dz = lwc()->m_QuarterWaveLength - z;
+          if(dz >= rho * sin_a){
+            result = z * sin_a / cos_a; // straight part of the quarter-wave
+          } else { // regular fold region
+            result = (lwc()->m_QuarterWaveLength * sin_a - rho) / cos_a
+                     + sqrt(rho * rho - dz * dz);
+          }
+          if(nqwave == 0) result = -result;
+          if(side < 0) result += lwc()->m_FanHalfThickness / cos_a;
+          else if(side > 0) result -= lwc()->m_FanHalfThickness / cos_a;
+        }
+      }
+    }
+    return result;
+  }
+
+}
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorSaggingOff.h b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorSaggingOff.h
new file mode 100644
index 0000000000000000000000000000000000000000..979d3fb64d6990b524ca345467a799a5bcaa7852
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorSaggingOff.h
@@ -0,0 +1,49 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef __LArWheelCalculator_Impl_DistanceCalculatorSaggingOff_H__
+#define __LArWheelCalculator_Impl_DistanceCalculatorSaggingOff_H__
+
+
+#include "IDistanceCalculator.h"
+
+class LArWheelCalculator;
+
+namespace LArWheelCalculator_Impl
+{
+
+  /// @class DistanceCalculatorSaggingOff
+  /// @brief Implements details of distance calculation to parts of the
+  /// LAr endcap without sagging corrections.
+  ///
+  class DistanceCalculatorSaggingOff : public IDistanceCalculator
+  {
+
+    public:
+
+      /// Constructor
+      DistanceCalculatorSaggingOff(LArWheelCalculator* lwc);
+
+      /// @name Geometry methods
+      /// @{
+      virtual double DistanceToTheNeutralFibre(const CLHEP::Hep3Vector &p, int fan_number) const;
+      virtual double DistanceToTheNeutralFibre_ref(const CLHEP::Hep3Vector &p, int fan_number) const;
+      virtual CLHEP::Hep3Vector NearestPointOnNeutralFibre(const CLHEP::Hep3Vector &p, int fan_number) const;
+      virtual CLHEP::Hep3Vector NearestPointOnNeutralFibre_ref(const CLHEP::Hep3Vector &p, int fan_number) const;
+      virtual double AmplitudeOfSurface(const CLHEP::Hep3Vector& P, int side, int fan_number) const;
+      /// @}
+
+      /// Return the calculator:
+      inline const LArWheelCalculator *lwc() const { return m_lwc; };
+
+    private:
+
+      LArWheelCalculator* m_lwc;
+      double m_EndQuarterWave;
+
+  };
+
+}
+
+#endif // __LArWheelCalculator_Impl_IDistanceCalculatorOff_H__
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorSaggingOn.cxx b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorSaggingOn.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..e68c10d06fa8c87e09855e99b93c6aa70117fadc
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorSaggingOn.cxx
@@ -0,0 +1,173 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef PORTABLE_LAR_SHAPE
+  #include "GaudiKernel/MsgStream.h"
+  #include "GaudiKernel/ISvcLocator.h"
+  #include "GaudiKernel/Bootstrap.h"
+#else
+  #include "PortableMsgStream/PortableMsgStream.h"
+#endif
+#include "DistanceCalculatorSaggingOn.h"
+#include "CLHEP/Vector/ThreeVector.h"
+
+#include <vector>
+#include <stdexcept>
+
+#include "GeoSpecialShapes/LArWheelCalculator.h"
+
+#include "GeoModelKernel/Units.h"
+
+using GeoModelKernelUnits::mm;
+
+
+namespace LArWheelCalculator_Impl
+{
+  DistanceCalculatorSaggingOn::DistanceCalculatorSaggingOn(const std::string& saggingOptions,
+                                                           LArWheelCalculator* lwc)
+    : parent(lwc),
+      m_saggingOptions(saggingOptions)
+
+  {
+    init_sagging_parameters();
+  }
+	
+  void DistanceCalculatorSaggingOn::init_sagging_parameters()
+  {
+
+    // Get pointer to the message service
+#ifndef PORTABLE_LAR_SHAPE
+    ISvcLocator* svcLocator = Gaudi::svcLocator();
+    IMessageSvc* msgSvc;
+    StatusCode status = svcLocator->service("MessageSvc", msgSvc);
+    if(status.isFailure()){
+      throw std::runtime_error("LArWheelCalculator constructor: \
+          cannot initialze message service");
+    }
+    MsgStream msg(msgSvc, "LArWheelCalculator_Impl::DistanceCalculatorSaggingOn");
+#else
+    PortableMsgStream msg("LArWheelCalculator_Impl::DistanceCalculatorSaggingOn");
+#endif
+    std::string sagging_opt_value = m_saggingOptions;
+    m_sagging_parameter.resize (lwc()->m_NumberOfFans, std::vector<double> (5, 0.));
+    //	if(m_SaggingOn) {
+    if(sagging_opt_value.substr(0, 4) == "file"){
+      std::string sag_file = sagging_opt_value.substr(5);
+      msg << MSG::DEBUG
+        << "geting sagging parameters from file "
+        << sag_file << " ..." << endmsg;
+      FILE *F = fopen(sag_file.c_str(), "r");
+      if(F == 0){
+        msg << MSG::FATAL
+          << "cannot open EMEC sagging parameters file "
+          << sag_file
+          << endmsg;
+        throw std::runtime_error("LArWheelCalculator: read sagging parameters from file");
+      }
+      int s, w, t, n;
+      double p0, p1, p2, p3, p4;
+      while(!feof(F) &&
+            fscanf(F, "%80d %80d %80d %80d %80le %80le %80le %80le %80le",
+                   &s, &w, &t, &n, &p0, &p1, &p2, &p3, &p4) == 9)
+      {
+        if(s == lwc()->m_AtlasZside &&
+           ((w == 0 && lwc()->m_isInner) || (w == 1 && !lwc()->m_isInner)) &&
+           ((t == 0 && !lwc()->m_isElectrode) || (t == 1 && lwc()->m_isElectrode)) &&
+           (n >= 0 && n < lwc()->m_NumberOfFans))
+        {
+          m_sagging_parameter[n][0] = p0;
+          m_sagging_parameter[n][1] = p1;
+          m_sagging_parameter[n][2] = p2;
+          m_sagging_parameter[n][3] = p3;
+          m_sagging_parameter[n][4] = p4;
+          msg << MSG::VERBOSE
+              << "sagging for " << s << " " << w << " " << t
+              << " " << n << ": " << p0 << " " << p1 << " "
+              << p2 << " " << p3 << endmsg;
+        }
+      }
+      fclose(F);
+    } else {
+      double a, b, c, d;
+      if(sscanf(sagging_opt_value.c_str(), "%80le %80le %80le %80le", &a, &b, &c, &d) != 4){
+        msg << MSG::ERROR
+            << "wrong value(s) "
+            << " for EMEC sagging parameters: "
+            << sagging_opt_value << ", defaults are used" << endmsg;
+      } else {
+        for(int j = 0; j < lwc()->m_NumberOfFans; j ++){
+          if(lwc()->m_isInner){
+            m_sagging_parameter[j][1] = a;
+            m_sagging_parameter[j][0] = b * mm;
+          } else {
+            m_sagging_parameter[j][1] = c;
+            m_sagging_parameter[j][0] = d * mm;
+          }
+        }
+      }
+    }
+    //	}
+    msg << MSG::INFO  << "Sagging parameters        : " << m_sagging_parameter[0][0] << " " << m_sagging_parameter[0][1] << endmsg
+        << "Sagging parameters        : " << m_sagging_parameter[1][0] << " " << m_sagging_parameter[1][1] << endmsg;
+  }
+
+  // Represents aproximate, probably underestimate, distance to the
+  // neutral fibre of the vertical fan. Sign of return value means
+  // side of the fan; negative - lower phi.
+  //
+  // Uses m_fan_number to compute sagging.
+  double DistanceCalculatorSaggingOn::DistanceToTheNeutralFibre(const CLHEP::Hep3Vector &p, int fan_number) const {
+    CLHEP::Hep3Vector sagging_corrected( p.x()+get_sagging(p, fan_number), p.y(), p.z() );
+    return parent::DistanceToTheNeutralFibre(sagging_corrected, fan_number);
+  }
+
+  CLHEP::Hep3Vector DistanceCalculatorSaggingOn::NearestPointOnNeutralFibre(const CLHEP::Hep3Vector &p, int fan_number) const {
+    CLHEP::Hep3Vector sagging_corrected( p.x()+get_sagging(p, fan_number), p.y(), p.z() );
+    return parent::NearestPointOnNeutralFibre(sagging_corrected, fan_number);
+  }
+
+  double DistanceCalculatorSaggingOn::AmplitudeOfSurface(const CLHEP::Hep3Vector& p, int side, int fan_number) const {
+    return parent::AmplitudeOfSurface(p, side, fan_number) - get_sagging(p, fan_number);
+  }
+
+
+  // the function uses m_fan_number for phi-dependent sagging computation
+  double DistanceCalculatorSaggingOn::get_sagging(const CLHEP::Hep3Vector &P, int fan_number) const {
+#ifdef HARDDEBUG
+    std::cout << "get_sagging: MFN = " << fan_number << std::endl;
+#endif
+    double dx = P.z() / lwc()->m_HalfWheelThickness - 1.;
+    dx *= dx;
+    dx = 1. - dx;
+    //dx *= SaggingAmplitude * sin(FanStepOnPhi * m_fan_number + ZeroFanPhi);
+    //int n = m_fan_number;
+    //if(n < 0) n += m_NumberOfFans;
+    //n += m_ZeroGapNumber;
+    //if(n >= m_NumberOfFans) n -= m_NumberOfFans;
+    //const std::vector<double>& sp = m_sagging_parameter[n];
+    const std::vector<double>& sp = m_sagging_parameter[fan_number];
+    double R = P.r() / mm;
+    double r = R;
+    double result = sp[0];
+    result += R * sp[1];
+    R *= r;
+    result += R * sp[2];
+    R *= r;
+    result += R * sp[3];
+    R *= r;
+    result += R * sp[4];
+
+#ifdef HARDDEBUG
+    /*printf("get_sagging: (%6.3f, %6.3f, %6.3f) %6.3f; MFN %4d;"
+      "n %3d; sp %6.4f %6.4f; dx %6.3f; %.6f\n", P.x()/mm, P.y()/mm, P.z()/mm,
+      r, m_fan_number, n, sp[0], sp[1], dx, result*dx);*/
+    printf("get_sagging: (%6.3f, %6.3f, %6.3f) %6.3f;"
+           " sp %6.4f %6.4f; dx %6.3f; %.6f\n", P.x()/mm, P.y()/mm, P.z()/mm,
+           r, sp[0], sp[1], dx, result*dx);
+#endif //HARDDEBUG
+
+    return result * dx;
+  }
+
+}
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorSaggingOn.h b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorSaggingOn.h
new file mode 100644
index 0000000000000000000000000000000000000000..082db31eeb9552d42f598d5cdc487d124eaf5894
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/DistanceCalculatorSaggingOn.h
@@ -0,0 +1,48 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef __LArWheelCalculator_Impl_DistanceCalculatorSaggingOn_H__
+#define __LArWheelCalculator_Impl_DistanceCalculatorSaggingOn_H__
+
+#include "DistanceCalculatorSaggingOff.h"
+#include <vector>
+class LArWheelCalculator;
+namespace LArWheelCalculator_Impl
+{
+
+  /// @class DistanceCalculatorSaggingOn
+  /// @brief Implements details of distance calculation to parts of the
+  /// LAr endcap with sagging taken into account.
+  ///
+  class DistanceCalculatorSaggingOn : public DistanceCalculatorSaggingOff
+  {
+
+    public:
+
+      typedef DistanceCalculatorSaggingOff parent;
+
+      /// Constructor
+      DistanceCalculatorSaggingOn(const std::string& saggingOptions,
+                                  LArWheelCalculator* lwc);
+
+      /// @name Geometry methods
+      /// @{
+      virtual double DistanceToTheNeutralFibre(const CLHEP::Hep3Vector &p, int fan_number) const;
+      virtual CLHEP::Hep3Vector NearestPointOnNeutralFibre(const CLHEP::Hep3Vector &p, int fan_number) const;
+      virtual double AmplitudeOfSurface(const CLHEP::Hep3Vector& P, int side, int fan_number) const;
+      /// @}
+
+    private:
+
+      double get_sagging(const CLHEP::Hep3Vector &P, int fan_number) const;
+      void init_sagging_parameters();
+
+      std::vector<std::vector<double> > m_sagging_parameter;
+      std::string m_saggingOptions;
+
+  };
+
+}
+
+#endif // __LArWheelCalculator_Impl_IDistanceCalculatorOn_H__
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/FanCalculatorFactory.cxx b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/FanCalculatorFactory.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..4b7c30941af1b6df7d3a3a46575469c046ed5332
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/FanCalculatorFactory.cxx
@@ -0,0 +1,27 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#include "FanCalculatorFactory.h"
+#include "ModuleFanCalculator.h"
+#include "WheelFanCalculator.h"
+
+namespace LArWheelCalculator_Impl
+{
+
+  IFanCalculator* FanCalculatorFactory::Create(bool isSaggingOn, bool isModule,
+                                               LArWheelCalculator* lwc)
+
+  {
+    if (isModule) {
+      return new ModuleFanCalculator(lwc);
+    }
+    if (isSaggingOn) {
+      return new WheelFanCalculator<SaggingOn_t>(lwc);
+    } else {
+      return new WheelFanCalculator<SaggingOff_t>(lwc);
+    }
+  }
+
+}
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/FanCalculatorFactory.h b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/FanCalculatorFactory.h
new file mode 100644
index 0000000000000000000000000000000000000000..e4f8f377c4e8ee5ee6395020a8a9c4fe30c57351
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/FanCalculatorFactory.h
@@ -0,0 +1,30 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef __LArWheelCalculator_Impl_FanCalculatorFactory_H__
+#define __LArWheelCalculator_Impl_FanCalculatorFactory_H__
+
+
+#include "IFanCalculator.h"
+
+class LArWheelCalculator;
+
+namespace LArWheelCalculator_Impl
+{
+
+  /// @class FanCalculatorFactory
+  /// @brief A factory for FanCalculators
+  ///
+  /// Calculator creation depends on sagging mode and wheel/module calo.
+  ///
+  class FanCalculatorFactory
+  {
+    public:
+      static IFanCalculator* Create(bool isSaggingOn, bool isModule,
+                                    LArWheelCalculator* lwc);
+  };
+
+}
+
+#endif // __LArWheelCalculator_Impl_FanCalculatorFactory_H__
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/IDistanceCalculator.h b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/IDistanceCalculator.h
new file mode 100644
index 0000000000000000000000000000000000000000..d45f4743c22d4051719065ca51c55ffb4d88c7a3
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/IDistanceCalculator.h
@@ -0,0 +1,44 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef __LArWheelCalculator_Impl_IDistanceCalculator_H__
+#define __LArWheelCalculator_Impl_IDistanceCalculator_H__
+
+
+#include "CLHEP/Vector/ThreeVector.h"
+
+namespace LArWheelCalculator_Impl
+{
+
+  /// @class IDistanceCalculator
+  /// Abstract interface for calculator classes that handle distance
+  /// calculation to parts of the LAr endcap.
+  ///
+  class IDistanceCalculator
+  {
+
+    public:
+
+      /// Virtual destructor
+      virtual ~IDistanceCalculator() {};
+
+      /// @name Geometry methods
+      /// @{
+
+      virtual double DistanceToTheNeutralFibre(const CLHEP::Hep3Vector &p,
+                                               int fan_number) const = 0;
+
+      virtual CLHEP::Hep3Vector NearestPointOnNeutralFibre(const CLHEP::Hep3Vector &p,
+                                                           int fan_number) const = 0;
+
+      virtual double AmplitudeOfSurface(const CLHEP::Hep3Vector& p, int side,
+                                        int fan_number) const = 0;
+
+      /// @}
+
+  };
+
+}
+
+#endif // __LArWheelCalculator_Impl_IDistanceCalculator_H__
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/IFanCalculator.h b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/IFanCalculator.h
new file mode 100644
index 0000000000000000000000000000000000000000..4cd70f9643594ab4df7054c5d3dab955f43b25a9
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/IFanCalculator.h
@@ -0,0 +1,42 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef __LArWheelCalculator_Impl_IFanCalculator_H__
+#define __LArWheelCalculator_Impl_IFanCalculator_H__
+
+
+#include "CLHEP/Vector/ThreeVector.h"
+
+namespace LArWheelCalculator_Impl
+{
+
+  /// @class IFanCalculator
+  /// Abstract interface for fan calculator classes that handle distance
+  /// calculation to parts of the LAr endcap.
+  ///
+  class IFanCalculator
+  {
+
+    public:
+
+      /// Virtual destructor
+      virtual ~IFanCalculator() {};
+
+      /// @name Geometry methods
+      /// @{
+
+      virtual double DistanceToTheNearestFan(CLHEP::Hep3Vector &p,
+                                             int & out_fan_number) const = 0;
+
+      virtual int PhiGapNumberForWheel(int i) const = 0;
+
+      virtual std::pair<int, int> GetPhiGapAndSide(const CLHEP::Hep3Vector &p) const = 0;
+
+      /// @}
+
+  };
+
+}
+
+#endif // __LArWheelCalculator_Impl_IFanCalculator_H__
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/ModuleFanCalculator.cxx b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/ModuleFanCalculator.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..a512fbdd5590556ea3f2c5d861847f7dd3b53bcd
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/ModuleFanCalculator.cxx
@@ -0,0 +1,106 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "ModuleFanCalculator.h"
+
+#include "GeoSpecialShapes/LArWheelCalculator.h"
+
+
+
+#ifdef HARDDEBUG
+#undef HARDDEBUG
+#endif
+
+namespace LArWheelCalculator_Impl
+{
+
+  ModuleFanCalculator::ModuleFanCalculator(LArWheelCalculator* lwc)
+    : m_lwc(lwc)
+  {
+  }
+
+  double ModuleFanCalculator::DistanceToTheNearestFan(CLHEP::Hep3Vector &p, int & out_fan_number) const
+  {
+    static const double halfpi=M_PI/2.0;
+    int fan_number = int((p.phi() - halfpi - lwc()->m_ZeroFanPhi_ForDetNeaFan) / lwc()->m_FanStepOnPhi);
+    double angle = lwc()->m_FanStepOnPhi * fan_number + lwc()->m_ZeroFanPhi_ForDetNeaFan;
+#ifdef HARDDEBUG
+    printf("DistanceToTheNearestFan: initial FN %4d\n", fan_number);
+#endif
+    p.rotateZ(-angle);
+    // determine search direction
+    double d0 = lwc()->DistanceToTheNeutralFibre(p, lwc()->adjust_fan_number(fan_number));
+    double d1 = d0;
+    int delta = 1;
+    if(d0 < 0.) delta = -1; // search direction has been determined
+    angle = - lwc()->m_FanStepOnPhi * delta;
+    do { // search:
+      p.rotateZ(angle);
+      fan_number += delta;
+      d1 = lwc()->DistanceToTheNeutralFibre(p, lwc()->adjust_fan_number(fan_number));
+    } while(d0 * d1 > 0.);
+    // if signs of d1 and d0 are different, the point is between current pair
+    if(delta > 0) fan_number --;
+
+    int adj_fan_number = fan_number;
+    if(adj_fan_number < lwc()->m_FirstFan) {
+      p.rotateZ((adj_fan_number - lwc()->m_FirstFan) * lwc()->m_FanStepOnPhi);
+      adj_fan_number = lwc()->m_FirstFan;
+    } else if(adj_fan_number >= lwc()->m_LastFan) {
+      p.rotateZ((adj_fan_number - lwc()->m_LastFan + 1) * lwc()->m_FanStepOnPhi);
+      adj_fan_number = lwc()->m_LastFan - 1;
+    }
+
+    p.rotateZ(-0.5 * angle);
+    out_fan_number = adj_fan_number;
+    return lwc()->DistanceToTheNeutralFibre(p, adj_fan_number);
+  }
+
+  int ModuleFanCalculator::PhiGapNumberForWheel(int i) const {
+    i += lwc()->m_ZeroGapNumber;
+    i -= lwc()->m_LastFan / 2;
+    if(i < 0) i += lwc()->m_NumberOfFans;
+    if(i >= lwc()->m_NumberOfFans) i -= lwc()->m_NumberOfFans;
+    return i;
+  }
+
+  std::pair<int, int> ModuleFanCalculator::GetPhiGapAndSide(const CLHEP::Hep3Vector &p) const
+  {
+    // Note: this object was changed from static to local for thread-safety.
+    // If this is found to be too costly we can re-evaluate.
+    CLHEP::Hep3Vector p1 = p;
+    static const double halfpi=M_PI/2.0;
+    int fan_number = int((p.phi() - halfpi - lwc()->m_ZeroFanPhi) / lwc()->m_FanStepOnPhi);
+
+    double angle = lwc()->m_FanStepOnPhi * fan_number + lwc()->m_ZeroFanPhi;
+    p1.rotateZ(-angle);
+
+    double d0 = lwc()->DistanceToTheNeutralFibre(p1, lwc()->adjust_fan_number(fan_number));
+    double d1 = d0;
+
+    int delta = 1;
+    if(d0 < 0.) delta = -1;
+    angle = - lwc()->m_FanStepOnPhi * delta;
+    do {
+      p1.rotateZ(angle);
+      fan_number += delta;
+      d1 = lwc()->DistanceToTheNeutralFibre(p1, lwc()->adjust_fan_number(fan_number));
+    } while(d0 * d1 > 0.);
+    if(delta > 0) fan_number --;
+    if(!lwc()->m_isElectrode) fan_number ++;
+
+    int adj_fan_number = fan_number;
+    if(adj_fan_number < lwc()->m_FirstFan) adj_fan_number = lwc()->m_FirstFan - 1;
+    else if(adj_fan_number > lwc()->m_LastFan) adj_fan_number = lwc()->m_LastFan;
+
+    p1.rotateZ(-0.5 * angle);
+    double dd = lwc()->DistanceToTheNeutralFibre(p1, adj_fan_number);
+    int side = dd < 0.? -1: 1;
+#ifdef HARDDEBUG
+    printf("GetPhiGapAndSide: MFN %4d\n", adj_fan_number);
+#endif
+    return std::pair<int, int>(adj_fan_number, side);
+  }
+
+}
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/ModuleFanCalculator.h b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/ModuleFanCalculator.h
new file mode 100644
index 0000000000000000000000000000000000000000..b9e3987af6bbcbbbe818804d0e1d64f777fef799
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/ModuleFanCalculator.h
@@ -0,0 +1,35 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef __LArWheelCalculator_Impl_ModuleFanCalculator_H__
+#define __LArWheelCalculator_Impl_ModuleFanCalculator_H__
+
+#include "IFanCalculator.h"
+
+class LArWheelCalculator;
+
+namespace LArWheelCalculator_Impl
+{
+
+  /// This is an interface of distance calculation to parts of the LAr endcap.
+  class ModuleFanCalculator : public IFanCalculator
+  {
+    public:
+      ModuleFanCalculator(LArWheelCalculator* lwc);
+
+      // geometry methods:
+      virtual double DistanceToTheNearestFan(CLHEP::Hep3Vector &p, int & out_fan_number) const;
+      virtual int PhiGapNumberForWheel(int i) const;
+      virtual std::pair<int, int> GetPhiGapAndSide(const CLHEP::Hep3Vector &p) const;
+
+      inline const LArWheelCalculator *lwc() const { return m_lwc; };
+
+    private:
+      LArWheelCalculator* m_lwc;
+
+  };
+
+}
+
+#endif // __LArWheelCalculator_Impl_ModuleFanCalculator_H__
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/WheelFanCalculator.h b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/WheelFanCalculator.h
new file mode 100644
index 0000000000000000000000000000000000000000..12c9a5a716a2929aeef76d69a3f4d6534ec989ac
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/WheelFanCalculator.h
@@ -0,0 +1,264 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef LARWHEELCALCULATOR_IMPL_WHEELFANCALCULATOR_H
+#define LARWHEELCALCULATOR_IMPL_WHEELFANCALCULATOR_H
+
+#include "IFanCalculator.h"
+#include "GeoSpecialShapes/LArWheelCalculator.h"
+
+#ifdef HARDDEBUG
+#undef HARDDEBUG
+#endif
+
+namespace LArWheelCalculator_Impl
+{
+
+  // mode marker classes
+  class SaggingOn_t {};
+  class SaggingOff_t {};
+
+  template <typename SaggingType>
+  class DistanceToTheNeutralFibre_OfFan {};
+
+  template<>
+  class DistanceToTheNeutralFibre_OfFan<SaggingOn_t>
+  {
+    public:
+      static inline double calculate(const LArWheelCalculator* lwc, int fan_number,     CLHEP::Hep3Vector &p) {
+        //lwc->set_m_fan_number(fan_number);
+        return lwc->DistanceToTheNeutralFibre(p, lwc->adjust_fan_number(fan_number));
+      }
+  };
+
+  template<> class DistanceToTheNeutralFibre_OfFan<SaggingOff_t>
+  {
+    public:
+      static inline double calculate(const LArWheelCalculator* lwc, int /*fan_number*/, CLHEP::Hep3Vector &p) {
+        // saggingOff distance calculations does not use fan_number, use arbitrary recognisible magic number
+        return lwc->DistanceToTheNeutralFibre(p, -531135);
+      }
+  };
+
+  enum FanSearchDirection_t {
+    FORWARD = 1,    // delta =  1
+    BACKWARD = -1   // delta = -1
+  };
+
+
+  template <typename SaggingType, FanSearchDirection_t dir >
+  class StepFan {};
+
+
+  template <FanSearchDirection_t dir >
+  class StepFan<SaggingOff_t, dir>
+  {
+    public:
+      static inline void next(int &/*fan_number*/) {}
+      static inline void adjust(int &/*fan_number*/) {}
+  };
+
+
+  template <>
+  class StepFan<SaggingOn_t, FORWARD>
+  {
+    public:
+      static inline void next(int &fan_number) {
+        fan_number++;
+      }
+      static inline void adjust(int &fan_number) {
+        fan_number--;
+      }
+  };
+
+  template <>
+  class StepFan<SaggingOn_t, BACKWARD>
+  {
+    public:
+      static inline void next(int &fan_number) {
+        fan_number--;
+      }
+      static inline void adjust(int &/*fan_number*/) {}
+  };
+
+  template <FanSearchDirection_t dir>
+  class DoSearch {};
+
+  template <>
+  class DoSearch<FORWARD>
+  {
+    public:
+      template <typename T >
+        static inline bool pred(T val) {
+          return (val > 0.);
+        }
+  };
+
+  template <>
+  class DoSearch<BACKWARD>
+  {
+    public:
+      template <typename T >
+        static inline bool pred(T val) {
+          return (val < 0.);
+        }
+  };
+
+  /// @todo Needs documentation
+  template <typename SaggingType, FanSearchDirection_t dir, class NFDistance >
+  inline void rotate_to_nearest_fan(const LArWheelCalculator* lwc, int &fan_number,
+                                    const double angle, CLHEP::Hep3Vector &p)
+  {
+    p.rotateZ(angle);
+    StepFan<SaggingType, dir>::next(fan_number);
+    //fan_number += delta;
+    double d1 = NFDistance::calculate(lwc, fan_number, p);
+
+    //while(d0 * d1 > 0.) -> dir*d1 > 0 -> FORWARD: d1 > 0., BACKWARD: d1 < 0.
+
+    while ( DoSearch<dir>::pred(d1) ) { // search:
+      p.rotateZ(angle);
+      StepFan<SaggingType, dir>::next(fan_number);
+      //fan_number += delta;
+
+      d1 = NFDistance::calculate(lwc, fan_number, p);
+      //lwc()->set_m_fan_number(fan_number);
+      //d1 = lwc()->DistanceToTheNeutralFibre(p);
+
+    }
+    // if signs of d1 and d0 are different, the point is between current pair
+    StepFan<SaggingType, dir>::adjust(fan_number);
+    //if(delta > 0) fan_number --;
+  }
+
+
+  /// LAr wheel fan calculator, templated for sagging settings.
+  ///
+  template <typename SaggingType>
+  class WheelFanCalculator : public IFanCalculator
+  {
+    public:
+      WheelFanCalculator(LArWheelCalculator* lwc)
+        : m_lwc(lwc)
+      {
+      }
+
+      /// @name Geometry methods
+      /// @{
+
+      virtual double DistanceToTheNearestFan(CLHEP::Hep3Vector &p, int & out_fan_number) const
+      {
+        static const double halfpi=M_PI/2.0;
+        int fan_number = int((p.phi() - halfpi - lwc()->m_ZeroFanPhi_ForDetNeaFan) / lwc()->m_FanStepOnPhi);
+        const double angle = lwc()->m_FanStepOnPhi * fan_number + lwc()->m_ZeroFanPhi_ForDetNeaFan;
+#ifdef HARDDEBUG
+        printf("DistanceToTheNearestFan: initial FN %4d\n", fan_number);
+#endif
+        p.rotateZ(-angle);
+        // determine search direction
+        typedef DistanceToTheNeutralFibre_OfFan<SaggingType> NFDistance;
+
+        const double d0 = NFDistance::calculate(lwc(), fan_number, p);
+        //lwc()->set_m_fan_number(fan_number);
+        //double d0 = lwc()->DistanceToTheNeutralFibre(p);
+
+        const int delta = (d0 < 0.) ? -1 : 1;
+        //int delta = 1;          // delta = signof(d0)
+        //if(d0 < 0.) delta = -1; // search direction has been determined
+
+        const double step_angle = - lwc()->m_FanStepOnPhi * delta;
+
+        if (delta > 0) { // forward search
+          rotate_to_nearest_fan< SaggingType, FORWARD, NFDistance >( lwc(), fan_number, step_angle, p);
+        } else { // backward search
+          rotate_to_nearest_fan< SaggingType, BACKWARD, NFDistance >( lwc(), fan_number, step_angle, p);
+        }
+
+        /*
+        double d1 = d0;
+        do { // search:
+          p.rotateZ(angle);
+          fan_number += delta;
+
+          d1 = NFDistance::calculate(lwc(), fan_number, p);
+          //lwc()->set_m_fan_number(fan_number);
+          //d1 = lwc()->DistanceToTheNeutralFibre(p);
+
+#ifdef HARDDEBUG
+          printf("DistanceToTheNearestFan: step FN %4d %4d\n", fan_number, lwc()->m_fan_number);
+#endif
+        } while(d0 * d1 > 0.);
+        // if signs of d1 and d0 are different, the point is between current pair
+        if(delta > 0) fan_number --;
+        */
+
+        p.rotateZ(-0.5 * step_angle);
+#ifdef HARDDEBUG
+        printf("DistanceToTheNearestFan: final FN %4d\n", fan_number);
+#endif
+
+        out_fan_number = lwc()->adjust_fan_number(fan_number);
+        return lwc()->DistanceToTheNeutralFibre(p, out_fan_number);
+      }
+
+      virtual int PhiGapNumberForWheel(int i) const
+      {
+        return i;
+      }
+
+      virtual std::pair<int, int> GetPhiGapAndSide(const CLHEP::Hep3Vector &p) const
+      {
+	static const double halfpi=M_PI/2.0;
+        CLHEP::Hep3Vector p1 = p;
+
+        int fan_number = int((p.phi() - halfpi - lwc()->m_ZeroFanPhi) / lwc()->m_FanStepOnPhi);
+        const double angle = lwc()->m_FanStepOnPhi * fan_number + lwc()->m_ZeroFanPhi;
+        p1.rotateZ(-angle);
+
+        typedef DistanceToTheNeutralFibre_OfFan<SaggingType> NFDistance;
+
+        const double d0 = NFDistance::calculate(lwc(), fan_number, p1);
+        //lwc()->set_m_fan_number(fan_number);
+        //double d0 = lwc()->DistanceToTheNeutralFibre(p1);
+
+        double d1 = d0;
+
+        const int delta = (d0 < 0.) ? -1 : 1;
+        //int delta = 1;
+        //if(d0 < 0.) delta = -1;
+        const double step_angle = - lwc()->m_FanStepOnPhi * delta;
+        do {
+          p1.rotateZ(step_angle);
+          fan_number += delta;
+          d1 = NFDistance::calculate(lwc(), fan_number, p1);
+          //lwc()->set_m_fan_number(fan_number);
+          //d1 = lwc()->DistanceToTheNeutralFibre(p1);
+        } while(d0 * d1 > 0.);
+
+        if(delta > 0) fan_number --;
+        if(!lwc()->m_isElectrode) fan_number ++;
+
+        p1.rotateZ(-0.5 * step_angle);
+
+        const int a_fan_number = lwc()->adjust_fan_number(fan_number);
+        double dd = lwc()->DistanceToTheNeutralFibre(p1, a_fan_number);
+        int side = dd < 0.? -1: 1;
+#ifdef HARDDEBUG
+        printf("GetPhiGapAndSide: MFN %4d\n", a_fan_number);
+#endif
+        return std::pair<int, int>(a_fan_number, side);
+      }
+
+      /// @}
+
+      inline const LArWheelCalculator *lwc() const { return m_lwc; };
+
+    private:
+      LArWheelCalculator* m_lwc;
+
+  };
+
+}
+
+#endif // LARWHEELCALCULATOR_IMPL_WHEELFANCALCULATOR_H
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/sincos_poly.cxx b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/sincos_poly.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..b9e35d653962f6da89d25a7953afefaac62ce7f2
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/DetectorDescription/GeoModel/GeoSpecialShapes/src/LArWheelCalculator_Impl/sincos_poly.cxx
@@ -0,0 +1,298 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "GeoSpecialShapes/LArWheelCalculator.h"
+
+#include "CxxUtils/sincos.h"
+
+#ifdef PORTABLE_LAR_SHAPE
+#define SINCOSPOLY_USE_EIGEN 1
+#endif
+
+#ifndef SINCOSPOLY_USE_EIGEN
+#include "TMath.h"
+#include "TMatrixD.h"
+#include "TVectorD.h"
+#include "TMatrixDLazy.h"
+#include "TDecompLU.h"
+#include "TDecompSVD.h"
+typedef TVectorD     VECTOR;
+typedef TMatrixD     MATRIX;
+typedef TMatrixDSym  SYMMATRIX;
+#else
+#include "Eigen/Dense"
+typedef Eigen::VectorXd  VECTOR;
+typedef Eigen::MatrixXd  MATRIX;
+typedef Eigen::MatrixXd  SYMMATRIX;
+#define Int_t    int
+#define Double_t double
+#endif
+
+#include <sys/time.h>
+#include <iostream>
+#include <iomanip>
+#include <math.h>
+#include <mutex>
+
+#define DEBUGPRINT 0
+#include "GeoModelKernel/Units.h"
+using namespace GeoModelKernelUnits;
+
+#ifndef SINCOSPOLY_USE_EIGEN
+template<typename T, typename Q>
+std::ostream & operator << (std::ostream & ostr, const TVectorT<T> & v)
+{
+  std::ios_base::fmtflags save_flags(ostr.flags());
+  ostr << '[';
+  ostr << std::scientific;
+  for(Int_t idx=v.GetLwb();idx<v.GetUpb();idx++) {
+    ostr << v[idx] << ", ";
+  }
+  ostr << v[v.GetUpb()];
+  ostr << ']';
+  ostr.flags(save_flags);
+  return ostr;
+}
+#endif
+
+// find best approximation of y values using linear combination of basis functions in bf
+static VECTOR findLinearApproximation(
+    const Int_t dataLen, const Int_t nBasisFuntions,
+    const VECTOR &y, const MATRIX & bf)
+{
+#ifndef SINCOSPOLY_USE_EIGEN
+  SYMMATRIX A(nBasisFuntions);
+  VECTOR    vY(nBasisFuntions);
+#else
+  SYMMATRIX A=SYMMATRIX::Zero(nBasisFuntions,nBasisFuntions);
+  VECTOR    vY=VECTOR::Zero(nBasisFuntions);
+#endif
+  for(Int_t j = 0; j < nBasisFuntions; ++ j){
+    for(Int_t k = 0; k < nBasisFuntions; ++ k){
+      Double_t Ajk = 0.0;
+      for(Int_t i = 0; i < dataLen; ++ i){
+        Ajk += bf(j, i) * bf(k, i);
+      }
+      A(j, k) = Ajk;
+    }
+  }
+
+  for(Int_t k = 0; k < nBasisFuntions; ++ k){
+    Double_t vYk = 0.0;
+    for(Int_t i = 0; i < dataLen; ++ i){
+      vYk += y[i]*bf(k,i);
+    }
+    vY[k] = vYk;
+  }
+#ifndef SINCOSPOLY_USE_EIGEN 
+  SYMMATRIX Ainv(A);
+  Ainv.Invert();
+  return Ainv*vY;
+#else
+  return A.inverse()*vY;
+#endif
+}
+
+using namespace CLHEP;
+
+void LArWheelCalculator::fill_sincos_parameterization()
+{
+  const Int_t nrPolyDegree = LARWC_SINCOS_POLY;
+#if LARWC_SINCOS_POLY > 4 && DEBUGPRINT
+  std::cout << "LARWC_SINCOS_POLY: " << LARWC_SINCOS_POLY << std::endl;
+#endif
+  const Int_t nBasisFunctions = nrPolyDegree + 1;
+
+  // We compute the polynomial approximations once per side, and store them in
+  // the static variables below for reuse in successive calculator instances.
+  // For multi-threading, then, this code needs to be mutex locked.
+  // FIXME: this could done in a cleaner way.
+  static std::mutex fillParamMutex;
+  std::lock_guard<std::mutex> lock(fillParamMutex);
+
+  static bool filled[2] = { false, false };
+  static double sin_parametrization[2][nBasisFunctions];
+  static double cos_parametrization[2][nBasisFunctions];
+
+  // Reuse the computation if already performed
+  size_t S = m_isInner? 0: 1;
+  if(filled[S]){
+    for(Int_t i = 0; i < nBasisFunctions; ++ i){
+      m_sin_parametrization[i] = sin_parametrization[S][i];
+      m_cos_parametrization[i] = cos_parametrization[S][i];
+    }
+
+    // Parameterization for the vectorized sincos calculation
+    // see ATLASSIM-4753 for details
+    // s4, s5, c4, c5
+    // s2, s3, c2, c3
+    // s0, s1, c0, c1
+    m_vsincos_par.param_0[0] = m_sin_parametrization[4];
+    m_vsincos_par.param_0[1] = m_sin_parametrization[5];
+    m_vsincos_par.param_0[2] = m_cos_parametrization[4];
+    m_vsincos_par.param_0[3] = m_cos_parametrization[5];
+    m_vsincos_par.param_1[0] = m_sin_parametrization[2];
+    m_vsincos_par.param_1[1] = m_sin_parametrization[3];
+    m_vsincos_par.param_1[2] = m_cos_parametrization[2];
+    m_vsincos_par.param_1[3] = m_cos_parametrization[3];
+    m_vsincos_par.param_2[0] = m_sin_parametrization[0];
+    m_vsincos_par.param_2[1] = m_sin_parametrization[1];
+    m_vsincos_par.param_2[2] = m_cos_parametrization[0];
+    m_vsincos_par.param_2[3] = m_cos_parametrization[1];
+    return;
+  }
+
+  //const Double_t Rmin = m_isInner? 290.*mm: 600.*mm;
+  //const Double_t Rmax = m_isInner? 710.*mm: 2050.*mm;
+  const Double_t Rmin = m_isInner? 250.*mm: 560.*mm;
+  const Double_t Rmax = m_isInner? 750.*mm: 2090.*mm;
+  //const Double_t Rmin = m_isInner? 220.*mm: 530.*mm;
+  //const Double_t Rmax = m_isInner? 780.*mm: 2120.*mm;
+  const Double_t Rstep = 1.*mm;
+  const Int_t nrPoints = (Rmax - Rmin) * (1./Rstep);
+  const Int_t dataLen = nrPoints + 1;
+#ifndef SINCOSPOLY_USE_EIGEN
+  VECTOR x(dataLen);  // angle points
+  VECTOR ysin(dataLen);  // to be approximated function values at angle points - sin
+  VECTOR ycos(dataLen);  // to be approximated function values at angle points - cos
+  MATRIX bf(nBasisFunctions, dataLen); // Matrix of values of basis functions at angle points
+#else
+  VECTOR x=VECTOR::Zero(dataLen);  // angle points
+  VECTOR ysin=VECTOR::Zero(dataLen);  // to be approximated function values at angle points - sin
+  VECTOR ycos=VECTOR::Zero(dataLen);  // to be approximated function values at angle points - cos
+  MATRIX bf=MATRIX::Zero(nBasisFunctions, dataLen); // Matrix of values of basis functions at angle points
+#endif
+  for(Int_t i = 0; i < dataLen; ++ i){
+    const Double_t a = Rmin + i * Rstep;
+    x[i] = a;
+    CxxUtils::sincos scalpha(parameterized_slant_angle(a));
+    ysin[i] = scalpha.sn;
+    ycos[i] = scalpha.cs;
+    for(Int_t n = 0; n < nBasisFunctions; ++ n) {
+      bf(n, i) = pow(a, n);
+    }
+  }
+
+  VECTOR params_sin =
+    findLinearApproximation(dataLen, nBasisFunctions, ysin, bf);
+  VECTOR params_cos =
+    findLinearApproximation(dataLen, nBasisFunctions, ycos, bf);
+
+  for(Int_t i = 0; i < nBasisFunctions; ++ i){
+    m_sin_parametrization[i] = params_sin[i];
+    m_cos_parametrization[i] = params_cos[i];
+    sin_parametrization[S][i] = params_sin[i];
+    cos_parametrization[S][i] = params_cos[i];
+  }
+
+  // Parameterization for the vectorized sincos calculation
+  // see ATLASSIM-4753 for details
+  // s4, s5, c4, c5
+  // s2, s3, c2, c3
+  // s0, s1, c0, c1
+  m_vsincos_par.param_0[0] = m_sin_parametrization[4];
+  m_vsincos_par.param_0[1] = m_sin_parametrization[5];
+  m_vsincos_par.param_0[2] = m_cos_parametrization[4];
+  m_vsincos_par.param_0[3] = m_cos_parametrization[5];
+  m_vsincos_par.param_1[0] = m_sin_parametrization[2];
+  m_vsincos_par.param_1[1] = m_sin_parametrization[3];
+  m_vsincos_par.param_1[2] = m_cos_parametrization[2];
+  m_vsincos_par.param_1[3] = m_cos_parametrization[3];
+  m_vsincos_par.param_2[0] = m_sin_parametrization[0];
+  m_vsincos_par.param_2[1] = m_sin_parametrization[1];
+  m_vsincos_par.param_2[2] = m_cos_parametrization[0];
+  m_vsincos_par.param_2[3] = m_cos_parametrization[1];
+  filled[S] = true;
+
+  // FIXME: nothing below is needed unless debug printing
+
+#if DEBUGPRINT
+  std::cout << "sin params:" << params_sin << std::endl;
+  std::cout << "cos params:" << params_cos << std::endl;
+
+  double dsinr = 0., dcosr = 0.;
+  double dtrigr = 0;
+#endif
+
+  double dsin = 0., dcos = 0.;
+  double dtrig = 0.;
+  for(double r = Rmin + 40.; r < Rmax - 40.; r += Rstep / 10.){
+    CxxUtils::sincos scalpha(parameterized_slant_angle(r));
+    double sin_a, cos_a;
+    double sin_a_v, cos_a_v;
+    parameterized_sincos(r, sin_a, cos_a);
+    m_vsincos_par.eval(r, sin_a_v, cos_a_v);
+#if DEBUGPRINT
+    std::streamsize ss = std::cout.precision();
+    std::cout.precision(16);
+    std::cout << "def: " << r << " " << sin_a << " " << cos_a << std::endl;
+    std::cout << "vec: " << r << " " << sin_a_v << " " << cos_a_v << std::endl;
+    std::cout << "dif: " << r << " " << (sin_a - sin_a_v) / sin_a << " " << (cos_a - cos_a_v) / cos_a << std::endl;
+    std::cout.precision(ss);
+#endif
+    double ds = fabs(scalpha.sn - sin_a);
+    if(ds > dsin){
+      dsin = ds;
+#if DEBUGPRINT
+      dsinr = r;
+#endif
+    }
+    double dc = fabs(scalpha.cs - cos_a);
+    if(dc > dcos){
+      dcos = dc;
+#if DEBUGPRINT
+      dcosr = r;
+#endif
+    }
+    double dt = fabs(sin_a*sin_a + cos_a*cos_a - 1.);
+    if(dt > dtrig){
+      dtrig = dt;
+#if DEBUGPRINT
+      dtrigr = r;
+#endif
+    }
+  }
+
+#if DEBUGPRINT
+  std::cout << "Max. difference: " << std::endl
+            << "\tsin: " << dsin << " at " << dsinr << std::endl
+            << "\tcos: " << dcos << " at " << dcosr << std::endl
+            << "\tsin^2+cos^2: " << dtrig << " at " << dtrigr << std::endl;
+#endif
+
+#ifdef HARDDEBUG
+  TVectorD y_test(dataLen);
+  const Int_t nIter=10000;
+  std::cout << "Perfomance test started, " << nIter << " iterations" << std::endl;
+
+  double y_testsin[dataLen];
+  double y_testcos[dataLen];
+  struct timeval tvsincos_start, tvsincos_stop;
+  gettimeofday(&tvsincos_start, 0);
+  for(Int_t iIter=0;iIter<nIter;iIter++) {
+    for(Int_t i=0;i<dataLen;i++) {
+      sincos(parameterized_slant_angle(x[i]), &y_testsin[i], &y_testcos[i]);
+    }
+  }
+  gettimeofday(&tvsincos_stop, 0);
+  double timeSinCos=(tvsincos_stop.tv_sec-tvsincos_start.tv_sec + 1E-6*(tvsincos_stop.tv_usec-tvsincos_start.tv_usec))/nIter;
+
+  std::cout.unsetf ( std::ios::fixed | std::ios::scientific );
+  std::cout << "Time to fill 2x" << dataLen << " elements using sincos function: " << timeSinCos << std::endl;
+
+  struct timeval tvpoly_start, tvpoly_stop;
+  gettimeofday(&tvpoly_start, 0);
+  for(Int_t iIter=0;iIter<nIter;iIter++) {
+    for(Int_t i=0;i<dataLen;i++) {
+      parameterized_sincos(x[i], y_testsin[i], y_testcos[i]);
+    }
+  }
+  gettimeofday(&tvpoly_stop, 0);
+  double timePoly=(tvpoly_stop.tv_sec - tvpoly_start.tv_sec + 1E-6*(tvpoly_stop.tv_usec - tvpoly_start.tv_usec))/nIter;
+  std::cout << "Time to fill 2x" << dataLen << " elements using approximation sin&cos: " << timePoly << std::endl;
+  std::cout.unsetf ( std::ios::fixed | std::ios::scientific );
+  std::cout << "Approximation is " << timeSinCos/timePoly << " faster " << std::endl;
+#endif
+
+}
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/G4ShiftedCone.cxx b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/G4ShiftedCone.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..82743f9a2f8f37ee804520b2257273f63d4a5d6b
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/G4ShiftedCone.cxx
@@ -0,0 +1,2266 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+//
+// The G4ShiftedCone class copied from standard G4Cons and modified to:
+// 1) have an arbitrary position along Z axis,
+// 2) represent a twopi-cone. Sectors are not supported but the
+//    corresponding code kept and just commented out.
+//
+
+//
+// ********************************************************************
+// * License and Disclaimer                                           *
+// *                                                                  *
+// * The  Geant4 software  is  copyright of the Copyright Holders  of *
+// * the Geant4 Collaboration.  It is provided  under  the terms  and *
+// * conditions of the Geant4 Software License,  included in the file *
+// * LICENSE and available at  http://cern.ch/geant4/license .  These *
+// * include a list of copyright holders.                             *
+// *                                                                  *
+// * Neither the authors of this software system, nor their employing *
+// * institutes,nor the agencies providing financial support for this *
+// * work  make  any representation or  warranty, express or implied, *
+// * regarding  this  software system or assume any liability for its *
+// * use.  Please see the license in the file  LICENSE  and URL above *
+// * for the full disclaimer and the limitation of liability.         *
+// *                                                                  *
+// * This  code  implementation is the result of  the  scientific and *
+// * technical work of the GEANT4 collaboration.                      *
+// * By using,  copying,  modifying or  distributing the software (or *
+// * any work based  on the software)  you  agree  to acknowledge its *
+// * use  in  resulting  scientific  publications,  and indicate your *
+// * acceptance of all terms of the Geant4 Software license.          *
+// ********************************************************************
+//
+//
+//
+//
+// class G4ShiftedCone
+//
+// Implementation for G4ShiftedCone class
+//
+// History:
+// 03.07.2019 A. Sukharev: copied from G4Cons
+// --------------------------------------------------------------------
+
+#include "G4ShiftedCone.h"
+
+#include "G4GeomTools.hh"
+#include "G4VoxelLimits.hh"
+#include "G4AffineTransform.hh"
+#include "G4BoundingEnvelope.hh"
+#include "G4GeometryTolerance.hh"
+
+#include "G4VPVParameterisation.hh"
+
+#include "meshdefs.hh"
+
+#include "Randomize.hh"
+
+#include "G4VGraphicsScene.hh"
+
+using namespace CLHEP;
+
+////////////////////////////////////////////////////////////////////////
+//
+// Private enum: Not for external use - used by distanceToOut
+
+enum ESide {kNull,kRMin,kRMax,kSPhi,kEPhi,kPZ,kMZ};
+
+// used by normal
+
+enum ENorm {kNRMin,kNRMax,kNSPhi,kNEPhi,kNZ};
+
+//////////////////////////////////////////////////////////////////////////
+//
+// constructor - check parameters, convert angles so 0<sphi+dpshi<=2_PI
+//               - note if pDPhi>2PI then reset to 2PI
+
+G4ShiftedCone::G4ShiftedCone( const G4String& pName,
+                      G4double pZ1, G4double pZ2,
+                      G4double  pRmin1, G4double pRmax1,
+                      G4double  pRmin2, G4double pRmax2)
+//                      G4double pDz,
+//                      G4double pSPhi, G4double pDPhi)
+  : G4CSGSolid(pName),
+    kRadTolerance (G4GeometryTolerance::GetInstance()->GetRadialTolerance()),
+    kAngTolerance (G4GeometryTolerance::GetInstance()->GetAngularTolerance()),
+    fRmin1(pRmin1), fRmin2(pRmin2),
+    fRmax1(pRmax1), fRmax2(pRmax2),
+    fDz((pZ2 - pZ1) * 0.5), fZshift(pZ1 + fDz),
+    // fSPhi(0.), fDPhi(0.)
+    halfCarTolerance (kCarTolerance*0.5),
+    halfRadTolerance (kRadTolerance*0.5),
+    halfAngTolerance (kAngTolerance*0.5)
+{
+  // Check z-len
+  //
+  if ( fDz < 0 )
+  {
+    std::ostringstream message;
+    message << "Invalid Z half-length for Solid: " << GetName() << G4endl
+            << "        hZ = " << fDz;
+    G4Exception("G4ShiftedCone::G4ShiftedCone()", "GeomSolids0002",
+                FatalException, message);
+  }
+
+  // Check radii
+  //
+  if (((pRmin1>=pRmax1) || (pRmin2>=pRmax2) || (pRmin1<0)) && (pRmin2<0))
+  {
+    std::ostringstream message;
+    message << "Invalid values of radii for Solid: " << GetName() << G4endl
+            << "        pRmin1 = " << pRmin1 << ", pRmin2 = " << pRmin2
+            << ", pRmax1 = " << pRmax1 << ", pRmax2 = " << pRmax2;
+    G4Exception("G4ShiftedCone::G4ShiftedCone()", "GeomSolids0002",
+                FatalException, message) ;
+  }
+  if( (pRmin1 == 0.0) && (pRmin2 > 0.0) ) { fRmin1 = 1e3*kRadTolerance ; }
+  if( (pRmin2 == 0.0) && (pRmin1 > 0.0) ) { fRmin2 = 1e3*kRadTolerance ; }
+
+  // Check angles
+  //
+//  CheckPhiAngles(pSPhi, pDPhi);
+}
+
+///////////////////////////////////////////////////////////////////////
+//
+// Fake default constructor - sets only member data and allocates memory
+//                            for usage restricted to object persistency.
+//
+G4ShiftedCone::G4ShiftedCone( __void__& a )
+  : G4CSGSolid(a), kRadTolerance(0.), kAngTolerance(0.),
+    fRmin1(0.), fRmin2(0.), fRmax1(0.), fRmax2(0.),
+    fDz(0.), fZshift(0.),
+//    fSPhi(0.), fDPhi(0.), sinCPhi(0.), cosCPhi(0.), cosHDPhiOT(0.),
+//    cosHDPhiIT(0.), sinSPhi(0.), cosSPhi(0.), sinEPhi(0.), cosEPhi(0.),
+//    fPhiFullCone(false),
+    halfCarTolerance(0.), halfRadTolerance(0.), halfAngTolerance(0.)
+{
+}
+
+///////////////////////////////////////////////////////////////////////
+//
+// Destructor
+
+G4ShiftedCone::~G4ShiftedCone()
+{
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Copy constructor
+
+G4ShiftedCone::G4ShiftedCone(const G4ShiftedCone& rhs)
+  : G4CSGSolid(rhs), kRadTolerance(rhs.kRadTolerance),
+    kAngTolerance(rhs.kAngTolerance), fRmin1(rhs.fRmin1), fRmin2(rhs.fRmin2),
+    fRmax1(rhs.fRmax1), fRmax2(rhs.fRmax2), fDz(rhs.fDz), fZshift(rhs.fZshift),
+//    fSPhi(rhs.fSPhi),
+//    fDPhi(rhs.fDPhi), sinCPhi(rhs.sinCPhi), cosCPhi(rhs.cosCPhi),
+//    cosHDPhiOT(rhs.cosHDPhiOT), cosHDPhiIT(rhs.cosHDPhiIT),
+//    sinSPhi(rhs.sinSPhi), cosSPhi(rhs.cosSPhi), sinEPhi(rhs.sinEPhi),
+//    cosEPhi(rhs.cosEPhi), fPhiFullCone(rhs.fPhiFullCone),
+    halfCarTolerance(rhs.halfCarTolerance),
+    halfRadTolerance(rhs.halfRadTolerance),
+    halfAngTolerance(rhs.halfAngTolerance)
+{
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Assignment operator
+
+G4ShiftedCone& G4ShiftedCone::operator = (const G4ShiftedCone& rhs)
+{
+   // Check assignment to self
+   //
+   if (this == &rhs)  { return *this; }
+
+   // Copy base class data
+   //
+   G4CSGSolid::operator=(rhs);
+
+   // Copy data
+   //
+   kRadTolerance = rhs.kRadTolerance;
+   kAngTolerance = rhs.kAngTolerance;
+   fRmin1 = rhs.fRmin1; fRmin2 = rhs.fRmin2;
+   fRmax1 = rhs.fRmax1; fRmax2 = rhs.fRmax2;
+   fDz = rhs.fDz; fZshift = rhs.fZshift;
+//   fSPhi = rhs.fSPhi; fDPhi = rhs.fDPhi;
+//   sinCPhi = rhs.sinCPhi; cosCPhi = rhs.cosCPhi;
+//   cosHDPhiOT = rhs.cosHDPhiOT; cosHDPhiIT = rhs.cosHDPhiIT;
+//   sinSPhi = rhs.sinSPhi; cosSPhi = rhs.cosSPhi;
+//   sinEPhi = rhs.sinEPhi; cosEPhi = rhs.cosEPhi;
+//   fPhiFullCone = rhs.fPhiFullCone;
+   halfCarTolerance = rhs.halfCarTolerance;
+   halfRadTolerance = rhs.halfRadTolerance;
+   halfAngTolerance = rhs.halfAngTolerance;
+
+   return *this;
+}
+
+/////////////////////////////////////////////////////////////////////
+//
+// Return whether point inside/outside/on surface
+
+EInside G4ShiftedCone::Inside(const G4ThreeVector& p) const
+{
+  G4double r2, rl, rh, /*pPhi,*/ tolRMin, tolRMax; // rh2, rl2 ;
+  EInside in;
+
+  G4double z = p.z() - fZshift;
+
+  if (std::fabs(z) > fDz + halfCarTolerance )  { return in = kOutside; }
+  else if(std::fabs(z) >= fDz - halfCarTolerance )    { in = kSurface; }
+  else                                                    { in = kInside;  }
+
+  r2 = p.x()*p.x() + p.y()*p.y() ;
+  rl = 0.5*(fRmin2*(z + fDz) + fRmin1*(fDz - z))/fDz ;
+  rh = 0.5*(fRmax2*(z+fDz)+fRmax1*(fDz-z))/fDz;
+
+  // rh2 = rh*rh;
+
+  tolRMin = rl - halfRadTolerance;
+  if ( tolRMin < 0 )  { tolRMin = 0; }
+  tolRMax = rh + halfRadTolerance;
+
+  if ( (r2<tolRMin*tolRMin) || (r2>tolRMax*tolRMax) ) { return in = kOutside; }
+
+  if (rl) { tolRMin = rl + halfRadTolerance; }
+  else    { tolRMin = 0.0; }
+  tolRMax = rh - halfRadTolerance;
+
+  if (in == kInside) // else it's kSurface already
+  {
+     if ( (r2 < tolRMin*tolRMin) || (r2 >= tolRMax*tolRMax) ) { in = kSurface; }
+  }
+/*
+  if ( !fPhiFullCone && ((p.x() != 0.0) || (p.y() != 0.0)) )
+  {
+    pPhi = std::atan2(p.y(),p.x()) ;
+
+    if ( pPhi < fSPhi - halfAngTolerance  )             { pPhi += twopi; }
+    else if ( pPhi > fSPhi + fDPhi + halfAngTolerance ) { pPhi -= twopi; }
+
+    if ( (pPhi < fSPhi - halfAngTolerance) ||
+         (pPhi > fSPhi + fDPhi + halfAngTolerance) )  { return in = kOutside; }
+
+    else if (in == kInside)  // else it's kSurface anyway already
+    {
+       if ( (pPhi < fSPhi + halfAngTolerance) ||
+            (pPhi > fSPhi + fDPhi - halfAngTolerance) )  { in = kSurface; }
+    }
+  }
+  else if ( !fPhiFullCone )  { in = kSurface; }
+*/
+  return in ;
+}
+
+/////////////////////////////////////////////////////////////////////////
+//
+// Dispatch to parameterisation for replication mechanism dimension
+// computation & modification.
+
+void G4ShiftedCone::ComputeDimensions(      G4VPVParameterisation*,// p,
+                               const G4int                  ,//n,
+                               const G4VPhysicalVolume*     )//pRep    )
+{
+    std::ostringstream message;
+    message << "ComputeDimensions is not implemented for Solid: " << GetName();
+    G4Exception("G4ShiftedCone::ComputeDimensions()", "GeomSolids0002",
+                FatalException, message) ;
+//  p->ComputeDimensions(*this,n,pRep) ;
+}
+
+///////////////////////////////////////////////////////////////////////
+//
+// Get bounding box
+
+void G4ShiftedCone::BoundingLimits(G4ThreeVector& pMin, G4ThreeVector& pMax) const
+{
+//  G4double rmin = std::min(GetInnerRadiusMinusZ(),GetInnerRadiusPlusZ());
+  G4double rmax = std::max(GetOuterRadiusMinusZ(),GetOuterRadiusPlusZ());
+
+  // Find bounding box
+  //
+/*  if (GetDeltaPhiAngle() < twopi)
+  {
+  G4double dz   = GetZHalfLength();
+    G4TwoVector vmin,vmax;
+    G4GeomTools::DiskExtent(rmin,rmax,
+                            GetSinStartPhi(),GetCosStartPhi(),
+                            GetSinEndPhi(),GetCosEndPhi(),
+                            vmin,vmax);
+    pMin.set(vmin.x(),vmin.y(),-dz);
+    pMax.set(vmax.x(),vmax.y(), dz);
+  }
+  else*/
+  {
+    pMin.set(-rmax,-rmax, fZshift - fDz);
+    pMax.set( rmax, rmax, fZshift + fDz);
+  }
+
+  // Check correctness of the bounding box
+  //
+  if (pMin.x() >= pMax.x() || pMin.y() >= pMax.y() || pMin.z() >= pMax.z())
+  {
+    std::ostringstream message;
+    message << "Bad bounding box (min >= max) for solid: "
+            << GetName() << " !"
+            << "\npMin = " << pMin
+            << "\npMax = " << pMax;
+    G4Exception("G4ShiftedCone::BoundingLimits()", "GeomMgt0001",
+                JustWarning, message);
+    DumpInfo();
+  }
+}
+
+///////////////////////////////////////////////////////////////////////
+//
+// Calculate extent under transform and specified limit
+
+G4bool G4ShiftedCone::CalculateExtent( const EAxis              pAxis,
+                                const G4VoxelLimits&     pVoxelLimit,
+                                const G4AffineTransform& pTransform,
+                                      G4double&          pMin,
+                                      G4double&          pMax ) const
+{
+  G4ThreeVector bmin, bmax;
+  G4bool exist;
+
+  // Get bounding box
+  BoundingLimits(bmin,bmax);
+
+  // Check bounding box
+  G4BoundingEnvelope bbox(bmin,bmax);
+#ifdef G4BBOX_EXTENT
+  if (true) return bbox.CalculateExtent(pAxis,pVoxelLimit,pTransform,pMin,pMax);
+#endif
+  if (bbox.BoundingBoxVsVoxelLimits(pAxis,pVoxelLimit,pTransform,pMin,pMax))
+  {
+    return exist = (pMin < pMax) ? true : false;
+  }
+
+  // Get parameters of the solid
+  G4double rmin1 = GetInnerRadiusMinusZ();
+  G4double rmax1 = GetOuterRadiusMinusZ();
+  G4double rmin2 = GetInnerRadiusPlusZ();
+  G4double rmax2 = GetOuterRadiusPlusZ();
+  G4double z1    = GetZ1();
+  G4double z2    = GetZ2();
+  G4double dphi  = GetDeltaPhiAngle();
+
+  // Find bounding envelope and calculate extent
+  //
+  const G4int NSTEPS = 24;            // number of steps for whole circle
+  G4double astep  = twopi/NSTEPS;     // max angle for one step
+  G4int    ksteps = (dphi <= astep) ? 1 : (G4int)((dphi-deg)/astep) + 1;
+  G4double ang    = dphi/ksteps;
+
+  G4double sinHalf = std::sin(0.5*ang);
+  G4double cosHalf = std::cos(0.5*ang);
+  G4double sinStep = 2.*sinHalf*cosHalf;
+  G4double cosStep = 1. - 2.*sinHalf*sinHalf;
+  G4double rext1   = rmax1/cosHalf;
+  G4double rext2   = rmax2/cosHalf;
+
+  // bounding envelope for full cone without hole consists of two polygons,
+  // in other cases it is a sequence of quadrilaterals
+  if (rmin1 == 0 && rmin2 == 0 && dphi == twopi)
+  {
+    G4double sinCur = sinHalf;
+    G4double cosCur = cosHalf;
+
+    G4ThreeVectorList baseA(NSTEPS),baseB(NSTEPS);
+    for (G4int k=0; k<NSTEPS; ++k)
+    {
+      baseA[k].set(rext1*cosCur,rext1*sinCur, z1);
+      baseB[k].set(rext2*cosCur,rext2*sinCur, z2);
+
+      G4double sinTmp = sinCur;
+      sinCur = sinCur*cosStep + cosCur*sinStep;
+      cosCur = cosCur*cosStep - sinTmp*sinStep;
+    }
+    std::vector<const G4ThreeVectorList *> polygons(2);
+    polygons[0] = &baseA;
+    polygons[1] = &baseB;
+    G4BoundingEnvelope benv(bmin,bmax,polygons);
+    exist = benv.CalculateExtent(pAxis,pVoxelLimit,pTransform,pMin,pMax);
+  }
+  else
+  {
+
+    G4double sinStart = GetSinStartPhi();
+    G4double cosStart = GetCosStartPhi();
+    G4double sinEnd   = GetSinEndPhi();
+    G4double cosEnd   = GetCosEndPhi();
+    G4double sinCur   = sinStart*cosHalf + cosStart*sinHalf;
+    G4double cosCur   = cosStart*cosHalf - sinStart*sinHalf;
+
+    // set quadrilaterals
+    G4ThreeVectorList pols[NSTEPS+2];
+    for (G4int k=0; k<ksteps+2; ++k) pols[k].resize(4);
+    pols[0][0].set(rmin2*cosStart,rmin2*sinStart, z2);
+    pols[0][1].set(rmin1*cosStart,rmin1*sinStart, z1);
+    pols[0][2].set(rmax1*cosStart,rmax1*sinStart, z1);
+    pols[0][3].set(rmax2*cosStart,rmax2*sinStart, z2);
+    for (G4int k=1; k<ksteps+1; ++k)
+    {
+      pols[k][0].set(rmin2*cosCur,rmin2*sinCur, z2);
+      pols[k][1].set(rmin1*cosCur,rmin1*sinCur, z1);
+      pols[k][2].set(rext1*cosCur,rext1*sinCur, z1);
+      pols[k][3].set(rext2*cosCur,rext2*sinCur, z2);
+
+      G4double sinTmp = sinCur;
+      sinCur = sinCur*cosStep + cosCur*sinStep;
+      cosCur = cosCur*cosStep - sinTmp*sinStep;
+    }
+    pols[ksteps+1][0].set(rmin2*cosEnd,rmin2*sinEnd, z2);
+    pols[ksteps+1][1].set(rmin1*cosEnd,rmin1*sinEnd, z1);
+    pols[ksteps+1][2].set(rmax1*cosEnd,rmax1*sinEnd, z1);
+    pols[ksteps+1][3].set(rmax2*cosEnd,rmax2*sinEnd, z2);
+
+    // set envelope and calculate extent
+    std::vector<const G4ThreeVectorList *> polygons;
+    polygons.resize(ksteps+2);
+    for (G4int k=0; k<ksteps+2; ++k) polygons[k] = &pols[k];
+    G4BoundingEnvelope benv(bmin,bmax,polygons);
+    exist = benv.CalculateExtent(pAxis,pVoxelLimit,pTransform,pMin,pMax);
+  }
+  return exist;
+}
+
+////////////////////////////////////////////////////////////////////////
+//
+// Return unit normal of surface closest to p
+// - note if point on z axis, ignore phi divided sides
+// - unsafe if point close to z axis a rmin=0 - no explicit checks
+
+G4ThreeVector G4ShiftedCone::SurfaceNormal( const G4ThreeVector& p) const
+{
+  G4int noSurfaces = 0;
+  G4double rho;//, pPhi;
+  G4double distZ, distRMin, distRMax;
+//  G4double distSPhi = kInfinity, distEPhi = kInfinity;
+  G4double tanRMin, secRMin, pRMin, widRMin;
+  G4double tanRMax, secRMax, pRMax, widRMax;
+
+  G4ThreeVector norm, sumnorm(0.,0.,0.), nZ = G4ThreeVector(0.,0.,1.);
+  G4ThreeVector nR, nr(0.,0.,0.), nPs, nPe;
+
+  G4double z = p.z() - fZshift;
+
+  distZ = std::fabs(std::fabs(z) - fDz);
+  rho   = std::sqrt(p.x()*p.x() + p.y()*p.y());
+
+  tanRMin  = (fRmin2 - fRmin1)*0.5/fDz;
+  secRMin  = std::sqrt(1 + tanRMin*tanRMin);
+  pRMin    = rho - z*tanRMin;
+  widRMin  = fRmin2 - fDz*tanRMin;
+  distRMin = std::fabs(pRMin - widRMin)/secRMin;
+
+  tanRMax  = (fRmax2 - fRmax1)*0.5/fDz;
+  secRMax  = std::sqrt(1+tanRMax*tanRMax);
+  pRMax    = rho - z*tanRMax;
+  widRMax  = fRmax2 - fDz*tanRMax;
+  distRMax = std::fabs(pRMax - widRMax)/secRMax;
+/*
+  if (!fPhiFullCone)   // Protected against (0,0,z)
+  {
+    if ( rho )
+    {
+      pPhi = std::atan2(p.y(),p.x());
+
+      if (pPhi  < fSPhi-halfCarTolerance)            { pPhi += twopi; }
+      else if (pPhi > fSPhi+fDPhi+halfCarTolerance)  { pPhi -= twopi; }
+
+      distSPhi = std::fabs( pPhi - fSPhi );
+      distEPhi = std::fabs( pPhi - fSPhi - fDPhi );
+    }
+    else if( !(fRmin1) || !(fRmin2) )
+    {
+      distSPhi = 0.;
+      distEPhi = 0.;
+    }
+    nPs = G4ThreeVector(std::sin(fSPhi), -std::cos(fSPhi), 0);
+    nPe = G4ThreeVector(-std::sin(fSPhi+fDPhi), std::cos(fSPhi+fDPhi), 0);
+  }*/
+  if ( rho > halfCarTolerance )
+  {
+    nR = G4ThreeVector(p.x()/rho/secRMax, p.y()/rho/secRMax, -tanRMax/secRMax);
+    if (fRmin1 || fRmin2)
+    {
+      nr = G4ThreeVector(-p.x()/rho/secRMin,-p.y()/rho/secRMin,tanRMin/secRMin);
+    }
+  }
+
+  if( distRMax <= halfCarTolerance )
+  {
+    noSurfaces ++;
+    sumnorm += nR;
+  }
+  if( (fRmin1 || fRmin2) && (distRMin <= halfCarTolerance) )
+  {
+    noSurfaces ++;
+    sumnorm += nr;
+  }
+/*  if( !fPhiFullCone )
+  {
+    if (distSPhi <= halfAngTolerance)
+    {
+      noSurfaces ++;
+      sumnorm += nPs;
+    }
+    if (distEPhi <= halfAngTolerance)
+    {
+      noSurfaces ++;
+      sumnorm += nPe;
+    }
+  }*/
+  if (distZ <= halfCarTolerance)
+  {
+    noSurfaces ++;
+    if ( z >= 0.)  { sumnorm += nZ; }
+    else               { sumnorm -= nZ; }
+  }
+  if ( noSurfaces == 0 )
+  {
+#ifdef G4CSGDEBUG
+    G4Exception("G4ShiftedCone::SurfaceNormal(p)", "GeomSolids1002",
+                JustWarning, "Point p is not on surface !?" );
+#endif
+     norm = ApproxSurfaceNormal(p);
+  }
+  else if ( noSurfaces == 1 )  { norm = sumnorm; }
+  else                         { norm = sumnorm.unit(); }
+
+  return norm ;
+}
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Algorithm for SurfaceNormal() following the original specification
+// for points not on the surface
+
+G4ThreeVector G4ShiftedCone::ApproxSurfaceNormal( const G4ThreeVector& p ) const
+{
+  ENorm side ;
+  G4ThreeVector norm ;
+  G4double rho;//, phi ;
+  G4double distZ, distRMin, distRMax;//, distSPhi, distEPhi, distMin ;
+  G4double tanRMin, secRMin, pRMin, widRMin ;
+  G4double tanRMax, secRMax, pRMax, widRMax ;
+
+  G4double z = p.z() - fZshift;
+
+  distZ = std::fabs(std::fabs(z) - fDz) ;
+  rho   = std::sqrt(p.x()*p.x() + p.y()*p.y()) ;
+
+  tanRMin  = (fRmin2 - fRmin1)*0.5/fDz ;
+  secRMin  = std::sqrt(1 + tanRMin*tanRMin) ;
+  pRMin    = rho - z*tanRMin ;
+  widRMin  = fRmin2 - fDz*tanRMin ;
+  distRMin = std::fabs(pRMin - widRMin)/secRMin ;
+
+  tanRMax  = (fRmax2 - fRmax1)*0.5/fDz ;
+  secRMax  = std::sqrt(1+tanRMax*tanRMax) ;
+  pRMax    = rho - z*tanRMax ;
+  widRMax  = fRmax2 - fDz*tanRMax ;
+  distRMax = std::fabs(pRMax - widRMax)/secRMax ;
+
+  if (distRMin < distRMax)  // First minimum
+  {
+    if (distZ < distRMin)
+    {
+//      distMin = distZ ;
+      side    = kNZ ;
+    }
+    else
+    {
+//      distMin = distRMin ;
+      side    = kNRMin ;
+    }
+  }
+  else
+  {
+    if (distZ < distRMax)
+    {
+//      distMin = distZ ;
+      side    = kNZ ;
+    }
+    else
+    {
+//      distMin = distRMax ;
+      side    = kNRMax ;
+    }
+  }
+/*
+  if ( !fPhiFullCone && rho )  // Protected against (0,0,z)
+  {
+    phi = std::atan2(p.y(),p.x()) ;
+
+    if (phi < 0)  { phi += twopi; }
+
+    if (fSPhi < 0)  { distSPhi = std::fabs(phi - (fSPhi + twopi))*rho; }
+    else            { distSPhi = std::fabs(phi - fSPhi)*rho; }
+
+    distEPhi = std::fabs(phi - fSPhi - fDPhi)*rho ;
+
+    // Find new minimum
+
+    if (distSPhi < distEPhi)
+    {
+      if (distSPhi < distMin)  { side = kNSPhi; }
+    }
+    else
+    {
+      if (distEPhi < distMin)  { side = kNEPhi; }
+    }
+  }*/
+  switch (side)
+  {
+    case kNRMin:      // Inner radius
+      rho *= secRMin ;
+      norm = G4ThreeVector(-p.x()/rho, -p.y()/rho, tanRMin/secRMin) ;
+      break ;
+    case kNRMax:      // Outer radius
+      rho *= secRMax ;
+      norm = G4ThreeVector(p.x()/rho, p.y()/rho, -tanRMax/secRMax) ;
+      break ;
+    case kNZ:         // +/- dz
+      if (z > 0)      { norm = G4ThreeVector(0,0,1);  }
+      else            { norm = G4ThreeVector(0,0,-1); }
+      break ;
+//    case kNSPhi:
+//      norm = G4ThreeVector(std::sin(fSPhi), -std::cos(fSPhi), 0) ;
+//      break ;
+//    case kNEPhi:
+//      norm=G4ThreeVector(-std::sin(fSPhi+fDPhi), std::cos(fSPhi+fDPhi), 0) ;
+//      break ;
+    default:          // Should never reach this case...
+      DumpInfo();
+      G4Exception("G4ShiftedCone::ApproxSurfaceNormal()",
+                  "GeomSolids1002", JustWarning,
+                  "Undefined side for valid surface normal to solid.");
+      break ;
+  }
+  return norm ;
+}
+
+////////////////////////////////////////////////////////////////////////
+//
+// Calculate distance to shape from outside, along normalised vector
+// - return kInfinity if no intersection, or intersection distance <= tolerance
+//
+// - Compute the intersection with the z planes
+//        - if at valid r, phi, return
+//
+// -> If point is outside cone, compute intersection with rmax1*0.5
+//        - if at valid phi,z return
+//        - if inside outer cone, handle case when on tolerant outer cone
+//          boundary and heading inwards(->0 to in)
+//
+// -> Compute intersection with inner cone, taking largest +ve root
+//        - if valid (in z,phi), save intersction
+//
+//    -> If phi segmented, compute intersections with phi half planes
+//        - return smallest of valid phi intersections and
+//          inner radius intersection
+//
+// NOTE:
+// - `if valid' implies tolerant checking of intersection points
+// - z, phi intersection from Tubs
+
+G4double G4ShiftedCone::DistanceToIn( const G4ThreeVector& p,
+                               const G4ThreeVector& v   ) const
+{
+  G4double snxt = kInfinity ;      // snxt = default return value
+  const G4double dRmax = 50*(fRmax1+fRmax2);// 100*(Rmax1+Rmax2)/2.
+
+  G4double tanRMax,secRMax,rMaxAv;//,rMaxOAv ;  // Data for cones
+  G4double tanRMin,secRMin,rMinAv;//,rMinOAv ;
+  G4double rout,rin ;
+
+  G4double tolORMin,/*tolORMin2,*/tolIRMin,tolIRMin2 ; // `generous' radii squared
+  G4double /*tolORMax2,*/tolIRMax,tolIRMax2 ;
+  G4double tolODz,tolIDz ;
+
+  G4double /*Dist,*/ sd,xi,yi,zi,ri=0.,risec,rhoi2;//,cosPsi ; // Intersection point vars
+
+  G4double t1,t2,t3,b,c,d ;    // Quadratic solver variables
+  G4double nt1,nt2,nt3 ;
+//  G4double Comp ;
+
+  G4ThreeVector Normal;
+
+  G4double z = p.z() - fZshift;
+
+  // Cone Precalcs
+
+  tanRMin = (fRmin2 - fRmin1)*0.5/fDz ;
+  secRMin = std::sqrt(1.0 + tanRMin*tanRMin) ;
+  rMinAv  = (fRmin1 + fRmin2)*0.5 ;
+/*
+  if (rMinAv > halfRadTolerance)
+  {
+    rMinOAv = rMinAv - halfRadTolerance ;
+  }
+  else
+  {
+    rMinOAv = 0.0 ;
+  }*/
+  tanRMax = (fRmax2 - fRmax1)*0.5/fDz ;
+  secRMax = std::sqrt(1.0 + tanRMax*tanRMax) ;
+  rMaxAv  = (fRmax1 + fRmax2)*0.5 ;
+//  rMaxOAv = rMaxAv + halfRadTolerance ;
+
+  // Intersection with z-surfaces
+
+  tolIDz = fDz - halfCarTolerance ;
+  tolODz = fDz + halfCarTolerance ;
+
+  if (std::fabs(z) >= tolIDz)
+  {
+    if ( z*v.z() < 0 )    // at +Z going in -Z or visa versa
+    {
+      sd = (std::fabs(z) - fDz)/std::fabs(v.z()) ; // Z intersect distance
+
+      if( sd < 0.0 )  { sd = 0.0; }                    // negative dist -> zero
+
+      xi   = p.x() + sd*v.x() ;  // Intersection coords
+      yi   = p.y() + sd*v.y() ;
+      rhoi2 = xi*xi + yi*yi  ;
+
+      // Check validity of intersection
+      // Calculate (outer) tolerant radi^2 at intersecion
+
+      if (v.z() > 0)
+      {
+        tolORMin  = fRmin1 - halfRadTolerance*secRMin ;
+        tolIRMin  = fRmin1 + halfRadTolerance*secRMin ;
+        tolIRMax  = fRmax1 - halfRadTolerance*secRMin ;
+        // tolORMax2 = (fRmax1 + halfRadTolerance*secRMax)*
+        //             (fRmax1 + halfRadTolerance*secRMax) ;
+      }
+      else
+      {
+        tolORMin  = fRmin2 - halfRadTolerance*secRMin ;
+        tolIRMin  = fRmin2 + halfRadTolerance*secRMin ;
+        tolIRMax  = fRmax2 - halfRadTolerance*secRMin ;
+        // tolORMax2 = (fRmax2 + halfRadTolerance*secRMax)*
+        //             (fRmax2 + halfRadTolerance*secRMax) ;
+      }
+      if ( tolORMin > 0 )
+      {
+        // tolORMin2 = tolORMin*tolORMin ;
+        tolIRMin2 = tolIRMin*tolIRMin ;
+      }
+      else
+      {
+        // tolORMin2 = 0.0 ;
+        tolIRMin2 = 0.0 ;
+      }
+      if ( tolIRMax > 0 )  { tolIRMax2 = tolIRMax*tolIRMax; }
+      else                 { tolIRMax2 = 0.0; }
+
+      if ( (tolIRMin2 <= rhoi2) && (rhoi2 <= tolIRMax2) )
+      {
+/*        if ( !fPhiFullCone && rhoi2 )
+        {
+          // Psi = angle made with central (average) phi of shape
+
+          cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
+
+          if (cosPsi >= cosHDPhiIT)  { return sd; }
+        }
+        else */
+        {
+          return sd;
+        }
+      }
+    }
+    else  // On/outside extent, and heading away  -> cannot intersect
+    {
+      return snxt ;
+    }
+  }
+
+// ----> Can not intersect z surfaces
+
+
+// Intersection with outer cone (possible return) and
+//                   inner cone (must also check phi)
+//
+// Intersection point (xi,yi,zi) on line x=p.x+t*v.x etc.
+//
+// Intersects with x^2+y^2=(a*z+b)^2
+//
+// where a=tanRMax or tanRMin
+//       b=rMaxAv  or rMinAv
+//
+// (vx^2+vy^2-(a*vz)^2)t^2+2t(pxvx+pyvy-a*vz(a*pz+b))+px^2+py^2-(a*pz+b)^2=0 ;
+//     t1                        t2                      t3
+//
+//  \--------u-------/       \-----------v----------/ \---------w--------/
+//
+
+  t1   = 1.0 - v.z()*v.z() ;
+  t2   = p.x()*v.x() + p.y()*v.y() ;
+  t3   = p.x()*p.x() + p.y()*p.y() ;
+  rin  = tanRMin*z + rMinAv ;
+  rout = tanRMax*z + rMaxAv ;
+
+  // Outer Cone Intersection
+  // Must be outside/on outer cone for valid intersection
+
+  nt1 = t1 - (tanRMax*v.z())*(tanRMax*v.z()) ;
+  nt2 = t2 - tanRMax*v.z()*rout ;
+  nt3 = t3 - rout*rout ;
+
+  if (std::fabs(nt1) > kRadTolerance)  // Equation quadratic => 2 roots
+  {
+    b = nt2/nt1;
+    c = nt3/nt1;
+    d = b*b-c  ;
+    if ( (nt3 > rout*rout*kRadTolerance*kRadTolerance*secRMax*secRMax)
+      || (rout < 0) )
+    {
+      // If outside real cone (should be rho-rout>kRadTolerance*0.5
+      // NOT rho^2 etc) saves a std::sqrt() at expense of accuracy
+
+      if (d >= 0)
+      {
+
+        if ((rout < 0) && (nt3 <= 0))
+        {
+          // Inside `shadow cone' with -ve radius
+          // -> 2nd root could be on real cone
+
+          if (b>0) { sd = c/(-b-std::sqrt(d)); }
+          else     { sd = -b + std::sqrt(d);   }
+        }
+        else
+        {
+          if ((b <= 0) && (c >= 0)) // both >=0, try smaller root
+          {
+            sd=c/(-b+std::sqrt(d));
+          }
+          else
+          {
+            if ( c <= 0 ) // second >=0
+            {
+              sd = -b + std::sqrt(d) ;
+              if((sd<0) & (sd>-halfRadTolerance)) sd=0;
+            }
+            else  // both negative, travel away
+            {
+              return kInfinity ;
+            }
+          }
+        }
+        if ( sd >= 0 )  // If 'forwards'. Check z intersection
+        {
+          if ( sd>dRmax ) // Avoid rounding errors due to precision issues on
+          {               // 64 bits systems. Split long distances and recompute
+            G4double fTerm = sd-std::fmod(sd,dRmax);
+            sd = fTerm + DistanceToIn(p+fTerm*v,v);
+          }
+          zi = z + sd*v.z() ;
+
+          if (std::fabs(zi) <= tolODz)
+          {
+            // Z ok. Check phi intersection if reqd
+
+            return sd;
+/*            if ( fPhiFullCone )  { return sd; }
+            else
+            {
+              xi     = p.x() + sd*v.x() ;
+              yi     = p.y() + sd*v.y() ;
+              ri     = rMaxAv + zi*tanRMax ;
+              cosPsi = (xi*cosCPhi + yi*sinCPhi)/ri ;
+
+              if ( cosPsi >= cosHDPhiIT )  { return sd; }
+            }*/
+          }
+        }                // end if (sd>0)
+      }
+    }
+    else
+    {
+      // Inside outer cone
+      // check not inside, and heading through G4ShiftedCone (-> 0 to in)
+
+      if ( ( t3  > (rin + halfRadTolerance*secRMin)*
+                   (rin + halfRadTolerance*secRMin) )
+        && (nt2 < 0) && (d >= 0) && (std::fabs(z) <= tolIDz) )
+      {
+        // Inside cones, delta r -ve, inside z extent
+        // Point is on the Surface => check Direction using  Normal.dot(v)
+
+        xi     = p.x() ;
+        yi     = p.y()  ;
+        risec  = std::sqrt(xi*xi + yi*yi)*secRMax ;
+        Normal = G4ThreeVector(xi/risec,yi/risec,-tanRMax/secRMax) ;
+/*        if ( !fPhiFullCone )
+        {
+          cosPsi = (p.x()*cosCPhi + p.y()*sinCPhi)/std::sqrt(t3) ;
+          if ( cosPsi >= cosHDPhiIT )
+          {
+            if ( Normal.dot(v) <= 0 )  { return 0.0; }
+          }
+        }
+        else*/
+        {
+          if ( Normal.dot(v) <= 0 )  { return 0.0; }
+        }
+      }
+    }
+  }
+  else  //  Single root case
+  {
+    if ( std::fabs(nt2) > kRadTolerance )
+    {
+      sd = -0.5*nt3/nt2 ;
+
+      if ( sd < 0 )  { return kInfinity; }   // travel away
+      else  // sd >= 0,  If 'forwards'. Check z intersection
+      {
+        zi = z + sd*v.z() ;
+
+        if ((std::fabs(zi) <= tolODz) && (nt2 < 0))
+        {
+          // Z ok. Check phi intersection if reqd
+          return sd;
+/*          if ( fPhiFullCone )  { return sd; }
+          else
+          {
+            xi     = p.x() + sd*v.x() ;
+            yi     = p.y() + sd*v.y() ;
+            ri     = rMaxAv + zi*tanRMax ;
+            cosPsi = (xi*cosCPhi + yi*sinCPhi)/ri ;
+
+            if (cosPsi >= cosHDPhiIT)  { return sd; }
+          }*/
+        }
+      }
+    }
+    else  //    travel || cone surface from its origin
+    {
+      sd = kInfinity ;
+    }
+  }
+
+  // Inner Cone Intersection
+  // o Space is divided into 3 areas:
+  //   1) Radius greater than real inner cone & imaginary cone & outside
+  //      tolerance
+  //   2) Radius less than inner or imaginary cone & outside tolarance
+  //   3) Within tolerance of real or imaginary cones
+  //      - Extra checks needed for 3's intersections
+  //        => lots of duplicated code
+
+  if (rMinAv)
+  {
+    nt1 = t1 - (tanRMin*v.z())*(tanRMin*v.z()) ;
+    nt2 = t2 - tanRMin*v.z()*rin ;
+    nt3 = t3 - rin*rin ;
+
+    if ( nt1 )
+    {
+      if ( nt3 > rin*kRadTolerance*secRMin )
+      {
+        // At radius greater than real & imaginary cones
+        // -> 2nd root, with zi check
+
+        b = nt2/nt1 ;
+        c = nt3/nt1 ;
+        d = b*b-c ;
+        if (d >= 0)   // > 0
+        {
+           if(b>0){sd = c/( -b-std::sqrt(d));}
+           else   {sd = -b + std::sqrt(d) ;}
+
+          if ( sd >= 0 )   // > 0
+          {
+            if ( sd>dRmax ) // Avoid rounding errors due to precision issues on
+            {               // 64 bits systems. Split long distance and recompute
+              G4double fTerm = sd-std::fmod(sd,dRmax);
+              sd = fTerm + DistanceToIn(p+fTerm*v,v);
+            }
+            zi = z + sd*v.z() ;
+
+            if ( std::fabs(zi) <= tolODz )
+            {
+/*              if ( !fPhiFullCone )
+              {
+                xi     = p.x() + sd*v.x() ;
+                yi     = p.y() + sd*v.y() ;
+                ri     = rMinAv + zi*tanRMin ;
+                cosPsi = (xi*cosCPhi + yi*sinCPhi)/ri ;
+
+                if (cosPsi >= cosHDPhiIT)
+                {
+                  if ( sd > halfRadTolerance )  { snxt=sd; }
+                  else
+                  {
+                    // Calculate a normal vector in order to check Direction
+
+                    risec  = std::sqrt(xi*xi + yi*yi)*secRMin ;
+                    Normal = G4ThreeVector(-xi/risec,-yi/risec,tanRMin/secRMin);
+                    if ( Normal.dot(v) <= 0 )  { snxt = sd; }
+                  }
+                }
+              }
+              else */
+              {
+                if ( sd > halfRadTolerance )  { return sd; }
+                else
+                {
+                  // Calculate a normal vector in order to check Direction
+
+                  xi     = p.x() + sd*v.x() ;
+                  yi     = p.y() + sd*v.y() ;
+                  risec  = std::sqrt(xi*xi + yi*yi)*secRMin ;
+                  Normal = G4ThreeVector(-xi/risec,-yi/risec,tanRMin/secRMin) ;
+                  if ( Normal.dot(v) <= 0 )  { return sd; }
+                }
+              }
+            }
+          }
+        }
+      }
+      else  if ( nt3 < -rin*kRadTolerance*secRMin )
+      {
+        // Within radius of inner cone (real or imaginary)
+        // -> Try 2nd root, with checking intersection is with real cone
+        // -> If check fails, try 1st root, also checking intersection is
+        //    on real cone
+
+        b = nt2/nt1 ;
+        c = nt3/nt1 ;
+        d = b*b - c ;
+
+        if ( d >= 0 )  // > 0
+        {
+          if (b>0) { sd = c/(-b-std::sqrt(d)); }
+          else     { sd = -b + std::sqrt(d);   }
+          zi = z + sd*v.z() ;
+          ri = rMinAv + zi*tanRMin ;
+
+          if ( ri > 0 )
+          {
+            if ( (sd >= 0) && (std::fabs(zi) <= tolODz) )  // sd > 0
+            {
+              if ( sd>dRmax ) // Avoid rounding errors due to precision issues
+              {               // seen on 64 bits systems. Split and recompute
+                G4double fTerm = sd-std::fmod(sd,dRmax);
+                sd = fTerm + DistanceToIn(p+fTerm*v,v);
+              }
+/*              if ( !fPhiFullCone )
+              {
+                xi     = p.x() + sd*v.x() ;
+                yi     = p.y() + sd*v.y() ;
+                cosPsi = (xi*cosCPhi + yi*sinCPhi)/ri ;
+
+                if (cosPsi >= cosHDPhiOT)
+                {
+                  if ( sd > halfRadTolerance )  { snxt=sd; }
+                  else
+                  {
+                    // Calculate a normal vector in order to check Direction
+
+                    risec  = std::sqrt(xi*xi + yi*yi)*secRMin ;
+                    Normal = G4ThreeVector(-xi/risec,-yi/risec,tanRMin/secRMin);
+                    if ( Normal.dot(v) <= 0 )  { snxt = sd; }
+                  }
+                }
+              }
+              else */
+              {
+                if( sd > halfRadTolerance )  { return sd; }
+                else
+                {
+                  // Calculate a normal vector in order to check Direction
+
+                  xi     = p.x() + sd*v.x() ;
+                  yi     = p.y() + sd*v.y() ;
+                  risec  = std::sqrt(xi*xi + yi*yi)*secRMin ;
+                  Normal = G4ThreeVector(-xi/risec,-yi/risec,tanRMin/secRMin) ;
+                  if ( Normal.dot(v) <= 0 )  { return sd; }
+                }
+              }
+            }
+          }
+          else
+          {
+            if (b>0) { sd = -b - std::sqrt(d);   }
+            else     { sd = c/(-b+std::sqrt(d)); }
+            zi = z + sd*v.z() ;
+            ri = rMinAv + zi*tanRMin ;
+
+            if ( (sd >= 0) && (ri > 0) && (std::fabs(zi) <= tolODz) ) // sd>0
+            {
+              if ( sd>dRmax ) // Avoid rounding errors due to precision issues
+              {               // seen on 64 bits systems. Split and recompute
+                G4double fTerm = sd-std::fmod(sd,dRmax);
+                sd = fTerm + DistanceToIn(p+fTerm*v,v);
+              }
+/*              if ( !fPhiFullCone )
+              {
+                xi     = p.x() + sd*v.x() ;
+                yi     = p.y() + sd*v.y() ;
+                cosPsi = (xi*cosCPhi + yi*sinCPhi)/ri ;
+
+                if (cosPsi >= cosHDPhiIT)
+                {
+                  if ( sd > halfRadTolerance )  { snxt=sd; }
+                  else
+                  {
+                    // Calculate a normal vector in order to check Direction
+
+                    risec  = std::sqrt(xi*xi + yi*yi)*secRMin ;
+                    Normal = G4ThreeVector(-xi/risec,-yi/risec,tanRMin/secRMin);
+                    if ( Normal.dot(v) <= 0 )  { snxt = sd; }
+                  }
+                }
+              }
+              else */
+              {
+                if ( sd > halfRadTolerance )  { return sd; }
+                else
+                {
+                  // Calculate a normal vector in order to check Direction
+
+                  xi     = p.x() + sd*v.x() ;
+                  yi     = p.y() + sd*v.y() ;
+                  risec  = std::sqrt(xi*xi + yi*yi)*secRMin ;
+                  Normal = G4ThreeVector(-xi/risec,-yi/risec,tanRMin/secRMin) ;
+                  if ( Normal.dot(v) <= 0 )  { return sd; }
+                }
+              }
+            }
+          }
+        }
+      }
+      else
+      {
+        // Within kRadTol*0.5 of inner cone (real OR imaginary)
+        // ----> Check not travelling through (=>0 to in)
+        // ----> if not:
+        //    -2nd root with validity check
+
+        if ( std::fabs(z) <= tolODz )
+        {
+          if ( nt2 > 0 )
+          {
+            // Inside inner real cone, heading outwards, inside z range
+
+/*            if ( !fPhiFullCone )
+            {
+              cosPsi = (p.x()*cosCPhi + p.y()*sinCPhi)/std::sqrt(t3) ;
+
+              if (cosPsi >= cosHDPhiIT)  { return 0.0; }
+            }
+            else */ { return 0.0; }
+          }
+          else
+          {
+            // Within z extent, but not travelling through
+            // -> 2nd root or kInfinity if 1st root on imaginary cone
+
+            b = nt2/nt1 ;
+            c = nt3/nt1 ;
+            d = b*b - c ;
+
+            if ( d >= 0 )   // > 0
+            {
+              if (b>0) { sd = -b - std::sqrt(d);   }
+              else     { sd = c/(-b+std::sqrt(d)); }
+              zi = z + sd*v.z() ;
+              ri = rMinAv + zi*tanRMin ;
+
+              if ( ri > 0 )   // 2nd root
+              {
+                if (b>0) { sd = c/(-b-std::sqrt(d)); }
+                else     { sd = -b + std::sqrt(d);   }
+
+                zi = z + sd*v.z() ;
+
+                if ( (sd >= 0) && (std::fabs(zi) <= tolODz) )  // sd>0
+                {
+                  if ( sd>dRmax ) // Avoid rounding errors due to precision issue
+                  {               // seen on 64 bits systems. Split and recompute
+                    G4double fTerm = sd-std::fmod(sd,dRmax);
+                    sd = fTerm + DistanceToIn(p+fTerm*v,v);
+                  }
+/*                  if ( !fPhiFullCone )
+                  {
+                    xi     = p.x() + sd*v.x() ;
+                    yi     = p.y() + sd*v.y() ;
+                    ri     = rMinAv + zi*tanRMin ;
+                    cosPsi = (xi*cosCPhi + yi*sinCPhi)/ri ;
+
+                    if ( cosPsi >= cosHDPhiIT )  { snxt = sd; }
+                  }
+                  else */ { return sd; }
+                }
+              }
+              else  { return kInfinity; }
+            }
+          }
+        }
+        else   // 2nd root
+        {
+          b = nt2/nt1 ;
+          c = nt3/nt1 ;
+          d = b*b - c ;
+
+          if ( d > 0 )
+          {
+            if (b>0) { sd = c/(-b-std::sqrt(d)); }
+            else     { sd = -b + std::sqrt(d) ;  }
+            zi = z + sd*v.z() ;
+
+            if ( (sd >= 0) && (std::fabs(zi) <= tolODz) )  // sd>0
+            {
+              if ( sd>dRmax ) // Avoid rounding errors due to precision issues
+              {               // seen on 64 bits systems. Split and recompute
+                G4double fTerm = sd-std::fmod(sd,dRmax);
+                sd = fTerm + DistanceToIn(p+fTerm*v,v);
+              }
+/*              if ( !fPhiFullCone )
+              {
+                xi     = p.x() + sd*v.x();
+                yi     = p.y() + sd*v.y();
+                ri     = rMinAv + zi*tanRMin ;
+                cosPsi = (xi*cosCPhi + yi*sinCPhi)/ri;
+
+                if (cosPsi >= cosHDPhiIT)  { snxt = sd; }
+              }
+              else */ { return sd; }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // Phi segment intersection
+  //
+  // o Tolerant of points inside phi planes by up to kCarTolerance*0.5
+  //
+  // o NOTE: Large duplication of code between sphi & ephi checks
+  //         -> only diffs: sphi -> ephi, Comp -> -Comp and half-plane
+  //            intersection check <=0 -> >=0
+  //         -> Should use some form of loop Construct
+/*
+  if ( !fPhiFullCone )
+  {
+    // First phi surface (starting phi)
+
+    Comp    = v.x()*sinSPhi - v.y()*cosSPhi ;
+
+    if ( Comp < 0 )    // Component in outwards normal dirn
+    {
+      Dist = (p.y()*cosSPhi - p.x()*sinSPhi) ;
+
+      if (Dist < halfCarTolerance)
+      {
+        sd = Dist/Comp ;
+
+        if ( sd < snxt )
+        {
+          if ( sd < 0 )  { sd = 0.0; }
+
+          zi = z + sd*v.z() ;
+
+          if ( std::fabs(zi) <= tolODz )
+          {
+            xi        = p.x() + sd*v.x() ;
+            yi        = p.y() + sd*v.y() ;
+            rhoi2     = xi*xi + yi*yi ;
+            tolORMin2 = (rMinOAv + zi*tanRMin)*(rMinOAv + zi*tanRMin) ;
+            tolORMax2 = (rMaxOAv + zi*tanRMax)*(rMaxOAv + zi*tanRMax) ;
+
+            if ( (rhoi2 >= tolORMin2) && (rhoi2 <= tolORMax2) )
+            {
+              // z and r intersections good - check intersecting with
+              // correct half-plane
+
+              if ((yi*cosCPhi - xi*sinCPhi) <= 0 )  { snxt = sd; }
+            }
+          }
+        }
+      }
+    }
+
+    // Second phi surface (Ending phi)
+
+    Comp    = -(v.x()*sinEPhi - v.y()*cosEPhi) ;
+
+    if ( Comp < 0 )   // Component in outwards normal dirn
+    {
+      Dist = -(p.y()*cosEPhi - p.x()*sinEPhi) ;
+      if (Dist < halfCarTolerance)
+      {
+        sd = Dist/Comp ;
+
+        if ( sd < snxt )
+        {
+          if ( sd < 0 )  { sd = 0.0; }
+
+          zi = z + sd*v.z() ;
+
+          if (std::fabs(zi) <= tolODz)
+          {
+            xi        = p.x() + sd*v.x() ;
+            yi        = p.y() + sd*v.y() ;
+            rhoi2     = xi*xi + yi*yi ;
+            tolORMin2 = (rMinOAv + zi*tanRMin)*(rMinOAv + zi*tanRMin) ;
+            tolORMax2 = (rMaxOAv + zi*tanRMax)*(rMaxOAv + zi*tanRMax) ;
+
+            if ( (rhoi2 >= tolORMin2) && (rhoi2 <= tolORMax2) )
+            {
+              // z and r intersections good - check intersecting with
+              // correct half-plane
+
+              if ( (yi*cosCPhi - xi*sinCPhi) >= 0.0 )  { snxt = sd; }
+            }
+          }
+        }
+      }
+    }
+  }*/
+  if (snxt < halfCarTolerance)  { snxt = 0.; }
+
+  return snxt ;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Calculate distance (<= actual) to closest surface of shape from outside
+// - Calculate distance to z, radial planes
+// - Only to phi planes if outside phi extent
+// - Return 0 if point inside
+
+G4double G4ShiftedCone::DistanceToIn(const G4ThreeVector& p) const
+{
+  G4double safe=0.0, rho, safeR1, safeR2, safeZ;//, safePhi, cosPsi ;
+  G4double tanRMin, secRMin, pRMin ;
+  G4double tanRMax, secRMax, pRMax ;
+
+  G4double z = p.z() - fZshift;
+
+  rho   = std::sqrt(p.x()*p.x() + p.y()*p.y()) ;
+  safeZ = std::fabs(z) - fDz ;
+
+  if ( fRmin1 || fRmin2 )
+  {
+    tanRMin = (fRmin2 - fRmin1)*0.5/fDz ;
+    secRMin = std::sqrt(1.0 + tanRMin*tanRMin) ;
+    pRMin   = tanRMin*z + (fRmin1 + fRmin2)*0.5 ;
+    safeR1  = (pRMin - rho)/secRMin ;
+
+    tanRMax = (fRmax2 - fRmax1)*0.5/fDz ;
+    secRMax = std::sqrt(1.0 + tanRMax*tanRMax) ;
+    pRMax   = tanRMax*z + (fRmax1 + fRmax2)*0.5 ;
+    safeR2  = (rho - pRMax)/secRMax ;
+
+    if ( safeR1 > safeR2) { safe = safeR1; }
+    else                  { safe = safeR2; }
+  }
+  else
+  {
+    tanRMax = (fRmax2 - fRmax1)*0.5/fDz ;
+    secRMax = std::sqrt(1.0 + tanRMax*tanRMax) ;
+    pRMax   = tanRMax*z + (fRmax1 + fRmax2)*0.5 ;
+    safe    = (rho - pRMax)/secRMax ;
+  }
+  if ( safeZ > safe )  { safe = safeZ; }
+
+/*  if ( !fPhiFullCone && rho )
+  {
+    // Psi=angle from central phi to point
+
+    cosPsi = (p.x()*cosCPhi + p.y()*sinCPhi)/rho ;
+
+    if ( cosPsi < std::cos(fDPhi*0.5) ) // Point lies outside phi range
+    {
+      if ( (p.y()*cosCPhi - p.x()*sinCPhi) <= 0.0 )
+      {
+        safePhi = std::fabs(p.x()*std::sin(fSPhi)-p.y()*std::cos(fSPhi));
+      }
+      else
+      {
+        safePhi = std::fabs(p.x()*sinEPhi-p.y()*cosEPhi);
+      }
+      if ( safePhi > safe )  { safe = safePhi; }
+    }
+  }*/
+  if ( safe < 0.0 )  { safe = 0.0; }
+
+  return safe ;
+}
+
+///////////////////////////////////////////////////////////////
+//
+// Calculate distance to surface of shape from 'inside', allowing for tolerance
+// - Only Calc rmax intersection if no valid rmin intersection
+
+G4double G4ShiftedCone::DistanceToOut( const G4ThreeVector& p,
+                                const G4ThreeVector& v,
+                                const G4bool calcNorm,
+                                      G4bool *validNorm,
+                                      G4ThreeVector *n) const
+{
+  ESide side = kNull, sider = kNull;//, sidephi = kNull;
+
+  G4double snxt,srd,/*sphi,*/pdist ;
+
+  G4double tanRMax, secRMax, rMaxAv ;  // Data for outer cone
+  G4double tanRMin, secRMin, rMinAv ;  // Data for inner cone
+
+  G4double t1, t2, t3, rout, rin, nt1, nt2, nt3 ;
+  G4double b, c, d, sr2, sr3 ;
+
+  // Vars for intersection within tolerance
+
+  ESide    sidetol = kNull ;
+  G4double slentol = kInfinity ;
+
+  // Vars for phi intersection:
+
+//  G4double pDistS, compS, pDistE, compE, sphi2, vphi ;
+  G4double zi, ri, deltaRoi2, xi, yi, risec ;
+
+  // Z plane intersection
+
+  G4double z = p.z() - fZshift;
+
+  if ( v.z() > 0.0 )
+  {
+    pdist = fDz - z ;
+
+    if (pdist > halfCarTolerance)
+    {
+      snxt = pdist/v.z() ;
+      side = kPZ ;
+    }
+    else
+    {
+      if (calcNorm)
+      {
+        *n         = G4ThreeVector(0,0,1) ;
+        *validNorm = true ;
+      }
+      return  snxt = 0.0;
+    }
+  }
+  else if ( v.z() < 0.0 )
+  {
+    pdist = fDz + z ;
+
+    if ( pdist > halfCarTolerance)
+    {
+      snxt = -pdist/v.z() ;
+      side = kMZ ;
+    }
+    else
+    {
+      if ( calcNorm )
+      {
+        *n         = G4ThreeVector(0,0,-1) ;
+        *validNorm = true ;
+      }
+      return snxt = 0.0 ;
+    }
+  }
+  else     // Travel perpendicular to z axis
+  {
+    snxt = kInfinity ;
+    side = kNull ;
+  }
+
+  // Radial Intersections
+  //
+  // Intersection with outer cone (possible return) and
+  //                   inner cone (must also check phi)
+  //
+  // Intersection point (xi,yi,zi) on line x=p.x+t*v.x etc.
+  //
+  // Intersects with x^2+y^2=(a*z+b)^2
+  //
+  // where a=tanRMax or tanRMin
+  //       b=rMaxAv  or rMinAv
+  //
+  // (vx^2+vy^2-(a*vz)^2)t^2+2t(pxvx+pyvy-a*vz(a*pz+b))+px^2+py^2-(a*pz+b)^2=0 ;
+  //     t1                        t2                      t3
+  //
+  //  \--------u-------/       \-----------v----------/ \---------w--------/
+
+  tanRMax = (fRmax2 - fRmax1)*0.5/fDz ;
+  secRMax = std::sqrt(1.0 + tanRMax*tanRMax) ;
+  rMaxAv  = (fRmax1 + fRmax2)*0.5 ;
+
+
+  t1   = 1.0 - v.z()*v.z() ;      // since v normalised
+  t2   = p.x()*v.x() + p.y()*v.y() ;
+  t3   = p.x()*p.x() + p.y()*p.y() ;
+  rout = tanRMax*z + rMaxAv ;
+
+  nt1 = t1 - (tanRMax*v.z())*(tanRMax*v.z()) ;
+  nt2 = t2 - tanRMax*v.z()*rout ;
+  nt3 = t3 - rout*rout ;
+
+  if (v.z() > 0.0)
+  {
+    deltaRoi2 = snxt*snxt*t1 + 2*snxt*t2 + t3
+                - fRmax2*(fRmax2 + kRadTolerance*secRMax);
+  }
+  else if ( v.z() < 0.0 )
+  {
+    deltaRoi2 = snxt*snxt*t1 + 2*snxt*t2 + t3
+                - fRmax1*(fRmax1 + kRadTolerance*secRMax);
+  }
+  else
+  {
+    deltaRoi2 = 1.0;
+  }
+
+  if ( nt1 && (deltaRoi2 > 0.0) )
+  {
+    // Equation quadratic => 2 roots : second root must be leaving
+
+    b = nt2/nt1 ;
+    c = nt3/nt1 ;
+    d = b*b - c ;
+
+    if ( d >= 0 )
+    {
+      // Check if on outer cone & heading outwards
+      // NOTE: Should use rho-rout>-kRadTolerance*0.5
+
+      if (nt3 > -halfRadTolerance && nt2 >= 0 )
+      {
+        if (calcNorm)
+        {
+          risec      = std::sqrt(t3)*secRMax ;
+          *validNorm = true ;
+          *n         = G4ThreeVector(p.x()/risec,p.y()/risec,-tanRMax/secRMax);
+        }
+        return snxt=0 ;
+      }
+      else
+      {
+        sider = kRMax  ;
+        if (b>0) { srd = -b - std::sqrt(d);    }
+        else     { srd = c/(-b+std::sqrt(d)) ; }
+
+        zi    = z + srd*v.z() ;
+        ri    = tanRMax*zi + rMaxAv ;
+
+        if ((ri >= 0) && (-halfRadTolerance <= srd) && (srd <= halfRadTolerance))
+        {
+          // An intersection within the tolerance
+          //   we will Store it in case it is good -
+          //
+          slentol = srd ;
+          sidetol = kRMax ;
+        }
+        if ( (ri < 0) || (srd < halfRadTolerance) )
+        {
+          // Safety: if both roots -ve ensure that srd cannot `win'
+          //         distance to out
+
+          if (b>0) { sr2 = c/(-b-std::sqrt(d)); }
+          else     { sr2 = -b + std::sqrt(d);   }
+          zi  = z + sr2*v.z() ;
+          ri  = tanRMax*zi + rMaxAv ;
+
+          if ((ri >= 0) && (sr2 > halfRadTolerance))
+          {
+            srd = sr2;
+          }
+          else
+          {
+            srd = kInfinity ;
+
+            if( (-halfRadTolerance <= sr2) && ( sr2 <= halfRadTolerance) )
+            {
+              // An intersection within the tolerance.
+              // Storing it in case it is good.
+
+              slentol = sr2 ;
+              sidetol = kRMax ;
+            }
+          }
+        }
+      }
+    }
+    else
+    {
+      // No intersection with outer cone & not parallel
+      // -> already outside, no intersection
+
+      if ( calcNorm )
+      {
+        risec      = std::sqrt(t3)*secRMax;
+        *validNorm = true;
+        *n         = G4ThreeVector(p.x()/risec,p.y()/risec,-tanRMax/secRMax);
+      }
+      return snxt = 0.0 ;
+    }
+  }
+  else if ( nt2 && (deltaRoi2 > 0.0) )
+  {
+    // Linear case (only one intersection) => point outside outer cone
+
+    if ( calcNorm )
+    {
+      risec      = std::sqrt(t3)*secRMax;
+      *validNorm = true;
+      *n         = G4ThreeVector(p.x()/risec,p.y()/risec,-tanRMax/secRMax);
+    }
+    return snxt = 0.0 ;
+  }
+  else
+  {
+    // No intersection -> parallel to outer cone
+    // => Z or inner cone intersection
+
+    srd = kInfinity ;
+  }
+
+  // Check possible intersection within tolerance
+
+  if ( slentol <= halfCarTolerance )
+  {
+    // An intersection within the tolerance was found.
+    // We must accept it only if the momentum points outwards.
+    //
+    // G4ThreeVector ptTol ;  // The point of the intersection
+    // ptTol= p + slentol*v ;
+    // ri=tanRMax*zi+rMaxAv ;
+    //
+    // Calculate a normal vector,  as below
+
+    xi    = p.x() + slentol*v.x();
+    yi    = p.y() + slentol*v.y();
+    risec = std::sqrt(xi*xi + yi*yi)*secRMax;
+    G4ThreeVector Normal = G4ThreeVector(xi/risec,yi/risec,-tanRMax/secRMax);
+
+    if ( Normal.dot(v) > 0 )    // We will leave the Cone immediatelly
+    {
+      if ( calcNorm )
+      {
+        *n         = Normal.unit() ;
+        *validNorm = true ;
+      }
+      return snxt = 0.0 ;
+    }
+    else // On the surface, but not heading out so we ignore this intersection
+    {    //                                        (as it is within tolerance).
+      slentol = kInfinity ;
+    }
+  }
+
+  // Inner Cone intersection
+
+  if ( fRmin1 || fRmin2 )
+  {
+    tanRMin = (fRmin2 - fRmin1)*0.5/fDz ;
+    nt1     = t1 - (tanRMin*v.z())*(tanRMin*v.z()) ;
+
+    if ( nt1 )
+    {
+      secRMin = std::sqrt(1.0 + tanRMin*tanRMin) ;
+      rMinAv  = (fRmin1 + fRmin2)*0.5 ;
+      rin     = tanRMin*z + rMinAv ;
+      nt2     = t2 - tanRMin*v.z()*rin ;
+      nt3     = t3 - rin*rin ;
+
+      // Equation quadratic => 2 roots : first root must be leaving
+
+      b = nt2/nt1 ;
+      c = nt3/nt1 ;
+      d = b*b - c ;
+
+      if ( d >= 0.0 )
+      {
+        // NOTE: should be rho-rin<kRadTolerance*0.5,
+        //       but using squared versions for efficiency
+
+        if (nt3 < kRadTolerance*(rin + kRadTolerance*0.25))
+        {
+          if ( nt2 < 0.0 )
+          {
+            if (calcNorm)  { *validNorm = false; }
+            return          snxt      = 0.0;
+          }
+        }
+        else
+        {
+          if (b>0) { sr2 = -b - std::sqrt(d);   }
+          else     { sr2 = c/(-b+std::sqrt(d)); }
+          zi  = z + sr2*v.z() ;
+          ri  = tanRMin*zi + rMinAv ;
+
+          if( (ri>=0.0)&&(-halfRadTolerance<=sr2)&&(sr2<=halfRadTolerance) )
+          {
+            // An intersection within the tolerance
+            // storing it in case it is good.
+
+            slentol = sr2 ;
+            sidetol = kRMax ;
+          }
+          if( (ri<0) || (sr2 < halfRadTolerance) )
+          {
+            if (b>0) { sr3 = c/(-b-std::sqrt(d)); }
+            else     { sr3 = -b + std::sqrt(d) ;  }
+
+            // Safety: if both roots -ve ensure that srd cannot `win'
+            //         distancetoout
+
+            if  ( sr3 > halfRadTolerance )
+            {
+              if( sr3 < srd )
+              {
+                zi = z + sr3*v.z() ;
+                ri = tanRMin*zi + rMinAv ;
+
+                if ( ri >= 0.0 )
+                {
+                  srd=sr3 ;
+                  sider=kRMin ;
+                }
+              }
+            }
+            else if ( sr3 > -halfRadTolerance )
+            {
+              // Intersection in tolerance. Store to check if it's good
+
+              slentol = sr3 ;
+              sidetol = kRMin ;
+            }
+          }
+          else if ( (sr2 < srd) && (sr2 > halfCarTolerance) )
+          {
+            srd   = sr2 ;
+            sider = kRMin ;
+          }
+          else if (sr2 > -halfCarTolerance)
+          {
+            // Intersection in tolerance. Store to check if it's good
+
+            slentol = sr2 ;
+            sidetol = kRMin ;
+          }
+          if( slentol <= halfCarTolerance  )
+          {
+            // An intersection within the tolerance was found.
+            // We must accept it only if  the momentum points outwards.
+
+            G4ThreeVector Normal ;
+
+            // Calculate a normal vector,  as below
+
+            xi     = p.x() + slentol*v.x() ;
+            yi     = p.y() + slentol*v.y() ;
+            if( sidetol==kRMax )
+            {
+              risec  = std::sqrt(xi*xi + yi*yi)*secRMax ;
+              Normal = G4ThreeVector(xi/risec,yi/risec,-tanRMax/secRMax) ;
+            }
+            else
+            {
+              risec  = std::sqrt(xi*xi + yi*yi)*secRMin ;
+              Normal = G4ThreeVector(-xi/risec,-yi/risec,tanRMin/secRMin) ;
+            }
+            if( Normal.dot(v) > 0 )
+            {
+              // We will leave the cone immediately
+
+              if( calcNorm )
+              {
+                *n         = Normal.unit() ;
+                *validNorm = true ;
+              }
+              return snxt = 0.0 ;
+            }
+            else
+            {
+              // On the surface, but not heading out so we ignore this
+              // intersection (as it is within tolerance).
+
+              slentol = kInfinity ;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // Linear case => point outside inner cone ---> outer cone intersect
+  //
+  // Phi Intersection
+/*
+  if ( !fPhiFullCone )
+  {
+    // add angle calculation with correction
+    // of the difference in domain of atan2 and Sphi
+
+    vphi = std::atan2(v.y(),v.x()) ;
+
+    if ( vphi < fSPhi - halfAngTolerance  )              { vphi += twopi; }
+    else if ( vphi > fSPhi + fDPhi + halfAngTolerance )  { vphi -= twopi; }
+
+    if ( p.x() || p.y() )   // Check if on z axis (rho not needed later)
+    {
+      // pDist -ve when inside
+
+      pDistS = p.x()*sinSPhi - p.y()*cosSPhi ;
+      pDistE = -p.x()*sinEPhi + p.y()*cosEPhi ;
+
+      // Comp -ve when in direction of outwards normal
+
+      compS = -sinSPhi*v.x() + cosSPhi*v.y() ;
+      compE = sinEPhi*v.x() - cosEPhi*v.y() ;
+
+      sidephi = kNull ;
+
+      if( ( (fDPhi <= pi) && ( (pDistS <= halfCarTolerance)
+                            && (pDistE <= halfCarTolerance) ) )
+         || ( (fDPhi >  pi) && !((pDistS >  halfCarTolerance)
+                              && (pDistE >  halfCarTolerance) ) )  )
+      {
+        // Inside both phi *full* planes
+        if ( compS < 0 )
+        {
+          sphi = pDistS/compS ;
+          if (sphi >= -halfCarTolerance)
+          {
+            xi = p.x() + sphi*v.x() ;
+            yi = p.y() + sphi*v.y() ;
+
+            // Check intersecting with correct half-plane
+            // (if not -> no intersect)
+            //
+            if ( (std::fabs(xi)<=kCarTolerance)
+              && (std::fabs(yi)<=kCarTolerance) )
+            {
+              sidephi= kSPhi;
+              if ( ( fSPhi-halfAngTolerance <= vphi )
+                && ( fSPhi+fDPhi+halfAngTolerance >=vphi ) )
+              {
+                sphi = kInfinity;
+              }
+            }
+            else
+            if ( (yi*cosCPhi-xi*sinCPhi)>=0 )
+            {
+              sphi = kInfinity ;
+            }
+            else
+            {
+              sidephi = kSPhi ;
+              if ( pDistS > -halfCarTolerance )
+              {
+                sphi = 0.0 ; // Leave by sphi immediately
+              }
+            }
+          }
+          else
+          {
+            sphi = kInfinity ;
+          }
+        }
+        else
+        {
+          sphi = kInfinity ;
+        }
+
+        if ( compE < 0 )
+        {
+          sphi2 = pDistE/compE ;
+
+          // Only check further if < starting phi intersection
+          //
+          if ( (sphi2 > -halfCarTolerance) && (sphi2 < sphi) )
+          {
+            xi = p.x() + sphi2*v.x() ;
+            yi = p.y() + sphi2*v.y() ;
+
+            // Check intersecting with correct half-plane
+
+            if ( (std::fabs(xi)<=kCarTolerance)
+              && (std::fabs(yi)<=kCarTolerance) )
+            {
+              // Leaving via ending phi
+
+              if(!( (fSPhi-halfAngTolerance <= vphi)
+                 && (fSPhi+fDPhi+halfAngTolerance >= vphi) ) )
+              {
+                sidephi = kEPhi ;
+                if ( pDistE <= -halfCarTolerance )  { sphi = sphi2; }
+                else                                { sphi = 0.0; }
+              }
+            }
+            else // Check intersecting with correct half-plane
+            if ( yi*cosCPhi-xi*sinCPhi >= 0 )
+            {
+              // Leaving via ending phi
+
+              sidephi = kEPhi ;
+              if ( pDistE <= -halfCarTolerance )  { sphi = sphi2; }
+              else                                { sphi = 0.0; }
+            }
+          }
+        }
+      }
+      else
+      {
+        sphi = kInfinity ;
+      }
+    }
+    else
+    {
+      // On z axis + travel not || to z axis -> if phi of vector direction
+      // within phi of shape, Step limited by rmax, else Step =0
+
+      if ( (fSPhi-halfAngTolerance <= vphi)
+        && (vphi <= fSPhi+fDPhi+halfAngTolerance) )
+      {
+        sphi = kInfinity ;
+      }
+      else
+      {
+        sidephi = kSPhi  ;   // arbitrary
+        sphi    = 0.0 ;
+      }
+    }
+    if ( sphi < snxt )  // Order intersecttions
+    {
+      snxt=sphi ;
+      side=sidephi ;
+    }
+  }
+*/
+  if ( srd < snxt )  // Order intersections
+  {
+    snxt = srd   ;
+    side = sider ;
+  }
+  if (calcNorm)
+  {
+    switch(side)
+    {                     // Note: returned vector not normalised
+      case kRMax:         // (divide by frmax for unit vector)
+        xi         = p.x() + snxt*v.x() ;
+        yi         = p.y() + snxt*v.y() ;
+        risec      = std::sqrt(xi*xi + yi*yi)*secRMax ;
+        *n         = G4ThreeVector(xi/risec,yi/risec,-tanRMax/secRMax) ;
+        *validNorm = true ;
+        break ;
+      case kRMin:
+        *validNorm = false ;  // Rmin is inconvex
+        break ;
+/*      case kSPhi:
+        if ( fDPhi <= pi )
+        {
+          *n         = G4ThreeVector(sinSPhi, -cosSPhi, 0);
+          *validNorm = true ;
+        }
+        else
+        {
+          *validNorm = false ;
+        }
+        break ;
+      case kEPhi:
+        if ( fDPhi <= pi )
+        {
+          *n = G4ThreeVector(-sinEPhi, cosEPhi, 0);
+          *validNorm = true ;
+        }
+        else
+        {
+          *validNorm = false ;
+        }
+        break ;*/
+      case kPZ:
+        *n         = G4ThreeVector(0,0,1) ;
+        *validNorm = true ;
+        break ;
+      case kMZ:
+        *n         = G4ThreeVector(0,0,-1) ;
+        *validNorm = true ;
+        break ;
+      default:
+        G4cout << G4endl ;
+        DumpInfo();
+        std::ostringstream message;
+        G4int oldprc = message.precision(16) ;
+        message << "Undefined side for valid surface normal to solid."
+                << G4endl
+                << "Position:"  << G4endl << G4endl
+                << "p.x() = "   << p.x()/mm << " mm" << G4endl
+                << "p.y() = "   << p.y()/mm << " mm" << G4endl
+                << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl
+                << "pho at z = "   << std::sqrt( p.x()*p.x()+p.y()*p.y() )/mm
+                << " mm" << G4endl << G4endl ;
+        if( p.x() != 0. || p.y() != 0.)
+        {
+           message << "point phi = "   << std::atan2(p.y(),p.x())/degree
+                   << " degree" << G4endl << G4endl ;
+        }
+        message << "Direction:" << G4endl << G4endl
+                << "v.x() = "   << v.x() << G4endl
+                << "v.y() = "   << v.y() << G4endl
+                << "v.z() = "   << v.z() << G4endl<< G4endl
+                << "Proposed distance :" << G4endl<< G4endl
+                << "snxt = "    << snxt/mm << " mm" << G4endl ;
+        message.precision(oldprc) ;
+        G4Exception("G4ShiftedCone::DistanceToOut(p,v,..)","GeomSolids1002",
+                    JustWarning, message) ;
+        break ;
+    }
+  }
+  if (snxt < halfCarTolerance)  { snxt = 0.; }
+
+  return snxt ;
+}
+
+//////////////////////////////////////////////////////////////////
+//
+// Calculate distance (<=actual) to closest surface of shape from inside
+
+G4double G4ShiftedCone::DistanceToOut(const G4ThreeVector& p) const
+{
+  G4double safe=0.0, rho, safeR1, safeR2, safeZ;//, safePhi;
+  G4double tanRMin, secRMin, pRMin;
+  G4double tanRMax, secRMax, pRMax;
+
+#ifdef G4CSGDEBUG
+  if( Inside(p) == kOutside )
+  {
+    G4int oldprc=G4cout.precision(16) ;
+    G4cout << G4endl ;
+    DumpInfo();
+    G4cout << "Position:"  << G4endl << G4endl ;
+    G4cout << "p.x() = "   << p.x()/mm << " mm" << G4endl ;
+    G4cout << "p.y() = "   << p.y()/mm << " mm" << G4endl ;
+    G4cout << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl ;
+    G4cout << "pho at z = "   << std::sqrt( p.x()*p.x()+p.y()*p.y() )/mm
+           << " mm" << G4endl << G4endl ;
+    if( (p.x() != 0.) || (p.x() != 0.) )
+    {
+      G4cout << "point phi = "   << std::atan2(p.y(),p.x())/degree
+             << " degree" << G4endl << G4endl ;
+    }
+    G4cout.precision(oldprc) ;
+    G4Exception("G4ShiftedCone::DistanceToOut(p)", "GeomSolids1002",
+                JustWarning, "Point p is outside !?" );
+  }
+#endif
+
+  G4double z = p.z() - fZshift;
+
+  rho = std::sqrt(p.x()*p.x() + p.y()*p.y()) ;
+  safeZ = fDz - std::fabs(z) ;
+
+  if (fRmin1 || fRmin2)
+  {
+    tanRMin = (fRmin2 - fRmin1)*0.5/fDz ;
+    secRMin = std::sqrt(1.0 + tanRMin*tanRMin) ;
+    pRMin   = tanRMin*z + (fRmin1 + fRmin2)*0.5 ;
+    safeR1  = (rho - pRMin)/secRMin ;
+  }
+  else
+  {
+    safeR1 = kInfinity ;
+  }
+
+  tanRMax = (fRmax2 - fRmax1)*0.5/fDz ;
+  secRMax = std::sqrt(1.0 + tanRMax*tanRMax) ;
+  pRMax   = tanRMax*z + (fRmax1+fRmax2)*0.5 ;
+  safeR2  = (pRMax - rho)/secRMax ;
+
+  if (safeR1 < safeR2)  { safe = safeR1; }
+  else                  { safe = safeR2; }
+  if (safeZ < safe)     { safe = safeZ ; }
+
+  // Check if phi divided, Calc distances closest phi plane
+/*
+  if (!fPhiFullCone)
+  {
+    // Above/below central phi of G4ShiftedCone?
+
+    if ( (p.y()*cosCPhi - p.x()*sinCPhi) <= 0 )
+    {
+      safePhi = -(p.x()*sinSPhi - p.y()*cosSPhi) ;
+    }
+    else
+    {
+      safePhi = (p.x()*sinEPhi - p.y()*cosEPhi) ;
+    }
+    if (safePhi < safe)  { safe = safePhi; }
+  }*/
+  if ( safe < 0 )  { safe = 0; }
+
+  return safe ;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// GetEntityType
+
+G4GeometryType G4ShiftedCone::GetEntityType() const
+{
+  return G4String("G4ShiftedCone");
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Make a clone of the object
+//
+G4VSolid* G4ShiftedCone::Clone() const
+{
+  return new G4ShiftedCone(*this);
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Stream object contents to an output stream
+
+std::ostream& G4ShiftedCone::StreamInfo(std::ostream& os) const
+{
+  G4int oldprc = os.precision(16);
+  os << "-----------------------------------------------------------\n"
+     << "    *** Dump for solid - " << GetName() << " ***\n"
+     << "    ===================================================\n"
+     << " Solid type: G4ShiftedCone\n"
+     << " Parameters: \n"
+     << "   inside  -fDz radius: "  << fRmin1/mm << " mm \n"
+     << "   outside -fDz radius: "  << fRmax1/mm << " mm \n"
+     << "   inside  +fDz radius: "  << fRmin2/mm << " mm \n"
+     << "   outside +fDz radius: "  << fRmax2/mm << " mm \n"
+     << "   Z1                 : "  << GetZ1()/mm << " mm \n"
+     << "   Z2                 : "  << GetZ2()/mm << " mm \n"
+//     << "   starting angle of segment: " << fSPhi/degree << " degrees \n"
+//     << "   delta angle of segment   : " << fDPhi/degree << " degrees \n"
+     << "-----------------------------------------------------------\n";
+  os.precision(oldprc);
+
+  return os;
+}
+
+
+
+/////////////////////////////////////////////////////////////////////////
+//
+// GetPointOnSurface
+
+G4ThreeVector G4ShiftedCone::GetPointOnSurface() const
+{
+  // declare working variables
+  //
+  G4double rone = (fRmax1-fRmax2)/(2.*fDz);
+  G4double rtwo = (fRmin1-fRmin2)/(2.*fDz);
+  G4double qone = (fRmax1 == fRmax2) ? 0. : fDz*(fRmax1+fRmax2)/(fRmax1-fRmax2);
+  G4double qtwo = (fRmin1 == fRmin2) ? 0. : fDz*(fRmin1+fRmin2)/(fRmin1-fRmin2);
+
+  G4double slin   = std::hypot(fRmin1-fRmin2, 2.*fDz);
+  G4double slout  = std::hypot(fRmax1-fRmax2, 2.*fDz);
+  G4double Aone   = 0.5*GetDeltaPhiAngle()*(fRmax2 + fRmax1)*slout; // outer surface
+  G4double Atwo   = 0.5*GetDeltaPhiAngle()*(fRmin2 + fRmin1)*slin;  // inner surface
+  G4double Athree = 0.5*GetDeltaPhiAngle()*(fRmax1*fRmax1-fRmin1*fRmin1); // base at -Dz
+  G4double Afour  = 0.5*GetDeltaPhiAngle()*(fRmax2*fRmax2-fRmin2*fRmin2); // base at +Dz
+  G4double Afive  = fDz*(fRmax1-fRmin1+fRmax2-fRmin2); // phi section
+
+  G4double phi    = G4RandFlat::shoot(GetStartPhiAngle(),GetStartPhiAngle() + GetDeltaPhiAngle());
+  G4double cosu   = std::cos(phi);
+  G4double sinu   = std::sin(phi);
+  G4double rRand1 = GetRadiusInRing(fRmin1, fRmax1);
+  G4double rRand2 = GetRadiusInRing(fRmin2, fRmax2);
+
+  G4bool fPhiFullCone = true;
+  if ( (GetStartPhiAngle() == 0.) && fPhiFullCone )  { Afive = 0.; }
+  G4double chose  = G4RandFlat::shoot(0.,Aone+Atwo+Athree+Afour+2.*Afive);
+
+  if( (chose >= 0.) && (chose < Aone) ) // outer surface
+  {
+    if(fRmax1 != fRmax2)
+    {
+      G4double zRand = G4RandFlat::shoot(-1.*fDz,fDz);
+      return G4ThreeVector (rone*cosu*(qone-zRand),
+                            rone*sinu*(qone-zRand), zRand + fZshift);
+    }
+    else
+    {
+      return G4ThreeVector(fRmax1*cosu, fRmax2*sinu,
+                           G4RandFlat::shoot(-1.*fDz,fDz) + fZshift);
+    }
+  }
+  else if( (chose >= Aone) && (chose < Aone + Atwo) )  // inner surface
+  {
+    if(fRmin1 != fRmin2)
+    {
+      G4double zRand = G4RandFlat::shoot(-1.*fDz,fDz);
+      return G4ThreeVector (rtwo*cosu*(qtwo-zRand),
+                            rtwo*sinu*(qtwo-zRand), zRand + fZshift);
+    }
+    else
+    {
+      return G4ThreeVector(fRmin1*cosu, fRmin2*sinu,
+                           G4RandFlat::shoot(-1.*fDz,fDz) + fZshift);
+    }
+  }
+  else if( (chose >= Aone + Atwo) && (chose < Aone + Atwo + Athree) ) // base at -Dz
+  {
+    return G4ThreeVector (rRand1*cosu, rRand1*sinu, -1*fDz + fZshift);
+  }
+  else if( (chose >= Aone + Atwo + Athree)
+        && (chose < Aone + Atwo + Athree + Afour) ) // base at +Dz
+  {
+    return G4ThreeVector (rRand2*cosu,rRand2*sinu,fDz + fZshift);
+  }
+  else if( (chose >= Aone + Atwo + Athree + Afour) // SPhi section
+        && (chose < Aone + Atwo + Athree + Afour + Afive) )
+  {
+    G4double zRand  = G4RandFlat::shoot(-1.*fDz,fDz);
+    rRand1 = G4RandFlat::shoot(fRmin2-((zRand-fDz)/(2.*fDz))*(fRmin1-fRmin2),
+                               fRmax2-((zRand-fDz)/(2.*fDz))*(fRmax1-fRmax2));
+    return G4ThreeVector (rRand1*GetCosStartPhi(),
+                          rRand1*GetSinStartPhi(), zRand + fZshift);
+  }
+  else // SPhi+DPhi section
+  {
+    G4double zRand  = G4RandFlat::shoot(-1.*fDz,fDz);
+    rRand1 = G4RandFlat::shoot(fRmin2-((zRand-fDz)/(2.*fDz))*(fRmin1-fRmin2),
+                               fRmax2-((zRand-fDz)/(2.*fDz))*(fRmax1-fRmax2));
+    return G4ThreeVector (rRand1*GetCosEndPhi(),
+                          rRand1*GetSinEndPhi(), zRand + fZshift);
+  }
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Methods for visualisation
+
+void G4ShiftedCone::DescribeYourselfTo (G4VGraphicsScene& scene) const
+{
+  scene.AddSolid (*this);
+}
+
+G4Polyhedron* G4ShiftedCone::CreatePolyhedron () const
+{
+  G4double rmin[2] = { GetRmin1(), GetRmin2() };
+  G4double rmax[2] = { GetRmax1(), GetRmax2() };
+  G4double z[2] = { GetZ1(), GetZ2() };
+  return new G4PolyhedronPcon(
+    GetStartPhiAngle(), GetDeltaPhiAngle(), 2, z, rmin, rmax
+  );
+}
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/G4ShiftedCone.h b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/G4ShiftedCone.h
new file mode 100644
index 0000000000000000000000000000000000000000..8107ba9347bd639ba9e2cab64da46cffb9cca0b1
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/G4ShiftedCone.h
@@ -0,0 +1,247 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+//
+// The G4ShiftedCone class copied from standard G4Cons and modified to:
+// 1) have an arbitrary position along Z axis,
+// 2) represent a twopi-cone. Sectors are not supported but the
+//    corresponding code kept and just commented out.
+//
+
+//
+// ********************************************************************
+// * License and Disclaimer                                           *
+// *                                                                  *
+// * The  Geant4 software  is  copyright of the Copyright Holders  of *
+// * the Geant4 Collaboration.  It is provided  under  the terms  and *
+// * conditions of the Geant4 Software License,  included in the file *
+// * LICENSE and available at  http://cern.ch/geant4/license .  These *
+// * include a list of copyright holders.                             *
+// *                                                                  *
+// * Neither the authors of this software system, nor their employing *
+// * institutes,nor the agencies providing financial support for this *
+// * work  make  any representation or  warranty, express or implied, *
+// * regarding  this  software system or assume any liability for its *
+// * use.  Please see the license in the file  LICENSE  and URL above *
+// * for the full disclaimer and the limitation of liability.         *
+// *                                                                  *
+// * This  code  implementation is the result of  the  scientific and *
+// * technical work of the GEANT4 collaboration.                      *
+// * By using,  copying,  modifying or  distributing the software (or *
+// * any work based  on the software)  you  agree  to acknowledge its *
+// * use  in  resulting  scientific  publications,  and indicate your *
+// * acceptance of all terms of the Geant4 Software license.          *
+// ********************************************************************
+//
+//
+//
+//
+// --------------------------------------------------------------------
+// GEANT 4 class header file
+//
+// G4ShiftedCone
+//
+// Class description:
+//
+//   A G4ShiftedCone is, in the general case, a Phi segment of a cone,
+//   inner and outer radii specified at z1 and z2.
+//   This version does not support phi segmetation, but the code is kept
+//   in case it would be necessary later.
+//   The Phi segment is described by a starting fSPhi angle, and the
+//   +fDPhi delta angle for the shape.
+//   If the delta angle is >=2*pi, the shape is treated as continuous
+//   in Phi
+//
+//   Member Data:
+//
+//  fRmin1  inside radius at  z1
+//  fRmin2  inside radius at  z2
+//  fRmax1  outside radius at z1
+//  fRmax2  outside radius at z2
+//  fDz  half length in z
+//
+//  fSPhi  starting angle of the segment in radians
+//  fDPhi  delta angle of the segment in radians
+//
+//  fPhiFullCone   Boolean variable used for indicate the Phi Section
+//  always true in the current version, hope compiler optimize
+//
+//   Note:
+//      Internally fSPhi & fDPhi are adjusted so that fDPhi<=2PI,
+//      and fDPhi+fSPhi<=2PI. This enables simpler comparisons to be
+//      made with (say) Phi of a point.
+
+// History:
+// 03.07.2019 A. Sukharev copied from G4Cons for ATLAS EMEC needs
+// --------------------------------------------------------------------
+#ifndef G4ShiftedCone_HH
+#define G4ShiftedCone_HH
+
+#include <CLHEP/Units/PhysicalConstants.h>
+
+#include "G4CSGSolid.hh"
+#include "G4Polyhedron.hh"
+
+class G4ShiftedCone : public G4CSGSolid
+{
+  public:  // with description
+
+    G4ShiftedCone(const G4String& pName,
+                 G4double pZ1, G4double pZ2,
+                 G4double pRmin1, G4double pRmax1,
+                 G4double pRmin2, G4double pRmax2);
+      //         G4double pSPhi, G4double pDPhi);
+      //
+      // Constructs a cone with the given name and dimensions
+
+   ~G4ShiftedCone() ;
+      //
+      // Destructor
+
+    // Accessors
+
+    inline G4double GetInnerRadiusMinusZ() const;
+    inline G4double GetOuterRadiusMinusZ() const;
+    inline G4double GetInnerRadiusPlusZ()  const;
+    inline G4double GetOuterRadiusPlusZ()  const;
+    inline G4double GetZHalfLength()       const;
+    inline G4double GetZ1()                const;
+    inline G4double GetZ2()                const;
+    inline G4double GetStartPhiAngle()     const;
+    inline G4double GetDeltaPhiAngle()     const;
+    inline G4double GetSinStartPhi()       const;
+    inline G4double GetCosStartPhi()       const;
+    inline G4double GetSinEndPhi()         const;
+    inline G4double GetCosEndPhi()         const;
+
+    // Modifiers
+
+    inline void SetInnerRadiusMinusZ (G4double Rmin1 );
+    inline void SetOuterRadiusMinusZ (G4double Rmax1 );
+    inline void SetInnerRadiusPlusZ  (G4double Rmin2 );
+    inline void SetOuterRadiusPlusZ  (G4double Rmax2 );
+//    inline void SetStartPhiAngle     (G4double newSPhi, G4bool trig=true);
+//    inline void SetDeltaPhiAngle     (G4double newDPhi);
+
+    // Other methods for solid
+
+    inline G4double GetCubicVolume();
+    inline G4double GetSurfaceArea();
+
+    void ComputeDimensions(       G4VPVParameterisation* p,
+                            const G4int n,
+                            const G4VPhysicalVolume* pRep );
+
+    void BoundingLimits(G4ThreeVector& pMin, G4ThreeVector& pMax) const;
+
+    G4bool CalculateExtent( const EAxis pAxis,
+                            const G4VoxelLimits& pVoxelLimit,
+                            const G4AffineTransform& pTransform,
+                                  G4double& pMin, G4double& pMax ) const;
+
+    EInside Inside( const G4ThreeVector& p ) const;
+
+    G4ThreeVector SurfaceNormal( const G4ThreeVector& p ) const;
+
+    G4double DistanceToIn (const G4ThreeVector& p,
+                           const G4ThreeVector& v) const;
+    G4double DistanceToIn (const G4ThreeVector& p) const;
+    G4double DistanceToOut(const G4ThreeVector& p,
+                           const G4ThreeVector& v,
+                           const G4bool calcNorm=G4bool(false),
+                                 G4bool *validNorm=0,
+                                 G4ThreeVector *n=0) const;
+    G4double DistanceToOut(const G4ThreeVector& p) const;
+
+    G4GeometryType GetEntityType() const;
+
+    G4ThreeVector GetPointOnSurface() const;
+
+    G4VSolid* Clone() const;
+
+    std::ostream& StreamInfo(std::ostream& os) const;
+
+    // Visualisation functions
+
+    void          DescribeYourselfTo( G4VGraphicsScene& scene ) const;
+    G4Polyhedron* CreatePolyhedron() const;
+
+  public:  // without description
+
+    G4ShiftedCone(__void__&);
+      //
+      // Fake default constructor for usage restricted to direct object
+      // persistency for clients requiring preallocation of memory for
+      // persistifiable objects.
+
+    G4ShiftedCone(const G4ShiftedCone& rhs);
+    G4ShiftedCone& operator=(const G4ShiftedCone& rhs);
+      // Copy constructor and assignment operator.
+
+    //  Old access functions
+
+    inline G4double    GetRmin1() const;
+    inline G4double    GetRmax1() const;
+    inline G4double    GetRmin2() const;
+    inline G4double    GetRmax2() const;
+//    inline G4double    GetSPhi() const;
+//    inline G4double    GetDPhi() const;
+
+  private:
+
+    inline void Initialize();
+      //
+      // Reset relevant values to zero
+
+//    inline void CheckSPhiAngle(G4double sPhi);
+//    inline void CheckDPhiAngle(G4double dPhi);
+//    inline void CheckPhiAngles(G4double sPhi, G4double dPhi);
+      //
+      // Reset relevant flags and angle values
+
+    inline void InitializeTrigonometry();
+      //
+      // Recompute relevant trigonometric values and cache them
+
+    G4ThreeVector ApproxSurfaceNormal(const G4ThreeVector& p) const;
+      //
+      // Algorithm for SurfaceNormal() following the original
+      // specification for points not on the surface
+
+  private:
+
+    // Used by distanceToOut
+    //
+    enum ESide {kNull,kRMin,kRMax,kSPhi,kEPhi,kPZ,kMZ};
+
+    // used by normal
+    //
+    enum ENorm {kNRMin,kNRMax,kNSPhi,kNEPhi,kNZ};
+
+    G4double kRadTolerance, kAngTolerance;
+      //
+      // Radial and angular tolerances
+
+    G4double fRmin1, fRmin2, fRmax1, fRmax2;
+    G4double fDz, fZshift;//, fSPhi, fDPhi;
+      //
+      // Radial and angular dimensions
+
+//    G4double sinCPhi, cosCPhi, cosHDPhiOT, cosHDPhiIT,
+//             sinSPhi, cosSPhi, sinEPhi, cosEPhi;
+      //
+      // Cached trigonometric values
+
+//    const G4bool fPhiFullCone;
+      //
+      // Flag for identification of section or full cone
+
+    G4double halfCarTolerance, halfRadTolerance, halfAngTolerance;
+      //
+      // Cached half tolerance values
+};
+
+#include "G4ShiftedCone.icc"
+
+#endif
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/G4ShiftedCone.icc b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/G4ShiftedCone.icc
new file mode 100644
index 0000000000000000000000000000000000000000..1d78d11180e0a87c9e425667aa2478355c1bb7fb
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/G4ShiftedCone.icc
@@ -0,0 +1,337 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+//
+// ********************************************************************
+// * License and Disclaimer                                           *
+// *                                                                  *
+// * The  Geant4 software  is  copyright of the Copyright Holders  of *
+// * the Geant4 Collaboration.  It is provided  under  the terms  and *
+// * conditions of the Geant4 Software License,  included in the file *
+// * LICENSE and available at  http://cern.ch/geant4/license .  These *
+// * include a list of copyright holders.                             *
+// *                                                                  *
+// * Neither the authors of this software system, nor their employing *
+// * institutes,nor the agencies providing financial support for this *
+// * work  make  any representation or  warranty, express or implied, *
+// * regarding  this  software system or assume any liability for its *
+// * use.  Please see the license in the file  LICENSE  and URL above *
+// * for the full disclaimer and the limitation of liability.         *
+// *                                                                  *
+// * This  code  implementation is the result of  the  scientific and *
+// * technical work of the GEANT4 collaboration.                      *
+// * By using,  copying,  modifying or  distributing the software (or *
+// * any work based  on the software)  you  agree  to acknowledge its *
+// * use  in  resulting  scientific  publications,  and indicate your *
+// * acceptance of all terms of the Geant4 Software license.          *
+// ********************************************************************
+//
+//
+//
+// --------------------------------------------------------------------
+// GEANT 4 inline definitions file
+//
+// G4ShiftedCone.icc
+//
+// Implementation of inline methods of G4ShiftedCone
+// --------------------------------------------------------------------
+
+inline
+G4double G4ShiftedCone::GetInnerRadiusMinusZ() const
+{
+  return fRmin1 ;
+}
+
+inline
+G4double G4ShiftedCone::GetOuterRadiusMinusZ() const
+{
+  return fRmax1 ;
+}
+
+inline
+G4double G4ShiftedCone::GetInnerRadiusPlusZ() const
+{
+  return fRmin2 ;
+}
+
+inline
+G4double G4ShiftedCone::GetOuterRadiusPlusZ() const
+{
+  return fRmax2 ;
+}
+
+inline
+G4double G4ShiftedCone::GetZHalfLength() const
+{
+  return fDz ;
+}
+
+inline
+G4double G4ShiftedCone::GetZ1() const
+{
+  return fZshift - fDz ;
+}
+
+inline
+G4double G4ShiftedCone::GetZ2() const
+{
+  return fZshift + fDz ;
+}
+
+inline
+G4double G4ShiftedCone::GetStartPhiAngle() const
+{
+  return 0.; // fSPhi ;
+}
+
+inline
+G4double G4ShiftedCone::GetDeltaPhiAngle() const
+{
+  return CLHEP::twopi; //fDPhi;
+}
+
+inline
+G4double G4ShiftedCone::GetSinStartPhi() const
+{
+  return 0.; //sinSPhi;
+}
+
+inline
+G4double G4ShiftedCone::GetCosStartPhi() const
+{
+  return 1.;// cosSPhi;
+}
+
+inline
+G4double G4ShiftedCone::GetSinEndPhi() const
+{
+  return 0.;// sinEPhi;
+}
+
+inline
+G4double G4ShiftedCone::GetCosEndPhi() const
+{
+  return 1.;//cosEPhi;
+}
+
+inline
+void G4ShiftedCone::Initialize()
+{
+  fCubicVolume = 0.;
+  fSurfaceArea = 0.;
+  fRebuildPolyhedron = true;
+}
+
+/*
+inline
+void G4ShiftedCone::InitializeTrigonometry()
+{
+  G4double hDPhi = 0.5*fDPhi;                       // half delta phi
+  G4double cPhi  = fSPhi + hDPhi;
+  G4double ePhi  = fSPhi + fDPhi;
+
+  sinCPhi    = std::sin(cPhi);
+  cosCPhi    = std::cos(cPhi);
+  cosHDPhiIT = std::cos(hDPhi - 0.5*kAngTolerance); // inner/outer tol half dphi
+  cosHDPhiOT = std::cos(hDPhi + 0.5*kAngTolerance);
+  sinSPhi = std::sin(fSPhi);
+  cosSPhi = std::cos(fSPhi);
+  sinEPhi = std::sin(ePhi);
+  cosEPhi = std::cos(ePhi);
+}
+
+inline void G4ShiftedCone::CheckSPhiAngle(G4double sPhi)
+{
+  // Ensure fSphi in 0-2PI or -2PI-0 range if shape crosses 0
+
+  if ( sPhi < 0 )
+  {
+    fSPhi = CLHEP::twopi - std::fmod(std::fabs(sPhi),CLHEP::twopi);
+  }
+  else
+  {
+    fSPhi = std::fmod(sPhi,CLHEP::twopi) ;
+  }
+  if ( fSPhi+fDPhi > CLHEP::twopi )
+  {
+    fSPhi -= CLHEP::twopi ;
+  }
+}
+
+inline void G4ShiftedCone::CheckDPhiAngle(G4double dPhi)
+{
+  fPhiFullCone = true;
+  if ( dPhi >= CLHEP::twopi-kAngTolerance*0.5 )
+  {
+    fDPhi=CLHEP::twopi;
+    fSPhi=0;
+  }
+  else
+  {
+    fPhiFullCone = false;
+    if ( dPhi > 0 )
+    {
+      fDPhi = dPhi;
+    }
+    else
+    {
+      std::ostringstream message;
+      message << "Invalid dphi." << G4endl
+              << "Negative or zero delta-Phi (" << dPhi << ") in solid: "
+              << GetName();
+      G4Exception("G4ShiftedCone::CheckDPhiAngle()", "GeomSolids0002",
+                  FatalException, message);
+    }
+  }
+}
+
+inline void G4ShiftedCone::CheckPhiAngles(G4double sPhi, G4double dPhi)
+{
+  CheckDPhiAngle(dPhi);
+  if ( (fDPhi<CLHEP::twopi) && (sPhi) ) { CheckSPhiAngle(sPhi); }
+  InitializeTrigonometry();
+}
+*/
+
+inline
+void G4ShiftedCone::SetInnerRadiusMinusZ( G4double Rmin1 )
+{
+  fRmin1= Rmin1 ;
+  Initialize();
+}
+
+inline
+void G4ShiftedCone::SetOuterRadiusMinusZ( G4double Rmax1 )
+{
+  fRmax1= Rmax1 ;
+  Initialize();
+}
+
+inline
+void G4ShiftedCone::SetInnerRadiusPlusZ ( G4double Rmin2 )
+{
+  fRmin2= Rmin2 ;
+  Initialize();
+}
+
+inline
+void G4ShiftedCone::SetOuterRadiusPlusZ ( G4double Rmax2 )
+{
+  fRmax2= Rmax2 ;
+  Initialize();
+}
+
+/*inline
+void G4ShiftedCone::SetZHalfLength ( G4double newDz )
+{
+  fDz= newDz ;
+  Initialize();
+}*/
+
+/*
+inline
+void G4ShiftedCone::SetStartPhiAngle ( G4double newSPhi, G4bool compute )
+{
+  // Flag 'compute' can be used to explicitely avoid recomputation of
+  // trigonometry in case SetDeltaPhiAngle() is invoked afterwards
+
+  CheckSPhiAngle(newSPhi);
+  fPhiFullCone = false;
+  if (compute)  { InitializeTrigonometry(); }
+  Initialize();
+}
+
+void G4ShiftedCone::SetDeltaPhiAngle ( G4double newDPhi )
+{
+  CheckPhiAngles(fSPhi, newDPhi);
+  Initialize();
+}
+*/
+// Old access methods ...
+
+inline
+G4double G4ShiftedCone::GetRmin1() const
+{
+  return GetInnerRadiusMinusZ();
+}
+
+inline
+G4double G4ShiftedCone::GetRmax1() const
+{
+  return GetOuterRadiusMinusZ();
+}
+
+inline
+G4double G4ShiftedCone::GetRmin2() const
+{
+  return GetInnerRadiusPlusZ();
+}
+
+inline
+G4double G4ShiftedCone::GetRmax2() const
+{
+  return GetOuterRadiusPlusZ();
+}
+/*
+inline
+G4double G4ShiftedCone::GetDz() const
+{
+  return GetZHalfLength();
+}
+
+inline
+G4double G4ShiftedCone::GetSPhi() const
+{
+  return GetStartPhiAngle();
+}
+
+inline
+G4double G4ShiftedCone::GetDPhi() const
+{
+  return GetDeltaPhiAngle();
+}
+*/
+inline
+G4double G4ShiftedCone::GetCubicVolume()
+{
+  if(fCubicVolume != 0.) {;}
+  else
+  {
+    G4double Rmean, rMean, deltaR, deltar;
+
+    Rmean  = 0.5*(fRmax1+fRmax2);
+    deltaR = fRmax1-fRmax2;
+
+    rMean  = 0.5*(fRmin1+fRmin2);
+    deltar = fRmin1-fRmin2;
+    fCubicVolume = GetDeltaPhiAngle()*fDz*(Rmean*Rmean-rMean*rMean
+                            +(deltaR*deltaR-deltar*deltar)/12);
+  }
+  return fCubicVolume;
+}
+
+inline
+G4double G4ShiftedCone::GetSurfaceArea()
+{
+  if(fSurfaceArea != 0.) {;}
+  else
+  {
+    G4double mmin, mmax, dmin, dmax;
+
+    mmin= (fRmin1+fRmin2)*0.5;
+    mmax= (fRmax1+fRmax2)*0.5;
+    dmin= (fRmin2-fRmin1);
+    dmax= (fRmax2-fRmax1);
+
+    fSurfaceArea = GetDeltaPhiAngle()*( mmin * std::sqrt(dmin*dmin+4*fDz*fDz)
+                         + mmax * std::sqrt(dmax*dmax+4*fDz*fDz)
+                         + 0.5*(fRmax1*fRmax1-fRmin1*fRmin1
+                               +fRmax2*fRmax2-fRmin2*fRmin2 ));
+/*    if(!fPhiFullCone)
+    {
+      fSurfaceArea = fSurfaceArea+4*fDz*(mmax-mmin);
+    }*/
+  }
+  return fSurfaceArea;
+}
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArFanSection.cxx b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArFanSection.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..62987dc38723cc36f02417c69e92c5c71483b44c
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArFanSection.cxx
@@ -0,0 +1,265 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "LArWheelSolid.h"
+#include "LArFanSection.h"
+#include "GeoSpecialShapes/LArWheelCalculator.h"
+#include <iostream>
+#include <cassert>
+
+void LArFanSections::print(void) const
+{
+  std::cout << "LArFanSections at " << this << std::endl
+            << "Amin = " << Amin << ", Amax = " << Amax
+            << std::endl
+            << "Bmin = " << Bmin << ", Bmax = " << Bmax << std::endl
+            << "xmin = " << xmin << ", xmax = " << xmax
+            << "Cflat2 = " << Cflat2 << std::endl;
+}
+
+LArFanSections::LArFanSections(
+	G4double ri1, G4double ri2, G4double ro1, G4double ro2,
+	G4double Xmax, G4double z1, G4double z2
+)
+  : Amin ((ri2 - ri1) / (z2 - z1)),
+    Amax ((ro2 - ro1) / (z2 - z1)),
+    Bmin (ri1 - Amin * z1),
+    Bmax (ro1 - Amax * z1),
+    Amin2 (Amin*Amin),
+    Amax2 (Amax*Amax),
+    Bmin2 (Bmin*Bmin),
+    Bmax2 (Bmax*Bmax),
+    xmin (-Xmax),
+    xmax (Xmax),
+    Cflat2 (ro2*ro2),
+    ABmax (Amax*Bmax),
+    ABmin (Amin*Bmin)
+{
+}
+
+G4bool LArWheelSolid::check_D(
+	G4double &t1, G4double A, G4double B, G4double C, G4bool out
+) const
+{
+	// G4bool out is to be set true if the point is surface-outside
+    // then have to discard first intersection
+
+	const G4double D = B*B - A*C;
+	LWSDBG(8, std::cout << "check D=" << D << " out=" << out << std::endl);
+	if(D < 0.) return false;
+
+	const G4double D1 = sqrt(D);
+	t1 = (-B + D1) / A;
+	const G4double t2 = (-B - D1) / A;
+	LWSDBG(8, std::cout << "t1=" << t1 << " t2=" << t2 << std::endl);
+	if(t1 > 0.){
+		if(t2 > 0.){
+			if(out){
+				if(t2 > t1) t1 = t2;
+			} else {
+				if(t2 < t1) t1 = t2;
+			}
+		} else if(t2 < 0.){
+			if(out) return false;
+		} else { // answer is t1
+		}
+	} else if(t1 < 0.){
+		if(t2 > 0.){
+			if(out) return false;
+			t1 = t2;
+		} else if(t2 < 0.){
+			return false;
+		} else {
+			return false;
+		}
+	} else {
+		if(t2 > 0.){
+			t1 = t2;
+		} else if(t2 < 0.){
+			return false;
+		} else {
+			return false;
+		}
+	}
+	return true;
+}
+
+// p must be not outside of the "FanBound"
+// if track crosses inner cone in valid (z, x) interval,
+// returns true, sets q to the cross point
+bool LArWheelSolid::fs_cross_lower(
+	const G4ThreeVector &p, const G4ThreeVector &v,
+	G4ThreeVector &q) const
+{
+	LWSDBG(7, std::cout << "fcl" << std::endl);
+	const G4double A = v.perp2() - m_fs->Amin2*v.z()*v.z();
+	const G4double B = p.x()*v.x() + p.y()*v.y()
+                     - m_fs->Amin2*p.z()*v.z() - m_fs->ABmin*v.z();
+	const G4double C = p.perp2() - m_fs->Amin2*p.z()*p.z()
+                     - 2.*m_fs->ABmin*p.z() - m_fs->Bmin2;
+	G4double t1(0.0);
+	const G4double out_dist = m_fs->Amin*p.z() + m_fs->Bmin - p.perp();
+	LWSDBG(8, std::cout << "fcl out_dist(p)=" << out_dist << " Tolerance=" << s_Tolerance << std::endl);
+	const G4bool out = out_dist >= 0.0;
+	if(check_D(t1, A, B, C, out)){
+		const G4double zz1 = p.z() + v.z() * t1;
+		if(zz1 < m_Zsect.front() || zz1 > m_Zsect.back()){
+			LWSDBG(8, std::cout << "fcl out on Z " << zz1 << std::endl);
+			return false;
+		}
+		const G4double xx1 = p.x() + v.x() * t1;
+		if(xx1 < m_fs->xmin || xx1 > m_fs->xmax){
+			LWSDBG(8, std::cout << "fcl out on X " << xx1 << std::endl);
+			return false;
+		}
+		if(out_dist == 0.){ // entry point is exactly on the cone
+		// here we got t1 > 0 from check_D, founded point seems to be in x and z ranges
+		// if the track leaves the surface, then the entry is the intersection,
+		// and the distance is 0
+		// if the track is on the surface, then there is no lower cone intersection
+
+		// estimate deviation of the track from the surface
+		// (exact calculations are too complicated)
+			const G4double xx2 = p.x() + v.x() * t1 * 0.5;
+			const G4double yy2 = p.y() + v.y() * t1 * 0.5;
+			const G4double dev = fabs(sqrt(xx2 *xx2 + yy2*yy2)
+		                       - m_fs->Amin*(p.z() + zz1)*0.5
+		                       - m_fs->Bmin);
+			if(dev < s_Tolerance){
+				LWSDBG(8, std::cout << "fcl on the surface" << std::endl);
+				return false;
+			} else {
+				LWSDBG(8, std::cout << "fcl out = in" << std::endl);
+				q = p;
+				return true;
+			}
+		}
+		q.setX(xx1);
+		q.setY(p.y() + v.y() * t1);
+		q.setZ(zz1);
+		LWSDBG(8, std::cout << "fcl got " << t1 << std::endl);
+		return true;
+	}
+	LWSDBG(8, std::cout << "fcl no intersection" << std::endl);
+	return false;
+}
+
+// p must be not outside of the "FanBound"
+// if track crosses outer cone in valid (z, x) interval,
+// returns true, adds to b the distance to the cross point,
+// sets q to the cross point
+bool LArWheelSolid::fs_cross_upper(
+	const G4ThreeVector &p, const G4ThreeVector &v,
+	G4ThreeVector &q) const
+{
+	LWSDBG(7, std::cout << "fcu" << std::endl);
+	G4double A = v.perp2();
+	G4double B = p.x()*v.x() + p.y()*v.y();
+	G4double C = p.perp2();
+
+	if(m_IsOuter){
+		const G4double &Af = A, &Bf = B;
+		const G4double Cf = C - m_fs->Cflat2;
+		G4double b1;
+		if(check_D(b1, Af, Bf, Cf, Cf >= 0.)){
+			const G4double zz1 = p.z() + v.z() * b1;
+			if(zz1 >= m_Zmid && zz1 <= m_Zsect.back()){
+				const G4double xx1 = p.x() + v.x() * b1;
+				if(xx1 < m_fs->xmin || xx1 > m_fs->xmax) return false;
+				q.setX(xx1);
+				q.setY(p.y() + v.y() * b1);
+				q.setZ(zz1);
+				return true;
+			}
+		}
+		LWSDBG(8, std::cout << "fcu no cyl intersection" << std::endl);
+	}
+
+	A -= m_fs->Amax2*v.z()*v.z();
+	B -= m_fs->Amax2*p.z()*v.z() + m_fs->ABmax*v.z();
+	C -= m_fs->Amax2*p.z()*p.z() + 2.*m_fs->ABmax*p.z() + m_fs->Bmax2;
+
+	G4double t1;
+	const G4bool out = m_fs->Amax*p.z() + m_fs->Bmax <= p.perp();
+	if(check_D(t1, A, B, C, out)){
+		const G4double zz1 = p.z() + v.z() * t1;
+		LWSDBG(8, std::cout << "fcu z = " << zz1 << ", lim: (" << m_Zsect.front() << ", " << m_Zmid << ")" << std::endl);
+		if(zz1 < m_Zsect.front() || zz1 > m_Zmid) return false;
+		const G4double xx1 = p.x() + v.x() * t1;
+		LWSDBG(8, std::cout << "fcu x = " << xx1 << ", lim: (" << m_fs->xmin << ", " << m_fs->xmax << ")" << std::endl);
+		if(xx1 < m_fs->xmin || xx1 > m_fs->xmax) return false;
+		q.setX(xx1);
+		q.setY(p.y() + v.y() * t1);
+		q.setZ(zz1);
+		return true;
+	}
+	LWSDBG(8, std::cout << "fcu no cone intersection" << std::endl);
+	return false;
+}
+
+/* p must be not outside "FanBound" */
+LArWheelSolid::FanBoundExit_t LArWheelSolid::find_exit_point(
+	const G4ThreeVector &p, const G4ThreeVector &v,
+	G4ThreeVector &q) const
+{
+	LWSDBG(6, std::cout << "in fep p" << MSG_VECTOR(p)<< ", v"<< MSG_VECTOR(v) << ", q" << MSG_VECTOR(q) << std::endl);
+
+/* by construction, cannot have true from both upper and lower */
+/* the only problem is the points on surface but "slightly outside" */
+/* fs_cross_* account for (x, z) range */
+// lower has to be checked first, since outer might find more distant
+// intersection in the acceptable (x, z) range
+	if(fs_cross_lower(p, v, q))	return ExitAtInner;
+	LWSDBG(6, std::cout << "after fs_cross_lower q" << MSG_VECTOR(q) << std::endl);
+	if(fs_cross_upper(p, v, q))	return ExitAtOuter;
+	LWSDBG(6, std::cout << "after fs_cross_upper q" << MSG_VECTOR(q) << std::endl);
+
+	FanBoundExit_t result = ExitAtSide;
+	G4double d;
+	if(v.x() > 0.) d = (m_fs->xmax - p.x()) / v.x();
+	else if(v.x() < 0.) d = (m_fs->xmin - p.x()) / v.x();
+	else d = kInfinity;
+
+	G4double dz;
+	FanBoundExit_t resultz = NoCross;
+	if(v.z() > 0.){
+		dz = (m_Zsect.back() - p.z()) / v.z();
+		resultz = ExitAtBack;
+	} else if(v.z() < 0.){
+		dz = (m_Zsect.front() - p.z()) / v.z();
+		resultz = ExitAtFront;
+	} else {
+		dz = kInfinity;
+	}
+	if(d > dz){
+		d = dz;
+		result = resultz;
+	}
+	q = p + v * d;
+	LWSDBG(7, std::cout << "fep side " << d << " " << result << " q" << MSG_VECTOR(q) << std::endl);
+	const G4double out_distlower = m_fs->Amin*q.z() + m_fs->Bmin - q.perp(); // > 0 - below lower cone
+	LWSDBG(7, std::cout << "fep out_distlower(q)=" << out_distlower << " Tolerance=" << s_Tolerance << std::endl);
+	if (out_distlower >= 0.0) {
+		// side intersection point is below lower cone
+		// initial point p was at exit boundary
+		q = p;
+		return NoCross;
+	}
+
+	if (m_IsOuter && q.z() >= m_Zmid && q.z() <= m_Zsect.back()+s_Tolerance && q.perp2() >= m_fs->Cflat2) {
+		// outside of upper cylinder
+		q = p;
+		return NoCross;
+	}
+	const G4double out_distupper = m_fs->Amax*q.z() + m_fs->Bmax - q.perp(); // < 0 - above upper cone
+	LWSDBG(7, std::cout << "fep out_distupper(q)=" << out_distupper << " Tolerance=" << s_Tolerance << std::endl);
+	if (out_distupper <= 0.0) {
+		// side intersection point is above upper cone
+		// initial point p was at exit boundary
+		q = p;
+		return NoCross;
+	}
+	assert((q - p).mag() < kInfinity);
+	return result;
+}
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArFanSection.h b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArFanSection.h
new file mode 100644
index 0000000000000000000000000000000000000000..af3327c318256ae78aca0c77a9269b8e92505509
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArFanSection.h
@@ -0,0 +1,30 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef __LARFANSECTION_H__
+#define __LARFANSECTION_H__
+
+// helper class to replace G4Polycone
+// in certain LArWheelSolid operations
+
+class LArFanSections
+{
+public:
+	G4double Amin, Amax;
+	G4double Bmin, Bmax;
+	G4double Amin2, Amax2;
+	G4double Bmin2, Bmax2;
+	G4double xmin, xmax;
+	G4double Cflat2, ABmax, ABmin;
+
+	LArFanSections(
+		G4double ri1, G4double ri2,
+		G4double ro1, G4double ro2,
+		G4double Xmax, G4double z1, G4double z2
+	);
+
+	void print(void) const;
+};
+
+#endif // __LARFANSECTION_H__
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSliceSolid.cxx b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSliceSolid.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..dd7a61903fd47354f305a76e924980a59313ec9a
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSliceSolid.cxx
@@ -0,0 +1,104 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "G4VGraphicsScene.hh"
+#include "G4VisExtent.hh"
+
+#include "GeoSpecialShapes/LArWheelCalculator.h"
+#include "LArWheelSliceSolid.h"
+
+class G4NURBS;
+class G4VoxelLimits;
+class G4AffineTransform;
+
+EInside LArWheelSliceSolid::Inside(const G4ThreeVector &inputP) const
+{
+    LWSDBG(10, std::cout << std::setprecision(25));
+    LWSDBG(1, std::cout << TypeStr() << " Inside " << MSG_VECTOR(inputP) << std::endl);
+    const EInside inside_BS = m_BoundingShape->Inside(inputP);
+    if(inside_BS == kOutside){
+        LWSDBG(2, std::cout << "outside BS" << std::endl);
+        return kOutside;
+    }
+    G4ThreeVector p(inputP);
+    int p_fan = 0;
+    const G4double d = fabs(GetCalculator()->DistanceToTheNearestFan(p, p_fan));
+    if(d > m_FHTplusT){
+        LWSDBG(2, std::cout << "outside fan d=" << d << ", m_FHTplusT=" << m_FHTplusT << std::endl);
+        return kOutside;
+    }
+    if(d < m_FHTminusT){
+        LWSDBG(2, std::cout << "inside fan d=" << d << ", m_FHTminusT=" << m_FHTminusT << ", inside_BS=" << inside(inside_BS) << std::endl);
+        return inside_BS;
+    }
+    LWSDBG(2, std::cout << "surface" << std::endl);
+    return kSurface;
+}
+
+G4ThreeVector LArWheelSliceSolid::SurfaceNormal(const G4ThreeVector &inputP) const
+{
+    LWSDBG(1, std::cout << TypeStr() << " SurfaceNormal" << MSG_VECTOR(inputP) << std::endl);
+    EInside inside_BS = m_BoundingShape->Inside(inputP);
+    if(inside_BS != kInside){
+        LWSDBG(2, std::cout << "not inside BS" << std::endl);
+        return m_BoundingShape->SurfaceNormal(inputP);
+    }
+    G4ThreeVector p( inputP );
+    int p_fan = 0;
+    GetCalculator()->DistanceToTheNearestFan(p, p_fan);
+    G4ThreeVector d = GetCalculator()->NearestPointOnNeutralFibre(p, p_fan);
+    d.rotateZ(inputP.phi() -  p.phi()); // rotate back to initial position
+    LWSDBG(4, std::cout << "npnf" << MSG_VECTOR(d) << std::endl);
+    p = inputP - d;
+    LWSDBG(2, std::cout << "sn " << MSG_VECTOR(p.unit()) << std::endl);
+    return(p.unit());
+}
+
+G4bool LArWheelSliceSolid::CalculateExtent(
+    const EAxis a, const G4VoxelLimits &vl,
+    const G4AffineTransform &t, G4double &p,
+    G4double &q
+) const
+{
+    return m_BoundingShape->CalculateExtent(a, vl, t, p, q);
+}
+
+G4String LArWheelSliceSolid::TypeStr(void) const
+{
+    G4String ret("");
+    switch(m_Pos){
+    case Inner:
+        switch(m_Type){
+        case Absorber: ret = "LArInnerAbsorberWheel"; break;
+        case Electrode: ret = "LArInnerElectrodWheel"; break;
+        case Glue: ret = "LArInnerGlueWheel"; break;
+        case Lead: ret = "LArInnerLeadWheel"; break;
+        }
+        break;
+    case Outer:
+        switch(m_Type){
+        case Absorber: ret = "LArOuterAbsorberWheel"; break;
+        case Electrode: ret = "LArOuterElectrodWheel"; break;
+        case Glue: ret = "LArOuterGlueWheel"; break;
+        case Lead: ret = "LArOuterLeadWheel"; break;
+        }
+        break;
+    }
+    return ret;
+}
+
+void LArWheelSliceSolid::DescribeYourselfTo(G4VGraphicsScene &scene) const
+{
+    scene.AddSolid(*this);
+}
+
+G4VisExtent LArWheelSliceSolid::GetExtent() const
+{
+    return m_BoundingShape->GetExtent();
+}
+
+G4Polyhedron* LArWheelSliceSolid::CreatePolyhedron() const
+{
+    return m_BoundingShape->CreatePolyhedron();
+}
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSliceSolid.h b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSliceSolid.h
new file mode 100644
index 0000000000000000000000000000000000000000..7d05aaa2d305de53486c75ab19c63b86aa39e9f7
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSliceSolid.h
@@ -0,0 +1,196 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef GEO2G4_LARWHEELSLICESOLID_H
+#define GEO2G4_LARWHEELSLICESOLID_H
+#ifndef PORTABLE_LAR_SHAPE
+#include "AthenaKernel/MsgStreamMember.h"
+#endif
+#include "G4VSolid.hh"
+
+// set this to allow debug output in LArWheelSliceSolid methods
+// disabled by default to avoid any performance degradation
+//#define DEBUG_LARWHEELSLICESOLID
+
+// set this to check in dti and dto functions if particle direction
+// pointing inside or outside of volume to return zero fast when it is required by spec.
+// currently at development stage, requires accurate surface normal calculations
+//#define CHECK_DIRTONORM_ANGLE_ON_SURFACE
+
+#ifdef DEBUG_LARWHEELSLICESOLID
+#define LWSDBG(a, b) if(Verbose >= a) b
+#define MSG_VECTOR(v) "(" << v.x() << ", " << v.y() << ", " << v.z() << ")"
+//#define LWS_HARD_TEST_DTI
+//#define LWS_HARD_TEST_DTO
+#else
+#define LWSDBG(a, b)
+#endif
+
+// Forward declarations.
+class G4VGraphicsScene;
+class G4VisExtent;
+class G4Polyhedron;
+class G4NURBS;
+class G4VoxelLimits;
+class G4AffineTransform;
+class LArWheelCalculator;
+class TF1;
+class G4Polyhedra;
+struct EMECData;
+class LArWheelSliceSolid : public G4VSolid
+{
+  public:
+    typedef enum { Inner, Outer } pos_t;
+    typedef enum { Absorber, Electrode, Glue, Lead } type_t;
+
+    LArWheelSliceSolid(
+        const G4String& name,
+        pos_t pos, type_t type, size_t slice,
+        G4int zside = 1,
+        const LArWheelCalculator *calc = 0,
+	const EMECData * emecData=0
+    );
+    LArWheelSliceSolid(const G4String& name, const EMECData *emecData);
+    virtual ~LArWheelSliceSolid(){}
+
+  // Mandatory for custom solid Geant4 functions
+    EInside Inside(const G4ThreeVector&) const;
+    G4double DistanceToIn(const G4ThreeVector&,
+                          const G4ThreeVector&) const;
+    G4double DistanceToIn(const G4ThreeVector&) const;
+    G4double DistanceToOut(const G4ThreeVector&,
+                           const G4ThreeVector&,
+                           const G4bool calcNorm = false,
+                           G4bool* validNorm = 0,
+                           G4ThreeVector* n = 0) const;
+    G4double DistanceToOut(const G4ThreeVector&) const;
+    G4ThreeVector SurfaceNormal (const G4ThreeVector&) const;
+    G4bool CalculateExtent(const EAxis,
+                           const G4VoxelLimits&,
+                           const G4AffineTransform&,
+                           G4double&,
+                           G4double&) const;
+    G4GeometryType GetEntityType() const { return TypeStr(); }
+    void DescribeYourselfTo(G4VGraphicsScene&) const;
+    G4VisExtent GetExtent() const;
+    G4Polyhedron* CreatePolyhedron() const;
+    virtual std::ostream& StreamInfo(std::ostream& os) const { return os; }
+
+#ifndef PORTABLE_LAR_SHAPE
+    G4ThreeVector GetPointOnSurface(void) const;
+    G4double GetCubicVolume(void);
+    G4double GetSurfaceArea(void);
+#endif
+  //
+
+    const G4VSolid *GetBoundingShape(void) const { return m_BoundingShape; }
+    const LArWheelCalculator *GetCalculator(void) const { return m_Calculator; }
+
+  private:
+    static const G4double s_Tolerance;
+    static const G4double s_AngularTolerance;
+    static const G4double s_IterationPrecision;
+    static const G4double s_IterationPrecision2;
+    static const unsigned int s_IterationsLimit;
+
+    pos_t m_Pos;
+    type_t m_Type;
+    const LArWheelCalculator *m_Calculator;
+    G4VSolid* m_BoundingShape;
+
+    G4double m_FanHalfThickness, m_FHTplusT, m_FHTminusT;
+
+  // limits used in DTI
+    G4double m_Xmin, m_Xmax;
+
+  // Special limit, used in dto
+    G4double m_Ymin;
+
+  // limits for use in service functions
+    G4double m_Zmin, m_Zmax, m_Rmin, m_Rmax;
+
+    void inner_solid_init(const G4String &, size_t slice);
+    void outer_solid_init(const G4String &, size_t slice);
+    void fill_zsect(std::vector<G4double> &, G4double zMid = 0.) const;
+
+    virtual G4double distance_to_in(G4ThreeVector &, const G4ThreeVector &, int) const;
+    G4double in_iteration_process(const G4ThreeVector &,
+                                  G4double, G4ThreeVector &, int) const;
+    G4double search_for_nearest_point(
+        const G4ThreeVector &, const G4double,
+        const G4ThreeVector &, int
+    ) const;
+    G4bool search_for_most_remoted_point(
+        const G4ThreeVector &, const G4ThreeVector &,
+        G4ThreeVector &, const int
+    ) const;
+    G4double out_iteration_process(
+        const G4ThreeVector &, G4ThreeVector &, const int
+    ) const;
+
+#ifndef PORTABLE_LAR_SHAPE
+    EInside Inside_accordion(const G4ThreeVector&) const;
+    void get_point_on_accordion_surface(G4ThreeVector &) const;
+    void get_point_on_polycone_surface(G4ThreeVector &) const;
+    void get_point_on_flat_surface(G4ThreeVector &) const;
+    void set_failover_point(G4ThreeVector &p, const char *m = 0) const;
+
+    G4double get_area_on_polycone(void) const;
+    G4double get_area_on_face(void) const;
+    G4double get_area_on_side(void) const;
+
+    G4double get_area_at_r(G4double r) const;
+    G4double get_length_at_r(G4double r) const;
+
+    void test(void);
+    void clean_tests(void);
+    void init_tests(void);
+  /// Log a message using the Athena controlled logging system
+    MsgStream& msg( MSG::Level lvl ) const { return m_msg << lvl; }
+  /// Check whether the logging system is active at the provided verbosity level
+    bool msgLvl( MSG::Level lvl ) const { return m_msg.get().level() <= lvl; }
+#endif
+  
+protected:
+  /// Private message stream member
+#ifndef PORTABLE_LAR_SHAPE
+    mutable Athena::MsgStreamMember m_msg;
+
+    TF1 *m_f_area, *m_f_vol, *m_f_area_on_pc, *m_f_length, *m_f_side_area;
+#endif
+  
+    double m_test_index;
+    friend double LArWheelSliceSolid_fcn_area(double *, double *);
+    friend double LArWheelSliceSolid_fcn_vol(double *, double *);
+    friend double LArWheelSliceSolid_fcn_area_on_pc(double *, double *);
+    friend double LArWheelSliceSolid_get_dl(double *, double *, G4int);
+    friend double LArWheelSliceSolid_fcn_side_area(double *, double *);
+
+#ifdef DEBUG_LARWHEELSLICESOLID
+    static const char* inside(EInside i)
+    {
+        switch(i){
+        case kInside: return "inside"; break;
+        case kSurface: return "surface"; break;
+        case kOutside: return "outside"; break;
+        }
+        return "unknown";
+    }
+
+  public:
+    static G4int Verbose;
+    void SetVerbose(G4int v){ Verbose = v; }
+    G4bool test_dti(const G4ThreeVector &p,
+                    const G4ThreeVector &v, const G4double distance) const;
+    G4bool test_dto(const G4ThreeVector &p,
+                    const G4ThreeVector &v, const G4double distance) const;
+#endif
+  private:
+    G4String TypeStr(void) const;
+
+  void createSolid(const G4String& name, G4int zside, size_t slice, const EMECData *emecData);
+    inline void check_slice(size_t size, size_t slice) const;
+};
+
+#endif // GEO2G4_LARWHEELSLICESOLID_H
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSliceSolidDisToIn.cxx b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSliceSolidDisToIn.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..56a6ac4f7f97b64d73130ecf5c9f625ebb3494f1
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSliceSolidDisToIn.cxx
@@ -0,0 +1,266 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+// DistanceToIn stuff for LArWheelSliceSolid
+#include <cassert>
+#ifndef PORTABLE_LAR_SHAPE
+#include "AthenaBaseComps/AthMsgStreamMacros.h"
+#endif
+#include "CLHEP/Units/PhysicalConstants.h"
+
+#include "GeoSpecialShapes/LArWheelCalculator.h"
+#include "LArWheelSliceSolid.h"
+
+G4double LArWheelSliceSolid::DistanceToIn(const G4ThreeVector &inputP) const
+{
+    LWSDBG(1, std::cout << TypeStr() << " DisToIn" << MSG_VECTOR(inputP) << std::endl);
+    if(m_BoundingShape->Inside(inputP) == kOutside) {
+    // here is an approximation - for the point outside m_BoundingShape
+    // the solid looks like a m_BoundingShape
+    // it's okay since the result could be underestimated
+        LWSDBG(2, std::cout << "Outside BS" << std::endl);
+        return m_BoundingShape->DistanceToIn(inputP);
+    }
+    G4ThreeVector p(inputP);
+
+    //
+    // DistanceToTheNearestFan:
+    // rotates point p to the localFan coordinates and returns fan number to out_fan_number parameter
+    // returns distance to fan as result
+    //
+
+    int p_fan = 0;
+    const G4double d = fabs(GetCalculator()->DistanceToTheNearestFan(p, p_fan));
+    if(d > m_FHTplusT){
+        const G4double result = d - m_FanHalfThickness;
+        LWSDBG(2, std::cout << "dti result = " << result << std::endl);
+        return result;
+    }
+    LWSDBG(2, std::cout << "already inside, return 0" << MSG_VECTOR(p) << std::endl);
+    return 0.;
+}
+
+G4double LArWheelSliceSolid::DistanceToIn(const G4ThreeVector &inputP,
+                                     const G4ThreeVector &inputV) const
+{
+    LWSDBG(1, std::cout << TypeStr() << " DisToIn" << MSG_VECTOR(inputP)
+                        << MSG_VECTOR(inputV) << std::endl);
+
+    G4double distance = 0.;
+    const EInside inside_BS = m_BoundingShape->Inside(inputP);
+    G4ThreeVector p(inputP);
+    if(inside_BS == kOutside) {
+        distance = m_BoundingShape->DistanceToIn(inputP, inputV);
+        if(distance == kInfinity) {
+            LWSDBG(2, std::cout << "Infinity distance to m_BoundingShape"
+                                << MSG_VECTOR(inputP) << MSG_VECTOR(inputV)
+                                << std::endl);
+            return kInfinity;
+        }
+        p += inputV * distance;
+        assert(m_BoundingShape->Inside(p) != kOutside);
+        LWSDBG(2, std::cout << "shift" << MSG_VECTOR(inputP) << std::endl);
+    }
+
+    const G4double phi0 = p.phi();
+    int p_fan = 0;
+    const G4double d = GetCalculator()->DistanceToTheNearestFan(p, p_fan);
+    if(fabs(d) < m_FHTminusT){
+        LWSDBG(2, std::cout << "already inside fan" << MSG_VECTOR(p) << std::endl);
+        // if initial point is on BS surface and inside fan volume it is a real surface
+        if(inside_BS == kSurface) {
+            LWSDBG(2, std::cout << "On BS surface" << std::endl);
+            return m_BoundingShape->DistanceToIn(inputP, inputV);
+        }
+        return distance;
+    }
+    G4ThreeVector v(inputV);
+    v.rotateZ(p.phi() - phi0);
+
+    const G4double d0 = distance_to_in(p, v, p_fan);
+    distance += d0;
+
+#ifdef DEBUG_LARWHEELSLICESOLID
+    if(Verbose > 2){
+        if(Verbose > 3){
+            std::cout << MSG_VECTOR(inputP)
+                      << " " << MSG_VECTOR(inputV) << std::endl;
+        }
+        std::cout << "dti: " << d0 << ", DTI: " << distance << std::endl;
+    }
+    if(Verbose > 3){
+        if(d0 < kInfinity){
+            G4ThreeVector p2 = inputP + inputV*distance;
+            EInside i = Inside(p2);
+            std::cout << "DTI hit at dist. " << distance << ", point "
+                      << MSG_VECTOR(p2) << ", "
+                      << inside(i) << std::endl;
+        } else {
+            std::cout << "got infinity from dti" << std::endl;
+        }
+    }
+#ifdef LWS_HARD_TEST_DTI
+    if(test_dti(inputP, inputV, distance)){
+        if(Verbose == 1){
+            std::cout << TypeStr() << " DisToIn" << MSG_VECTOR(inputP)
+                      << MSG_VECTOR(inputV) << std::endl;
+        }
+    }
+    if(Verbose == 1){
+        std::cout << TypeStr() << " DisToIn" << MSG_VECTOR(inputP)
+                  << MSG_VECTOR(inputV) << " " << distance << std::endl;
+    }
+#endif // ifdef LWS_HARD_TEST_DTI
+
+#endif // ifdef DEBUG_LARWHEELSLICESOLID
+
+  return distance;
+}
+
+G4double LArWheelSliceSolid::distance_to_in(G4ThreeVector &p, const G4ThreeVector &v, int p_fan) const
+{
+    LWSDBG(4, std::cout << "dti:           " << MSG_VECTOR(p) << " "
+                        << MSG_VECTOR(v) << std::endl);
+
+    G4double distance = 0.;
+
+    if(p.x() > m_Xmax) {
+        if(v.x() >= 0.) return kInfinity;
+        const G4double b = (m_Xmax - p.x()) / v.x();
+        const G4double y2 = p.y() + v.y() * b;
+        const G4double z2 = p.z() + v.z() * b;
+        p.set(m_Xmax, y2, z2);
+        distance += b;
+    } else if(p.x() < m_Xmin) {
+        if(v.x() <= 0.)    return kInfinity;
+        const G4double b = (m_Xmin - p.x()) / v.x();
+        const G4double y2 = p.y() + v.y() * b;
+        const G4double z2 = p.z() + v.z() * b;
+        p.set(m_Xmin, y2, z2);
+        distance += b;
+    }
+
+// here p is on surface of or inside the "FanBound",
+// distance corrected, misses are accounted for
+    LWSDBG(5, std::cout << "dti corrected: " << MSG_VECTOR(p) << std::endl);
+
+    G4double dist_p = GetCalculator()->DistanceToTheNeutralFibre(p, p_fan);
+    if(fabs(dist_p) < m_FHTminusT) {
+        LWSDBG(5, std::cout << "hit fan dist_p=" << dist_p << ", m_FHTminusT=" << m_FHTminusT << std::endl);
+        return distance;
+    }
+
+    G4ThreeVector q;
+    q = p + v * m_BoundingShape->DistanceToOut(p, v);
+    G4double dist_q = GetCalculator()->DistanceToTheNeutralFibre(q, p_fan);
+    LWSDBG(5, std::cout << "dti exit point: " << MSG_VECTOR(q) << " "
+                        << dist_q << std::endl);
+    G4double dd = kInfinity;
+    if(dist_p * dist_q < 0.){// it certanly cross current half-wave
+        dd = in_iteration_process(p, dist_p, q, p_fan);
+    }
+    G4double d2 = search_for_nearest_point(p, dist_p, q, p_fan);
+    if(d2 < kInfinity){
+        return distance + d2; // this half-wave is intersected
+    } else if(dd < kInfinity){
+        return distance + dd;
+    }
+    return kInfinity;
+}
+
+// This functions should be called in the case when we are sure that
+// points p (which sould be OUTSIDE of vertical fan) and out_point have
+// the surface of the vertical fan between them.
+// returns distance from point p to absorber surface
+// sets last parameter to the founded point
+G4double LArWheelSliceSolid::in_iteration_process(
+    const G4ThreeVector &p, G4double dist_p, G4ThreeVector &B, int p_fan
+) const
+{
+    LWSDBG(6, std::cout << "iip from " << p << " to " << B
+                        << " dir " << (B - p).unit()
+                        << std::endl);
+
+    G4ThreeVector A, C, diff;
+    A = p;
+    G4double dist_c;
+    unsigned int niter = 0;
+  //  assert(fabs(GetCalculator()->DistanceToTheNeutralFibre(A)) > m_FHTplusT);
+  //  assert(GetCalculator()->DistanceToTheNeutralFibre(A) == dist_p);
+    do {
+        C = A + B;
+        C *= 0.5;
+        dist_c = GetCalculator()->DistanceToTheNeutralFibre(C, p_fan);
+        if(dist_c * dist_p < 0. || fabs(dist_c) < m_FHTminusT){
+            B = C;
+        } else {
+            A = C;
+        }
+        niter ++;
+        diff = A - B;
+    } while(diff.mag2() > s_IterationPrecision2 && niter < s_IterationsLimit);
+    assert(niter < s_IterationsLimit);
+    assert(fabs(GetCalculator()->DistanceToTheNeutralFibre(B, p_fan)) < m_FHTplusT);
+    diff = p - B;
+    LWSDBG(7, std::cout << "iip result in " << niter << " = " << B
+                        << " " << diff.mag() << std::endl);
+    return diff.mag();
+}
+
+// search for the nearest to the neutral fibre of the vertical fan point
+// on the segment between p_in and p_out
+G4double LArWheelSliceSolid::search_for_nearest_point(
+    const G4ThreeVector &p_in, const G4double dist_p_in,
+    const G4ThreeVector &p_out, int p_fan
+) const
+{
+    LWSDBG(6, std::cout << "sfnp " << MSG_VECTOR(p_in) << " "
+                        << MSG_VECTOR(p_out) << std::endl);
+
+    G4ThreeVector A, B, C, l, diff;
+    A = p_in;
+    B = p_out;
+    diff = B - A;
+    l = diff.unit() * s_IterationPrecision;
+  // this is to correctly take the sign of the distance into account
+    G4double sign = dist_p_in < 0.? -1. : 1.;
+    G4double d_prime;
+    G4double dist_c;
+    unsigned long niter = 0;
+    do {
+        C = A + B;
+        C *= 0.5;
+        dist_c = GetCalculator()->DistanceToTheNeutralFibre(C, p_fan);
+        if(dist_c * sign <= 0.){ // we are in coditions for in_iteration_process
+            LWSDBG(7, std::cout << "sfnp0 " << dist_c << std::endl);
+            return in_iteration_process(p_in, dist_p_in, C, p_fan);
+        }
+    // calculate sign of derivative of distance to the neutral fibre
+    // hope this substitution is acceptable
+        diff = C - l;
+        d_prime = (dist_c - GetCalculator()->DistanceToTheNeutralFibre(diff, p_fan)) * sign;
+        if(d_prime < 0.) A = C;
+        else B = C;
+        niter ++;
+        diff = A - B;
+    } while(diff.mag2() > s_IterationPrecision2 && niter < s_IterationsLimit);
+    assert(niter < s_IterationsLimit);
+    if(fabs(dist_c) < m_FHTminusT){
+        LWSDBG(7, std::cout << "sfnp1 " << dist_c << std::endl);
+        return in_iteration_process(p_in, dist_p_in, C, p_fan);
+    }
+  //  let's check at p_in and p_out
+    if(dist_p_in * sign < dist_c * sign){
+        C = p_in;
+        dist_c = dist_p_in;
+    }
+    G4double dist_p_out = GetCalculator()->DistanceToTheNeutralFibre(p_out, p_fan);
+    if(dist_p_out *sign < dist_c * sign) C = p_out;
+    if(fabs(dist_p_out) < m_FHTminusT){
+        LWSDBG(7, std::cout << "sfnp2 " << dist_p_out << std::endl);
+        return in_iteration_process(p_in, dist_p_in, C, p_fan);
+    }
+    return kInfinity;
+}
+
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSliceSolidDisToOut.cxx b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSliceSolidDisToOut.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..c64837128115ea2d08092ae3f282d7aa6f00f8fe
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSliceSolidDisToOut.cxx
@@ -0,0 +1,206 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include <cassert>
+
+#include "GeoSpecialShapes/LArWheelCalculator.h"
+#include "LArWheelSliceSolid.h"
+
+G4double LArWheelSliceSolid::DistanceToOut(const G4ThreeVector &inputP) const
+{
+    LWSDBG(1, std::cout << TypeStr() << " DisToOut" << MSG_VECTOR(inputP) << std::endl);
+    if(m_BoundingShape->Inside(inputP) != kInside){
+        LWSDBG(2, std::cout << "DistanceToOut(p):"
+                            << " point " << MSG_VECTOR(inputP)
+                            << " is not inside of the m_BoundingShape."
+                            << std::endl);
+        return 0.;
+    }
+    G4ThreeVector p(inputP);
+    int p_fan = 0;
+    const G4double d = m_FanHalfThickness - fabs(GetCalculator()->DistanceToTheNearestFan(p, p_fan));
+    if(d < s_Tolerance){
+        LWSDBG(2, std::cout << "already not inside " << MSG_VECTOR(p) << std::endl);
+        return 0.;
+    }
+    const G4double d0 = m_BoundingShape->DistanceToOut(inputP);
+    LWSDBG(2, std::cout << "dto " << d << " " << d0 << std::endl);
+    if(d > d0) return d0;
+    else return d;
+}
+
+G4double LArWheelSliceSolid::DistanceToOut(const G4ThreeVector &inputP,
+                                      const G4ThreeVector &inputV,
+                                      const G4bool calcNorm,
+                                      G4bool* validNorm,
+                                      G4ThreeVector* sn) const
+{
+    LWSDBG(1, std::cout << TypeStr() << " DisToOut" << MSG_VECTOR(inputP)
+                        << MSG_VECTOR(inputV) << std::endl);
+
+    const EInside inside_BS = m_BoundingShape->Inside(inputP);
+    if(inside_BS == kOutside){
+        LWSDBG(2, std::cout << "DistanceToOut(p):"
+                            << " point " << MSG_VECTOR(inputP)
+                            << " is outside of m_BoundingShape." << std::endl);
+        if(calcNorm) *validNorm = false;
+        return 0.;
+    }
+
+    // If here inside or on surface of BS
+    G4ThreeVector p(inputP);
+    int p_fan = 0;
+    const G4double adtnf_p = fabs(GetCalculator()->DistanceToTheNearestFan(p, p_fan));
+    if(adtnf_p >= m_FHTplusT) {
+        LWSDBG(2, std::cout << "DistanceToOut(p, v): point "
+                            << MSG_VECTOR(inputP)
+                            << " is outside of solid." << std::endl);
+        if(calcNorm) *validNorm = false;
+        return 0.;
+    }
+
+    G4ThreeVector v(inputV);
+    const G4double phi0 = p.phi() - inputP.phi();
+    v.rotateZ(phi0);
+
+// former distance_to_out starts here
+    LWSDBG(4, std::cout << "dto: " << MSG_VECTOR(p) << " "
+                        << MSG_VECTOR(v) << std::endl);
+
+    G4ThreeVector q(p);
+
+    const G4double dto_bs = m_BoundingShape->DistanceToOut(
+        inputP, inputV, calcNorm, validNorm, sn
+    );
+    q = p + v * dto_bs;
+    if(q.y() < m_Ymin){
+        LWSDBG(5, std::cout << "dto exit point too low " << MSG_VECTOR(q) << std::endl);
+        const G4double dy = (m_Ymin - p.y()) / v.y();
+        q.setX(p.x() + v.x() * dy);
+        q.setY(m_Ymin);
+        q.setZ(p.z() + v.z() * dy);
+    }
+    LWSDBG(5, std::cout << "dto exit point " << MSG_VECTOR(q) << std::endl);
+
+    G4double tmp;
+    G4double distance = 0.;
+    if(fabs(GetCalculator()->DistanceToTheNeutralFibre(q, p_fan)) > m_FHTplusT){
+        LWSDBG(5, std::cout << "q=" << MSG_VECTOR(q)
+                            << " outside fan cur distance="
+                            << distance << ", m_FHTplusT="
+                            << m_FHTplusT << std::endl);
+        tmp = out_iteration_process(p, q, p_fan);
+    } else {
+        tmp = (q - p).mag();
+    }
+    G4ThreeVector C;
+    if(search_for_most_remoted_point(p, q, C, p_fan)){
+        tmp = out_iteration_process(p, C, p_fan);
+    }
+    distance += tmp;
+
+    LWSDBG(5, std::cout << "At end_dto distance=" << distance  << std::endl);
+    if(calcNorm && distance < dto_bs) *validNorm = false;
+
+#ifdef DEBUG_LARWHEELSLICESOLID
+    if(Verbose > 2){
+        std::cout << "DTO: " << distance << " ";
+        if (*validNorm) {
+          std::cout << *validNorm << " " << MSG_VECTOR((*sn));
+        } else {
+          std::cout << "Norm is not valid";
+        }
+        std::cout << std::endl;
+        if(Verbose > 3){
+            G4ThreeVector p2 = inputP + inputV * distance;
+            EInside i = Inside(p2);
+            std::cout << "DTO hit at " << MSG_VECTOR(p2) << ", "
+                      << inside(i) << std::endl;
+        }
+    }
+#ifdef LWS_HARD_TEST_DTO
+    if(test_dto(inputP, inputV, distance)){
+        if(Verbose == 1){
+            std::cout << TypeStr() << " DisToOut" << MSG_VECTOR(inputP)
+                      << MSG_VECTOR(inputV) << std::endl;
+        }
+    }
+#endif
+#endif
+    return distance;
+}
+
+// This functions should be called in the case when we are sure that
+// point p is NOT OUTSIDE of vertical fan and point out is NOT INSIDE vertical fan
+// returns distance from point p to fan surface, sets last
+// parameter to the found point
+// may give wrong answer - see description
+G4double LArWheelSliceSolid::out_iteration_process(const G4ThreeVector &p,
+                                              G4ThreeVector &B, const int p_fan) const
+{
+    LWSDBG(6, std::cout << "oip: " << p << " " << B);
+    assert(fabs(GetCalculator()->DistanceToTheNeutralFibre(p, p_fan)) < m_FHTplusT);
+    assert(fabs(GetCalculator()->DistanceToTheNeutralFibre(B, p_fan)) > m_FHTminusT);
+    G4ThreeVector A(p), C, diff;
+    unsigned int niter = 0;
+    do {
+        C = A + B;
+        C *= 0.5;
+        if(fabs(GetCalculator()->DistanceToTheNeutralFibre(C, p_fan)) < m_FHTplusT){
+            A = C;
+        } else {
+            B = C;
+        }
+        niter ++;
+        diff = A - B;
+    } while(diff.mag2() > s_IterationPrecision2 && niter < s_IterationsLimit);
+    assert(fabs(GetCalculator()->DistanceToTheNeutralFibre(B, p_fan)) > m_FHTplusT);
+    assert(niter < s_IterationsLimit);
+    diff = p - B;
+    LWSDBG(7, std::cout << " -> " << B << " " << diff.mag());
+    LWSDBG(6, std::cout << std::endl);
+    return diff.mag();
+}
+
+// returns true if the point being outside vert. fan is found, also set C to
+// that point in this case
+// returns false if the whole track looks like being inside vert. fan
+G4bool LArWheelSliceSolid::search_for_most_remoted_point(
+    const G4ThreeVector &a, const G4ThreeVector &b, G4ThreeVector &C, const int p_fan
+) const
+{
+    LWSDBG(6, std::cout << "sfmrp " << a << " " << b << std::endl);
+    G4ThreeVector diff(b - a);
+
+    if(diff.mag2() <= s_IterationPrecision2) return false;
+    G4ThreeVector A(a), B(b), l(diff.unit() * s_IterationPrecision);
+  // find the most remoted point on the line AB
+  // and check if it is outside vertical fan
+  // small vector along the segment AB
+    G4double d1, d2;
+    unsigned int niter = 0;
+  // searching for maximum of (GetCalculator()->DistanceToTheNeutralFibre)^2 along AB
+    do {
+        C = A + B;
+        C *= 0.5;
+        d1 = GetCalculator()->DistanceToTheNeutralFibre(C, p_fan);
+        if(fabs(d1) > m_FHTplusT){
+      // here out_iteration_process gives the answer
+            LWSDBG(7, std::cout << "sfmrp -> " << C << " " << fabs(d1)
+                                << "  " << (C - a).unit() << " "
+                                << (C - a).mag() << std::endl);
+            return true;
+        }
+    // sign of derivative
+    //d1 = GetCalculator()->DistanceToTheNeutralFibre(C + l);
+        d2 = GetCalculator()->DistanceToTheNeutralFibre(C - l, p_fan);
+        if(d1 * d1 - d2 * d2 > 0.) A = C;
+        else B = C;
+        niter ++;
+        diff = A - B;
+    } while(diff.mag2() > s_IterationPrecision2 && niter < s_IterationsLimit);
+  // the line entirely lies inside fan
+    assert(niter < s_IterationsLimit);
+    return false;
+}
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSliceSolidInit.cxx b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSliceSolidInit.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..252ff49b421322ea3b08938fc02ab456abb255c7
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSliceSolidInit.cxx
@@ -0,0 +1,283 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include <cassert>
+#include <stdexcept>
+#include <iostream>
+
+#ifndef  PORTABLE_LAR_SHAPE
+#include "RDBAccessSvc/IRDBAccessSvc.h"
+#include "RDBAccessSvc/IRDBRecord.h"
+#include "RDBAccessSvc/IRDBRecordset.h"
+
+#include "GeoModelInterfaces/IGeoModelSvc.h"
+#include "GeoModelInterfaces/IGeoDbTagSvc.h"
+#include "GeoModelUtilities/DecodeVersionKey.h"
+#include "GeoSpecialShapes/toEMECData.h"
+
+
+#include "GaudiKernel/Bootstrap.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/IMessageSvc.h"
+#endif
+
+#include "GeoSpecialShapes/EMECData.h"
+#include "CLHEP/Units/PhysicalConstants.h"
+#ifndef PORTABLE_LAR_SHAPE
+#include "AthenaBaseComps/AthMsgStreamMacros.h"
+#include "AthenaBaseComps/AthCheckMacros.h"
+#endif
+#include "G4GeometryTolerance.hh"
+
+#include "GeoSpecialShapes/LArWheelCalculator.h"
+#include "LArWheelSliceSolid.h"
+#include "G4ShiftedCone.h"
+
+#ifdef DEBUG_LARWHEELSLICESOLID
+G4int LArWheelSliceSolid::Verbose = 0;
+#endif
+
+// these are internal technical constants, should not go in DB
+const unsigned int LArWheelSliceSolid::s_IterationsLimit = 50; // That's enough even for 10e-15 IterationPrecision
+const G4double LArWheelSliceSolid::s_Tolerance = G4GeometryTolerance::GetInstance()->GetSurfaceTolerance() / 2;
+const G4double LArWheelSliceSolid::s_AngularTolerance = G4GeometryTolerance::GetInstance()->GetAngularTolerance() / 2;
+const G4double LArWheelSliceSolid::s_IterationPrecision = 0.001*CLHEP::mm;
+const G4double LArWheelSliceSolid::s_IterationPrecision2 = s_IterationPrecision * s_IterationPrecision;
+
+LArWheelSliceSolid::LArWheelSliceSolid(
+    const G4String& name,
+    pos_t pos, type_t type, size_t slice,
+    G4int zside,
+    const LArWheelCalculator *calc,
+    const EMECData           *emecData
+) : G4VSolid(name), m_Pos(pos), m_Type(type),
+    m_Calculator(calc)
+#ifndef PORTABLE_LAR_SHAPE    
+  ,m_msg("LArWSS")
+#endif
+{
+  createSolid(name, zside, slice, emecData);
+}
+
+LArWheelSliceSolid::LArWheelSliceSolid(const G4String& name, const EMECData *emecData)
+  : G4VSolid(name), m_Calculator(0)
+#ifndef PORTABLE_LAR_SHAPE    
+  , m_msg("LArWSS")
+#endif
+{
+    if(name.find("::Inner") != G4String::npos) m_Pos = Inner;
+    else if(name.find("::Outer") != G4String::npos) m_Pos = Outer;
+    else G4Exception(
+        "LArWheelSliceSolid", "NoPos", FatalException,
+        (std::string("Constructor: can't get Inner/Outer from ") + name).c_str()
+    );
+
+    if(name.find("::Absorber") != G4String::npos) m_Type = Absorber;
+    else if(name.find("::Electrode") != G4String::npos) m_Type = Electrode;
+    else if(name.find("::Glue") != G4String::npos) m_Type = Glue;
+    else if(name.find("::Lead") != G4String::npos) m_Type = Lead;
+    else G4Exception(
+        "LArWheelSliceSolid", "NoType", FatalException,
+        (std::string("Constructor: can't get Type from ") + name).c_str()
+    );
+
+    size_t s = name.find("Slice");
+    size_t slice = 0;
+    if(G4String::npos != s){
+        slice = (size_t) atoi(name.substr(s + 5, 2).c_str());
+    } else G4Exception(
+        "LArWheelSliceSolid", "NoSlice", FatalException,
+        (std::string("Constructor: can't get Slice from ") + name).c_str()
+    );
+
+    G4int zside = 0;
+    if(name.find("::Pos::") != G4String::npos) zside = 1;
+    else if(name.find("::Neg::") != G4String::npos) zside = -1;
+    else G4Exception(
+        "LArWheelSliceSolid", "NoSide", FatalException,
+        (std::string("Constructor: can't get zSide from ") + name).c_str()
+    );
+    createSolid(name, zside, slice, emecData);
+}
+
+void LArWheelSliceSolid::createSolid(const G4String& name, G4int zside, size_t slice, const EMECData *emecData)
+{
+#ifndef PORTABLE_LAR_SHAPE
+    m_f_area = m_f_vol = m_f_area_on_pc = m_f_length = m_f_side_area = 0;
+#endif
+    
+    LArG4::LArWheelCalculator_t calc_type = LArG4::LArWheelCalculator_t(0);
+    switch(m_Pos){
+    case Inner:
+        switch(m_Type){
+        case Absorber: calc_type = LArG4::InnerAbsorberWheel; break;
+        case Electrode: calc_type = LArG4::InnerElectrodWheel; break;
+        case Glue: calc_type = LArG4::InnerGlueWheel; break;
+        case Lead: calc_type = LArG4::InnerLeadWheel; break;
+        }
+        break;
+    case Outer:
+        switch(m_Type){
+        case Absorber: calc_type = LArG4::OuterAbsorberWheel; break;
+        case Electrode: calc_type = LArG4::OuterElectrodWheel; break;
+        case Glue: calc_type = LArG4::OuterGlueWheel; break;
+        case Lead: calc_type = LArG4::OuterLeadWheel; break;
+        }
+        break;
+    }
+    if(m_Calculator == 0) {
+      m_Calculator = new LArWheelCalculator(*emecData, calc_type, zside);
+
+    }
+    else if(m_Calculator->type() != calc_type){
+        G4Exception(
+            "LArWheelSliceSolid", "WrongCalculatorType", FatalException,
+            "Constructor: external LArWheelCalculator of wrong type provided"
+        );
+    }
+
+    const G4String bs_name = name + "-Bounding";
+
+#ifdef DEBUG_LARWHEELSLICESOLID
+    const char *venv = getenv("LARWHEELSLICESOLID_VERBOSE");
+    if(venv) Verbose = atoi(venv);
+    static bool first = true;
+    if(first){
+        std::cout << "The LArWheelSliceSolid build "
+                  << __DATE__ << " " << __TIME__  << std::endl
+                  << "LArWheelSliceSolid verbosity level is "
+                  << Verbose << std::endl;
+        first = false;
+    }
+#endif
+
+    m_FanHalfThickness = GetCalculator()->GetFanHalfThickness();
+    m_FHTplusT = m_FanHalfThickness + s_Tolerance;
+    m_FHTminusT = m_FanHalfThickness - s_Tolerance;
+
+    switch(m_Pos){
+    case Inner: inner_solid_init(bs_name, slice); break;
+    case Outer: outer_solid_init(bs_name, slice); break;
+    }
+#ifndef PORTABLE_LAR_SHAPE
+    ATH_MSG_DEBUG(m_BoundingShape->GetName() + " is the m_BoundingShape");
+
+    init_tests();
+    test(); // activated by env. variable
+    clean_tests();
+
+    ATH_MSG_DEBUG("slice " << m_Pos << " "  << m_Type
+		  << " " << slice << " initialized" << endmsg);
+
+#endif
+    
+ #ifdef DEBUG_LARWHEELSLICESOLID
+    std::cout << "LArWSS(" << m_Pos << ", " << m_Type
+              << "): slice " << slice << ", Zmin = " << m_Zmin
+              << ", Zmax = " << m_Zmax << std::endl
+              << GetName() << std::endl;
+#endif
+}
+
+inline void LArWheelSliceSolid::check_slice(size_t size, size_t slice) const
+{
+    if(size <= slice + 1){
+        G4Exception(
+            "LArWheelSliceSolid", "WrongSlice", FatalException,
+            std::string("Constructor: Slice number too big: " + GetName()).c_str()
+        );
+    }
+}
+
+void LArWheelSliceSolid::inner_solid_init(const G4String &bs_name, size_t slice)
+{
+    G4double zPlane[2], rInner[2], rOuter[2];
+    zPlane[0] = 0.;
+    zPlane[1] = GetCalculator()->GetWheelThickness();
+    GetCalculator()->GetWheelInnerRadius(rInner);
+    GetCalculator()->GetWheelOuterRadius(rOuter);
+
+    std::vector<G4double> zsect;
+    fill_zsect(zsect);
+    check_slice(zsect.size(), slice);
+
+    m_Zmin = zsect[slice]; m_Zmax = zsect[slice + 1];
+    m_Rmin = rInner[0];      m_Rmax = rOuter[1];
+    const G4double ainn = (rInner[1] - rInner[0]) / (zPlane[1] - zPlane[0]);
+    const G4double aout = (rOuter[1] - rOuter[0]) / (zPlane[1] - zPlane[0]);
+    const G4double R1inn = ainn * (m_Zmin - zPlane[0]) + rInner[0];
+    const G4double R1out = aout * (m_Zmin - zPlane[0]) + rOuter[0];
+    const G4double R2inn = ainn * (m_Zmax - zPlane[0]) + rInner[0];
+    const G4double R2out = aout * (m_Zmax - zPlane[0]) + rOuter[0];
+
+    m_BoundingShape = new G4ShiftedCone(
+        bs_name + "Cone", m_Zmin, m_Zmax, R1inn, R1out, R2inn, R2out
+    );
+
+    const G4double FanPhiAmplitude = 0.065; // internal technical constant, should not go in DB
+    const G4double phi_min = CLHEP::halfpi
+                           - FanPhiAmplitude
+                           - GetCalculator()->GetFanStepOnPhi() * 2;
+    m_Xmax = rOuter[1]*cos(phi_min);
+    m_Xmin = -m_Xmax;
+
+    m_Ymin = m_Rmin * 0.9;
+}
+
+void LArWheelSliceSolid::outer_solid_init(const G4String &bs_name, size_t slice)
+{
+    G4double zPlane[3], rInner[3], rOuter[3];
+    zPlane[0] = 0.;
+    zPlane[1] = GetCalculator()->GetWheelInnerRadius(rInner);
+    zPlane[2] = GetCalculator()->GetWheelThickness();
+    GetCalculator()->GetWheelOuterRadius(rOuter);
+
+    std::vector<G4double> zsect;
+    fill_zsect(zsect, zPlane[1]);
+    check_slice(zsect.size(), slice);
+
+    m_Zmin = zsect[slice]; m_Zmax = zsect[slice + 1];
+    m_Rmin = rInner[0];      m_Rmax = rOuter[1];
+    const size_t i = m_Zmax > zPlane[1]? 1: 0;
+    const G4double dz = zPlane[i + 1] - zPlane[i];
+    const G4double ainn = (rInner[i + 1] - rInner[i]) / dz;
+    const G4double aout = (rOuter[i + 1] - rOuter[i]) / dz;
+    const G4double R1inn = ainn * (m_Zmin - zPlane[i]) + rInner[i];
+    const G4double R1out = aout * (m_Zmin - zPlane[i]) + rOuter[i];
+    const G4double R2inn = ainn * (m_Zmax - zPlane[i]) + rInner[i];
+    const G4double R2out = aout * (m_Zmax - zPlane[i]) + rOuter[i];
+
+    m_BoundingShape = new G4ShiftedCone(
+        bs_name + "Cone", m_Zmin, m_Zmax, R1inn, R1out, R2inn, R2out
+    );
+
+    const G4double FanPhiAmplitude = 0.02; // internal technical constant, should not go in DB
+    const G4double phi_min = CLHEP::halfpi
+                           - FanPhiAmplitude
+                           - GetCalculator()->GetFanStepOnPhi() * 2;
+    m_Xmax = rOuter[1]*cos(phi_min);
+    m_Xmin = -m_Xmax;
+
+    m_Ymin = m_Rmin * 0.9;
+}
+
+void LArWheelSliceSolid::fill_zsect(std::vector<G4double> &zsect, G4double zMid) const
+{
+    const G4double half_wave_length = GetCalculator()->GetHalfWaveLength();
+    const G4double sss = GetCalculator()->GetStraightStartSection();
+    const G4int num_fs = GetCalculator()->GetNumberOfHalfWaves() + 1;
+    const G4double wheel_thickness = GetCalculator()->GetWheelThickness();
+
+    zsect.push_back(0.);
+    zsect.push_back(sss + half_wave_length * 0.25);
+    for(G4int i = 2; i < num_fs; i ++){
+        const G4double zi = half_wave_length * (i - 1) + sss;
+        if(m_Pos == Outer && zsect.back() < zMid && zi > zMid){
+            zsect.push_back(zMid);
+        }
+        zsect.push_back(zi);
+    }
+    zsect.push_back(wheel_thickness - sss - half_wave_length * 0.25);
+    zsect.push_back(wheel_thickness);
+}
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSolid.cxx b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSolid.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..a0224fed57ef4690083d182b3a6f70f6d222a7a9
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSolid.cxx
@@ -0,0 +1,170 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "G4VGraphicsScene.hh"
+#include "G4VisExtent.hh"
+
+#include "GeoSpecialShapes/LArWheelCalculator.h"
+#include "LArWheelSolid.h"
+
+class G4NURBS;
+class G4VoxelLimits;
+class G4AffineTransform;
+
+EInside LArWheelSolid::Inside(const G4ThreeVector &inputP) const
+{
+	LWSDBG(10, std::cout << std::setprecision(25));
+	LWSDBG(1, std::cout << TypeStr() << " Inside " << MSG_VECTOR(inputP) << std::endl);
+	const EInside inside_BS = m_BoundingShape->Inside(inputP);
+	if(inside_BS == kOutside){
+		LWSDBG(2, std::cout << "outside BS" << std::endl);
+		return kOutside;
+	}
+	G4ThreeVector p( inputP );
+	int p_fan = 0;
+	const G4double d = fabs(GetCalculator()->DistanceToTheNearestFan(p, p_fan));
+	if(d > m_FHTplusT){
+		LWSDBG(2, std::cout << "outside fan d=" << d << ", m_FHTplusT=" << m_FHTplusT << std::endl);
+		return kOutside;
+	}
+	if(d < m_FHTminusT){
+		LWSDBG(2, std::cout << "inside fan d=" << d << ", m_FHTminusT=" << m_FHTminusT << ", inside_BS=" << inside(inside_BS) << std::endl);
+		return inside_BS;
+	}
+	LWSDBG(2, std::cout << "surface" << std::endl);
+	return kSurface;
+}
+
+G4ThreeVector LArWheelSolid::SurfaceNormal(const G4ThreeVector &inputP) const
+{
+	LWSDBG(1, std::cout << TypeStr() << " SurfaceNormal" << MSG_VECTOR(inputP) << std::endl);
+	EInside inside_BS = m_BoundingShape->Inside(inputP);
+	if(inside_BS != kInside){
+		LWSDBG(2, std::cout << "not inside BS" << std::endl);
+		return m_BoundingShape->SurfaceNormal(inputP);
+	}
+	G4ThreeVector p( inputP );
+	int p_fan = 0;
+	GetCalculator()->DistanceToTheNearestFan(p, p_fan);
+	G4ThreeVector d = GetCalculator()->NearestPointOnNeutralFibre(p, p_fan);
+	d.rotateZ(inputP.phi() -  p.phi()); // rotate back to initial position
+	LWSDBG(4, std::cout << "npnf" << MSG_VECTOR(d) << std::endl);
+	p = inputP - d;
+	LWSDBG(2, std::cout << "sn " << MSG_VECTOR(p.unit()) << std::endl);
+	return(p.unit());
+}
+
+G4bool LArWheelSolid::CalculateExtent(const EAxis a, const G4VoxelLimits &vl,
+                                      const G4AffineTransform &t, G4double &p,
+                                      G4double &q) const
+{
+	return m_BoundingShape->CalculateExtent(a, vl, t, p, q);
+}
+
+G4GeometryType LArWheelSolid::GetEntityType() const
+{
+  switch(m_Type){
+  case InnerAbsorberWheel:
+    return G4String("LArInnerAbsorberWheel");
+    break;
+  case OuterAbsorberWheel:
+    return G4String("LArOuterAbsorberWheel");
+    break;
+  case InnerElectrodWheel:
+    return G4String("LArInnerElecrodWheel");
+    break;
+  case OuterElectrodWheel:
+    return G4String("LArOuterElecrodWheel");
+    break;
+  case InnerAbsorberModule:
+    return G4String("LArInnerAbsorberModule");
+    break;
+  case OuterAbsorberModule:
+    return G4String("LArOuterAbsorberModule");
+    break;
+  case InnerElectrodModule:
+    return G4String("LArInnerElecrodModule");
+    break;
+  case OuterElectrodModule:
+    return G4String("LArOuterElecrodModule");
+    break;
+  case InnerGlueWheel:
+    return G4String("LArInnerGlueWheel");
+    break;
+  case OuterGlueWheel:
+    return G4String("LArOuterGlueWheel");
+    break;
+  case InnerLeadWheel:
+    return G4String("LArInnerLeadWheel");
+    break;
+  case OuterLeadWheel:
+    return G4String("LArOuterLeadWheel");
+    break;
+  case InnerAbsorberCone:
+    return G4String("LArInnerAbsorberCone");
+    break;
+  case InnerElectrodCone:
+    return G4String("LArInnerElectrodCone");
+    break;
+  case InnerGlueCone:
+    return G4String("LArInnerGlueCone");
+    break;
+  case InnerLeadCone:
+    return G4String("LArInnerLeadCone");
+    break;
+  case OuterAbsorberFrontCone:
+    return G4String("LArOuterAbsorberFrontCone");
+    break;
+  case OuterElectrodFrontCone:
+    return G4String("LArOuterElectrodFrontCone");
+    break;
+  case OuterGlueFrontCone:
+    return G4String("LArOuterGlueFrontCone");
+    break;
+  case OuterLeadFrontCone:
+    return G4String("LArOuterLeadFrontCone");
+    break;
+  case OuterAbsorberBackCone:
+    return G4String("LArOuterAbsorberBackCone");
+    break;
+  case OuterElectrodBackCone:
+    return G4String("LArOuterElectrodBackCone");
+    break;
+  case OuterGlueBackCone:
+    return G4String("LArOuterGlueBackCone");
+    break;
+  case OuterLeadBackCone:
+    return G4String("LArOuterLeadBackCone");
+    break;
+  default:
+    G4Exception("LArWheelSolid", "UnknownSolidType", FatalException,"GetEntityType: Unknown LArWheelType.");
+  }
+  return G4String("");
+}
+
+void LArWheelSolid::DescribeYourselfTo(G4VGraphicsScene &scene) const
+{
+  scene.AddSolid(*this);
+}
+
+G4VisExtent LArWheelSolid::GetExtent() const
+{
+  return m_BoundingShape->GetExtent();
+}
+
+G4Polyhedron* LArWheelSolid::CreatePolyhedron() const
+{
+  return m_BoundingShape->CreatePolyhedron();
+}
+
+/*
+ * returns the number of lower z boundary of z-section containing Z
+ */
+G4int LArWheelSolid::select_section(const G4double &Z) const
+{
+	for(G4int i = m_Zsect_start_search; i > 0; -- i){
+		if(Z > m_Zsect[i]) return i;
+	}
+	return 0;
+}
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSolid.h b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSolid.h
new file mode 100644
index 0000000000000000000000000000000000000000..f52005db4d015bbd507715f2a4ffcc5f9f1ca79c
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSolid.h
@@ -0,0 +1,266 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef GEO2G4_LARWHEELSOLID_H
+#define GEO2G4_LARWHEELSOLID_H
+#ifndef PORTABLE_LAR_SHAPE
+#include "AthenaKernel/MsgStreamMember.h"
+#endif
+#include "G4VSolid.hh"
+
+// set this to allow debug output in LArWheelSolid methods
+// disabled by default to avoid any performance degradation
+//#define DEBUG_LARWHEELSOLID
+
+// set this to use native G4 FanBound's methods for DisToIn
+// instead of local calculations
+//#define LARWHEELSOLID_USE_FANBOUND
+
+// set this to use BoundingShape's methods for DisToOut
+// instead of local calculations
+#define LARWHEELSOLID_USE_BS_DTO
+
+// change this to have more z sections
+#define LARWHEELSOLID_ZSECT_MULT 1
+
+
+// set this to check in dti and dto functions if particle direction
+// pointing inside or outside of volume to return zero fast when it is required by spec.
+// currently at development stage, requires accurate surface normal calculations
+//#define CHECK_DIRTONORM_ANGLE_ON_SURFACE
+
+#ifdef DEBUG_LARWHEELSOLID
+#define LWSDBG(a, b) if(Verbose >= a) b
+#define MSG_VECTOR(v) "(" << v.x() << ", " << v.y() << ", " << v.z() << ")"
+//#define LWS_HARD_TEST_DTI
+//#define LWS_HARD_TEST_DTO
+#else
+#define LWSDBG(a, b)
+#endif
+
+// Forward declarations.
+class G4VGraphicsScene;
+class G4VisExtent;
+class G4Polyhedron;
+class G4NURBS;
+class G4VoxelLimits;
+class G4AffineTransform;
+class G4Polycone;
+class LArWheelCalculator;
+class TF1;
+class LArFanSections;
+class G4Polyhedra;
+struct EMECData;
+
+#include "LArWheelSolid_type.h"
+
+inline const char *LArWheelSolidTypeString(LArWheelSolid_t type)
+{
+  switch(type){
+  case InnerAbsorberWheel: return("InnerAbsorberWheel");
+  case OuterAbsorberWheel: return("OuterAbsorberWheel");
+  case InnerElectrodWheel: return("InnerElectrodWheel");
+  case OuterElectrodWheel: return("OuterElectrodWheel");
+  case InnerAbsorberModule: return("InnerAbsorberModule");
+  case OuterAbsorberModule: return("OuterAbsorberModule");
+  case InnerElectrodModule: return("InnerElectrodModule");
+  case OuterElectrodModule: return("OuterElectrodModule");
+  case InnerLeadWheel: return("InnerLeadWheel");
+  case OuterLeadWheel: return("OuterLeadWheel");
+  case InnerGlueWheel: return("InnerGlueWheel");
+  case OuterGlueWheel: return("OuterGlueWheel");
+  case InnerAbsorberCone: return("InnerAbsorberCone");
+  case InnerElectrodCone: return("InnerElectrodCone");
+  case InnerGlueCone: return("InnerGlueCone");
+  case InnerLeadCone: return("InnerLeadCone");
+  case OuterAbsorberFrontCone: return("OuterAbsorberFrontCone");
+  case OuterElectrodFrontCone: return("OuterElectrodFrontCone");
+  case OuterGlueFrontCone: return("OuterGlueFrontCone");
+  case OuterLeadFrontCone: return("OuterLeadFrontCone");
+  case OuterAbsorberBackCone: return("OuterAbsorberBackCone");
+  case OuterElectrodBackCone: return("OuterElectrodBackCone");
+  case OuterGlueBackCone: return("OuterGlueBackCone");
+  case OuterLeadBackCone: return("OuterLeadBackCone");
+  }
+  return("unknown");
+}
+
+class LArWheelSolid : public G4VSolid
+{
+public:
+
+  LArWheelSolid(const G4String& name, LArWheelSolid_t type,
+     G4int zside = 1,
+		LArWheelCalculator *calc = 0,
+		const EMECData     *emecData=0
+  );
+  virtual ~LArWheelSolid();
+
+  // Mandatory for custom solid Geant4 functions
+  EInside Inside(const G4ThreeVector&) const;
+  G4double DistanceToIn(const G4ThreeVector&,
+                        const G4ThreeVector&) const;
+  G4double DistanceToIn(const G4ThreeVector&) const;
+  G4double DistanceToOut(const G4ThreeVector&,
+                         const G4ThreeVector&,
+                         const G4bool calcNorm = false,
+                         G4bool* validNorm = 0,
+                         G4ThreeVector* n = 0) const;
+  G4double DistanceToOut(const G4ThreeVector&) const;
+  G4ThreeVector SurfaceNormal (const G4ThreeVector&) const;
+  G4bool CalculateExtent(const EAxis,
+                         const G4VoxelLimits&,
+                         const G4AffineTransform&,
+                         G4double&,
+                         G4double&) const;
+  G4GeometryType GetEntityType() const;
+  void DescribeYourselfTo(G4VGraphicsScene&) const;
+  G4VisExtent GetExtent() const;
+  G4Polyhedron* CreatePolyhedron() const;
+
+  // 07-Feb-2003 WGS: For compatibility with Geant 4.5.0
+  virtual std::ostream& StreamInfo(std::ostream& os) const { return os; }
+
+  const G4VSolid *GetBoundingShape(void) const { return m_BoundingShape; }
+  const LArWheelCalculator *GetCalculator(void) const { return m_Calculator; }
+  LArWheelSolid_t GetType(void) const { return m_Type; }
+
+#ifndef PORTABLE_LAR_SHAPE
+  G4ThreeVector GetPointOnSurface(void) const;
+  G4double GetCubicVolume(void);
+  G4double GetSurfaceArea(void);
+#endif
+  
+private:
+  static const G4double s_Tolerance;
+  static const G4double s_AngularTolerance;
+  static const G4double s_IterationPrecision;
+  static const G4double s_IterationPrecision2;
+  static const unsigned int s_IterationsLimit;
+
+  G4bool m_IsOuter;
+  const LArWheelSolid_t m_Type;
+  LArWheelCalculator *m_Calculator;
+  G4double m_FanHalfThickness, m_FHTplusT, m_FHTminusT;
+  G4double m_FanPhiAmplitude;
+  G4double m_MinPhi;
+  G4double m_MaxPhi;
+  const G4double m_PhiPosition;
+  G4VSolid* m_BoundingShape;
+#ifdef LARWHEELSOLID_USE_FANBOUND
+  G4VSolid* m_FanBound;
+#endif
+
+  std::vector<G4double> m_Zsect;
+  G4int m_Zsect_start_search;
+
+  LArFanSections *m_fs;
+
+  // z at outer wheel "bend"
+  G4double m_Zmid;
+  // Special limit, used in dto
+  G4double m_Ymin;
+  // limits for use in service functions
+  G4double m_Zmin, m_Zmax, m_Rmin, m_Rmax;
+  //artificial level to distinguish between inner and outer cones
+  G4double m_Ymid;
+
+  void inner_solid_init(const G4String &);
+  void outer_solid_init(const G4String &);
+  void set_phi_size(void);
+
+  virtual G4double distance_to_in(G4ThreeVector &,
+                                  const G4ThreeVector &, int) const;
+  G4double in_iteration_process(const G4ThreeVector &,
+                                G4double, G4ThreeVector &, int) const;
+  G4double search_for_nearest_point(const G4ThreeVector &, const G4double,
+                                    const G4ThreeVector &, int) const;
+  G4bool search_for_most_remoted_point(const G4ThreeVector &,
+                                       const G4ThreeVector &,
+                                       G4ThreeVector &, const int) const;
+  G4double out_iteration_process(const G4ThreeVector &,
+                                 G4ThreeVector &, const int) const;
+
+  typedef enum {
+    NoCross, ExitAtInner, ExitAtOuter,
+    ExitAtFront, ExitAtBack, ExitAtSide
+  } FanBoundExit_t;
+
+  FanBoundExit_t find_exit_point(const G4ThreeVector &p,
+                                 const G4ThreeVector &v,
+                                 G4ThreeVector &q) const;
+  G4bool fs_cross_lower(const G4ThreeVector &p, const G4ThreeVector &v,
+                        G4ThreeVector &q) const;
+  G4bool fs_cross_upper(const G4ThreeVector &p, const G4ThreeVector &v,
+                        G4ThreeVector &q) const;
+  G4bool check_D(G4double &b,
+                 G4double A, G4double B, G4double C, G4bool) const;
+
+  G4int select_section(const G4double &Z) const;
+
+#ifndef PORTABLE_LAR_SHAPE
+  EInside Inside_accordion(const G4ThreeVector&) const;
+  void get_point_on_accordion_surface(G4ThreeVector &) const;
+  void get_point_on_polycone_surface(G4ThreeVector &) const;
+  void get_point_on_flat_surface(G4ThreeVector &) const;
+  void set_failover_point(G4ThreeVector &p, const char *m = 0) const;
+
+  G4double get_area_on_polycone(void) const;
+  G4double get_area_on_face(void) const;
+  G4double get_area_on_side(void) const;
+
+  G4double get_area_at_r(G4double r) const;
+  G4double get_length_at_r(G4double r) const;
+
+  void test(void);
+  void clean_tests(void);
+  void init_tests(void);
+  /// Log a message using the Athena controlled logging system
+  MsgStream& msg( MSG::Level lvl ) const { return m_msg << lvl; }
+  /// Check whether the logging system is active at the provided verbosity level
+  bool msgLvl( MSG::Level lvl ) const { return m_msg.get().level() <= lvl; }
+#endif
+
+protected:
+  /// Private message stream member
+#ifndef PORTABLE_LAR_SHAPE
+  mutable Athena::MsgStreamMember m_msg;
+#endif
+
+  TF1 *m_f_area, *m_f_vol, *m_f_area_on_pc, *m_f_length, *m_f_side_area;
+
+  double m_test_index;
+  friend double LArWheelSolid_fcn_area(double *, double *);
+  friend double LArWheelSolid_fcn_vol(double *, double *);
+  friend double LArWheelSolid_fcn_area_on_pc(double *, double *);
+  friend double LArWheelSolid_get_dl(double *, double *, G4int);
+  friend double LArWheelSolid_fcn_side_area(double *, double *);
+
+#ifdef DEBUG_LARWHEELSOLID
+  static const char* inside(EInside i)
+  {
+    switch(i){
+    case kInside: return "inside"; break;
+    case kSurface: return "surface"; break;
+    case kOutside: return "outside"; break;
+    }
+    return "unknown";
+  }
+
+public:
+  static G4int Verbose;
+  void SetVerbose(G4int v){ Verbose = v; }
+#ifndef PORTABLE_LAR_SHAPE
+  G4bool test_dti(const G4ThreeVector &p,
+                  const G4ThreeVector &v, const G4double distance) const;
+  G4bool test_dto(const G4ThreeVector &p,
+                  const G4ThreeVector &v, const G4double distance) const;
+#endif
+private:
+  const char *TypeStr(void) const { return LArWheelSolidTypeString(m_Type); }
+#endif
+};
+
+#endif // GEO2G4_LARWHEELSOLID_H
+
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSolidDisToIn.cxx b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSolidDisToIn.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..74937cfe67c33209106d04234d1a64e36c99e1bb
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSolidDisToIn.cxx
@@ -0,0 +1,325 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+// DistanceToIn stuff for LArWheelSolid
+#include <cassert>
+#ifndef PORTABLE_LAR_SHAPE
+#include "AthenaBaseComps/AthMsgStreamMacros.h"
+#else
+#endif
+#include "CLHEP/Units/PhysicalConstants.h"
+
+#include "GeoSpecialShapes/LArWheelCalculator.h"
+#include "LArWheelSolid.h"
+#include "LArFanSection.h"
+
+G4double LArWheelSolid::DistanceToIn(const G4ThreeVector &inputP) const
+{
+	LWSDBG(1, std::cout << TypeStr() << " DisToIn" << MSG_VECTOR(inputP) << std::endl);
+	if(m_BoundingShape->Inside(inputP) == kOutside) {
+    // here is an approximation - for the point outside m_BoundingShape
+    // the solid looks like a m_BoundingShape
+    // it's okay since the result could be underestimated
+		LWSDBG(2, std::cout << "Outside BS" << std::endl);
+		return m_BoundingShape->DistanceToIn(inputP);
+	}
+	G4ThreeVector p(inputP);
+
+	//
+	// DistanceToTheNearestFan:
+	// rotates point p to the localFan coordinates and returns fan number to out_fan_number parameter
+	// returns distance to fan as result
+	//
+
+	int p_fan = 0;
+	const G4double d = fabs(GetCalculator()->DistanceToTheNearestFan(p, p_fan));
+	if(d > m_FHTplusT){
+		const G4double result = d - m_FanHalfThickness;
+		LWSDBG(2, std::cout << "dti result = " << result << std::endl);
+		return result;
+	}
+	LWSDBG(2, std::cout << "already inside, return 0" << MSG_VECTOR(p) << std::endl);
+	return 0.;
+}
+
+G4double LArWheelSolid::DistanceToIn(const G4ThreeVector &inputP,
+                                     const G4ThreeVector &inputV) const
+{
+	LWSDBG(1, std::cout << TypeStr() << " DisToIn" << MSG_VECTOR(inputP)
+	                    << MSG_VECTOR(inputV) << std::endl);
+
+	G4double distance = 0.;
+	const EInside inside_BS = m_BoundingShape->Inside(inputP);
+	G4ThreeVector p(inputP);
+	if(inside_BS == kOutside) {
+		distance = m_BoundingShape->DistanceToIn(inputP, inputV);
+		if(distance == kInfinity) {
+			LWSDBG(2, std::cout << "Infinity distance to m_BoundingShape" << MSG_VECTOR(inputP) << MSG_VECTOR(inputV) << std::endl);
+			return kInfinity;
+		}
+		p += inputV * distance;
+		assert(m_BoundingShape->Inside(p) != kOutside);
+		LWSDBG(2, std::cout << "shift" << MSG_VECTOR(inputP) << std::endl);
+	}
+
+	const G4double phi0 = p.phi();
+	int p_fan = 0;
+	const G4double d = GetCalculator()->DistanceToTheNearestFan(p, p_fan);
+	if(fabs(d) < m_FHTminusT){
+		LWSDBG(2, std::cout << "already inside fan" << MSG_VECTOR(p) << std::endl);
+		// if initial point is on BS surface and inside fan volume it is a real surface
+		if(inside_BS == kSurface) {
+			LWSDBG(2, std::cout << "On BS surface" << std::endl);
+			return m_BoundingShape->DistanceToIn(inputP, inputV);
+		}
+		return distance;
+	}
+	G4ThreeVector v(inputV);
+	v.rotateZ(p.phi() - phi0);
+
+	const G4double d0 = distance_to_in(p, v, p_fan);
+	distance += d0;
+
+#ifdef DEBUG_LARWHEELSOLID
+	if(Verbose > 2){
+		if(Verbose > 3){
+			std::cout << MSG_VECTOR(inputP)
+			          << " " << MSG_VECTOR(inputV) << std::endl;
+		}
+		std::cout << "dti: " << d0 << ", DTI: " << distance << std::endl;
+	}
+	if(Verbose > 3){
+		if(d0 < kInfinity){
+			G4ThreeVector p2 = inputP + inputV*distance;
+			EInside i = Inside(p2);
+			std::cout << "DTI hit at dist. " << distance << ", point "
+			          << MSG_VECTOR(p2) << ", "
+			          << inside(i) << std::endl;
+		} else {
+			std::cout << "got infinity from dti" << std::endl;
+		}
+	}
+#ifdef LWS_HARD_TEST_DTI
+	if(test_dti(inputP, inputV, distance)){
+		if(Verbose == 1){
+			std::cout << TypeStr() << " DisToIn" << MSG_VECTOR(inputP)
+	                  << MSG_VECTOR(inputV) << std::endl;
+		}
+	}
+	if(Verbose == 1){
+		std::cout << TypeStr() << " DisToIn" << MSG_VECTOR(inputP)
+	              << MSG_VECTOR(inputV) << " " << distance << std::endl;
+	}
+#endif // ifdef LWS_HARD_TEST_DTI
+
+#endif // ifdef DEBUG_LARWHEELSOLID
+
+  return distance;
+}
+
+// This functions should be called in the case when we are sure that
+// points p (which sould be OUTSIDE of vertical fan) and out_point have
+// the surface of the vertical fan between them.
+// returns distance from point p to absorber surface
+// sets last parameter to the founded point
+G4double LArWheelSolid::in_iteration_process(
+	const G4ThreeVector &p, G4double dist_p, G4ThreeVector &B, int p_fan
+) const
+{
+	LWSDBG(6, std::cout << "iip from " << p << " to " << B
+	                    << " dir " << (B - p).unit()
+	                    << std::endl);
+
+	G4ThreeVector A, C, diff;
+	A = p;
+	G4double dist_c;
+	unsigned int niter = 0;
+  //  assert(fabs(GetCalculator()->DistanceToTheNeutralFibre(A)) > m_FHTplusT);
+  //  assert(GetCalculator()->DistanceToTheNeutralFibre(A) == dist_p);
+	do {
+		C = A + B;
+		C *= 0.5;
+		dist_c = GetCalculator()->DistanceToTheNeutralFibre(C, p_fan);
+		if(dist_c * dist_p < 0. || fabs(dist_c) < m_FHTminusT){
+			B = C;
+		} else {
+			A = C;
+		}
+		niter ++;
+		diff = A - B;
+	} while(diff.mag2() > s_IterationPrecision2 && niter < s_IterationsLimit);
+	assert(niter < s_IterationsLimit);
+	assert(fabs(GetCalculator()->DistanceToTheNeutralFibre(B, p_fan)) < m_FHTplusT);
+	diff = p - B;
+	LWSDBG(7, std::cout << "iip result in " << niter << " = " << B
+	                    << " " << diff.mag() << std::endl);
+	return diff.mag();
+}
+
+// search for the nearest to the neutral fibre of the vertical fan point
+// on the segment between p_in and p_out
+G4double LArWheelSolid::search_for_nearest_point(
+	const G4ThreeVector &p_in, const G4double dist_p_in,
+	const G4ThreeVector &p_out, int p_fan
+) const
+{
+	LWSDBG(6, std::cout << "sfnp " << MSG_VECTOR(p_in) << " "
+	                    << MSG_VECTOR(p_out) << std::endl);
+
+	G4ThreeVector A, B, C, l, diff;
+	A = p_in;
+	B = p_out;
+	diff = B - A;
+	l = diff.unit() * s_IterationPrecision;
+  // this is to correctly take the sign of the distance into account
+	G4double sign = dist_p_in < 0.? -1. : 1.;
+	G4double d_prime;
+	G4double dist_c;
+	unsigned long niter = 0;
+	do {
+		C = A + B;
+		C *= 0.5;
+		dist_c = GetCalculator()->DistanceToTheNeutralFibre(C, p_fan);
+		if(dist_c * sign <= 0.){ // we are in coditions for in_iteration_process
+			LWSDBG(7, std::cout << "sfnp0 " << dist_c << std::endl);
+			return in_iteration_process(p_in, dist_p_in, C, p_fan);
+		}
+    // calculate sign of derivative of distance to the neutral fibre
+    // hope this substitution is acceptable
+		diff = C - l;
+		d_prime = (dist_c - GetCalculator()->DistanceToTheNeutralFibre(diff, p_fan)) * sign;
+		if(d_prime < 0.) A = C;
+		else B = C;
+		niter ++;
+		diff = A - B;
+	} while(diff.mag2() > s_IterationPrecision2 && niter < s_IterationsLimit);
+	assert(niter < s_IterationsLimit);
+	if(fabs(dist_c) < m_FHTminusT){
+		LWSDBG(7, std::cout << "sfnp1 " << dist_c << std::endl);
+		return in_iteration_process(p_in, dist_p_in, C, p_fan);
+	}
+  //  let's check at p_in and p_out
+	if(dist_p_in * sign < dist_c * sign){
+		C = p_in;
+		dist_c = dist_p_in;
+	}
+	G4double dist_p_out = GetCalculator()->DistanceToTheNeutralFibre(p_out, p_fan);
+	if(dist_p_out *sign < dist_c * sign) C = p_out;
+	if(fabs(dist_p_out) < m_FHTminusT){
+		LWSDBG(7, std::cout << "sfnp2 " << dist_p_out << std::endl);
+		return in_iteration_process(p_in, dist_p_in, C, p_fan);
+	}
+	return kInfinity;
+}
+
+G4double LArWheelSolid::distance_to_in(G4ThreeVector &p, const G4ThreeVector &v, int p_fan) const
+{
+	LWSDBG(4, std::cout << "dti:           " << MSG_VECTOR(p) << " "
+	                    << MSG_VECTOR(v) << std::endl);
+
+	G4double distance = 0.;
+#ifdef LARWHEELSOLID_USE_FANBOUND
+	if(FanBound->Inside(p) == kOutside) {
+		const G4double d = FanBound->DistanceToIn(p, v);
+		p += v * d;
+		distance += d;
+	}
+#else
+	if(p.x() > m_fs->xmax) {
+		if(v.x() >= 0.) return kInfinity;
+		const G4double b = (m_fs->xmax - p.x()) / v.x();
+		const G4double y2 = p.y() + v.y() * b;
+		const G4double z2 = p.z() + v.z() * b;
+		p.set(m_fs->xmax, y2, z2);
+		distance += b;
+	} else if(p.x() < m_fs->xmin) {
+		if(v.x() <= 0.)	return kInfinity;
+		const G4double b = (m_fs->xmin - p.x()) / v.x();
+		const G4double y2 = p.y() + v.y() * b;
+		const G4double z2 = p.z() + v.z() * b;
+		p.set(m_fs->xmin, y2, z2);
+		distance += b;
+	}
+#endif
+
+// here p is on surface of or inside the "FanBound",
+// distance corrected, misses are accounted for
+	LWSDBG(5, std::cout << "dti corrected: " << MSG_VECTOR(p) << std::endl);
+
+	G4double dist_p = GetCalculator()->DistanceToTheNeutralFibre(p, p_fan);
+	if(fabs(dist_p) < m_FHTminusT) {
+		LWSDBG(5, std::cout << "hit fan dist_p=" << dist_p << ", m_FHTminusT=" << m_FHTminusT << std::endl);
+		return distance;
+	}
+
+#ifdef CHECK_DIRTONORM_ANGLE_ON_SURFACE
+	if(fabs(dist_p) > m_FHTplusT) {
+		LWSDBG(5, std::cout << "outside fan dist_p=" << dist_p << ", m_FHTplusT=" << m_FHTplusT << std::endl);
+	} else {
+		LWSDBG(5, std::cout << "on fan surface dist_p=" << dist_p << ", m_FHTplusT=" << m_FHTplusT << ", m_FHTminusT=" << m_FHTminusT << std::endl);
+
+		const G4ThreeVector d = GetCalculator()->NearestPointOnNeutralFibre(p, p_fan);
+		// check dot product between normal and v
+		if ( (p-d).cosTheta(v) < -AngularTolerance ) {
+			// direction "v" definitely pointing inside
+			// return 0.0, it should be in "distance"
+			return distance;
+		}
+	}
+#endif
+
+	G4ThreeVector q;
+#ifdef LARWHEELSOLID_USE_FANBOUND
+	q = p + v * FanBound->DistanceToOut(p, v);
+#else
+	find_exit_point(p, v, q);
+#endif
+
+	G4int start = select_section(p.z());
+	G4int stop = select_section(q.z());
+	G4int step = -1;
+	if(stop > start) { step = 1;	start ++; stop ++; }
+	LWSDBG(5, std::cout << "dti sections " << start << " " << stop
+	                    << " " << step << std::endl);
+	G4ThreeVector p1;
+	for(G4int i = start; i != stop; i += step){
+// v.z() can't be 0, otherwise start == stop, so the exit point could be only
+// at z border of the fan section
+		const G4double d1 = (m_Zsect[i] - p.z()) / v.z();
+		const G4double x1 = p.x() + v.x() * d1, y1 = p.y() + v.y() * d1;
+		p1.set(x1, y1, m_Zsect[i]);
+		G4double dist_p1 = GetCalculator()->DistanceToTheNeutralFibre(p1, p_fan);
+		LWSDBG(5, std::cout << i << ">" << p << " " << dist_p << " "
+		                    << p1 << " " << dist_p1 << std::endl);
+		G4double dd = kInfinity;
+		if(dist_p * dist_p1 < 0.){// it certanly cross current half-wave
+			dd = in_iteration_process(p, dist_p, p1, p_fan);
+		}
+		G4double d2 = search_for_nearest_point(p, dist_p, p1, p_fan);
+		LWSDBG(6, std::cout << i << "> dd=" << dd << ", d2=" << d2 << ", distance=" << distance << std::endl);
+		if(d2 < kInfinity){
+			return distance + d2; // this half-wave is intersected
+		} else if(dd < kInfinity){
+			return distance + dd;
+		}
+		distance += d1;
+		p.set(x1, y1, m_Zsect[i]);
+		dist_p = dist_p1;
+	}
+
+	G4double dist_q = GetCalculator()->DistanceToTheNeutralFibre(q, p_fan);
+	LWSDBG(5, std::cout << "dti exit point: " << MSG_VECTOR(q) << " "
+	                    << dist_q << std::endl);
+	G4double dd = kInfinity;
+	if(dist_p * dist_q < 0.){// it certanly cross current half-wave
+		dd = in_iteration_process(p, dist_p, q, p_fan);
+	}
+	G4double d2 = search_for_nearest_point(p, dist_p, q, p_fan);
+	if(d2 < kInfinity){
+		return distance + d2; // this half-wave is intersected
+	} else if(dd < kInfinity){
+		return distance + dd;
+	}
+	return kInfinity;
+}
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSolidDisToOut.cxx b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSolidDisToOut.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..847a7c03f7213de8e607efd94e39248e36fa2320
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSolidDisToOut.cxx
@@ -0,0 +1,296 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include <cassert>
+
+#include "GeoSpecialShapes/LArWheelCalculator.h"
+#include "LArWheelSolid.h"
+#include "LArFanSection.h"
+
+G4double LArWheelSolid::DistanceToOut(const G4ThreeVector &inputP) const
+{
+	LWSDBG(1, std::cout << TypeStr() << " DisToOut" << MSG_VECTOR(inputP) << std::endl);
+	if(m_BoundingShape->Inside(inputP) != kInside){
+		LWSDBG(2, std::cout << "DistanceToOut(p):"
+                            << " point " << MSG_VECTOR(inputP)
+                            << " is not inside of the m_BoundingShape."
+                            << std::endl);
+		return 0.;
+	}
+	G4ThreeVector p( inputP );
+	int p_fan = 0;
+	const G4double d = m_FanHalfThickness - fabs(GetCalculator()->DistanceToTheNearestFan(p, p_fan));
+	if(d < s_Tolerance){
+		LWSDBG(2, std::cout << "already not inside " << MSG_VECTOR(p) << std::endl);
+		return 0.;
+	}
+	const G4double d0 = m_BoundingShape->DistanceToOut(inputP);
+	LWSDBG(2, std::cout << "dto " << d << " " << d0 << std::endl);
+	if(d > d0) return d0;
+	else return d;
+}
+
+G4double LArWheelSolid::DistanceToOut(const G4ThreeVector &inputP,
+                                      const G4ThreeVector &inputV,
+                                      const G4bool calcNorm,
+                                      G4bool* validNorm,
+                                      G4ThreeVector* sn) const
+{
+	LWSDBG(1, std::cout << TypeStr() << " DisToOut" << MSG_VECTOR(inputP)
+	                    << MSG_VECTOR(inputV) << std::endl);
+
+	const EInside inside_BS = m_BoundingShape->Inside(inputP);
+	if(inside_BS == kOutside){
+		LWSDBG(2, std::cout << "DistanceToOut(p):"
+                            << " point " << MSG_VECTOR(inputP)
+                            << " is outside of m_BoundingShape." << std::endl);
+		if(calcNorm) *validNorm = false;
+		return 0.;
+	}
+
+	// If here inside or on surface of BS
+	G4ThreeVector p(inputP);
+	int p_fan = 0;
+	const G4double adtnf_p = fabs(GetCalculator()->DistanceToTheNearestFan(p, p_fan));
+	if(adtnf_p >= m_FHTplusT) {
+		LWSDBG(2, std::cout << "DistanceToOut(p, v): point "
+	                        << MSG_VECTOR(inputP)
+	                        << " is outside of solid." << std::endl);
+		if(calcNorm) *validNorm = false;
+		return 0.;
+	}
+
+	G4ThreeVector v(inputV);
+	const G4double phi0 = p.phi() - inputP.phi();
+	v.rotateZ(phi0);
+
+#ifdef CHECK_DIRTONORM_ANGLE_ON_SURFACE
+	if(adtnf_p < FHTminusT) {
+		LWSDBG(5, std::cout << "inside fan point " << MSG_VECTOR(inputP) << ", FHTminusT=" << FHTminusT << std::endl);
+	} else {
+		LWSDBG(5, std::cout << "on fan surface adtnf_p=" << adtnf_p << ", m_FHTplusT=" << m_FHTplusT << ", FHTminusT=" << FHTminusT << std::endl);
+
+		const G4ThreeVector d = GetCalculator()->NearestPointOnNeutralFibre(p, p_fan);
+		// check dot product between normal and v
+		if ( (p-d).cosTheta(v) > AngularTolerance ) {
+			// direction "v" definitely pointing outside
+			// return 0.0
+			return 0.;
+		}
+	}
+#endif
+
+// former distance_to_out starts here
+	LWSDBG(4, std::cout << "dto: " << MSG_VECTOR(p) << " "
+	                    << MSG_VECTOR(v) << std::endl);
+
+	G4ThreeVector q(p);
+#ifdef LARWHEELSOLID_USE_BS_DTO
+	const G4double dto_bs = m_BoundingShape->DistanceToOut(
+		inputP, inputV, calcNorm, validNorm, sn
+	);
+	q = p + v * dto_bs;
+	if(q.y() < m_Ymin){
+		LWSDBG(5, std::cout << "dto exit point too low " << MSG_VECTOR(q) << std::endl);
+		const G4double dy = (m_Ymin - p.y()) / v.y();
+		q.setX(p.x() + v.x() * dy);
+		q.setY(m_Ymin);
+		q.setZ(p.z() + v.z() * dy);
+	}
+#else
+	FanBoundExit_t exit = find_exit_point(p, v, q);
+	LWSDBG(5, std::cout << "dto exit " << exit << std::endl);
+#endif
+	LWSDBG(5, std::cout << "dto exit point " << MSG_VECTOR(q) << std::endl);
+
+	G4double distance = 0.;
+	G4int start = select_section(p.z());
+	G4int stop = select_section(q.z());
+	G4int step = -1;
+	if(stop > start){ step = 1;	start ++; stop ++; }
+	LWSDBG(5, std::cout << "dto sections " << start << " " << stop << " " << step << std::endl);
+
+	G4double tmp;
+	G4ThreeVector p1, C;
+
+	for(G4int i = start; i != stop; i += step){
+		const G4double d1 = (m_Zsect[i] - p.z()) / v.z();
+// v.z() can't be 0, otherwise start == stop, so the exit point could be only
+// at z border of the fan section
+		LWSDBG(5, std::cout << "at " << i << " dist to zsec = " << d1 << std::endl);
+		const G4double x1 = p.x() + v.x() * d1, y1 = p.y() + v.y() * d1;
+		p1.set(x1, y1, m_Zsect[i]);
+		const G4double dd = fabs(GetCalculator()->DistanceToTheNeutralFibre(p1, p_fan));
+		if(dd > m_FHTplusT){
+			tmp = out_iteration_process(p, p1, p_fan);
+			//while(search_for_most_remoted_point(p, out_section, C)){
+			if(search_for_most_remoted_point(p, p1, C, p_fan)){
+				tmp = out_iteration_process(p, C, p_fan);
+			}
+			distance += tmp;
+#ifndef LARWHEELSOLID_USE_BS_DTO
+			exit = NoCross;
+#endif
+			goto end_dto;
+		}
+		if(search_for_most_remoted_point(p, p1, C, p_fan)){
+			distance += out_iteration_process(p, C, p_fan);
+#ifndef LARWHEELSOLID_USE_BS_DTO
+			exit = NoCross;
+#endif
+			goto end_dto;
+		}
+		distance += d1;
+		p.set(x1, y1, m_Zsect[i]);
+	}
+
+	if(fabs(GetCalculator()->DistanceToTheNeutralFibre(q, p_fan)) > m_FHTplusT){
+		LWSDBG(5, std::cout << "q=" << MSG_VECTOR(q) << " outside fan cur distance=" << distance << ", m_FHTplusT=" << m_FHTplusT << std::endl);
+		tmp = out_iteration_process(p, q, p_fan);
+#ifndef LARWHEELSOLID_USE_BS_DTO
+		exit = NoCross;
+#endif
+    } else {
+		tmp = (q - p).mag();
+	}
+	//while(search_for_most_remoted_point(out, out1, C, p_fan)){
+	if(search_for_most_remoted_point(p, q, C, p_fan)){
+		tmp = out_iteration_process(p, C, p_fan);
+#ifndef LARWHEELSOLID_USE_BS_DTO
+		exit = NoCross;
+#endif
+	}
+	distance += tmp;
+// former distance_to_out ends here
+  end_dto:
+	LWSDBG(5, std::cout << "At end_dto distance=" << distance  << std::endl);
+#ifdef LARWHEELSOLID_USE_BS_DTO
+	if(calcNorm && distance < dto_bs) *validNorm = false;
+#else
+	if(calcNorm){
+		LWSDBG(5, std::cout << "dto calc norm " << exit << std::endl);
+		switch(exit){
+			case ExitAtBack:
+				sn->set(0., 0., 1.);
+				*validNorm = true;
+				break;
+			case ExitAtFront:
+				sn->set(0., 0., -1.);
+				*validNorm = true;
+				break;
+			case ExitAtOuter:
+				q.rotateZ(-phi0);
+				sn->set(q.x(), q.y(), 0.);
+				if(q.z() <= m_Zmid) sn->setZ(- q.perp() * m_fs->Amax);
+				sn->setMag(1.0);
+				*validNorm = true;
+				break;
+			default:
+				*validNorm = false;
+				break;
+		}
+	}
+#endif
+
+#ifdef DEBUG_LARWHEELSOLID
+	if(Verbose > 2){
+		std::cout << "DTO: " << distance << " ";
+		if (*validNorm) {
+		  std::cout << *validNorm << " " << MSG_VECTOR((*sn));
+		} else {
+		  std::cout << "Norm is not valid";
+		}
+		std::cout << std::endl;
+		if(Verbose > 3){
+			G4ThreeVector p2 = inputP + inputV * distance;
+			EInside i = Inside(p2);
+			std::cout << "DTO hit at " << MSG_VECTOR(p2) << ", "
+			          << inside(i) << std::endl;
+		}
+	}
+#ifdef LWS_HARD_TEST_DTO
+	if(test_dto(inputP, inputV, distance)){
+		if(Verbose == 1){
+			std::cout << TypeStr() << " DisToOut" << MSG_VECTOR(inputP)
+	                  << MSG_VECTOR(inputV) << std::endl;
+		}
+	}
+#endif
+#endif
+	return distance;
+}
+
+// This functions should be called in the case when we are sure that
+// point p is NOT OUTSIDE of vertical fan and point out is NOT INSIDE vertical fan
+// returns distance from point p to fan surface, sets last
+// parameter to the found point
+// may give wrong answer - see description
+G4double LArWheelSolid::out_iteration_process(const G4ThreeVector &p,
+                                              G4ThreeVector &B, const int p_fan) const
+{
+	LWSDBG(6, std::cout << "oip: " << p << " " << B);
+	assert(fabs(GetCalculator()->DistanceToTheNeutralFibre(p, p_fan)) < m_FHTplusT);
+	assert(fabs(GetCalculator()->DistanceToTheNeutralFibre(B, p_fan)) > m_FHTminusT);
+	G4ThreeVector A(p), C, diff;
+	unsigned int niter = 0;
+	do {
+		C = A + B;
+		C *= 0.5;
+		if(fabs(GetCalculator()->DistanceToTheNeutralFibre(C, p_fan)) < m_FHTplusT){
+			A = C;
+		} else {
+			B = C;
+		}
+		niter ++;
+		diff = A - B;
+	} while(diff.mag2() > s_IterationPrecision2 && niter < s_IterationsLimit);
+	assert(fabs(GetCalculator()->DistanceToTheNeutralFibre(B, p_fan)) > m_FHTplusT);
+	assert(niter < s_IterationsLimit);
+	diff = p - B;
+	LWSDBG(7, std::cout << " -> " << B << " " << diff.mag());
+	LWSDBG(6, std::cout << std::endl);
+	return diff.mag();
+}
+
+// returns true if the point being outside vert. fan is found, also set C to
+// that point in this case
+// returns false if the whole track looks like being inside vert. fan
+G4bool LArWheelSolid::search_for_most_remoted_point(
+	const G4ThreeVector &a, const G4ThreeVector &b, G4ThreeVector &C, const int p_fan
+) const
+{
+	LWSDBG(6, std::cout << "sfmrp " << a << " " << b << std::endl);
+	G4ThreeVector diff(b - a);
+
+	if(diff.mag2() <= s_IterationPrecision2) return false;
+	G4ThreeVector A(a), B(b), l(diff.unit() * s_IterationPrecision);
+  // find the most remoted point on the line AB
+  // and check if it is outside vertical fan
+  // small vector along the segment AB
+	G4double d1, d2;
+	unsigned int niter = 0;
+  // searching for maximum of (GetCalculator()->DistanceToTheNeutralFibre)^2 along AB
+	do {
+		C = A + B;
+		C *= 0.5;
+		d1 = GetCalculator()->DistanceToTheNeutralFibre(C, p_fan);
+		if(fabs(d1) > m_FHTplusT){
+      // here out_iteration_process gives the answer
+			LWSDBG(7, std::cout << "sfmrp -> " << C << " " << fabs(d1)
+                                << "  " << (C - a).unit() << " "
+                                << (C - a).mag() << std::endl);
+			return true;
+		}
+    // sign of derivative
+    //d1 = GetCalculator()->DistanceToTheNeutralFibre(C + l);
+		d2 = GetCalculator()->DistanceToTheNeutralFibre(C - l, p_fan);
+		if(d1 * d1 - d2 * d2 > 0.) A = C;
+		else B = C;
+		niter ++;
+		diff = A - B;
+	} while(diff.mag2() > s_IterationPrecision2 && niter < s_IterationsLimit);
+  // the line entirely lies inside fan
+	assert(niter < s_IterationsLimit);
+	return false;
+}
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSolidInit.cxx b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSolidInit.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..3055281847d5e30fa53a74391e781c21a4b0fdda
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSolidInit.cxx
@@ -0,0 +1,403 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include <cassert>
+#include <stdexcept>
+#include <iostream>
+
+
+#include "GeoSpecialShapes/EMECData.h"
+
+#include "CLHEP/Units/PhysicalConstants.h"
+#include "G4GeometryTolerance.hh"
+#include "G4Polycone.hh"
+
+#include "GeoSpecialShapes/LArWheelCalculator.h"
+#include "LArWheelSolid.h"
+#include "LArFanSection.h"
+#include "G4ShiftedCone.h"
+
+#ifndef PORTABLE_LAR_SHAPE
+#include "AthenaBaseComps/AthMsgStreamMacros.h"
+#endif
+
+#ifdef DEBUG_LARWHEELSOLID
+G4int LArWheelSolid::Verbose = 0;
+#endif
+
+// these are internal technical constants, should not go in DB
+const unsigned int LArWheelSolid::s_IterationsLimit = 50; // That's enough even for 10e-15 IterationPrecision
+const G4double LArWheelSolid::s_Tolerance = G4GeometryTolerance::GetInstance()->GetSurfaceTolerance() / 2;
+const G4double LArWheelSolid::s_AngularTolerance = G4GeometryTolerance::GetInstance()->GetAngularTolerance() / 2;
+const G4double LArWheelSolid::s_IterationPrecision = 0.001*CLHEP::mm;
+const G4double LArWheelSolid::s_IterationPrecision2 = s_IterationPrecision * s_IterationPrecision;
+
+LArWheelSolid::LArWheelSolid(const G4String& name, LArWheelSolid_t type,
+                             G4int zside,
+                             LArWheelCalculator *calc,
+			     const EMECData *emecData
+                             )
+  : G4VSolid(name), m_Type(type), m_Calculator(calc), m_PhiPosition(CLHEP::halfpi), m_fs(0)
+#ifndef PORTABLE_LAR_SHAPE
+  , m_msg("LArWheelSolid")
+#endif
+{
+#ifndef PORTABLE_LAR_SHAPE
+#ifdef LARWHEELSOLID_USE_FANBOUND
+  ATH_MSG_INFO ( "compiled with G4 FanBound" );
+#else
+  ATH_MSG_INFO ( "compiled with private find_exit_point" );
+#endif
+#endif
+
+  LArG4::LArWheelCalculator_t calc_type = LArG4::LArWheelCalculator_t(0);
+  switch(m_Type){
+  case InnerAbsorberWheel:
+    calc_type = LArG4::InnerAbsorberWheel;
+    break;
+  case OuterAbsorberWheel:
+    calc_type = LArG4::OuterAbsorberWheel;
+    break;
+  case InnerElectrodWheel:
+    calc_type = LArG4::InnerElectrodWheel;
+    break;
+  case OuterElectrodWheel:
+    calc_type = LArG4::OuterElectrodWheel;
+    break;
+  case InnerAbsorberModule:
+    calc_type = LArG4::InnerAbsorberModule;
+    break;
+  case OuterAbsorberModule:
+    calc_type = LArG4::OuterAbsorberModule;
+    break;
+  case InnerElectrodModule:
+    calc_type = LArG4::InnerElectrodModule;
+    break;
+  case OuterElectrodModule:
+    calc_type = LArG4::OuterElectrodModule;
+    break;
+  case InnerGlueWheel:
+    calc_type = LArG4::InnerGlueWheel;
+    break;
+  case OuterGlueWheel:
+    calc_type = LArG4::OuterGlueWheel;
+    break;
+  case InnerLeadWheel:
+    calc_type = LArG4::InnerLeadWheel;
+    break;
+  case OuterLeadWheel:
+    calc_type = LArG4::OuterLeadWheel;
+    break;
+  case InnerAbsorberCone:
+    calc_type = LArG4::InnerAbsorberWheel;
+    break;
+  case InnerElectrodCone:
+    calc_type = LArG4::InnerElectrodWheel;
+    break;
+  case InnerGlueCone:
+    calc_type = LArG4::InnerGlueWheel;
+    break;
+  case InnerLeadCone:
+    calc_type = LArG4::InnerLeadWheel;
+    break;
+  case OuterAbsorberFrontCone:
+    calc_type = LArG4::OuterAbsorberWheel;
+    break;
+  case OuterElectrodFrontCone:
+    calc_type = LArG4::OuterElectrodWheel;
+    break;
+  case OuterGlueFrontCone:
+    calc_type = LArG4::OuterGlueWheel;
+    break;
+  case OuterLeadFrontCone:
+    calc_type = LArG4::OuterLeadWheel;
+    break;
+  case OuterAbsorberBackCone:
+    calc_type = LArG4::OuterAbsorberWheel;
+    break;
+  case OuterElectrodBackCone:
+    calc_type = LArG4::OuterElectrodWheel;
+    break;
+  case OuterGlueBackCone:
+    calc_type = LArG4::OuterGlueWheel;
+    break;
+  case OuterLeadBackCone:
+    calc_type = LArG4::OuterLeadWheel;
+    break;
+  default:
+    G4Exception("LArWheelSolid", "UnknownSolidType", FatalException,
+                "Constructor: unknown LArWheelSolid_t");
+  }
+
+  if(m_Calculator == 0) m_Calculator = new LArWheelCalculator(*emecData,calc_type, zside);
+
+  const G4String bs_name = name + "-Bounding";
+#ifdef DEBUG_LARWHEELSOLID
+  const char *venv = getenv("LARWHEELSOLID_VERBOSE");
+  if(venv) Verbose = atoi(venv);
+  std::cout << "The LArWheelSolid build " << __DATE__ << " " << __TIME__
+            << std::endl;
+  std::cout << "LArWheelSolid verbosity level is " << Verbose << std::endl;
+#endif
+
+  // Initialize code that depends on wheel type:
+  m_FanHalfThickness = GetCalculator()->GetFanHalfThickness();
+  m_FHTplusT = m_FanHalfThickness + s_Tolerance;
+  m_FHTminusT = m_FanHalfThickness - s_Tolerance;
+  switch(m_Type){
+  case InnerAbsorberWheel:
+  case InnerElectrodWheel:
+  case InnerAbsorberModule:
+  case InnerElectrodModule:
+  case InnerGlueWheel:
+  case InnerLeadWheel:
+  case InnerAbsorberCone:
+  case InnerElectrodCone:
+  case InnerGlueCone:
+  case InnerLeadCone:
+    inner_solid_init(bs_name);
+    break;
+  case OuterAbsorberWheel:
+  case OuterElectrodWheel:
+  case OuterAbsorberModule:
+  case OuterElectrodModule:
+  case OuterGlueWheel:
+  case OuterLeadWheel:
+  case OuterAbsorberFrontCone:
+  case OuterElectrodFrontCone:
+  case OuterGlueFrontCone:
+  case OuterLeadFrontCone:
+  case OuterAbsorberBackCone:
+  case OuterElectrodBackCone:
+  case OuterGlueBackCone:
+  case OuterLeadBackCone:
+    outer_solid_init(bs_name);
+    break;
+  default:
+    G4Exception("LArWheelSolid", "UnknownSolidType", FatalException,
+                "Constructor: unknown LArWheelSolid_t");
+  }
+
+  m_Zsect_start_search = (m_Zsect.size() - 1) - 1;
+#ifndef PORTABLE_LAR_SHAPE
+  init_tests();
+  test(); // activated by env. variable
+  clean_tests();
+#endif
+  
+#ifdef DEBUG_LARWHEELSOLID
+  m_fs->print();
+  std::cout << "Limits: (" << m_Zsect.size() << ")" << std::endl;
+  for(size_t i = 0; i < m_Zsect.size(); ++ i){
+    std::cout << i << " " << m_Zsect[i] << std::endl;
+  }
+#endif
+#ifndef PORTABLE_LAR_SHAPE
+  ATH_MSG_DEBUG ( "solid of type "
+                  << LArWheelCalculator::LArWheelCalculatorTypeString(calc_type)
+                  << " initialized" );
+#endif
+}
+
+LArWheelSolid::~LArWheelSolid()
+{
+  if(m_fs) delete m_fs;
+}
+
+// initialization of inner Absorber or Electrod wheels
+void LArWheelSolid::inner_solid_init(const G4String &bs_name)
+{
+    m_IsOuter = false;
+    m_FanPhiAmplitude = 0.065; // internal technical constant, should not go in DB
+    set_phi_size();
+
+    G4double zPlane[2], rInner[2], rOuter[2];
+    zPlane[0] = 0.;
+    zPlane[1] = GetCalculator()->GetWheelThickness();
+    G4double wheel_thickness = zPlane[1] - zPlane[0];
+    GetCalculator()->GetWheelInnerRadius(rInner);
+    GetCalculator()->GetWheelOuterRadius(rOuter);
+    const G4double phi_min = m_PhiPosition - m_FanPhiAmplitude
+                           - GetCalculator()->GetFanStepOnPhi() * 2;
+
+    m_Zmin = zPlane[0]; m_Zmax = zPlane[1];
+    m_Rmin = rInner[0]; m_Rmax = rOuter[1];
+    m_Ymin = m_Rmin * 0.9;
+    m_Zmid = zPlane[1];
+    m_Ymid = (rInner[0] + rOuter[1]) * 0.5;
+
+    if(m_Type == InnerAbsorberCone
+    || m_Type == InnerElectrodCone
+    || m_Type == InnerGlueCone
+    || m_Type == InnerLeadCone
+    ){
+        m_BoundingShape = new G4ShiftedCone(
+            bs_name + "Cone", zPlane[0], zPlane[1],
+            rInner[0], rOuter[0], rInner[1], rOuter[1]
+        );
+    } else {
+        m_BoundingShape = new G4Polycone(
+            bs_name + "Polycone", m_MinPhi, m_MaxPhi - m_MinPhi,
+            2, zPlane, rInner, rOuter
+        );
+    }
+#ifdef LARWHEELSOLID_USE_FANBOUND
+    const G4double phi_size = (m_FanPhiAmplitude + GetCalculator()->GetFanStepOnPhi() * 2) * 2;
+    FanBound = new G4Polycone(bs_name + "ofFan", phi_min, phi_size,
+                              2, zPlane, rInner, rOuter);
+#endif
+#ifndef PORTABLE_LAR_SHAPE
+    ATH_MSG_INFO(m_BoundingShape->GetName() + " is the m_BoundingShape");
+#endif
+    
+    const G4double half_wave_length = GetCalculator()->GetHalfWaveLength();
+    const G4double sss = GetCalculator()->GetStraightStartSection();
+    m_Zsect.push_back(0.);
+    m_Zsect.push_back(sss + half_wave_length * 0.25);
+    const G4int num_fs = GetCalculator()->GetNumberOfHalfWaves() + 1;
+    for(G4int i = 2; i < num_fs; i ++){
+          const G4double zi = half_wave_length * (i - 1) + sss;
+#if LARWHEELSOLID_ZSECT_MULT > 1
+          for(G4int j = LARWHEELSOLID_ZSECT_MULT - 1; j > 0; -- j){
+            m_Zsect.push_back(zi - half_wave_length * j / LARWHEELSOLID_ZSECT_MULT);
+          }
+#endif
+          m_Zsect.push_back(zi);
+    }
+    m_Zsect.push_back(wheel_thickness - m_Zsect[1]);
+    m_Zsect.push_back(wheel_thickness - m_Zsect[0]);
+
+    m_fs = new LArFanSections(
+        rInner[0], rInner[1], rOuter[0], rOuter[1],
+        m_Rmax*cos(phi_min), m_Zsect.front(), m_Zsect.back()
+    );
+}
+
+// initialization of outer Absorber or Electrod wheels
+void LArWheelSolid::outer_solid_init(const G4String &bs_name)
+{
+    m_IsOuter = true;
+    m_FanPhiAmplitude = 0.02; // internal technical constant, should not go in DB
+    set_phi_size();
+
+    G4double zPlane[3], rInner[3], rOuter[3];
+    zPlane[0] = 0.;
+    zPlane[2] = GetCalculator()->GetWheelThickness();
+    G4double wheel_thickness = zPlane[2] - zPlane[0];
+    zPlane[1] = GetCalculator()->GetWheelInnerRadius(rInner);
+    GetCalculator()->GetWheelOuterRadius(rOuter);
+    const G4double phi_min =
+        m_PhiPosition - m_FanPhiAmplitude -
+        GetCalculator()->GetFanStepOnPhi() * 2;
+
+    m_Zmid = zPlane[1];
+    m_Ymid = (rInner[0] + rOuter[2]) * 0.5;
+
+    bool hasFrontSections = false;
+    bool hasBackSections = false;
+    if(m_Type == OuterAbsorberFrontCone
+    || m_Type == OuterElectrodFrontCone
+    || m_Type == OuterGlueFrontCone
+    || m_Type == OuterLeadFrontCone
+    ){
+        m_Zmin = zPlane[0]; m_Zmax = zPlane[1];
+        m_Rmin = rInner[0]; m_Rmax = rOuter[1];
+        m_BoundingShape = new G4ShiftedCone(
+            bs_name + "FrontCone", zPlane[0], zPlane[1],
+            rInner[0], rOuter[0], rInner[1], rOuter[1]
+        );
+        hasFrontSections = true;
+    } else if(m_Type == OuterAbsorberBackCone
+    || m_Type == OuterElectrodBackCone
+    || m_Type == OuterGlueBackCone
+    || m_Type == OuterLeadBackCone
+    ){
+        m_Zmin = zPlane[1]; m_Zmax = zPlane[2];
+        m_Rmin = rInner[1]; m_Rmax = rOuter[2];
+        m_BoundingShape = new G4ShiftedCone(
+            bs_name + "BackCone", zPlane[1], zPlane[2],
+            rInner[1], rOuter[1], rInner[2], rOuter[2]
+        );
+        hasBackSections = true;
+    } else {
+        m_Zmin = zPlane[0]; m_Zmax = zPlane[2];
+        m_Rmin = rInner[0]; m_Rmax = rOuter[2];
+        m_BoundingShape = new G4Polycone(
+            bs_name + "Polycone", m_MinPhi, m_MaxPhi - m_MinPhi,
+            3, zPlane, rInner, rOuter
+        );
+        hasFrontSections = true;
+        hasBackSections = true;
+    }
+
+    m_Ymin = m_Rmin * 0.9;
+
+#ifdef LARWHEELSOLID_USE_FANBOUND
+  const G4double phi_size = (m_FanPhiAmplitude + GetCalculator()->GetFanStepOnPhi() * 2) * 2;
+  FanBound = new G4Polycone(bs_name + "ofFan", phi_min, phi_size,
+                            3, zPlane, rInner, rOuter);
+#endif
+#ifndef PORTABLE_LAR_SHAPE
+    ATH_MSG_INFO(m_BoundingShape->GetName() + " is the m_BoundingShape");
+#endif
+    const G4double half_wave_length = GetCalculator()->GetHalfWaveLength();
+    const G4double sss = GetCalculator()->GetStraightStartSection();
+
+    if(hasFrontSections){
+        m_Zsect.push_back(0.);
+        m_Zsect.push_back(sss + half_wave_length * 0.25);
+    } else {
+        m_Zsect.push_back(m_Zmid);
+    }
+    const G4int num_fs = GetCalculator()->GetNumberOfHalfWaves() + 1;
+
+    for(G4int i = 2; i < num_fs; i ++){
+        const G4double zi = half_wave_length * (i - 1) + sss;
+#if LARWHEELSOLID_ZSECT_MULT > 1
+        for(G4int j = LARWHEELSOLID_ZSECT_MULT - 1; j > 0; -- j){
+            G4double zj = zi - half_wave_length * j / LARWHEELSOLID_ZSECT_MULT;
+            if(hasFrontSections && hasBackSections
+            && m_Zsect.back() < m_Zmid && zj >= m_Zmid){
+                m_Zsect.push_back(m_Zmid);
+            }
+            if((zj < m_Zmid && hasFrontSections)
+            || (zj > m_Zmid && hasBackSections)){
+                m_Zsect.push_back(zj);
+            }
+        }
+#endif
+        if(hasFrontSections && hasBackSections
+        && m_Zsect.back() < m_Zmid && zi >= m_Zmid){
+            m_Zsect.push_back(m_Zmid);
+        }
+        if((zi < m_Zmid && hasFrontSections)
+        || (zi > m_Zmid && hasBackSections)){
+            m_Zsect.push_back(zi);
+        }
+    }
+    if(hasBackSections){
+        m_Zsect.push_back(wheel_thickness - sss - half_wave_length * 0.25);
+        m_Zsect.push_back(wheel_thickness);
+    } else {
+        m_Zsect.push_back(m_Zmid);
+    }
+
+    m_fs = new LArFanSections(
+        rInner[0], rInner[1], rOuter[0], rOuter[1],
+        m_Rmax*cos(phi_min), m_Zsect.front(), m_Zmid
+    );
+}
+
+// it should be called after m_FanPhiAmplitude has been set
+// and before m_BoundingShape creation
+void LArWheelSolid::set_phi_size(void)
+{
+    if(GetCalculator()->GetisModule()){
+        m_MinPhi = m_PhiPosition - CLHEP::pi * (1. / 8.) - m_FanPhiAmplitude;
+        m_MaxPhi = m_PhiPosition + CLHEP::pi * (1. / 8.) + m_FanPhiAmplitude;
+    } else {
+        m_MinPhi = 0.;
+        m_MaxPhi = CLHEP::twopi;
+    }
+}
diff --git a/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSolid_type.h b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSolid_type.h
new file mode 100644
index 0000000000000000000000000000000000000000..c7b692841c551b1713bd4a313b5afcf24d82bb8d
--- /dev/null
+++ b/ATLAS-Extensions/LArCustomSolidExtension/import/Simulation/G4Utilities/Geo2G4/src/LArWheelSolid_type.h
@@ -0,0 +1,40 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// LArWheelSolid_type
+// Author: D. A. Maximov
+// typedefs for LArWheelSolid
+
+#ifndef __LArWheelSolid_type_HH__
+#define __LArWheelSolid_type_HH__
+
+typedef enum {
+  InnerAbsorberWheel,
+  OuterAbsorberWheel,
+  InnerElectrodWheel,
+  OuterElectrodWheel,
+  InnerAbsorberModule,
+  OuterAbsorberModule,
+  InnerElectrodModule,
+  OuterElectrodModule,
+  InnerGlueWheel,
+  OuterGlueWheel,
+  InnerLeadWheel,
+  OuterLeadWheel,
+  InnerAbsorberCone,
+  InnerElectrodCone,
+  InnerGlueCone,
+  InnerLeadCone,
+  OuterAbsorberFrontCone,
+  OuterElectrodFrontCone,
+  OuterGlueFrontCone,
+  OuterLeadFrontCone,
+  OuterAbsorberBackCone,
+  OuterElectrodBackCone,
+  OuterGlueBackCone,
+  OuterLeadBackCone,
+} LArWheelSolid_t;
+
+
+#endif // __LArWheelSolid_type_HH__
diff --git a/CI/Brewfile b/CI/Brewfile
index f2c87b601cdcb429b435f069ddfd4780d7b34f9f..3e753495074701637cfc0bd178df17de19e420f7 100644
--- a/CI/Brewfile
+++ b/CI/Brewfile
@@ -14,3 +14,4 @@ brew "nlohmann-json"
 brew "xerces-c"
 brew "eigen"
 brew "hdf5"
+brew "hepmc3"
diff --git a/CI/DockerfileBase b/CI/DockerfileBase
index 2627e7b237e832933440e8200f8fcd38e1683d68..64c15cbe021cd7910d5e2418815951b29b90a205 100755
--- a/CI/DockerfileBase
+++ b/CI/DockerfileBase
@@ -32,8 +32,11 @@ RUN apt-get update && apt-get install -y sqlite3 libsqlite3-dev
 ### Install eigen to avoid the own installation
 RUN apt-get update && apt-get install -y libeigen3-dev 
 
-### Install eigen to avoid the own installation
-RUN apt-get update && apt-get install -y libhdf5-dev 
+### Install hdf5 to avoid the own installation
+RUN apt-get update && apt-get install -y libhdf5-dev
+
+### Install hepmc3 to avoid the own installation
+RUN apt-get update && apt-get install -y libhepmc3-dev
 
 ### Another layer of building xer
 RUN wget https://cern.ch/lcgpackages/tarFiles/sources/xerces-c-${XercesC_VERSION}.tar.gz && \
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c685519ef51ef8d95f5f5055a6e42f8f707c5190..5e45b305379473cf2adcc23a380a85789fa807c7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -53,7 +53,10 @@ option(GEOMODEL_BUILD_VISUALIZATION "Enable the build of GeoModelVisualization"
 option(GEOMODEL_BUILD_GEOMODELG4 "Enable the build of GeoModelG4" OFF)
 option(GEOMODEL_BUILD_FULLSIMLIGHT "Enable the build of FullSimLight" OFF)
 option(GEOMODEL_BUILD_FULLSIMLIGHT_PROFILING "Enable FullSimLight profiling targets" OFF)
-option(GEOMODEL_BUILD_LARCUSTOMSOLIDEXTENSION "Build the LArCustomSolidExtension" OFF)
+#option(GEOMODEL_BUILD_LARCUSTOMSOLIDEXTENSION "Build the LArCustomSolidExtension" OFF)
+option(GEOMODEL_BUILD_ATLASEXTENSIONS "Build the Custom ATLAS Extensions" OFF)
+
+
 
 if(GEOMODEL_BUILD_FULLSIMLIGHT_PROFILING)
   set(GEOMODEL_BUILD_FULLSIMLIGHT ON CACHE BOOL "Enable the build of FullSimLight" FORCE)
@@ -98,11 +101,17 @@ if(GEOMODEL_BUILD_FULLSIMLIGHT)
   list( APPEND BUILT_PACKAGES "FSL")
 endif()
 
-if(GEOMODEL_BUILD_LARCUSTOMSOLIDEXTENSION)
-  add_subdirectory(LArCustomSolidExtension)
-  list( APPEND BUILT_PACKAGES "LArCustomSolidExtension")
+#if(GEOMODEL_BUILD_LARCUSTOMSOLIDEXTENSION)
+#  add_subdirectory(LArCustomSolidExtension)
+#  list( APPEND BUILT_PACKAGES "LArCustomSolidExtension")
+#endif()
+
+if(GEOMODEL_BUILD_ATLASEXTENSIONS)
+  add_subdirectory(ATLAS-Extensions)
+  list( APPEND BUILT_PACKAGES "ATLAS-Extensions")
 endif()
 
+
 if(GEOMODEL_BUILD_GEOMODELG4 OR GEOMODEL_BUILD_EXAMPLES_W_GEANT4)
   add_subdirectory(GeoModelG4)
   list( APPEND BUILT_PACKAGES "GeoModelG4")
diff --git a/FSL/src/BetterTextBrowser.h b/FSL/src/BetterTextBrowser.h
new file mode 100644
index 0000000000000000000000000000000000000000..56ee88ca8aaa8d21ca519f24275ebba52d26f69d
--- /dev/null
+++ b/FSL/src/BetterTextBrowser.h
@@ -0,0 +1,49 @@
+#ifndef _BETTERTEXTBROWSER_H_
+#define _BETTERTEXTBROWSER_H_
+#include <QTextBrowser>
+#include <QMenu>
+#include <QAction>
+#include <iostream>
+#include <QContextMenuEvent>
+#include <QIcon>
+#include <QStyle>
+#include "fsl_mainwindow.h"
+class BetterTextBrowser:public QTextBrowser {
+  Q_OBJECT
+
+
+ public:
+
+  // Constructor:
+  BetterTextBrowser(QWidget * parent):QTextBrowser(parent){}
+
+  // Destructor:
+  ~BetterTextBrowser() {}
+
+  // Set main window:
+  void setMainWindow(FSLMainWindow *mainWindow) {
+    m_mainWindow=mainWindow;
+  }
+  
+  // Override from base class. Add an action for "save text to file"
+  void contextMenuEvent(QContextMenuEvent *event) {
+
+    QMenu *menu = createStandardContextMenu();
+    QAction *action=menu->addAction(tr("Save to file"));
+
+    
+    action->setIcon((style()->standardIcon(QStyle::StandardPixmap(QStyle::SP_DialogSaveButton), nullptr, this)));
+    action->setEnabled(!document()->isEmpty());
+
+    action->setShortcut(QKeySequence(QKeySequence(Qt::CTRL + Qt::Key_S)));
+    connect(action, &QAction::triggered, m_mainWindow, &FSLMainWindow::save_display_output);
+    menu->exec(event->globalPos());
+    delete menu;
+  }
+
+  
+ private:
+  FSLMainWindow *m_mainWindow {nullptr};
+
+};
+#endif
diff --git a/FSL/src/fsl_mainwindow.cpp b/FSL/src/fsl_mainwindow.cpp
index cb463517100bfb44f5171ccb30ea7b0571fcc88e..bd422077bd28dba2ae11875555a5923903db1cd8 100644
--- a/FSL/src/fsl_mainwindow.cpp
+++ b/FSL/src/fsl_mainwindow.cpp
@@ -20,19 +20,24 @@ FSLMainWindow::FSLMainWindow(QWidget *parent)
     , ui(new Ui::FSLMainWindow)
 {
     ui->setupUi(this);
-    this->setWindowTitle("FullSimLight-GUI (beta version)");
+    this->setWindowTitle("FullSimLight-GUI");
     std::setlocale(LC_NUMERIC, "C");
 
-    //Setting up Models
-    sens_det_model = new QStringListModel(this);
-    g4ui_model = new QStringListModel(this);
-  //  shape_model = new QStringListModel(this);
-  //  ui->shape_view->setEditTriggers(QAbstractItemView::DoubleClicked);
-    ui->sens_det_view->setModel(sens_det_model);
-    ui->g4ui_view->setModel(g4ui_model);
-  //  ui->shape_view->setModel(shape_model);
-    ui->sens_det_view->setEditTriggers(QAbstractItemView::DoubleClicked);
-    ui->g4ui_view->setEditTriggers(QAbstractItemView::DoubleClicked);
+    ui->tB_view_config->setMainWindow(this);
+    
+    //Setting up g4ui Model
+    //g4ui_model = new QStringListModel(this);
+    //ui->g4ui_view->setModel(g4ui_model);
+    //ui->g4ui_view->setEditTriggers(QAbstractItemView::DoubleClicked);
+    
+    //Setting up Sensitive Detector Model
+    sens_det_model = new QStandardItemModel(this);
+    sens_det_horizontalHeader.append("Plugins List");
+    sens_det_model->setHorizontalHeaderLabels(sens_det_horizontalHeader);
+    ui->sens_det_table->setModel(sens_det_model);
+    ui->sens_det_table->horizontalHeader()->setStretchLastSection(true);
+    ui->sens_det_table->resizeRowsToContents();
+    ui->sens_det_table->resizeColumnsToContents();
 
 
     //Setting up the Regions Display
@@ -54,7 +59,7 @@ FSLMainWindow::FSLMainWindow(QWidget *parent)
     //Setting up the User Actions Display
     user_action_model = new QStandardItemModel(this);
    // user_action_horizontalHeader.append("Type of Action");
-    user_action_horizontalHeader.append("File");
+    user_action_horizontalHeader.append("Plugins List");
     user_action_model->setHorizontalHeaderLabels(user_action_horizontalHeader);
     ui->user_action_table->setModel(user_action_model);
     ui->user_action_table->horizontalHeader()->setStretchLastSection(true);
@@ -65,6 +70,8 @@ FSLMainWindow::FSLMainWindow(QWidget *parent)
     //Setting up Connections
     connect(ui->pB_geom, &QPushButton::released, this, &FSLMainWindow::assign_geom_file);
     connect(ui->actionSave, &QAction::triggered, this, &FSLMainWindow::save_configuration);
+    connect(ui->pB_save_config, &QPushButton::released, this, &FSLMainWindow::save_configuration);
+
     connect(ui->actionSave_as, &QAction::triggered, this, &FSLMainWindow::save_configuration_as);
     connect(ui->actionOpen, &QAction::triggered, this, &FSLMainWindow::load_configuration);
     connect(ui->pB_view, &QPushButton::released, this, &FSLMainWindow::view_configuration);
@@ -72,7 +79,10 @@ FSLMainWindow::FSLMainWindow(QWidget *parent)
     connect(ui->pB_gmex, &QPushButton::released, this, &FSLMainWindow::run_gmex);
     connect(ui->pB_gmclash, &QPushButton::released, this, &FSLMainWindow::run_gmclash);
     connect(ui->pB_main_clear, &QPushButton::released, this, &FSLMainWindow::clear_main_status);
+    connect(ui->pB_browse_phys_list, &QPushButton::released, this, &FSLMainWindow::assign_phys_list_plugin);
     connect(ui->pB_pythia_browse, &QPushButton::released, this, &FSLMainWindow::assign_pythia_file);
+    connect(ui->pB_hepmc3_browse_files, &QPushButton::released, this, &FSLMainWindow::assign_hepmc3_file);
+    connect(ui->pB_gen_plug_browse_files, &QPushButton::released, this, &FSLMainWindow::assign_gen_plug_file);
     connect(ui->pB_magnetic_field_plugin, &QPushButton::released, this, &FSLMainWindow::assign_magnetic_field_plugin_file);
     connect(ui->pB_magnetic_field_map, &QPushButton::released, this, &FSLMainWindow::assign_magnetic_field_map);
 
@@ -80,8 +90,8 @@ FSLMainWindow::FSLMainWindow(QWidget *parent)
     connect(ui->pB_del_sens_det, &QPushButton::released, this, &FSLMainWindow::del_sens_det);
     connect(ui->pB_add_region, &QPushButton::released, this, &FSLMainWindow::pop_up_regions);
     connect(ui->pB_del_region, &QPushButton::released, this, &FSLMainWindow::del_region);
-    connect(ui->pB_add_g4ui, &QPushButton::released, this, &FSLMainWindow::add_g4ui);
-    connect(ui->pB_del_g4ui, &QPushButton::released, this, &FSLMainWindow::del_g4ui);
+    //connect(ui->pB_add_g4ui, &QPushButton::released, this, &FSLMainWindow::add_g4ui);
+    //connect(ui->pB_del_g4ui, &QPushButton::released, this, &FSLMainWindow::del_g4ui);
    // connect(ui->pB_add_shape_ext_file, &QPushButton::released, this, &FSLMainWindow::add_shape_ext);
    // connect(ui->pB_del_shape_ext_file, &QPushButton::released, this, &FSLMainWindow::del_shape_ext);
 
@@ -99,6 +109,7 @@ FSLMainWindow::FSLMainWindow(QWidget *parent)
 
     //Setting widget properties
     ui->sB_NOE->setMaximum(10000);
+    ui->sB_NOT->setMinimum(1);
     ui->sB_NOT->setMaximum(std::thread::hardware_concurrency());
     ui->sB_control->setMaximum(5);
     ui->sB_run->setMaximum(5);
@@ -108,6 +119,9 @@ FSLMainWindow::FSLMainWindow(QWidget *parent)
     ui->cB_particle->setCurrentIndex(0);
     ui->pB_pythia_browse->setEnabled(false);
     ui->cB_pythia_type_of_eve->setEnabled(false);
+    ui->pB_hepmc3_browse_files->setEnabled(false);
+    ui->cB_hepmc3_type_of_eve->setEnabled(false);
+    ui->pB_gen_plug_browse_files->setEnabled(false);
     ui->pB_magnetic_field_map->setEnabled(false);
     ui->pB_magnetic_field_plugin->setEnabled(false);
     ui->cB_particle->setCurrentIndex(0);
@@ -120,8 +134,8 @@ FSLMainWindow::FSLMainWindow(QWidget *parent)
     ui->sB_NOT->setValue(std::thread::hardware_concurrency());
     ui->sB_NOE->setValue(10);
     number_of_primaries_per_event = 1;
-    ui->lE_hits->setText("HITS.root");
-    ui->lE_histo->setText("HISTO.root");
+   // ui->lE_hits->setText("HITS.root");
+   // ui->lE_histo->setText("HISTO.root");
     ui->tB_view_config->setCurrentFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
     ui->tB_view_config->setFontPointSize(13);
 
@@ -143,8 +157,8 @@ FSLMainWindow::FSLMainWindow(QWidget *parent)
     ui->lE_pz->setValidator(p_z_validator);
     ui->lE_fixed_MF->setValidator(mag_field_validator);
 
-    ui->lE_hits->setEnabled(false);
-    ui->lE_histo->setEnabled(false);
+ //   ui->lE_hits->setEnabled(false);
+ //   ui->lE_histo->setEnabled(false);
 
 
   //  ui->tab->setEnabled(false);//Shape tab (Change name on UI)
@@ -158,9 +172,9 @@ FSLMainWindow::FSLMainWindow(QWidget *parent)
     connect(ui->cB_magnetic_field, QOverload<int>::of(&QComboBox::currentIndexChanged), this ,&FSLMainWindow::configure_magnetic_field);
     connect(ui->cB_pythia_type_of_eve, QOverload<int>::of(&QComboBox::currentIndexChanged), this ,&FSLMainWindow::check_if_pythia_file);
 
-    connect(this, &FSLMainWindow::send_error_message, this, &FSLMainWindow::catch_error_message);
-    connect(ui->sens_det_view, SIGNAL(clicked(QModelIndex)), this, SLOT(get_sens_det_index(QModelIndex)));
-    connect(ui->g4ui_view, SIGNAL(clicked(QModelIndex)), this, SLOT(get_g4ui_index(QModelIndex)));
+    //connect(this, &FSLMainWindow::send_error_message, this, &FSLMainWindow::catch_error_message);
+  //  connect(ui->sens_det_view, SIGNAL(clicked(QModelIndex)), this, SLOT(get_sens_det_index(QModelIndex)));
+    //connect(ui->g4ui_view, SIGNAL(clicked(QModelIndex)), this, SLOT(get_g4ui_index(QModelIndex)));
   //  connect(ui->shape_view, SIGNAL(clicked(QModelIndex)), this, SLOT(get_shape_index(QModelIndex)));
     connect(region,&ConfigRegions::send_config,this,&FSLMainWindow::add_region);
     connect(&fullSimLight_process,SIGNAL(readyReadStandardOutput()),this,SLOT(fsmlreadyReadStandardOutput()));
@@ -179,7 +193,7 @@ FSLMainWindow::~FSLMainWindow()
 {
     delete ui;
     delete sens_det_model;
-    delete g4ui_model;
+    //delete g4ui_model;
     delete region;
     delete region_model;
     delete user_action_model;
@@ -193,26 +207,27 @@ FSLMainWindow::~FSLMainWindow()
 
 
 //Custom Signal to append to status bars
-void FSLMainWindow::catch_error_message(std::string info)
+/*void FSLMainWindow::catch_error_message(std::string info)
 {
     std::cout << info << std::endl;
-}
+}*/
 
 //Get index of the row in the sensitive detector extensions display when clicked
-void FSLMainWindow::get_sens_det_index(QModelIndex sens_det_index)
+/*void FSLMainWindow::get_sens_det_index(QModelIndex sens_det_index)
 {
     sens_det_number = sens_det_index.row();
-}
+}*/
 
 //Add the Sensitive detector file
 void FSLMainWindow::add_sens_det()
 {
-    std::string sens_det_file = this->get_file_name();
-    if(sens_det_file.find(".dylib") != std::string::npos || sens_det_file.find(".so") != std::string::npos)
-    {QString q_sens_det_file = QString::fromUtf8(sens_det_file.c_str());
-    sens_det_model->insertRow(sens_det_model->rowCount());
-    QModelIndex sens_det_index = sens_det_model->index(sens_det_model->rowCount()-1);
-    sens_det_model->setData(sens_det_index, q_sens_det_file);
+    QString q_sens_det_file_name =  QString::fromUtf8((this->get_file_name()).c_str());
+
+    if(q_sens_det_file_name!="")
+    {
+    int rows = ui->sens_det_table->model()->rowCount();
+    ui->sens_det_table->model()->insertRows(rows,1);
+    ui->sens_det_table->model()->setData(ui->sens_det_table->model()->index(rows,0),q_sens_det_file_name);
     }
 }
 
@@ -220,7 +235,11 @@ void FSLMainWindow::add_sens_det()
 //Delete the sensitive_detector_ext
 void FSLMainWindow::del_sens_det()
 {
-    sens_det_model->removeRow(sens_det_number);
+    QModelIndexList sens_det_indexes =  ui->sens_det_table->selectionModel()->selectedRows();
+    int countRow = sens_det_indexes.count();
+
+    for( int i = countRow; i > 0; i--)
+        sens_det_model->removeRow( sens_det_indexes.at(i-1).row(), QModelIndex());
 }
 
 
@@ -228,77 +247,91 @@ void FSLMainWindow::del_sens_det()
 void FSLMainWindow::configure_sens_det_actions()
 {
     sensitive_detector_extensions.clear();
-    for(int i = 0; i<=ui->sens_det_view->model()->rowCount()-1; i++)
+
+    for(int row = 0 ; row < ui->sens_det_table->model()->rowCount(); ++row )
     {
-        sensitive_detector_extensions.push_back((sens_det_model->
-                           index( i, 0 ).data( Qt::DisplayRole ).toString()).toStdString());
+        std::string associated_file = ((ui->sens_det_table->model()->index(row,0)).data().toString()).toStdString();
+        sensitive_detector_extensions.push_back(associated_file);
 
     }
-}
 
 
-//Get index of the row in the Shape extensions display when clicked
-/*void FSLMainWindow::get_shape_index(QModelIndex shape_index)
-{
-    shape_number = shape_index.row();
 }
 
-//Add a shape extension
-void FSLMainWindow::add_shape_ext()
-{
-    std::string shape_ext_file = this->get_file_name();
-    if(shape_ext_file.find(".dylib") != std::string::npos || shape_ext_file.find(".so") != std::string::npos)
-    {QString q_shape_ext_file = QString::fromUtf8(shape_ext_file.c_str());
-    shape_model->insertRow(shape_model->rowCount());
-    QModelIndex shape_index = shape_model->index(shape_model->rowCount()-1);
-    shape_model->setData(shape_index, q_shape_ext_file);
-    }
-}
-
-//Delete the shape extension
-void FSLMainWindow::del_shape_ext()
-{
-    shape_model->removeRow(shape_number);
-}
-
-//Add the shape extenions to the shape extensions list.
-void FSLMainWindow::configure_shape_ext()
-{
-    shape_extensions.clear();
-    for(int i = 0; i<=ui->shape_view->model()->rowCount()-1; i++)
-    {
-        shape_extensions.push_back((shape_model->
-                           index( i, 0 ).data( Qt::DisplayRole ).toString()).toStdString());
-
-    }
-}*/
-
 
 //Get index of row in g4ui display when clicked
-void FSLMainWindow::get_g4ui_index(QModelIndex sens_det_index)
+/*void FSLMainWindow::get_g4ui_index(QModelIndex sens_det_index)
 {
     g4ui_number = sens_det_index.row();
-}
+}*/
 
 //Add a g4ui command
-void FSLMainWindow::add_g4ui()
+/*void FSLMainWindow::add_g4ui()
 {
-    QString g4ui_comm = ui->lE_g4ui->text();
-    if(g4ui_comm!="")
+    QString q_g4ui_comm =ui->lE_g4ui->text();
+    std::string g4ui_comm = ui->lE_g4ui->text().toStdString();
+    
+    if(g4ui_comm=="")
+    {
+    QMessageBox::information(this, "Info", "Can't add empty command");
+    }
+    
+    else if(g4ui_comm.find("/FSLgun/energy") != std::string::npos)
+    {
+    QMessageBox::information(this, "Info", "Particle energy is set from Generator tab");
+    }
+    
+    else if(g4ui_comm.find("/FSLgun/particle") != std::string::npos)
+    {
+    QMessageBox::information(this, "Info", "Particle must be set from Generator tab");
+    }
+    
+    else if(g4ui_comm.find("/FSLgun/direction") != std::string::npos)
+    {
+    QMessageBox::information(this, "Info", "Particle direction is set from Generator tab");
+    }
+    
+    else if(g4ui_comm.find("/FSLdet/setField") != std::string::npos)
+    {
+    QMessageBox::information(this, "Info", "Magnetic Field is set from Magnetic Field tab");
+    }
+    
+    else if(g4ui_comm.find("/FSLgun/primaryPerEvt") != std::string::npos)
+    {
+    QMessageBox::information(this, "Info", "Primary per evt can be set by editing config file in external editor");
+    }
+    
+    else if(g4ui_comm.find("/run/numberOfThreads") != std::string::npos)
+    {
+    QMessageBox::information(this, "Info", "Number of Threads can be set in the main tab");
+    }
+    
+    else if(g4ui_comm.find("/run/initialize") != std::string::npos)
+    {
+    QMessageBox::information(this, "Info", "Command already executed in FullSimLight");
+    }
+    
+    else if(g4ui_comm.find("/run/beamOn") != std::string::npos)
+    {
+    QMessageBox::information(this, "Info", "Command already executed in FullSimLight");
+    }
+    
+    else
     {
     g4ui_model->insertRow(g4ui_model->rowCount());
     QModelIndex g4ui_index = g4ui_model->index(g4ui_model->rowCount()-1);
-    g4ui_model->setData(g4ui_index, g4ui_comm);
+    g4ui_model->setData(g4ui_index, q_g4ui_comm);
     ui->lE_g4ui->clear();
     }
 
+
 }
 
 //Delete a g4ui command
 void FSLMainWindow::del_g4ui()
 {
     g4ui_model->removeRow(g4ui_number);
-}
+}*/
 
 //Add g4ui commands to g4ui commands list
 void FSLMainWindow::configure_g4ui_command()
@@ -334,13 +367,13 @@ void FSLMainWindow::configure_g4ui_command()
         g4ui_commands.push_back(tracking_command);
     }
 
-    for(int row = 0; row < ui->g4ui_view->model()->rowCount(); ++row)
+    /*for(int row = 0; row < ui->g4ui_view->model()->rowCount(); ++row)
     {
         std::string comm = (ui->g4ui_view->model()->index(row,0).data().toString()).toStdString();
         if(comm != ""){
         g4ui_commands.push_back(comm);
     }
-    }
+    }*/
 
     g4ui_commands.push_back("/control/cout/prefixString G4Worker_");
     g4ui_commands.push_back("/run/numberOfThreads " + ui->sB_NOT->text().toStdString());
@@ -354,13 +387,18 @@ void FSLMainWindow::configure_g4ui_command()
     {
     g4ui_commands.push_back("/run/initialize");
     }
+    
+    if(generator=="HepMC3 File")
+    {
+    g4ui_commands.push_back("/run/initialize");
+    }
 
     if(generator=="Particle Gun")
     {
     g4ui_commands.push_back("/FSLgun/primaryPerEvt " + std::to_string(number_of_primaries_per_event));
-    g4ui_commands.push_back("/FSLgun/energy  " + particle_energy);
-    g4ui_commands.push_back("/FSLgun/particle  " + particle);
-    g4ui_commands.push_back("/FSLgun/direction  " + particle_direction);
+    g4ui_commands.push_back("/FSLgun/energy " + particle_energy);
+    g4ui_commands.push_back("/FSLgun/particle " + particle);
+    g4ui_commands.push_back("/FSLgun/direction " + particle_direction);
 
     }
 
@@ -657,12 +695,31 @@ void FSLMainWindow::assign_geom_file()
     }
 }
 
+//Function to Physics List Plugin
+void FSLMainWindow::assign_phys_list_plugin()
+{
+    std::string phys_list = this->get_file_name();
+    if(phys_list!=""){ui->lE_PLN->setText(phys_list.c_str());}
+}
+
 //Function to select a pythia file
 void FSLMainWindow::assign_pythia_file()
 {
     pythia_input_file = this->get_file_name();
 }
 
+//Function to select a HepMC3 file
+void FSLMainWindow::assign_hepmc3_file()
+{
+    hepmc3_input_file = this->get_file_name();
+}
+
+//Function to select a Generator Plugin
+void FSLMainWindow::assign_gen_plug_file()
+{
+    generator_plugin = this->get_file_name();
+}
+
 void FSLMainWindow::check_if_pythia_file()
 {
     if(ui->cB_pythia_type_of_eve->currentIndex()==3)
@@ -715,9 +772,14 @@ void FSLMainWindow::configure_generator()
 
     if(generator=="Particle Gun")
     {
+        ui->sB_NOT->setEnabled(true);
+        
         this->configure_energy_direction();
         ui->pB_pythia_browse->setEnabled(false);
         ui->cB_pythia_type_of_eve->setEnabled(false);
+        ui->pB_hepmc3_browse_files->setEnabled(false);
+        ui->cB_hepmc3_type_of_eve->setEnabled(false);
+        ui->pB_gen_plug_browse_files->setEnabled(false);
         ui->cB_particle->setEnabled(true);
         ui->lE_px->setEnabled(true);
         ui->lE_py->setEnabled(true);
@@ -727,16 +789,25 @@ void FSLMainWindow::configure_generator()
         particle = (ui->cB_particle->currentText()).toStdString();
         pythia_type_of_event = "";
         pythia_input_file = "";
+        hepmc3_input_file = "";
+        hepmc3_type_of_file = "";
+        generator_plugin = "";
     }
 
     else if(generator=="Pythia")
     {
+        ui->sB_NOT->setEnabled(true);
+        
         ui->cB_pythia_type_of_eve->setEnabled(true);
 
         ui->cB_particle->setEnabled(false);
         ui->lE_px->setEnabled(false);
         ui->lE_py->setEnabled(false);
         ui->lE_pz->setEnabled(false);
+        ui->pB_hepmc3_browse_files->setEnabled(false);
+        ui->cB_hepmc3_type_of_eve->setEnabled(false);
+        ui->pB_gen_plug_browse_files->setEnabled(false);
+
 
         if(ui->cB_pythia_type_of_eve->currentIndex()==3)
         {
@@ -756,11 +827,76 @@ void FSLMainWindow::configure_generator()
         particle = "";
         particle_energy = "";
         particle_direction = "";
+        hepmc3_input_file = "";
+        hepmc3_type_of_file = "";
+        generator_plugin = "";
         p_x = 0;
         p_y = 0;
         p_z = 0;
 
     }
+    
+    else if(generator=="HepMC3 File")
+    {
+        ui->sB_NOT->setValue(1);
+        ui->sB_NOT->setEnabled(false);
+        
+        ui->pB_hepmc3_browse_files->setEnabled(true);
+        ui->cB_hepmc3_type_of_eve->setEnabled(true);
+        
+        ui->cB_particle->setEnabled(false);
+        ui->lE_px->setEnabled(false);
+        ui->lE_py->setEnabled(false);
+        ui->lE_pz->setEnabled(false);
+        ui->pB_pythia_browse->setEnabled(false);
+        ui->cB_pythia_type_of_eve->setEnabled(false);
+        ui->pB_gen_plug_browse_files->setEnabled(false);
+        
+        hepmc3_type_of_file = (ui->cB_hepmc3_type_of_eve->currentText()).toStdString();
+        
+        
+        particle = "";
+        particle_energy = "";
+        particle_direction = "";
+        pythia_type_of_event = "";
+        pythia_input_file = "";
+        generator_plugin = "";
+        p_x = 0;
+        p_y = 0;
+        p_z = 0;
+        
+    }
+    
+    
+    else if(generator=="Generator Plugin")
+    {
+        ui->sB_NOT->setEnabled(true);
+        
+        ui->pB_gen_plug_browse_files->setEnabled(true);
+        
+        ui->pB_hepmc3_browse_files->setEnabled(false);
+        ui->cB_hepmc3_type_of_eve->setEnabled(false);
+        
+        ui->cB_particle->setEnabled(false);
+        ui->lE_px->setEnabled(false);
+        ui->lE_py->setEnabled(false);
+        ui->lE_pz->setEnabled(false);
+        ui->pB_pythia_browse->setEnabled(false);
+        ui->cB_pythia_type_of_eve->setEnabled(false);
+        
+        
+        particle = "";
+        particle_energy = "";
+        particle_direction = "";
+        pythia_type_of_event = "";
+        pythia_input_file = "";
+        hepmc3_input_file = "";
+        hepmc3_type_of_file = "";
+        p_x = 0;
+        p_y = 0;
+        p_z = 0;
+        
+    }
 
 }
 
@@ -838,6 +974,24 @@ void FSLMainWindow::save_configuration()
     o << std::setw(4) << j << std::endl;
 }
 
+void FSLMainWindow::save_display_output()
+{
+    std::string text = ui->tB_view_config->toPlainText().toStdString();
+    if (save_display_directory.empty()) save_display_directory= (QDir::currentPath()).toStdString() +"/fullSimLight.log";
+    
+    QString displayfileName = QFileDialog::getSaveFileName(this,
+                            tr("Save Output"), save_display_directory.c_str(), tr("Log Files (*.log)"));
+    if (displayfileName.isEmpty()) return;
+    std::string   save_display_file=displayfileName.toStdString();
+    std::string   save_display_base=basename(const_cast<char *> (save_display_file.c_str()));
+    save_display_directory=dirname(const_cast<char *> (save_display_file.c_str()));
+    
+    std::ofstream DisplayFile(save_display_directory+"/"+save_display_base);
+    DisplayFile << text;
+    DisplayFile.close();
+    
+}
+
 //Function to save current configuration
 void FSLMainWindow::save_configuration_as()
 {
@@ -1022,20 +1176,22 @@ void FSLMainWindow::load_configuration()
     physics_list_name = j_load["Physics list name"];
     ui->lE_PLN->setText(QString::fromUtf8(physics_list_name.c_str()));
 
-    number_of_threads = j_load["Number of threads"];
-    ui->sB_NOT->setValue(number_of_threads);
+  //  number_of_threads = j_load["Number of threads"];
+  //  ui->sB_NOT->setValue(number_of_threads);
 
     number_of_events = j_load["Number of events"];
     ui->sB_NOE->setValue(number_of_events);
 
-    magnetic_field = j_load["Magnetic Field Intensity"];
-    ui->lE_fixed_MF->setText(QString::fromUtf8(magnetic_field.c_str()));
+  //  magnetic_field = j_load["Magnetic Field Intensity"];
+  //  ui->lE_fixed_MF->setText(QString::fromUtf8(magnetic_field.c_str()));
 
 
     generator = j_load["Generator"];
 
     if(generator=="Particle Gun")
     {
+        ui->sB_NOT->setEnabled(true);
+        
         ui->cB_gen_options->setCurrentIndex(0);
 
         ui->cB_particle->setEnabled(true);
@@ -1043,8 +1199,8 @@ void FSLMainWindow::load_configuration()
         ui->lE_py->setEnabled(true);
         ui->lE_pz->setEnabled(true);
 
-        particle = j_load["Particle"];
-        ui->cB_particle->setCurrentText(QString::fromUtf8(particle.c_str()));
+   //     particle = j_load["Particle"];
+   //     ui->cB_particle->setCurrentText(QString::fromUtf8(particle.c_str()));
 
         p_x = j_load["p_x"];
         p_y = j_load["p_y"];
@@ -1058,17 +1214,25 @@ void FSLMainWindow::load_configuration()
         ui->cB_pythia_type_of_eve->setCurrentIndex(0);
         ui->pB_pythia_browse->setEnabled(false);
         ui->cB_pythia_type_of_eve->setEnabled(false);
+        
+        ui->cB_hepmc3_type_of_eve->setCurrentIndex(0);
+        ui->pB_hepmc3_browse_files->setEnabled(false);
+        ui->cB_hepmc3_type_of_eve->setEnabled(false);
+        
+        ui->pB_gen_plug_browse_files->setEnabled(false);
+
 
     }
 
-    else
+    else if(generator=="Pythia")
     {
+        ui->sB_NOT->setEnabled(true);
+        
+        ui->cB_gen_options->setCurrentIndex(1);
 
         ui->cB_pythia_type_of_eve->setEnabled(true);
 
-        ui->cB_gen_options->setCurrentIndex(1);
-
-        pythia_type_of_event = j_load["Type of event"];
+        pythia_type_of_event = j_load["Pythia type of event"];
 
         if(pythia_type_of_event != "")
         {
@@ -1078,7 +1242,7 @@ void FSLMainWindow::load_configuration()
         }
         else 
         {
-        pythia_input_file = j_load["Event input file"];
+        pythia_input_file = j_load["Pythia event input file"];
         ui->cB_pythia_type_of_eve->setCurrentIndex(3);
         ui->pB_pythia_browse->setEnabled(true);
 
@@ -1089,23 +1253,519 @@ void FSLMainWindow::load_configuration()
         ui->lE_px->clear();
         ui->lE_py->clear();
         ui->lE_pz->clear();
+        ui->cB_particle->setCurrentIndex(0);
+        ui->cB_particle->setEnabled(false);
+        ui->lE_px->setEnabled(false);
+        ui->lE_py->setEnabled(false);
+        ui->lE_pz->setEnabled(false);
+        
+        ui->cB_hepmc3_type_of_eve->setCurrentIndex(0);
+        ui->pB_hepmc3_browse_files->setEnabled(false);
+        ui->cB_hepmc3_type_of_eve->setEnabled(false);
+        
+        ui->pB_gen_plug_browse_files->setEnabled(false);
 
 
+    }
+        
+    else if(generator=="HepMC3 File")
+    {
+        ui->sB_NOT->setValue(1);
+        ui->sB_NOT->setEnabled(false);
+        
+        ui->cB_gen_options->setCurrentIndex(2);
+        ui->pB_hepmc3_browse_files->setEnabled(true);
+        ui->cB_hepmc3_type_of_eve->setEnabled(true);
+        
+        hepmc3_type_of_file = j_load["HepMC3 type of file"];
+        hepmc3_input_file = j_load["HepMC3 file"];
+        
+        ui->cB_hepmc3_type_of_eve->setCurrentText(QString::fromUtf8(hepmc3_type_of_file.c_str()));
+        
+        ui->lE_px->clear();
+        ui->lE_py->clear();
+        ui->lE_pz->clear();
+        ui->cB_particle->setCurrentIndex(0);
         ui->cB_particle->setEnabled(false);
         ui->lE_px->setEnabled(false);
         ui->lE_py->setEnabled(false);
         ui->lE_pz->setEnabled(false);
+        
+        ui->cB_pythia_type_of_eve->setCurrentIndex(0);
+        ui->pB_pythia_browse->setEnabled(false);
+        ui->cB_pythia_type_of_eve->setEnabled(false);
+        
+        ui->pB_gen_plug_browse_files->setEnabled(false);
 
+        
+    }
+        
+    else if(generator=="Generator Plugin")
+    {
+        ui->sB_NOT->setEnabled(true);
+        
+        ui->cB_gen_options->setCurrentIndex(3);
+        ui->pB_gen_plug_browse_files->setEnabled(true);
+        generator_plugin = j_load["Generator Plugin"];
+        
+        ui->lE_px->clear();
+        ui->lE_py->clear();
+        ui->lE_pz->clear();
+        ui->cB_particle->setCurrentIndex(0);
+        ui->cB_particle->setEnabled(false);
+        ui->lE_px->setEnabled(false);
+        ui->lE_py->setEnabled(false);
+        ui->lE_pz->setEnabled(false);
+        
+        ui->cB_pythia_type_of_eve->setCurrentIndex(0);
+        ui->pB_pythia_browse->setEnabled(false);
+        ui->cB_pythia_type_of_eve->setEnabled(false);
+        
+        ui->cB_hepmc3_type_of_eve->setCurrentIndex(0);
+        ui->pB_hepmc3_browse_files->setEnabled(false);
+        ui->cB_hepmc3_type_of_eve->setEnabled(false);
+        
+        
     }
 
     sens_det_model->removeRows(0,sens_det_model->rowCount());
-    for(const auto& element : j_load["Sensitive Detector Extensions"] )
+    for(const auto& element : j_load["Sensitive Detector Extensions"])
+    {
+        std::string sens_det_file = element;
+        QString q_sens_det_file = QString::fromUtf8(sens_det_file.c_str());
+        int rows = ui->sens_det_table->model()->rowCount();
+        ui->sens_det_table->model()->insertRows(rows,1);
+        ui->sens_det_table->model()->setData(ui->sens_det_table->model()->index(rows,0),q_sens_det_file);
+
+    }
+
+    region_model->removeRows(0,region_model->rowCount());
+    for(const auto& element: j_load["Regions data"]){
+        Region r;
+        from_json(element,r);
+
+        int rows = ui->regions_table->model()->rowCount();
+        ui->regions_table->model()->insertRows(rows,1);
+
+        QString q_r_name = QString::fromUtf8(r.Region_name.c_str());
+        std::string r_froot = "";
+
+        for(const auto& element: r.fRootLVnames)
+        {
+            r_froot = r_froot+element+",";
+        }
+
+        if(r_froot!="")
+        {
+            r_froot.pop_back();
+        }
+
+        QString q_r_froot = QString::fromUtf8(r_froot.c_str());
+
+
+        ui->regions_table->model()->setData(ui->regions_table->model()->index(rows,0),q_r_name);
+        ui->regions_table->model()->setData(ui->regions_table->model()->index(rows,1),q_r_froot);
+        ui->regions_table->model()->setData(ui->regions_table->model()->index(rows,2),r.electron_cut);
+        ui->regions_table->model()->setData(ui->regions_table->model()->index(rows,3),r.proton_cut);
+        ui->regions_table->model()->setData(ui->regions_table->model()->index(rows,4),r.positron_cut);
+        ui->regions_table->model()->setData(ui->regions_table->model()->index(rows,5),r.gamma_cut);
+
+
+    }
+
+    magnetic_field_type = j_load["Magnetic Field Type"];
+
+    if(magnetic_field_type == "Fixed Axial")
+    {
+        ui->lE_fixed_MF->setEnabled(true);
+        ui->cB_magnetic_field->setCurrentIndex(0);
+       // magnetic_field = j_load["Magnetic Field Intensity"];
+        //ui->lE_fixed_MF->setText(QString::fromUtf8(magnetic_field.c_str()));
+
+
+        magnetic_field_plugin_file = "";
+      //  ui->lE_magnetic_field_map->clear();
+      //  ui->lE_magnetic_field_map->setEnabled(false);
+        magnetic_field_map = "";
+        ui->pB_magnetic_field_map->setEnabled(false);
+        ui->pB_magnetic_field_plugin->setEnabled(false);
+
+    }
+
+    else{
+        ui->pB_magnetic_field_map->setEnabled(true);
+        ui->pB_magnetic_field_plugin->setEnabled(true);
+        ui->cB_magnetic_field->setCurrentIndex(1);
+        magnetic_field_plugin_file = j_load["Magnetic Field Plugin"];
+        magnetic_field_map = j_load["Magnetic Field Map"];
+       // ui->lE_magnetic_field_map->setText(QString::fromUtf8(magnetic_field_map.c_str()));
+
+        magnetic_field = "";
+        ui->lE_fixed_MF->clear();
+        ui->lE_fixed_MF->setEnabled(false);
+
+
+    }
+
+    //g4ui_model->removeRows(0,g4ui_model->rowCount());
+    ui->cB_control->setCheckState(Qt::Unchecked);
+    ui->cB_event->setCheckState(Qt::Unchecked);
+    ui->cB_run->setCheckState(Qt::Unchecked);
+    ui->cB_tracking->setCheckState(Qt::Unchecked);
+    ui->sB_control->setValue(0);
+    ui->sB_event->setValue(0);
+    ui->sB_run->setValue(0);
+    ui->sB_tracking->setValue(0);
+
+    for(const auto& element : j_load["g4ui_commands"])
+    {
+        std::string g4ui_comm = element;
+
+
+        if(g4ui_comm.find("/run/verbose") != std::string::npos)
+        {
+            ui->cB_run->setCheckState(Qt::Checked);
+            int verbosity = g4ui_comm.back() - '0';
+            ui->sB_run->setValue(verbosity);
+
+        }
+
+        else if(g4ui_comm.find("/control/verbose") != std::string::npos)
+        {
+            ui->cB_control->setCheckState(Qt::Checked);
+            int verbosity = g4ui_comm.back() - '0';
+            ui->sB_control->setValue(verbosity);
+
+        }
+
+        else if(g4ui_comm.find("/event/verbose") != std::string::npos)
+        {
+            ui->cB_event->setCheckState(Qt::Checked);
+            int verbosity = g4ui_comm.back() - '0';
+            ui->sB_event->setValue(verbosity);
+
+        }
+
+        else if(g4ui_comm.find("/tracking/verbose") != std::string::npos)
+        {
+            ui->cB_tracking->setCheckState(Qt::Checked);
+            int verbosity = g4ui_comm.back() - '0';
+            ui->sB_tracking->setValue(verbosity);
+
+        }
+
+        else if(g4ui_comm.find("/FSLdet/setField") != std::string::npos)
+            
+        {
+            magnetic_field = g4ui_comm.substr(17,g4ui_comm.size()-17-6);
+            ui->lE_fixed_MF->setText(QString::fromUtf8(magnetic_field.c_str()));
+        }
+        
+        else if(g4ui_comm.find("/FSLgun/particle") != std::string::npos)
+        {
+            particle = g4ui_comm.substr(17,g4ui_comm.size()-16);
+            ui->cB_particle->setCurrentText(QString::fromUtf8(particle.c_str()));
+        }
+
+        else if(g4ui_comm.find("/run/numberOfThreads") != std::string::npos)
+        {
+            number_of_threads = stoi(g4ui_comm.substr(21,g4ui_comm.size()-20));
+            if(generator!="HepMC3 File"){ui->sB_NOT->setValue(number_of_threads);}
+        }
+        
+        else if(g4ui_comm.find("/FSLgun/primaryPerEvt") != std::string::npos)
+        {
+            number_of_primaries_per_event = stoi(g4ui_comm.substr(22,g4ui_comm.size()-21));
+        }
+        
+        else if(g4ui_comm.find("/FSLgun/energy") != std::string::npos
+                || g4ui_comm.find("/FSLgun/direction") != std::string::npos
+                || g4ui_comm.find("/control/cout/prefixString G4Worker_") != std::string::npos
+                || g4ui_comm.find("/run/initialize") != std::string::npos
+                || g4ui_comm.find("/run/beamOn") != std::string::npos)
+        {
+
+        }
+
+        else
+        {
+           // QString q_g4ui_comm = QString::fromUtf8(g4ui_comm.c_str());
+           // g4ui_model->insertRow(g4ui_model->rowCount());
+           // QModelIndex g4ui_index = g4ui_model->index(g4ui_model->rowCount()-1);
+            //g4ui_model->setData(g4ui_index, q_g4ui_comm);
+        }
+
+
+    }
+
+   /* user_action_model->removeRows(0,user_action_model->rowCount());
+    for(const auto& element : j_load["Run Actions"])
+    {
+        std::string run_file = element;
+        QString q_run_file = QString::fromUtf8(run_file.c_str());
+        int rows = ui->user_action_table->model()->rowCount();
+        ui->user_action_table->model()->insertRows(rows,1);
+        ui->user_action_table->model()->setData(ui->user_action_table->model()->index(rows,0),"Run");
+        ui->user_action_table->model()->setData(ui->user_action_table->model()->index(rows,1),q_run_file);
+
+    }
+
+    for(const auto& element : j_load["Event Actions"])
+    {
+        std::string event_file = element;
+        QString q_event_file = QString::fromUtf8(event_file.c_str());
+        int rows = ui->user_action_table->model()->rowCount();
+        ui->user_action_table->model()->insertRows(rows,1);
+        ui->user_action_table->model()->setData(ui->user_action_table->model()->index(rows,0),"Event");
+        ui->user_action_table->model()->setData(ui->user_action_table->model()->index(rows,1),q_event_file);
+
+    }
+
+    for(const auto& element : j_load["Stepping Actions"])
+    {
+        std::string stepping_file = element;
+        QString q_stepping_file = QString::fromUtf8(stepping_file.c_str());
+        int rows = ui->user_action_table->model()->rowCount();
+        ui->user_action_table->model()->insertRows(rows,1);
+        ui->user_action_table->model()->setData(ui->user_action_table->model()->index(rows,0),"Stepping");
+        ui->user_action_table->model()->setData(ui->user_action_table->model()->index(rows,1),q_stepping_file);
+
+    }
+
+    for(const auto& element : j_load["Stacking Actions"])
+    {
+        std::string stacking_file = element;
+        QString q_stacking_file = QString::fromUtf8(stacking_file.c_str());
+        int rows = ui->user_action_table->model()->rowCount();
+        ui->user_action_table->model()->insertRows(rows,1);
+        ui->user_action_table->model()->setData(ui->user_action_table->model()->index(rows,0),"Stacking");
+        ui->user_action_table->model()->setData(ui->user_action_table->model()->index(rows,1),q_stacking_file);
+
+    }
+
+    for(const auto& element : j_load["Tracking Actions"])
+    {
+        std::string tracking_file = element;
+        QString q_tracking_file = QString::fromUtf8(tracking_file.c_str());
+        int rows = ui->user_action_table->model()->rowCount();
+        ui->user_action_table->model()->insertRows(rows,1);
+        ui->user_action_table->model()->setData(ui->user_action_table->model()->index(rows,0),"Tracking");
+        ui->user_action_table->model()->setData(ui->user_action_table->model()->index(rows,1),q_tracking_file);
+
+    }
+    */
+
+   /* shape_model->removeRows(0,shape_model->rowCount());
+    for(const auto& element : j_load["Shape Extensions"] )
     {
         std::string ele = element;
         QString q_element = QString::fromUtf8(ele.c_str());
-        sens_det_model->insertRow(sens_det_model->rowCount());
-        QModelIndex sens_det_index = sens_det_model->index(sens_det_model->rowCount()-1);
-        sens_det_model->setData(sens_det_index, q_element);
+        shape_model->insertRow(shape_model->rowCount());
+        QModelIndex shape_index = shape_model->index(shape_model->rowCount()-1);
+        shape_model->setData(shape_index, q_element);
+    }*/
+    
+
+    user_action_model->removeRows(0,user_action_model->rowCount());
+    for(const auto& element : j_load["User Action Extensions"])
+    {
+        std::string run_file = element;
+        QString q_run_file = QString::fromUtf8(run_file.c_str());
+        int rows = ui->user_action_table->model()->rowCount();
+        ui->user_action_table->model()->insertRows(rows,1);
+        ui->user_action_table->model()->setData(ui->user_action_table->model()->index(rows,0),q_run_file);
+
+    }
+
+}
+
+}
+
+
+//Function to load configuration from Command Line
+void FSLMainWindow::load_configuration_CL(std::string config_file_path)
+{
+    config_file_name = config_file_path;
+
+    if(config_file_name.find(".json") != std::string::npos)
+    {
+    std::ifstream ifs(config_file_name);
+    auto j_load = nlohmann::json::parse(ifs);
+
+    //    QFileInfo file(QString::fromUtf8(load_file_name.c_str()));
+    ui->lE_CFN->setText(("Config file: " + config_file_name).c_str());
+    ui->lE_CFN->adjustSize();
+    
+    geom_file_address = j_load["Geometry"];
+
+    ui->le_GI->setText(geom_file_address.c_str());
+    ui->le_GI->adjustSize();
+
+    
+    physics_list_name = j_load["Physics list name"];
+    ui->lE_PLN->setText(QString::fromUtf8(physics_list_name.c_str()));
+
+  //  number_of_threads = j_load["Number of threads"];
+  //  ui->sB_NOT->setValue(number_of_threads);
+
+    number_of_events = j_load["Number of events"];
+    ui->sB_NOE->setValue(number_of_events);
+
+  //  magnetic_field = j_load["Magnetic Field Intensity"];
+  //  ui->lE_fixed_MF->setText(QString::fromUtf8(magnetic_field.c_str()));
+
+
+    generator = j_load["Generator"];
+
+    if(generator=="Particle Gun")
+    {
+        ui->sB_NOT->setEnabled(true);
+        
+        ui->cB_gen_options->setCurrentIndex(0);
+
+        ui->cB_particle->setEnabled(true);
+        ui->lE_px->setEnabled(true);
+        ui->lE_py->setEnabled(true);
+        ui->lE_pz->setEnabled(true);
+
+   //     particle = j_load["Particle"];
+   //     ui->cB_particle->setCurrentText(QString::fromUtf8(particle.c_str()));
+
+        p_x = j_load["p_x"];
+        p_y = j_load["p_y"];
+        p_z = j_load["p_z"];
+
+        ui->lE_px->setText(QString::number(p_x));
+        ui->lE_py->setText(QString::number(p_y));
+        ui->lE_pz->setText(QString::number(p_z));
+
+
+        ui->cB_pythia_type_of_eve->setCurrentIndex(0);
+        ui->pB_pythia_browse->setEnabled(false);
+        ui->cB_pythia_type_of_eve->setEnabled(false);
+        
+        ui->cB_hepmc3_type_of_eve->setCurrentIndex(0);
+        ui->pB_hepmc3_browse_files->setEnabled(false);
+        ui->cB_hepmc3_type_of_eve->setEnabled(false);
+        
+        ui->pB_gen_plug_browse_files->setEnabled(false);
+
+
+    }
+
+    else if(generator=="Pythia")
+    {
+        ui->sB_NOT->setEnabled(true);
+        
+        ui->cB_gen_options->setCurrentIndex(1);
+
+        ui->cB_pythia_type_of_eve->setEnabled(true);
+
+        pythia_type_of_event = j_load["Pythia type of event"];
+
+        if(pythia_type_of_event != "")
+        {
+        ui->cB_pythia_type_of_eve->setCurrentText(QString::fromUtf8(pythia_type_of_event.c_str()));
+        ui->pB_pythia_browse->setEnabled(false);
+
+        }
+        else
+        {
+        pythia_input_file = j_load["Pythia event input file"];
+        ui->cB_pythia_type_of_eve->setCurrentIndex(3);
+        ui->pB_pythia_browse->setEnabled(true);
+
+        }
+
+
+
+        ui->lE_px->clear();
+        ui->lE_py->clear();
+        ui->lE_pz->clear();
+        ui->cB_particle->setCurrentIndex(0);
+        ui->cB_particle->setEnabled(false);
+        ui->lE_px->setEnabled(false);
+        ui->lE_py->setEnabled(false);
+        ui->lE_pz->setEnabled(false);
+        
+        ui->cB_hepmc3_type_of_eve->setCurrentIndex(0);
+        ui->pB_hepmc3_browse_files->setEnabled(false);
+        ui->cB_hepmc3_type_of_eve->setEnabled(false);
+        
+        ui->pB_gen_plug_browse_files->setEnabled(false);
+
+
+    }
+        
+    else if(generator=="HepMC3 File")
+    {
+        ui->sB_NOT->setValue(1);
+        ui->sB_NOT->setEnabled(false);
+        
+        ui->cB_gen_options->setCurrentIndex(2);
+        ui->pB_hepmc3_browse_files->setEnabled(true);
+        ui->cB_hepmc3_type_of_eve->setEnabled(true);
+        
+        hepmc3_type_of_file = j_load["HepMC3 type of file"];
+        hepmc3_input_file = j_load["HepMC3 file"];
+        
+        ui->cB_hepmc3_type_of_eve->setCurrentText(QString::fromUtf8(hepmc3_type_of_file.c_str()));
+        
+        ui->lE_px->clear();
+        ui->lE_py->clear();
+        ui->lE_pz->clear();
+        ui->cB_particle->setCurrentIndex(0);
+        ui->cB_particle->setEnabled(false);
+        ui->lE_px->setEnabled(false);
+        ui->lE_py->setEnabled(false);
+        ui->lE_pz->setEnabled(false);
+        
+        ui->cB_pythia_type_of_eve->setCurrentIndex(0);
+        ui->pB_pythia_browse->setEnabled(false);
+        ui->cB_pythia_type_of_eve->setEnabled(false);
+        
+        ui->pB_gen_plug_browse_files->setEnabled(false);
+
+        
+    }
+        
+    else if(generator=="Generator Plugin")
+    {
+        ui->sB_NOT->setEnabled(true);
+        
+        ui->cB_gen_options->setCurrentIndex(3);
+        ui->pB_gen_plug_browse_files->setEnabled(true);
+        generator_plugin = j_load["Generator Plugin"];
+        
+        ui->lE_px->clear();
+        ui->lE_py->clear();
+        ui->lE_pz->clear();
+        ui->cB_particle->setCurrentIndex(0);
+        ui->cB_particle->setEnabled(false);
+        ui->lE_px->setEnabled(false);
+        ui->lE_py->setEnabled(false);
+        ui->lE_pz->setEnabled(false);
+        
+        ui->cB_pythia_type_of_eve->setCurrentIndex(0);
+        ui->pB_pythia_browse->setEnabled(false);
+        ui->cB_pythia_type_of_eve->setEnabled(false);
+        
+        ui->cB_hepmc3_type_of_eve->setCurrentIndex(0);
+        ui->pB_hepmc3_browse_files->setEnabled(false);
+        ui->cB_hepmc3_type_of_eve->setEnabled(false);
+        
+        
+    }
+        
+    sens_det_model->removeRows(0,sens_det_model->rowCount());
+    for(const auto& element : j_load["Sensitive Detector Extensions"])
+    {
+        std::string sens_det_file = element;
+        QString q_sens_det_file = QString::fromUtf8(sens_det_file.c_str());
+        int rows = ui->sens_det_table->model()->rowCount();
+        ui->sens_det_table->model()->insertRows(rows,1);
+        ui->sens_det_table->model()->setData(ui->sens_det_table->model()->index(rows,0),q_sens_det_file);
+
     }
 
     region_model->removeRows(0,region_model->rowCount());
@@ -1148,8 +1808,8 @@ void FSLMainWindow::load_configuration()
     {
         ui->lE_fixed_MF->setEnabled(true);
         ui->cB_magnetic_field->setCurrentIndex(0);
-        magnetic_field = j_load["Magnetic Field Intensity"];
-        ui->lE_fixed_MF->setText(QString::fromUtf8(magnetic_field.c_str()));
+       // magnetic_field = j_load["Magnetic Field Intensity"];
+        //ui->lE_fixed_MF->setText(QString::fromUtf8(magnetic_field.c_str()));
 
 
         magnetic_field_plugin_file = "";
@@ -1176,7 +1836,7 @@ void FSLMainWindow::load_configuration()
 
     }
 
-    g4ui_model->removeRows(0,g4ui_model->rowCount());
+    //g4ui_model->removeRows(0,g4ui_model->rowCount());
     ui->cB_control->setCheckState(Qt::Unchecked);
     ui->cB_event->setCheckState(Qt::Unchecked);
     ui->cB_run->setCheckState(Qt::Unchecked);
@@ -1223,14 +1883,33 @@ void FSLMainWindow::load_configuration()
 
         }
 
-        else if(g4ui_comm.find("/FSLdet/setField") != std::string::npos
-                || g4ui_comm.find("/FSLgun/primaryPerEvt") != std::string::npos
-                || g4ui_comm.find("/FSLgun/energy") != std::string::npos
-                || g4ui_comm.find("/FSLgun/particle") != std::string::npos
+        else if(g4ui_comm.find("/FSLdet/setField") != std::string::npos)
+            
+        {
+            magnetic_field = g4ui_comm.substr(17,g4ui_comm.size()-17-6);
+            ui->lE_fixed_MF->setText(QString::fromUtf8(magnetic_field.c_str()));
+        }
+        
+        else if(g4ui_comm.find("/FSLgun/particle") != std::string::npos)
+        {
+            particle = g4ui_comm.substr(17,g4ui_comm.size()-16);
+            ui->cB_particle->setCurrentText(QString::fromUtf8(particle.c_str()));
+        }
+
+        else if(g4ui_comm.find("/run/numberOfThreads") != std::string::npos)
+        {
+            number_of_threads = stoi(g4ui_comm.substr(21,g4ui_comm.size()-20));
+            if(generator!="HepMC3 File"){ui->sB_NOT->setValue(number_of_threads);}
+        }
+        
+        else if(g4ui_comm.find("/FSLgun/primaryPerEvt") != std::string::npos)
+        {
+            number_of_primaries_per_event = stoi(g4ui_comm.substr(22,g4ui_comm.size()-21));
+        }
+        
+        else if(g4ui_comm.find("/FSLgun/energy") != std::string::npos
                 || g4ui_comm.find("/FSLgun/direction") != std::string::npos
-                || g4ui_comm.find("/run/numberOfThreads") != std::string::npos
                 || g4ui_comm.find("/control/cout/prefixString G4Worker_") != std::string::npos
-                || g4ui_comm.find("/process/list") != std::string::npos
                 || g4ui_comm.find("/run/initialize") != std::string::npos
                 || g4ui_comm.find("/run/beamOn") != std::string::npos)
         {
@@ -1239,15 +1918,16 @@ void FSLMainWindow::load_configuration()
 
         else
         {
-            QString q_g4ui_comm = QString::fromUtf8(g4ui_comm.c_str());
-            g4ui_model->insertRow(g4ui_model->rowCount());
-            QModelIndex g4ui_index = g4ui_model->index(g4ui_model->rowCount()-1);
-            g4ui_model->setData(g4ui_index, q_g4ui_comm);
+           // QString q_g4ui_comm = QString::fromUtf8(g4ui_comm.c_str());
+            //g4ui_model->insertRow(g4ui_model->rowCount());
+           // QModelIndex g4ui_index = g4ui_model->index(g4ui_model->rowCount()-1);
+            //g4ui_model->setData(g4ui_index, q_g4ui_comm);
         }
 
 
     }
 
+
    /* user_action_model->removeRows(0,user_action_model->rowCount());
     for(const auto& element : j_load["Run Actions"])
     {
@@ -1328,6 +2008,7 @@ void FSLMainWindow::load_configuration()
     }
 
 }
+    else {std::cout << "File must be of json type" << std::endl; exit(-1);}
 
 }
 
@@ -1338,33 +2019,37 @@ void FSLMainWindow::create_configuration()
     number_of_events = ui->sB_NOE->value();
     number_of_threads = ui->sB_NOT->value();
     physics_list_name = (ui->lE_PLN->text()).toStdString();
-    hits_file = (ui->lE_hits->text()).toStdString();
-    histo_file = (ui->lE_histo->text()).toStdString();
+   // hits_file = (ui->lE_hits->text()).toStdString();
+   // histo_file = (ui->lE_histo->text()).toStdString();
 
     j["Geometry"] = geom_file_address;
     j["Physics list name"] = physics_list_name;
-    j["Number of threads"] = number_of_threads;
+ //   j["Number of threads"] = number_of_threads;
     j["Number of events"] = number_of_events;
 
     this->configure_generator();
     j["Generator"] = generator;
-    j["Particle"] = particle;
+ //   j["Particle"] = particle;
     j["p_x"] = p_x;
     j["p_y"] = p_y;
     j["p_z"] = p_z;
-    j["Particle energy"] = particle_energy;
-    j["Particle direction"] = particle_direction;
-    j["Event input file"] = pythia_input_file;
-    j["Type of event"] = pythia_type_of_event;
+//    j["Particle energy"] = particle_energy;
+//    j["Particle direction"] = particle_direction;
+    j["Pythia event input file"] = pythia_input_file;
+    j["Pythia type of event"] = pythia_type_of_event;
+    j["HepMC3 file"] = hepmc3_input_file;
+    j["HepMC3 type of file"] = hepmc3_type_of_file;
+    j["Generator Plugin"] = generator_plugin;
+
 
     this->configure_sens_det_actions();
     j["Sensitive Detector Extensions"] = sensitive_detector_extensions;
-    j["Output Hits file"] = hits_file;
-    j["Output Histo file"] = histo_file;
+ //   j["Output Hits file"] = hits_file;
+ //   j["Output Histo file"] = histo_file;
 
     this->configure_magnetic_field();
     j["Magnetic Field Type"] = magnetic_field_type;
-    j["Magnetic Field Intensity"] = magnetic_field;
+ //   j["Magnetic Field Intensity"] = magnetic_field;
     j["Magnetic Field Map"] = magnetic_field_map;
     j["Magnetic Field Plugin"] = magnetic_field_plugin_file;
 
diff --git a/FSL/src/fsl_mainwindow.h b/FSL/src/fsl_mainwindow.h
index e0879c1765babefc46796a68d9cdb5b71bfe6cdf..78b0f30d14eea02c61ba205b9d00d31310ab821a 100644
--- a/FSL/src/fsl_mainwindow.h
+++ b/FSL/src/fsl_mainwindow.h
@@ -66,7 +66,8 @@ public:
     int number_of_events = 0;
     std::string config_file_name = "";
     std::string save_directory = "";
-
+    std::string save_display_directory = "";
+    
     //Parameters associated with the Generator tab
     std::string generator = "";
     std::string particle = "";
@@ -81,12 +82,16 @@ public:
     int number_of_primaries_per_event;
     std::string pythia_input_file = "";
     std::string pythia_type_of_event = "";
+    
+    std::string hepmc3_input_file = "";
+    std::string hepmc3_type_of_file = "";
 
+    std::string generator_plugin = "";
     //Parameters associated with the Sensitive Detectors tab
     std::vector<std::string> sensitive_detector_extensions;
-    int sens_det_number;
-    std::string hits_file = "";
-    std::string histo_file = "";
+    //int sens_det_number;
+   // std::string hits_file = "";
+   // std::string histo_file = "";
 
     //Parameters associated with the Magnetic field tab
     std::string magnetic_field_type = "";
@@ -104,7 +109,7 @@ public:
 
     //Parameters associated with the g4ui commands tab
     std::vector<std::string> g4ui_commands;
-    int g4ui_number;
+   // int g4ui_number;
 
     //Parameters associated with the shape commands tab
   //  int shape_number;
@@ -124,12 +129,14 @@ public:
     //Functions used in Configuration
     void save_configuration();
     void save_configuration_as();
+    void save_display_output();
     void create_configuration();
     void view_configuration();
     void run_configuration();
     void run_gmex();
     void run_gmclash();
     void load_configuration();
+    void load_configuration_CL(std::string config_file_path); //Load configuration from Command Line
     std::vector<std::string> display_configuration(const std::string &s);
 
     void clear_main_status();
@@ -140,8 +147,8 @@ public:
     void del_sens_det();
     void configure_sens_det_actions();
 
-    void add_g4ui();
-    void del_g4ui();
+  //  void add_g4ui();
+  //  void del_g4ui();
     void configure_g4ui_command();
 
     void pop_up_regions();
@@ -159,6 +166,9 @@ public:
    // void configure_shape_ext();
 
     void assign_geom_file();
+    void assign_phys_list_plugin();
+    void assign_gen_plug_file();
+    void assign_hepmc3_file();
     void assign_pythia_file();
     void assign_magnetic_field_plugin_file();
     void assign_magnetic_field_map();
@@ -187,9 +197,9 @@ signals:
     void send_error_message(std::string info);
 
 private slots:
-    void catch_error_message(std::string info);
-    void get_sens_det_index(QModelIndex region_index);
-    void get_g4ui_index(QModelIndex g4ui_index);
+   // void catch_error_message(std::string info);
+   // void get_sens_det_index(QModelIndex region_index);
+   // void get_g4ui_index(QModelIndex g4ui_index);
    // void get_shape_index(QModelIndex g4ui_index);
     void add_region(std::string  region_name, std::string frootLV_names
                     ,double electron_cut , double proton_cut
@@ -209,14 +219,16 @@ private slots:
 
 private:
     Ui::FSLMainWindow *ui;
-    QStringListModel *sens_det_model;
-    QStringListModel *g4ui_model;
+ //   QStringListModel *sens_det_model;
+  //  QStringListModel *g4ui_model;
   //  QStringListModel *shape_model;
     ConfigRegions *region;
     QStandardItemModel *region_model;
     QStandardItemModel *user_action_model;
+    QStandardItemModel *sens_det_model;
     QStringList region_horizontalHeader;
     QStringList user_action_horizontalHeader;
+    QStringList sens_det_horizontalHeader;
     QDoubleValidator *p_x_validator;
     QDoubleValidator *p_y_validator;
     QDoubleValidator *p_z_validator;
diff --git a/FSL/src/fsl_mainwindow.ui b/FSL/src/fsl_mainwindow.ui
index cdfe65a92fbf2fa200996073a66a4fbe6c09ad26..030dc0f7011f2d95edd1658231526986a7cb8d09 100644
--- a/FSL/src/fsl_mainwindow.ui
+++ b/FSL/src/fsl_mainwindow.ui
@@ -6,7 +6,7 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>1261</width>
+    <width>1355</width>
     <height>726</height>
    </rect>
   </property>
@@ -35,133 +35,24 @@
        <attribute name="title">
         <string>Main</string>
        </attribute>
-       <layout class="QGridLayout" name="gridLayout_10">
-        <item row="1" column="0" colspan="2">
-         <widget class="QTextBrowser" name="tB_view_config">
-          <property name="textInteractionFlags">
-           <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
-          </property>
-         </widget>
-        </item>
-        <item row="0" column="0">
-         <widget class="QGroupBox" name="groupBox_3">
-          <property name="font">
-           <font>
-            <pointsize>15</pointsize>
-           </font>
-          </property>
+       <layout class="QGridLayout" name="gridLayout_15">
+        <item row="3" column="0" colspan="2">
+         <widget class="QGroupBox" name="groupBox_11">
           <property name="title">
            <string/>
           </property>
-          <layout class="QGridLayout" name="gridLayout_9">
-           <item row="2" column="0">
-            <widget class="QPushButton" name="pB_geom">
-             <property name="text">
-              <string>Geometry input</string>
-             </property>
-            </widget>
-           </item>
-           <item row="0" column="0" colspan="2">
-            <widget class="QLabel" name="lE_CFN">
-             <property name="text">
-              <string/>
-             </property>
-             <property name="scaledContents">
-              <bool>true</bool>
-             </property>
-            </widget>
-           </item>
-           <item row="6" column="0">
-            <widget class="QLabel" name="label_2">
-             <property name="font">
-              <font>
-               <pointsize>15</pointsize>
-              </font>
-             </property>
-             <property name="text">
-              <string>Number of Events</string>
-             </property>
-            </widget>
-           </item>
-           <item row="6" column="1">
-            <widget class="QSpinBox" name="sB_NOE"/>
-           </item>
-           <item row="4" column="1">
-            <widget class="QSpinBox" name="sB_NOT"/>
-           </item>
-           <item row="2" column="1">
-            <widget class="QLabel" name="le_GI">
-             <property name="text">
-              <string/>
-             </property>
-            </widget>
-           </item>
-           <item row="3" column="0">
-            <widget class="QLabel" name="label">
-             <property name="font">
-              <font>
-               <pointsize>15</pointsize>
-              </font>
-             </property>
-             <property name="text">
-              <string>Physics List Name</string>
-             </property>
-            </widget>
-           </item>
-           <item row="3" column="1">
-            <widget class="QLineEdit" name="lE_PLN"/>
-           </item>
-           <item row="4" column="0">
-            <widget class="QLabel" name="label_3">
-             <property name="font">
-              <font>
-               <pointsize>15</pointsize>
-              </font>
-             </property>
+          <layout class="QGridLayout" name="gridLayout_10">
+           <item row="0" column="4">
+            <widget class="QCommandLinkButton" name="pB_gmex">
              <property name="text">
-              <string>Number of Threads</string>
+              <string>Gmex</string>
              </property>
             </widget>
            </item>
-          </layout>
-         </widget>
-        </item>
-        <item row="2" column="0">
-         <widget class="QGroupBox" name="groupBox_11">
-          <property name="title">
-           <string/>
-          </property>
-          <layout class="QGridLayout" name="gridLayout_13">
-           <item row="0" column="0">
-            <spacer name="horizontalSpacer_28">
-             <property name="orientation">
-              <enum>Qt::Horizontal</enum>
-             </property>
-             <property name="sizeHint" stdset="0">
-              <size>
-               <width>110</width>
-               <height>20</height>
-              </size>
-             </property>
-            </spacer>
-           </item>
-           <item row="0" column="1">
-            <spacer name="horizontalSpacer_32">
-             <property name="orientation">
-              <enum>Qt::Horizontal</enum>
-             </property>
-             <property name="sizeHint" stdset="0">
-              <size>
-               <width>109</width>
-               <height>17</height>
-              </size>
-             </property>
-            </spacer>
-           </item>
-           <item row="0" column="2">
-            <widget class="QPushButton" name="pB_view">
+           <item row="0" column="6">
+            <widget class="QCommandLinkButton" name="pB_Run">
              <property name="text">
-              <string>View</string>
+              <string>FullSimLight</string>
              </property>
             </widget>
            </item>
@@ -178,57 +69,260 @@
              </property>
             </spacer>
            </item>
-           <item row="0" column="4">
-            <widget class="QPushButton" name="pB_main_clear">
+           <item row="0" column="1">
+            <widget class="QPushButton" name="pB_save_config">
              <property name="text">
-              <string>Clear</string>
+              <string>Save Configuration</string>
              </property>
             </widget>
            </item>
-           <item row="0" column="5">
-            <spacer name="horizontalSpacer_26">
-             <property name="orientation">
-              <enum>Qt::Horizontal</enum>
-             </property>
-             <property name="sizeHint" stdset="0">
-              <size>
-               <width>58</width>
-               <height>17</height>
-              </size>
-             </property>
-            </spacer>
-           </item>
-           <item row="0" column="6">
-            <widget class="QCommandLinkButton" name="pB_gmex">
+           <item row="0" column="2">
+            <widget class="QPushButton" name="pB_view">
              <property name="text">
-              <string>Gmex</string>
+              <string>View Configuration</string>
              </property>
             </widget>
            </item>
-           <item row="0" column="7">
+           <item row="0" column="5">
             <widget class="QCommandLinkButton" name="pB_gmclash">
              <property name="text">
               <string>Gmclash</string>
              </property>
             </widget>
            </item>
-           <item row="0" column="8">
-            <widget class="QCommandLinkButton" name="pB_Run">
+           <item row="0" column="0">
+            <widget class="QPushButton" name="pB_main_clear">
              <property name="text">
-              <string>FullSimLight</string>
+              <string>Clear</string>
              </property>
             </widget>
            </item>
           </layout>
          </widget>
         </item>
+        <item row="0" column="0" rowspan="3" colspan="2">
+         <layout class="QVBoxLayout" name="verticalLayout_2">
+          <item>
+           <layout class="QGridLayout" name="gridLayout_11">
+            <item row="0" column="0" rowspan="2">
+             <widget class="QGroupBox" name="groupBox_3">
+              <property name="font">
+               <font>
+                <pointsize>15</pointsize>
+               </font>
+              </property>
+              <property name="title">
+               <string/>
+              </property>
+              <layout class="QGridLayout" name="gridLayout_9">
+               <item row="0" column="0">
+                <widget class="QLabel" name="lE_CFN">
+                 <property name="text">
+                  <string/>
+                 </property>
+                 <property name="scaledContents">
+                  <bool>true</bool>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="0">
+                <widget class="QPushButton" name="pB_geom">
+                 <property name="text">
+                  <string>Geometry input</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="1">
+                <widget class="QLabel" name="le_GI">
+                 <property name="text">
+                  <string/>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="0">
+                <widget class="QLabel" name="label">
+                 <property name="font">
+                  <font>
+                   <pointsize>15</pointsize>
+                  </font>
+                 </property>
+                 <property name="text">
+                  <string>Physics List Name</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="1">
+                <widget class="QLineEdit" name="lE_PLN"/>
+               </item>
+               <item row="2" column="2">
+                <widget class="QPushButton" name="pB_browse_phys_list">
+                 <property name="text">
+                  <string>+</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="3" column="0">
+                <widget class="QLabel" name="label_3">
+                 <property name="font">
+                  <font>
+                   <pointsize>15</pointsize>
+                  </font>
+                 </property>
+                 <property name="text">
+                  <string>Number of Threads</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="3" column="1" colspan="2">
+                <widget class="QSpinBox" name="sB_NOT"/>
+               </item>
+               <item row="4" column="0">
+                <widget class="QLabel" name="label_2">
+                 <property name="font">
+                  <font>
+                   <pointsize>15</pointsize>
+                  </font>
+                 </property>
+                 <property name="text">
+                  <string>Number of Events</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="4" column="1" colspan="2">
+                <widget class="QSpinBox" name="sB_NOE"/>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item row="0" column="1">
+             <widget class="QGroupBox" name="groupBox_8">
+              <property name="font">
+               <font>
+                <pointsize>15</pointsize>
+               </font>
+              </property>
+              <property name="title">
+               <string/>
+              </property>
+              <layout class="QGridLayout" name="gridLayout_12">
+               <item row="0" column="0">
+                <layout class="QVBoxLayout" name="verticalLayout_7">
+                 <item>
+                  <widget class="QLabel" name="label_26">
+                   <property name="font">
+                    <font>
+                     <pointsize>16</pointsize>
+                    </font>
+                   </property>
+                   <property name="text">
+                    <string>G4Ui Commands</string>
+                   </property>
+                  </widget>
+                 </item>
+                 <item>
+                  <widget class="QLabel" name="label_36">
+                   <property name="font">
+                    <font>
+                     <pointsize>16</pointsize>
+                    </font>
+                   </property>
+                   <property name="text">
+                    <string>Verbosity </string>
+                   </property>
+                  </widget>
+                 </item>
+                </layout>
+               </item>
+               <item row="0" column="1">
+                <layout class="QVBoxLayout" name="verticalLayout_3">
+                 <item>
+                  <widget class="QCheckBox" name="cB_control">
+                   <property name="text">
+                    <string>  Control</string>
+                   </property>
+                  </widget>
+                 </item>
+                 <item>
+                  <widget class="QSpinBox" name="sB_control"/>
+                 </item>
+                </layout>
+               </item>
+               <item row="0" column="2">
+                <layout class="QVBoxLayout" name="verticalLayout_4">
+                 <item>
+                  <widget class="QCheckBox" name="cB_event">
+                   <property name="text">
+                    <string>  Event</string>
+                   </property>
+                  </widget>
+                 </item>
+                 <item>
+                  <widget class="QSpinBox" name="sB_event"/>
+                 </item>
+                </layout>
+               </item>
+               <item row="0" column="3">
+                <layout class="QVBoxLayout" name="verticalLayout_5">
+                 <item>
+                  <widget class="QCheckBox" name="cB_run">
+                   <property name="text">
+                    <string>  Run</string>
+                   </property>
+                  </widget>
+                 </item>
+                 <item>
+                  <widget class="QSpinBox" name="sB_run"/>
+                 </item>
+                </layout>
+               </item>
+               <item row="0" column="4">
+                <layout class="QVBoxLayout" name="verticalLayout_6">
+                 <item>
+                  <widget class="QCheckBox" name="cB_tracking">
+                   <property name="text">
+                    <string>  Tracking</string>
+                   </property>
+                  </widget>
+                 </item>
+                 <item>
+                  <widget class="QSpinBox" name="sB_tracking"/>
+                 </item>
+                </layout>
+               </item>
+              </layout>
+             </widget>
+            </item>
+           </layout>
+          </item>
+          <item>
+           <widget class="BetterTextBrowser" name="tB_view_config">
+            <property name="textInteractionFlags">
+             <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
+        <item row="1" column="0">
+         <spacer name="verticalSpacer">
+          <property name="orientation">
+           <enum>Qt::Vertical</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>20</width>
+            <height>18</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
        </layout>
       </widget>
       <widget class="QWidget" name="generator">
        <attribute name="title">
         <string>Generator</string>
        </attribute>
-       <layout class="QGridLayout" name="gridLayout_8">
+       <layout class="QGridLayout" name="gridLayout_14">
         <item row="0" column="0">
          <widget class="QLabel" name="label_11">
           <property name="font">
@@ -267,7 +361,7 @@
           </property>
          </spacer>
         </item>
-        <item row="1" column="4">
+        <item row="1" column="3">
          <widget class="QGroupBox" name="groupBox_2">
           <property name="font">
            <font>
@@ -293,12 +387,22 @@
                <string>Pythia</string>
               </property>
              </item>
+             <item>
+              <property name="text">
+               <string>HepMC3 File</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Generator Plugin</string>
+              </property>
+             </item>
             </widget>
            </item>
           </layout>
          </widget>
         </item>
-        <item row="1" column="5">
+        <item row="1" column="4">
          <spacer name="horizontalSpacer_22">
           <property name="orientation">
            <enum>Qt::Horizontal</enum>
@@ -374,6 +478,11 @@
                <string>geantino</string>
               </property>
              </item>
+             <item>
+              <property name="text">
+               <string>gamma</string>
+              </property>
+             </item>
             </widget>
            </item>
            <item row="1" column="0" colspan="2">
@@ -472,7 +581,7 @@
           </layout>
          </widget>
         </item>
-        <item row="2" column="3">
+        <item row="2" column="3" rowspan="2">
          <spacer name="verticalSpacer_3">
           <property name="orientation">
            <enum>Qt::Vertical</enum>
@@ -485,20 +594,7 @@
           </property>
          </spacer>
         </item>
-        <item row="3" column="0" colspan="2">
-         <spacer name="horizontalSpacer_4">
-          <property name="orientation">
-           <enum>Qt::Horizontal</enum>
-          </property>
-          <property name="sizeHint" stdset="0">
-           <size>
-            <width>122</width>
-            <height>20</height>
-           </size>
-          </property>
-         </spacer>
-        </item>
-        <item row="3" column="5" rowspan="2">
+        <item row="2" column="4">
          <widget class="QGroupBox" name="groupBox_5">
           <property name="font">
            <font>
@@ -567,282 +663,163 @@
           </layout>
          </widget>
         </item>
-        <item row="4" column="3" colspan="2">
-         <spacer name="horizontalSpacer_5">
-          <property name="orientation">
-           <enum>Qt::Horizontal</enum>
-          </property>
-          <property name="sizeHint" stdset="0">
-           <size>
-            <width>186</width>
-            <height>20</height>
-           </size>
-          </property>
-         </spacer>
-        </item>
-        <item row="4" column="6">
-         <spacer name="horizontalSpacer_20">
-          <property name="orientation">
-           <enum>Qt::Horizontal</enum>
-          </property>
-          <property name="sizeHint" stdset="0">
-           <size>
-            <width>104</width>
-            <height>20</height>
-           </size>
-          </property>
-         </spacer>
-        </item>
-        <item row="5" column="2">
-         <spacer name="verticalSpacer_10">
-          <property name="orientation">
-           <enum>Qt::Vertical</enum>
-          </property>
-          <property name="sizeHint" stdset="0">
-           <size>
-            <width>20</width>
-            <height>32</height>
-           </size>
-          </property>
-         </spacer>
-        </item>
-        <item row="5" column="5">
-         <spacer name="verticalSpacer_11">
-          <property name="orientation">
-           <enum>Qt::Vertical</enum>
-          </property>
-          <property name="sizeHint" stdset="0">
-           <size>
-            <width>20</width>
-            <height>32</height>
-           </size>
-          </property>
-         </spacer>
-        </item>
-       </layout>
-      </widget>
-      <widget class="QWidget" name="G4UI">
-       <attribute name="title">
-        <string>G4UI</string>
-       </attribute>
-       <layout class="QGridLayout" name="gridLayout_14">
-        <item row="0" column="0" colspan="2">
-         <widget class="QGroupBox" name="groupBox_8">
+        <item row="3" column="4">
+         <widget class="QGroupBox" name="groupBox_10">
           <property name="font">
            <font>
             <pointsize>15</pointsize>
            </font>
           </property>
           <property name="title">
-           <string>Set Commands</string>
+           <string>HepMC3</string>
           </property>
-          <layout class="QGridLayout" name="gridLayout_12">
+          <layout class="QGridLayout" name="gridLayout_7">
            <item row="0" column="0">
-            <layout class="QVBoxLayout" name="verticalLayout_7">
-             <item>
-              <widget class="QLabel" name="label_26">
-               <property name="font">
-                <font>
-                 <pointsize>16</pointsize>
-                </font>
-               </property>
-               <property name="text">
-                <string>G4Ui Commands</string>
-               </property>
-              </widget>
-             </item>
-             <item>
-              <widget class="QLabel" name="label_36">
-               <property name="font">
-                <font>
-                 <pointsize>16</pointsize>
-                </font>
-               </property>
-               <property name="text">
-                <string>Set Verbosity</string>
-               </property>
-              </widget>
-             </item>
-            </layout>
+            <widget class="QLabel" name="label_16">
+             <property name="font">
+              <font>
+               <pointsize>15</pointsize>
+              </font>
+             </property>
+             <property name="text">
+              <string>HepMC3  Event File</string>
+             </property>
+            </widget>
            </item>
            <item row="0" column="1">
-            <layout class="QVBoxLayout" name="verticalLayout_3">
-             <item>
-              <widget class="QCheckBox" name="cB_control">
-               <property name="text">
-                <string>  Control</string>
-               </property>
-              </widget>
-             </item>
-             <item>
-              <widget class="QSpinBox" name="sB_control"/>
-             </item>
-            </layout>
-           </item>
-           <item row="0" column="2">
-            <layout class="QVBoxLayout" name="verticalLayout_4">
-             <item>
-              <widget class="QCheckBox" name="cB_event">
-               <property name="text">
-                <string>  Event</string>
-               </property>
-              </widget>
-             </item>
-             <item>
-              <widget class="QSpinBox" name="sB_event"/>
-             </item>
-            </layout>
+            <widget class="QPushButton" name="pB_hepmc3_browse_files">
+             <property name="text">
+              <string>Browse Files</string>
+             </property>
+            </widget>
            </item>
-           <item row="0" column="3">
-            <layout class="QVBoxLayout" name="verticalLayout_5">
-             <item>
-              <widget class="QCheckBox" name="cB_run">
-               <property name="text">
-                <string>  Run</string>
-               </property>
-              </widget>
-             </item>
-             <item>
-              <widget class="QSpinBox" name="sB_run"/>
-             </item>
-            </layout>
+           <item row="1" column="0">
+            <widget class="QLabel" name="label_17">
+             <property name="font">
+              <font>
+               <pointsize>15</pointsize>
+              </font>
+             </property>
+             <property name="text">
+              <string>File Format</string>
+             </property>
+            </widget>
            </item>
-           <item row="0" column="4">
-            <layout class="QVBoxLayout" name="verticalLayout_6">
+           <item row="1" column="1">
+            <widget class="QComboBox" name="cB_hepmc3_type_of_eve">
              <item>
-              <widget class="QCheckBox" name="cB_tracking">
-               <property name="text">
-                <string>  Tracking</string>
-               </property>
-              </widget>
+              <property name="text">
+               <string>Ascii</string>
+              </property>
              </item>
              <item>
-              <widget class="QSpinBox" name="sB_tracking"/>
+              <property name="text">
+               <string>Asciiv3</string>
+              </property>
              </item>
-            </layout>
+            </widget>
            </item>
           </layout>
          </widget>
         </item>
-        <item row="4" column="0" rowspan="2">
-         <widget class="QGroupBox" name="groupBox_9">
+        <item row="4" column="0" colspan="2">
+         <spacer name="horizontalSpacer_4">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>122</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item row="4" column="4" rowspan="2">
+         <widget class="QGroupBox" name="groupBox_12">
+          <property name="font">
+           <font>
+            <pointsize>15</pointsize>
+           </font>
+          </property>
           <property name="title">
-           <string/>
+           <string>Plugin</string>
           </property>
-          <layout class="QGridLayout" name="gridLayout_15">
+          <layout class="QGridLayout" name="gridLayout_8">
            <item row="0" column="0">
-            <widget class="QPushButton" name="pB_add_g4ui">
+            <widget class="QLabel" name="label_18">
+             <property name="font">
+              <font>
+               <pointsize>15</pointsize>
+              </font>
+             </property>
              <property name="text">
-              <string>Add Command</string>
+              <string>Generator Plugin</string>
              </property>
             </widget>
            </item>
            <item row="0" column="1">
-            <widget class="QLineEdit" name="lE_g4ui"/>
+            <widget class="QPushButton" name="pB_gen_plug_browse_files">
+             <property name="text">
+              <string>Browse Files</string>
+             </property>
+            </widget>
            </item>
           </layout>
          </widget>
         </item>
-        <item row="3" column="0" colspan="6">
-         <widget class="QListView" name="g4ui_view"/>
-        </item>
-        <item row="1" column="3" colspan="3">
-         <spacer name="horizontalSpacer_11">
+        <item row="5" column="3">
+         <spacer name="horizontalSpacer_5">
           <property name="orientation">
            <enum>Qt::Horizontal</enum>
           </property>
           <property name="sizeHint" stdset="0">
            <size>
-            <width>361</width>
+            <width>186</width>
             <height>20</height>
            </size>
           </property>
          </spacer>
         </item>
         <item row="5" column="5">
-         <spacer name="verticalSpacer_6">
-          <property name="orientation">
-           <enum>Qt::Vertical</enum>
-          </property>
-          <property name="sizeHint" stdset="0">
-           <size>
-            <width>20</width>
-            <height>58</height>
-           </size>
-          </property>
-         </spacer>
-        </item>
-        <item row="0" column="2">
-         <spacer name="verticalSpacer_5">
-          <property name="orientation">
-           <enum>Qt::Vertical</enum>
-          </property>
-          <property name="sizeHint" stdset="0">
-           <size>
-            <width>20</width>
-            <height>108</height>
-           </size>
-          </property>
-         </spacer>
-        </item>
-        <item row="4" column="4">
-         <spacer name="horizontalSpacer_12">
+         <spacer name="horizontalSpacer_20">
           <property name="orientation">
            <enum>Qt::Horizontal</enum>
           </property>
           <property name="sizeHint" stdset="0">
            <size>
-            <width>275</width>
+            <width>104</width>
             <height>20</height>
            </size>
           </property>
          </spacer>
         </item>
-        <item row="4" column="3" rowspan="2">
-         <spacer name="verticalSpacer_4">
+        <item row="6" column="2">
+         <spacer name="verticalSpacer_10">
           <property name="orientation">
            <enum>Qt::Vertical</enum>
           </property>
           <property name="sizeHint" stdset="0">
            <size>
             <width>20</width>
-            <height>95</height>
+            <height>32</height>
            </size>
           </property>
          </spacer>
         </item>
-        <item row="4" column="5">
-         <widget class="QPushButton" name="pB_del_g4ui">
-          <property name="text">
-           <string>−</string>
-          </property>
-         </widget>
-        </item>
-        <item row="1" column="0">
-         <spacer name="horizontalSpacer_9">
+        <item row="6" column="4">
+         <spacer name="verticalSpacer_11">
           <property name="orientation">
-           <enum>Qt::Horizontal</enum>
+           <enum>Qt::Vertical</enum>
           </property>
           <property name="sizeHint" stdset="0">
            <size>
-            <width>320</width>
-            <height>20</height>
+            <width>20</width>
+            <height>106</height>
            </size>
           </property>
          </spacer>
         </item>
-        <item row="1" column="1">
-         <widget class="QLabel" name="label_45">
-          <property name="font">
-           <font>
-            <pointsize>16</pointsize>
-           </font>
-          </property>
-          <property name="text">
-           <string>Additional G4Ui Commands</string>
-          </property>
-         </widget>
-        </item>
        </layout>
       </widget>
       <widget class="QWidget" name="Mag_Field">
@@ -1078,44 +1055,6 @@
         <string>Regions</string>
        </attribute>
        <layout class="QGridLayout" name="gridLayout_5">
-        <item row="0" column="0" colspan="2">
-         <spacer name="horizontalSpacer_7">
-          <property name="orientation">
-           <enum>Qt::Horizontal</enum>
-          </property>
-          <property name="sizeHint" stdset="0">
-           <size>
-            <width>405</width>
-            <height>20</height>
-           </size>
-          </property>
-         </spacer>
-        </item>
-        <item row="0" column="2">
-         <widget class="QLabel" name="label_43">
-          <property name="font">
-           <font>
-            <pointsize>15</pointsize>
-           </font>
-          </property>
-          <property name="text">
-           <string>Regions</string>
-          </property>
-         </widget>
-        </item>
-        <item row="0" column="3">
-         <spacer name="horizontalSpacer_8">
-          <property name="orientation">
-           <enum>Qt::Horizontal</enum>
-          </property>
-          <property name="sizeHint" stdset="0">
-           <size>
-            <width>458</width>
-            <height>20</height>
-           </size>
-          </property>
-         </spacer>
-        </item>
         <item row="1" column="0" colspan="4">
          <widget class="QTableView" name="regions_table"/>
         </item>
@@ -1150,70 +1089,26 @@
           </property>
          </spacer>
         </item>
-       </layout>
-      </widget>
-      <widget class="QWidget" name="Sens_det">
-       <attribute name="title">
-        <string>Sensitive Detectors</string>
-       </attribute>
-       <layout class="QGridLayout" name="gridLayout_7">
         <item row="0" column="0">
-         <widget class="QLabel" name="label_33">
-          <property name="font">
-           <font>
-            <pointsize>15</pointsize>
-           </font>
-          </property>
-          <property name="text">
-           <string>Output Hits</string>
-          </property>
-         </widget>
-        </item>
-        <item row="0" column="1">
-         <widget class="QLineEdit" name="lE_hits"/>
-        </item>
-        <item row="0" column="2" rowspan="2">
-         <spacer name="verticalSpacer_7">
-          <property name="orientation">
-           <enum>Qt::Vertical</enum>
-          </property>
-          <property name="sizeHint" stdset="0">
-           <size>
-            <width>20</width>
-            <height>49</height>
-           </size>
-          </property>
-         </spacer>
-        </item>
-        <item row="1" column="0">
-         <widget class="QLabel" name="label_34">
+         <widget class="QLabel" name="label_43">
           <property name="font">
            <font>
             <pointsize>15</pointsize>
            </font>
           </property>
           <property name="text">
-           <string>Output Histo</string>
+           <string>Regions</string>
           </property>
          </widget>
         </item>
-        <item row="1" column="1">
-         <widget class="QLineEdit" name="lE_histo"/>
-        </item>
-        <item row="2" column="0" colspan="2">
-         <spacer name="horizontalSpacer_13">
-          <property name="orientation">
-           <enum>Qt::Horizontal</enum>
-          </property>
-          <property name="sizeHint" stdset="0">
-           <size>
-            <width>332</width>
-            <height>20</height>
-           </size>
-          </property>
-         </spacer>
-        </item>
-        <item row="2" column="2">
+       </layout>
+      </widget>
+      <widget class="QWidget" name="Sens_det">
+       <attribute name="title">
+        <string>Sensitive Detectors</string>
+       </attribute>
+       <layout class="QGridLayout" name="gridLayout_6">
+        <item row="0" column="0" colspan="2">
          <widget class="QLabel" name="label_27">
           <property name="font">
            <font>
@@ -1225,23 +1120,10 @@
           </property>
          </widget>
         </item>
-        <item row="2" column="3">
-         <spacer name="horizontalSpacer_14">
-          <property name="orientation">
-           <enum>Qt::Horizontal</enum>
-          </property>
-          <property name="sizeHint" stdset="0">
-           <size>
-            <width>378</width>
-            <height>20</height>
-           </size>
-          </property>
-         </spacer>
-        </item>
-        <item row="3" column="0" colspan="4">
-         <widget class="QListView" name="sens_det_view"/>
+        <item row="1" column="0" colspan="2">
+         <widget class="QTableView" name="sens_det_table"/>
         </item>
-        <item row="4" column="0">
+        <item row="2" column="0">
          <layout class="QHBoxLayout" name="horizontalLayout_8">
           <item>
            <widget class="QPushButton" name="pB_add_sens_det">
@@ -1259,7 +1141,7 @@
           </item>
          </layout>
         </item>
-        <item row="4" column="1" colspan="3">
+        <item row="2" column="1">
          <spacer name="horizontalSpacer_19">
           <property name="orientation">
            <enum>Qt::Horizontal</enum>
@@ -1279,20 +1161,7 @@
         <string>User Actions</string>
        </attribute>
        <layout class="QGridLayout" name="gridLayout_4">
-        <item row="0" column="1">
-         <spacer name="horizontalSpacer_2">
-          <property name="orientation">
-           <enum>Qt::Horizontal</enum>
-          </property>
-          <property name="sizeHint" stdset="0">
-           <size>
-            <width>362</width>
-            <height>20</height>
-           </size>
-          </property>
-         </spacer>
-        </item>
-        <item row="0" column="2">
+        <item row="0" column="0" colspan="2">
          <widget class="QLabel" name="label_25">
           <property name="font">
            <font>
@@ -1304,18 +1173,8 @@
           </property>
          </widget>
         </item>
-        <item row="0" column="3">
-         <spacer name="horizontalSpacer_3">
-          <property name="orientation">
-           <enum>Qt::Horizontal</enum>
-          </property>
-          <property name="sizeHint" stdset="0">
-           <size>
-            <width>144</width>
-            <height>20</height>
-           </size>
-          </property>
-         </spacer>
+        <item row="1" column="0" colspan="2">
+         <widget class="QTableView" name="user_action_table"/>
         </item>
         <item row="2" column="0">
          <layout class="QHBoxLayout" name="horizontalLayout_12">
@@ -1335,7 +1194,7 @@
           </item>
          </layout>
         </item>
-        <item row="2" column="1" colspan="3">
+        <item row="2" column="1">
          <spacer name="horizontalSpacer_24">
           <property name="orientation">
            <enum>Qt::Horizontal</enum>
@@ -1348,9 +1207,6 @@
           </property>
          </spacer>
         </item>
-        <item row="1" column="0" colspan="4">
-         <widget class="QTableView" name="user_action_table"/>
-        </item>
        </layout>
       </widget>
      </widget>
@@ -1362,7 +1218,7 @@
     <rect>
      <x>0</x>
      <y>0</y>
-     <width>1261</width>
+     <width>1355</width>
      <height>24</height>
     </rect>
    </property>
@@ -1406,6 +1262,13 @@
    </property>
   </action>
  </widget>
+ <customwidgets>
+  <customwidget>
+   <class>BetterTextBrowser</class>
+   <extends>QTextBrowser</extends>
+   <header>../FSL/src/BetterTextBrowser.h</header>
+  </customwidget>
+ </customwidgets>
  <resources/>
  <connections/>
 </ui>
diff --git a/FSL/src/main.cpp b/FSL/src/main.cpp
index e87b71ddedc08cc2751586563e9ed6122a0c39a7..d193ee356f990bed17c8519eb059a718dd7e0352 100644
--- a/FSL/src/main.cpp
+++ b/FSL/src/main.cpp
@@ -1,5 +1,5 @@
 #include "fsl_mainwindow.h"
-
+#include <iostream>
 #include <QApplication>
 
 int main(int argc, char *argv[])
@@ -7,6 +7,28 @@ int main(int argc, char *argv[])
     QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
     QApplication a(argc, argv);
     FSLMainWindow w;
+    
+    
+    if(argc>1 && std::string(argv[1]) == "-c")
+    {
+    w.show();
+    w.load_configuration_CL(std::string(argv[2]));
+    }
+    
+    else if(argc>1 && std::string(argv[1]) != "-c")
+    {
+    std::cout << std::endl;
+    std::cerr << "Usage:" << std::endl;
+    std::cerr << "fsl" << std::endl;
+    std::cerr << "fsl -c /path/to/ConfigFile" << std::endl;
+    std::cout << std::endl;
+    exit(-1);
+    }
+    
+    else
+    {
     w.show();
+    }
+    
     return a.exec();
 }
diff --git a/FullSimLight/CMakeLists.txt b/FullSimLight/CMakeLists.txt
index 4eb3ca1aca317a8e2ecf41e9793aba938ab06e08..e3cb021a129c1b867b8b7628b1aed917d1f64643 100644
--- a/FullSimLight/CMakeLists.txt
+++ b/FullSimLight/CMakeLists.txt
@@ -55,6 +55,10 @@ message( STATUS "Found Geant4: ${Geant4_INCLUDE_DIR}")
 #----------------------------------------------------------------------------
 
 find_package(Pythia QUIET) # optional
+find_package(HepMC3 REQUIRED)
+#find_package(ROOT QUIET) #optional
+#option(HEPMC3_ROOTIO_USE "Enable the use of HEPMC3 with ROOT" OFF)
+
 
 #----------------------------------------------------------------------------
 # Setup Geant4 include directories and compile definitions
@@ -117,6 +121,26 @@ if(Pythia_FOUND)
   target_link_libraries(fullSimLight PRIVATE Pythia::Pythia)
 endif()
 
+#if(HEPMC3_ROOTIO_USE)
+#  message( STATUS "HEPMC3 Root Dependency Switched on")
+#  target_link_libraries(gmclash PUBLIC  ${HEPMC3_ROOTIO_LIB} ${ROOT_LIBRARIES})
+ # target_link_libraries(gmmasscalc PUBLIC  ${HEPMC3_ROOTIO_LIB} ${ROOT_LIBRARIES})
+ # target_link_libraries(fullSimLight PUBLIC ${HEPMC3_ROOTIO_LIB} ${ROOT_LIBRARIES})
+ # target_link_libraries(gmgeantino PUBLIC  ${HEPMC3_ROOTIO_LIB} ${ROOT_LIBRARIES})
+ # target_link_libraries(gm2gdml PUBLIC ${HEPMC3_ROOTIO_LIB} ${ROOT_LIBRARIES})
+ # target_include_directories(gmclash PUBLIC ${ROOT_INCLUDE_DIRS})
+ # target_include_directories(gmmasscalc PUBLIC  ${ROOT_INCLUDE_DIRS})
+ # target_include_directories(fullSimLight PUBLIC  ${ROOT_INCLUDE_DIRS})
+ # target_include_directories(gmgeantino PUBLIC  ${ROOT_INCLUDE_DIRS})
+ # target_include_directories(gm2gdml PUBLIC  ${ROOT_INCLUDE_DIRS})
+
+ # target_compile_definitions(gmclash PRIVATE USE_ROOT)
+  #target_compile_definitions(gmmasscalc PRIVATE USE_ROOT)
+ # target_compile_definitions(fullSimLight PRIVATE USE_ROOT)
+ # target_compile_definitions(gmgeantino PRIVATE USE_ROOT)
+ # target_compile_definitions(gm2gdml PRIVATE USE_ROOT)
+#endif()
+
 #----------------------------------------------------------------------------
 # Add extra 'include' directories
 #
@@ -152,11 +176,17 @@ target_link_libraries(testMagneticField ${Geant4_LIBRARIES} MagFieldServices Mag
 #target_link_libraries(plotGeantinoMaps ${ROOT_LIBRARIES})
 
 
-target_link_libraries(gmclash PUBLIC GeoModel2G4 ${Geant4_LIBRARIES} MagFieldServices MagFieldInterfaces)
-target_link_libraries(gmmasscalc PUBLIC GeoModel2G4 ${Geant4_LIBRARIES} MagFieldServices MagFieldInterfaces)
-target_link_libraries(fullSimLight PUBLIC GeoModel2G4 ${Geant4_LIBRARIES} MagFieldServices MagFieldInterfaces)
-target_link_libraries(gmgeantino PUBLIC GeoModel2G4 ${Geant4_LIBRARIES} MagFieldServices MagFieldInterfaces)
-target_link_libraries(gm2gdml PUBLIC GeoModel2G4 ${Geant4_LIBRARIES} MagFieldServices MagFieldInterfaces)
+target_link_libraries(gmclash PUBLIC GeoModel2G4 ${Geant4_LIBRARIES} MagFieldServices MagFieldInterfaces ${HEPMC3_LIB})
+target_link_libraries(gmmasscalc PUBLIC GeoModel2G4 ${Geant4_LIBRARIES} MagFieldServices MagFieldInterfaces ${HEPMC3_LIB})
+target_link_libraries(fullSimLight PUBLIC GeoModel2G4 ${Geant4_LIBRARIES} MagFieldServices MagFieldInterfaces ${HEPMC3_LIB})
+target_link_libraries(gmgeantino PUBLIC GeoModel2G4 ${Geant4_LIBRARIES} MagFieldServices MagFieldInterfaces ${HEPMC3_LIB})
+target_link_libraries(gm2gdml PUBLIC GeoModel2G4 ${Geant4_LIBRARIES} MagFieldServices MagFieldInterfaces ${HEPMC3_LIB})
+
+target_include_directories(gmclash PUBLIC ${HEPMC3_INCLUDE_DIR})
+target_include_directories(gmmasscalc PUBLIC ${HEPMC3_INCLUDE_DIR})
+target_include_directories(fullSimLight PUBLIC ${HEPMC3_INCLUDE_DIR})
+target_include_directories(gmgeantino PUBLIC ${HEPMC3_INCLUDE_DIR})
+target_include_directories(gm2gdml PUBLIC ${HEPMC3_INCLUDE_DIR})
 
 # Check if we are building FullSimLight individually,
 # or as a part of the main GeoModel.
@@ -292,15 +322,15 @@ install( TARGETS FullSimLight
    INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} )
 
    
-install(FILES include/FSLSensitiveDetectorPlugin.h 
-              include/MagFieldPlugin.h 
-              include/FSLPhysicsListPlugin.h
-	      include/FSLUserActionPlugin.h
-              include/FSLUserRunActionPlugin.h
-              include/FSLUserEventActionPlugin.h
-              include/FSLUserSteppingActionPlugin.h
-              include/FSLUserTrackingActionPlugin.h
-              include/FSLUserStackingActionPlugin.h
+install(FILES FullSimLight/FSLSensitiveDetectorPlugin.h 
+              FullSimLight/MagFieldPlugin.h 
+              FullSimLight/FSLPhysicsListPlugin.h
+	      FullSimLight/FSLUserActionPlugin.h
+              FullSimLight/FSLUserRunActionPlugin.h
+              FullSimLight/FSLUserEventActionPlugin.h
+              FullSimLight/FSLUserSteppingActionPlugin.h
+              FullSimLight/FSLUserTrackingActionPlugin.h
+              FullSimLight/FSLUserStackingActionPlugin.h
 	          DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/FullSimLight
   COMPONENT Development )
 
diff --git a/FullSimLight/include/FSLPhysicsListPlugin.h b/FullSimLight/FullSimLight/FSLPhysicsListPlugin.h
similarity index 100%
rename from FullSimLight/include/FSLPhysicsListPlugin.h
rename to FullSimLight/FullSimLight/FSLPhysicsListPlugin.h
diff --git a/FullSimLight/include/FSLSensitiveDetectorPlugin.h b/FullSimLight/FullSimLight/FSLSensitiveDetectorPlugin.h
similarity index 100%
rename from FullSimLight/include/FSLSensitiveDetectorPlugin.h
rename to FullSimLight/FullSimLight/FSLSensitiveDetectorPlugin.h
diff --git a/FullSimLight/include/FSLUserActionPlugin.h b/FullSimLight/FullSimLight/FSLUserActionPlugin.h
similarity index 88%
rename from FullSimLight/include/FSLUserActionPlugin.h
rename to FullSimLight/FullSimLight/FSLUserActionPlugin.h
index 729c260e5acc3d98af8bf29b3cab2abcc6cbf3a2..9b1318da895d96d94ff9b42201288d9a75aad91c 100644
--- a/FullSimLight/include/FSLUserActionPlugin.h
+++ b/FullSimLight/FullSimLight/FSLUserActionPlugin.h
@@ -5,6 +5,7 @@ class G4UserRunAction;
 class G4UserSteppingAction;
 class G4UserTrackingAction;
 class G4UserStackingAction;
+class G4VUserPrimaryGeneratorAction;
 
 class FSLUserActionPlugin {
  
@@ -23,6 +24,8 @@ class FSLUserActionPlugin {
   virtual G4UserSteppingAction *getSteppingAction() const { return nullptr;}
   virtual G4UserTrackingAction *getTrackingAction() const { return nullptr;}
   virtual G4UserStackingAction *getStackingAction() const { return nullptr;}
+  virtual G4VUserPrimaryGeneratorAction *getPrimaryGeneratorAction() const {return nullptr;}
+
 
   
  private:
diff --git a/FullSimLight/include/FSLUserEventActionPlugin.h b/FullSimLight/FullSimLight/FSLUserEventActionPlugin.h
similarity index 100%
rename from FullSimLight/include/FSLUserEventActionPlugin.h
rename to FullSimLight/FullSimLight/FSLUserEventActionPlugin.h
diff --git a/FullSimLight/include/FSLUserRunActionPlugin.h b/FullSimLight/FullSimLight/FSLUserRunActionPlugin.h
similarity index 100%
rename from FullSimLight/include/FSLUserRunActionPlugin.h
rename to FullSimLight/FullSimLight/FSLUserRunActionPlugin.h
diff --git a/FullSimLight/include/FSLUserStackingActionPlugin.h b/FullSimLight/FullSimLight/FSLUserStackingActionPlugin.h
similarity index 100%
rename from FullSimLight/include/FSLUserStackingActionPlugin.h
rename to FullSimLight/FullSimLight/FSLUserStackingActionPlugin.h
diff --git a/FullSimLight/include/FSLUserSteppingActionPlugin.h b/FullSimLight/FullSimLight/FSLUserSteppingActionPlugin.h
similarity index 100%
rename from FullSimLight/include/FSLUserSteppingActionPlugin.h
rename to FullSimLight/FullSimLight/FSLUserSteppingActionPlugin.h
diff --git a/FullSimLight/include/FSLUserTrackingActionPlugin.h b/FullSimLight/FullSimLight/FSLUserTrackingActionPlugin.h
similarity index 100%
rename from FullSimLight/include/FSLUserTrackingActionPlugin.h
rename to FullSimLight/FullSimLight/FSLUserTrackingActionPlugin.h
diff --git a/FullSimLight/include/MagFieldPlugin.h b/FullSimLight/FullSimLight/MagFieldPlugin.h
similarity index 85%
rename from FullSimLight/include/MagFieldPlugin.h
rename to FullSimLight/FullSimLight/MagFieldPlugin.h
index 7bd6cf15d7829ec7530cf8ed56ef885375621aef..eb2f0af05f8385af563408685673fca94266f2c0 100644
--- a/FullSimLight/include/MagFieldPlugin.h
+++ b/FullSimLight/FullSimLight/MagFieldPlugin.h
@@ -11,7 +11,7 @@ class MagFieldPlugin {
   // Destructor
   virtual ~MagFieldPlugin()=default;
   
-  virtual const G4MagneticField *getField() const=0;
+  virtual G4MagneticField *getField(std::string map_path) =0;
   
  private:
   
diff --git a/FullSimLight/fullSimLight.cc b/FullSimLight/fullSimLight.cc
index ee219783bcf5611bf1bd8313702455cd594fe1ca..7105c247f9f1ed0598140430c2a87ee03e8db123 100644
--- a/FullSimLight/fullSimLight.cc
+++ b/FullSimLight/fullSimLight.cc
@@ -24,7 +24,7 @@
 #include "FSLActionInitialization.hh"
 #include "FSLConfigurator.hh"
 #include "PythiaPrimaryGeneratorAction.hh"
-#include "FSLUserActionPlugin.h"
+#include "FullSimLight/FSLUserActionPlugin.h"
 #include <getopt.h>
 #include <err.h>
 #include <iostream>
@@ -189,6 +189,14 @@ int main(int argc, char** argv) {
                        );
 
     }
+    
+    if(!isBatch)
+    {
+        actInit->SetGenerator(simConfig::fsl.eventGeneratorName);
+        actInit->SetHepMC3FilePath(simConfig::fsl.hepmc3InputFile);
+        actInit->SetHepMC3FileType(simConfig::fsl.hepmc3TypeOfFile);
+        actInit->SetGeneratorPlugin(simConfig::fsl.generatorPlugin);
+    }
 
     // set the name of a region in which we are interested to see a very basic simulation
     // stat e.g. "EMEC" (NOTE: only if the given region can be found and executed in
@@ -224,6 +232,12 @@ int main(int argc, char** argv) {
         
         detector->SetGeometryFileName (simConfig::fsl.geometry);
         runManager->SetUserInitialization(detector);
+        
+        if(simConfig::fsl.magFieldType=="Map")
+        {
+        detector->SetMagFieldPluginPath(simConfig::fsl.magFieldPlugin);
+        detector->SetMagFieldMapPath(simConfig::fsl.magFieldMap);
+        }
                 
         
         //parse RegionsData
@@ -288,7 +302,7 @@ int main(int argc, char** argv) {
         << "   Geometry file      =  " << simConfig::fsl.geometry                    << G4endl
         << "   Physics list name  =  " << simConfig::fsl.physicsList                 << G4endl
         << "   Generator          =  " << simConfig::fsl.eventGeneratorName          << G4endl
-        << "   Magnetic Field     =  " << simConfig::fsl.magFieldIntensity           << G4endl
+        << "   Magnetic Field     =  " << simConfig::fsl.magFieldType           << G4endl
         << "   Run Overlap Check  =  " << parRunOverlapCheck              << G4endl
         << " ===================================================== "      << G4endl;
         
diff --git a/FullSimLight/include/FSLActionInitialization.hh b/FullSimLight/include/FSLActionInitialization.hh
index 08675e5047380fa180993b5f66410e6a8483ca05..217308a03eafb635ade008303f665d43c9e1c7e1 100644
--- a/FullSimLight/include/FSLActionInitialization.hh
+++ b/FullSimLight/include/FSLActionInitialization.hh
@@ -23,13 +23,33 @@ public:
     {
       userActions = usrActions;
     };
+  void SetGenerator(std::string gen)
+    {
+      generator = gen;
+    }
+  void SetHepMC3FilePath(std::string file_path)
+    {
+      hepmc3_file_path = file_path;
+    }
+  void SetHepMC3FileType(std::string file_type)
+    {
+      hepmc3_file_type = file_type;
+    }
+  void SetGeneratorPlugin(std::string gen_plug)
+    {
+      generator_plugin = gen_plug;
+    }
     
 private:
   GeantinoMapsConfigurator* fGeantinoMapsConfig;
   bool     fIsPerformance;
   bool     fCustomUserActions;
   G4String fSpecialScoringRegionName;
-    
+  std::string generator;
+  std::string hepmc3_file_path = "";
+  std::string hepmc3_file_type = "";
+  std::string generator_plugin = "";
+
     
   std::vector<std::string> userActions;
     
diff --git a/FullSimLight/include/FSLConfigurator.hh b/FullSimLight/include/FSLConfigurator.hh
index bc678af605db6c47fcfae46360a3c41da9fa9ce9..54160424622c32cc633e09b8e2b4c3161044aba1 100644
--- a/FullSimLight/include/FSLConfigurator.hh
+++ b/FullSimLight/include/FSLConfigurator.hh
@@ -50,14 +50,19 @@ struct fslConfig{
     std::string eventInputFile;
     std::string typeOfEvent;
     
+    std::string hepmc3InputFile;
+    std::string hepmc3TypeOfFile;
+    
+    std::string generatorPlugin;
+    
     std::vector<std::string> sensitiveDetectors;
-    std::string outputHitsFile;
-    std::string outputHistoFile;
+ //   std::string outputHitsFile;
+ //   std::string outputHistoFile;
     
     std::vector<regionConfig> regionsData;
     
     std::string magFieldType;
-    std::string magFieldIntensity;
+  //  std::string magFieldIntensity;
     std::string magFieldMap;
     std::string magFieldPlugin;
     
@@ -72,7 +77,10 @@ regionConfig rc;
 json jf;
 
 inline void to_json(json& j, const fslConfig& p) {
-    j = json{{"Geometry", p.geometry},{"Physics list name", p.physicsList},{"Number of events", p.nEvents},{"Magnetic Field Intensity", p.magFieldIntensity},{"Generator", p.eventGeneratorName},{"Event input file", p.eventInputFile},{"Type of event", p.typeOfEvent},{"Sensitive Detector Extensions", p.sensitiveDetectors},{"Output Hits file", p.outputHitsFile},{"Output Histo file", p.outputHistoFile},{"Magnetic Field Type", p.magFieldType},{"Magnetic Field Map", p.magFieldMap},{"Magnetic Field Plugin", p.magFieldPlugin},{"User Action Extensions", p.userActions},{"g4ui_commands", p.g4UiCommands}};
+    j = json{{"Geometry", p.geometry},{"Physics list name", p.physicsList},{"Number of events", p.nEvents},{"Generator", p.eventGeneratorName},{"Pythia event input file", p.eventInputFile},{"Pythia type of event", p.typeOfEvent},{"Sensitive Detector Extensions", p.sensitiveDetectors},{"Magnetic Field Type", p.magFieldType},{"Magnetic Field Map", p.magFieldMap},{"Magnetic Field Plugin", p.magFieldPlugin},{"User Action Extensions", p.userActions},{"g4ui_commands", p.g4UiCommands},
+        {"HepMC3 file", p.hepmc3InputFile}, {"HepMC3 type of file", p.hepmc3TypeOfFile},
+        {"Generator Plugin", p.generatorPlugin}
+    };
     
 }
 inline void to_json(json& j, const regionConfig& r) {
@@ -83,19 +91,17 @@ inline void from_json(const json& j, fslConfig& p) {
     p.physicsList=j.at("Physics list name").get<std::string>();
     p.nEvents=j.at("Number of events").get<int>();
     p.magFieldType=j.at("Magnetic Field Type").get<std::string>();
-    p.magFieldIntensity=j.at("Magnetic Field Intensity").get<std::string>();
     p.eventGeneratorName=j.at("Generator").get<std::string>();
-    p.eventInputFile=j.at("Event input file").get<std::string>();
-    p.typeOfEvent=j.at("Type of event").get<std::string>();
+    p.eventInputFile=j.at("Pythia event input file").get<std::string>();
+    p.typeOfEvent=j.at("Pythia type of event").get<std::string>();
     p.sensitiveDetectors=j.at("Sensitive Detector Extensions").get<std::vector<std::string>>();
-    p.outputHitsFile=j.at("Output Hits file").get<std::string>();
-    p.outputHistoFile=j.at("Output Histo file").get<std::string>();
     p.magFieldMap=j.at("Magnetic Field Map").get<std::string>();
     p.magFieldPlugin=j.at("Magnetic Field Plugin").get<std::string>();
     p.userActions=j.at("User Action Extensions").get<std::vector<std::string>>();
-
     p.g4UiCommands=j.at("g4ui_commands").get<std::vector<std::string>>();
-    
+    p.hepmc3InputFile = j.at("HepMC3 file").get<std::string>();
+    p.hepmc3TypeOfFile = j.at("HepMC3 type of file").get<std::string>();
+    p.generatorPlugin = j.at("Generator Plugin").get<std::string>();
 }
 
 inline void from_json(const json& j, regionConfig& r) {
diff --git a/FullSimLight/include/FSLDetectorConstruction.hh b/FullSimLight/include/FSLDetectorConstruction.hh
index cdc727f3d504a05af38fa79a6b853230f0e36231..997224bfb0ee4bff7e84d8dd16dfc241cb22d180 100644
--- a/FullSimLight/include/FSLDetectorConstruction.hh
+++ b/FullSimLight/include/FSLDetectorConstruction.hh
@@ -65,6 +65,8 @@ public:
   }
 
   void AddSensitiveDetectorPlugin(const std::string & SDPluginName) { sensitiveDetectorPluginName.push_back(SDPluginName);}
+  void SetMagFieldPluginPath(const std::string &MFPluginName) {mag_field_plugin_path = MFPluginName;}
+  void SetMagFieldMapPath(const std::string &MFPathName){mag_field_map_path = MFPathName;}
     
   void ConfigureRegionsFSL(std::vector<std::string> reg,
                            std::vector<std::vector<G4String>> root_lv_names,
@@ -119,6 +121,8 @@ private:
 
 
   std::vector<std::string> sensitiveDetectorPluginName;
+  std::string mag_field_plugin_path;
+  std::string mag_field_map_path;
   
   //Regions Data
     std::vector<std::string> Regions;
diff --git a/FullSimLight/include/HepMC3G4AsciiReader.hh b/FullSimLight/include/HepMC3G4AsciiReader.hh
new file mode 100644
index 0000000000000000000000000000000000000000..c97669533a8c325b58d5922cba14024aac40538b
--- /dev/null
+++ b/FullSimLight/include/HepMC3G4AsciiReader.hh
@@ -0,0 +1,65 @@
+
+#ifndef HEPMC3_G4_ASCII_READER_H
+#define HEPMC3_G4_ASCII_READER_H
+
+#include "HepMC3G4Interface.hh"
+#include "HepMC3/GenEvent.h"
+#include "HepMC3/Print.h"
+#include "HepMC3/Units.h"
+#include "G4AutoLock.hh"
+#include "HepMC3/ReaderAsciiHepMC2.h"
+#include "HepMC3/ReaderAscii.h"
+
+
+/*#if USE_ROOT
+#include "HepMC3/ReaderRoot.h"
+#endif*/
+
+namespace {
+G4Mutex mutino = G4MUTEX_INITIALIZER;
+
+}
+class HepMC3G4AsciiReader : public HepMC3G4Interface {
+    
+public:
+    
+    ~HepMC3G4AsciiReader();
+    static HepMC3G4AsciiReader * GetInstance(G4String name,G4String reader_type)
+    {
+        if (m_pOnlyOneInstance == NULL)
+        {
+            G4cout<<"Going to lock in HepMC3G4AsciiReader::GetInstance"<<G4endl;
+            G4AutoLock lock(mutino);
+            G4cout<<"HepMC3G4AsciiReader::GetInstance MUTEX IS MINE"<<G4endl;
+            if (m_pOnlyOneInstance == NULL)
+                // Solution 1 and 2 gaps addressed by moving
+                // critical section block and by re-doing this check!
+            {
+                m_pOnlyOneInstance = new HepMC3G4AsciiReader(name,reader_type);
+            }
+            lock.unlock();
+            G4cout<<"HepMC3G4AsciiReader::GetInstance MUTEX unlocked"<<G4endl;
+        }
+        return m_pOnlyOneInstance;
+    }
+protected:
+    HepMC3G4AsciiReader(G4String name,G4String reader_type );
+    HepMC3::ReaderAsciiHepMC2* asciiInput;
+    HepMC3::ReaderAscii* asciiv3Input;
+    
+/*#if USE_ROOT
+    HepMC3::ReaderRoot* rootInput;
+#endif*/
+    
+    std::shared_ptr<HepMC3::GenEvent>  evt;
+    //HepMC3::ReaderMT<HepMC3::ReaderAsciiHepMC2,8> *asciiInput;
+    virtual HepMC3::GenEvent* GenerateHepMC3Event() final override;
+private:
+    G4String reader;
+    static HepMC3G4AsciiReader * m_pOnlyOneInstance;
+    
+};
+
+
+#endif
+
diff --git a/FullSimLight/include/HepMC3G4Interface.hh b/FullSimLight/include/HepMC3G4Interface.hh
new file mode 100644
index 0000000000000000000000000000000000000000..d3d43e2b21c164843540006618b6e2abcbf5038e
--- /dev/null
+++ b/FullSimLight/include/HepMC3G4Interface.hh
@@ -0,0 +1,53 @@
+
+#ifndef HEPMC3_G4_INTERFACE_H
+#define HEPMC3_G4_INTERFACE_H
+
+#include "G4VPrimaryGenerator.hh"
+#include "HepMC3/GenEvent.h"
+#include "HepMC3/GenVertex.h"
+#include "HepMC3/GenParticle.h"
+
+
+/// A base class for primary generation via HepMC3 object.
+/// This class is derived from G4VPrimaryGenerator.
+
+class HepMC3G4Interface : public G4VPrimaryGenerator {
+protected:
+  // Note that the life of HepMC3 event object must be handled by users.
+  // In the default implementation, a current HepMC event will be
+  // deleted at GeneratePrimaryVertex() in the next event.
+  HepMC3::GenEvent* hepmcEvent; // (care for single event case only)
+
+  // We  have to take care for the position of primaries because
+  // primary vertices outside the world voulme give rise to G4Execption.
+  // If the default implementation is not adequate, an alternative
+  // can be implemented in your own class.
+  virtual G4bool CheckVertexInsideWorld(const G4ThreeVector& pos) const;
+
+  // service method for conversion from HepMC::GenEvent to G4Event
+  void HepMC32G4(const HepMC3::GenEvent* hepmcevt, G4Event* g4event);
+
+  // Implement this method in his/her own concrete class.
+  // An empty event will be created in default.
+  virtual HepMC3::GenEvent* GenerateHepMC3Event();
+
+public:
+  HepMC3G4Interface();
+  virtual ~HepMC3G4Interface();
+
+  // set/get methods
+  HepMC3::GenEvent* GetHepMC3GenEvent() const;
+
+  // The default behavior is that a single HepMC event generated by
+  // GenerateHepMCEvent() will be converted to G4Event through HepMC2G4().
+  virtual void GeneratePrimaryVertex(G4Event* anEvent);
+};
+
+//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
+inline HepMC3::GenEvent* HepMC3G4Interface::GetHepMC3GenEvent() const
+{
+  return hepmcEvent;
+}
+
+#endif
+
diff --git a/FullSimLight/include/HepMC3PrimaryGeneratorAction.hh b/FullSimLight/include/HepMC3PrimaryGeneratorAction.hh
new file mode 100644
index 0000000000000000000000000000000000000000..7e642239db39c65116de3e31b55375b6f0626423
--- /dev/null
+++ b/FullSimLight/include/HepMC3PrimaryGeneratorAction.hh
@@ -0,0 +1,27 @@
+#ifndef HepMC3PrimaryGeneratorAction_h
+#define HepMC3PrimaryGeneratorAction_h 1
+
+#include "G4VUserPrimaryGeneratorAction.hh"
+#include "globals.hh"
+#include "HepMC3G4AsciiReader.hh"
+
+class G4Event;
+class G4VPrimaryGenerator;
+
+class HepMC3PrimaryGeneratorAction : public G4VUserPrimaryGeneratorAction
+
+{
+
+public:
+    HepMC3PrimaryGeneratorAction(std::string file_path,std::string file_type);
+    ~HepMC3PrimaryGeneratorAction();
+    virtual void GeneratePrimaries(G4Event* anEvent);
+
+private:
+    HepMC3G4AsciiReader* HEPMC3_generator;
+
+
+    
+};
+
+#endif
diff --git a/FullSimLight/src/FSLActionInitialization.cc b/FullSimLight/src/FSLActionInitialization.cc
index a7a8c9943af116e9dd3971325b54bbb7f4e587a7..60ce0820bde90b72903dd41c92a490f0b040c728 100644
--- a/FullSimLight/src/FSLActionInitialization.cc
+++ b/FullSimLight/src/FSLActionInitialization.cc
@@ -1,18 +1,18 @@
-
 #include "FSLActionInitialization.hh"
 #include "GeoModelKernel/GeoPluginLoader.h"
 #include "FSLPrimaryGeneratorAction.hh"
+#include "HepMC3PrimaryGeneratorAction.hh"
 #include "FSLRunAction.hh"
 #include "FSLEventAction.hh"
 #include "FSLSteppingAction.hh"
 #include "FSLTrackingAction.hh"
 #include "PythiaPrimaryGeneratorAction.hh"
-#include "FSLUserActionPlugin.h"
-#include "FSLUserRunActionPlugin.h"
-#include "FSLUserEventActionPlugin.h"
-#include "FSLUserStackingActionPlugin.h"
-#include "FSLUserTrackingActionPlugin.h"
-#include "FSLUserSteppingActionPlugin.h"
+#include "FullSimLight/FSLUserActionPlugin.h"
+#include "FullSimLight/FSLUserRunActionPlugin.h"
+#include "FullSimLight/FSLUserEventActionPlugin.h"
+#include "FullSimLight/FSLUserStackingActionPlugin.h"
+#include "FullSimLight/FSLUserTrackingActionPlugin.h"
+#include "FullSimLight/FSLUserSteppingActionPlugin.h"
 
 
 #include "G4Version.hh"
@@ -71,7 +71,31 @@ void FSLActionInitialization::BuildForMaster() const {
 void FSLActionInitialization::Build() const {
 
 #if !USE_PYTHIA
-  SetUserAction(new FSLPrimaryGeneratorAction());
+  
+  if(generator == "HepMC3 File")
+  {
+      std::cout << "Reading in events from file: " << hepmc3_file_path << std::endl;
+      SetUserAction(new HepMC3PrimaryGeneratorAction(hepmc3_file_path,hepmc3_file_type));
+      
+  }
+    
+  else if(generator == "Generator Plugin")
+  {
+      std::cout << "Loading in event generator from plugin" << std::endl;
+      GeoPluginLoader<FSLUserActionPlugin> generator_loader;
+      const FSLUserActionPlugin * gen_plugin = generator_loader.load(generator_plugin);
+      
+      if (gen_plugin->getPrimaryGeneratorAction()) SetUserAction(gen_plugin->getPrimaryGeneratorAction());
+      else
+      {
+          std::cout << "Error loading in Plugin, exiting now..." << std::endl;
+          exit(-1);
+      }
+      
+  }
+  else
+  {SetUserAction(new FSLPrimaryGeneratorAction());}
+
 #else
   if (use_pythia()) {
     // seed each generator/thread by 1234 if perfomance mode run and use the event
@@ -79,7 +103,30 @@ void FSLActionInitialization::Build() const {
     G4int pythiaSeed = fIsPerformance ? -1 : 0;
     SetUserAction(new PythiaPrimaryGeneratorAction(pythiaSeed));
   } else {
-    SetUserAction(new FSLPrimaryGeneratorAction());
+      if(generator == "HepMC3 File")
+      {
+          std::cout << "Reading in events from file: " << hepmc3_file_path << std::endl;
+          SetUserAction(new HepMC3PrimaryGeneratorAction(hepmc3_file_path,hepmc3_file_type));
+          
+      }
+        
+      else if(generator == "Generator Plugin")
+      {
+          std::cout << "Loading in event generator from plugin" << std::endl;
+          GeoPluginLoader<FSLUserActionPlugin> generator_loader;
+          const FSLUserActionPlugin * gen_plugin = generator_loader.load(generator_plugin);
+          
+          if (gen_plugin->getPrimaryGeneratorAction()) SetUserAction(gen_plugin->getPrimaryGeneratorAction());
+          else
+          {
+              std::cout << "Error loading in Plugin, exiting now..." << std::endl;
+              exit(-1);
+          }
+          
+      }
+      else
+      {SetUserAction(new FSLPrimaryGeneratorAction());}
+
   }
 #endif
 
diff --git a/FullSimLight/src/FSLDetectorConstruction.cc b/FullSimLight/src/FSLDetectorConstruction.cc
index 377126b0d4bb6bc288342d837a52f9f0f62adacf..2864c2d74b7f691219878cba37210fe101b51eaa 100644
--- a/FullSimLight/src/FSLDetectorConstruction.cc
+++ b/FullSimLight/src/FSLDetectorConstruction.cc
@@ -1,6 +1,7 @@
 #include "FSLDetectorConstruction.hh"
 #include "FSLDetectorMessenger.hh"
 #include "RegionConfigurator.hh"
+#include "FullSimLight/MagFieldPlugin.h"
 #include "MassCalculator.hh"
 #include "ClashDetector.hh"
 #include "MagFieldServices/AtlasFieldSvc.h"
@@ -71,7 +72,7 @@
 #include "GeoModelKernel/GeoVolumeCursor.h"
 
 // For Sensitive Detector plugins:
-#include "FSLSensitiveDetectorPlugin.h"
+#include "FullSimLight/FSLSensitiveDetectorPlugin.h"
 #include "FSLSDPluginLoader.h"
 #include "G4VSensitiveDetector.hh"
 #include "G4LogicalVolumeStore.hh"
@@ -410,14 +411,26 @@ void FSLDetectorConstruction::ConstructSDandField()
         G4cout << G4endl << " *** MAGNETIC FIELD IS OFF  *** " << G4endl << G4endl;
     }
     else // if (!fFieldConstant)
-    {
-      G4cout << G4endl << " *** MAGNETIC FIELD SET FROM FILE  *** " << G4endl << G4endl;
-      if (fField.Get() == 0)
-      {
-          StandardFieldSvc* FSLMagField = new StandardFieldSvc("StandardFieldSvc");
-          G4MagneticField* g4Field =  FSLMagField->getField();
-          if(g4Field==nullptr) std::cout<<"Error, g4Field is null!"<<std::endl;
-          fField.Put(g4Field);
+        {
+          G4cout << G4endl << " *** MAGNETIC FIELD SET FROM FILE  *** " << G4endl << G4endl;
+          if (fField.Get() == 0)
+          {
+              if(mag_field_plugin_path == "")
+              {
+              StandardFieldSvc* FSLMagField = new StandardFieldSvc("StandardFieldSvc");
+              G4MagneticField* g4Field =  FSLMagField->getField();
+              if(g4Field==nullptr) std::cout<<"Error, g4Field is null!"<<std::endl;
+              fField.Put(g4Field);
+              }
+              else
+              {
+              GeoPluginLoader<MagFieldPlugin> loader;
+              MagFieldPlugin *plugin=loader.load(mag_field_plugin_path);
+              G4MagneticField *g4Field=plugin->getField(mag_field_map_path);
+              delete plugin;
+              if(g4Field==nullptr) std::cout<<"Error, g4Field is null!"<<std::endl;
+              fField.Put(g4Field);
+              }
 
           //This is thread-local
           G4FieldManager* fieldMgr =
diff --git a/FullSimLight/src/FSLPhysListFactory.cc b/FullSimLight/src/FSLPhysListFactory.cc
index 34e9584786dd240144e743ef65c31986089bd427..35527802982f64de2bafa464be44ac2a8417cfd9 100644
--- a/FullSimLight/src/FSLPhysListFactory.cc
+++ b/FullSimLight/src/FSLPhysListFactory.cc
@@ -11,7 +11,7 @@
 #include "EmExtraPhysics.hh"
 
 #include "GeoModelKernel/GeoPluginLoader.h"
-#include "FSLPhysicsListPlugin.h"
+#include "FullSimLight/FSLPhysicsListPlugin.h"
 
 #include <string>
 
@@ -107,4 +107,4 @@ G4VModularPhysicsList* FSLPhysListFactory::GetPhysList(const std::string physLis
 
 }
 
-//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
\ No newline at end of file
+//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
diff --git a/FullSimLight/src/HepMC3G4AsciiReader.cc b/FullSimLight/src/HepMC3G4AsciiReader.cc
new file mode 100644
index 0000000000000000000000000000000000000000..91588d2ed06bf1b818063cd4f92ce893e4ded35b
--- /dev/null
+++ b/FullSimLight/src/HepMC3G4AsciiReader.cc
@@ -0,0 +1,100 @@
+
+
+#include "HepMC3G4AsciiReader.hh"
+#include <iostream>
+#include <fstream>
+#include "G4AutoLock.hh"
+
+
+HepMC3G4AsciiReader* HepMC3G4AsciiReader::m_pOnlyOneInstance=nullptr;
+
+//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
+HepMC3G4AsciiReader::HepMC3G4AsciiReader(G4String name,G4String reader_type )
+{
+  //  evt = HepMC3::GenEvent(HepMC3::Units::GEV ,HepMC3::Units::MM);
+    
+reader = reader_type;
+evt= std::make_shared<HepMC3::GenEvent>();
+    
+    //
+    if(reader=="Ascii")
+    {
+        asciiInput = new HepMC3::ReaderAsciiHepMC2(name.c_str());
+    }
+    else if (reader=="Asciiv3")
+    {
+        asciiv3Input = new HepMC3::ReaderAscii(name.c_str());
+    }
+/*#if USE_ROOT
+    else if (reader=="Root")
+    {
+        rootInput = new HepMC3::ReaderRoot(name.c_str());
+    }
+#endif*/
+    else
+    {
+        std::cout << "Unsupported Event File type: Exiting now" << std::endl;
+        exit(-1);
+    }
+    
+//std::cout << "Hey once!!!!!" << std::endl;
+
+  //asciiInput = new HepMC3::ReaderMT<HepMC3::ReaderAsciiHepMC2,8>(filename);
+}
+
+//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
+HepMC3G4AsciiReader::~HepMC3G4AsciiReader()
+{
+    if(reader=="Ascii")
+    {
+        delete asciiInput;
+    }
+    else if (reader=="Asciiv3")
+    {
+        delete asciiv3Input;
+    }
+/*#if USE_ROOT
+    else if (reader=="Root")
+    {
+        delete rootInput;
+    }
+#endif*/
+}
+
+
+//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
+HepMC3::GenEvent* HepMC3G4AsciiReader::GenerateHepMC3Event()
+{
+    evt->clear();
+   // evt = HepMC3::GenEvent(HepMC3::Units::GEV ,HepMC3::Units::MM);
+   // evt= std::make_shared<HepMC3::GenEvent>();
+    
+    if(reader=="Ascii")
+    {
+        asciiInput->read_event(*evt);
+        HepMC3::Print::listing(*evt);
+        HepMC3::Print::content(*evt);
+        if(asciiInput->failed()) return 0;
+    }
+    
+    else if(reader=="Asciiv3")
+    {
+        asciiv3Input->read_event(*evt);
+        HepMC3::Print::listing(*evt);
+        HepMC3::Print::content(*evt);
+        if(asciiv3Input->failed()) return 0;
+    }
+    
+/*#if USE_ROOT
+
+    else if(reader=="Root")
+    {
+        rootInput->read_event(*evt);
+        if(rootInput->failed()) return 0;
+    }
+#endif*/
+ // no more event
+   
+    return evt.get();
+}
+
diff --git a/FullSimLight/src/HepMC3G4Interface.cc b/FullSimLight/src/HepMC3G4Interface.cc
new file mode 100644
index 0000000000000000000000000000000000000000..851d3acd57d00b4ce0b1345acbcc208aa5fd713c
--- /dev/null
+++ b/FullSimLight/src/HepMC3G4Interface.cc
@@ -0,0 +1,116 @@
+
+#include "HepMC3G4Interface.hh"
+
+#include "G4RunManager.hh"
+#include "G4LorentzVector.hh"
+#include "G4Event.hh"
+#include "G4PrimaryParticle.hh"
+#include "G4PrimaryVertex.hh"
+#include "G4TransportationManager.hh"
+#include "G4PhysicalConstants.hh"
+#include "G4SystemOfUnits.hh"
+
+//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
+HepMC3G4Interface::HepMC3G4Interface()
+{
+}
+
+//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
+HepMC3G4Interface::~HepMC3G4Interface()
+{
+  //delete hepmcEvent;
+}
+
+//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
+G4bool HepMC3G4Interface::CheckVertexInsideWorld
+                         (const G4ThreeVector& pos) const
+{
+  G4Navigator* navigator= G4TransportationManager::GetTransportationManager()
+                                                 -> GetNavigatorForTracking();
+
+  G4VPhysicalVolume* world= navigator-> GetWorldVolume();
+  G4VSolid* solid= world-> GetLogicalVolume()-> GetSolid();
+  EInside qinside= solid-> Inside(pos);
+
+  if( qinside != kInside) return false;
+  else return true;
+}
+
+//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
+void HepMC3G4Interface::HepMC32G4(const HepMC3::GenEvent* hepmcevt,G4Event* g4event)
+{
+  for(const auto &vitr : hepmcevt->vertices() ) { // loop for vertex ...
+      
+    // real vertex?
+    G4bool qvtx=false;
+    for (const auto &pitr : vitr->particles_out()) {
+
+      if (!pitr->end_vertex() && pitr->status()==1) {
+        qvtx=true;
+        break;
+      }
+    }
+      
+    if (!qvtx) continue;
+
+    // check world boundary
+    HepMC3::FourVector pos= vitr->position();
+    G4LorentzVector xvtx(pos.x(), pos.y(), pos.z(), pos.t());
+      if (! CheckVertexInsideWorld(xvtx.vect()*mm))
+      {
+          G4cout<<"Vertex is not inside the World"<<G4endl;
+          continue;
+          
+      }
+
+    // create G4PrimaryVertex and associated G4PrimaryParticles
+    G4PrimaryVertex* g4vtx=
+      new G4PrimaryVertex(xvtx.x()*mm, xvtx.y()*mm, xvtx.z()*mm,
+                          xvtx.t()*mm/c_light);
+
+      for (const auto &vpitr : vitr->particles_out()) {
+
+          if( vpitr->status() != 1 ) {
+              G4cout<<"Status not equal to one!"<<G4endl;
+              continue;
+              
+          }
+
+      G4int pdgcode= vpitr-> pid();
+      G4cout << "Particle pdgcode: "<<pdgcode << G4endl;
+      HepMC3::FourVector pos1= vpitr->momentum();
+      G4LorentzVector p(pos1.px(), pos1.py(), pos1.pz(), pos1.e());
+      G4PrimaryParticle* g4prim=
+        new G4PrimaryParticle(pdgcode, p.x()*GeV, p.y()*GeV, p.z()*GeV);
+
+      g4vtx-> SetPrimary(g4prim);
+    }
+    g4event-> AddPrimaryVertex(g4vtx);
+  }
+}
+
+//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
+HepMC3::GenEvent* HepMC3G4Interface::GenerateHepMC3Event()
+{
+  HepMC3::GenEvent* aevent= new HepMC3::GenEvent();
+  return aevent;
+}
+
+//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
+void HepMC3G4Interface::GeneratePrimaryVertex(G4Event* anEvent)
+{
+  // delete previous event object
+  
+
+  // generate next event
+  hepmcEvent= GenerateHepMC3Event();
+  if(! hepmcEvent) {
+    G4cout << "HepMCInterface: no generated particles. run terminated..."
+           << G4endl;
+    G4RunManager::GetRunManager()-> AbortRun();
+    return;
+  }
+  HepMC32G4(hepmcEvent, anEvent);
+  //delete hepmcEvent;
+}
+
diff --git a/FullSimLight/src/HepMC3PrimaryGeneratorAction.cc b/FullSimLight/src/HepMC3PrimaryGeneratorAction.cc
new file mode 100644
index 0000000000000000000000000000000000000000..1b4f7b138ed7b085d23671ee07d470875204bb17
--- /dev/null
+++ b/FullSimLight/src/HepMC3PrimaryGeneratorAction.cc
@@ -0,0 +1,30 @@
+#include "HepMC3PrimaryGeneratorAction.hh"
+#include "G4ParticleGun.hh"
+#include "HepMC3G4AsciiReader.hh"
+#include "G4AutoLock.hh"
+
+namespace {
+ G4Mutex mutex_gen = G4MUTEX_INITIALIZER;
+ 
+}
+
+
+HepMC3PrimaryGeneratorAction::HepMC3PrimaryGeneratorAction(std::string file_path,std::string file_type) : G4VUserPrimaryGeneratorAction()
+{
+    HEPMC3_generator=HepMC3G4AsciiReader::GetInstance(file_path, file_type);
+}
+HepMC3PrimaryGeneratorAction::~HepMC3PrimaryGeneratorAction()
+{
+delete HEPMC3_generator;
+}
+
+void HepMC3PrimaryGeneratorAction::GeneratePrimaries(G4Event* anEvent)
+
+{
+    G4cout<<"HepMC3PrimaryGeneratorAction::GeneratePrimaries going to lock"<<G4endl;
+    G4AutoLock lock(&mutex_gen);
+    G4cout<<"HepMC3PrimaryGeneratorAction::GeneratePrimaries MUTEX IS MINE"<<G4endl;
+    HEPMC3_generator->GeneratePrimaryVertex(anEvent);
+    lock.unlock();
+    G4cout<<"HepMC3PrimaryGeneratorAction::GeneratePrimaries MUTEX unlocked"<<G4endl;
+}
diff --git a/GeoModelVisualization/GXHitDisplaySystems/CMakeLists.txt b/GeoModelVisualization/GXHitDisplaySystems/CMakeLists.txt
index ff87b6186a4dbf20aa68ad3b0bd62246fb99483b..6637e020d8f1144db3c3632cbf85051d6c462fb9 100644
--- a/GeoModelVisualization/GXHitDisplaySystems/CMakeLists.txt
+++ b/GeoModelVisualization/GXHitDisplaySystems/CMakeLists.txt
@@ -4,12 +4,12 @@
 file( GLOB SOURCES src/*.cxx )
 file( GLOB HEADERS GXHitDisplaySystems/*.h )
 file( GLOB UIS src/*.ui )
-
+find_package (HDF5 REQUIRED)
 # Add the library.
 add_library( GXHitDisplaySystems SHARED ${SOURCES} ${HEADERS} ${UIS} )
 target_link_libraries( GXHitDisplaySystems
    PUBLIC Coin::Coin GXBase 
-   PRIVATE Qt5::Core Qt5::Widgets ${HDF5_LIBRARIES} )
+   PRIVATE Qt5::Core Qt5::Widgets ${HDF5_CXX_LIBRARIES} ${HDF5_LIBRARIES} )
 target_include_directories( GXHitDisplaySystems PUBLIC
    ${HDF5_CXX_INCLUDE_DIRS} 
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
diff --git a/LArCustomSolidExtension b/LArCustomSolidExtension
deleted file mode 160000
index b0c7b11bca941c71200572bb80d55ca3f2e398d5..0000000000000000000000000000000000000000
--- a/LArCustomSolidExtension
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit b0c7b11bca941c71200572bb80d55ca3f2e398d5