6 #include "G4Ellipsoid.hh" 7 #include "G4IntersectionSolid.hh" 8 #include "G4NistManager.hh" 10 #include "G4Polycone.hh" 11 #include "G4Polyhedra.hh" 16 #include "G4EllipticalTube.hh" 17 #include "G4Paraboloid.hh" 19 #include "G4Sphere.hh" 20 #include "G4SubtractionSolid.hh" 21 #include "G4UnionSolid.hh" 22 #include "G4IntersectionSolid.hh" 23 #include "G4UnitsTable.hh" 24 #include "G4RotationMatrix.hh" 49 if(SolidV)
delete SolidV;
51 string hd_msg = gemcOpt.
args[
"LOG_MSG"].args +
" Solid: >> ";
52 double VERB = gemcOpt.
args[
"G4P_VERBOSITY"].arg ;
53 string catch_v = gemcOpt.
args[
"CATCH"].args;
60 if(dimensions.size() != 3)
62 cout << hd_msg <<
" Fatal Error: the number of dimensions for " << name
63 <<
" is " << dimensions.size() <<
":" << endl;
64 for(
unsigned int i=0; i<dimensions.size(); i++) cout <<
" dimension " << i + 1 <<
": " << dimensions[i] << endl;
65 cout <<
" This does not match a G4Box. Exiting" << endl << endl;
69 SolidV =
new G4Box(name,
80 if(type ==
"Parallelepiped")
82 if(dimensions.size() != 6)
84 cout << hd_msg <<
" Fatal Error: the number of dimensions for " << name
85 <<
" is " << dimensions.size() <<
":" << endl;
86 for(
unsigned int i=0; i<dimensions.size(); i++) cout <<
" dimension " << i + 1 <<
": " << dimensions[i] << endl;
87 cout <<
" This does not match a G4Para. Exiting" << endl << endl;
91 SolidV =
new G4Para(name,
108 if(dimensions.size() != 6)
110 cout << hd_msg <<
" Fatal Error: the number of dimensions for " << name
111 <<
" is " << dimensions.size() <<
":" << endl;
112 for(
unsigned int i=0; i<dimensions.size(); i++) cout <<
" dimension " << i + 1 <<
": " << dimensions[i] << endl;
113 cout <<
" This does not match a G4Sphere. Exiting." << endl << endl;
117 SolidV =
new G4Sphere(name,
132 if(type ==
"Ellipsoid")
134 if(dimensions.size() != 5)
136 cout << hd_msg <<
" Fatal Error: the number of dimensions for " << name
137 <<
" is " << dimensions.size() <<
":" << endl;
138 for(
unsigned int i=0; i<dimensions.size(); i++) cout <<
" dimension " << i + 1 <<
": " << dimensions[i] << endl;
139 cout <<
" This does not match a G4Ellipsoid. Exiting." << endl << endl;
143 SolidV =
new G4Ellipsoid(name,
156 if(type ==
"Paraboloid")
158 if(dimensions.size() != 3)
160 cout << hd_msg <<
" Fatal Error: the number of dimensions for " << name
161 <<
" is " << dimensions.size() <<
":" << endl;
162 for(
unsigned int i=0; i<dimensions.size(); i++) cout <<
" dimension " << i + 1 <<
": " << dimensions[i] << endl;
163 cout <<
" This does not match a G4Paraboloid. Exiting." << endl << endl;
167 SolidV =
new G4Paraboloid(name,
181 if(dimensions.size() != 5)
183 cout << hd_msg <<
" Fatal Error: the number of dimensions for " << name
184 <<
" is " << dimensions.size() <<
":" << endl;
185 for(
unsigned int i=0; i<dimensions.size(); i++) cout <<
" dimension " << i + 1 <<
": " << dimensions[i] << endl;
186 cout <<
" This does not match a G4Hype. Exiting." << endl << endl;
190 SolidV =
new G4Hype(name,
207 if(dimensions.size() != 5)
209 cout << hd_msg <<
" Fatal Error: the number of dimensions for " << name
210 <<
" is " << dimensions.size() <<
":" << endl;
211 for(
unsigned int i=0; i<dimensions.size(); i++) cout <<
" dimension " << i + 1 <<
": " << dimensions[i] << endl;
212 cout <<
" This does not match a G4Tubs. Exiting" << endl << endl;
216 SolidV =
new G4Tubs(name,
229 if(type ==
"EllipticalTube" || type ==
"Eltu")
231 if(dimensions.size() != 3)
233 cout << hd_msg <<
" Fatal Error: the number of dimensions for " << name
234 <<
" is " << dimensions.size() <<
":" << endl;
235 for(
unsigned int i=0; i<dimensions.size(); i++) cout <<
" dimension " << i + 1 <<
": " << dimensions[i] << endl;
236 cout <<
" This does not match a G4ElipticalTube. Exiting" << endl << endl;
240 SolidV =
new G4EllipticalTube(name,
255 if(dimensions.size() != 7)
257 cout << hd_msg <<
" Fatal Error: the number of dimensions for " << name
258 <<
" is " << dimensions.size() <<
":" << endl;
259 for(
unsigned int i=0; i<dimensions.size(); i++) cout <<
" dimension " << i + 1 <<
": " << dimensions[i] << endl;
260 cout <<
" This does not match a G4Cons. Exiting" << endl << endl;
264 SolidV =
new G4Cons(name,
281 if(dimensions.size() != 5)
283 cout << hd_msg <<
" Fatal Error: the number of dimensions for " << name
284 <<
" is " << dimensions.size() <<
":" << endl;
285 for(
unsigned int i=0; i<dimensions.size(); i++) cout <<
" dimension " << i + 1 <<
": " << dimensions[i] << endl;
286 cout <<
" This does not match a G4Torus. Exiting" << endl << endl;
289 SolidV =
new G4Torus(name,
306 if(dimensions.size() != 5)
308 cout << hd_msg <<
" Fatal Error: the number of dimensions for " << name
309 <<
" is " << dimensions.size() <<
":" << endl;
310 for(
unsigned int i=0; i<dimensions.size(); i++) cout <<
" dimension " << i + 1 <<
": " << dimensions[i] << endl;
311 cout <<
" This does not match a G4Trd. Exiting" << endl << endl;
314 SolidV =
new G4Trd(name,
329 if(dimensions.size() != 7)
331 cout << hd_msg <<
" Fatal Error: the number of dimensions for " << name
332 <<
" is " << dimensions.size() <<
":" << endl;
333 for(
unsigned int i=0; i<dimensions.size(); i++) cout <<
" dimension " << i + 1 <<
": " << dimensions[i] << endl;
334 cout <<
" This does not match a ITrd. Exiting" << endl << endl;
338 double alph_xz = dimensions[5];
339 double alph_yz = dimensions[6];
340 double x = tan(alph_xz);
341 double y = tan(alph_yz);
342 double r = sqrt(x*x + y*y);
345 double pDz = dimensions[4];
346 double pTheta = atan2(r, 1);
347 double pPhi = atan2(y, x);
348 double pDy1 = dimensions[2];
349 double pDx1 = dimensions[0];
352 double pDy2 = dimensions[3];
353 double pDx3 = dimensions[1];
357 SolidV =
new G4Trap(name,
379 if(dimensions.size() != 11)
381 cout << hd_msg <<
" Fatal Error: the number of dimensions for " << name
382 <<
" is " << dimensions.size() <<
":" << endl;
383 for(
unsigned int i=0; i<dimensions.size(); i++) cout <<
" dimension " << i + 1 <<
": " << dimensions[i] << endl;
384 cout <<
" This does not match a G4Trd. Exiting" << endl << endl;
387 SolidV =
new G4Trap(name,
406 if(type ==
"G4TrapC")
408 if(dimensions.size() != 24)
410 cout << hd_msg <<
" Fatal Error: the number of dimensions for " << name
411 <<
" is " << dimensions.size() <<
":" << endl;
412 for(
unsigned int i=0; i<dimensions.size(); i++) cout <<
" dimension " << i + 1 <<
": " << dimensions[i] << endl;
413 cout <<
" This does not match a G4Trd. Exiting" << endl << endl;
417 G4ThreeVector points[8];
418 for(
int v=0; v<8; v++)
420 points[v].setX(dimensions[v*3+0]/cm * cm);
421 points[v].setY(dimensions[v*3+1]/cm * cm);
422 points[v].setZ(dimensions[v*3+2]/cm * cm);
425 SolidV =
new G4Trap(name,
439 if(dimensions.size() < 8)
441 cout << hd_msg <<
" Fatal Error: the number of dimensions for " << name
442 <<
" is " << dimensions.size() <<
":" << endl;
443 for(
unsigned int i=0; i<dimensions.size(); i++) cout <<
" dimension " << i + 1 <<
": " << dimensions[i] << endl;
444 cout <<
" This does not match a G4Polyhedra. Exiting" << endl << endl;
447 int nsides = (int) dimensions[2];
448 int zplanes = (int) dimensions[3];
451 cout << hd_msg <<
" Fatal Error: no sides for " << name
452 <<
"... should be a G4Polyhedra. Exiting" << endl << endl;
455 double* zPlane =
new double[zplanes];
456 double* rInner =
new double[zplanes];
457 double* rOuter =
new double[zplanes];
459 for(
int zpl=0; zpl<zplanes; zpl++)
461 rInner[zpl] = dimensions[4 + 0*zplanes + zpl] ;
462 rOuter[zpl] = dimensions[4 + 1*zplanes + zpl] ;
463 zPlane[zpl] = dimensions[4 + 2*zplanes + zpl] ;
465 SolidV =
new G4Polyhedra(name,
481 if(type ==
"Polycone")
483 if(dimensions.size() < 7)
485 cout << hd_msg <<
" Fatal Error: the number of dimensions for " << name
486 <<
" is " << dimensions.size() <<
":" << endl;
487 for(
unsigned int i=0; i<dimensions.size(); i++) cout <<
" dimension " << i + 1 <<
": " << dimensions[i] << endl;
488 cout <<
" This does not match a G4Polycone. Exiting" << endl << endl;
491 int zplanes = (int) dimensions[2];
493 double* zPlane =
new double[zplanes];
494 double* rInner =
new double[zplanes];
495 double* rOuter =
new double[zplanes];
497 for(
int zpl=0; zpl<zplanes; zpl++)
502 rInner[zpl] = dimensions[3 + 0*zplanes + zpl] ;
503 rOuter[zpl] = dimensions[3 + 1*zplanes + zpl] ;
504 zPlane[zpl] = dimensions[3 + 2*zplanes + zpl] ;
506 SolidV =
new G4Polycone(name,
521 if(type.find(
"CopyOf") != string::npos && type.find(
"CopyOf") == 0)
523 hd_msg = gemcOpt.
args[
"LOG_MSG"].args +
" Copy: >> ";
524 string original(type, 6, 190);
527 map<string, detector>::iterator it = (*Map).find(
TrimSpaces(original));
528 if(it == (*Map).end())
530 cout << hd_msg <<
" <" << original <<
"> not found. Exiting." << endl << endl;
535 if(VERB>4 || name.find(catch_v) != string::npos)
537 cout << hd_msg <<
" " << name <<
" is a copy of <" <<
TrimSpaces(original) <<
">. Pointing to its logical volume." << endl;
539 SetLogical(it->second.GetLogical());
548 if(type.find(
"Operation:") != string::npos && type.find(
"Operation:") == 0)
550 hd_msg = gemcOpt.
args[
"LOG_MSG"].args +
" Operation: >> ";
552 bool translationFirst =
false;
555 size_t posTld = type.find(
"~");
556 if( posTld != string::npos )
558 translationFirst =
true;
559 type.replace( posTld, 1,
" " );
563 bool absolutecoordinates =
false;
567 size_t pos_at = type.find(
"@");
568 if( pos_at != string::npos )
570 absolutecoordinates =
true;
571 type.replace( pos_at, 1,
" " );
581 string operation(type, 10, 190);
582 posp = operation.find(
"+");
583 posm = operation.find(
"-");
584 post = operation.find(
"*");
585 if (posp != string::npos) pos = posp;
586 else if(posm != string::npos) pos = posm;
587 else if(post != string::npos) pos = post;
588 if(!posp && !posm && !post)
590 cout << hd_msg <<
" Operation " << operation <<
" for " << name <<
" not recognized. Exiting." << endl;
595 string solid1, solid2;
596 string tsolid1, tsolid2;
597 solid1.assign(operation, 0, pos);
598 solid2.assign(operation, pos+1, operation.size());
603 map<string, detector>::iterator it1 = (*Map).find(tsolid1);
604 map<string, detector>::iterator it2 = (*Map).find(tsolid2);
605 if(it1 == (*Map).end())
607 cout << hd_msg <<
" " << tsolid1 <<
" Not found. Exiting." << endl << endl;
610 if(it2 == (*Map).end())
612 cout << hd_msg <<
" " << tsolid2 <<
" Not found. Exiting." << endl << endl;
617 G4RotationMatrix rotate = it2->second.rot ;
618 G4ThreeVector translate = it2->second.pos;
619 G4RotationMatrix invRot = rotate.invert() ;
620 G4Transform3D transf1( invRot, G4ThreeVector( 0, 0, 0 ) );
621 G4Transform3D transf2( G4RotationMatrix(), translate );
622 G4Transform3D transform = transf2 * transf1 ;
627 G4RotationMatrix invrot1 = (it1->second.rot).inverse();
628 G4RotationMatrix rotate2 = it2->second.rot;
630 G4ThreeVector net_translation = it2->second.pos - it1->second.pos;
635 G4RotationMatrix invnet_rotation = (rotate2 * invrot1).invert();
641 net_translation *= it1->second.rot;
642 transform = G4Transform3D( invnet_rotation, net_translation );
644 translationFirst =
false;
651 if( translationFirst )
653 transform = transf1 * transf2 ;
656 if(posp != string::npos)
658 SolidV =
new G4UnionSolid( name, it1->second.GetSolid(), it2->second.GetSolid(), transform );
660 if(posm != string::npos)
662 SolidV =
new G4SubtractionSolid( name, it1->second.GetSolid(), it2->second.GetSolid(), transform );
664 if(post != string::npos)
666 SolidV =
new G4IntersectionSolid(name, it1->second.GetSolid(), it2->second.GetSolid(), transform );
669 if(VERB>4 || name.find(catch_v) != string::npos)
671 cout << hd_msg <<
" " << name <<
" is the " << (pos==posp ?
" sum " :
" difference ") <<
" of " << tsolid1 <<
" and " << tsolid2 << endl;;
676 if(VERB>4 || name.find(catch_v) != string::npos)
678 cout << hd_msg <<
" " << name <<
" solid " << type <<
" built." << endl;
684 cout << hd_msg <<
" " << name <<
" solid " << type <<
" not recognized. Exiting." << endl;
697 string hd_msg = gemcOpt.
args[
"LOG_MSG"].args +
" Logical: >> ";
698 double VERB = gemcOpt.
args[
"G4P_VERBOSITY"].arg ;
699 string catch_v = gemcOpt.
args[
"CATCH"].args;
700 string defmat = gemcOpt.
args[
"DEFAULT_MATERIAL"].args;
701 if(material==
"Component")
703 if(VERB>4 || name.find(catch_v) != string::npos)
704 cout << hd_msg <<
" " << name <<
" is a Solid Component. Logical Volume will not be built." << endl;
709 map<string, G4Material*>::iterator i = MMats->find(material);
712 if(i == MMats->end() && LogicV == 0)
714 G4NistManager* matman = G4NistManager::Instance();
715 if(matman->FindOrBuildMaterial(material)) (*MMats)[material] = matman->FindOrBuildMaterial(material);
717 i = MMats->find(material);
720 if(i == MMats->end() && LogicV == 0)
724 cout << hd_msg <<
" Warning: " << material <<
" is not defined. Exiting" << endl;
725 cout << hd_msg <<
" You can set the DEFAULT_MATERIAL flag to replace an undefined material. " << endl;
731 if(MMats->find(material)== MMats->end())
733 cout << hd_msg <<
" Warning: " << defmat <<
" set with DEFAULT_MATERIAL is not found. Exiting" << endl;
744 LogicV =
new G4LogicalVolume(SolidV, (*MMats)[material], name);
745 if(name ==
"root") LogicV->SetVisAttributes(G4VisAttributes::GetInvisible());
746 else LogicV->SetVisAttributes(VAtts);
748 if(VERB>4 || name.find(catch_v) != string::npos)
750 cout << hd_msg <<
" " << name <<
" Logical Volume built." << endl;
759 string hd_msg = gemcOpt.
args[
"LOG_MSG"].args +
" Physical: >> ";
760 double VERB = gemcOpt.
args[
"G4P_VERBOSITY"].arg ;
761 bool OVERL = gemcOpt.
args[
"CHECK_OVERLAPS"].arg > 0 ;
762 string catch_v = gemcOpt.
args[
"CATCH"].args;
763 if(PhysicalV)
delete PhysicalV;
764 if(material==
"Component")
766 if(VERB>4 || name.find(catch_v) != string::npos)
767 cout << hd_msg <<
" " << name <<
" is a Solid Component. Physical Volume will not be built." << endl;
772 PhysicalV =
new G4PVPlacement(0,
781 PhysicalV =
new G4PVPlacement(&rot,
790 if(VERB>4 || name.find(catch_v) != string::npos)
793 cout << hd_msg <<
" " << name <<
" Physical Volume(s) built inside " << mamma->GetName() <<
"." << endl;
805 vector< vector<string> > dtypes;
809 if(solidtype ==
"Box")
811 dt[0] =
"half length in X";
813 dtypes.push_back(dt);
814 dt[0] =
"half length in Y";
815 dtypes.push_back(dt);
816 dt[0] =
"half length in Z";
817 dtypes.push_back(dt);
820 if(solidtype ==
"Sphere")
823 dt[0] =
"Inner radius";
824 dtypes.push_back(dt);
825 dt[0] =
"Outer radius";
826 dtypes.push_back(dt);
828 dt[0] =
"Starting Phi angle of the segment";
829 dtypes.push_back(dt);
830 dt[0] =
"Delta Phi angle of the segment";
831 dtypes.push_back(dt);
832 dt[0] =
"Starting Theta angle of the segment";
833 dtypes.push_back(dt);
834 dt[0] =
"Delta Theta angle of the segment";
835 dtypes.push_back(dt);
837 if(solidtype ==
"Tube")
840 dt[0] =
"Inner radius";
841 dtypes.push_back(dt);
842 dt[0] =
"Outer radius";
843 dtypes.push_back(dt);
844 dt[0] =
"Half length in z";
845 dtypes.push_back(dt);
847 dt[0] =
"Starting Phi angle";
848 dtypes.push_back(dt);
849 dt[0] =
"Delta Phi angle";
850 dtypes.push_back(dt);
852 if(solidtype ==
"Trd")
855 dt[0] =
"Half-length along x at the surface at -dz";
856 dtypes.push_back(dt);
857 dt[0] =
"Half-length along x at the surface at +dz";
858 dtypes.push_back(dt);
859 dt[0] =
"Half-length along y at the surface at -dz";
860 dtypes.push_back(dt);
861 dt[0] =
"Half-length along y at the surface at +dz";
862 dtypes.push_back(dt);
863 dt[0] =
"dz: Half-length along z axis";
864 dtypes.push_back(dt);
866 if(solidtype ==
"Cons")
869 dt[0] =
"Inner radius at start";
870 dtypes.push_back(dt);
871 dt[0] =
"Outer radius at start";
872 dtypes.push_back(dt);
873 dt[0] =
"Inner radius at end";
874 dtypes.push_back(dt);
875 dt[0] =
"Outer radius at end";
876 dtypes.push_back(dt);
877 dt[0] =
"Half length in z";
878 dtypes.push_back(dt);
880 dt[0] =
"Starting Phi angle";
881 dtypes.push_back(dt);
882 dt[0] =
"Delta Phi angle";
883 dtypes.push_back(dt);
885 if(solidtype ==
"G4Trap")
888 dt[0] =
"Half z length ";
889 dtypes.push_back(dt);
891 dt[0] =
"Polar angle of the line joining the centres of the faces";
892 dtypes.push_back(dt);
893 dt[0] =
"Azimuthal angle of the line joining the centre of the face";
894 dtypes.push_back(dt);
896 dt[0] =
"Half y length at -pDz";
897 dtypes.push_back(dt);
898 dt[0] =
"Half x length of the side at y=-pDy1, -pDz";
899 dtypes.push_back(dt);
900 dt[0] =
"Half x length of the side at y=+pDy1, -pDz";
901 dtypes.push_back(dt);
903 dt[0] =
"Angle to the y axis from the centre (lower endcap) ";
904 dtypes.push_back(dt);
906 dt[0] =
"Half y length at +pDz";
907 dtypes.push_back(dt);
908 dt[0] =
"Half x length of the side at y=-pDy2, +pDz";
909 dtypes.push_back(dt);
910 dt[0] =
"Half x length of the side at y=+pDy2, +pDz";
911 dtypes.push_back(dt);
913 dt[0] =
"Angle to the y axis from the centre (upper endcap) ";
914 dtypes.push_back(dt);
917 if(solidtype ==
"G4EllipticalTube")
920 dt[0]=
"Half length x";
921 dtypes.push_back(dt);
923 dt[0]=
"Half length y";
924 dtypes.push_back(dt);
926 dt[0]=
"Half length z";
927 dtypes.push_back(dt);
937 cout <<
" Detector name: " << Detector.
name <<
" - " << Detector.
description << endl;
938 cout <<
" Mother: " << Detector.
mother << endl;
939 cout <<
" Position (cm): " << Detector.
pos/cm << endl;
940 cout <<
" Rotation: " << Detector.
rot << endl;
941 cout <<
" Color: " << Detector.
VAtts.GetColour() << endl;
942 cout <<
" Type: " << Detector.
type << endl;
945 if(dtypes.size() != Detector.
dimensions.size() && Detector.
type.find(
"CopyOf") != 0)
947 for(
unsigned int i=0; i<Detector.
dimensions.size(); i++)
948 cout <<
" Size " << i + 1 <<
": " << Detector.
dimensions[i] << endl;
950 if(dtypes.size() == Detector.
dimensions.size() && Detector.
type.find(
"CopyOf") != 0)
951 for(
unsigned int i=0; i<dtypes.size(); i++)
952 cout <<
" " << dtypes[i][0] <<
": " << G4BestUnit(Detector.
dimensions[i], dtypes[i][1]) << endl;
954 cout <<
" Material: " << Detector.
material << endl;
955 cout <<
" Magnetic Field: " << Detector.
magfield << endl;
956 cout <<
" Copy Number: " << Detector.
ncopy << endl;
957 cout <<
" Activated: " << ( Detector.
exist==1 ?
"yes" :
"no" ) << endl;
958 cout <<
" Visible: " << ( Detector.
visible==1 ?
"yes" :
"no" ) << endl;
959 cout <<
" Style: " << ( Detector.
style==1 ?
"solid" :
"wireframe" ) << endl;
960 cout <<
" Sensitivity: " << Detector.
sensitivity << endl;
962 cout <<
" hitType: " << Detector.
hitType << endl;
966 if(Detector.
identity[0].name !=
"Mirror")
977 if(D.
name == this->name)
int create_logical_volume(map< string, G4Material * > *, gemc_opts)
Creates the Logical Volume.
string sensitivity
Defines the Sensitive Detector. possible choices: "no" "hits collection name".
string mother
Mother Volume name.
ostream & operator<<(ostream &stream, detector Detector)
G4ThreeVector pos
Position relative to the mother volume, as G4ThreeVector.
bool operator==(const detector &D) const
Overloaded "==" operator for detector class.
int create_solid(gemc_opts, map< string, detector > *)
Creates the Solid. If it's a Copy Placement, retrieve and assigns LogicV.
G4VisAttributes VAtts
Visual Attributes: color, transparency, style (wireframe, solid), visibility.
vector< vector< string > > dimensionstype(string solidtype)
Returns dimensions nomenclature for different solid type.
string name
Name of the volume. Since this is the key of the STL map, it has to be unique.
string material
Volume Material name.
int visible
visibility of the detector: 0=invisible 1=visible
int style
Visual style: 0=wireframe 1=solid.
string hitType
Hit Process routine name. A Hit Process MPHBaseClass derived class must exists with this name...
vector< identifier > identity
Vector of identifiers. Example: superlayer manual 1 type manual 2 segment manual 3 strip manual 4...
string magfield
Magnetic Field. The string "no" means that the field is inherited from the mother volume...
map< string, opts > args
Options map.
G4RotationMatrix rot
Rotation Matrix, defined by rotations along x,y,z axis relative to the mother volume.
int exist
detector ON/OFF switch
int create_physical_volumes(gemc_opts, G4LogicalVolume *)
Creates the Physical Volume.
string type
solid type. This follows the GEANT4 definitions
vector< double > dimensions
vector of dimensions. Size, units depends on solid type
string TrimSpaces(string in)
Removes leading and trailing spaces.
string description
Volume Description for documentation.