Conversation
|
Take a look at following design: https://gist.github.com/grzanka/1c63ccb5921756bd0f0ea93642af627c Key rule: every heavy ion gets Scoring filters — small converter fix needed
Design decision: catalogue in UI, PARTICLE_DICT in converterThe UI owns the isotope catalogue (display names, aliases, abundance, sortPriority). The converter owns the per-simulator Data Model (UI repo:
|
displayName |
id |
a |
z |
sortPriority |
aliases |
simulators |
|---|---|---|---|---|---|---|
| Proton | 2 | 1 | 1 | 0 | ["proton", "p", "protium", "hydrogen-1", "H-1", "hydrogen", "1H"] |
SH, FLUKA, G4 |
| Neutron | 1 | 1 | 0 | 1 | ["neutron", "n"] |
SH, FLUKA, G4 |
| Photon | 3 (G4) | — | — | 1 | ["photon", "gamma", "γ"] |
G4 |
| Electron (G4) | 4 (G4) | — | — | 1 | ["electron", "e-"] |
G4 |
| Electron (FLUKA) | 26 | — | — | 1 | ["electron", "e-"] |
FLUKA |
| Positron | 5 (G4) | — | — | 1 | ["positron", "e+"] |
G4 |
| Alpha (G4) | 6 (G4) | 4 | 2 | 1 | ["alpha", "helium-4", "He-4", "4He"] |
G4 |
| Helium-4 | 24 | 4 | 2 | 1 | ["helium-4", "He-4", "4He", "alpha"] |
SH, FLUKA |
| Deuteron | 21 | 2 | 1 | 2 | ["deuteron", "d", "deuterium", "hydrogen-2", "H-2", "2H"] |
SH, FLUKA |
| Triton | 22 | 3 | 1 | 2 | ["triton", "t", "tritium", "hydrogen-3", "H-3", "3H"] |
SH, FLUKA |
| Helium-3 | 23 | 3 | 2 | 2 | ["helium-3", "He-3", "3He"] |
SH, FLUKA |
| Pion π- | 3 | — | — | 10 | ["pion pi-", "pion-", "π-"] |
SH, FLUKA |
| Pion π+ | 4 | — | — | 10 | ["pion pi+", "pion+", "π+"] |
SH, FLUKA |
| Anti-proton | 7 | 1 | 1 | 10 | ["anti-proton", "antiproton", "p-bar"] |
SH, FLUKA |
| Kaon κ- | 8 | — | — | 10 | ["kaon k-", "kaon-", "κ-"] |
SH, FLUKA |
| Kaon κ+ | 9 | — | — | 10 | ["kaon k+", "kaon+", "κ+"] |
SH, FLUKA |
| Kaon κ0 | 10 | — | — | 10 | ["kaon k0", "kaon0", "κ0"] |
SH, FLUKA |
| Kaon κ~ | 11 | — | — | 10 | ["kaon k~", "kaon~", "κ~"] |
SH, FLUKA |
| Muon µ- | 15 | — | — | 10 | ["muon mu-", "muon-", "µ-"] |
SH, FLUKA, G4 |
| Muon µ+ | 16 | — | — | 10 | ["muon mu+", "muon+", "µ+"] |
SH, FLUKA, G4 |
| Pion π- (G4) | 9 (G4) | — | — | 10 | ["pion pi-", "pion-", "π-"] |
G4 |
| Pion π+ (G4) | 10 (G4) | — | — | 10 | ["pion pi+", "pion+", "π+"] |
G4 |
Note: Particles shared across simulators with different IDs (e.g. Electron is
id:26in FLUKA butid:4in Geant4) exist as separate entries, disambiguated bysimulators.
Heavy-ion isotopes (all get id: 25)
All isotopes from Li (Z=3) through U (Z=92), including both stable and commonly-used unstable isotopes. Each entry has abundance from IUPAC data and sortPriority based on research usage frequency.
Example entries:
// sortPriority 0 — everyday workhorse
{
displayName: "Carbon-12",
aliases: ["carbon-12", "C-12", "12C", "carbon"],
id: 25, a: 12, z: 6,
abundance: 98.93,
sortPriority: 0,
simulators: [SimulatorType.SHIELDHIT, SimulatorType.FLUKA, SimulatorType.GEANT4]
},
// sortPriority 2 — frequently used
{
displayName: "Oxygen-16",
aliases: ["oxygen-16", "O-16", "16O", "oxygen"],
id: 25, a: 16, z: 8,
abundance: 99.757,
sortPriority: 2,
simulators: [SimulatorType.SHIELDHIT, SimulatorType.FLUKA, SimulatorType.GEANT4]
},
// sortPriority 3 — notable special-purpose
{
displayName: "Uranium-235",
aliases: ["uranium-235", "U-235", "235U"],
id: 25, a: 235, z: 92,
abundance: 0.72,
sortPriority: 3,
simulators: [SimulatorType.SHIELDHIT, SimulatorType.FLUKA, SimulatorType.GEANT4]
},
// sortPriority 10 (default) — everything else
{
displayName: "Carbon-13",
aliases: ["carbon-13", "C-13", "13C"],
id: 25, a: 13, z: 6,
abundance: 1.07,
sortPriority: 10,
simulators: [SimulatorType.SHIELDHIT, SimulatorType.FLUKA, SimulatorType.GEANT4]
},Complete sortPriority assignments for heavy-ion isotopes:
sortPriority |
Isotopes |
|---|---|
| 0 | C-12 |
| 2 | N-14, O-16, Ne-20, Ar-40, Fe-56 |
| 3 | C-14, Si-28, Ca-40, Pb-208, U-235, U-238 |
| 10 | All remaining isotopes |
Alias rules for isotopes:
"{element}-{A}"lowercase (e.g."carbon-12")"{Sym}-{A}"(e.g."C-12")"{A}{Sym}"(e.g."12C")- If this is the most abundant isotope of its element (derived from
abundance), also the bare element name (e.g."carbon") - Full element name always included (e.g.
"carbon-12"contains"carbon"as substring, ensuring match)
Deduplication with light particles:
- Proton (
id:2) covers H-1. No separateid:25, a:1, z:1entry. - Deuteron (
id:21) covers H-2. No separateid:25, a:2, z:1. - Triton (
id:22) covers H-3. No separateid:25, a:3, z:1. - He-3 (
id:23) covers He-3. No separateid:25, a:3, z:2. - He-4/Alpha (
id:24/id:6) covers He-4. No separateid:25, a:4, z:2. - Heavy-ion isotopes start at Li-6 (
id:25, a:6, z:3).
4. Helper functions
// src/types/ParticleCatalogue.ts
/** Return particles available for a given simulator, pre-sorted by sortPriority */
export function getParticlesForSimulator(sim: SimulatorType): readonly ParticleEntry[];
/** Lookup by (id, a, z) — used for deserialisation */
export function findParticleByIdAZ(
id: number, a?: number, z?: number, sim?: SimulatorType
): ParticleEntry | undefined;
/** True if particle is a heavy ion (id === 25) */
export function isHeavyIon(p: ParticleEntry): boolean;
/**
* Filter particles by query string against aliases (case-insensitive substring).
* Returns results sorted by: sortPriority ASC, then Z ASC, then abundance DESC.
*/
export function filterParticles(
query: string, particles: readonly ParticleEntry[]
): ParticleEntry[];
/**
* Sort comparator for particle list.
* Primary: sortPriority ASC (lower = first)
* Secondary: Z ASC (lighter elements first)
* Tertiary: abundance DESC (most abundant isotope first)
*/
export function compareParticles(a: ParticleEntry, b: ParticleEntry): number;
/**
* Returns true if this particle is the most naturally abundant isotope
* for its element (same Z) in the given catalogue subset.
*/
export function isMostAbundantIsotope(
particle: ParticleEntry, catalogue: readonly ParticleEntry[]
): boolean;5. Catalogue is closed
No manual A/Z input. The user must pick from the catalogue dropdown. Missing isotopes require a code change to PARTICLE_CATALOGUE.
Inputs
| Input | Type | Constraints |
|---|---|---|
| Particle type | Single autocomplete text field | Must resolve to exactly one ParticleEntry. Free text for searching; selection from dropdown only. |
Behavior
Dropdown / Search
- User sees a single field labelled "Particle type".
- On focus or typing, a filterable dropdown appears with all particles for the current simulator.
- Filtering: case-insensitive substring match against all aliases of each
ParticleEntry. E.g.:"elec"→ Electron"C-1"→ Carbon-12, Carbon-13, Carbon-14"carbon"→ all carbon isotopes (C-12 first, bold)"p"→ Proton (and any others matching "p")"12C"→ Carbon-12"H-2"→ Deuteron (not a heavy-ion entry, because H-2 is deduplicated to deuteron)
- Ordering (uses
compareParticles()):- Primary:
sortPriorityascending — Proton (0), C-12 (0) at top, rare isotopes (10) at bottom. - Secondary:
zascending — lighter elements before heavier. - Tertiary:
abundancedescending — most abundant isotope of an element first.
- Primary:
- Rendering:
- Each option shows
displayName. - If
isMostAbundantIsotope(entry, currentList)→ rendered in bold + ★ indicator. - Non-bold items use normal weight.
- Each option shows
- User selects →
onChangefires with fullParticleEntry.
Default (unfiltered) dropdown appearance
When the user focuses the field without typing:
Proton ← sortPriority 0
Carbon-12 ★ ← sortPriority 0, abundance 98.93%
Neutron ← sortPriority 1
Electron ← sortPriority 1
Helium-4 ★ ← sortPriority 1
…
Deuteron ← sortPriority 2
Nitrogen-14 ★ ← sortPriority 2, abundance 99.63%
Oxygen-16 ★ ← sortPriority 2, abundance 99.76%
Iron-56 ★ ← sortPriority 2, abundance 91.75%
…
Carbon-14 ← sortPriority 3, abundance 0 (trace/unstable)
Uranium-238 ★ ← sortPriority 3, abundance 99.27%
Uranium-235 ← sortPriority 3, abundance 0.72%
…
Lithium-7 ★ ← sortPriority 10, abundance 92.41%
Lithium-6 ← sortPriority 10, abundance 7.59%
Beryllium-9 ★ ← sortPriority 10, abundance 100%
Boron-11 ★ ← sortPriority 10, abundance 80.1%
Boron-10 ← sortPriority 10, abundance 19.9%
… (remaining isotopes by Z, then abundance)
After Selection
- The
displayNameis shown in the input field. - If
isHeavyIon(selected), an info row appears below: A = {a}, Z = {z} (read-only, informational). - The old separate "Element", "Isotope", editable Z, editable A fields are removed.
- Energy unit toggle (
MeVvsMeV/nucl) appears whena > 1orid === 25.
Serialisation (UI → JSON → Converter)
Beam.toSerialized()writes:"particle": { "id": 25, "name": "Carbon-12", "a": 12, "z": 6 }
namecarries the resolveddisplayName. FieldsabundanceandsortPriorityare not serialised — they are UI-only metadata.Beam.fromSerialized()callsfindParticleByIdAZ(id, a, z)to restore the fullParticleEntry. Falls back to raw data if not found (legacy/forward compat).ParticleFilterandGeantScoringFilterserialisation follows the same pattern.
Edge Cases
- Unknown particle in loaded file: If
findParticleByIdAZreturnsundefined, preserve raw data. Display"{name} (id={id})"with a ⚠ warning icon. - Simulator switch: If the selected particle is not in the new simulator's list, reset to Proton.
- Legacy
"Heavy ions"name:fromSerializedmatches by(id=25, a, z)regardless ofnamevalue — handles old project files that say"Heavy ions". - Unstable isotopes with 0% abundance: Entries like C-14, Th-232, U-235 have
abundancevalues from NUBASE2020 (trace amounts or 0). They still appear in the catalogue and can be searched by alias. They will never get bold/★ rendering (they are never the most abundant).
Output
- Single MUI
Autocompletecomponent replaces oldParticleSelect. - Dropdown pre-sorted by
sortPriority→z→abundance, putting commonly-used particles at top. - Bold + ★ for the most abundant isotope of each element (derived from
abundance). - After selection: read-only A/Z info line for heavy ions.
- Serialised JSON backward-compatible (
id: 25for heavy ions, dedicated IDs for light particles). abundanceandsortPrioritynever appear in serialised JSON — they are UI-only.- All three converter backends continue to work without wire format changes.
Acceptance Criteria
Data Layer
-
PARTICLE_CATALOGUEcontains all non-ion particles from currentCOMMON_PARTICLE_TYPES,FLUKA_PARTICLE_TYPES,GEANT4_PARTICLE_TYPES. -
PARTICLE_CATALOGUEcontains isotopes from Li (Z=3) through U (Z=92) withid: 25, including both stable and commonly-used unstable isotopes (C-14, U-235, Th-232, etc.). - Proton, Deuteron, Triton, He-3, He-4 are not duplicated as
id: 25entries. - Every isotope entry has aliases:
"{element}-{A}","{Sym}-{A}","{A}{Sym}". - Bare element name alias (e.g.
"carbon") maps only to the most abundant isotope of that element. - Proton aliases:
"proton","p","protium","hydrogen-1","H-1","hydrogen","1H". - Deuteron aliases:
"deuteron","d","deuterium","hydrogen-2","H-2","2H". - Electron aliases:
"electron","e-". - Positron aliases:
"positron","e+".
Abundance Data
- Every isotope entry has an
abundancevalue sourced from IUPAC 2021 / NUBASE2020. -
abundanceis a number between 0 and 100 (percentage). - For each element (same Z), exactly one isotope has the highest
abundance— this is the one that renders bold + ★. - Unstable isotopes not in IUPAC stable tables have
abundance: 0(or the trace value if listed). - Non-baryonic particles (electron, photon, pions, muons, kaons) have
abundance: undefined. - Non-ion light particles with dedicated IDs (proton, deuteron, etc.) have
abundance: undefined.
Sort Priority
- Proton has
sortPriority: 0. - Carbon-12 has
sortPriority: 0. - Neutron, Electron, Positron, Photon, Helium-4/Alpha have
sortPriority: 1. - Deuteron, Triton, He-3, N-14, O-16, Ne-20, Ar-40, Fe-56 have
sortPriority: 2. - U-235, U-238, C-14, Pb-208, Si-28 have
sortPriority: 3. - All other isotopes and rarely used non-ion particles default to
sortPriority: 10. -
getParticlesForSimulator()returns a list pre-sorted bycompareParticles().
Simulator Scoping
-
getParticlesForSimulator(SHIELDHIT)returns COMMON particles + heavy-ion isotopes (no Geant4-only particles). -
getParticlesForSimulator(GEANT4)returns Geant4 particles + heavy-ion isotopes (no SHIELD-HIT-only kaons, etc.). -
getParticlesForSimulator(FLUKA)returns COMMON particles + Electron (id:26) + heavy-ion isotopes.
UI — Beam Configuration
- Only one field labelled "Particle type" is shown (no Element, Isotope, Z, A editable fields).
- Typing
"elec"filters to show Electron. - Typing
"carbon"shows all carbon isotopes; C-12 appears first and is bold + ★. - Typing
"C-12"or"12C"matches Carbon-12. - Typing
"p"matches Proton. - Typing
"H-2"matches Deuteron. - Selecting Carbon-12 shows read-only info:
A = 12, Z = 6. - Selecting Proton does not show A/Z info.
- Selecting a particle with
a > 1orid === 25shows MeV/nucl toggle. - Opening the dropdown without typing shows Proton and C-12 at the very top (sortPriority 0).
UI — Particle Filters
- SHIELD/FLUKA
ParticleFilterConfigurationuses the unified selector with all particles (including heavy-ion isotopes selected from dropdown). - Geant4
GeantScoringFilterConfigurationmulti-select uses the sameParticleEntrycatalogue scoped to Geant4. - Selecting a heavy-ion isotope in a particle filter serialises
{ id: 25, name: "Carbon-12", a: 12, z: 6 }.
Serialisation & Backward Compatibility
- Saving beam with Carbon-12 →
particle: { id: 25, name: "Carbon-12", a: 12, z: 6 }. - Saving beam with Proton →
particle: { id: 2, name: "Proton", a: 1, z: 1 }. -
abundanceandsortPriorityare never written to serialised JSON. - Loading legacy
{ id: 25, name: "Heavy ions", a: 12, z: 6 }resolves to Carbon-12. - Loading unknown
(id, a, z)degrades gracefully — displays raw name, no crash. - Converter SHIELD-HIT parser continues to emit
JPART0 25+HIPROJ 12 6for Carbon-12. - Converter FLUKA parser continues to emit
HEAVYION+HI-PROPE 6 12for Carbon-12. - Converter Geant4 generator continues to emit
/gps/particle ion+/gps/ion 6 12 0 0for Carbon-12.
Converter — Scoring Filter Fix (small change in yaptide/converter)
-
converter/shieldhit/parser.pyparse_scoring_filter(): whenparticle.id == 25, emit[("Z", "==", particle.z), ("A", "==", particle.a)]instead of failing on missing"filter"key. -
converter/geant4/Geant4MacroGenerator.py_append_quantity(): when filter particleid == 25, emit ion-syntax filter.
Code Quality
- Old
COMMON_PARTICLE_TYPES,FLUKA_PARTICLE_TYPES,GEANT4_PARTICLE_TYPESreplaced bygetParticlesForSimulator(). - No hardcoded
=== 25checks for showing/hiding UI fields — useisHeavyIon(). -
HEAVY_ION_LISTfrom PR handle heavy ions #2353 refactored into catalogue with proper aliases, abundance, and sortPriority. - All commented-out dead code from PR handle heavy ions #2353 removed.
- No
anytypes for particle data flowing through components. - Unit tests for
filterParticles()alias matching (at least 10 cases). - Unit tests for
findParticleByIdAZ()including legacy name fallback. - Unit tests for
getParticlesForSimulator()per simulator. - Unit tests for
compareParticles()sort order. - Unit tests for
isMostAbundantIsotope().
Implementation Plan
Phase 1 — Data Layer (yaptide/ui — src/types/)
- Create
src/types/ParticleCatalogue.ts:- Define
ParticleEntryinterface withabundanceandsortPriority. - Build
PARTICLE_CATALOGUEarray with all entries, aliases, IUPAC abundance values, and sortPriority tiers. - Export helpers:
getParticlesForSimulator(),findParticleByIdAZ(),isHeavyIon(),filterParticles(),compareParticles(),isMostAbundantIsotope().
- Define
- Update
src/types/Particle.ts:- Re-export
ParticleEntryasParticle(backward-compat type alias). - Remove/deprecate
COMMON_PARTICLE_TYPES,FLUKA_PARTICLE_TYPES,GEANT4_PARTICLE_TYPES.
- Re-export
Phase 2 — Selector Component (yaptide/ui — src/ThreeEditor/components/Select/)
- Rewrite
ParticleSelect.tsx:- Props:
particles: readonly ParticleEntry[],value: ParticleEntry | null,onChange: (entry: ParticleEntry) => void. - MUI
Autocompletewith customfilterOptionscallingfilterParticles(). - Custom
renderOption: bold + ★ whenisMostAbundantIsotope()returns true. - Options pre-sorted by
compareParticles().
- Props:
Phase 3 — Beam Configuration (yaptide/ui — src/ThreeEditor/components/Sidebar/)
- Update
BeamConfiguration.tsx:- Replace
supportedParticlesconstruction withgetParticlesForSimulator(currentSimulator). - Remove editable Z/A number fields.
- Add read-only A/Z info line for
isHeavyIon(selected). - Replace
shouldShowEnergyUnitlogic withisHeavyIon()helper +a > 1check.
- Replace
Phase 4 — Particle Filters (yaptide/ui)
- Update
ParticleFilterConfiguration.tsx:- Use unified selector with full particle list (remove old
filter(p => p.id !== 25)). - On selection, serialise full
{ id, name, a, z }.
- Use unified selector with full particle list (remove old
- Update
GeantScoringFilterConfiguration.tsx:- Multi-select from
getParticlesForSimulator(GEANT4).
- Multi-select from
Phase 5 — Models & Serialisation (yaptide/ui)
- Update
Beam.ts:- Change
particleDatatype toParticleEntry. toSerialized(): emit only{ id, name: displayName, a, z }— stripabundance,sortPriority,aliases,simulators.fromSerialized(): resolve viafindParticleByIdAZ(particle.id, particle.a, particle.z).
- Change
- Update
ParticleFilter.ts:- Extend
ParticleFilterJSONto include optionalaandz. - Same serialisation/deserialisation pattern.
- Extend
- Update
GeantScoringFilter.ts:FilterData.particleTypesbecomesParticleEntry[].
Phase 6 — Converter Fix (yaptide/converter)
converter/shieldhit/parser.py→parse_scoring_filter():- When
particle.id == 25, emit[("Z", "==", z), ("A", "==", a)].
- When
converter/geant4/Geant4MacroGenerator.py→_append_quantity():- When filter particle
id == 25, handle ion filter.
- When filter particle
- Add converter tests for heavy-ion filter serialisation.
Phase 7 — Cleanup & Tests
- Remove all dead/commented code from PR handle heavy ions #2353.
- Add unit tests per acceptance criteria above.
- Add integration/snapshot tests for
ParticleSelectrendering.
Files Changed (Summary)
yaptide/ui
| File | Action |
|---|---|
src/types/ParticleCatalogue.ts |
NEW — catalogue, ParticleEntry, helpers, abundance data |
src/types/Particle.ts |
Simplify — re-export from catalogue, remove static arrays |
src/ThreeEditor/components/Select/ParticleSelect.tsx |
Rewrite — alias filtering, bold/★ from isMostAbundantIsotope(), sorted by compareParticles() |
src/ThreeEditor/components/Sidebar/…/BeamConfiguration.tsx |
Update — single field, info line, remove Z/A inputs, use isHeavyIon() |
src/ThreeEditor/components/Sidebar/…/ParticleFilterConfiguration.tsx |
Update — use unified selector with all particles including isotopes |
src/ThreeEditor/components/Sidebar/…/GeantScoringFilterConfiguration.tsx |
Update — use catalogue |
src/ThreeEditor/Simulation/Physics/Beam.ts |
Update — ParticleEntry type, strip UI-only fields in toSerialized(), resolve in fromSerialized() |
src/ThreeEditor/Simulation/Scoring/ParticleFilter.ts |
Update — extend JSON type with a/z, resolve on deserialisation |
src/ThreeEditor/Simulation/Scoring/GeantScoringFilter.ts |
Update — ParticleEntry[] in filter data |
yaptide/converter
| File | Action |
|---|---|
converter |
No description provided.