Revised EDM Model Notes 03-08 March 1999 Rob Kennedy I) Prototype Scale-up plans II) Miscellaneous III) ToyModel description IV) Review of existing use-cases V) StorableObject ------------------------------------------------------------------------------- I) The design and implementation of the new Event Data Model will proceed in at least three incremental stages 1) ToyModel - Quicky implementation of bare bones EDM infrastructure to test ideas and use-cases in the EDM proposal. Toy implementations of a few higher-level physics objects, collections, and non-physics objects are used to demonstrate use-cases. Actual I/O is primitive, if at all. Completion due 01 April 1999. This date is firm, as proposal should be "delivered" before this date. 2) Prototype - More realistic implementation of EDM infrastructure, uses a few realistic (near-release-form) higher-level physics objects and associated collections, and uses an initial implementation of I/O using ROOT. Integration with Framework/AC++ begins after this. Completion due 01 May 1999. This date is arbitrarily chosen. 3) Release - Robust implementation of all of EDM infrastructure, uses a variety of released higher-level physics objects and associated collections, and uses a robust implementation of I/O using ROOT and ROOT branches. Integration with Framework is completed. Completion due 01 June 1999. This date is firm, as it coincides with the June Milestone. ------------------------------------------------------------------------------- II) Miscellaneous A) ObjectNumbers are unique across all ObjectNames. This is a change from what has been presented (where ObjectNumbers were unique only for a particular ObjectName). The altered approach will be easier to implement, yields space savings. Is there a backward-compatibility problem as was feared? B) EDM calls setLinks() inside of File::write(), setPointers() inside of File::read(). The user does not have to do anything by default. In a change from the presentation Wednesday on the EDM, several of us have agreed to consider having the setPointers() and setLinks() methods to manage StorablePointers called by the EDM on all objects in the EventRecord automatically as a part of writing an event out or reading it back in. Users would not have to call these methods at all. SetPointers would be called in File::read(). IMHO, we should not call setLinks() in EventRecord::insert() since then the user would have to get the order of insert() calls for associated objects correct. So, we call setLinks() in File::write(). A consequence of this is that mutually associated objects can be managed without a separate association object having to be stored. In other words, cyclic link chains can be stored in place if we call setLinks() in File::write() and setPointers() in File::read(). This loosens the constraint that all objects be read-only in a very specific way... StorablePointers keep their logical state, but change in their physical state during read() and write() to/from a transient format and a storable format. Every StorableObject object would have to have a setLinks() and a setPointers() method even if it does not contain a StorablePointer. This permits the EDM to iterate through all objects in the EventRecord, setting links or restoring transient pointers. If an object is dropped from an event (due to an I/O list drop), associations to it will still be set (setLinks() is called before I/O module drops objects) in order to keep the maximum internal consistency. Users may still prefer to use separate AssociationSummary objects to store complicated associations separate from other StorableObject's data, but that is a choice now, rather than a requirement of the model. ------------------------------------------------------------------------------- III) ToyModel description A) Goal The goal of the ToyModel stage is to demonstrate the use-cases listed in the EDM proposal and explore possible variants in design and implementation that may come from discussions on the EDM. Implementation terminology (class names, methods) as well as the EDM itself are somewhat frozen after this stage is completed. The ToyModel does *not* fully implement the final EDM, only those pieces necessary to demonstrate the basics in the proposal are valid. B) Classes to implement Round 1 1) StorableObject 2) ObjectName 3) ObjectNumber 4) ObjectId 5) StorableCollection 6) StorableRefCollection 7) StorableRefList 8) EventRecordId 9) EventRecordInternals 10) ObjectNumberManager (used by EventRecord) 11) EventRecord 12) StorablePointer 13) ObjectHandle 14) ToyTrack This is enough to create an event (header + tracks), store it, restore it, and testing ObjectNumber policy as it applies to the EventRecordInternals object itself. Round 2 1) ToyMuon 2) ToyMuonList 3) ToyTrackList 4) ToyBrother 5) ToySister 6) AssociationSummary This will test storing and retrieving storable objects, unidirectional associations, homogeneous collection support, cyclic association chains, and heterogenous collection support. Round 3 ?) I/O List classes ?) Primitive ROOT I/O This will test I/O filtering ideas and ROOT I/O ideas. ------------------------------------------------------------------------------- IV) TotalMissingE is a class derived from StorableObject which has no associations. // Create and Store a StorableObject // ================================= bool event_AAA(EventRecord* p_event) { float E_mag = 0.0 ; float E_eta = 0.0 ; float E_phi = 0.0 ; ObjectHandle myMissECalc = new TotalMissingE(E_mag, E_eta, E_phi) ; // Note: Add an argument identifying *who* created this StorableObject // Note: Add an argument identifying *what* were parameters used StorableLink myMissEID = p_record->insert(myMissECalc) ; if (myMissEID.is_invalid()) { /* ERROR: */ } return(true) ; } // Retrieve a StorableObject // ================================= bool event_BBB(EventRecord* p_event) { RecordIterator otherIter(p_record, SelectByName("TotalMissingE") ) ; if otherIter.is_invalid()) { /* ERROR: object is not in event */ } ObjectHandle otherMissECalc(myIter) ; // OBJECT MARKED AS READ-ONLY if (otherMissECalc.is_invalid()) { /* ERROR: failure in constructor */ } float E_mag = otherMissECalc->e_mag() ; float E_eta = otherMissECalc->e_eta() ; float E_phi = otherMissECalc->e_phi() ; otherMissECalc.set_et_mag(9.0) ; // ERROR: object is read-only return(true) ; } // Update a StorableObject // ================================= bool event_BBB(EventRecord* p_event) { RecordIterator otherIter(p_record, SelectByName("TotalMissingE") ) ; if otherIter.is_invalid()) { /* ERROR: object is not in event */ } ObjectHandle otherMissECalc(myIter) ; // OBJECT MARKED AS READ-ONLY if (otherMissECalc.is_invalid()) { /* ERROR: failure in constructor */ } ObjectHandle otherMissECalc = new TotalMissingE(otherMissECalc) ; // Note: Add an argument identifying *who* created this StorableObject // Note: Add an argument identifying *what* were parameters used myMissECalc->set_e_mag(9.0) ; myMissECalc->set_e_eta(1.0) ; myMissECalc->set_e_phi(3.0) ; ObjectId myMissEID = p_record->insert(myMissECalc) ; // myMissEID != otherMissEID if (myMissEID.is_invalid()) { /* ERROR: insert failed */ } return(true) ; } // Delete a StorableObject // ================================= // Only AC++ actually deletes objects. A user can add a particular object or // a class of objects to a ``drop on output'' object list. Muon is associated with a Track expressed as a StorablePointer. Both Track and Muon derive from StorableObject. i) Create and Store ObjectHandle = new Track(...) ; ObjectHandle = new Muon(&aTrack) ; ObjectId trackId = p_record->insert(aTrack) ; ObjectId muonId = p_record->insert(aMuon) ; // setLinks() is called in File::write(). ii) retrieve // setPointers() is called in File::read(). RecordIterator muonIter(p_record, SelectByName("Muon") ) ; ObjectHandle aMuon(muonIter) ; // OBJECT MARKED AS READ-ONLY ObjectHandle aTrack(aMuon->track()) ; // OBJECT MARKED AS READ-ONLY double muon_pt = aMuon->pt() ; i) Create and Store ObjectHandle = new Brother(...) ; // NULL link to sister ObjectHandle = new Sister(&aBrother) ; aBrother.setPointer(&aSister) ; // state now fully defined ObjectId brotherId = p_record->insert(aBrother) ; ObjectId sisterId = p_record->insert(aSister) ; // If setLinks() is called in File::write(), Bi-directional links now supported. ii) Retrieve // setPointers() is called in File::read(). ObjectId brotherId = relationship.brotherId() ; RecordIterator brotherIter(p_record, SelectById(brotherId) ) ; ObjectHandle aBrother(brotherIter) ; // READ-ONLY ObjectHandle aSister(aBrother->sister()) ; // READ-ONLY ------------------------------------------------------------------------------- V) These are first drafts for the ToyModel. JBK has an alternative means of forcing the inheritance tree onto the heap. class StorableObject: virtual public MemoryPool { public: StorableObject(void) ; StorableObject(const StorableObject& object) ; StorableObject& operator=(const StorableObject& object) ; friend bool operator==(const StorableObject& object1, const StorableObject& object2) ; friend bool operator!=(const StorableObject& object1, const StorableObject& object2) ; virtual ObjectId id(void) const ; virtual ObjectName name(void) const ; virtual ObjectNumber number(void) const ; virtual bool is_stored(void) const ; // == (_number != ObjectNumber::Null) virtual bool is_valid(void) const ; virtual void print(void) const ; virtual bool set_links(void) = 0 ; virtual bool set_pointers(void) = 0 ; virtual bool stream_read(STREAM& stream) = 0 ; // STREAM == ?TBuffer? virtual bool stream_write(STREAM& stream) = 0 ; // STREAM == ?TBuffer? virtual bool is_persistable(void) const { return(false) ; } protected: ObjectNumber _number ; bool _is_read_only ; void set_object_number(const ObjectNumber& number) ; // EventRecord::insert() void set_read_only(void) ; // EventRecord::insert() virtual ~StorableObject(void) ; // Can only create via new on the heap } ; class ToyTrack: virtual public StorableObject { public: ToyTrack(void) ; ToyTrack(const ToyTrack& object) ; ToyTrack& operator=(const ToyTrack& object) ; friend bool operator==(const ToyTrack& object1, const ToyTrack& object2) ; friend bool operator!=(const ToyTrack& object1, const ToyTrack& object2) ; virtual bool set_links(void) ; virtual bool set_pointers(void) ; virtual bool stream_read(STREAM& stream) ; virtual bool stream_write(STREAM& stream) ; virtual bool is_persistable(void) const { return(true) ; } SignValue em_charge_sign(void) const ; // == positive, negative, neutral float x0(void) const ; float y0(void) const ; float z0(void) const ; float px(void) const ; float py(void) const ; float pz(void) const ; float pt(void) const ; float energy(void) const ; void set_em_charge_sign(const SignValue value) ; void set_x0(const float value) ; void set_y0(const float value) ; void set_z0(const float value) ; void set_px(const float value) ; void set_py(const float value) ; void set_pz(const float value) ; void set_energy(const float value) ; protected: SignValue _em_charge_sign ; float _x0 ; // SpaceLocation of Intercept float _y0 ; float _z0 ; float _px ; // FourMomentum float _py ; float _pz ; float _energy ; virtual ~ToyTrack(void) ; // Can only create via new on the heap } ;