Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,13 @@ struct FT3BaseParam : public o2::conf::ConfigurableParamHelper<FT3BaseParam> {
double staveTolOTInner = 0.;
double staveTolOTOuter = 0.;

// What to place over x=0 line in case of full outer-outer stave: Gap or Module
bool placeSensorStackInMiddleOfStave = false;
/*
* Place the sensor stacks from the tabulated layout in FT3ModuleConstants.h
* (StaveConfig::exactStaveFills) instead of filling every stave greedily
* with the stack sizes in kSensorsPerStack. The tabulated layout is taken as
* given: none of the radial tolerances above are applied to it.
*/
bool useExactStavePlacement = true;

// Draw reference circles at inner and outer radius of stave layer, for visualisation
bool drawReferenceCircles = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

o2_add_library(FT3Simulation
SOURCES
src/FT3Materials.cxx
src/FT3Module.cxx
src/FT3Layer.cxx
src/Detector.cxx
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,25 +59,11 @@ class FT3Layer : public TObject
/// \param motherVolume the TGeoVolume owing the volume structure
virtual void createLayer(TGeoVolume* motherVolume);

static void initialize_mat();

// create layer for disk support
void createSeparationLayer(TGeoVolume* motherVolume, const std::string& separationLayerName);
void createSeparationLayer_waterCooling(TGeoVolume* motherVolume, const std::string& separationLayerName);
void createReferenceCircles(TGeoVolume* motherVolume, const std::string& name);

static TGeoMaterial* carbonFiberMat;
static TGeoMedium* medCarbonFiber;

static TGeoMixture* kaptonMat;
static TGeoMedium* kaptonMed;

static TGeoMaterial* waterMat;
static TGeoMedium* waterMed;

static TGeoMaterial* foamMat;
static TGeoMedium* medFoam;

private:
Int_t mLayerNumber = -1; ///< Current layer number
Int_t mDirection; ///< Layer direction 0=Forward 1 = Backward
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

/// \file FT3Materials.h
/// \brief Materials of the FT3 detector, and access to the media made from them

#ifndef FT3MATERIALS_H
#define FT3MATERIALS_H

#include <array>
#include <unordered_map>
#include <TColor.h>

class TGeoMedium;

namespace o2::ft3
{
namespace Materials
{
// The name FT3 registers itself under with the MaterialManager. getMedium()
// looks media up under this key, so it has to be the same name the Detector
// hands to DetImpl<Detector>.
constexpr const char* moduleName = "FT3";

/*
* Materials of the FT3 detector.
*
* Everything the simulation needs to know about a material lives in the
* materials map below, keyed by its FT3-local ID: composition, density,
* transport parameters and the colour its volumes are drawn in.
* Detector::createMaterials() registers the whole map with the MaterialManager,
* and FT3Module/FT3Layer reach the media through getMedium() below, so no ID,
* density or colour is ever written out by hand.
*/
enum class MaterialID : unsigned {
Air = 1,
Silicon,
Copper,
Kapton,
CarbonFiber,
Epoxy,
Aluminum,
Foam,
Water
};

// Transport parameters of a medium, in the order expected by Detector::Medium()
struct TrackingParams {
float tmaxfd; // maximum field-induced angular deviation per step, degrees
float stemax; // maximum step length, cm
float deemax; // maximum fractional energy loss per step
float epsil; // tracking precision, cm
float stmin; // minimum step length, cm
};

constexpr TrackingParams sensitiveTracking = {0.1f, 0.0075f, 0.1f, 1.0e-4f, 0.0f};
constexpr TrackingParams passiveTracking = {0.1f, 1.0f, 0.1f, 1.0e-4f, 0.0f};

// Maximum number of elements any of the mixtures below is built from
constexpr unsigned maxMaterialComponents = 4;
using ComponentArray = std::array<float, maxMaterialComponents>;

struct MaterialProperties {
const char* name;
int colour; // ROOT colour every volume made of this material is drawn in
float density; // g/cm3
// Radiation and nuclear interaction length, cm. Only single elements carry
// them: Mixture() derives both from the composition and takes no such
// arguments. A non-positive value lets the transport engine compute it.
float radl;
float absl;
int nComponents; // 0: single element; > 0: mixture by weight; < 0: mixture by atom count
ComponentArray a; // mass numbers; only a[0] is used for a single element
ComponentArray z; // atomic numbers; only z[0] is used for a single element
ComponentArray w; // weight fractions or atom counts; unused for a single element
TrackingParams tracking;
};

/*
* Silicon, copper and carbon fibre are shared with TRK and are kept numerically
* identical to its SILICON$, COPPER$ and CARBONFIBER$ (TRK Detector::createMaterials()),
* down to the radiation lengths. Kapton, epoxy and aluminium have no TRK
* counterpart: TRK models the flex as the effective FPC$ mixture instead.
*/
inline const std::unordered_map<MaterialID, MaterialProperties> materials = {
// Air volumes get their colour set individually where they are built
{MaterialID::Air, {"Air", kWhite, 1.20479e-3f, 0.0f, 0.0f, 4, {12.0107f, 14.0067f, 15.9994f, 39.948f}, {6.0f, 7.0f, 8.0f, 18.0f}, {0.000124f, 0.755267f, 0.231781f, 0.012827f}, passiveTracking}},
{MaterialID::Silicon, {"Silicon", kGreen, 2.33f, 9.36f, 999.0f, 0, {28.086f}, {14.0f}, {}, sensitiveTracking}},
// Copper planes of the end-of-stave cards: X0 = 1.436 cm
{MaterialID::Copper, {"Copper", kOrange, 8.96f, 1.436f, 999.0f, 0, {63.546f}, {29.0f}, {}, passiveTracking}},
// Kapton: C22 H10 N2 O5, by weight fraction. Also the cooling pipe material.
{MaterialID::Kapton, {"Kapton", kYellow, 1.346f, 0.0f, 0.0f, 4, {12.0107f, 1.00794f, 14.0067f, 15.999f}, {6.0f, 1.0f, 7.0f, 8.0f}, {0.5641f, 0.2564f, 0.0513f, 0.1282f}, passiveTracking}},
// Carbon fibre: density tuned so X0 ~ 27 cm, as in TRK
// TODO: Check with Rene the exact type of carbon fiber
{MaterialID::CarbonFiber, {"CarbonFiber", kGray + 1, 1.45f, 27.0f, 999.0f, 0, {12.0107f}, {6.0f}, {}, passiveTracking}},
// Epoxy: C18 H19 O3, by atom count (negative nComponents)
{MaterialID::Epoxy, {"Epoxy", kBlue, 2.186f, 0.0f, 0.0f, -3, {12.0107f, 1.00794f, 15.999f}, {6.0f, 1.0f, 8.0f}, {18.0f, 19.0f, 3.0f}, passiveTracking}},
// No TRK counterpart; X0 and lambda are left to the transport engine
{MaterialID::Aluminum, {"Aluminum", kBlack, 2.7f, 0.0f, 0.0f, 0, {26.98f}, {13.0f}, {}, passiveTracking}},
// Carbon foam core of the disk separation layer
{MaterialID::Foam, {"Foam", kBlack, 0.17f, 0.0f, 0.0f, 0, {12.0107f}, {6.0f}, {}, passiveTracking}},
// Coolant inside the kapton pipes
{MaterialID::Water, {"Water", kBlue, 1.064f, 0.0f, 0.0f, 0, {18.01528f}, {8.0f}, {}, passiveTracking}}};
// The inactive rim of a sensor is made of silicon as well, but is drawn
// separately so that it can be told apart from the active area.
const int SiInactiveColor = kRed;
} // namespace Materials

/// Retrieve one of the media registered by Detector::createMaterials().
///
/// A free function rather than a member of Detector: the media live in the
/// MaterialManager singleton keyed by Materials::moduleName, not in the
/// detector object, so the lookup needs no Detector instance.
TGeoMedium* getMedium(Materials::MaterialID id);
} // namespace o2::ft3

#endif // FT3MATERIALS_H
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
#ifndef FT3MODULE_H
#define FT3MODULE_H

#include <TGeoMedium.h>
#include <TGeoVolume.h>
#include <string>
#include <vector>

#include "FT3Simulation/FT3Materials.h"
#include "FT3Simulation/FT3ModuleConstants.h"

// define types for y positions, second element is the stack height
Expand All @@ -28,25 +30,12 @@ using PosNegPositionTypes = std::pair<PositionTypes, PositionTypes>;
// define type of the y position range: First pair is (min, max) for positive y
using PositionRangeType = std::pair<std::pair<double, double>, std::pair<double, double>>;
namespace Constants = o2::ft3::ModuleConstants;
namespace Materials = o2::ft3::Materials;

class FT3Module
{

public:
static void initialize_materials();
static TGeoMaterial* siliconMat;
static TGeoMedium* siliconMed;
static TGeoMaterial* copperMat;
static TGeoMedium* copperMed;
static TGeoMixture* kaptonMat;
static TGeoMedium* kaptonMed;
static TGeoMaterial* epoxyMat;
static TGeoMedium* epoxyMed;
static TGeoMaterial* AluminumMat;
static TGeoMedium* AluminumMed;
static TGeoMaterial* carbonFiberMat;
static TGeoMedium* carbonFiberMed;

const char* mDetName;

static void createModule(
Expand All @@ -70,10 +59,39 @@ class FT3Module
double Rout, double z_offset_local, const Constants::StaveConfig& staveConfig,
TGeoVolume* motherVolume);

// Walk every stave of a layer, create its volumes and work out where its
// modules go, leaving the positions in y_positionsPosNeg
void build_staves_exact(
TGeoVolume* motherVolume, int layerNumber, int direction,
const Constants::StaveConfig& staveConfig,
const std::array<std::array<double, 3>, 4>& staveTriangles,
double z_offset_to_carbon_face,
std::vector<PosNegPositionTypes>& y_positionsPosNeg,
unsigned& staveVolumeCount);

void build_staves_greedy(
TGeoVolume* motherVolume, int layerNumber, int direction, double Rin, double Rout,
const Constants::StaveConfig& staveConfig,
const std::array<std::array<double, 3>, 4>& staveTriangles,
double z_offset_to_carbon_face,
std::vector<PosNegPositionTypes>& y_positionsPosNeg, unsigned& staveVolumeCount);

// Shared by both: one stave's carbon shell, plus its mirror where needed
void add_stave_volumes(
TGeoVolume* motherVolume, int layerNumber, int direction,
const Constants::StaveConfig& staveConfig, unsigned i_stave,
const std::array<std::array<double, 3>, 4>& staveTriangles,
double z_offset_to_carbon_face, std::pair<double, double>& absAllowedYRange,
double y_midpoint, bool mirrorStaveAroundX, unsigned* staveVolumeCount);

// Helper functions
void fill_stave(PosNegPositionTypes& y_positions, double Rin, double Rout,
double x_left, unsigned kSensorStack, PositionRangeType y_range,
std::pair<double, double>& absAllowedYRange);
void fill_stave_greedy(
PosNegPositionTypes& y_positions, unsigned kSensorStack,
PositionRangeType y_range,
std::pair<double, double>& absAllowedYRange);

PositionTypes fill_stave_exact(const std::vector<Constants::StaveFill>& fills);

void addStaveVolume(
TGeoVolume* motherVolume, std::string volumeName, int direction,
unsigned* volume_count, double staveLength,
Expand Down
Loading
Loading