Building a Geometry¶
Creating a volume¶
When building a geometry, we are generally considering a set of volumes positioned in a 3D space. We define these by creating a logvol:
<logvol name="MyLogVol" shape="MyShape" material="MyMaterial"/>
a logvol should have a name (names are generally needed for identifying elements and referring to them elsewhere in the code), a shape defining its extent in space, and a material which specificies its composition and density.
A shape to be used in this logvol can be defined as follows:
<shapes>
<!-other shapes can be defined here too-->
<box name=“MyShape” xhalflength=“100.” yhalflength=“100.” zhalflength=“500.” />
<\shapes>
shape of type box, which is defined by 3 parameters: its half-length in x,y, and z, set to 100, 100, and 500 mm, respecively. A full list of supported shapes can be found here.
A material to be used in the logvol can be defined as follows, building up chemical molecules from elements, to make materials which can themselves be mixed in different fractions to create new materials:
<materials>
<!- a more complicated definition to show some possibilities ->
<element name="Carbon" shortname="C" Z="6" A=“12.011"/>
<element name="Argon" shortname="N" Z="18" A="39.72"/>
<element name="Oxygen" shortname="O" Z="8" A=“15.9994"/>
<chemical name="CO2">
<elemcontent ref="Carbon" quantity="1"/>
<elemcontent ref="Oxygen" quantity="2"/>
</chemical>
<material name="CO2Gas" density="1.98e-3">
<chemicalref fraction="1" ref="CO2"/>
</material>
<material name="ArgonGas" density="1.789e-3" >
<elementref fraction="1.00000" ref="Argon"/>
</material>
<material name="MyMaterial" density="1.79e-3" >
<materialref ref="ArgonGas" fraction="0.93" />
<materialref ref="CO2Gas" fraction="0.07" />
</material>
</materials>
Placing a volume¶
By default, volumes are created at the origin of the reference frame. To place a volume within 3D space, it should be placed within a transform element and have a transformation applied. For example, to place the centre of the box we created at a position 250 mm along the z-axis:
<transform>
<transformation name="MyTransformation">
<translation z="250">
</transformation>
<logvolref ref="MyLogVol">
</transform>
logvolref to refer to a previously defined logvol. This can also be done with a transformation that has been previously defined (for example to allow re-use), e.g.
<transformation name="MyTransformation">
<translation z="250">
</transformation>
<transform>
<transformationref ref="MyTransformation">
<logvolref ref="MyLogVol">
</transform>
transformation, e.g. to combine a translation and a rotation:
<transformation name="MyTranslateRotate" >
<translation x=“600." y="600." z="600." />
<rotation xcos="1/sqrt(3.)" ycos="1/sqrt(3.)" zcos="1/sqrt(3.)" angle="PI/4." />
</transformation>
0 unless set, and so a rotation about the z-axis can be performed like <rotation zcos="1.0" angle="PI/4." />.
Combining volumes¶
Since a full detetor will typically consist of very many volumes, some nested within others, it is necessary to be able to combine them. It is possible to simply place one logvol inside another (NB they must be full contained and not protruding otherwise this will cause a clash):
<!- assuming you've defined "MyBigBox", "MySmallBox", "MySmallVolume", ","MyMaterial" already->
<logvol name="MyBigVolume" shape="MyBigBox" material="MyMaterial">
<logvolref ref="MySmallVolume"/>
</logvol>
logvols, and can also include a transformation, e.g:
<!- assuming you've defined "MyBigBox", "MySmallBox", "MySmallVolume", ","MyMaterial" already->
<logvol name="MyBigVolume" shape="MyBigBox" material="MyMaterial">
<transform>
<transformation name"MyZShift">
<translation z="5"/>
</transformation>
<logvolref ref="MySmallVolume"/>
</transform>
<transform>
<transformation name"MyNegativeZShift">
<translation z="-5"/>
</transformation>
<logvolref ref="MySmallVolume"/>
</transform>
</logvol>
logvol by definition requires a shape. Sometimes this can be inconvenient; e.g. one may need to define a very complicated shape to encompass all the volumes contained, without generating clashes with other volumes. A good solution can be to create an assembly which simply binds a set of volumes together, without requiring an “outer” logvol and shape:
<!- assuming you've defined "MyBigBox", "MySmallBox", "MySmallVolume", ","MyMaterial" already->
<assembly name="MyBigVolume">
<transform>
<transformation name"MyZShift">
<translation z="5"/>
</transformation>
<logvolref ref="MySmallVolume"/>
</transform>
<transform>
<transformation name"MyNegativeZShift">
<translation z="-5"/>
</transformation>
<logvolref ref="MySmallVolume"/>
</transform>
</assembly>
assembly can consist of a single positioned logvol as a convenient way to bind a transformation to a volume, and an assembly can also contain mutliple other assemblies or assemblyrefs:
<!- assuming you've defined "MyBigBox", "MySmallBox", "MySmallVolume", ","MyMaterial" already->
<assembly name="MySmallVolumePos">
<transform>
<transformation name="MyZShift">
<translation z="5"/>
</transformation>
<logvolref ref="MySmallVolume">
</transform>
</assembly>
<assembly name="MySmallVolumeNeg">
<transform>
<transformation name="MyNegativeZShift">
<translation z="-5"/>
</transformation>
<logvolref ref="MySmallVolume">
</transform>
</assembly>
<assembly name="MyBigVolume">
<assemblyref ref="MySmallVolumePos"/>
<assemblyref ref="MySmallVolumeNeg"/>
</assembly>
Defining variables¶
You may notice that in the previous examples, we have used explicit numerical values for e.g. the magnitude of translations. In general, as stated in the Good practise guidelines, it is better to always use defined variables where possible. This can be done as follows:
<defines>
<var name=“PI” value=“3.14159265359” />
<vector name=“digits” value=“0 1 2 3 4 5 6 7 8 9” />
<matrix name=“ints” coldim=“3” value=“ 1 2 3 4 5 6 7 8 9” />
</defines>
<defines>
<var name="position1" value="1">
<var name="position2" value="3.5">
<var name="position3" value="pos1+pos2">
<var name="position4" value="(pos1+pos2)/(pos2 - pos1)">
</defines>
_N (_N_M) notation:
<defines>
<var name=“PI” value=“3.14159265359” />
<vector name=“digits” value=“0 1 2 3 4 5 6 7 8 9” />
<matrix name=“ints” coldim=“3” value=“ 1 2 3 4 5 6 7 8 9” />
</defines>
<defines>
<var name=“PIby2” value=“PI/digits_2” />
<var name=“AlsoPIby2” value=“PI/ints_0_1”/>
</defines>
Repeated positionings¶
Detector geometries are in general built of repeated structures, and so it is of great convenience (nigh-on essential) to be able to place identical volumes in a series different positions, reflecting e.g. detector modules placed at regular z-intervals. This can be achieved through the use of a multicopy. The multicopy will place n instances of the logvol/assembly it is applied to, applying the same transformation each time. For instance, placing a volume 5 times with a regular z-interval of 5mm:
<multicopy name="MyVolsAlongZ" n="5">
<transformation name="MyZShift">
<translation z="5"/>
</transformation>
<logvolref ref="MyLogVol">
</multicopy>
multicopy to place volumes with more complex sets of positions via the use of a loopvar. The loopvar is a vector of values, for which the multicopy will use element _N for the n\(^{th}\) copy:
<defines>
<vector name=“MyZPositions” value=“-11 -10 -5 1 4” />
</defines>
<multicopy name="MyVolsAlongZ" n="5" loopvar="MyZPositions">
<transformation name="MyZShift">
<translation z="MyZPositions"/> <!--This will evaluate to MyZPositions_N-->
</transformation>
<logvolref ref="MyLogVol">
</multicopy>
loopvar variable can also be used in formulae:
<defines>
<vector name=“MyScaleFactors” value=“1 2 3 4 5” />
</defines>
<multicopy name="MyVolsAlongZ" n="5" loopvar="MyScaleFactors">
<transformation name="MyZShift">
<translation z="10*MyScaleFactors"/>
</transformation>
<logvolref ref="MyLogVol">
</multicopy>
multicopy without a loopvar, there is an implicit multiplication by 0 … n in the iterations. When a loopvar is defined the periodicity is fully explicit and so the following:
<defines>
<vector name=“MyZPositions” value=“0 1 2 3 4” />
</defines>
<multicopy name="MyVolsAlongZ" n="5" loopvar="MyZPositions">
<transformation name="MyZShift">
<translation z="5"/>
</transformation>
<logvolref ref="MyLogVol">
</multicopy>
loopvar:
<defines>
<vector name=“MyZPositions” value=“0 1 2 3 4” />
</defines
<multicopy name="MyVolsAlongZ" n="5" loopvar="MyZPositions">
<transformation name="MyZShift">
<translation z="5*MyZPositions"/>
</transformation>
<logvolref ref="MyLogVol">
</multicopy>
ReplicaXYArray etc here, but I’ve not used these so might be better that someone else does it ;-)]
Working with shapes¶
While the built-in shapes give considerable options for the type of volumes that can be constructed, some shapes cannot be directly described by the built-in options. In this case boolean solids can be useful. In the following, the three types of boolean operations are shown - union, subtraction, and intersection:
<shapes>
<tube name="Cylinder” rmin="100" rmax="200" zhalflength=“1000"/>
<box name="Box" xhalflength="300" yhalflength="300" zhalflength=“500"/>
<union name="BoxAndCylinder" >
<shaperef ref="Box" />
<transformation name="trivialShift">
<translation z="0."/>
</transformation>
<shaperef ref="Cylinder" />
</union>
<subtraction name="BoxMinusCylinder" >
<shaperef ref="Box" />
<transformation name="trivialShift" >
<translation z="0."/>
</transformation>
<shaperef ref="Cylinder" />
</subtraction>
<intersection name="BoxIntersectCylinder" >
<shaperef ref="Box" />
<transformation name="rotations" >
<rotation xcos="1" angle="PI/4."/>
<rotation ycos="1" angle="PI/4."/>
<rotation zcos="1" angle="PI/4."/>
</transformation>
<shaperef ref="Cylinder" />
</intersection>
</shapes>
boolean operations can lead to high computational overheads, and so their use should be carefully considered (e.g. best avoided for volumes for which very many copies will created).