From 30b5160ce9db8cbe800b3393271224a014b0aecf Mon Sep 17 00:00:00 2001 From: Edward Moyse Date: Mon, 12 Apr 2021 15:10:53 +0200 Subject: [PATCH] feat(event-display): Remove LineHits and use hits to extend tracks jivexml-loader: - Store hits associated with tracks (would be nice not to show this in the collections view) - Add new function jivexml-loader:getPositionOfHit using hit identifiers - Rework how we store hits, so eventually we can maybe cut on them - Remove calls to HitLines, since now Line hits are just another type of hit phoenix-loader: - Rework how we handle hits to remove HitLines. Added ability to concatonateObjs in addObjectType and addObjectCollection, so we can still benefit from the performance of having one BufferGeometry per Hit type. phoenix-objects: - getHits can now handle types (default is 'Point') - added new hitsToPoints and hitsToLines to handle the two current cases --- .../src/loaders/jivexml-loader.ts | 179 ++++++++++++------ .../src/loaders/objects/phoenix-objects.ts | 104 ++++------ .../src/loaders/phoenix-loader.ts | 38 ++-- 3 files changed, 185 insertions(+), 136 deletions(-) diff --git a/packages/phoenix-event-display/src/loaders/jivexml-loader.ts b/packages/phoenix-event-display/src/loaders/jivexml-loader.ts index 6bce78c8b..586ea83cf 100644 --- a/packages/phoenix-event-display/src/loaders/jivexml-loader.ts +++ b/packages/phoenix-event-display/src/loaders/jivexml-loader.ts @@ -41,7 +41,6 @@ export class JiveXMLLoader extends PhoenixLoader { lumiBlock: firstEvent.getAttribute('lumiBlock'), time: firstEvent.getAttribute('dateTime'), Hits: undefined, - HitLines: {}, Tracks: {}, Jets: {}, CaloClusters: {}, @@ -51,9 +50,6 @@ export class JiveXMLLoader extends PhoenixLoader { Photons: {}, }; - // Tracks - this.getTracks(firstEvent, eventData); - // Hits this.getPixelClusters(firstEvent, eventData); this.getSCTClusters(firstEvent, eventData); @@ -63,6 +59,10 @@ export class JiveXMLLoader extends PhoenixLoader { this.getMuonPRD(firstEvent, 'TGC', eventData); this.getMuonPRD(firstEvent, 'CSCD', eventData); + // Tracks + // (must be filled after hits because it might use them) + this.getTracks(firstEvent, eventData); + // Jets this.getJets(firstEvent, eventData); @@ -115,13 +115,35 @@ export class JiveXMLLoader extends PhoenixLoader { .split(' ') .map(String); } + /** + * Try to get the position of a hit (i.e. linked from a track) + * @param hitIdentifier The unique identifier of this hit. + * @param eventData The complete eventData, which must contain hits. + * @returns [found, x, y, z]. + */ + private getPositionOfHit( + hitIdentifier, + eventData: { Hits: [[{ id: number; pos: any }]] } + ) { + for (const hitcollection in eventData.Hits) { + for (const hit of eventData.Hits[hitcollection]) { + if (hit == null) { + console.log('Empty hit'); + } else { + if ('id' in hit && hit.id == hitIdentifier) + return [true, hit.pos[0], hit.pos[1], hit.pos[2]]; + } + } + } + return [false, 0, 0, 0]; + } /** * Extract Tracks from the JiveXML data format and process them. * @param firstEvent First "Event" element in the XML DOM of the JiveXML data format. * @param eventData Event data object to be updated with Tracks. */ - public getTracks(firstEvent: Element, eventData: { Tracks: any }) { + public getTracks(firstEvent: Element, eventData: { Tracks: any; Hits: any }) { const tracksHTML = firstEvent.getElementsByTagName('Track'); const trackCollections = Array.from(tracksHTML); const nameOfCollection = 'Tracks'; @@ -174,39 +196,73 @@ export class JiveXMLLoader extends PhoenixLoader { const z0 = this.getNumberArrayFromHTML(collection, 'z0'); const phi0 = this.getNumberArrayFromHTML(collection, 'phi0'); const cotTheta = this.getNumberArrayFromHTML(collection, 'cotTheta'); + const hits = this.getNumberArrayFromHTML(collection, 'hits'); + const numHits = this.getNumberArrayFromHTML(collection, 'numHits'); - if (collection.getElementsByTagName('numDoF').length) { - var author = collection - .getElementsByTagName('numDoF')[0] - .innerHTML.replace(/\r\n|\n|\r/gm, ' ') - .trim() - .split(' ') - .map(Number); + if (collection.getElementsByTagName('trackAuthor').length) { + var trackAuthor = this.getNumberArrayFromHTML( + collection, + 'trackAuthor' + ); } - const numHits = this.getNumberArrayFromHTML(collection, 'numHits'); - - let polylineCounter = 0; + let polylineCounter = 0, + hitsCounter = 0; for (let i = 0; i < numOfTracks; i++) { - const track = { chi2: 0.0, dof: 0.0, pT: 0.0, pos: [], dparams: [] }; - if (chi2.length > 0) track.chi2 = chi2[i]; - if (numDoF.length > 0) track.dof = numDoF[i]; + const track = { + chi2: 0.0, + dof: 0.0, + pT: 0.0, + pos: [], + dparams: [], + hits: {}, + author: {}, + }; + if (chi2.length >= i) track.chi2 = chi2[i]; + if (numDoF.length >= i) track.dof = numDoF[i]; + if (trackAuthor.length >= i) track.author = trackAuthor[i]; const theta = Math.tan(cotTheta[i]); track.pT = Math.abs(pT[i]); const momentum = (pT[i] / Math.sin(theta)) * 1000; // JiveXML uses GeV track.dparams = [d0[i], z0[i], phi0[i], theta, 1.0 / momentum]; - const pos = []; + const pos = [], + listOfHits = []; + let maxR = 0.0, + x = 0.0, + y = 0.0, + z = 0.0; if (numPolyline) { for (let p = 0; p < numPolyline[i]; p++) { - pos.push([ - polylineX[polylineCounter + p], - polylineY[polylineCounter + p], - polylineZ[polylineCounter + p], - ]); + x = polylineX[polylineCounter + p]; + y = polylineY[polylineCounter + p]; + z = polylineZ[polylineCounter + p]; + pos.push([x, y, z]); + maxR = Math.sqrt(x * x + y * y + z * z); } polylineCounter += numPolyline[i]; track.pos = pos; } + // Now loop over hits, and if possible, see if we can extend the track + if (numHits.length>0) { + let hitIdentifier = 0; + let distance = 0.0; + let found = false; + for (let p = 0; p < numHits[i]; p++) { + hitIdentifier = hits[hitsCounter + p]; + listOfHits.push(hitIdentifier); + // Now try to find matching hit + [found, x, y, z] = this.getPositionOfHit(hitIdentifier, eventData); + if (found) { + distance = Math.sqrt(x * x + y * y + z * z); + if (distance > maxR) { + track.pos.push([x, y, z]); + } + } + } + hitsCounter += numHits[i]; + track.hits = listOfHits; + } + jsontracks.push(track); } @@ -227,7 +283,7 @@ export class JiveXMLLoader extends PhoenixLoader { } const pixClustersHTML = firstEvent.getElementsByTagName('PixCluster')[0]; const numOfClusters = Number(pixClustersHTML.getAttribute('count')); - + const id = this.getNumberArrayFromHTML(pixClustersHTML, 'id'); const x0 = pixClustersHTML .getElementsByTagName('x0')[0] .innerHTML.replace(/\r\n|\n|\r/gm, ' ') @@ -248,11 +304,13 @@ export class JiveXMLLoader extends PhoenixLoader { .map(Number); eventData.Hits.Pixel = []; - const temp = []; // Ugh + for (let i = 0; i < numOfClusters; i++) { - temp.push([x0[i] * 10.0, y0[i] * 10.0, z0[i] * 10.0]); + let pixel = { pos: [], id: 0 }; + pixel.pos = [x0[i] * 10.0, y0[i] * 10.0, z0[i] * 10.0]; + pixel.id = id[i]; + eventData.Hits.Pixel.push(pixel); } - eventData.Hits.Pixel.push(temp); } /** @@ -328,13 +386,13 @@ export class JiveXMLLoader extends PhoenixLoader { .split(' ') .map(Number); eventData.Hits.SCT = []; - const temp = []; // Ugh for (let i = 0; i < numOfSCTClusters; i++) { - temp.push([x0[i] * 10.0, y0[i] * 10.0, z0[i] * 10.0]); + let sct = { pos: [], id: 0 }; + sct.pos = [x0[i] * 10.0, y0[i] * 10.0, z0[i] * 10.0]; + sct.id = id[i]; + eventData.Hits.SCT.push(sct); } - - eventData.Hits.SCT.push(temp); } /** @@ -342,10 +400,7 @@ export class JiveXMLLoader extends PhoenixLoader { * @param firstEvent First "Event" element in the XML DOM of the JiveXML data format. * @param eventData Event data object to be updated with TRT Drift Circles. */ - public getTRT_DriftCircles( - firstEvent: Element, - eventData: { HitLines: any } - ) { + public getTRT_DriftCircles(firstEvent: Element, eventData: { Hits: any }) { if (firstEvent.getElementsByTagName('TRT').length === 0) { return; } @@ -402,41 +457,49 @@ export class JiveXMLLoader extends PhoenixLoader { .split(' ') .map(Number); - eventData.HitLines.TRT = []; - let temp = []; // Ugh + eventData.Hits.TRT = []; + // Hardcoding TRT size here. Could maybe think of generalising this? for (let i = 0; i < numOfDC; i++) { + let trt = { + pos: [], + id: 0, + type: 'Line', + driftR: 0.0, + threshold: 0.0, + timeOverThreshold: 0.0, + }; + if (sub[i] == 1 || sub[i] == 2) { // Barrel - rhoz = radial position const z1 = sub[i] == 1 ? -3.5 : 3.5; const z2 = sub[i] == 1 ? -742 : 742; - temp.push([ + trt.pos = [ Math.cos(phi[i]) * rhoz[i] * 10.0, Math.sin(phi[i]) * rhoz[i] * 10.0, - z1, - ]); - temp.push([ - Math.cos(phi[i]) * rhoz[i] * 10.0, + z1, Math.cos(phi[i]) * rhoz[i] * 10.0, Math.sin(phi[i]) * rhoz[i] * 10.0, z2, - ]); + ]; } else { // endcap - rhoz = z position const r1 = Math.abs(rhoz[i]) > 280 ? 480 : 640; const r2 = 1030; - temp.push([ + trt.pos = [ Math.cos(phi[i]) * r1, Math.sin(phi[i]) * r1, rhoz[i] * 10.0, - ]); - temp.push([ Math.cos(phi[i]) * r2, Math.sin(phi[i]) * r2, rhoz[i] * 10.0, - ]); + ]; } + trt.id = id[i]; + trt.driftR = driftR[i]; + trt.threshold = threshold[i]; + trt.timeOverThreshold = timeOverThreshold[i]; + eventData.Hits.TRT.push(trt); } - eventData.HitLines.TRT.push(temp); } /** @@ -447,7 +510,7 @@ export class JiveXMLLoader extends PhoenixLoader { public getMuonPRD( firstEvent: Element, name: string, - eventData: { HitLines: any } + eventData: { Hits: any } ) { if (firstEvent.getElementsByTagName(name).length === 0) { return; @@ -500,26 +563,28 @@ export class JiveXMLLoader extends PhoenixLoader { .split(' ') .map(Number); - eventData.HitLines[name] = []; - let temp = []; // Ugh + eventData.Hits[name] = []; + let radius = 0.0, scaling = 0.0; for (let i = 0; i < numOfDC; i++) { + let muonHit = { pos: [], id: 0, type: 'Line', identifier: 0 }; + radius = Math.sqrt(x[i] * x[i] + y[i] * y[i]); scaling = length[i] / radius; - temp.push([ + muonHit.pos = [ x[i] * 10.0 - y[i] * scaling, y[i] * 10.0 + x[i] * scaling, z[i] * 10.0, - ]); - temp.push([ x[i] * 10.0 + y[i] * scaling, y[i] * 10.0 - x[i] * scaling, z[i] * 10.0, - ]); + ]; + muonHit.id = id[i]; + muonHit.identifier = identifier[i]; + eventData.Hits[name].push(muonHit); } - eventData.HitLines[name].push(temp); } /** diff --git a/packages/phoenix-event-display/src/loaders/objects/phoenix-objects.ts b/packages/phoenix-event-display/src/loaders/objects/phoenix-objects.ts index 2c7ac8b82..e0de12069 100644 --- a/packages/phoenix-event-display/src/loaders/objects/phoenix-objects.ts +++ b/packages/phoenix-event-display/src/loaders/objects/phoenix-objects.ts @@ -55,11 +55,6 @@ export class PhoenixObjects { if (!positions) { return; } - // Track with no points - // if (positions.length==0) { - // console.log("Track with no positions.") - // return; - // } // Track with too few points are extrapolated with RungeKutta if (positions.length < 3) { @@ -75,7 +70,8 @@ export class PhoenixObjects { positions = RKHelper.extrapolateTrackPositions(trackParams, inBounds); } - } + } + // Check again, in case there was an issue with the extrapolation. if (positions.length < 3) { return; @@ -86,15 +82,6 @@ export class PhoenixObjects { ? parseInt(trackParams.color, 16) : EVENT_DATA_TYPE_COLORS.Tracks.getHex(); - // // Apply pT cut TODO - make this configurable. - // const momentum = trackParams.mom; - // if (momentum) { - // if (momentum[0] * momentum[0] + momentum[1] * momentum[1] + momentum[2] * momentum[2] < 0.25) { - // // console.log('Track mom<0.5 GeV. Skipping. Positions are: ' + positions + ' particle_id: ' + track.particle_id); - // return; - // } - // } - const points = []; for (let i = 0; i < positions.length; i++) { @@ -205,33 +192,46 @@ export class PhoenixObjects { /** * Process the Hits from the given parameters and get them as a geometry. - * @param hitsParams Parameters for the Hits. + * @param hitsParams Hit object. Must contain 'pos', the array of [x,y,z] positions, Can optionally contain extraInfo, which will be added to the resultant hit. + * @param type Tells Phoenix how to draw this - currently can be Point (default), or Line. * @returns Hits object. */ - public static getHits(hitsParams: any): Object3D { - let positions: any[]; - let hitsParamsClone: any; - - // If the parameters is an object then take out 'pos' for hits positions - if (typeof hitsParams === 'object' && !Array.isArray(hitsParams)) { - positions = [hitsParams.pos]; - hitsParamsClone = hitsParams; - } else { - positions = hitsParams; - hitsParamsClone = { pos: hitsParams }; + public static getHits(hitsParams: [ { pos:[], type?: string }]): Object3D { + let hitsParamsClone = hitsParams; + let type:string = 'Point'; // Default is point and 3 coordinates per hit + let coordlength = 3; + if (hitsParams.length>1){ + // Peek at first one. Would be better to make these properties of the collections. + if ('type' in hitsParams[0]) { + type = hitsParams[0].type; + coordlength=6; + } } - // attributes - const pointPos = new Float32Array(positions.length * 3); + const pointPos = new Float32Array(hitsParams.length*coordlength); let i = 0; - for (const hit of positions) { - pointPos[i] = hit[0]; - pointPos[i + 1] = hit[1]; - pointPos[i + 2] = hit[2]; - i += 3; + let imax = 0; + for (const hit of hitsParams) { + imax = i+coordlength; + for (let j=0 ; j cut.field in objectCollection[0]); @@ -307,21 +305,31 @@ export class PhoenixLoader implements EventDataLoader { * @param collectionName Label to UNIQUELY identify the collection. * @param getObject Handles reconstructing the objects of the collection. * @param objectGroup Group containing the collections of the same object type. + * @param concatonateObjs If true, don't process objects individually, but process as a group (e.g. for point hits). */ private addCollection( objectCollection: any, collectionName: string, getObject: (object: any) => Object3D, - objectGroup: Group + objectGroup: Group, + concatonateObjs: Boolean ) { const collscene = new Group(); collscene.name = collectionName; - for (const objectParams of objectCollection) { - const object = getObject.bind(this)(objectParams); + if (concatonateObjs) { + //in this case, we just pass the entire collection in + const object = getObject.bind(this)(objectCollection); if (object) { collscene.add(object); } + } else { + for (const objectParams of objectCollection) { + const object = getObject.bind(this)(objectParams); + if (object) { + collscene.add(object); + } + } } objectGroup.add(collscene);