src/loaders/physlite-loader.ts
Loader for ATLAS PHYSLITE (DAOD_PHYSLITE) ROOT files.
Uses jsroot to read the CollectionTree TTree and converts xAOD auxiliary-data branches into Phoenix event data format.
Properties |
|
Methods |
|
constructor(maxEvents: number)
|
||||||||
|
Defined in src/loaders/physlite-loader.ts:117
|
||||||||
|
Create a PHYSLITE loader.
Parameters :
|
| Private collectionDefs |
Type : PHYSLITECollectionDef[]
|
|
Defined in src/loaders/physlite-loader.ts:117
|
|
Collection definitions describing which branches to read. |
| Private maxEvents |
Type : number
|
|
Defined in src/loaders/physlite-loader.ts:115
|
|
Maximum number of events to load from the file. |
| Protected eventData |
Type : PhoenixEventData
|
|
Inherited from
PhoenixLoader
|
|
Defined in
PhoenixLoader:35
|
|
Event data processed by the loader. |
| Private graphicsLibrary |
Type : ThreeManager
|
|
Inherited from
PhoenixLoader
|
|
Defined in
PhoenixLoader:31
|
|
ThreeService to perform three.js related functions. |
| Protected labelsObject |
Type : literal type
|
Default value : {}
|
|
Inherited from
PhoenixLoader
|
|
Defined in
PhoenixLoader:41
|
|
Object containing event object labels. |
| Protected loadingManager |
Type : LoadingManager
|
|
Inherited from
PhoenixLoader
|
|
Defined in
PhoenixLoader:37
|
|
Loading manager for loadable resources |
| Protected stateManager |
Type : StateManager
|
|
Inherited from
PhoenixLoader
|
|
Defined in
PhoenixLoader:39
|
|
Loading manager for loadable resources |
| Private ui |
Type : UIManager
|
|
Inherited from
PhoenixLoader
|
|
Defined in
PhoenixLoader:33
|
|
UIService to perform UI related functions. |
| Private convertCaloClusters | |||||||||
convertCaloClusters(keys: literal type, tgt: any)
|
|||||||||
|
Defined in src/loaders/physlite-loader.ts:459
|
|||||||||
|
Convert egammaClusters branch data into Phoenix CaloClusterParams array.
Parameters :
Returns :
any[]
|
| Private convertCollection | ||||||||||||
convertCollection(def: PHYSLITECollectionDef, keys: literal type, tgt: any)
|
||||||||||||
|
Defined in src/loaders/physlite-loader.ts:254
|
||||||||||||
|
Convert one collection's branch data for a single event entry into an array of Phoenix-format objects.
Parameters :
Returns :
[] | null
|
| Private convertCompoundObjects |
convertCompoundObjects(keys: literal type, tgt: any, isCharged: boolean)
|
|
Defined in src/loaders/physlite-loader.ts:285
|
|
Convert compound objects (Electrons, Muons, Photons). These are rendered as extrapolated tracks (charged) or clusters (neutral). Energy is computed from pt, eta, and mass: E = sqrt((pt*cosh(eta))^2 + m^2).
Returns :
any[]
|
| Private convertJets | |||||||||
convertJets(keys: literal type, tgt: any)
|
|||||||||
|
Defined in src/loaders/physlite-loader.ts:336
|
|||||||||
|
Convert jet branch data into Phoenix JetParams array. Energy computed from pt, eta, m: E = sqrt((pt*cosh(eta))^2 + m^2).
Parameters :
Returns :
any[]
|
| Private convertMET | |||||||||
convertMET(keys: literal type, tgt: any)
|
|||||||||
|
Defined in src/loaders/physlite-loader.ts:417
|
|||||||||
|
Convert MET branch data into Phoenix MissingEnergyParams array.
Parameters :
Returns :
any[]
|
| Private convertTracks | |||||||||
convertTracks(keys: literal type, tgt: any)
|
|||||||||
|
Defined in src/loaders/physlite-loader.ts:368
|
|||||||||
|
Convert InDetTrackParticles branch data into Phoenix TrackParams array. Uses dparams [d0, z0, phi, theta, qOverP] for Runge-Kutta extrapolation.
Parameters :
Returns :
any[]
|
| Private convertVertices | |||||||||
convertVertices(keys: literal type, tgt: any)
|
|||||||||
|
Defined in src/loaders/physlite-loader.ts:435
|
|||||||||
|
Convert PrimaryVertices branch data into Phoenix VertexParams array.
Parameters :
Returns :
any[]
|
| Async getEventData | ||||||||
getEventData(fileSource: File | string)
|
||||||||
|
Defined in src/loaders/physlite-loader.ts:135
|
||||||||
|
Open a PHYSLITE ROOT file and return all events as a PhoenixEventsData object (keyed by event name).
Parameters :
Returns :
Promise<any>
Promise resolving to the events data. |
| Private toArray | ||||||
toArray(val: any)
|
||||||
|
Defined in src/loaders/physlite-loader.ts:486
|
||||||
|
Ensure a value is a plain array (jsroot may return typed arrays).
Parameters :
Returns :
[] | null
|
| 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:315
|
||||||||||||||||||||||||||||
|
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:532
|
||||||||||||||||
|
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:234
|
|||||||||||||||||||||||||||||||||||
|
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:569
|
||||||||||||||||
|
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:59
|
||||||||||||||||||||
|
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:132
|
||||||||
|
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:111
|
|
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:380
|
|||||||||||||||||||||||||
|
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:370
|
|
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:364
|
|
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:478
|
|
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:95
|
||||||||
|
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:558
|
|
Get the object containing labels.
Returns :
literal type
The labels object. |
| Private getObjectTypeCollections | ||||||||
getObjectTypeCollections(object: any)
|
||||||||
|
Inherited from
PhoenixLoader
|
||||||||
|
Defined in
PhoenixLoader:350
|
||||||||
|
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:154
|
|
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:162
|
||||||||
|
Load all object types from event data using the registry configs.
Parameters :
Returns :
void
|
import { PhoenixLoader } from './phoenix-loader';
import { openFile, settings as jsrootSettings } from 'jsroot';
import { TSelector, treeProcess } from 'jsroot/tree';
import { CoordinateHelper } from '../helpers/coordinate-helper';
/**
* Branch mapping for a PHYSLITE collection.
* Each entry describes which ROOT branches to read
* and how to convert them into Phoenix event data.
*/
interface PHYSLITECollectionDef {
/** Branch prefix in the ROOT file (e.g. 'AnalysisElectronsAuxDyn'). */
prefix: string;
/** Branch suffixes to read (e.g. ['pt', 'eta', 'phi', 'm']). */
fields: string[];
/** Phoenix object type this maps to. */
phoenixType: string;
/** Name for the Phoenix collection. */
collectionName: string;
}
/**
* Default PHYSLITE branch mapping for ATLAS Open Data.
* Branch names verified against the official get_json_phoenix.py script.
*/
const DEFAULT_COLLECTIONS: PHYSLITECollectionDef[] = [
{
prefix: 'AnalysisElectronsAuxDyn',
fields: ['pt', 'eta', 'phi', 'm'],
phoenixType: 'Electrons',
collectionName: 'AnalysisElectrons',
},
{
prefix: 'AnalysisMuonsAuxDyn',
fields: ['pt', 'eta', 'phi'],
phoenixType: 'Muons',
collectionName: 'AnalysisMuons',
},
{
prefix: 'AnalysisPhotonsAuxDyn',
fields: ['pt', 'eta', 'phi', 'm'],
phoenixType: 'Photons',
collectionName: 'AnalysisPhotons',
},
{
prefix: 'AnalysisJetsAuxDyn',
fields: ['pt', 'eta', 'phi', 'm'],
phoenixType: 'Jets',
collectionName: 'AnalysisJets',
},
{
prefix: 'AnalysisLargeRJetsAuxDyn',
fields: ['pt', 'eta', 'phi', 'm'],
phoenixType: 'Jets',
collectionName: 'AnalysisLargeRJets',
},
{
prefix: 'InDetTrackParticlesAuxDyn',
fields: ['d0', 'z0', 'theta', 'phi', 'qOverP'],
phoenixType: 'Tracks',
collectionName: 'InDetTrackParticles',
},
{
prefix: 'MuonSpectrometerTrackParticlesAuxDyn',
fields: ['d0', 'z0', 'theta', 'phi', 'qOverP'],
phoenixType: 'Tracks',
collectionName: 'MuonSpectrometerTrackParticles',
},
{
prefix: 'CombinedMuonTrackParticlesAuxDyn',
fields: ['d0', 'z0', 'theta', 'phi', 'qOverP'],
phoenixType: 'Tracks',
collectionName: 'CombinedMuonTrackParticles',
},
{
prefix: 'ExtrapolatedMuonTrackParticlesAuxDyn',
fields: ['d0', 'z0', 'theta', 'phi', 'qOverP'],
phoenixType: 'Tracks',
collectionName: 'ExtrapolatedMuonTrackParticles',
},
{
prefix: 'GSFTrackParticlesAuxDyn',
fields: ['d0', 'z0', 'theta', 'phi', 'qOverP'],
phoenixType: 'Tracks',
collectionName: 'GSFTrackParticles',
},
{
prefix: 'MET_Core_AnalysisMETAuxDyn',
fields: ['mpx', 'mpy'],
phoenixType: 'MissingEnergy',
collectionName: 'MET',
},
{
prefix: 'PrimaryVerticesAuxDyn',
fields: ['x', 'y', 'z'],
phoenixType: 'Vertices',
collectionName: 'PrimaryVertices',
},
{
prefix: 'egammaClustersAuxDyn',
fields: ['calE', 'calEta', 'calPhi'],
phoenixType: 'CaloClusters',
collectionName: 'egammaClusters',
},
];
/**
* Loader for ATLAS PHYSLITE (DAOD_PHYSLITE) ROOT files.
*
* Uses jsroot to read the CollectionTree TTree and converts
* xAOD auxiliary-data branches into Phoenix event data format.
*/
export class PHYSLITELoader extends PhoenixLoader {
/** Maximum number of events to load from the file. */
private maxEvents: number;
/** Collection definitions describing which branches to read. */
private collectionDefs: PHYSLITECollectionDef[];
/**
* Create a PHYSLITE loader.
* @param maxEvents Maximum number of events to read from the file.
*/
constructor(maxEvents: number = 100) {
super();
this.maxEvents = maxEvents;
this.collectionDefs = DEFAULT_COLLECTIONS;
}
/**
* Open a PHYSLITE ROOT file and return all events as a
* PhoenixEventsData object (keyed by event name).
* @param fileSource File object or URL of the .root file.
* @returns Promise resolving to the events data.
*/
async getEventData(fileSource: File | string): Promise<any> {
jsrootSettings.UseStamp = false;
const file = await openFile(fileSource as any);
const tree: any = await file.readObject('CollectionTree');
if (!tree) {
throw new Error(
'No CollectionTree found in this ROOT file. It may not be a PHYSLITE file.',
);
}
const nEntries: number = tree.fEntries ?? 0;
const nToProcess = Math.min(nEntries, this.maxEvents);
if (nToProcess === 0) {
throw new Error('CollectionTree has no entries.');
}
// --- Add branches to the selector, skipping missing collections ---
// jsroot throws if a non-existent branch is added to treeProcess.
// AuxDyn branches are stored as flat top-level entries (e.g.
// "AnalysisJetsAuxDyn.pt"), so we check exact names before adding.
const topLevelNames = new Set<string>(
tree.fBranches.arr.map((b: any) => b.fName as string),
);
const activeDefs: {
def: PHYSLITECollectionDef;
selectorKeys: { [field: string]: string };
}[] = [];
const selector = new TSelector();
for (const def of this.collectionDefs) {
// Check that at least the first required field branch exists
const firstBranch = `${def.prefix}.${def.fields[0]}`;
if (!topLevelNames.has(firstBranch)) {
continue;
}
const keys: { [field: string]: string } = {};
let allFound = true;
for (const field of def.fields) {
const branchName = `${def.prefix}.${field}`;
if (!topLevelNames.has(branchName)) {
allFound = false;
break;
}
const key = `${def.prefix}__${field}`;
keys[field] = key;
}
if (!allFound) continue;
// All branches verified — add them to the selector
for (const field of def.fields) {
const branchName = `${def.prefix}.${field}`;
selector.addBranch(branchName, keys[field]);
}
activeDefs.push({ def, selectorKeys: keys });
}
// Event info branches (metadata)
const eventNumberKey = 'evtinfo_eventNumber';
const runNumberKey = 'evtinfo_runNumber';
selector.addBranch('EventInfoAuxDyn.eventNumber', eventNumberKey);
selector.addBranch('EventInfoAuxDyn.runNumber', runNumberKey);
// --- Process entries ---
const eventsData: any = {};
let eventIndex = 0;
selector.Process = (entry: number) => {
if (eventIndex >= nToProcess) {
selector.Abort();
return;
}
const tgt = selector.tgtobj;
const eventNumber = eventNumberKey ? tgt[eventNumberKey] : eventIndex;
const runNumber = runNumberKey ? tgt[runNumberKey] : 0;
// Pre-initialize all active collection types so Phoenix registers
// them from the first event (even if empty for that event).
const eventData: any = {
'event number': eventNumber,
'run number': runNumber,
};
for (const { def } of activeDefs) {
if (!eventData[def.phoenixType]) {
eventData[def.phoenixType] = {};
}
}
for (const { def, selectorKeys } of activeDefs) {
const collection = this.convertCollection(def, selectorKeys, tgt);
if (collection && collection.length > 0) {
eventData[def.phoenixType][def.collectionName] = collection;
}
}
const eventKey = `Event ${eventNumber}`;
eventsData[eventKey] = eventData;
eventIndex++;
};
await treeProcess(tree, selector, { numentries: nToProcess });
return eventsData;
}
/**
* Convert one collection's branch data for a single event entry
* into an array of Phoenix-format objects.
*/
private convertCollection(
def: PHYSLITECollectionDef,
keys: { [field: string]: string },
tgt: any,
): any[] | null {
switch (def.phoenixType) {
case 'Electrons':
case 'Muons':
return this.convertCompoundObjects(keys, tgt, true);
case 'Photons':
return this.convertCompoundObjects(keys, tgt, false);
case 'Jets':
return this.convertJets(keys, tgt);
case 'Tracks':
return this.convertTracks(keys, tgt);
case 'MissingEnergy':
return this.convertMET(keys, tgt);
case 'Vertices':
return this.convertVertices(keys, tgt);
case 'CaloClusters':
return this.convertCaloClusters(keys, tgt);
default:
return null;
}
}
/**
* Convert compound objects (Electrons, Muons, Photons).
* These are rendered as extrapolated tracks (charged) or clusters (neutral).
* Energy is computed from pt, eta, and mass: E = sqrt((pt*cosh(eta))^2 + m^2).
*/
private convertCompoundObjects(
keys: { [field: string]: string },
tgt: any,
isCharged: boolean,
): any[] {
const ptArr = keys['pt'] ? this.toArray(tgt[keys['pt']]) : null;
const etaArr = keys['eta'] ? this.toArray(tgt[keys['eta']]) : null;
const phiArr = keys['phi'] ? this.toArray(tgt[keys['phi']]) : null;
const mArr = keys['m'] ? this.toArray(tgt[keys['m']]) : null;
if (!etaArr || !phiArr) return [];
const n = etaArr.length;
const objects: any[] = [];
for (let i = 0; i < n; i++) {
const eta = etaArr[i];
const phi = phiArr[i];
const pt = ptArr ? ptArr[i] : 0; // MeV
const m = mArr ? mArr[i] : 0;
// E = sqrt((pt*cosh(eta))^2 + m^2)
const p = pt * Math.cosh(eta);
const energy = Math.sqrt(p * p + m * m);
const obj: any = {
eta,
phi,
pt,
energy,
};
// For charged particles, derive sign from charge if available,
// otherwise assume negative (electron/muon convention)
if (isCharged) {
// PHYSLITE muons don't have explicit pt — they are linked to tracks.
// For electrons, charge can be inferred from track qOverP sign.
// Default to negative charge convention for track curvature.
obj.pdgId = -1;
}
objects.push(obj);
}
return objects;
}
/**
* Convert jet branch data into Phoenix JetParams array.
* Energy computed from pt, eta, m: E = sqrt((pt*cosh(eta))^2 + m^2).
*/
private convertJets(keys: { [field: string]: string }, tgt: any): any[] {
const ptArr = keys['pt'] ? this.toArray(tgt[keys['pt']]) : null;
const etaArr = keys['eta'] ? this.toArray(tgt[keys['eta']]) : null;
const phiArr = keys['phi'] ? this.toArray(tgt[keys['phi']]) : null;
const mArr = keys['m'] ? this.toArray(tgt[keys['m']]) : null;
if (!etaArr || !phiArr) return [];
const n = etaArr.length;
const jets: any[] = [];
for (let i = 0; i < n; i++) {
const pt = ptArr ? ptArr[i] : 0;
const eta = etaArr[i];
const m = mArr ? mArr[i] : 0;
const p = pt * Math.cosh(eta);
const energy = Math.sqrt(p * p + m * m);
jets.push({
eta,
phi: phiArr[i],
energy,
});
}
return jets;
}
/**
* Convert InDetTrackParticles branch data into Phoenix TrackParams array.
* Uses dparams [d0, z0, phi, theta, qOverP] for Runge-Kutta extrapolation.
*/
private convertTracks(keys: { [field: string]: string }, tgt: any): any[] {
const d0Arr = keys['d0'] ? this.toArray(tgt[keys['d0']]) : null;
const z0Arr = keys['z0'] ? this.toArray(tgt[keys['z0']]) : null;
const thetaArr = keys['theta'] ? this.toArray(tgt[keys['theta']]) : null;
const phiArr = keys['phi'] ? this.toArray(tgt[keys['phi']]) : null;
const qOverPArr = keys['qOverP'] ? this.toArray(tgt[keys['qOverP']]) : null;
if (!phiArr || !thetaArr || !qOverPArr) return [];
const n = phiArr.length;
const tracks: any[] = [];
for (let i = 0; i < n; i++) {
const d0 = d0Arr ? d0Arr[i] : 0;
const z0 = z0Arr ? z0Arr[i] : 0;
const phi = phiArr[i];
const theta = thetaArr[i];
const qOverP = qOverPArr[i];
// Skip tracks with invalid parameters to avoid NaN in Runge-Kutta
if (
!qOverP ||
!isFinite(1.0 / qOverP) ||
theta <= 0 ||
theta >= Math.PI
) {
continue;
}
const p = Math.abs(1.0 / qOverP);
const pt = p * Math.sin(theta);
const eta = CoordinateHelper.thetaToEta(theta);
tracks.push({
dparams: [d0, z0, phi, theta, qOverP],
phi,
eta,
pT: pt,
d0,
z0,
});
}
return tracks;
}
/**
* Convert MET branch data into Phoenix MissingEnergyParams array.
*/
private convertMET(keys: { [field: string]: string }, tgt: any): any[] {
const mpx = keys['mpx'] ? tgt[keys['mpx']] : null;
const mpy = keys['mpy'] ? tgt[keys['mpy']] : null;
if (mpx == null || mpy == null) return [];
// MET may be a single value or an array with one element
return [
{
etx: typeof mpx === 'number' ? mpx : (mpx[0] ?? 0),
ety: typeof mpy === 'number' ? mpy : (mpy[0] ?? 0),
},
];
}
/**
* Convert PrimaryVertices branch data into Phoenix VertexParams array.
*/
private convertVertices(keys: { [field: string]: string }, tgt: any): any[] {
const xArr = keys['x'] ? this.toArray(tgt[keys['x']]) : null;
const yArr = keys['y'] ? this.toArray(tgt[keys['y']]) : null;
const zArr = keys['z'] ? this.toArray(tgt[keys['z']]) : null;
if (!xArr || !yArr || !zArr) return [];
const n = xArr.length;
const vertices: any[] = [];
for (let i = 0; i < n; i++) {
vertices.push({
x: xArr[i],
y: yArr[i],
z: zArr[i],
});
}
return vertices;
}
/**
* Convert egammaClusters branch data into Phoenix CaloClusterParams array.
*/
private convertCaloClusters(
keys: { [field: string]: string },
tgt: any,
): any[] {
const eArr = keys['calE'] ? this.toArray(tgt[keys['calE']]) : null;
const etaArr = keys['calEta'] ? this.toArray(tgt[keys['calEta']]) : null;
const phiArr = keys['calPhi'] ? this.toArray(tgt[keys['calPhi']]) : null;
if (!eArr || !etaArr || !phiArr) return [];
const n = eArr.length;
const clusters: any[] = [];
for (let i = 0; i < n; i++) {
clusters.push({
energy: eArr[i],
eta: etaArr[i],
phi: phiArr[i],
});
}
return clusters;
}
/**
* Ensure a value is a plain array (jsroot may return typed arrays).
*/
private toArray(val: any): number[] | null {
if (val == null) return null;
if (Array.isArray(val)) return val;
if (ArrayBuffer.isView(val)) return Array.from(val as any);
if (typeof val === 'number') return [val];
return null;
}
}