Warning, /firebird/firebird-ng/src/app/phoenix-overload/eic-edm4hep-json-loader.ts is written in an unsupported language. File is not indexed.
0001 import {PhoenixLoader} from "phoenix-event-display";
0002
0003
0004 /**
0005 * Edm4hepJsonLoader for loading EDM4hep json dumps
0006 */
0007 export class EicEdm4hepJsonLoader extends PhoenixLoader {
0008 /** Event data loaded from EDM4hep JSON file */
0009 private rawEventData: any;
0010
0011 /** Create Edm4hepJsonLoader */
0012 constructor() {
0013 super();
0014 this.eventData = {};
0015 }
0016
0017 /** Put raw EDM4hep JSON event data into the loader */
0018 setRawEventData(rawEventData: any) {
0019 this.rawEventData = rawEventData;
0020 }
0021
0022 /** Process raw EDM4hep JSON event data into the Phoenix format */
0023 processEventData(): boolean {
0024 Object.entries(this.rawEventData).forEach(([eventName, event]) => {
0025 const oneEventData = {
0026 Vertices: {},
0027 Tracks: {},
0028 Hits: {},
0029 CaloCells: {},
0030 CaloClusters: {},
0031 Jets: {},
0032 MissingEnergy: {},
0033 'event number': this.getEventNumber(event),
0034 'run number': this.getRunNumber(event),
0035 };
0036
0037 this.colorTracks(event);
0038
0039 oneEventData.Vertices = this.getVertices(event);
0040 oneEventData.Tracks = this.getTracks(event);
0041 oneEventData.Hits = this.getHits(event);
0042 oneEventData.CaloCells = this.getCells(event);
0043 oneEventData.CaloClusters = this.getCaloClusters(event);
0044 oneEventData.Jets = this.getJets(event);
0045 oneEventData.MissingEnergy = this.getMissingEnergy(event);
0046
0047 this.eventData[eventName] = oneEventData;
0048 });
0049
0050 return true;
0051 }
0052
0053 /** Output event data in Phoenix compatible format */
0054 getEventData(): any {
0055 return this.eventData;
0056 }
0057
0058 /** Return number of events */
0059 private getNumEvents(): number {
0060 return Object.keys(this.rawEventData).length;
0061 }
0062
0063 /** Return run number (or 0, if not defined) */
0064 private getRunNumber(event: any): number {
0065 if (!('EventHeader' in event)) {
0066 return 0;
0067 }
0068
0069 const eventHeader = event['EventHeader']['collection'];
0070
0071 if (!('runNumber' in eventHeader)) {
0072 return eventHeader[0]['runNumber'];
0073 }
0074
0075 return 0;
0076 }
0077
0078 /** Return event number (or 0, if not defined) */
0079 private getEventNumber(event: any): number {
0080 if (!('EventHeader' in event)) {
0081 return 0;
0082 }
0083
0084 const eventHeader = event['EventHeader']['collection'];
0085
0086 if (!('eventNumber' in eventHeader)) {
0087 return eventHeader[0]['eventNumber'];
0088 }
0089
0090 return 0;
0091 }
0092
0093 /** Assign default color to Tracks*/
0094 private colorTracks(event: any) {
0095 let recoParticles: any[];
0096 if ('ReconstructedParticles' in event) {
0097 recoParticles = event['ReconstructedParticles']['collection'];
0098 } else {
0099 return;
0100 }
0101
0102 let mcParticles: any[];
0103 if ('Particle' in event) {
0104 mcParticles = event['Particle']['collection'];
0105 } else {
0106 return;
0107 }
0108
0109 let mcRecoAssocs: any[];
0110 if ('MCRecoAssociations' in event) {
0111 mcRecoAssocs = event['MCRecoAssociations']['collection'];
0112 } else {
0113 return;
0114 }
0115
0116 let tracks: any[];
0117 if ('EFlowTrack' in event) {
0118 tracks = event['EFlowTrack']['collection'];
0119 } else {
0120 return;
0121 }
0122
0123 mcRecoAssocs.forEach((mcRecoAssoc: any) => {
0124 const recoIndex = mcRecoAssoc['rec']['index'];
0125 const mcIndex = mcRecoAssoc['sim']['index'];
0126
0127 const pdgid = mcParticles[mcIndex]['PDG'];
0128 const trackRefs = recoParticles[recoIndex]['tracks'];
0129
0130 trackRefs.forEach((trackRef: any) => {
0131 const track = tracks[trackRef['index']];
0132 if (Math.abs(pdgid) === 11) {
0133 track['color'] = '00ff00';
0134 track['pid'] = 'electron';
0135 } else if (Math.abs(pdgid) === 22) {
0136 track['color'] = 'ff0000';
0137 track['pid'] = 'photon';
0138 } else if (Math.abs(pdgid) === 211 || Math.abs(pdgid) === 111) {
0139 track['color'] = 'a52a2a';
0140 track['pid'] = 'pion';
0141 } else if (Math.abs(pdgid) === 2212) {
0142 track['color'] = '778899';
0143 track['pid'] = 'proton';
0144 } else if (Math.abs(pdgid) === 321) {
0145 track['color'] = '5f9ea0';
0146 track['pid'] = 'kaon';
0147 } else {
0148 track['color'] = '0000cd';
0149 track['pid'] = 'other';
0150 }
0151 track['pdgid'] = pdgid;
0152 });
0153 });
0154 }
0155
0156 /** Return the vertices */
0157 private getVertices(event: any) {
0158 const allVertices: { [key: string]: any[] } = {};
0159
0160 for (const collName in event) {
0161 if (event[collName].constructor != Object) {
0162 continue;
0163 }
0164
0165 const collDict = event[collName];
0166
0167 if (!('collType' in collDict)) {
0168 continue;
0169 }
0170
0171 if (!('collection' in collDict)) {
0172 continue;
0173 }
0174
0175 if (!(collDict['collType'] === 'edm4hep::VertexCollection')) {
0176 continue;
0177 }
0178
0179 const vertices: any[] = [];
0180 const rawVertices = collDict['collection'];
0181 const vertexColor = this.randomColor();
0182
0183 rawVertices.forEach((rawVertex: any) => {
0184 const position: any[] = [];
0185 if ('position' in rawVertex) {
0186 position.push(rawVertex['position']['x']);
0187 position.push(rawVertex['position']['y']);
0188 position.push(rawVertex['position']['z']);
0189 }
0190
0191 const vertex = {
0192 pos: position,
0193 size: 3,
0194 color: '#' + vertexColor,
0195 };
0196 vertices.push(vertex);
0197 });
0198
0199 allVertices[collName] = vertices;
0200 }
0201
0202 return allVertices;
0203 }
0204
0205 /** Return tracks */
0206 private getTracks(event: any) {
0207 const allTracks: { [key: string]: any[] } = {};
0208
0209 for (const collName in event) {
0210 if (event[collName].constructor != Object) {
0211 continue;
0212 }
0213
0214 const collDict = event[collName];
0215
0216 if (!('collType' in collDict)) {
0217 continue;
0218 }
0219
0220 if (!(collDict['collType'] === 'edm4hep::TrackCollection')) {
0221 continue;
0222 }
0223
0224 if (!('collection' in collDict)) {
0225 continue;
0226 }
0227
0228 const rawTracks = collDict['collection'];
0229 const electrons: any[] = [];
0230 const photons: any[] = [];
0231 const pions: any[] = [];
0232 const protons: any[] = [];
0233 const kaons: any[] = [];
0234 const other: any[] = [];
0235
0236 rawTracks.forEach((rawTrack: any) => {
0237 const positions: any[] = [];
0238 if ('trackerHits' in rawTrack) {
0239 const trackerHitRefs = rawTrack['trackerHits'];
0240 trackerHitRefs.forEach((trackerHitRef: any) => {
0241 const trackerHits = this.getCollByID(
0242 event,
0243 trackerHitRef['collectionID'],
0244 );
0245 const trackerHit = trackerHits[trackerHitRef['index']];
0246 positions.push([
0247 trackerHit['position']['x'],
0248 trackerHit['position']['y'],
0249 trackerHit['position']['z'],
0250 ]);
0251 });
0252 }
0253 if ('trackStates' in rawTrack && positions.length === 0) {
0254 const trackStates = rawTrack['trackStates'];
0255 trackStates.forEach((trackState: any) => {
0256 if ('referencePoint' in trackState) {
0257 positions.push([
0258 trackState['referencePoint']['x'],
0259 trackState['referencePoint']['y'],
0260 trackState['referencePoint']['z'],
0261 ]);
0262 }
0263 });
0264 }
0265
0266 let trackColor = '0000cd';
0267 if ('color' in rawTrack) {
0268 trackColor = rawTrack['color'];
0269 }
0270
0271 const track = {
0272 pos: positions,
0273 color: trackColor,
0274 };
0275
0276 if ('pid' in rawTrack) {
0277 if (rawTrack['pid'] == 'electron') {
0278 electrons.push(track);
0279 } else if (rawTrack['pid'] == 'photon') {
0280 photons.push(track);
0281 } else if (rawTrack['pid'] == 'pion') {
0282 pions.push(track);
0283 } else if (rawTrack['pid'] == 'proton') {
0284 protons.push(track);
0285 } else if (rawTrack['pid'] == 'kaon') {
0286 kaons.push(track);
0287 } else {
0288 other.push(track);
0289 }
0290 } else {
0291 other.push(track);
0292 }
0293 });
0294
0295 allTracks[collName + ' | Electrons'] = electrons;
0296 allTracks[`${collName} | Photons`] = photons;
0297 allTracks[collName + ' | Pions'] = pions;
0298 allTracks[collName + ' | Protons'] = protons;
0299 allTracks[collName + ' | Kaons'] = kaons;
0300 allTracks[collName + ' | Other'] = other;
0301 }
0302
0303 return allTracks;
0304 }
0305
0306 /** Not implemented */
0307 private getHits(event: any) {
0308 const allHits: { [key: string]: any[] } = {};
0309
0310 for (const collName in event) {
0311 if (event[collName].constructor != Object) {
0312 continue;
0313 }
0314
0315 const collDict = event[collName];
0316
0317 if (!('collType' in collDict)) {
0318 continue;
0319 }
0320
0321 if (!collDict['collType'].includes('edm4hep::')) {
0322 continue;
0323 }
0324
0325 if (!collDict['collType'].includes('TrackerHitCollection')) {
0326 continue;
0327 }
0328
0329 if (!('collection' in collDict)) {
0330 continue;
0331 }
0332
0333 const rawHits = collDict['collection'];
0334 const hits: any[] = [];
0335 const hitColor = this.randomColor();
0336
0337 rawHits.forEach((rawHit: any) => {
0338 const position: any[] = [];
0339 if ('position' in rawHit) {
0340 position.push(rawHit['position']['x']);
0341 position.push(rawHit['position']['y']);
0342 position.push(rawHit['position']['z']);
0343 }
0344
0345 const hit = {
0346 type: 'CircularPoint',
0347 pos: position,
0348 color: '#' + hitColor,
0349 size: 20,
0350 };
0351 hits.push(hit);
0352 });
0353
0354 allHits[collName] = hits;
0355 }
0356
0357 return allHits;
0358 }
0359
0360 /** Returns the cells */
0361 private getCells(event: any) {
0362 const allCells: { [key: string]: any[] } = {};
0363
0364 for (const collName in event) {
0365 if (event[collName].constructor != Object) {
0366 continue;
0367 }
0368
0369 const collDict = event[collName];
0370
0371 if (!('collType' in collDict)) {
0372 continue;
0373 }
0374
0375 if (!collDict['collType'].includes('edm4hep::')) {
0376 continue;
0377 }
0378
0379 if (!collDict['collType'].includes('CalorimeterHitCollection')) {
0380 continue;
0381 }
0382
0383 if (!('collection' in collDict)) {
0384 continue;
0385 }
0386
0387 const rawCells = collDict['collection'];
0388 const cells: any[] = [];
0389
0390 // Find smallest distance between cell centers and use it as cell size
0391 let drmin = 1e9;
0392 for (let i = 0; i < 1e4; ++i) {
0393 const j = Math.floor(Math.random() * rawCells.length);
0394 const k = Math.floor(Math.random() * rawCells.length);
0395 if (j === k) {
0396 continue;
0397 }
0398
0399 const dx2 = Math.pow(
0400 rawCells[j].position.x - rawCells[k].position.x,
0401 2,
0402 );
0403 const dy2 = Math.pow(
0404 rawCells[j].position.y - rawCells[k].position.y,
0405 2,
0406 );
0407 const dz2 = Math.pow(
0408 rawCells[j].position.z - rawCells[k].position.z,
0409 2,
0410 );
0411 const dr = Math.sqrt(dx2 + dy2 + dz2);
0412
0413 if (dr < drmin) {
0414 drmin = dr;
0415 }
0416 }
0417 const cellSide =
0418 Math.floor(drmin) * 0.1 > 1 ? Math.floor(drmin) * 0.1 : 1;
0419 const cellsHue = Math.floor(Math.random() * 358);
0420
0421 rawCells.forEach((rawCell: any) => {
0422 const x = rawCell.position.x;
0423 const y = rawCell.position.y;
0424 const z = rawCell.position.z;
0425
0426 const r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2));
0427 const rho = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
0428 const eta = Math.asinh(z / rho);
0429 const phi = Math.acos(x / rho) * Math.sign(y);
0430 const cellLightness = this.valToLightness(rawCell.energy, 1e-3, 1);
0431 const cellOpacity = this.valToOpacity(rawCell.energy, 1e-3, 1);
0432
0433 const cell = {
0434 eta: eta,
0435 phi: phi,
0436 energy: rawCell.energy,
0437 radius: r,
0438 side: cellSide,
0439 length: cellSide, // expecting cells in multiple layers
0440 color: '#' + this.convHSLtoHEX(cellsHue, 90, cellLightness),
0441 opacity: cellOpacity,
0442 };
0443 cells.push(cell);
0444 });
0445
0446 allCells[collName] = cells;
0447 }
0448
0449 return allCells;
0450 }
0451
0452 /** Return Calo clusters */
0453 private getCaloClusters(event: any) {
0454 const allClusters: { [key: string]: any[] } = {};
0455
0456 for (const collName in event) {
0457 if (event[collName].constructor != Object) {
0458 continue;
0459 }
0460
0461 const collDict = event[collName];
0462
0463 if (!('collType' in collDict)) {
0464 continue;
0465 }
0466
0467 if (!(collDict['collType'] === 'edm4hep::ClusterCollection')) {
0468 continue;
0469 }
0470
0471 if (!('collection' in collDict)) {
0472 continue;
0473 }
0474
0475 const rawClusters = collDict['collection'];
0476 const clusters: any[] = [];
0477
0478 rawClusters.forEach((rawCluster: any) => {
0479 const x = rawCluster.position.x;
0480 const y = rawCluster.position.y;
0481 const z = rawCluster.position.z;
0482
0483 const r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2));
0484 const rho = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
0485 const eta = Math.asinh(z / rho);
0486 const phi = Math.acos(x / rho) * Math.sign(y);
0487
0488 const cluster = {
0489 eta: eta,
0490 phi: phi,
0491 energy: rawCluster.energy * 100,
0492 radius: r,
0493 side: 40,
0494 };
0495 clusters.push(cluster);
0496 });
0497
0498 allClusters[collName] = clusters;
0499 }
0500
0501 return allClusters;
0502 }
0503
0504 /** Return jets */
0505 private getJets(event: any) {
0506 const allJets: { [key: string]: any[] } = {};
0507
0508 for (const collName in event) {
0509 if (event[collName].constructor != Object) {
0510 continue;
0511 }
0512
0513 const collDict = event[collName];
0514
0515 if (!('collType' in collDict)) {
0516 continue;
0517 }
0518
0519 if (
0520 !(collDict['collType'] === 'edm4hep::ReconstructedParticleCollection')
0521 ) {
0522 continue;
0523 }
0524
0525 if (!(collName.includes('Jet') || collName.includes('jet'))) {
0526 continue;
0527 }
0528
0529 if (!('collection' in collDict)) {
0530 continue;
0531 }
0532
0533 const jets: any[] = [];
0534 const rawJets = collDict['collection'];
0535
0536 rawJets.forEach((rawJet: any) => {
0537 if (!('momentum' in rawJet)) {
0538 return;
0539 }
0540 if (!('energy' in rawJet)) {
0541 return;
0542 }
0543 const px = rawJet['momentum']['x'];
0544 const py = rawJet['momentum']['y'];
0545 const pz = rawJet['momentum']['z'];
0546
0547 const pt = Math.sqrt(Math.pow(px, 2) + Math.pow(py, 2));
0548 const eta = Math.asinh(pz / pt);
0549 const phi = Math.acos(px / pt) * Math.sign(py);
0550
0551 const jet = {
0552 eta: eta,
0553 phi: phi,
0554 energy: 1000 * rawJet.energy,
0555 };
0556 jets.push(jet);
0557 });
0558 allJets[collName] = jets;
0559 }
0560
0561 return allJets;
0562 }
0563
0564 /** Return missing energy */
0565 private getMissingEnergy(event: any) {
0566 const allMETs: { [key: string]: any[] } = {};
0567
0568 for (const collName in event) {
0569 if (event[collName].constructor != Object) {
0570 continue;
0571 }
0572
0573 const collDict = event[collName];
0574
0575 if (!('collType' in collDict)) {
0576 continue;
0577 }
0578
0579 if (
0580 !(collDict['collType'] === 'edm4hep::ReconstructedParticleCollection')
0581 ) {
0582 continue;
0583 }
0584
0585 if (!(collName.includes('Missing') || collName.includes('missing'))) {
0586 continue;
0587 }
0588
0589 if (!('collection' in collDict)) {
0590 continue;
0591 }
0592
0593 const METs: any[] = [];
0594 const rawMETs = collDict['collection'];
0595 const METColor = '#ff69b4';
0596
0597 rawMETs.forEach((rawMET: any) => {
0598 if (!('momentum' in rawMET)) {
0599 return;
0600 }
0601 if (!('energy' in rawMET)) {
0602 return;
0603 }
0604 const px = rawMET['momentum']['x'];
0605 const py = rawMET['momentum']['y'];
0606 const pz = rawMET['momentum']['z'];
0607
0608 const p = Math.sqrt(
0609 Math.pow(px, 2) + Math.pow(py, 2) + Math.pow(pz, 2),
0610 );
0611 const etx = (rawMET['energy'] * px) / p;
0612 const ety = (rawMET['energy'] * py) / p;
0613
0614 const MET = {
0615 etx: etx * 100,
0616 ety: ety * 100,
0617 color: '#ff69b4',
0618 };
0619 METs.push(MET);
0620 });
0621 allMETs[collName] = METs;
0622 }
0623
0624 return allMETs;
0625 }
0626
0627 /** Return a random colour */
0628 private randomColor() {
0629 return Math.floor(Math.random() * 16777215)
0630 .toString(16)
0631 .padStart(6, '0')
0632 .toUpperCase();
0633 }
0634
0635 /** Helper conversion of HSL to hexadecimal */
0636 private convHSLtoHEX(h: number, s: number, l: number): string {
0637 l /= 100;
0638 const a = (s * Math.min(l, 1 - l)) / 100;
0639 const f = (n: number) => {
0640 const k = (n + h / 30) % 12;
0641 const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
0642 return Math.round(255 * color)
0643 .toString(16)
0644 .padStart(2, '0');
0645 };
0646
0647 return `${f(0)}${f(8)}${f(4)}`;
0648 }
0649
0650 /** Return a lightness value from the passed number and range */
0651 private valToLightness(v: number, min: number, max: number): number {
0652 let lightness = 80 - ((v - min) * 65) / (max - min);
0653 if (lightness < 20) {
0654 lightness = 20;
0655 }
0656 if (lightness > 85) {
0657 lightness = 85;
0658 }
0659
0660 return lightness;
0661 }
0662
0663 /** Return a opacity value from the passed number and range */
0664 private valToOpacity(v: number, min: number, max: number): number {
0665 let opacity = 0.5 + ((v - min) * 0.65) / (max - min);
0666 if (opacity < 0.5) {
0667 opacity = 0.5;
0668 }
0669 if (opacity > 1) {
0670 opacity = 1;
0671 }
0672
0673 return opacity;
0674 }
0675
0676 /** Get the required collection */
0677 private getCollByID(event: any, id: number) {
0678 for (const collName in event) {
0679 if (event[collName].constructor != Object) {
0680 continue;
0681 }
0682
0683 const collDict = event[collName];
0684
0685 if (!('collID' in collDict)) {
0686 continue;
0687 }
0688
0689 if (collDict['collID'] === id) {
0690 return collDict['collection'];
0691 }
0692 }
0693 }
0694 }