Skip to content

Commit

Permalink
simulator selection button (#1004)
Browse files Browse the repository at this point in the history
* simulator selection button - not working yet

* working on simulator selection

* works somewhat, generates locally dummy topas file

* generates locally dummy fluka file as well

* default files change when simulator changes

* updated output file names type

* fixed imports in topas converter

* removed topas simulation service (backend). Simulator choice doesnt appear in demo

* added tests for fluka and topas converters

* pass chosen simulator type to simulations tab

* removed unnecessary file and imports from tests. fixed text on shiledhit select button. renamed variable for fluka button in test.

* changed text on change simulator alert. made suggested changes to input files types

* simulator selection do nothing on select selected option

* Format files

---------

Co-authored-by: Jakub Niechaj <quban123@gmail.com>
  • Loading branch information
Bogdan2423 and Derstilon authored May 22, 2023
1 parent 9efb3ee commit 545e328
Show file tree
Hide file tree
Showing 13 changed files with 428 additions and 80 deletions.
8 changes: 5 additions & 3 deletions src/PythonConverter/PythonConverterService.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { createGenericContext } from '../services/GenericContext';
import { PythonWorker } from './PythonWorker';
import { EditorJson } from '../ThreeEditor/js/EditorJson';
import { SimulationInputFiles } from '../types/ResponseTypes';
import { SimulatorType } from '../types/RequestTypes';

declare global {
interface Window {
Expand All @@ -19,7 +20,8 @@ export interface PythonConverterProps {

export interface PythonConverterContext {
convertJSON: (
editorJson: EditorJson
editorJson: EditorJson,
simulator: SimulatorType
) => Promise<Map<keyof RemoveIndex<SimulationInputFiles>, string>>;
isConverterReady: boolean;
}
Expand Down Expand Up @@ -68,8 +70,8 @@ const PythonConverter = (props: PythonConverterProps) => {
};
});

const convertJSON = async (editorJSON: EditorJson) => {
return await workerRef.current!.convertJSON(editorJSON);
const convertJSON = async (editorJSON: EditorJson, simulator: SimulatorType) => {
return await workerRef.current!.convertJSON(editorJSON, simulator);
};

const value: PythonConverterContext = {
Expand Down
25 changes: 14 additions & 11 deletions src/PythonConverter/PythonWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import * as Comlink from 'comlink';
import { SimulationInputFiles } from '../types/ResponseTypes';
import { EditorJson } from '../ThreeEditor/js/EditorJson';
import { SimulatorType } from '../types/RequestTypes';

// as for now there is no reasonable npm package for pyodide
// CND method is suggested in https://pyodide.org/en/stable/usage/downloading-and-deploying.html
Expand All @@ -12,20 +13,13 @@ export interface PythonWorker {
close: () => void;
runPython: <T>(string: string) => T;
checkConverter: () => boolean;

convertJSON: (
editorJson: EditorJson
editorJson: EditorJson,
simulator: SimulatorType
) => Promise<Map<keyof RemoveIndex<SimulationInputFiles>, string>>;
}

const pythonConverterCode = `
import os
from converter.api import run_parser
from converter.api import get_parser_from_str
def convertJson(editor_json, parser_name = 'shieldhit'):
parser=get_parser_from_str(parser_name)
return run_parser(parser, editor_json.to_py(), None, True)
convertJson`;

const pythonCheckConverterCode = `
def checkIfConverterReady():
try:
Expand Down Expand Up @@ -75,7 +69,16 @@ print(micropip.list())
return self.pyodide.runPython(pythonCheckConverterCode);
}

convertJSON(editorJson: object) {
convertJSON(editorJson: object, simulator: SimulatorType) {
const pythonConverterCode = `
import os
from converter.api import run_parser
from converter.api import get_parser_from_str
def convertJson(editor_json, parser_name = '${simulator}'):
parser=get_parser_from_str(parser_name)
return run_parser(parser, editor_json.to_py(), None, True)
convertJson`;

return self.pyodide.runPython(pythonConverterCode)(editorJson).toJs();
}
}
Expand Down
32 changes: 16 additions & 16 deletions src/ThreeEditor/components/Dialog/OpenFileDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,22 +95,22 @@ export function OpenFileDialog(props: OpenFileProps) {
<List id={'Examples list'}>
{EXAMPLES.map((example, idx) => (
<ListItem
disablePadding
key={
example?.inputJson?.project?.title ??
'Example_' + idx.toString()
}
value={idx}
aria-labelledby={`example-btn-${idx}`}
aria-selected={exampleIndex === idx}
onClick={() => setExampleIndex(idx)}>
<ListItemButton
id={`example-btn-${idx}`}
selected={exampleIndex === idx}>
{example?.inputJson?.project?.title ??
'Example_' + idx.toString()}
</ListItemButton>
</ListItem>
disablePadding
key={
example?.inputJson?.project?.title ??
'Example_' + idx.toString()
}
value={idx}
aria-labelledby={`example-btn-${idx}`}
aria-selected={exampleIndex === idx}
onClick={() => setExampleIndex(idx)}>
<ListItemButton
id={`example-btn-${idx}`}
selected={exampleIndex === idx}>
{example?.inputJson?.project?.title ??
'Example_' + idx.toString()}
</ListItemButton>
</ListItem>
))}
</List>
<Button
Expand Down
8 changes: 7 additions & 1 deletion src/WrapperApp/WrapperApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { TabPanel } from './components/Panels/TabPanel';
import ResultsPanel from './components/Results/ResultsPanel';
import SimulationPanel from './components/Simulation/SimulationPanel';
import NavDrawer from './components/NavDrawer/NavDrawer';
import { SimulatorType } from '../types/RequestTypes';

function WrapperApp() {
const { editorRef, resultsSimulationData, setResultsSimulationData } = useStore();
Expand All @@ -29,6 +30,9 @@ function WrapperApp() {
const [tabsValue, setTabsValue] = useState('editor');

const [providedInputFiles, setProvidedInputFiles] = useState<SimulationInputFiles>();
const [currentSimulator, setCurrentSimulator] = useState<SimulatorType>(
SimulatorType.SHIELDHIT
);
useEffect(() => {
if (providedInputFiles && tabsValue !== 'simulations') setProvidedInputFiles(undefined);
}, [providedInputFiles, tabsValue]);
Expand Down Expand Up @@ -123,8 +127,9 @@ function WrapperApp() {
index={'inputFiles'}
persistentIfVisited>
<InputEditorPanel
goToRun={(inputFiles?: SimulationInputFiles) => {
goToRun={(simulator: SimulatorType, inputFiles?: SimulationInputFiles) => {
setProvidedInputFiles(inputFiles);
setCurrentSimulator(simulator);
setTabsValue('simulations');
}}
/>
Expand All @@ -136,6 +141,7 @@ function WrapperApp() {
<SimulationPanel
goToResults={() => setTabsValue('results')}
forwardedInputFiles={providedInputFiles}
forwardedSimulator={currentSimulator}
/>
</TabPanel>

Expand Down
85 changes: 79 additions & 6 deletions src/WrapperApp/components/InputEditor/InputEditorPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,41 +11,61 @@ import { DEMO_MODE } from '../../../config/Config';
import { InputFilesEditor } from './InputFilesEditor';
import { readFile } from '../../../services/DataLoaderService';
import { DragDropFiles } from './DragDropFiles';
import { SimulationInputFiles, _defaultInputFiles } from '../../../types/ResponseTypes';
import {
ResponsePostJob,
ResponseShConvert,
ResponseTopasConvert,
SimulationInputFiles,
_defaultFlukaInputFiles,
_defaultShInputFiles,
_defaultTopasInputFiles
} from '../../../types/ResponseTypes';
import {
RequestPostJob,
RequestShConvert,
RequestTopasConvert,
SimulatorType
} from '../../../types/RequestTypes';
interface InputEditorPanelProps {
goToRun?: (InputFiles?: SimulationInputFiles) => void;
goToRun?: (simulator: SimulatorType, InputFiles?: SimulationInputFiles) => void;
}

type GeneratorLocation = 'local' | 'remote';

type ConvertToInputFiles =
| ((...args: RequestShConvert) => Promise<ResponseShConvert>)
| ((...args: RequestTopasConvert) => Promise<ResponseTopasConvert>);

export default function InputEditorPanel({ goToRun }: InputEditorPanelProps) {
const { enqueueSnackbar } = useSnackbar();
const { editorRef } = useStore();
const { convertToInputFiles } = useShSimulation();
const { isConverterReady, convertJSON } = usePythonConverter();

const [isInProgress, setInProgress] = useState(false);
const [inputFiles, setInputFiles] = useState<SimulationInputFiles>(_defaultInputFiles);
const [inputFiles, setInputFiles] = useState<SimulationInputFiles>(_defaultShInputFiles);
const [generator, setGenerator] = useState<GeneratorLocation>('local');
const [simulator, setSimulator] = useState<SimulatorType>(SimulatorType.SHIELDHIT);
const [chosenSimulator, setChosenSimulator] = useState<SimulatorType>(SimulatorType.SHIELDHIT);

const [controller] = useState(new AbortController());

const handleConvert = useCallback(
async (editorJSON: EditorJson) => {
async (editorJSON: EditorJson): Promise<SimulationInputFiles> => {
switch (generator) {
case 'remote':
return convertToInputFiles(editorJSON, controller.signal).then(res => {
return res.inputFiles;
});
case 'local':
return convertJSON(editorJSON).then(
return convertJSON(editorJSON, simulator).then(
res => Object.fromEntries(res) as unknown as SimulationInputFiles
);
default:
throw new Error('Unknown generator: ' + generator);
}
},
[controller.signal, convertJSON, convertToInputFiles, generator]
[controller.signal, convertJSON, convertToInputFiles, generator, simulator]
);

const onClickGenerate = useCallback(() => {
Expand Down Expand Up @@ -120,6 +140,58 @@ export default function InputEditorPanel({ goToRun }: InputEditorPanelProps) {
</ToggleButton>
)}
</ToggleButtonGroup>

<ToggleButtonGroup
sx={{
marginLeft: '1rem'
}}
value={chosenSimulator}
exclusive
onChange={(_e, chosenSimulator) => {
if (chosenSimulator) {
setChosenSimulator(chosenSimulator);
if (chosenSimulator !== simulator)
if (
window.confirm(
'Current input files will be lost. Are you sure?'
)
)
setSimulator(chosenSimulator);
switch (chosenSimulator) {
case SimulatorType.SHIELDHIT:
setInputFiles(_defaultShInputFiles);
break;
case SimulatorType.TOPAS:
setInputFiles(_defaultTopasInputFiles);
break;
case SimulatorType.FLUKA:
setInputFiles(_defaultFlukaInputFiles);
break;
default:
throw new Error('Unknown simulator: ' + chosenSimulator);
}
}
}}>
<ToggleButton
value={SimulatorType.SHIELDHIT}
color='info'>
SHIELD-HIT12A
</ToggleButton>
{!DEMO_MODE && (
<ToggleButton
value={SimulatorType.TOPAS}
color='info'>
TOPAS
</ToggleButton>
)}
{!DEMO_MODE && (
<ToggleButton
value={SimulatorType.FLUKA}
color='info'>
Fluka
</ToggleButton>
)}
</ToggleButtonGroup>
</Box>

<DragDropFiles
Expand Down Expand Up @@ -148,6 +220,7 @@ export default function InputEditorPanel({ goToRun }: InputEditorPanelProps) {
/>

<InputFilesEditor
simulator={simulator}
inputFiles={inputFiles}
onChange={inputFiles => setInputFiles(inputFiles)}
runSimulation={!DEMO_MODE ? goToRun : undefined}
Expand Down
37 changes: 27 additions & 10 deletions src/WrapperApp/components/InputEditor/InputFilesEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,41 @@ import useTheme from '@mui/system/useTheme';
import CodeEditor from '@uiw/react-textarea-code-editor';
import {
SimulationInputFiles,
_defaultInputFiles,
_orderedInputFilesNames,
_defaultFlukaInputFiles,
_defaultShInputFiles,
_defaultTopasInputFiles,
_orderedShInputFilesNames,
isKnownInputFile
} from '../../../types/ResponseTypes';
import { DEMO_MODE } from '../../../config/Config';
import { saveString } from '../../../util/File';
import { SimulatorType } from '../../../types/RequestTypes';

interface InputFilesEditorProps {
simulator: SimulatorType;
inputFiles: SimulationInputFiles | undefined;
onChange?: (inputFiles: SimulationInputFiles) => void;
runSimulation?: (inputFiles: SimulationInputFiles) => void;
runSimulation?: (simulator: SimulatorType, inputFiles: SimulationInputFiles) => void;
saveAndExit?: (inputFiles: SimulationInputFiles) => void;
closeEditor?: () => void;
}

export function InputFilesEditor(props: InputFilesEditorProps) {
const inputFiles = props.inputFiles ?? _defaultInputFiles;
const inputFiles = props.inputFiles ?? _defaultShInputFiles;
const simulator = props.simulator;
const theme = useTheme();

const canBeDeleted = (name: string) => {
return !(name in _defaultInputFiles);
switch (props.simulator) {
case SimulatorType.SHIELDHIT:
return !(name in _defaultShInputFiles);
case SimulatorType.TOPAS:
return !(name in _defaultTopasInputFiles);
case SimulatorType.FLUKA:
return !(name in _defaultFlukaInputFiles);
default:
return false;
}
};

const updateInputFiles = (updateFn: (old: SimulationInputFiles) => SimulationInputFiles) => {
Expand All @@ -42,7 +56,7 @@ export function InputFilesEditor(props: InputFilesEditorProps) {
color='success'
variant='contained'
disabled={DEMO_MODE}
onClick={() => props.runSimulation?.call(null, inputFiles)}>
onClick={() => props.runSimulation?.call(null, simulator, inputFiles)}>
Run with these input files
</Button>
)}
Expand Down Expand Up @@ -74,10 +88,10 @@ export function InputFilesEditor(props: InputFilesEditorProps) {
{Object.entries(inputFiles)
.sort(([name1, _1], [name2, _2]) => {
const index1 = isKnownInputFile(name1)
? _orderedInputFilesNames.indexOf(name1)
? _orderedShInputFilesNames.indexOf(name1)
: -1 + 1;
const index2 = isKnownInputFile(name2)
? _orderedInputFilesNames.indexOf(name2)
? _orderedShInputFilesNames.indexOf(name2)
: -1 + 1;
return index1 - index2;
})
Expand Down Expand Up @@ -110,10 +124,13 @@ export function InputFilesEditor(props: InputFilesEditorProps) {
{canBeDeleted(name) && (
<Button
color='error'
disabled={name in _defaultInputFiles}
disabled={name in _defaultShInputFiles}
onClick={() => {
updateInputFiles(old => {
delete old[name];
if (name in old)
delete old[
name as keyof SimulationInputFiles
];
return { ...old };
});
}}
Expand Down
Loading

0 comments on commit 545e328

Please sign in to comment.