Design Issues for the Particle Data Table Classes in StdHep ----------------------------------------------------------- This document discusses several design issues related to the HepPDT classes. Why TempParticleData? --------------------- When a ParticleData is added to the ParticleDataTable, it must be a full, valid, and usable set of particle information. But some sources of particle properties do not keep all the data about each particle contiguously. TempParticleData is used as an interim object, holding particle properties as they are made available. All essential data members are initialized to their correct values or, if not yet available, to recognizable invalid values. A ParticleData is always constructed from a completed TempParticleData; the ParticleData constructor ensures that it is a sensible and well-formed particle. Input utilities like addPythiaParticles() use TempParticleData. Ownership, Reference Counting, and Auto_ptr's -------------------------------------------- It is important to have a clear understanding of who is responsible for and/or allowed to delete which objects. The following design avoids accidental deletion of an object in use, and minimizes the likelihood of memory leaks when the HepPDT objects are used. ParticleDataTable::addParticle has an auto_ptr argument The use model would be: ParticleDataTable table; TempParticleData tp; // ... fill tp here table.addParticle( new ParticleData(tp) ); ParticleDataTable takes ownerhip of the new ParticleData object; the user has no further responsibility for it and must make no further use of it. ParticleData is reference counted Each of the following points to exactly one ParticleData: Constituent DecayChannelProduct Each entry in ParticleDataTable Reference counting ensures that a ParticleData will remain in existence so long as there is any use for it. For instance, a Constituent may use a ParticleData even after the ParticleData's entry has been removed from the ParticleDataTable. Every time Constituent or DecayChannel is constructed (destructed) or an entry is added (removed) to ParticleDataTable, the ParticleData's increment (decrement) method is called. In this way, the ParticleData object will exist so long as it is needed, and can be safely discarded once it is no longer in use. Other objects that refer to ParticleData (e.g., GenParticle) can have this safety as well. They need only to follow the same pattern of increment/decrement calls, etc. If they don't, they don't quite have the same safety: if a ParticleDataTable disappears, for example, then any remaining references to any ParticleData are invalid. DecayData has a constructor taking a vector argument DecayData takes ownership of each of the DecayChannel objects in the argument vector; the user has no further responsibility for them and must not reuse any of them. DecayData::appendMode takes an auto_ptr argument DecayData takes ownerhip of the new DecayChannel object; the user has no further responsibility for it and must make no further use of it. DecayData is reference counted DecayData is referred to by: ParticleData DecayChannelProduct SomeParticle Under certain circumstances, the same DecayData may be referred to by two or more of the above items. For example, a custom DecayData used in a chain might well be referred to by other objects of the same type in the chain. Custom Decay Chains ------------------- One design goal was to provide a way to "force" a particle to decay in a certain way. For example, within a particular detector you may only be interested in decays that produce a muon - or you may decide not to track decays of this particle at all. When discussing this need, it was noted that you also would want control over the decay channels of that particle's descendents. To insert custom decay information, and thereby override use of the DecayData associated with the ParticleData, provide a non-null pointer to a custom DecayData in the generated particle. The DecayData structure has, for each DecayChannel, a vector. Each DecayChannelProduct refers to the appropriate ParticleData and, optionally, to a DecayData. To customize an entire decay chain, make a DecayData with products that refer to other custom DecayData objects. Class Extensibility ------------------- ParticleDataTable - extensible; see ParticleData ParticleData - not extensible; reference-counting is needed and interferes with derivation. To add new properties, design a new class (e.g., ExtensionData) with the desired new information, and also implement a new map (perhaps in an extension of ParticleDataTable). ParticleID - not extensible; ParticleData contains a ParticleID non-polymorphically Quarks - not extensible; part of ParticleID (q.v.) ResonanceStructure - extensible; an application may wish to implement a more complex line shape DecayData - not extensible; reference-counting is needed and interferes with derivation DecayChannel - extensible DecayChannelProduct - not extensible; collections of DecayChannelProduct are in use and would simply slice off any extension DecayModel - extensible; this is the anticipated and recommended way to organize custom decay parameters and related information SpinState - not extensible; ParticleData contains a SpinState non-polymorphically Constituent - not extensible; collections of Constituent are in use and would simply slice off any extension TempParticleData - extensible; however, the ParticleData constructor will not make any use of any extension Measurement - not extensible; additional functionality can be provided, if needed, via free functions (e.g., operator + () ) SomeParticle - extensible; however, it is present only for illustrative purposes Helicity - not extensible; however, related concepts may be incorporated into an extended DecayModel Templates: What and Why ------------------------ In the explanation of DecayModel, we said, DecayModel has a virtual method, decay(), to apply the DecayChannel. Logically, this method has to modify (or return) a collection of generated particles (e.g., GenParticle rather than ParticleData). Therefore, decay() will have a signature involving a particle class which is OUTSIDE the HepPDT package. This is discussed in detail in a companion document "Templates in the Particle Data Table Classes." To summarize, all the main classes must be templates of ParticleType. This template scheme is the most natural way of dealing with decay issues, because other approaches force each application to deal with such issues piecemeal. We considered a number of other schemes using few or no templates at all; after a lot of thought we have concluded that the natural template design is the best choice available. At first glance, our decision would seem to force all applications -- even those that care nothing about decays -- to be burdened with template issues. However, HepPDT provides (e.g., in "ParticleData.hh") typedef's to serve as simple names for the actual templated types. (This is the same approach taken by the C++ standard library; string, for example, is a typedef for a class template.) In this way, the interface looks like simple classes. The HepMC package will provide similar typedef's using GenParticle as the template's ParticleType parameter. DecayModelFactory and Registration ---------------------------------- This note explains the purpose of the DecayModelFactory class, and relations among that and the user classes derived from DecayModel. Consider a routine (for example, addPythiaData) which needs to form the DecayChannels for particles, based on a stream of input. The decay information about a channel consists of . an indication of which class of DecayModel is to be used, and . data to affect the particulars of this instance of that class. (For concreteness, we take the former to be a string `name' and the latter to be a vector of doubles `params'.) Given this information, the code needs to instantiate an instance of the proper class, configured based on those parameters. The class DecayModelFactory does this: DecayModel * DecayModelFactory::make ( name, params ) returns a pointer to an object that has three properties: . The object is an instance of some class, derived from DecayModel . The type of class is selected based on the name supplied to make() . The object has been configured based on params To be able to do this, 1 The classes derived from DecayModel must implement a make() method which will configure their data based on the passed in params. 2 DecayModelFactory must have a map (or the logical equivalent) that lets it take a name and deliver a pointer to the make() method of the class associated with that name. 3 When the factory is given a string name, and asked for a DecayModel*, it calls the appropiate make() method. Requirement 1 implies that each class derived from DecayModel must implement a static method make(vector params), which in turn calls the class constructor. The mechanism for requirement 2 is more subtle. Each class derived from DecayModel should have a static ModelRegister installer. Outside the class definition (probably in its .cc file but you could safely do it in the header), that static member is defined as: ModelRegister YourModelClassName::installer( "YourModelName", YourModelClassName::make ); This accomplishes requirement 2 because the constructor of ModelRegister gets the (singleton) DecayModelFactory and calls registerMe ( name, maker ) which does myMap[name] = maker. Applications involving multiple ParticleDataTable's will still have one DecayModelFactory: The meaning of a model "3-body" will be the same whether it is used by a particle in table A or table B. Once the make method and installer are in place, requirement 3 is easy: DecayModelFactory just looks up the make() to invoke, invokes it and returns the result. How do we document this? We need to provide a short step-by-step mantra for making the boilerplate of a DecayModel, and the simplest possible correct example, which users will use as a starting point.