1- import type { IReplyErrorContent } from "@jupyterlab/services/lib/kernel/messages" ;
2- import { For , Match , Show , Switch , createMemo , createResource , onCleanup } from "solid-js" ;
1+ import { For , Match , Show , Switch , createMemo } from "solid-js" ;
32import { isMatching } from "ts-pattern" ;
43
54import type { DiagramAnalysisProps } from "../../analysis" ;
@@ -22,6 +21,7 @@ import type { ModelJudgment, MorphismDecl } from "../../model";
2221import type { DiagramAnalysisMeta } from "../../theory" ;
2322import { uniqueIndexArray } from "../../util/indexing" ;
2423import { PDEPlot2D , type PDEPlotData2D } from "../../visualization" ;
24+ import { createJuliaKernel , executeAndRetrieve } from "./jupyter" ;
2525
2626import Loader from "lucide-solid/icons/loader" ;
2727import RotateCcw from "lucide-solid/icons/rotate-ccw" ;
@@ -31,19 +31,14 @@ import "./decapodes.css";
3131import "./simulation.css" ;
3232
3333/** Configuration for a Decapodes analysis of a diagram. */
34- export type DecapodesContent = JupyterSettings & {
34+ export type DecapodesContent = {
3535 domain : string | null ;
3636 mesh : string | null ;
3737 initialConditions : Record < string , string > ;
3838 plotVariables : Record < string , boolean > ;
3939 scalars : Record < string , number > ;
4040} ;
4141
42- type JupyterSettings = {
43- baseUrl ?: string ;
44- token ?: string ;
45- } ;
46-
4742export function configureDecapodes ( options : {
4843 id ?: string ;
4944 name ?: string ;
@@ -73,88 +68,37 @@ export function configureDecapodes(options: {
7368 */
7469export function Decapodes ( props : DiagramAnalysisProps < DecapodesContent > ) {
7570 // Step 1: Start the Julia kernel.
76- const [ kernel , { refetch : restartKernel } ] = createResource ( async ( ) => {
77- const jupyter = await import ( "@jupyterlab/services" ) ;
78-
79- const serverSettings = jupyter . ServerConnection . makeSettings ( {
80- baseUrl : props . content . baseUrl ?? "http://127.0.0.1:8888" ,
81- token : props . content . token ?? "" ,
82- } ) ;
83-
84- const kernelManager = new jupyter . KernelManager ( { serverSettings } ) ;
85- const kernel = await kernelManager . startNew ( { name : "julia-1.11" } ) ;
86-
87- return kernel ;
71+ const [ kernel , restartKernel ] = createJuliaKernel ( {
72+ baseUrl : "http://127.0.0.1:8888" ,
73+ token : "" ,
8874 } ) ;
8975
90- onCleanup ( ( ) => kernel ( ) ?. shutdown ( ) ) ;
91-
9276 // Step 2: Run initialization code in the kernel.
9377 const startedKernel = ( ) => ( kernel . error ? undefined : kernel ( ) ) ;
9478
95- const [ options ] = createResource ( startedKernel , async ( kernel ) => {
96- // Request that the kernel run code to initialize the service.
97- const future = kernel . requestExecute ( { code : initCode } ) ;
98-
99- // Look for simulation options as output from the kernel.
100- let options : SimulationOptions | undefined ;
101- future . onIOPub = ( msg ) => {
102- if ( msg . header . msg_type === "execute_result" ) {
103- const content = msg . content as JsonDataContent < SimulationOptions > ;
104- options = content [ "data" ] ?. [ "application/json" ] ;
105- }
106- } ;
107-
108- const reply = await future . done ;
109- if ( reply . content . status === "error" ) {
110- await kernel . shutdown ( ) ;
111- throw new Error ( formatError ( reply . content ) ) ;
112- }
113- if ( ! options ) {
114- throw new Error ( "Allowed options not received after initialization" ) ;
115- }
116- return {
79+ const [ options ] = executeAndRetrieve (
80+ startedKernel ,
81+ makeInitCode ,
82+ ( options : SimulationOptions ) => ( {
11783 domains : uniqueIndexArray ( options . domains , ( domain ) => domain . name ) ,
118- } ;
119- } ) ;
84+ } ) ,
85+ ) ;
12086
12187 // Step 3: Run the simulation in the kernel!
12288 const initedKernel = ( ) =>
12389 kernel . error || options . error || options . loading ? undefined : kernel ( ) ;
12490
125- const [ result , { refetch : rerunSimulation } ] = createResource ( initedKernel , async ( kernel ) => {
126- // Construct the data to send to kernel.
127- const simulationData = makeSimulationData ( props . liveDiagram , props . content ) ;
128- if ( ! simulationData ) {
129- return undefined ;
130- }
131- console . log ( JSON . parse ( JSON . stringify ( simulationData ) ) ) ;
132- // Request that the kernel run a simulation with the given data.
133- const future = kernel . requestExecute ( {
134- code : makeSimulationCode ( simulationData ) ,
135- } ) ;
136-
137- // Look for simulation results as output from the kernel.
138- let result : PDEPlotData2D | undefined ;
139- future . onIOPub = ( msg ) => {
140- if (
141- msg . header . msg_type === "execute_result" &&
142- msg . parent_header . msg_id === future . msg . header . msg_id
143- ) {
144- const content = msg . content as JsonDataContent < PDEPlotData2D > ;
145- result = content [ "data" ] ?. [ "application/json" ] ;
91+ const [ result , rerunSimulation ] = executeAndRetrieve (
92+ initedKernel ,
93+ ( ) => {
94+ const simulationData = makeSimulationData ( props . liveDiagram , props . content ) ;
95+ if ( ! simulationData ) {
96+ return undefined ;
14697 }
147- } ;
148-
149- const reply = await future . done ;
150- if ( reply . content . status === "error" ) {
151- throw new Error ( formatError ( reply . content ) ) ;
152- }
153- if ( ! result ) {
154- throw new Error ( "Result not received from the simulator" ) ;
155- }
156- return result ;
157- } ) ;
98+ return makeSimulationCode ( simulationData ) ;
99+ } ,
100+ ( data : PDEPlotData2D ) => data ,
101+ ) ;
158102
159103 const obDecls = createMemo < DiagramObjectDecl [ ] > ( ( ) =>
160104 props . liveDiagram . formalJudgments ( ) . filter ( ( jgmt ) => jgmt . tag === "object" ) ,
@@ -346,17 +290,6 @@ export function Decapodes(props: DiagramAnalysisProps<DecapodesContent>) {
346290 ) ;
347291}
348292
349- const formatError = ( content : IReplyErrorContent ) : string =>
350- // Trackback list already includes `content.evalue`.
351- content . traceback . join ( "\n" ) ;
352-
353- /** JSON data returned from a Jupyter kernel. */
354- type JsonDataContent < T > = {
355- data ?: {
356- "application/json" ?: T ;
357- } ;
358- } ;
359-
360293/** Options supported by Decapodes, defined by the Julia service. */
361294type SimulationOptions = {
362295 /** Geometric domains supported by Decapodes. */
@@ -400,14 +333,15 @@ type SimulationData = {
400333} ;
401334
402335/** Julia code run after kernel is started. */
403- const initCode = `
404- import IJulia
405- IJulia.register_jsonmime(MIME"application/json"())
336+ const makeInitCode = ( ) =>
337+ `
338+ import IJulia
339+ IJulia.register_jsonmime(MIME"application/json"())
406340
407- using AlgebraicJuliaService
341+ using AlgebraicJuliaService
408342
409- JsonValue(supported_decapodes_geometries())
410- ` ;
343+ JsonValue(supported_decapodes_geometries())
344+ `;
411345
412346/** Julia code run to perform a simulation. */
413347const makeSimulationCode = ( data : SimulationData ) =>
0 commit comments