diff --git a/compact/far_backward/magnets.xml b/compact/far_backward/magnets.xml index 6976fa2d4..4bed1c920 100644 --- a/compact/far_backward/magnets.xml +++ b/compact/far_backward/magnets.xml @@ -49,6 +49,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Electron side magnets @@ -120,9 +146,49 @@ rout1="B2BeR_InnerRadius" rout2="B2BeR_InnerRadius"> - Electron side beam magnet volumes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Hadron side magnets - @@ -507,7 +572,6 @@ - diff --git a/src/BeamlineDipoleMagnet_geo.cpp b/src/BeamlineDipoleMagnet_geo.cpp new file mode 100644 index 000000000..05ded7a8b --- /dev/null +++ b/src/BeamlineDipoleMagnet_geo.cpp @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2023-2025 Justin Chan, Simon Gardner + +#include "DD4hep/DetFactoryHelper.h" +#include "DD4hep/Printout.h" +#include "TMath.h" +#include + +using namespace std; +using namespace dd4hep; + +static Ref_t create_detector(Detector& det, xml_h e, SensitiveDetector /* sens */) { + + using namespace ROOT::Math; + xml_det_t x_det = e; + string det_name = x_det.nameStr(); + DetElement sdet(det_name, x_det.id()); + Assembly assembly(det_name + "_assembly"); + Material m_Iron = det.material("Iron"); + const string vis1 = getAttrOrDefault(x_det, _Unicode(vis), "AnlGreen"); + + // Creates the outer box for the main body + xml::Component outer_box_dim = x_det.child(_Unicode(dimensions_mainbody_outer)); + double outer_height = outer_box_dim.attr(_Unicode(y)); + double outer_width = outer_box_dim.attr(_Unicode(x)); + double outer_depth = outer_box_dim.attr(_Unicode(z)); + + Box box_outer(outer_width / 2., outer_height / 2., outer_depth / 2.); + + // Creates the innner box for the main body + xml::Component inner_box_dim = x_det.child(_Unicode(dimensions_mainbody_inner)); + double inner_height = inner_box_dim.attr(_Unicode(y)); + double inner_width = inner_box_dim.attr(_Unicode(x)); + + // Yoke/coil parameters + double pole_gap = getAttrOrDefault(x_det, _Unicode(pole_gap), inner_height / 2.0); + double yoke_height = inner_height / 2.0 - pole_gap / 2; + double yoke_outer_dim = getAttrOrDefault(x_det, _Unicode(yoke_outer_width), inner_width); + double yoke_inner_dim = getAttrOrDefault(x_det, _Unicode(yoke_inner_width), inner_width); + + // Sets box position + xml::Component box_pos = x_det.child(_Unicode(position)); + double x = box_pos.attr(_Unicode(x)); + double y = box_pos.attr(_Unicode(y)); + double z = box_pos.attr(_Unicode(z)); + + // Calculate wall thickness + double thickness_x = (outer_width - inner_width) / 2.0; + double thickness_y = (outer_height - inner_height) / 2.0; + + // Create 4 separate box volumes for the sides of the rectangular tube + // Top wall (full width, thickness in y, full depth) + Box box_top(outer_width / 2.0, thickness_y / 2.0, outer_depth / 2.0); + Volume vol_top(det_name + "_vol_top", box_top, m_Iron); + vol_top.setAttributes(det, x_det.regionStr(), x_det.limitsStr(), vis1); + + // Bottom wall (full width, thickness in y, full depth) + Volume vol_bottom(det_name + "_vol_bottom", box_top, m_Iron); + vol_bottom.setAttributes(det, x_det.regionStr(), x_det.limitsStr(), vis1); + + // Left wall (thickness in x, inner height, full depth) + Box box_side(thickness_x / 2.0, inner_height / 2.0, outer_depth / 2.0); + Volume vol_left(det_name + "_vol_left", box_side, m_Iron); + vol_left.setAttributes(det, x_det.regionStr(), x_det.limitsStr(), vis1); + + // Right wall (thickness in x, inner height, full depth) + Volume vol_right(det_name + "_vol_right", box_side, m_Iron); + vol_right.setAttributes(det, x_det.regionStr(), x_det.limitsStr(), vis1); + + // Upper coils/yoke - This is not entirely accurate as CAD has curved edges + Trd1 yoke_trap(yoke_outer_dim / 2.0, yoke_inner_dim / 2.0, outer_depth / 2.0, yoke_height / 2.0); + Volume vol_yoke(det_name + "_vol_yoke", yoke_trap, m_Iron); + vol_yoke.setAttributes(det, x_det.regionStr(), x_det.limitsStr(), vis1); + + // Create assembly for the magnet + Assembly magnet_assembly(det_name + "_magnet_assembly"); + + // Place the 4 walls + // Top wall at +y + magnet_assembly.placeVolume(vol_top, Position(0, outer_height / 2.0 - thickness_y / 2.0, 0)); + + // Bottom wall at -y + magnet_assembly.placeVolume(vol_bottom, Position(0, -outer_height / 2.0 + thickness_y / 2.0, 0)); + + // Left wall at -x + magnet_assembly.placeVolume(vol_left, Position(-outer_width / 2.0 + thickness_x / 2.0, 0, 0)); + + // Right wall at +x + magnet_assembly.placeVolume(vol_right, Position(outer_width / 2.0 - thickness_x / 2.0, 0, 0)); + + //Translation for top and bottom yoke + + // Place the top yoke with position and rotation around X axis + magnet_assembly.placeVolume(vol_yoke, + Transform3D(RotationX(TMath::Pi() / 2), + Position(0, inner_height / 2.0 - yoke_height / 2.0, 0))); + + // Place the bottom yoke + magnet_assembly.placeVolume(vol_yoke, + Transform3D(RotationX(-TMath::Pi() / 2), + Position(0, -inner_height / 2.0 + yoke_height / 2.0, 0))); + + // Coil parameters + for (xml_coll_t coil_coll(x_det, _Unicode(coil)); coil_coll; ++coil_coll) { + double coil_width = coil_coll.attr(_Unicode(width)); + double coil_height = coil_coll.attr(_Unicode(height)); + double coil_length = coil_coll.attr(_Unicode(length)); + // Create box for coil + Box coil_box(coil_width / 2., coil_height / 2., coil_length / 2.); + Volume vol_coil(det_name + "_vol_coil", coil_box, m_Iron); + vol_coil.setAttributes(det, x_det.regionStr(), x_det.limitsStr(), vis1); + for (xml_coll_t pos(coil_coll, _Unicode(position)); pos; ++pos) { + double posX = pos.attr(_Unicode(x)); + double posY = pos.attr(_Unicode(y)); + double posZ = pos.attr(_Unicode(z)); + // Place coil at specified position + magnet_assembly.placeVolume(vol_coil, Position(posX, posY, posZ)); + } + } + + // Final placement + auto pv_assembly = det.pickMotherVolume(sdet).placeVolume( + magnet_assembly, Transform3D(RotationZYX(0.0, 0.0, 0.0), Position(x, y, z))); + + sdet.setPlacement(pv_assembly); + + // assembly->GetShape()->ComputeBBox(); + + return sdet; +} + +DECLARE_DETELEMENT(BeamlineDipoleMagnet, create_detector)