diff --git a/compact/colors.xml b/compact/colors.xml
index 4c320b287c..b233b2fb2b 100644
--- a/compact/colors.xml
+++ b/compact/colors.xml
@@ -11,6 +11,7 @@
+
diff --git a/compact/display.xml b/compact/display.xml
index 69e76b048d..1ba062ac8c 100644
--- a/compact/display.xml
+++ b/compact/display.xml
@@ -141,9 +141,13 @@
Beam line with magnets
-
-
-
+
+
+
+
+
+
+
ZDC visualization
diff --git a/compact/display_detailed.xml b/compact/display_detailed.xml
index 0c98e51c29..9857e2d3b4 100644
--- a/compact/display_detailed.xml
+++ b/compact/display_detailed.xml
@@ -114,13 +114,6 @@
-
- Beam line with magnets
-
-
-
-
-
ZDC visualization
diff --git a/compact/display_geoviewer.xml b/compact/display_geoviewer.xml
index eadeee7ff1..9ea8e3750c 100644
--- a/compact/display_geoviewer.xml
+++ b/compact/display_geoviewer.xml
@@ -114,13 +114,6 @@
-
- Beam line with magnets
-
-
-
-
-
ZDC visualization
diff --git a/compact/far_backward/beamline_extension_electron.xml b/compact/far_backward/beamline_extension_electron.xml
index e1346df240..d3542c26da 100644
--- a/compact/far_backward/beamline_extension_electron.xml
+++ b/compact/far_backward/beamline_extension_electron.xml
@@ -1,5 +1,5 @@
-
+
@@ -9,7 +9,6 @@
-
Electron side extended beam pipe volumes
-
+
@@ -10,20 +10,12 @@
-
Hadron side beam magnet volumes
-
-
-
-
-
-
-
+ vis="MagnetVis">
@@ -50,7 +42,6 @@
-
Hadron side beam pipe volumes
diff --git a/compact/far_backward/magnets.xml b/compact/far_backward/magnets.xml
index f4bc82bd45..6976fa2d4d 100644
--- a/compact/far_backward/magnets.xml
+++ b/compact/far_backward/magnets.xml
@@ -3,15 +3,93 @@
+
+ See compact/far_forward/ion_beamline.xml for hadron magnet element coordinate description-the same is applied to the electron magnet elements
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Electron side magnets
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Electron side beam pipe volumes
Electron side beam magnet volumes
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ Hadron side magnets
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/compact/far_forward/B0_tracker.xml b/compact/far_forward/B0_tracker.xml
index 88ec565e7a..3d44537cb3 100644
--- a/compact/far_forward/B0_tracker.xml
+++ b/compact/far_forward/B0_tracker.xml
@@ -46,19 +46,19 @@
-
+
-
+
-
+
-
+
diff --git a/compact/far_forward/beampipe_hadron_B0.xml b/compact/far_forward/beampipe_hadron_B0.xml
index f23e72b590..21501127d7 100644
--- a/compact/far_forward/beampipe_hadron_B0.xml
+++ b/compact/far_forward/beampipe_hadron_B0.xml
@@ -16,7 +16,7 @@
-
+
diff --git a/compact/far_forward/electron_beamline.xml b/compact/far_forward/electron_beamline.xml
index 6dbac133dc..e4af388608 100644
--- a/compact/far_forward/electron_beamline.xml
+++ b/compact/far_forward/electron_beamline.xml
@@ -1,10 +1,26 @@
-
+
+
+ See compact/far_forward/ion_beamline.xml for hadron magnet element coordinate description-the same is applied to the electron magnet elements
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -22,69 +38,60 @@
-
-
+
+
-
+
-
-
-
-
-
-
-
+
-
-
-
-
-
- !--unchecked--
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
- !--unchecked--
-
-
-
-
-
-
-
-
+
+
+
+
+
diff --git a/compact/far_forward/ion_beamline.xml b/compact/far_forward/ion_beamline.xml
index 17e27815c1..8fea2e790c 100644
--- a/compact/far_forward/ion_beamline.xml
+++ b/compact/far_forward/ion_beamline.xml
@@ -1,10 +1,93 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Instructions on how to obtain magnet element coordinates and dimensions.
+
+ The reference STEP file, provided by the Superconducting Magnet Division (SMD), is available on SharePoint:
+ EIC Public Sharing Docs → Documents → Experimental Program → ePIC → Engineering → STR-Files → IR6_CRYOSTAT_2200m_top_4-24-2025.stp
+ [https://brookhavenlab.sharepoint.com/:u:/s/EICPublicSharingDocs/EdR44ODny1BEmMTTks61CCwBe8tJYMc0iWkPIYY_EjYEuw?e=2XLUEt]
+
+ -The file can be opened using Onshape (www.onshape.com), a free, cloud-based CAD platform for 3D modeling and mechanical design.
+ -Both the forward (FWD) and backward (BWD/rear) cryostats contain multiple sub-cryostats, primarily associated with hadron magnets.
+ -The main components are the magnet yokes; most other elements—such as coils, support tubes, yoke shielding, heat shielding, and vessels—are positioned relative to these yokes.
+ -Since the STEP file provides coordinates in the RHIC coordinate system, a rotation and translation must be applied before using them in the geometry XML files. See page 19 of the following PDF from the IR Meeting on May 30, 2025:
+ [https://brookhavenlab.sharepoint.com/:b:/r/sites/eRHIC/bnl%26slac/EIC%20IR%20Meeting%20Minutes%20%20Documents/IR-Meeting/2025/05-30-2025/EIC_CRYO_GEANT4_May2025_NATOCHII_UPDATED.pdf]
+
+ -- Apply a shift of 81 cm (the offset between RHIC and EIC IP6 interaction points).
+ -- Apply a rotation of 8 mrad to align with the ePIC detector axis.
+
+ Example of how to extract coordinates from Onshape and compute the yoke's position and orientation:
+
+ TVector3 coord1(-47.389536*cm, 0, -1704.865494*cm); // B1pF yoke endcap — IP side
+ TVector3 coord2(-41.351827*cm, 0, -2009.882603*cm); // B1pF yoke endcap — non-IP side
+ // Rotate around the Y axis by (π-8e-3)
+ double angle = M_PI-8e-3; // π flips the Z-axis; 8e-3 rad aligns with ePIC axis
+ coord1.RotateY(angle*rad);
+ coord2.RotateY(angle*rad);
+ // Compute center
+ TVector3 center = 0.5*(coord1+coord2);
+ center += TVector3(-81.*cm, 0, 0); // Apply RHIC → EIC IP6 offset
+ // Compute rotation angle around Y
+ double deltaX = coord2.X()-coord1.X();
+ double deltaZ = coord2.Z()-coord1.Z();
+ double rotY_angle = atan(deltaX/deltaZ)*rad;
+ // Threshold small angles
+ if (1e-6 > std::abs(rotY_angle)) {
+ rotY_angle = 0;
+ }
+ // Add 'center' and 'rotY_angle' to the XML file
+
+ -Some barrel elements (e.g., heat shield, vessel) and endplates are described using polygons with two or more Z-planes. Example:
+
+
+
+
+
+ Here, 160.817560 cm is the distance between the inner surfaces of the two Q1ApF endplates (mounted on both ends of the magnet),
+ and 15.246221 cm is the Z-thickness of the endplate on the IP side (note the minus sign).
+
+
=====================================
(170-177) Forward Hadron Beamline Magnets (up to B0pf)
@@ -21,54 +104,601 @@
=====================================
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
diff --git a/compact/far_forward/offM_tracker.xml b/compact/far_forward/offM_tracker.xml
index b3ba6d2f3f..d2fe86676e 100644
--- a/compact/far_forward/offM_tracker.xml
+++ b/compact/far_forward/offM_tracker.xml
@@ -30,7 +30,7 @@
readout="ForwardOffMTrackerHits"
vis="FFTrackerVis"
reflect="false">
-
+
@@ -55,7 +55,7 @@
readout="ForwardOffMTrackerHits"
vis="AnlRed"
reflect="false">
-
+
@@ -80,7 +80,7 @@
readout="ForwardOffMTrackerHits"
vis="FFTrackerVis"
reflect="false">
-
+
@@ -105,7 +105,7 @@
readout="ForwardOffMTrackerHits"
vis="FFTrackerVis"
reflect="false">
-
+
diff --git a/compact/far_forward/vacuum.xml b/compact/far_forward/vacuum.xml
index ad5c8acab7..15b2688ca6 100644
--- a/compact/far_forward/vacuum.xml
+++ b/compact/far_forward/vacuum.xml
@@ -14,49 +14,49 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/compact/materials.xml b/compact/materials.xml
index 358f4b6954..7e79bb1f4a 100644
--- a/compact/materials.xml
+++ b/compact/materials.xml
@@ -547,4 +547,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/CryostatMagnet_geo.cpp b/src/CryostatMagnet_geo.cpp
new file mode 100644
index 0000000000..42276e3746
--- /dev/null
+++ b/src/CryostatMagnet_geo.cpp
@@ -0,0 +1,394 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright (C) 2022 Alex Jentsch, Wouter Deconinck, Whitney Armstrong, Andrii Natochii
+
+#include "DD4hep/DetFactoryHelper.h"
+#include "DD4hep/Printout.h"
+#include "DD4hep/Shapes.h"
+#include "DDRec/DetectorData.h"
+#include "DDRec/Surface.h"
+#include "TMath.h"
+#include "XML/Layering.h"
+
+using namespace std;
+using namespace dd4hep;
+using namespace dd4hep::rec;
+using namespace ROOT::Math;
+
+void buildTubeElement(dd4hep::DetElement& sdet, dd4hep::Assembly& assembly, dd4hep::Detector& dtor,
+ dd4hep::xml::DetElement x_det, string element, dd4hep::Material material);
+void buildPolyElement(dd4hep::DetElement& sdet, dd4hep::Assembly& assembly, dd4hep::Detector& dtor,
+ dd4hep::xml::DetElement x_det, string element, dd4hep::Material material);
+
+static Ref_t build_magnet(Detector& dtor, xml_h e, SensitiveDetector /* sens */) {
+ xml_det_t x_det = e;
+ int det_id = x_det.id();
+ string det_name = x_det.nameStr();
+
+ DetElement sdet(det_name, det_id);
+ Assembly assembly(det_name + "_assembly");
+
+ // get materials
+ Material iron = dtor.material("Iron");
+ Material nbti = dtor.material("SolenoidCoil");
+ Material steel_304l = dtor.material("StainlessSteelSAE304");
+ Material alum = dtor.material("Al6061T6");
+ Material steel_a53 = dtor.material("StainlessSteelA53");
+
+ // build magnet components
+ buildTubeElement(sdet, assembly, dtor, x_det, "yoke", iron);
+ buildTubeElement(sdet, assembly, dtor, x_det, "coil", nbti);
+ buildTubeElement(sdet, assembly, dtor, x_det, "tube", steel_304l);
+ buildPolyElement(sdet, assembly, dtor, x_det, "endplate", steel_304l);
+ buildTubeElement(sdet, assembly, dtor, x_det, "yokeshield", steel_304l);
+ buildTubeElement(sdet, assembly, dtor, x_det, "heatshieldbarrel", alum);
+ buildPolyElement(sdet, assembly, dtor, x_det, "heatshieldend", alum);
+ buildTubeElement(sdet, assembly, dtor, x_det, "cryobarrel", steel_a53);
+ buildPolyElement(sdet, assembly, dtor, x_det, "cryoend", steel_a53);
+
+ // final placement
+ auto pv_assembly = dtor.pickMotherVolume(sdet).placeVolume(assembly);
+ pv_assembly.addPhysVolID("system", det_id);
+ sdet.setPlacement(pv_assembly);
+
+ // update bounding box
+ assembly->GetShape()->ComputeBBox();
+
+ return sdet;
+}
+
+void buildPolyElement(dd4hep::DetElement& sdet, dd4hep::Assembly& assembly, dd4hep::Detector& dtor,
+ dd4hep::xml::DetElement x_det, string element, dd4hep::Material material) {
+ // get all elems
+ xml_coll_t elems_c(x_det, element.c_str());
+ int elem_id = 1;
+
+ // loop over elems
+ for (; elems_c; ++elems_c) {
+ // get one element
+ xml_comp_t elem_c = elems_c;
+ string elem_name = elem_c.nameStr();
+ // get placement coordinates
+ xml_dim_t elem_pos = elem_c.child(_U(placement));
+ double elem_theta = elem_pos.attr("theta");
+ std::vector z;
+ std::vector rmax;
+ std::vector rmin;
+ // loop over z-planes
+ xml_coll_t zplanes_c(elem_c, _Unicode(zplane));
+ for (; zplanes_c; ++zplanes_c) {
+ // get z-plane
+ xml_comp_t zplane_c = zplanes_c;
+ z.push_back(zplane_c.attr(_Unicode(z)));
+ rmin.push_back(zplane_c.attr(_Unicode(rmin)));
+ rmax.push_back(zplane_c.attr(_Unicode(rmax)));
+ }
+
+ // set attributes
+ const string elem_vis =
+ dd4hep::getAttrOrDefault(elem_c, _Unicode(vis), "FFMagnetVis");
+ sdet.setAttributes(dtor, assembly, x_det.regionStr(), x_det.limitsStr(), elem_vis);
+
+ // build solid
+ Polycone elem_tube(0, 2.0 * M_PI, rmin, rmax, z);
+ Solid elem_final(elem_tube);
+
+ // get all adds
+ xml_coll_t adds_c(elem_c, _Unicode(add));
+ // loop over adds
+ for (; adds_c; ++adds_c) {
+ Solid add_elem;
+ // get one cut
+ xml_comp_t add_c = adds_c;
+ // get shape
+ string add_shape = add_c.attr("shape");
+ // get placement coordinates
+ xml_dim_t add_pos = add_c.child(_U(placement));
+ double add_theta = add_pos.attr("theta");
+ // get dimentions
+ xml_dim_t add_dim = add_c.child(_U(dimensions));
+
+ Transform3D tf_tmp(RotationZYX(0, add_theta, 0),
+ Position(add_pos.x(), add_pos.y(), add_pos.z()));
+
+ if (add_shape == "Prism") {
+ double add_pdx1 = add_dim.attr("pdx1");
+ double add_pdx2 = add_dim.attr("pdx2");
+ double add_pdy1 = add_dim.attr("pdy1");
+ double add_pdy2 = add_dim.attr("pdy2");
+ double add_pdz = add_dim.attr("pdz");
+
+ // build a solid
+ Trapezoid add_prism(add_pdx1, add_pdx2, add_pdy1, add_pdy2, add_pdz);
+ add_elem = add_prism;
+ } else if (add_shape == "Tube") {
+ double add_rmin = add_dim.attr("rmin");
+ double add_rmax = add_dim.attr("rmax");
+ double add_half_l = add_dim.attr("half_length");
+
+ // build a solid
+ Tube add_tube(add_rmin, add_rmax, add_half_l);
+ add_elem = add_tube;
+ }
+
+ // unite the add with the element solid
+ elem_final = UnionSolid("elem_final", elem_final, add_elem, tf_tmp);
+ }
+
+ // get all cuts
+ xml_coll_t cuts_c(elem_c, _Unicode(cut));
+
+ Solid cut_final;
+ // loop over cuts
+ for (; cuts_c; ++cuts_c) {
+ // get one cut
+ xml_comp_t cut_c = cuts_c;
+ // get shape
+ string cut_shape = dd4hep::getAttrOrDefault(cut_c, _Unicode(shape), "Tube");
+
+ // get placement coordinates
+ xml_dim_t cut_pos = cut_c.child(_U(placement));
+ double cut_rotX = cut_pos.attr("rotX");
+ double cut_rotY = cut_pos.attr("rotY");
+ double cut_rotZ = cut_pos.attr("rotZ");
+ // get rotation coordinates
+ xml_dim_t cut_rot = cut_c.child(_U(rotation));
+ int cut_rot_num = cut_rot.attr("num");
+ double cut_rot_step = cut_rot.attr("step");
+ double cut_rot_start = cut_rot.attr("start");
+ string cut_rot_axis = cut_rot.attr("axis");
+
+ if (cut_shape == "Cone") {
+ // get dimentions
+ xml_dim_t cut_dim = cut_c.child(_U(dimensions));
+ double cut_rmin1 = cut_dim.attr("rmin1");
+ double cut_rmax1 = cut_dim.attr("rmax1");
+ double cut_rmin2 = cut_dim.attr("rmin2");
+ double cut_rmax2 = cut_dim.attr("rmax2");
+ double cut_dz = cut_dim.attr("dz");
+
+ // build a solid
+ Cone cut_cone(cut_dz, cut_rmin1, cut_rmax1, cut_rmin2, cut_rmax2);
+ cut_final = cut_cone;
+ } else if (cut_shape == "Tube") {
+ // get dimentions
+ xml_dim_t cut_dim = cut_c.child(_U(dimensions));
+ double cut_rmin = cut_dim.attr("rmin");
+ double cut_rmax = cut_dim.attr("rmax");
+ double cut_half_l = cut_dim.attr("half_length");
+
+ // build a solid
+ Tube cut_tube(cut_rmin, cut_rmax, cut_half_l);
+ cut_final = cut_tube;
+ }
+
+ // loop over rot steps
+ for (int i = 0; i < cut_rot_num; i++) {
+ Position pos_tmp(cut_pos.x(), cut_pos.y(), cut_pos.z());
+ double ang_tmp = cut_rot_start + i * cut_rot_step;
+ Rotation3D rot_tmp;
+ if (cut_rot_axis == "X") {
+ rot_tmp = RotationX(ang_tmp);
+ } else if (cut_rot_axis == "Y") {
+ rot_tmp = RotationY(ang_tmp);
+ } else {
+ rot_tmp = RotationZ(ang_tmp);
+ }
+ pos_tmp = rot_tmp * pos_tmp;
+
+ Transform3D tf_tmp(RotationZYX(cut_rotZ, cut_rotY, cut_rotX), pos_tmp);
+ // subtract the cut from the element solid
+ elem_final = SubtractionSolid("elem_final", elem_final, cut_final, tf_tmp);
+ }
+ }
+
+ // create volume
+ Volume elem_vol(elem_name, elem_final, material);
+
+ // placement
+ auto elem_pv = assembly.placeVolume(
+ elem_vol, Transform3D(Translation3D(elem_pos.x(), elem_pos.y(), elem_pos.z()) *
+ RotationY(elem_theta)));
+ elem_pv.addPhysVolID(element, elem_id);
+ DetElement elem_de(sdet, elem_name, elem_id);
+ elem_de.setPlacement(elem_pv);
+ elem_de.setAttributes(dtor, elem_vol, x_det.regionStr(), x_det.limitsStr(), elem_vis);
+ elem_id++;
+ }
+
+ return;
+}
+
+void buildTubeElement(dd4hep::DetElement& sdet, dd4hep::Assembly& assembly, dd4hep::Detector& dtor,
+ dd4hep::xml::DetElement x_det, string element, dd4hep::Material material) {
+ // get all elems
+ xml_coll_t elems_c(x_det, element.c_str());
+ int elem_id = 1;
+
+ // loop over elems
+ for (; elems_c; ++elems_c) {
+ // get one element
+ xml_comp_t elem_c = elems_c;
+ string elem_name = elem_c.nameStr();
+ // get placement coordinates
+ xml_dim_t elem_pos = elem_c.child(_U(placement));
+ double elem_theta = elem_pos.attr("theta");
+
+ // set attributes
+ const string elem_vis =
+ dd4hep::getAttrOrDefault(elem_c, _Unicode(vis), "FFMagnetVis");
+ sdet.setAttributes(dtor, assembly, x_det.regionStr(), x_det.limitsStr(), elem_vis);
+
+ // get shape
+ string elem_shape = dd4hep::getAttrOrDefault(elem_c, _Unicode(shape), "Tube");
+
+ Solid elem_sub;
+ if (elem_shape == "Cone") {
+ // get dimentions
+ xml_dim_t elem_dim = elem_c.child(_U(dimensions));
+ double elem_rmin1 = elem_dim.attr("rmin1");
+ double elem_rmax1 = elem_dim.attr("rmax1");
+ double elem_rmin2 = elem_dim.attr("rmin2");
+ double elem_rmax2 = elem_dim.attr("rmax2");
+ double elem_dz = elem_dim.attr("dz");
+
+ // build a solid
+ Cone elem_cone(elem_dz, elem_rmin1, elem_rmax1, elem_rmin2, elem_rmax2);
+ elem_sub = elem_cone;
+ } else if (elem_shape == "ConeSegment") {
+ // get dimentions
+ xml_dim_t elem_dim = elem_c.child(_U(dimensions));
+ double elem_rmin1 = elem_dim.attr("rmin1");
+ double elem_rmax1 = elem_dim.attr("rmax1");
+ double elem_rmin2 = elem_dim.attr("rmin2");
+ double elem_rmax2 = elem_dim.attr("rmax2");
+ double elem_dz = elem_dim.attr("dz");
+ double elem_sphi = elem_dim.attr("sphi");
+ double elem_dphi = elem_dim.attr("dphi");
+
+ // build a solid
+ ConeSegment elem_conesegment(elem_dz, elem_rmin1, elem_rmax1, elem_rmin2, elem_rmax2,
+ elem_sphi, elem_sphi + elem_dphi);
+ elem_sub = elem_conesegment;
+ } else if (elem_shape == "Tube") {
+ // get dimentions
+ xml_dim_t elem_dim = elem_c.child(_U(dimensions));
+ double elem_rmin = elem_dim.attr("rmin");
+ double elem_rmax = elem_dim.attr("rmax");
+ double elem_half_l = elem_dim.attr("half_length");
+ double elem_sphi = elem_dim.attr("sphi");
+ double elem_dphi = elem_dim.attr("dphi");
+
+ // build solid
+ Tube elem_tube(elem_rmin, elem_rmax, elem_half_l, elem_sphi, elem_sphi + elem_dphi);
+ elem_sub = elem_tube;
+ }
+
+ Solid elem_final(elem_sub);
+
+ // combine sub-elements
+ if (elem_pos.hasAttr(_Unicode(phiNum))) {
+ int phi_num = elem_pos.attr("phiNum");
+ double phi_step = elem_pos.attr("phiStep");
+ double phi_start = elem_pos.attr("phiStart");
+
+ // loop over steps
+ for (int i = 0; i < phi_num; i++) {
+ double phi_tmp = phi_start + i * phi_step;
+ Transform3D tf_tmp(RotationZ(phi_tmp), Position(0, 0, 0));
+ // unite sub-elements
+ elem_final = UnionSolid("elem_final", elem_final, elem_sub, tf_tmp);
+ }
+ }
+
+ // get all cuts
+ xml_coll_t cuts_c(elem_c, _Unicode(cut));
+ // loop over cuts
+ for (; cuts_c; ++cuts_c) {
+ // get one cut
+ xml_comp_t cut_c = cuts_c;
+ // get shape
+ string add_shape = dd4hep::getAttrOrDefault(cut_c, _Unicode(shape), "Tube");
+ // get placement coordinates
+ xml_dim_t cut_pos = cut_c.child(_U(placement));
+ double cut_rotX = cut_pos.attr("rotX");
+ double cut_rotY = cut_pos.attr("rotY");
+ double cut_rotZ = cut_pos.attr("rotZ");
+ // get rotation coordinates
+ xml_dim_t cut_rot = cut_c.child(_U(rotation));
+ int cut_rot_num = cut_rot.attr("num");
+ double cut_rot_step = cut_rot.attr("step");
+ double cut_rot_start = cut_rot.attr("start");
+ string cut_rot_axis = cut_rot.attr("axis");
+
+ Solid cut_elem;
+ if (add_shape == "Box") {
+ // get dimentions
+ xml_dim_t cut_dim = cut_c.child(_U(dimensions));
+ double cut_dx = cut_dim.attr("dx");
+ double cut_dy = cut_dim.attr("dy");
+ double cut_dz = cut_dim.attr("dz");
+
+ // build a solid
+ Box cut_box(cut_dx, cut_dy, cut_dz);
+ cut_elem = cut_box;
+ } else if (add_shape == "Tube") {
+ // get dimentions
+ xml_dim_t cut_dim = cut_c.child(_U(dimensions));
+ double cut_rmin = cut_dim.attr("rmin");
+ double cut_rmax = cut_dim.attr("rmax");
+ double cut_half_l = cut_dim.attr("half_length");
+
+ // build a solid
+ Tube cut_tube(cut_rmin, cut_rmax, cut_half_l);
+ cut_elem = cut_tube;
+ } else if (add_shape == "Cone") {
+ // get dimentions
+ xml_dim_t cut_dim = cut_c.child(_U(dimensions));
+ double cut_rmin1 = cut_dim.attr("rmin1");
+ double cut_rmax1 = cut_dim.attr("rmax1");
+ double cut_rmin2 = cut_dim.attr("rmin2");
+ double cut_rmax2 = cut_dim.attr("rmax2");
+ double cut_dz = cut_dim.attr("dz");
+
+ // build a solid
+ Cone cut_cone(cut_dz, cut_rmin1, cut_rmax1, cut_rmin2, cut_rmax2);
+ cut_elem = cut_cone;
+ }
+ // loop over rot steps
+ for (int i = 0; i < cut_rot_num; i++) {
+ Position pos_tmp(cut_pos.x(), cut_pos.y(), cut_pos.z());
+ double ang_tmp = cut_rot_start + i * cut_rot_step;
+ Rotation3D rot_tmp;
+ if (cut_rot_axis == "X") {
+ rot_tmp = RotationX(ang_tmp);
+ } else if (cut_rot_axis == "Y") {
+ rot_tmp = RotationY(ang_tmp);
+ } else {
+ rot_tmp = RotationZ(ang_tmp);
+ }
+ pos_tmp = rot_tmp * pos_tmp;
+
+ Transform3D tf_tmp(RotationZYX(cut_rotZ, cut_rotY, cut_rotX), pos_tmp);
+ // subtract the cut from the element solid
+ elem_final = SubtractionSolid("elem_final", elem_final, cut_elem, tf_tmp);
+ }
+ }
+
+ // create volume
+ Volume elem_vol(elem_name, elem_final, material);
+
+ // placement
+ auto elem_pv = assembly.placeVolume(
+ elem_vol, Transform3D(Translation3D(elem_pos.x(), elem_pos.y(), elem_pos.z()) *
+ RotationY(elem_theta)));
+ elem_pv.addPhysVolID(element, elem_id);
+ DetElement elem_de(sdet, elem_name, elem_id);
+ elem_de.setPlacement(elem_pv);
+ elem_de.setAttributes(dtor, elem_vol, x_det.regionStr(), x_det.limitsStr(), elem_vis);
+ elem_id++;
+ }
+
+ return;
+}
+
+DECLARE_DETELEMENT(ip6_CryostatMagnet, build_magnet)
diff --git a/src/CylindricalDipoleMagnet_geo.cpp b/src/CylindricalDipoleMagnet_geo.cpp
deleted file mode 100644
index 33f9318849..0000000000
--- a/src/CylindricalDipoleMagnet_geo.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-or-later
-// Copyright (C) 2022 Alex Jentsch, Wouter Deconinck, Whitney Armstrong
-
-#include "DD4hep/DetFactoryHelper.h"
-#include "DD4hep/Printout.h"
-#include "DD4hep/Shapes.h"
-#include "DDRec/DetectorData.h"
-#include "DDRec/Surface.h"
-#include "TMath.h"
-#include "XML/Layering.h"
-
-using namespace std;
-using namespace dd4hep;
-using namespace dd4hep::rec;
-using namespace ROOT::Math;
-
-static Ref_t build_magnet(Detector& dtor, xml_h e, SensitiveDetector /* sens */) {
- xml_det_t x_det = e;
- int det_id = x_det.id();
- string det_name = x_det.nameStr();
- xml_dim_t pos = x_det.child(_U(placement));
- double pos_x = pos.x();
- double pos_y = pos.y();
- double pos_z = pos.z();
- double pos_theta = pos.attr(_U(theta));
- xml_dim_t dims = x_det.dimensions();
- double dim_r = dims.r();
- double dim_z = dims.z();
- xml_dim_t apperture = x_det.child(_Unicode(apperture));
- double app_r = apperture.r();
- Material iron = dtor.material("Iron");
-
- DetElement sdet(det_name, det_id);
- Assembly assembly(det_name + "_assembly");
-
- const string module_name = "Quad_magnet";
-
- const string yoke_vis =
- dd4hep::getAttrOrDefault(x_det, _Unicode(vis), "FFMagnetVis");
-
- sdet.setAttributes(dtor, assembly, x_det.regionStr(), x_det.limitsStr(), yoke_vis);
-
- // -- yoke
- Tube yoke_tube(app_r, dim_r, 0.5 * dim_z);
- Volume yoke_vol("yoke_vol", yoke_tube, iron);
- auto yoke_pv = assembly.placeVolume(yoke_vol);
- yoke_pv.addPhysVolID("element", 1);
- DetElement yoke_de(sdet, "yoke_de", 1);
- yoke_de.setPlacement(yoke_pv);
- yoke_de.setAttributes(dtor, yoke_vol, x_det.regionStr(), x_det.limitsStr(), yoke_vis);
-
- // -- finishing steps
- auto final_pos = Transform3D(Translation3D(pos_x, pos_y, pos_z) * RotationY(pos_theta));
- auto pv = dtor.pickMotherVolume(sdet).placeVolume(assembly, final_pos);
- pv.addPhysVolID("system", det_id);
- sdet.setPlacement(pv);
-
- assembly->GetShape()->ComputeBBox();
- return sdet;
-}
-
-DECLARE_DETELEMENT(ip6_CylindricalDipoleMagnet, build_magnet)
diff --git a/src/forwardBeamPipeBrazil.cpp b/src/forwardBeamPipeBrazil.cpp
index be797c9181..3e02e587ef 100644
--- a/src/forwardBeamPipeBrazil.cpp
+++ b/src/forwardBeamPipeBrazil.cpp
@@ -260,6 +260,8 @@ static Ref_t create_detector(Detector& det, xml_h e, SensitiveDetector /* sens *
//------------------------------------------
//begin building main volumes here
//------------------------------------------
+ // A box to cut out from the beam pipe to avoid overlaps with the fwd cryostat
+ Box cutout_for_FWD_cryo(1 * dd4hep::m, 1 * dd4hep::m, 2 * dd4hep::m);
//-------------------------------------------------------------------
@@ -290,6 +292,12 @@ static Ref_t create_detector(Detector& det, xml_h e, SensitiveDetector /* sens *
tmpAfterB1APF, neutral_exit_window_cutout,
Position(160.0 * dd4hep::mm, 0.0, 0.5 * beampipe_dimensions[pieceIdx].length));
+ //-----------------------------------------------------------------
+ // Cut on the IP side to avoid overlaps with the fwd cryostat
+ tmpAfterB1APF = SubtractionSolid(tmpAfterB1APF, cutout_for_FWD_cryo,
+ Position(0.0, 0.0, (-beampipe_dimensions[pieceIdx].length) / 2));
+ //-----------------------------------------------------------------
+
Volume v_pipeAfterB1APF(Form("v_pipeAfterB1APF_%d", pieceIdx), tmpAfterB1APF, m_SS);
sdet.setAttributes(det, v_pipeAfterB1APF, x_det.regionStr(), x_det.limitsStr(), vis_name);
@@ -517,10 +525,23 @@ static Ref_t create_detector(Detector& det, xml_h e, SensitiveDetector /* sens *
SubtractionSolid final_vacuum_main_pipe(
vacuum_main_pipe, cutout_for_OMD_station,
- Position(0.0, 0.0, (2251.0 - beampipe_dimensions[pieceIdx].zCenter)));
- final_vacuum_main_pipe =
- SubtractionSolid(final_vacuum_main_pipe, cutout_for_OMD_station,
- Position(0.0, 0.0, (2451.0 - beampipe_dimensions[pieceIdx].zCenter)));
+ Position(0.0, 0.0, (2551.0 * dd4hep::cm - beampipe_dimensions[pieceIdx].zCenter)));
+ final_vacuum_main_pipe = SubtractionSolid(
+ final_vacuum_main_pipe, cutout_for_OMD_station,
+ Position(0.0, 0.0, (2701.0 * dd4hep::cm - beampipe_dimensions[pieceIdx].zCenter)));
+
+ //-----------------------------------------------------------------
+ // Cut on the IP side to avoid overlaps with the fwd cryostat
+ final_vacuum_main_pipe = SubtractionSolid(
+ final_vacuum_main_pipe, cutout_for_FWD_cryo,
+ Position(0.0, 0.0,
+ (2400.0 * dd4hep::cm - 2 * dd4hep::m - beampipe_dimensions[pieceIdx].zCenter)));
+ Tube pipe_for_FWD_cryo(0.0, 16.0 * dd4hep::cm, 97.0 * dd4hep::cm);
+ final_vacuum_main_pipe = UnionSolid(
+ final_vacuum_main_pipe, pipe_for_FWD_cryo,
+ Position(6.5 * dd4hep::cm, 0.0,
+ (2400.0 * dd4hep::cm - 97.0 * dd4hep::cm - beampipe_dimensions[pieceIdx].zCenter)));
+ //-----------------------------------------------------------------
Volume v_vacuum_main_pipe("v_vacuum_main_pipe", final_vacuum_main_pipe, m_vac);
sdet.setAttributes(det, v_vacuum_main_pipe, x_det.regionStr(), x_det.limitsStr(), "AnlBlue");
diff --git a/src/magnetVacuumFF.cpp b/src/magnetVacuumFF.cpp
index 94f915bbba..2f26763206 100644
--- a/src/magnetVacuumFF.cpp
+++ b/src/magnetVacuumFF.cpp
@@ -81,15 +81,15 @@ static Ref_t create_detector(Detector& det, xml_h e, SensitiveDetector /* sens *
for (xml_coll_t c(x_det, _U(element)); c; ++c) {
- xml_dim_t pos = c.child(_U(placement));
- double pos_x = pos.x();
- double pos_y = pos.y();
- double pos_z = pos.z();
- double pos_theta = pos.attr(_U(theta));
- xml_dim_t dims = c.child(_U(dimensions)); //dimensions();
- double dim_z = dims.z();
- xml_dim_t apperture = c.child(_Unicode(apperture));
- double app_r = apperture.r();
+ xml_dim_t pos = c.child(_U(placement));
+ double pos_x = pos.x();
+ double pos_y = pos.y();
+ double pos_z = pos.z();
+ double pos_theta = pos.attr(_U(theta));
+ xml_dim_t dims = c.child(_U(dimensions)); //dimensions();
+ double dim_z = dims.z();
+ xml_dim_t aperture = c.child(_Unicode(aperture));
+ double app_r = aperture.r();
radii_magnet.push_back(app_r); // cm
lengths_magnet.push_back(dim_z); //cm
@@ -246,8 +246,8 @@ static Ref_t create_detector(Detector& det, xml_h e, SensitiveDetector /* sens *
std::string piece_name = Form("GapVacuum%d", numGaps + numMagnets);
- Cone specialGap(piece_name, specialGapLength / 2, 0.0, vacuumDiameterEntrance / 2, 0.0,
- vacuumDiameterExit / 2);
+ ConeSegment specialGap(piece_name, specialGapLength / 2, 0.0, vacuumDiameterEntrance / 2, 0.0,
+ vacuumDiameterExit / 2, 40 * deg, (360 - 40) * deg);
Volume specialGap_v(piece_name, specialGap, m_Vac);
sdet.setAttributes(det, specialGap_v, x_det.regionStr(), x_det.limitsStr(), vis_name);