Back to home page

EIC code displayed by LXR

 
 

    


Warning, /firebird/packages/firebird-core/src/data-catalog.ts is written in an unsupported language. File is not indexed.

0001 /**
0002  * Data catalog: the datasets an installation offers to open — named presets
0003  * (geometry + events), tagged so a UI can filter them by physics facets, and
0004  * plain URL lists for manual pick.
0005  *
0006  * Plain TS on purpose: pyrobird serves the same JSON shape through
0007  * config.jsonc (`dataCatalog`), a remote catalog file has this shape too, and
0008  * experiment packs contribute it through `withDataCatalog()` in the Angular
0009  * app. Helpers here are pure so the same filtering runs in any host.
0010  */
0011 
0012 /** One dataset: what to load, and how it is described and tagged. */
0013 export interface DataCatalogEntry {
0014   /** Display name in preset lists. */
0015   name: string;
0016   /** One or two sentences shown under the name. */
0017   description?: string;
0018   /** Web page with more information about the dataset. */
0019   link?: string;
0020   /** Detector geometry URL. Absent: keep whatever geometry is loaded/configured. */
0021   geometry?: string;
0022   /** Event data URL: a DEX file, or a ROOT file the loaders can convert. */
0023   events?: string;
0024   /** Event numbers to convert when `events` is a ROOT file: '0', '0-4', '1,3'. */
0025   eventRange?: string;
0026   /** Collection groups to convert when `events` is a ROOT file ('' or absent = all). */
0027   collections?: string;
0028   /**
0029    * Facet values, e.g. `{ process: 'dis-nc', beam: '10x100', minQ2: '1000' }`.
0030    * Keys are free; the facets an installation declares decide which keys a
0031    * physics-selection UI turns into choices.
0032    */
0033   tags?: Record<string, string>;
0034 }
0035 
0036 /** Description of one tag value: what a UI shows when the value is chosen. */
0037 export interface DataCatalogFacetValue {
0038   /** Chip caption; the raw tag value when absent. */
0039   label?: string;
0040   /** Explanation shown when the value is selected. */
0041   description?: string;
0042   /** Web page with more information. */
0043   link?: string;
0044 }
0045 
0046 /** One selectable dimension of the physics picker: a tag key with its known values. */
0047 export interface DataCatalogFacet {
0048   /** Tag key in `DataCatalogEntry.tags`. */
0049   key: string;
0050   /** Row caption, e.g. 'Process', 'Beam'. */
0051   label: string;
0052   /**
0053    * Known values in display order, with captions and descriptions. Values
0054    * that entries use but this map omits are still offered, after these, in
0055    * first-seen order.
0056    */
0057   values?: Record<string, DataCatalogFacetValue>;
0058 }
0059 
0060 /** Everything one source (pack, server, remote file) contributes. */
0061 export interface DataCatalog {
0062   entries?: DataCatalogEntry[];
0063   facets?: DataCatalogFacet[];
0064   /** Extra geometry URLs offered for manual pick, beyond those of `entries`. */
0065   geometrySources?: string[];
0066   /** Extra event data URLs offered for manual pick, beyond those of `entries`. */
0067   eventSources?: string[];
0068 }
0069 
0070 /** Merges catalogs in order; facets with the same key merge their value maps (later wins). */
0071 export function mergeDataCatalogs(catalogs: ReadonlyArray<DataCatalog | null | undefined>): DataCatalog {
0072   const merged: Required<DataCatalog> = { entries: [], facets: [], geometrySources: [], eventSources: [] };
0073   const facetsByKey = new Map<string, DataCatalogFacet>();
0074   for (const catalog of catalogs) {
0075     if (!catalog) continue;
0076     merged.entries.push(...(catalog.entries ?? []));
0077     merged.geometrySources.push(...(catalog.geometrySources ?? []));
0078     merged.eventSources.push(...(catalog.eventSources ?? []));
0079     for (const facet of catalog.facets ?? []) {
0080       const existing = facetsByKey.get(facet.key);
0081       if (existing) {
0082         facetsByKey.set(facet.key, { ...existing, ...facet, values: { ...existing.values, ...facet.values } });
0083       } else {
0084         facetsByKey.set(facet.key, { ...facet, values: { ...facet.values } });
0085       }
0086     }
0087   }
0088   merged.facets = [...facetsByKey.values()];
0089   return merged;
0090 }
0091 
0092 /** Distinct values of `key` across entries: the facet's declared order first, then first-seen order. */
0093 export function facetValues(entries: ReadonlyArray<DataCatalogEntry>, facet: DataCatalogFacet): string[] {
0094   const seen = new Set<string>();
0095   for (const entry of entries) {
0096     const value = entry.tags?.[facet.key];
0097     if (value !== undefined) seen.add(value);
0098   }
0099   const declared = Object.keys(facet.values ?? {}).filter(value => seen.has(value));
0100   const undeclared = [...seen].filter(value => !(value in (facet.values ?? {})));
0101   return [...declared, ...undeclared];
0102 }
0103 
0104 /** Entries whose tags carry every selected value (an empty selection matches all). */
0105 export function matchEntries(
0106   entries: ReadonlyArray<DataCatalogEntry>,
0107   selectedTags: Readonly<Record<string, string>>,
0108 ): DataCatalogEntry[] {
0109   const wanted = Object.entries(selectedTags);
0110   return entries.filter(entry => wanted.every(([key, value]) => entry.tags?.[key] === value));
0111 }
0112 
0113 /** Distinct, order-preserving list of geometry URLs the catalog offers. */
0114 export function catalogGeometrySources(catalog: DataCatalog): string[] {
0115   return distinct([
0116     ...(catalog.entries ?? []).map(entry => entry.geometry),
0117     ...(catalog.geometrySources ?? []),
0118   ]);
0119 }
0120 
0121 /** Distinct, order-preserving list of event data URLs the catalog offers. */
0122 export function catalogEventSources(catalog: DataCatalog): string[] {
0123   return distinct([
0124     ...(catalog.entries ?? []).map(entry => entry.events),
0125     ...(catalog.eventSources ?? []),
0126   ]);
0127 }
0128 
0129 function distinct(values: ReadonlyArray<string | undefined>): string[] {
0130   const result: string[] = [];
0131   const seen = new Set<string>();
0132   for (const value of values) {
0133     if (!value || seen.has(value)) continue;
0134     seen.add(value);
0135     result.push(value);
0136   }
0137   return result;
0138 }