Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 22 additions & 11 deletions src/ThreeEditor/Simulation/Physics/Beam.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { Line2 } from 'three/examples/jsm/lines/Line2.js';
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js';
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js';

import { COMMON_PARTICLE_TYPES, FLUKA_PARTICLE_TYPES, Particle } from '../../../types/Particle';
// Import of 'lines' from examples subfolder follows the official guidelines of threejs.editor (see https://threejs.org/docs/#manual/en/introduction/Installation)
import { PARTICLE_CATALOGUE, ParticleEntry } from '../../../types/ParticleCatalogue';
import { SimulatorType } from '../../../types/RequestTypes';
import { ConfigSourceFile } from '../../../types/SimulationTypes/ConfigTypes';
import { SerializableState } from '../../js/EditorJson';
import { YaptideEditor } from '../../js/YaptideEditor';
Expand Down Expand Up @@ -34,6 +35,10 @@ export type BeamSourceType = keyof typeof BEAM_SOURCE_TYPE;
export const EnergyUnits = ['MeV', 'MeV/nucl'];
export type EnergyUnit = (typeof EnergyUnits)[number];

interface Particle {
pdg: number;
}

export type BeamJSON = Omit<
SimulationElementJSON & {
position: THREE.Vector3Tuple;
Expand Down Expand Up @@ -84,10 +89,13 @@ const _default = {
distanceToFocal: 0
},
particle: {
id: 2,
name: 'Proton',
pdg: 2212,
displayName: 'Proton',
aliases: ['p', 'proton'],
a: 1,
z: 1
z: 1,
sortPriority: 0,
simulators: [SimulatorType.SHIELDHIT, SimulatorType.FLUKA, SimulatorType.GEANT4]
},
sigma: {
type: SIGMA_TYPE.Gaussian,
Expand Down Expand Up @@ -159,14 +167,12 @@ export class Beam extends SimulationElement implements SerializableState<BeamJSO

numberOfParticles: number;

particleData: Particle;
particleData: ParticleEntry;

sourceFile: ConfigSourceFile;

get particle(): Particle {
return [...COMMON_PARTICLE_TYPES, ...FLUKA_PARTICLE_TYPES].find(
p => p.id === this.particleData.id
) as Particle;
get particle(): ParticleEntry {
return PARTICLE_CATALOGUE.find(p => p.pdg === this.particleData.pdg) as ParticleEntry;
}

constructor(editor: YaptideEditor) {
Expand Down Expand Up @@ -298,6 +304,10 @@ export class Beam extends SimulationElement implements SerializableState<BeamJSO
}

toSerialized() {
const particle: Particle = {
pdg: this.particleData.pdg ?? 2212
};

const jsonObject: BeamJSON = {
...super.toSerialized(),
position: this.position.toArray(),
Expand All @@ -310,7 +320,7 @@ export class Beam extends SimulationElement implements SerializableState<BeamJSO
sigma: this.sigma,
sad: this.sad,
divergence: this.divergence,
particle: this.particleData,
particle: particle,
colorHex: this.material.color.getHex(),
numberOfParticles: this.numberOfParticles,
sourceFile: this.sourceFile,
Expand All @@ -331,7 +341,8 @@ export class Beam extends SimulationElement implements SerializableState<BeamJSO
this.energyLowCutoff = loadedData.energyLowCutoff;
this.energyHighCutoff = loadedData.energyHighCutoff;
this.divergence = loadedData.divergence;
this.particleData = loadedData.particle;
this.particleData =
PARTICLE_CATALOGUE.find(p => p.pdg === loadedData.particle.pdg) ?? _default.particle;
this.material.color.setHex(loadedData.colorHex);
this.numberOfParticles = loadedData.numberOfParticles;
this.sourceFile = loadedData.sourceFile;
Expand Down
4 changes: 2 additions & 2 deletions src/ThreeEditor/Simulation/Scoring/GeantScoringFilter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Particle } from '../../../types/Particle';
import { ParticleEntry } from '../../../types/ParticleCatalogue';
import { YaptideEditor } from '../../js/YaptideEditor';
import { SimulationElementJSON } from '../Base/SimulationElement';
import { ScoringFilter } from './ScoringFilter';
Expand All @@ -11,7 +11,7 @@ export type FilterType =
| 'particleWithKineticEnergy';

type FilterData = {
particleTypes: Particle[];
particleTypes: ParticleEntry[];
kineticEnergyLow: number;
kineticEnergyHigh: number;
kineticEnergyUnit: string;
Expand Down
8 changes: 4 additions & 4 deletions src/ThreeEditor/Simulation/Scoring/ParticleFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ScoringFilter } from './ScoringFilter';
export type ParticleFilterJSON = Omit<
SimulationElementJSON & {
particle: {
id: number;
pdg: number;
name: string;
};
},
Expand All @@ -17,15 +17,15 @@ export function isParticleFilterJSON(filter: any): filter is ParticleFilterJSON
}

export class ParticleFilter extends ScoringFilter {
particleData: { id: number; name: string };
particleData: { pdg: number; name: string };

constructor(editor: YaptideEditor) {
super(editor);
this.particleData = { id: 2, name: 'Proton' };
this.particleData = { pdg: 2212, name: 'Proton' };
}

clear(): this {
this.particleData = { id: 2, name: 'Proton' };
this.particleData = { pdg: 2212, name: 'Proton' };
this.name = 'Filter';

return this;
Expand Down
41 changes: 35 additions & 6 deletions src/ThreeEditor/components/Select/ParticleSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,56 @@
import { Box, Typography } from '@mui/material';
import { SyntheticEvent } from 'react';

import { Particle } from '../../../types/Particle';
import {
filterParticles,
isMostAbundantIsotope,
ParticleEntry} from '../../../types/ParticleCatalogue';
import { AutoCompleteSelect } from '../../../util/genericComponents/AutoCompleteSelect';

export interface ParticleSelectProps {
onChange?: (event: SyntheticEvent<Element, Event>, newValue: number) => void;
particles: readonly Particle[];
particles: readonly ParticleEntry[];
value?: number;
}

export function ParticleSelect(props: ParticleSelectProps) {
const getOptionLabel = ({ id, name }: Particle) => {
return `[${id}] ${name}`;
const getOptionLabel = ({ pdg, displayName }: ParticleEntry) => {
return `${displayName}`;
};

// Custom render for dropdown options
const renderOption = (liProps: any, particle: ParticleEntry) => {
const isMostAbundant = isMostAbundantIsotope(particle, props.particles) || false;
// const abundanceText = particle.abundance ? ` (${particle.abundance.toFixed(2)}%)` : '';

return (
<Box
component='li'
{...liProps}>
<Typography
variant='body2'
sx={{
fontWeight: isMostAbundant ? 'bold' : 'normal',
width: '100%'
}}>
{particle.displayName}
{/* {abundanceText} */}
{isMostAbundant && ' ★'}
</Typography>
</Box>
);
};

return (
<AutoCompleteSelect
onChange={(event, newValue) => {
if (newValue !== null) props.onChange?.call(null, event, newValue.id);
if (newValue !== null) props.onChange?.call(null, event, newValue.pdg);
}}
value={props.particles.find(p => p.id === props.value)}
value={props.particles.find(p => p.pdg === props.value)}
options={props.particles}
getOptionLabel={option => getOptionLabel(option)}
renderOption={renderOption}
filterOptions={(options, state) => filterParticles(state.inputValue, options)}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ import { Object3D } from 'three';

import { StyledExclusiveToggleButtonGroup } from '../../../../../shared/components/StyledExclusiveToggleButtonGroup';
import {
COMMON_PARTICLE_TYPES,
FLUKA_PARTICLE_TYPES,
GEANT4_PARTICLE_TYPES,
Particle
} from '../../../../../types/Particle';
getParticlesForSimulator,
isIon,
ParticleEntry} from '../../../../../types/ParticleCatalogue';
import { SimulatorType } from '../../../../../types/RequestTypes';
import { useSmartWatchEditorState } from '../../../../../util/hooks/signals';
import { SetValueCommand } from '../../../../js/commands/SetValueCommand';
Expand Down Expand Up @@ -157,23 +155,25 @@ function BeamConfigurationFields(props: { editor: YaptideEditor; object: Beam })
const { object, editor } = props;
const { state: watchedObject } = useSmartWatchEditorState(editor, object, true);

// const elementOptions = HEAVY_ION_LIST.map(ion => ion.name);

// energyUnit should be held in react state so the change re-renders the component
// watchedObject.energyUnit is kept in sync in updateEnergyInputs()
const [energyUnit, setEnergyUnit] = useState<EnergyUnit>(watchedObject.energyUnit);

let supportedParticles: Particle[] = [];
let supportedParticles: ParticleEntry[] = [];

switch (editor.contextManager.currentSimulator) {
case SimulatorType.GEANT4:
supportedParticles.push(...GEANT4_PARTICLE_TYPES);
supportedParticles.push(...getParticlesForSimulator(SimulatorType.GEANT4));

break;
case SimulatorType.FLUKA:
supportedParticles.push(...COMMON_PARTICLE_TYPES, ...FLUKA_PARTICLE_TYPES);
supportedParticles.push(...getParticlesForSimulator(SimulatorType.FLUKA));

break;
default:
supportedParticles.push(...COMMON_PARTICLE_TYPES);
supportedParticles.push(...getParticlesForSimulator(SimulatorType.SHIELDHIT));
}

const setValueCommand = useCallback(
Expand All @@ -189,16 +189,16 @@ function BeamConfigurationFields(props: { editor: YaptideEditor; object: Beam })
}
}, [editor.contextManager.currentSimulator, setValueCommand]);

const shouldShowEnergyUnit = useCallback((particle: Particle) => {
const shouldShowEnergyUnit = useCallback((particle: ParticleEntry) => {
return (
particle.a &&
(particle.a > 1 || // every particle with a > 1
particle.id === 25) // heavy ions, even if a == 1
isIon(particle)) // heavy ions, even if a == 1
);
}, []);

const updateEnergyInputs = useCallback(
(oldUnit: EnergyUnit, newUnit: EnergyUnit, newParticle?: Particle) => {
(oldUnit: EnergyUnit, newUnit: EnergyUnit, newParticle?: ParticleEntry) => {
let massNumber = watchedObject.particleData.a ?? 1;

if (oldUnit === 'MeV' && newUnit === 'MeV/nucl') {
Expand Down Expand Up @@ -250,9 +250,9 @@ function BeamConfigurationFields(props: { editor: YaptideEditor; object: Beam })
<PropertyField label='Particle type'>
<ParticleSelect
particles={supportedParticles}
value={watchedObject.particleData.id}
value={watchedObject.particleData.pdg}
onChange={(_, v) => {
const newParticleData = supportedParticles.find(p => p.id === v);
const newParticleData = supportedParticles.find(p => p.pdg === v);

if (!newParticleData) {
return;
Expand All @@ -271,7 +271,7 @@ function BeamConfigurationFields(props: { editor: YaptideEditor; object: Beam })
/>
</PropertyField>

{watchedObject.particleData.id === 25 && (
{/* {watchedObject.particleData.id >= 25 && (
<>
<NumberPropertyField
label='charge (Z)'
Expand All @@ -283,7 +283,7 @@ function BeamConfigurationFields(props: { editor: YaptideEditor; object: Beam })
}
/>
<NumberPropertyField
label='nucleons (A)'
label='nucleons (A)'
precision={0}
step={1}
value={watchedObject.particleData.a ?? 12}
Expand All @@ -292,6 +292,102 @@ function BeamConfigurationFields(props: { editor: YaptideEditor; object: Beam })
}
/>
</>
)} */}
{isIon(watchedObject.particleData) && ( // select-lists should be moved to select directory
<>
{/* <PropertyField label="Element">
<ParticleSelect
particles={HEAVY_ION_LIST}
value={watchedObject.particleData.z ?? 6}
onChange={(_, newZ) => {
const ion = HEAVY_ION_LIST.find(i => i.z === newZ);
if (ion) {
setValueCommand({
...watchedObject.particleData,
a: ion.a,
z: ion.z
}, 'particleData');
}
}}
/>
</PropertyField> */}

{/* <SelectPropertyField
label='Element'
value={watchedObject.particleData.name}
options={elementOptions}
onChange={newName => {
const ion = HEAVY_ION_LIST.find(i => i.name === newName);

if (ion) {
setValueCommand(
{
...watchedObject.particleData,
id: 25,
name: ion.name,
a: ion.a,
z: ion.z
},
'particleData'
);
}
}}
/>

<SelectPropertyField
label='Isotope'
value={watchedObject.particleData.name + '-' + watchedObject.particleData.a}
options={
ISOTOPES[watchedObject.particleData.name]?.map(
iso => watchedObject.particleData.name + '-' + iso.a.toString()
) || []
}
onChange={newA => {
const ion = ISOTOPES[watchedObject.particleData.name];

if (ion) {
setValueCommand(
{
...watchedObject.particleData,
a: newA
},
'particleData'
);
}
}}
/> */}
{/* <PropertyField>
<label>
<span style={{ marginLeft: '128px' }}>
Z = {watchedObject.particleData.z}
</span>
<span style={{ marginLeft: '32px' }}>
A = {watchedObject.particleData.a}
</span>
</label>
</PropertyField> */}

<PropertyField label='A'>
<div
style={{
padding: '8px',
color: 'gray',
borderBottom: '1px solid #444'
}}>
{watchedObject.particleData.a}
</div>
</PropertyField>
<PropertyField label='Z'>
<div
style={{
padding: '8px',
color: 'gray',
borderBottom: '1px solid #444'
}}>
{watchedObject.particleData.z}
</div>
</PropertyField>
</>
)}

<NumberPropertyField
Expand Down
Loading
Loading