src/loaders/edm4hep-json-loader.ts
Edm4hepJsonLoader for loading EDM4hep json dumps
Properties |
|
Methods |
|
constructor()
|
|
Defined in src/loaders/edm4hep-json-loader.ts:43
|
|
Create Edm4hepJsonLoader |
| Private Static Readonly pidColors |
Type : Record<edm4hep.ParticleType | string>
|
Default value : {
[edm4hep.ParticleType.Electron]: '00ff00',
[edm4hep.ParticleType.Muon]: 'ff00ff',
[edm4hep.ParticleType.Photon]: 'ff0000',
[edm4hep.ParticleType.Pion]: 'a52a2a',
[edm4hep.ParticleType.Proton]: '778899',
[edm4hep.ParticleType.Kaon]: '5f9ea0',
[edm4hep.ParticleType.Other]: '0000cd',
}
|
|
Defined in src/loaders/edm4hep-json-loader.ts:32
|
|
Colors per particle type, shared between tracks and hits Tracks receive hex color without '#' |
| Private Static Readonly pidNames |
Type : Record<number | edm4hep.ParticleType>
|
Default value : {
11: edm4hep.ParticleType.Electron,
13: edm4hep.ParticleType.Muon,
22: edm4hep.ParticleType.Photon,
111: edm4hep.ParticleType.Pion,
211: edm4hep.ParticleType.Pion,
2212: edm4hep.ParticleType.Proton,
321: edm4hep.ParticleType.Kaon,
}
|
|
Defined in src/loaders/edm4hep-json-loader.ts:20
|
|
PDG ID to particle type name |
| Private rawEventData |
Type : any
|
|
Defined in src/loaders/edm4hep-json-loader.ts:43
|
|
Event data loaded from EDM4hep JSON file |
| Protected eventData |
Type : PhoenixEventData
|
|
Inherited from
PhoenixLoader
|
|
Defined in
PhoenixLoader:34
|
|
Event data processed by the loader. |
| Private graphicsLibrary |
Type : ThreeManager
|
|
Inherited from
PhoenixLoader
|
|
Defined in
PhoenixLoader:30
|
|
ThreeService to perform three.js related functions. |
| Protected labelsObject |
Type : literal type
|
Default value : {}
|
|
Inherited from
PhoenixLoader
|
|
Defined in
PhoenixLoader:40
|
|
Object containing event object labels. |
| Protected loadingManager |
Type : LoadingManager
|
|
Inherited from
PhoenixLoader
|
|
Defined in
PhoenixLoader:36
|
|
Loading manager for loadable resources |
| Protected stateManager |
Type : StateManager
|
|
Inherited from
PhoenixLoader
|
|
Defined in
PhoenixLoader:38
|
|
Loading manager for loadable resources |
| Private ui |
Type : UIManager
|
|
Inherited from
PhoenixLoader
|
|
Defined in
PhoenixLoader:32
|
|
UIService to perform UI related functions. |
| Private assignPID | ||||||
assignPID(rawEvent: any)
|
||||||
|
Defined in src/loaders/edm4hep-json-loader.ts:143
|
||||||
|
Define particle PID based on link
Parameters :
Returns :
void
|
| Private convHSLtoHEX |
convHSLtoHEX(h: number, energy: number)
|
|
Defined in src/loaders/edm4hep-json-loader.ts:516
|
|
Helper conversion of HSL to hexadecimal
Returns :
string
|
| Private getCaloCells | ||||||
getCaloCells(caloCellCollection: edm4hep.CaloCell[])
|
||||||
|
Defined in src/loaders/edm4hep-json-loader.ts:337
|
||||||
|
Returns Calo cells
Parameters :
Returns :
CaloCellParams[]
|
| Private getCaloClusters | ||||||
getCaloClusters(caloClusterCollection: edm4hep.CaloCluster[])
|
||||||
|
Defined in src/loaders/edm4hep-json-loader.ts:396
|
||||||
|
Return Calo clusters
Parameters :
Returns :
CaloClusterParams[]
|
| Private getCollByID |
getCollByID(event: any, id: number)
|
|
Defined in src/loaders/edm4hep-json-loader.ts:484
|
|
Get the required collection
Returns :
any
|
| getEventData |
getEventData()
|
|
Defined in src/loaders/edm4hep-json-loader.ts:133
|
|
Output event data in Phoenix compatible format
Returns :
any
|
| Private getHits | |||||||||
getHits(rawEvent: any, hitCollection: edm4hep.Hit[])
|
|||||||||
|
Defined in src/loaders/edm4hep-json-loader.ts:261
|
|||||||||
|
Return tracker hits
Parameters :
Returns :
unknown[]
|
| Private getJets | ||||||
getJets(jetCollection: edm4hep.ReconstructedParticle[])
|
||||||
|
Defined in src/loaders/edm4hep-json-loader.ts:425
|
||||||
|
Return jets
Parameters :
Returns :
JetParams[]
|
| Private getMissingEnergy | ||||||
getMissingEnergy(missingEnergyCollection: edm4hep.ReconstructedParticle[])
|
||||||
|
Defined in src/loaders/edm4hep-json-loader.ts:454
|
||||||
|
Return missing energy
Parameters :
Returns :
MissingEnergyParams[]
|
| Private getNumEvents |
getNumEvents()
|
|
Defined in src/loaders/edm4hep-json-loader.ts:138
|
|
Return number of events
Returns :
number
|
| Private getTracks | |||||||||
getTracks(rawEvent: any, trackCollection: edm4hep.Track[])
|
|||||||||
|
Defined in src/loaders/edm4hep-json-loader.ts:199
|
|||||||||
|
Return tracks
Parameters :
Returns :
unknown[]
|
| Private getVertices | ||||||
getVertices(vertexCollection: edm4hep.Vertex[])
|
||||||
|
Defined in src/loaders/edm4hep-json-loader.ts:183
|
||||||
|
Return vertices
Parameters :
Returns :
VertexParams[]
|
| processEventData |
processEventData()
|
|
Defined in src/loaders/edm4hep-json-loader.ts:57
|
|
Process raw EDM4hep JSON event data into the Phoenix format
Returns :
boolean
|
| Private randomColor |
randomColor()
|
|
Defined in src/loaders/edm4hep-json-loader.ts:476
|
|
Return a random colour
Returns :
string
|
| setRawEventData | ||||||
setRawEventData(rawEventData: any)
|
||||||
|
Defined in src/loaders/edm4hep-json-loader.ts:52
|
||||||
|
Put raw EDM4hep JSON event data into the loader
Parameters :
Returns :
void
|
| Private valToLightness |
valToLightness(v: number, min: number, max: number)
|
|
Defined in src/loaders/edm4hep-json-loader.ts:490
|
|
Return a lightness value from the passed number and range
Returns :
number
|
| Private valToOpacity |
valToOpacity(v: number, min: number, max: number)
|
|
Defined in src/loaders/edm4hep-json-loader.ts:503
|
|
Return a opacity value from the passed number and range
Returns :
number
|
| Private addCollection | ||||||||||||||||||||||||||||
addCollection(objectCollection: any, collectionName: string, getObject: (object: any, typeName: string) => void, typeName: string, objectGroup: Group, concatonateObjs: boolean)
|
||||||||||||||||||||||||||||
|
Inherited from
PhoenixLoader
|
||||||||||||||||||||||||||||
|
Defined in
PhoenixLoader:314
|
||||||||||||||||||||||||||||
|
Adds to the event display all the objects inside a collection.
Parameters :
Returns :
void
|
| Public addLabelToEventObject | ||||||||||||||||
addLabelToEventObject(label: string, collection: string, indexInCollection: number)
|
||||||||||||||||
|
Inherited from
PhoenixLoader
|
||||||||||||||||
|
Defined in
PhoenixLoader:531
|
||||||||||||||||
|
Add label of event object to the labels object.
Parameters :
Returns :
string
A unique label ID string. |
| Protected addObjectType | |||||||||||||||||||||||||||||||||||
addObjectType(object: any, getObject: any, typeName: string, concatonateObjs: boolean, cuts?: Cut[], extendEventDataTypeUI?: (typeFolder?: GUI, typeFolderPM?: PhoenixMenuNode) => void)
|
|||||||||||||||||||||||||||||||||||
|
Inherited from
PhoenixLoader
|
|||||||||||||||||||||||||||||||||||
|
Defined in
PhoenixLoader:233
|
|||||||||||||||||||||||||||||||||||
|
Adds to the event display all collections of a given object type.
Parameters :
Returns :
void
|
| Public addScaleOptions | ||||||||||||||||
addScaleOptions(configKey: string, configLabel: string, scaleFunction: (value: number) => void)
|
||||||||||||||||
|
Inherited from
PhoenixLoader
|
||||||||||||||||
|
Defined in
PhoenixLoader:568
|
||||||||||||||||
|
Get function to add options to scale event object type by.
Parameters :
Function which adds scale options to Phoenix menu and dat.GUI menu. |
| Public buildEventData | ||||||||||||||||||||
buildEventData(eventData: PhoenixEventData, graphicsLibrary: ThreeManager, ui: UIManager, infoLogger: InfoLogger)
|
||||||||||||||||||||
|
Inherited from
PhoenixLoader
|
||||||||||||||||||||
|
Defined in
PhoenixLoader:58
|
||||||||||||||||||||
|
Takes an object that represents ONE event and takes care of adding the different objects to the graphics library and the UI controls.
Parameters :
Returns :
void
|
| Public getCollection | ||||||||
getCollection(collectionName: string)
|
||||||||
|
Inherited from
PhoenixLoader
|
||||||||
|
Defined in
PhoenixLoader:131
|
||||||||
|
Get the collection with the given collection name from the event data.
Parameters :
Returns :
any
An object containing the collection. |
| Public getCollections |
getCollections()
|
|
Inherited from
PhoenixLoader
|
|
Defined in
PhoenixLoader:110
|
|
Get list of collections in the event data.
Returns :
literal type
List of all collection names. |
| Protected getCompound | |||||||||||||||||||||||||
getCompound(params: any, name: string, objectIsTrack: boolean, objectIsCluster: boolean)
|
|||||||||||||||||||||||||
|
Inherited from
PhoenixLoader
|
|||||||||||||||||||||||||
|
Defined in
PhoenixLoader:379
|
|||||||||||||||||||||||||
|
Process the compound object (e.g. Muon, Electron, Photon) from the given parameters and get it as a group. FIXME. This is currently here and not in PhoenixObjects because we need to handle linked objects.
Parameters :
Returns :
Object3D
Muon group containing Clusters and Tracks. |
| Protected getCompoundCluster |
getCompoundCluster(params: any, name: string)
|
|
Inherited from
PhoenixLoader
|
|
Defined in
PhoenixLoader:369
|
|
Process the compound object of cluster type (e.g. Photon, ..) from the given parameters and get it as a group.
Returns :
Object3D
|
| Protected getCompoundTrack |
getCompoundTrack(params: any, name: string)
|
|
Inherited from
PhoenixLoader
|
|
Defined in
PhoenixLoader:363
|
|
Process the compound object of track type (e.g. Muon, Electron, ..) from the given parameters and get it as a group.
Returns :
Object3D
|
| getEventMetadata |
getEventMetadata()
|
|
Inherited from
PhoenixLoader
|
|
Defined in
PhoenixLoader:477
|
|
Get metadata associated to the event (experiment info, time, run, event...).
Returns :
any[]
Metadata of the event. |
| Public getEventsList | ||||||||
getEventsList(eventsData: PhoenixEventsData)
|
||||||||
|
Inherited from
PhoenixLoader
|
||||||||
|
Defined in
PhoenixLoader:94
|
||||||||
|
Get the list of event names from the event data.
Parameters :
Returns :
string[]
List of event names. |
| Public getLabelsObject |
getLabelsObject()
|
|
Inherited from
PhoenixLoader
|
|
Defined in
PhoenixLoader:557
|
|
Get the object containing labels.
Returns :
literal type
The labels object. |
| Private getObjectTypeCollections | ||||||||
getObjectTypeCollections(object: any)
|
||||||||
|
Inherited from
PhoenixLoader
|
||||||||
|
Defined in
PhoenixLoader:349
|
||||||||
|
Get collection names of a given object type.
Parameters :
Returns :
string[]
List of collection names of an object type (Tracks, Jets, CaloClusters etc.). |
| Protected getObjectTypeConfigs |
getObjectTypeConfigs()
|
|
Inherited from
PhoenixLoader
|
|
Defined in
PhoenixLoader:153
|
|
Get the object type configs for this loader. Override in subclasses to add experiment-specific types or modify defaults.
Returns :
ObjectTypeConfig[]
|
| Protected loadObjectTypes | ||||||||
loadObjectTypes(eventData: PhoenixEventData)
|
||||||||
|
Inherited from
PhoenixLoader
|
||||||||
|
Defined in
PhoenixLoader:161
|
||||||||
|
Load all object types from event data using the registry configs.
Parameters :
Returns :
void
|
import { PhoenixLoader } from './phoenix-loader';
import {
PhoenixEventData,
VertexParams,
TrackParams,
HitParams,
CaloCellParams,
CaloClusterParams,
JetParams,
MissingEnergyParams,
} from '../lib/types/event-data';
import { edm4hep } from '../lib/types/edm4hep';
import { ObjectID } from '../lib/types/edm4hep-schemas/utils';
/**
* Edm4hepJsonLoader for loading EDM4hep json dumps
*/
export class Edm4hepJsonLoader extends PhoenixLoader {
/** PDG ID to particle type name */
private static readonly pidNames: Record<number, edm4hep.ParticleType> = {
11: edm4hep.ParticleType.Electron,
13: edm4hep.ParticleType.Muon,
22: edm4hep.ParticleType.Photon,
111: edm4hep.ParticleType.Pion,
211: edm4hep.ParticleType.Pion,
2212: edm4hep.ParticleType.Proton,
321: edm4hep.ParticleType.Kaon,
};
/** Colors per particle type, shared between tracks and hits
* Tracks receive hex color without '#' */
private static readonly pidColors: Record<edm4hep.ParticleType, string> = {
[edm4hep.ParticleType.Electron]: '00ff00',
[edm4hep.ParticleType.Muon]: 'ff00ff',
[edm4hep.ParticleType.Photon]: 'ff0000',
[edm4hep.ParticleType.Pion]: 'a52a2a',
[edm4hep.ParticleType.Proton]: '778899',
[edm4hep.ParticleType.Kaon]: '5f9ea0',
[edm4hep.ParticleType.Other]: '0000cd',
};
/** Event data loaded from EDM4hep JSON file */
private rawEventData: any;
/** Create Edm4hepJsonLoader */
constructor() {
super();
this.eventData = {};
}
/** Put raw EDM4hep JSON event data into the loader */
setRawEventData(rawEventData: any) {
this.rawEventData = rawEventData;
}
/** Process raw EDM4hep JSON event data into the Phoenix format */
processEventData(): boolean {
// Iterate over events
Object.entries(this.rawEventData).forEach(
([eventName, rawEvent]: [string, edm4hep.Event]) => {
const newEvent: PhoenixEventData = {
'event number': 0,
'run number': 0,
Vertices: {},
Tracks: {},
Hits: {},
CaloClusters: {},
CaloCells: {},
Jets: {},
MissingEnergy: {},
};
this.assignPID(rawEvent);
// Iterate over event collections
Object.entries(rawEvent).forEach(
([collName, { collType, collection }]: [string, edm4hep.Item]) => {
switch (collType) {
case 'edm4hep::EventHeaderCollection':
newEvent['event number'] = Number(
collection[0].eventNumber ?? 0,
);
newEvent['run number'] = Number(collection[0].runNumber ?? 0);
break;
case 'edm4hep::VertexCollection':
newEvent.Vertices[collName] = this.getVertices(collection);
break;
case 'edm4hep::TrackCollection':
this.getTracks(rawEvent, collection as edm4hep.Track[]).forEach(
([label, arr]) => {
newEvent.Tracks[`${collName} | ${label}`] = arr;
},
);
break;
case 'edm4hep::TrackerHitCollection':
case 'edm4hep::TrackerHit3DCollection':
case 'edm4hep::SimTrackerHitCollection':
// case 'edm4hep::TrackerHitPlaneCollection':
// case 'edm4hep::SenseWireHitCollection':
this.getHits(rawEvent, collection).forEach(([label, arr]) => {
newEvent.Hits[`${collName} | ${label}`] = arr;
});
break;
case 'edm4hep::CalorimeterHitCollection':
case 'edm4hep::SimCalorimeterHitCollection':
newEvent.CaloCells[collName] = this.getCaloCells(collection);
break;
case 'edm4hep::ClusterCollection':
newEvent.CaloClusters[collName] =
this.getCaloClusters(collection);
break;
case 'edm4hep::ReconstructedParticleCollection':
if (collName === 'Jet')
newEvent.Jets[collName] = this.getJets(collection);
else if (collName.toLowerCase().includes('missing'))
newEvent.MissingEnergy[collName] =
this.getMissingEnergy(collection);
break;
}
},
);
this.eventData[eventName] = newEvent;
console.log(eventName, newEvent);
},
);
return true;
}
/** Output event data in Phoenix compatible format */
getEventData(): any {
return this.eventData;
}
/** Return number of events */
private getNumEvents(): number {
return Object.keys(this.rawEventData).length;
}
/** Define particle PID based on link */
private assignPID(rawEvent: any) {
let linkCollection: edm4hep.Link[] | edm4hep.Association[] | null = null;
if ('MCRecoAssociations' in rawEvent)
// Schema 1 and 2
linkCollection = rawEvent.MCRecoAssociations.collection;
else if ('RecoMCLink' in rawEvent)
// Schema 2 and 3
linkCollection = rawEvent.RecoMCLink.collection;
const reconstructedParticleCollection = rawEvent.ReconstructedParticles
?.collection as edm4hep.ReconstructedParticle[];
const mcParticleCollection = rawEvent.Particle
?.collection as edm4hep.MCParticle[];
const eFlowTrackCollection = rawEvent.EFlowTrack
?.collection as edm4hep.Track[];
if (
!linkCollection ||
!reconstructedParticleCollection ||
!mcParticleCollection ||
!eFlowTrackCollection
)
return;
linkCollection.forEach((link: edm4hep.Association | edm4hep.Link) => {
const recIndex = 'rec' in link ? link.rec.index : link.from.index;
const simIndex = 'sim' in link ? link.sim.index : link.to.index;
const pdgid = Math.abs(mcParticleCollection[simIndex].PDG);
reconstructedParticleCollection[recIndex].tracks.forEach(({ index }) => {
eFlowTrackCollection[index].pid =
Edm4hepJsonLoader.pidNames[pdgid] ?? edm4hep.ParticleType.Other;
});
});
}
/** Return vertices */
private getVertices(vertexCollection: edm4hep.Vertex[]): VertexParams[] {
const color = this.randomColor();
return vertexCollection.map((rawVertex: edm4hep.Vertex) => ({
pos: [
rawVertex.position.x * 0.1,
rawVertex.position.y * 0.1,
rawVertex.position.z * 0.1,
],
size: 1,
vertexType: 'type' in rawVertex ? rawVertex.type : null,
color,
}));
}
/** Return tracks */
private getTracks(
rawEvent: any,
trackCollection: edm4hep.Track[],
): [string, TrackParams[]][] {
const categories = Object.fromEntries(
Object.keys(Edm4hepJsonLoader.pidColors).map((type) => [
type,
[] as TrackParams[],
]),
) as Record<string, TrackParams[]>;
trackCollection.forEach((rawTrack: edm4hep.Track) => {
const pos: number[][] = []; // An array of positions is needed to render the tracks as bars
// @todo trackerhits might always exist
if ('trackerHits' in rawTrack && rawTrack.trackerHits.length > 0) {
rawTrack.trackerHits.forEach((trackerHitRef: ObjectID) => {
const trackerHits: edm4hep.Hit[] = this.getCollByID(
rawEvent,
trackerHitRef.collectionID,
);
pos.push([
trackerHits[trackerHitRef.index].position.x * 0.1,
trackerHits[trackerHitRef.index].position.y * 0.1,
trackerHits[trackerHitRef.index].position.z * 0.1,
]);
});
} else {
rawTrack.trackStates.forEach((trackState: edm4hep.TrackState) => {
pos.push([
trackState.referencePoint.x * 0.1,
trackState.referencePoint.y * 0.1,
trackState.referencePoint.z * 0.1,
]);
});
}
const category = rawTrack.pid ?? 'other';
categories[category].push({
pos,
// @todo dparams (helix parameters for Runge-Kutta extrapolation: qOverP requires the magnetic field)
// @todo phi (aimuthal angle)
// @todo eta (peudorapidity)
// @todo d0 (tansverse impact parameter)
// @todo z0 (lngitudinal impact parameter)
// @todo pt (transverse momentum requires the magnetic field)
chi2: rawTrack.chi2, // no use by phoenix-object.ts
dof: rawTrack.ndf, // no use by phoenix-object.ts
color: Edm4hepJsonLoader.pidColors[rawTrack.pid ?? 'other'], // tracks receive hex color without '#'
linewidth: 1,
});
});
return Object.entries(categories).filter(([, arr]) => arr.length !== 0) as [
string,
TrackParams[],
][];
}
/** Return tracker hits */
private getHits(
rawEvent: any,
hitCollection: edm4hep.Hit[],
): [string, HitParams[]][] {
const categories = Object.fromEntries([
...Object.keys(Edm4hepJsonLoader.pidColors).map((type) => [
type,
[] as HitParams[],
]),
['overlay', [] as HitParams[]],
['secondary', [] as HitParams[]],
]) as Record<string, HitParams[]>;
const colorOverlay = this.randomColor();
const colorSecondary = this.randomColor();
hitCollection.forEach((rawHit) => {
const pos: [number, number, number] = [
rawHit.position.x * 0.1,
rawHit.position.y * 0.1,
rawHit.position.z * 0.1,
];
if ((rawHit.quality & (1 << 31)) !== 0) {
/* BITOverlay = 31
* https://github.com/key4hep/EDM4hep/blob/fe5a54046a91a7e648d0b588960db7841aebc670/edm4hep.yaml#L349
*/
categories.overlay.push({
type: 'CircularPoint',
pos,
color: `#${colorOverlay}`,
size: 2,
});
} else if ((rawHit.quality & (1 << 30)) !== 0) {
/* BITProducedBySecondary = 30
* https://github.com/key4hep/EDM4hep/blob/fe5a54046a91a7e648d0b588960db7841aebc670/edm4hep.yaml#L350
*/
categories.secondary.push({
type: 'CircularPoint',
pos,
color: `#${colorSecondary}`,
size: 2,
});
} else {
let ref: ObjectID | null = null;
if ('particle' in rawHit && rawHit.particle.length > 0) {
// 'particle' exists in type SimTrackerHit from Schema2 onwards
ref = rawHit.particle[0];
} else if ('MCParticle' in rawHit) {
// 'MCParticle' only exists in type SimTrackerHit within Schema1
ref = rawHit.MCParticle;
}
if (ref !== null) {
const pdg =
this.getCollByID(rawEvent, ref.collectionID)?.[ref.index]?.PDG ??
null;
const particleType =
Edm4hepJsonLoader.pidNames[pdg] ?? edm4hep.ParticleType.Other;
categories[particleType].push({
type: 'CircularPoint',
pos,
color: `#${Edm4hepJsonLoader.pidColors[particleType]}`,
size: 2,
});
}
}
});
return Object.entries(categories).filter(([, arr]) => arr.length !== 0);
}
/** Returns Calo cells */
private getCaloCells(
caloCellCollection: edm4hep.CaloCell[],
): CaloCellParams[] {
const cells: Omit<CaloCellParams, 'uuid'>[] = [];
const hue = Math.floor(Math.random() * 358);
// Find smallest distance between cell centers and use it as cell size
let drmin = 1e9;
for (let i = 0; i < 1e4; ++i) {
const j = Math.floor(Math.random() * caloCellCollection.length);
const k = Math.floor(Math.random() * caloCellCollection.length);
if (j === k) {
continue;
}
const dx2 = Math.pow(
caloCellCollection[j].position.x - caloCellCollection[k].position.x,
2,
);
const dy2 = Math.pow(
caloCellCollection[j].position.y - caloCellCollection[k].position.y,
2,
);
const dz2 = Math.pow(
caloCellCollection[j].position.z - caloCellCollection[k].position.z,
2,
);
const dr = Math.sqrt(dx2 + dy2 + dz2);
if (dr < drmin) {
drmin = dr;
}
}
const side = Math.floor(drmin) * 0.1 > 1 ? Math.floor(drmin) * 0.1 : 1;
caloCellCollection.forEach((rawCell) => {
const x = rawCell.position.x * 0.1;
const y = rawCell.position.y * 0.1;
const z = rawCell.position.z * 0.1;
const rho = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
cells.push({
eta: rho === 0 ? 0 : Math.asinh(z / rho), // Check because '0 / 0 = NaN'
phi: Math.atan2(y, x), // Safer equivalent to 'Math.acos(x / rho) * Math.sign(y)'
energy: rawCell.energy,
radius: Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2)),
// z,
color: this.convHSLtoHEX(hue, rawCell.energy),
opacity: this.valToOpacity(rawCell.energy, 1e-3, 1),
side: side,
length: side, // expecting cells in multiple layers
});
});
return cells as CaloCellParams[];
}
/** Return Calo clusters */
private getCaloClusters(
caloClusterCollection: edm4hep.CaloCluster[],
): CaloClusterParams[] {
const clusters: CaloClusterParams[] = [];
caloClusterCollection.forEach((rawCluster) => {
const x = rawCluster.position.x * 0.1;
const y = rawCluster.position.y * 0.1;
const z = rawCluster.position.z * 0.1;
const rho = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
clusters.push({
eta: rho === 0 ? 0 : Math.asinh(z / rho), // Check because '0 / 0 = NaN'
phi: Math.atan2(y, x),
energy: rawCluster.energy * 100,
radius: Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2)),
// z: (overrides z position)
side: 4,
// length (overrides length (depth) of the cluster box)
// color (overrides color for rendering)
theta: Math.atan2(rho, z),
// opacity (overrides opacity value)
});
});
return clusters;
}
/** Return jets */
private getJets(jetCollection: edm4hep.ReconstructedParticle[]): JetParams[] {
const jets: JetParams[] = [];
jetCollection.forEach((rawJet) => {
const px: number = rawJet.momentum.x;
const py: number = rawJet.momentum.y;
const pz: number = rawJet.momentum.z;
const p = Math.sqrt(px * px + py * py + pz * pz);
const pt = Math.sqrt(Math.pow(px, 2) + Math.pow(py, 2));
const eta = pt === 0 ? 0 : Math.asinh(pz / pt);
jets.push({
eta,
phi: Math.atan2(py, px),
theta: 2 * Math.atan(Math.exp(-eta)),
energy: rawJet.energy * 100,
et: p === 0 ? 0 : (rawJet.energy * pt) / p,
// coneR: (overrides cone radius for visualization width)
origin_X: rawJet.referencePoint.x,
origin_Y: rawJet.referencePoint.y,
origin_Z: rawJet.referencePoint.z,
// color: (overrides color for rendering)
});
});
return jets;
}
/** Return missing energy */
private getMissingEnergy(
missingEnergyCollection: edm4hep.ReconstructedParticle[],
): MissingEnergyParams[] {
const missingEnergies: MissingEnergyParams[] = [];
missingEnergyCollection.forEach((rawMissingEnergy: any) => {
const px: number = rawMissingEnergy.momentum.x;
const py: number = rawMissingEnergy.momentum.y;
const pz: number = rawMissingEnergy.momentum.z;
const p = Math.sqrt(Math.pow(px, 2) + Math.pow(py, 2) + Math.pow(pz, 2));
missingEnergies.push({
etx: ((rawMissingEnergy.energy * px) / p) * 10,
ety: ((rawMissingEnergy.energy * py) / p) * 10,
color: '#ff69b4',
});
});
return missingEnergies;
}
/** Return a random colour */
private randomColor() {
return `#${Math.floor(Math.random() * 16777215)
.toString(16)
.padStart(6, '0')
.toUpperCase()}`;
}
/** Get the required collection */
private getCollByID(event: any, id: number) {
const coll = Object.values(event).find((c: any) => c?.collID === id) as any;
return coll?.collection;
}
/** Return a lightness value from the passed number and range */
private valToLightness(v: number, min: number, max: number): number {
let lightness = 80 - ((v - min) * 65) / (max - min);
if (lightness < 20) {
lightness = 20;
}
if (lightness > 85) {
lightness = 85;
}
return lightness;
}
/** Return a opacity value from the passed number and range */
private valToOpacity(v: number, min: number, max: number): number {
let opacity = 0.2 + ((v - min) * 0.65) / (max - min);
if (opacity < 0.2) {
opacity = 0.2;
}
if (opacity > 0.8) {
opacity = 0.8;
}
return opacity;
}
/** Helper conversion of HSL to hexadecimal */
private convHSLtoHEX(h: number, energy: number): string {
const s = Math.floor(Math.random() * 101);
const l = this.valToLightness(energy, 1e-3, 1) / 100;
const a = (s * Math.min(l, 1 - l)) / 100;
const f = (n: number) => {
const k = (n + h / 30) % 12;
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
return Math.round(255 * color)
.toString(16)
.padStart(2, '0');
};
return `#${f(0)}${f(8)}${f(4)}`;
}
}