From 6cbf249b19a397a8b9c799b9ef7cecc47b28216f Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 2 Dec 2024 19:27:53 -0500 Subject: [PATCH 01/32] WIP: code to parse Navier-Stokes simulation --- packages/algjulia-service/Project.toml | 2 + packages/algjulia-service/src/decapodes.jl | 61 ++++++++++++++++------ packages/algjulia-service/test/runtests.jl | 24 +++++---- 3 files changed, 61 insertions(+), 26 deletions(-) diff --git a/packages/algjulia-service/Project.toml b/packages/algjulia-service/Project.toml index f991b4fe..95a7895d 100644 --- a/packages/algjulia-service/Project.toml +++ b/packages/algjulia-service/Project.toml @@ -17,6 +17,7 @@ JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" [compat] @@ -30,4 +31,5 @@ JSON3 = "1" LinearAlgebra = "1" MLStyle = "0.4" OrdinaryDiffEq = "6" +Plots = "1.40.9" StaticArrays = "1" diff --git a/packages/algjulia-service/src/decapodes.jl b/packages/algjulia-service/src/decapodes.jl index 3ad51e78..f666f649 100644 --- a/packages/algjulia-service/src/decapodes.jl +++ b/packages/algjulia-service/src/decapodes.jl @@ -7,8 +7,9 @@ # algebraicjulia dependencies using ACSets -using Decapodes using DiagrammaticEquations +using Decapodes +using Decapodes: dec_mat_dual_differential, dec_mat_hodge using CombinatorialSpaces # dependencies @@ -31,11 +32,12 @@ Base.showerror(io::IO, e::ImplError) = print(io, "$(e.name) not implemented") ## THEORY BUILDING -function to_pode end -export to_pode +""" Functions to build a dictionary associating ids in the theory to elements in the model""" +function to_decapode_theory end +export to_decapode_theory """ Helper function to convert CatColab values (Obs) in Decapodes """ -function to_pode(::Val{:Ob}, name::String) +function to_decapode_theory(::Val{:Ob}, name::String) @match lowercase(name) begin "0-form" => :Form0 "1-form" => :Form1 @@ -48,7 +50,7 @@ function to_pode(::Val{:Ob}, name::String) end """ Helper function to convert CatColab values (Homs) in Decapodes """ -function to_pode(::Val{:Hom}, name::String) +function to_decapode_theory(::Val{:Hom}, name::String) @match name begin "∂t" => :∂ₜ "∂ₜ" => :∂ₜ @@ -62,13 +64,24 @@ function to_pode(::Val{:Hom}, name::String) "★" => :⋆₁ "★⁻¹" => :⋆₀⁻¹ "diffusivity" => :diffusivity + # new + "d01" => :d₀ + "d12" => :d₁ + "⋆1" => :⋆₁ + "⋆2" => :⋆₂ + "♭♯" => :♭♯ + "∧ᵈᵖ₁₀(-, ⋆d(-))" => :dpsw # dual-primal self-wedge x => throw(ImplError(x)) end end # Build the theory -# @active patterns are MLStyle-implementations of F# active patterns that forces us to work in the Maybe/Option design pattern. They make @match statements cleaner. +#= +@active patterns are MLStyle-implementations of F# active patterns that forces us to work in the Maybe/Option pattern. +Practically, yet while a matter of opinion, they make @match statements cleaner; a statement amounts to a helpful pattern +name and the variables we intend to capture. +=# @active IsObject(x) begin x[:tag] == "object" ? Some(x) : nothing end @@ -95,6 +108,9 @@ export TheoryElement Base.nameof(t::TheoryElement) = t.name +struct ObData <: ElementData end +# TODO not being used right now but added for completeness. + struct HomData <: ElementData dom::Any cod::Any @@ -103,7 +119,9 @@ struct HomData <: ElementData end end export HomData +# TODO type dom/cod +"""Struct wrapping a dictionary""" struct Theory data::Dict{String, TheoryElement} function Theory() @@ -113,7 +131,7 @@ end export Theory # TODO engooden -Base.show(io::IO, theory::Theory) = println(io, theory.data) +Base.show(io::IO, theory::Theory) = show(io, theory.data) Base.values(theory::Theory) = values(theory.data) @@ -121,12 +139,13 @@ function add_to_theory! end export add_to_theory! function add_to_theory!(theory::Theory, content::Any, type::Val{:Ob}) - push!(theory.data, content[:id] => TheoryElement(;name=to_pode(type, content[:name]))) + push!(theory.data, + content[:id] => TheoryElement(;name=to_decapode_theory(type, content[:name]))) end function add_to_theory!(theory::Theory, content::Any, type::Val{:Hom}) push!(theory.data, content[:id] => - TheoryElement(;name=to_pode(type, content[:name]), + TheoryElement(;name=to_decapode_theory(type, content[:name]), val=HomData(dom=content[:dom][:content], cod=content[:cod][:content]))) end @@ -160,6 +179,9 @@ function add_to_pode!(d::SummationDecapode, nc::Vector{Int}, ::Val{:Ob}) theory_elem = theory.data[content[:over][:content]] # indexes the theory by UUID + # checks if the cell is an anonymous (intermediate) variable. + # if so, we increment the intermediate variable counter and make an intermediate variable name. + # otherwise we use the existing name of the given content. name = if isempty(content[:name]) nc[1] += 1 Symbol("•$(nc[1])") @@ -171,6 +193,10 @@ function add_to_pode!(d::SummationDecapode, return d end +function op1_name(theory::Theory, content::JSON3.Object) + Symbol(theory.data[content[:over][:content]].name) +end + # TODO we are restricted to Op1 function add_to_pode!(d::SummationDecapode, vars::Dict{String, Int}, # mapping between UUID and ACSet ID @@ -179,10 +205,11 @@ function add_to_pode!(d::SummationDecapode, scalars::Any, anons::Dict{Symbol, Any}, ::Val{:Hom}) - dom = content[:dom][:content] - cod = content[:cod][:content] + dom = content[:dom][:content]; cod = content[:cod][:content] + # TODO we need a safe way to fail this if haskey(vars, dom) && haskey(vars, cod) - op1 = Symbol(theory.data[content[:over][:content]].name) + # get the name of the Op1 and add it to the theory + op1 = op1_name(theory, content) add_part!(d, :Op1, src=vars[dom], tgt=vars[cod], op1=op1) # we need to add an inclusion to the TVar table if op1 == :∂ₜ @@ -238,7 +265,6 @@ function create_mesh(statevar::Symbol) end export create_mesh - struct System pode::SummationDecapode statevar::Symbol @@ -271,10 +297,16 @@ function System(json_string::String) # mesh and initial conditions s, sd, u0, _ = create_mesh(statevar); - # generate + # operators and generate function + ♭♯_m = ♭♯_mat(sd); + wedge_dp10 = dec_wedge_product_dp(Tuple{1,0}, sd); + dual_d1_m = dec_mat_dual_differential(1, sd); + star1_m = dec_mat_hodge(1, sd, GeometricHodge()); function sys_generate(s, my_symbol; hodge=GeometricHodge()) op = @match my_symbol begin sym && if sym ∈ keys(anons) end => anons[sym] + :♭♯ => x -> ♭♯_m * x # [1] + :dpsw => x -> wedge_dp10(x, star1_m*(dual_d1_m*x)) _ => default_dec_matrix_generate(s, my_symbol, hodge) end return (args...) -> op(args...) @@ -328,4 +360,3 @@ function Base.reshape(::Heatmap, data) l = floor(Int64, sqrt(length(data))) reshape(data, l, l) end - diff --git a/packages/algjulia-service/test/runtests.jl b/packages/algjulia-service/test/runtests.jl index 86f4cb89..5acdf4d1 100644 --- a/packages/algjulia-service/test/runtests.jl +++ b/packages/algjulia-service/test/runtests.jl @@ -23,18 +23,18 @@ model = data[:model]; @testset "Text-to-Pode" begin - @test to_pode(Val(:Ob), "0-form") == :Form0 - @test to_pode(Val(:Ob), "1-form") == :Form1 - @test to_pode(Val(:Ob), "2-form") == :Form2 - @test to_pode(Val(:Ob), "dual 0-form") == :DualForm0 - @test to_pode(Val(:Ob), "dual 1-form") == :DualForm1 - @test to_pode(Val(:Ob), "dual 2-form") == :DualForm2 + @test to_decapode_theory(Val(:Ob), "0-form") == :Form0 + @test to_decapode_theory(Val(:Ob), "1-form") == :Form1 + @test to_decapode_theory(Val(:Ob), "2-form") == :Form2 + @test to_decapode_theory(Val(:Ob), "dual 0-form") == :DualForm0 + @test to_decapode_theory(Val(:Ob), "dual 1-form") == :DualForm1 + @test to_decapode_theory(Val(:Ob), "dual 2-form") == :DualForm2 - @test_throws AlgebraicJuliaService.ImplError to_pode(Val(:Ob), "Form3") + @test_throws AlgebraicJuliaService.ImplError to_decapode_theory(Val(:Ob), "Form3") - @test to_pode(Val(:Hom), "∂t") == :∂ₜ - @test to_pode(Val(:Hom), "Δ") == :Δ - @test_throws AlgebraicJuliaService.ImplError to_pode(Val(:Hom), "∧") + @test to_decapode_theory(Val(:Hom), "∂t") == :∂ₜ + @test to_decapode_theory(Val(:Hom), "Δ") == :Δ + @test_throws AlgebraicJuliaService.ImplError to_decapode_theory(Val(:Hom), "∧") end @@ -57,7 +57,9 @@ end IsObject(content) => add_to_theory!(theory, content, Val(:Ob)) _ => nothing end - @test theory.data["019323fa-49cb-7373-8c5d-c395bae4006d"] == TheoryElement(;name=:Form0, val=nothing) + + _id = "019323fa-49cb-7373-8c5d-c395bae4006d"; + @test theory.data[_id] == TheoryElement(;name=:Form0, val=nothing) end From 179e2622dcb3a2ac975087f34b6242a506823a4a Mon Sep 17 00:00:00 2001 From: Kevin Carlson Date: Tue, 3 Dec 2024 11:36:52 -0700 Subject: [PATCH 02/32] Removing statevar from System --- packages/algjulia-service/Project.toml | 2 -- packages/algjulia-service/src/decapodes.jl | 21 +++++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/algjulia-service/Project.toml b/packages/algjulia-service/Project.toml index 95a7895d..f991b4fe 100644 --- a/packages/algjulia-service/Project.toml +++ b/packages/algjulia-service/Project.toml @@ -17,7 +17,6 @@ JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" -Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" [compat] @@ -31,5 +30,4 @@ JSON3 = "1" LinearAlgebra = "1" MLStyle = "0.4" OrdinaryDiffEq = "6" -Plots = "1.40.9" StaticArrays = "1" diff --git a/packages/algjulia-service/src/decapodes.jl b/packages/algjulia-service/src/decapodes.jl index f666f649..7a5d03a2 100644 --- a/packages/algjulia-service/src/decapodes.jl +++ b/packages/algjulia-service/src/decapodes.jl @@ -42,6 +42,9 @@ function to_decapode_theory(::Val{:Ob}, name::String) "0-form" => :Form0 "1-form" => :Form1 "2-form" => :Form2 + "primal 0-form" => :Form0 + "primal 1-form" => :Form1 + "primal 2-form" => :Form2 "dual 0-form" => :DualForm0 "dual 1-form" => :DualForm1 "dual 2-form" => :DualForm2 @@ -51,12 +54,13 @@ end """ Helper function to convert CatColab values (Homs) in Decapodes """ function to_decapode_theory(::Val{:Hom}, name::String) - @match name begin + @match replace(name," " => "") begin "∂t" => :∂ₜ "∂ₜ" => :∂ₜ "Δ" => :Δ "d" => :d₀ "d*" => :dual_d₁ + "d̃₁" => :dual_d₁ # \star on LHS "⋆" => :⋆₁ "⋆⁻¹" => :⋆₀⁻¹ @@ -70,7 +74,8 @@ function to_decapode_theory(::Val{:Hom}, name::String) "⋆1" => :⋆₁ "⋆2" => :⋆₂ "♭♯" => :♭♯ - "∧ᵈᵖ₁₀(-, ⋆d(-))" => :dpsw # dual-primal self-wedge + "∧ᵈᵖ₁₀(-,⋆d(-))" => :dpsw # dual-primal self-wedge + "-" => :neg x => throw(ImplError(x)) end end @@ -267,7 +272,7 @@ export create_mesh struct System pode::SummationDecapode - statevar::Symbol + plotvar::Symbol scalars::Dict{Symbol, Any} # closures # TODO rename scalars => anons mesh::HasDeltaSet dualmesh::HasDeltaSet @@ -289,13 +294,13 @@ function System(json_string::String) # any scalars? scalars = haskey(json_object, :scalars) ? json_object[:scalars] : []; - # pode, anons, and statevar + # pode, anons, and plotvar decapode, anons = Decapode(diagram, theory; scalars=scalars); - statevars = infer_state_names(decapode); - statevar = length(statevars) == 1 ? first(statevars) : error("$statevars must be length one") + plotvar = json_object[:plotvar] + # mesh and initial conditions - s, sd, u0, _ = create_mesh(statevar); + s, sd, u0, _ = create_mesh(plotvar); # operators and generate function ♭♯_m = ♭♯_mat(sd); @@ -312,7 +317,7 @@ function System(json_string::String) return (args...) -> op(args...) end - return System(decapode, statevar, anons, s, sd, u0, sys_generate) + return System(decapode, plotvar, anons, s, sd, u0, sys_generate) end From 68b8e78be8be1e3b715e1808e4a2bc3e766dc8d0 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Tue, 3 Dec 2024 12:16:06 -0700 Subject: [PATCH 03/32] ENH: Check boxes to set which Decapodes variables are plotted. --- .../src/components/fixed_table_editor.tsx | 59 ++++++++++++++++--- .../src/stdlib/analyses/decapodes.tsx | 39 +++++++++++- .../src/stdlib/analyses/lotka_volterra.tsx | 2 + 3 files changed, 90 insertions(+), 10 deletions(-) diff --git a/packages/frontend/src/components/fixed_table_editor.tsx b/packages/frontend/src/components/fixed_table_editor.tsx index 54eff3a6..3f58ce68 100644 --- a/packages/frontend/src/components/fixed_table_editor.tsx +++ b/packages/frontend/src/components/fixed_table_editor.tsx @@ -3,8 +3,12 @@ import { For, Match, Show, Switch, createEffect, createSignal } from "solid-js"; import "./fixed_table_editor.css"; -/** Schema for a column in a table editor. */ -export type ColumnSchema = { +type ContentType = "string" | "boolean"; + +type BaseColumnSchema = { + /** Type of content displayed in the column. */ + contentType: ContentType; + /** Name of column. */ name?: string; @@ -12,22 +16,35 @@ export type ColumnSchema = { header?: boolean; /** Content of the column at the given row. */ - content: (row: Row) => string; + content: (row: Row) => Content; /** Is the text valid as content for the column at the given row? If not specified, any content is considered valid. */ - validate?: (row: Row, text: string) => boolean; + validate?: (row: Row, content: Content) => boolean; /** Sets the content for the columns at the given row, if possible. Returns whether setting the content was successful. If not specified, the column is not editable. */ - setContent?: (row: Row, text: string) => boolean; + setContent?: (row: Row, content: Content) => boolean; }; +/** Schema for a text column in a table editor. */ +export type TextColumnSchema = BaseColumnSchema & { + contentType: "string"; +}; + +/** Schema for a boolean column in a table editor. */ +export type BooleanColumnSchema = BaseColumnSchema & { + contentType: "boolean"; +}; + +/** Schema for a column in a table editor. */ +export type ColumnSchema = TextColumnSchema | BooleanColumnSchema; + /** Create schema for a column with numerical (floating point) data. */ export const createNumericalColumn = (args: { name?: string; @@ -36,7 +53,8 @@ export const createNumericalColumn = (args: { default?: number; validate?: (row: Row, data: number) => boolean; setData?: (row: Row, data: number) => void; -}): ColumnSchema => ({ +}): TextColumnSchema => ({ + contentType: "string", name: args.name, header: args.header, content: (row) => { @@ -115,14 +133,21 @@ function Cell(props: { const { row, schema } = destructure(props); return ( - + + + {(schema) => } + + + {(schema) => } + + ); } -function CellEditor(props: { +function TextCellEditor(props: { row: Row; - schema: ColumnSchema; + schema: TextColumnSchema; }) { const { row, schema } = destructure(props); @@ -152,3 +177,19 @@ function CellEditor(props: { /> ); } + +function BooleanCellEditor(props: { + row: Row; + schema: BooleanColumnSchema; +}) { + const { row, schema } = destructure(props); + + return ( + schema().setContent?.(row(), evt.currentTarget.checked)} + /> + ); +} diff --git a/packages/frontend/src/stdlib/analyses/decapodes.tsx b/packages/frontend/src/stdlib/analyses/decapodes.tsx index b2ce598d..6f6bfc94 100644 --- a/packages/frontend/src/stdlib/analyses/decapodes.tsx +++ b/packages/frontend/src/stdlib/analyses/decapodes.tsx @@ -11,7 +11,12 @@ import { Warning, createNumericalColumn, } from "../../components"; -import { type DiagramJudgment, type LiveDiagramDocument, fromCatlogDiagram } from "../../diagram"; +import { + type DiagramJudgment, + type DiagramObjectDecl, + type LiveDiagramDocument, + fromCatlogDiagram, +} from "../../diagram"; import type { ModelJudgment, MorphismDecl } from "../../model"; import type { DiagramAnalysisMeta } from "../../theory"; import { PDEPlot2D, type PDEPlotData2D } from "../../visualization"; @@ -25,6 +30,7 @@ import "./simulation.css"; /** Configuration for a Decapodes analysis of a diagram. */ export type DecapodesContent = JupyterSettings & { scalars: Record; + plotVariables: Record; }; type JupyterSettings = { @@ -49,6 +55,7 @@ export function configureDecapodes(options: { component: (props) => , initialContent: () => ({ scalars: {}, + plotVariables: {}, }), }; } @@ -116,6 +123,10 @@ export function Decapodes(props: DiagramAnalysisProps) { return result; }); + const obDecls = createMemo(() => + props.liveDiagram.formalJudgments().filter((jgmt) => jgmt.tag === "object"), + ); + const scalarDecls = createMemo(() => { const liveModel = props.liveDiagram.liveModel; return liveModel.formalJudgments().filter((jgmt) => @@ -134,6 +145,7 @@ export function Decapodes(props: DiagramAnalysisProps) { const scalarSchema: ColumnSchema[] = [ { + contentType: "string", header: true, name: "Scalar constant", content: (mor) => mor.name, @@ -148,6 +160,26 @@ export function Decapodes(props: DiagramAnalysisProps) { }), ]; + const plotVariableSchema: ColumnSchema[] = [ + { + contentType: "string", + header: true, + name: "Variable", + content: (ob) => ob.name, + }, + { + contentType: "boolean", + name: "Plot", + content: (ob) => props.content.plotVariables[ob.id] ?? false, + setContent: (ob, value) => { + props.changeContent((content) => { + content.plotVariables[ob.id] = value; + }); + return true; + }, + }, + ]; + const header = () => (
Simulation @@ -180,6 +212,7 @@ export function Decapodes(props: DiagramAnalysisProps) {
+
@@ -223,6 +256,9 @@ type SimulationData = { /** Mapping from IDs of scalar operations to numerical values. */ scalars: Record; + + /** Variables to plot. */ + plotVariables: Array; }; /** Julia code run after kernel is started. */ @@ -260,5 +296,6 @@ const makeSimulationData = ( ), model: liveDiagram.liveModel.formalJudgments(), scalars: content.scalars, + plotVariables: Object.keys(content.plotVariables).filter((v) => content.plotVariables[v]), }; }; diff --git a/packages/frontend/src/stdlib/analyses/lotka_volterra.tsx b/packages/frontend/src/stdlib/analyses/lotka_volterra.tsx index d8ae180a..782e9f49 100644 --- a/packages/frontend/src/stdlib/analyses/lotka_volterra.tsx +++ b/packages/frontend/src/stdlib/analyses/lotka_volterra.tsx @@ -70,6 +70,7 @@ export function LotkaVolterra( const obSchema: ColumnSchema[] = [ { + contentType: "string", header: true, content: (ob) => ob.name, }, @@ -94,6 +95,7 @@ export function LotkaVolterra( const morSchema: ColumnSchema[] = [ { + contentType: "string", header: true, content: (mor) => mor.name, }, From c81bebd45041356c0b3f0ad19b23459733a14774 Mon Sep 17 00:00:00 2001 From: Kevin Carlson Date: Tue, 3 Dec 2024 12:20:25 -0700 Subject: [PATCH 04/32] PodeSystems function implemented and tests passing --- packages/algjulia-service/Project.toml | 2 + packages/algjulia-service/src/decapodes.jl | 55 ++++++++++--------- packages/algjulia-service/test/Project.toml | 1 - .../algjulia-service/test/diffusion_data.json | 2 +- .../test/diffusion_long_trip.json | 2 +- .../test/diffusivity_constant.json | 2 +- packages/algjulia-service/test/example.json | 2 +- packages/algjulia-service/test/runtests.jl | 15 ++--- 8 files changed, 44 insertions(+), 37 deletions(-) diff --git a/packages/algjulia-service/Project.toml b/packages/algjulia-service/Project.toml index f991b4fe..73f49961 100644 --- a/packages/algjulia-service/Project.toml +++ b/packages/algjulia-service/Project.toml @@ -8,6 +8,7 @@ version = "0.1.0" ACSets = "227ef7b5-1206-438b-ac65-934d6da304b8" CombinatorialSpaces = "b1c52339-7909-45ad-8b6a-6e388f7c67f2" ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" +Debugger = "31a5f54b-26ea-5ae9-a837-f05ce5417438" Decapodes = "679ab3ea-c928-4fe6-8d59-fd451142d391" DiagrammaticEquations = "6f00c28b-6bed-4403-80fa-30e0dc12f317" Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" @@ -22,6 +23,7 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" [compat] ACSets = "0.2.21" ComponentArrays = "0.15" +Debugger = "0.7.10" Decapodes = "0.5.6" DiagrammaticEquations = "0.1.7" Distributions = "0.25" diff --git a/packages/algjulia-service/src/decapodes.jl b/packages/algjulia-service/src/decapodes.jl index 7a5d03a2..f821f86b 100644 --- a/packages/algjulia-service/src/decapodes.jl +++ b/packages/algjulia-service/src/decapodes.jl @@ -257,20 +257,20 @@ export Decapode # the proper name for this constructor should be "SummationDecapode" # TODO we need to make this dynamic -function create_mesh(statevar::Symbol) +function create_mesh(plotvar::Symbol) s = triangulated_grid(100,100,2,2,Point2{Float64}) sd = EmbeddedDeltaDualComplex2D{Bool, Float64, Point2{Float64}}(s) subdivide_duals!(sd, Circumcenter()) c_dist = MvNormal([50, 50], LinearAlgebra.Diagonal(map(abs2, [7.5, 7.5]))) c = [pdf(c_dist, [p[1], p[2]]) for p in sd[:point]] - u0 = ComponentArray(; Dict(statevar=>c)...) + u0 = ComponentArray(; Dict(plotvar=>c)...) return (s, sd, u0, ()) end export create_mesh -struct System +struct PodeSystem pode::SummationDecapode plotvar::Symbol scalars::Dict{Symbol, Any} # closures # TODO rename scalars => anons @@ -279,9 +279,12 @@ struct System init::Any # TODO specify. Is it always ComponentVector? generate::Any end -export System +export PodeSystem -function System(json_string::String) +""" +Construct a vector of `PodeSystem` objects from a JSON string. +""" +function PodeSystems(json_string::String) json_object = JSON3.read(json_string); # converts the JSON of (the fragment of) the theory @@ -294,32 +297,34 @@ function System(json_string::String) # any scalars? scalars = haskey(json_object, :scalars) ? json_object[:scalars] : []; - # pode, anons, and plotvar + # pode, anons, and plotvars decapode, anons = Decapode(diagram, theory; scalars=scalars); - plotvar = json_object[:plotvar] + plotvars = Symbol.(json_object[:plotVariables]) # mesh and initial conditions - s, sd, u0, _ = create_mesh(plotvar); + meshes = create_mesh.(plotvars) + ss, sds, u0s = [[m[1] for m in meshes],[m[2] for m in meshes],[m[3] for m in meshes]]; # operators and generate function - ♭♯_m = ♭♯_mat(sd); - wedge_dp10 = dec_wedge_product_dp(Tuple{1,0}, sd); - dual_d1_m = dec_mat_dual_differential(1, sd); - star1_m = dec_mat_hodge(1, sd, GeometricHodge()); - function sys_generate(s, my_symbol; hodge=GeometricHodge()) - op = @match my_symbol begin - sym && if sym ∈ keys(anons) end => anons[sym] - :♭♯ => x -> ♭♯_m * x # [1] - :dpsw => x -> wedge_dp10(x, star1_m*(dual_d1_m*x)) - _ => default_dec_matrix_generate(s, my_symbol, hodge) - end - return (args...) -> op(args...) + map(zip(ss,sds,plotvars,u0s)) do (s,sd,plotvar,u0) + ♭♯_m = ♭♯_mat(sd); + wedge_dp10 = dec_wedge_product_dp(Tuple{1,0}, sd); + dual_d1_m = dec_mat_dual_differential(1, sd); + star1_m = dec_mat_hodge(1, sd, GeometricHodge()); + function sys_generate(s, my_symbol; hodge=GeometricHodge()) + op = @match my_symbol begin + sym && if sym ∈ keys(anons) end => anons[sym] + :♭♯ => x -> ♭♯_m * x # [1] + :dpsw => x -> wedge_dp10(x, star1_m*(dual_d1_m*x)) + _ => default_dec_matrix_generate(s, my_symbol, hodge) + end + return (args...) -> op(args...) + end + return PodeSystem(decapode, plotvar, anons, s, sd, u0, sys_generate) end - - return System(decapode, plotvar, anons, s, sd, u0, sys_generate) end - +export PodeSystems function run_sim(fm, u0, t0, constparam) prob = ODEProblem(fm, u0, (0, t0), constparam) @@ -335,14 +340,14 @@ struct SimResult end export SimResult -function SimResult(sol::ODESolution, system::System) +function SimResult(sol::ODESolution, system::PodeSystem) points = collect(values(system.mesh.subparts.point.m)); xlen = 51; ylen = 51; function at_time(sol::ODESolution, timeidx::Int) - [SVector(i, j, getproperty(sol.u[timeidx], system.statevar)[xlen*(i-1) + j]) for i in 1:xlen, j in 1:ylen] + [SVector(i, j, getproperty(sol.u[timeidx], system.plotvar)[xlen*(i-1) + j]) for i in 1:xlen, j in 1:ylen] end state_vals = map(1:length(sol.t)) do i diff --git a/packages/algjulia-service/test/Project.toml b/packages/algjulia-service/test/Project.toml index c12e2820..a2ed5fe7 100644 --- a/packages/algjulia-service/test/Project.toml +++ b/packages/algjulia-service/test/Project.toml @@ -9,6 +9,5 @@ JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" -Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/packages/algjulia-service/test/diffusion_data.json b/packages/algjulia-service/test/diffusion_data.json index 452b2435..fef44cce 100644 --- a/packages/algjulia-service/test/diffusion_data.json +++ b/packages/algjulia-service/test/diffusion_data.json @@ -1,4 +1,4 @@ -{ +{ "plotVariables": ["C"], "diagram": [ { "id": "01932402-bcf5-7432-8d14-dbae9eabf907", diff --git a/packages/algjulia-service/test/diffusion_long_trip.json b/packages/algjulia-service/test/diffusion_long_trip.json index 08050f8c..af9be5df 100644 --- a/packages/algjulia-service/test/diffusion_long_trip.json +++ b/packages/algjulia-service/test/diffusion_long_trip.json @@ -1,4 +1,4 @@ -{ +{ "plotVariables": ["C"], "diagram": [ { "tag": "object", diff --git a/packages/algjulia-service/test/diffusivity_constant.json b/packages/algjulia-service/test/diffusivity_constant.json index fe132633..4bb67cc9 100644 --- a/packages/algjulia-service/test/diffusivity_constant.json +++ b/packages/algjulia-service/test/diffusivity_constant.json @@ -1,4 +1,4 @@ -{ +{ "plotVariables": ["u"], "diagram": [ { "tag": "object", diff --git a/packages/algjulia-service/test/example.json b/packages/algjulia-service/test/example.json index 3f30feca..d74244d4 100644 --- a/packages/algjulia-service/test/example.json +++ b/packages/algjulia-service/test/example.json @@ -1,4 +1,4 @@ -{ +{ "plotVariables": ["C"], "diagram": [ { "tag": "object", diff --git a/packages/algjulia-service/test/runtests.jl b/packages/algjulia-service/test/runtests.jl index 5acdf4d1..fd269017 100644 --- a/packages/algjulia-service/test/runtests.jl +++ b/packages/algjulia-service/test/runtests.jl @@ -14,7 +14,7 @@ using LinearAlgebra import OrdinaryDiffEq: ReturnCode # visualization -using Plots +#using Plots # load data data = open(JSON3.read, joinpath(@__DIR__, "diffusion_data.json"), "r") @@ -40,7 +40,7 @@ end @testset "Parsing the Theory JSON Object" begin - @test Set(keys(data)) == Set([:diagram, :model]) + @test Set(keys(data)) == Set([:diagram, :model,:plotVariables]) @test @match model[1] begin IsObject(_) => true @@ -82,10 +82,11 @@ end end + @testset "Simulation" begin json_string = read(joinpath(@__DIR__, "diffusion_data.json"), String); - system = System(json_string); + system = only(PodeSystems(json_string)); simulator = evalsim(system.pode) f = simulator(system.dualmesh, default_dec_generate, DiagonalHodge()); @@ -117,7 +118,7 @@ model = data[:model]; @testset "Parsing the Theory JSON Object" begin - @test Set(keys(data)) == Set([:diagram, :model, :scalars]) + @test Set(keys(data)) == Set([:diagram, :model, :scalars,:plotVariables]) @test @match model[1] begin IsObject(_) => true @@ -142,7 +143,7 @@ end @testset "Simulation ..." begin json_string = read(joinpath(@__DIR__, "diffusion_long_trip.json"), String); - system = System(json_string); + system = only(PodeSystems(json_string)); simulator = evalsim(system.pode) # open("test_sim.jl", "w") do f @@ -178,7 +179,7 @@ scalars = data[:scalars]; @testset "Parsing the Theory JSON Object" begin - @test Set(keys(data)) == Set([:diagram, :model, :scalars]) + @test Set(keys(data)) == Set([:diagram, :model, :scalars,:plotVariables]) @test @match model[1] begin IsObject(_) => true @@ -203,7 +204,7 @@ end @testset "Simulation ..." begin json_string = read(joinpath(@__DIR__, "diffusivity_constant.json"), String); - system = System(json_string); + system = only(PodeSystems(json_string)); simulator = evalsim(system.pode) # open("test_sim.jl", "w") do f From 5e273341694d7cd95b9e52b34a2d4cfc0f48327a Mon Sep 17 00:00:00 2001 From: Kevin Carlson Date: Tue, 3 Dec 2024 12:28:24 -0700 Subject: [PATCH 05/32] Tried a two-variable test, concatenation error in the variables remove comment fix dumb --- .../test/diffusion_data_twovars.json | 147 ++++++++++++++++++ packages/algjulia-service/test/runtests.jl | 27 ++++ 2 files changed, 174 insertions(+) create mode 100644 packages/algjulia-service/test/diffusion_data_twovars.json diff --git a/packages/algjulia-service/test/diffusion_data_twovars.json b/packages/algjulia-service/test/diffusion_data_twovars.json new file mode 100644 index 00000000..5ccf7219 --- /dev/null +++ b/packages/algjulia-service/test/diffusion_data_twovars.json @@ -0,0 +1,147 @@ +{ "plotVariables": ["C","dC/dt"], + "diagram": [ + { + "id": "01932402-bcf5-7432-8d14-dbae9eabf907", + "name": "C", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019323fa-49cb-7373-8c5d-c395bae4006d", + "tag": "Basic" + }, + "tag": "object" + }, + { + "id": "01932403-5b6c-7231-90d7-d7cece275eb2", + "name": "dC/dt", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019323fa-49cb-7373-8c5d-c395bae4006d", + "tag": "Basic" + }, + "tag": "object" + }, + { + "cod": { + "content": "01932403-5b6c-7231-90d7-d7cece275eb2", + "tag": "Basic" + }, + "dom": { + "content": "01932402-bcf5-7432-8d14-dbae9eabf907", + "tag": "Basic" + }, + "id": "01932403-c4cd-7563-8ebd-080dd37a9c7e", + "morType": { + "content": { + "content": "Object", + "tag": "Basic" + }, + "tag": "Hom" + }, + "name": "", + "over": { + "content": "019323fb-3652-7e91-aee9-06187a954fc6", + "tag": "Basic" + }, + "tag": "morphism" + }, + { + "cod": { + "content": "01932403-5b6c-7231-90d7-d7cece275eb2", + "tag": "Basic" + }, + "dom": { + "content": "01932402-bcf5-7432-8d14-dbae9eabf907", + "tag": "Basic" + }, + "id": "01932404-10e5-7128-bb94-835e5d8d643f", + "morType": { + "content": { + "content": "Object", + "tag": "Basic" + }, + "tag": "Hom" + }, + "name": "", + "over": { + "content": "019323ff-1af6-79da-b776-8ee11c88a8a0", + "tag": "Basic" + }, + "tag": "morphism" + } + ], + "model": [ + { + "id": "019323fa-49cb-7373-8c5d-c395bae4006d", + "name": "0-form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + { + "id": "019323fa-783b-72c8-af20-c0718fde3ac8", + "name": "1-form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + { + "id": "019323fb-175b-784e-aab8-7b78fa576571", + "name": "2-form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + { + "cod": { + "content": "019323fa-49cb-7373-8c5d-c395bae4006d", + "tag": "Basic" + }, + "dom": { + "content": "019323fa-49cb-7373-8c5d-c395bae4006d", + "tag": "Basic" + }, + "id": "019323fb-3652-7e91-aee9-06187a954fc6", + "morType": { + "content": { + "content": "Object", + "tag": "Basic" + }, + "tag": "Hom" + }, + "name": "∂t", + "tag": "morphism" + }, + { + "cod": { + "content": "019323fa-49cb-7373-8c5d-c395bae4006d", + "tag": "Basic" + }, + "dom": { + "content": "019323fa-49cb-7373-8c5d-c395bae4006d", + "tag": "Basic" + }, + "id": "019323ff-1af6-79da-b776-8ee11c88a8a0", + "morType": { + "content": { + "content": "Object", + "tag": "Basic" + }, + "tag": "Hom" + }, + "name": "Δ", + "tag": "morphism" + } + ] +} diff --git a/packages/algjulia-service/test/runtests.jl b/packages/algjulia-service/test/runtests.jl index fd269017..7f80fd57 100644 --- a/packages/algjulia-service/test/runtests.jl +++ b/packages/algjulia-service/test/runtests.jl @@ -110,6 +110,33 @@ end end +@testset "SimulationTwoVar" begin + + json_string = read(joinpath(@__DIR__, "diffusion_data_twovars.json"), String); + systems = PodeSystems(json_string); + + simulators = map(systems) do system evalsim(system.pode) end + fs = map(zip(simulators,systems)) do (simulator,system) simulator(system.dualmesh, default_dec_generate, DiagonalHodge()) end; + + # time + solns = map(zip(fs,systems)) do (f,system) run_sim(f, system.init, 50.0, ComponentArray(k=0.5,)) end; + # returns ::ODESolution + # - retcode + # - interpolation + # - t + # - u::Vector{ComponentVector} + + for soln in solns @test soln.retcode == ReturnCode.Success end + + results = map(zip(solns,systems)) do (soln,system) SimResult(soln, system) end; + + for result in results @test typeof(result.state) == Vector{Matrix{SVector{3, Float64}}} end + + jvs = JsonValue.(results); + +end + + ##### data = open(JSON3.read, joinpath(@__DIR__, "diffusion_long_trip.json"), "r") From 5767f289e54f2e2db8064ef7e8b009a21070ebbb Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Tue, 3 Dec 2024 14:18:40 -0700 Subject: [PATCH 06/32] TST: Show full traceback when AlgJulia service errors. --- .../frontend/src/stdlib/analyses/decapodes.tsx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/stdlib/analyses/decapodes.tsx b/packages/frontend/src/stdlib/analyses/decapodes.tsx index 6f6bfc94..e4723553 100644 --- a/packages/frontend/src/stdlib/analyses/decapodes.tsx +++ b/packages/frontend/src/stdlib/analyses/decapodes.tsx @@ -1,3 +1,4 @@ +import type { IReplyErrorContent } from "@jupyterlab/services/lib/kernel/messages"; import { Match, Switch, createMemo, createResource, onCleanup } from "solid-js"; import { isMatching } from "ts-pattern"; @@ -79,7 +80,7 @@ export function Decapodes(props: DiagramAnalysisProps) { if (reply.content.status === "error") { await kernel.shutdown(); - throw new Error(reply.content.evalue); + throw new Error(formatError(reply.content)); } return kernel; @@ -115,7 +116,7 @@ export function Decapodes(props: DiagramAnalysisProps) { const reply = await future.done; if (reply.content.status === "error") { - throw new Error(reply.content.evalue); + throw new Error(formatError(reply.content)); } if (!result) { throw new Error("Result not received from the simulator"); @@ -220,13 +221,17 @@ export function Decapodes(props: DiagramAnalysisProps) { {(error) => ( - {error().message} +
{error().message}
)}
{"Running the simulation..."} - {(error) => {error().message}} + {(error) => ( + +
{error().message}
+
+ )}
@@ -239,6 +244,10 @@ export function Decapodes(props: DiagramAnalysisProps) { ); } +const formatError = (content: IReplyErrorContent): string => + // Trackback list already includes `content.evalue`. + content.traceback.join("\n"); + /** JSON data returned from a Jupyter kernel. */ type JsonDataContent = { data?: { From aef1585398d0474ec4fd35d9fc573aa6c62e74ea Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 3 Dec 2024 17:39:05 -0500 Subject: [PATCH 07/32] wip test cases need to be updated with plotvars --- packages/algjulia-service/Project.toml | 2 - packages/algjulia-service/src/decapodes.jl | 84 ++++++++++++---------- packages/algjulia-service/test/runtests.jl | 31 ++++---- 3 files changed, 65 insertions(+), 52 deletions(-) diff --git a/packages/algjulia-service/Project.toml b/packages/algjulia-service/Project.toml index 73f49961..f991b4fe 100644 --- a/packages/algjulia-service/Project.toml +++ b/packages/algjulia-service/Project.toml @@ -8,7 +8,6 @@ version = "0.1.0" ACSets = "227ef7b5-1206-438b-ac65-934d6da304b8" CombinatorialSpaces = "b1c52339-7909-45ad-8b6a-6e388f7c67f2" ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" -Debugger = "31a5f54b-26ea-5ae9-a837-f05ce5417438" Decapodes = "679ab3ea-c928-4fe6-8d59-fd451142d391" DiagrammaticEquations = "6f00c28b-6bed-4403-80fa-30e0dc12f317" Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" @@ -23,7 +22,6 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" [compat] ACSets = "0.2.21" ComponentArrays = "0.15" -Debugger = "0.7.10" Decapodes = "0.5.6" DiagrammaticEquations = "0.1.7" Distributions = "0.25" diff --git a/packages/algjulia-service/src/decapodes.jl b/packages/algjulia-service/src/decapodes.jl index f821f86b..7f129315 100644 --- a/packages/algjulia-service/src/decapodes.jl +++ b/packages/algjulia-service/src/decapodes.jl @@ -238,10 +238,9 @@ This returns a Decapode given a jsondiagram and a theory. function Decapode(diagram::AbstractVector{JSON3.Object}, theory::Theory; scalars=[]) # initiatize decapode and its mapping between UUIDs and ACSet IDs pode = SummationDecapode(parse_decapode(quote end)); - vars = Dict{String, Int}(); + vars = Dict{String, Int}(); # UUID => ACSetID nc = [0]; # array is a mutable container anons = Dict{Symbol, Any}(); - # for each cell in the notebook, add it to the diagram foreach(diagram) do cell @match cell begin @@ -251,40 +250,44 @@ function Decapode(diagram::AbstractVector{JSON3.Object}, theory::Theory; scalars _ => throw(ImplError(cell[:content][:tag])) end end - pode, anons + return pode, anons, vars end export Decapode # the proper name for this constructor should be "SummationDecapode" # TODO we need to make this dynamic -function create_mesh(plotvar::Symbol) +function create_mesh() s = triangulated_grid(100,100,2,2,Point2{Float64}) sd = EmbeddedDeltaDualComplex2D{Bool, Float64, Point2{Float64}}(s) subdivide_duals!(sd, Circumcenter()) - c_dist = MvNormal([50, 50], LinearAlgebra.Diagonal(map(abs2, [7.5, 7.5]))) - c = [pdf(c_dist, [p[1], p[2]]) for p in sd[:point]] - u0 = ComponentArray(; Dict(plotvar=>c)...) - - return (s, sd, u0, ()) + return (s, sd) end export create_mesh +function init_conditions(plotvars::Vector{Symbol}, sd::HasDeltaSet) + c_dist = MvNormal([50, 50], LinearAlgebra.Diagonal(map(abs2, [7.5, 7.5]))) + c = [pdf(c_dist, [p[1], p[2]]) for p in sd[:point]] + u0 = ComponentArray(; Dict([plotvar=>c for plotvar in plotvars])...) + return u0 +end + struct PodeSystem pode::SummationDecapode - plotvar::Symbol + plotvar::Vector{Symbol} scalars::Dict{Symbol, Any} # closures # TODO rename scalars => anons mesh::HasDeltaSet dualmesh::HasDeltaSet init::Any # TODO specify. Is it always ComponentVector? generate::Any + uuiddict::Dict{Symbol, String} end export PodeSystem """ Construct a vector of `PodeSystem` objects from a JSON string. """ -function PodeSystems(json_string::String) +function PodeSystem(json_string::String) json_object = JSON3.read(json_string); # converts the JSON of (the fragment of) the theory @@ -297,34 +300,37 @@ function PodeSystems(json_string::String) # any scalars? scalars = haskey(json_object, :scalars) ? json_object[:scalars] : []; - # pode, anons, and plotvars - decapode, anons = Decapode(diagram, theory; scalars=scalars); + # pode, anons, and vars (UUID => ACSetId) + decapode, anons, vars = Decapode(diagram, theory; scalars=scalars); + # plotting variables plotvars = Symbol.(json_object[:plotVariables]) - # mesh and initial conditions - meshes = create_mesh.(plotvars) - ss, sds, u0s = [[m[1] for m in meshes],[m[2] for m in meshes],[m[3] for m in meshes]]; - - # operators and generate function - map(zip(ss,sds,plotvars,u0s)) do (s,sd,plotvar,u0) - ♭♯_m = ♭♯_mat(sd); - wedge_dp10 = dec_wedge_product_dp(Tuple{1,0}, sd); - dual_d1_m = dec_mat_dual_differential(1, sd); - star1_m = dec_mat_hodge(1, sd, GeometricHodge()); - function sys_generate(s, my_symbol; hodge=GeometricHodge()) - op = @match my_symbol begin - sym && if sym ∈ keys(anons) end => anons[sym] + s, sd = create_mesh() + u0 = init_conditions(plotvars, sd) + + ♭♯_m = ♭♯_mat(sd); + wedge_dp10 = dec_wedge_product_dp(Tuple{1,0}, sd); + dual_d1_m = dec_mat_dual_differential(1, sd); + star1_m = dec_mat_hodge(1, sd, GeometricHodge()); + function sys_generate(s, my_symbol; hodge=GeometricHodge()) + op = @match my_symbol begin + sym && if sym ∈ keys(anons) end => anons[sym] :♭♯ => x -> ♭♯_m * x # [1] :dpsw => x -> wedge_dp10(x, star1_m*(dual_d1_m*x)) _ => default_dec_matrix_generate(s, my_symbol, hodge) end - return (args...) -> op(args...) - end - return PodeSystem(decapode, plotvar, anons, s, sd, u0, sys_generate) + return (args...) -> op(args...) end + + # symbol => uuid. we need this to reassociate the var + symb2uuid = Dict( + [(subpart(decapode, vars[key], :name) => key) for key ∈ keys(vars)] + ); + + return PodeSystem(decapode, plotvars, anons, s, sd, u0, sys_generate, symb2uuid) end -export PodeSystems +export PodeSystem function run_sim(fm, u0, t0, constparam) prob = ODEProblem(fm, u0, (0, t0), constparam) @@ -334,7 +340,7 @@ export run_sim struct SimResult time::Vector{Float64} - state::Vector{Matrix{SVector{3, Float64}}} + state::Dict{String, Vector{Matrix{SVector{3, Float64}}}} x::Vector{Float64} # axis y::Vector{Float64} end @@ -346,16 +352,20 @@ function SimResult(sol::ODESolution, system::PodeSystem) xlen = 51; ylen = 51; - function at_time(sol::ODESolution, timeidx::Int) - [SVector(i, j, getproperty(sol.u[timeidx], system.plotvar)[xlen*(i-1) + j]) for i in 1:xlen, j in 1:ylen] + function at_time(sol::ODESolution, plotvar::Symbol, timeidx::Int) + [SVector(i, j, getproperty(sol.u[timeidx], plotvar)[xlen*(i-1) + j]) for i in 1:xlen, j in 1:ylen] end - state_vals = map(1:length(sol.t)) do i - at_time(sol, i) + function state_vals(plotvar::Symbol) + map(1:length(sol.t)) do i + at_time(sol, plotvar, i) + end end + state_val_dict = Dict([(system.uuiddict[plotvar] => state_vals(plotvar)) for plotvar in system.plotvar]) - # TODO engooden - SimResult(sol.t, state_vals, 0:xlen-1, 0:ylen-1) + # TODO engooden, return names to UUIDs + # Dict("UUID1" => VectorMatrixSVectr...) + SimResult(sol.t, state_val_dict, 0:xlen-1, 0:ylen-1) end # TODO generalize to HasDeltaSet diff --git a/packages/algjulia-service/test/runtests.jl b/packages/algjulia-service/test/runtests.jl index 7f80fd57..74fb77aa 100644 --- a/packages/algjulia-service/test/runtests.jl +++ b/packages/algjulia-service/test/runtests.jl @@ -17,7 +17,7 @@ import OrdinaryDiffEq: ReturnCode #using Plots # load data -data = open(JSON3.read, joinpath(@__DIR__, "diffusion_data.json"), "r") +data = open(JSON3.read, joinpath(@__DIR__, "test", "diffusion_data.json"), "r") diagram = data[:diagram]; model = data[:model]; @@ -76,7 +76,7 @@ end add_part!(handcrafted_pode, :Op1, src=1, tgt=2, op1=:Δ); # no scalars in second position - decapode, _ = Decapode(diagram, theory); + decapode, _, _ = Decapode(diagram, theory); @test decapode == handcrafted_pode @@ -86,7 +86,7 @@ end @testset "Simulation" begin json_string = read(joinpath(@__DIR__, "diffusion_data.json"), String); - system = only(PodeSystems(json_string)); + system = only(PodeSystem(json_string)); simulator = evalsim(system.pode) f = simulator(system.dualmesh, default_dec_generate, DiagonalHodge()); @@ -113,26 +113,31 @@ end @testset "SimulationTwoVar" begin json_string = read(joinpath(@__DIR__, "diffusion_data_twovars.json"), String); - systems = PodeSystems(json_string); + system = PodeSystem(json_string); - simulators = map(systems) do system evalsim(system.pode) end - fs = map(zip(simulators,systems)) do (simulator,system) simulator(system.dualmesh, default_dec_generate, DiagonalHodge()) end; + # DEBUGGING + open("test_sim.jl", "w") do f + write(f, string(gensim(system.pode))) + end + simulator = include("test_sim.jl") + + f = simulator(system.dualmesh, system.generate, DiagonalHodge()) # time - solns = map(zip(fs,systems)) do (f,system) run_sim(f, system.init, 50.0, ComponentArray(k=0.5,)) end; + soln = run_sim(f, system.init, 50.0, ComponentArray(k=0.5,)); # returns ::ODESolution # - retcode # - interpolation # - t # - u::Vector{ComponentVector} - for soln in solns @test soln.retcode == ReturnCode.Success end + @test soln.retcode == ReturnCode.Success - results = map(zip(solns,systems)) do (soln,system) SimResult(soln, system) end; + result = SimResult(soln, system); - for result in results @test typeof(result.state) == Vector{Matrix{SVector{3, Float64}}} end + @test typeof(result.state) == Vector{Matrix{SVector{3, Float64}}} - jvs = JsonValue.(results); + jvs = JsonValue(results); end @@ -170,7 +175,7 @@ end @testset "Simulation ..." begin json_string = read(joinpath(@__DIR__, "diffusion_long_trip.json"), String); - system = only(PodeSystems(json_string)); + system = only(PodeSystem(json_string)); simulator = evalsim(system.pode) # open("test_sim.jl", "w") do f @@ -231,7 +236,7 @@ end @testset "Simulation ..." begin json_string = read(joinpath(@__DIR__, "diffusivity_constant.json"), String); - system = only(PodeSystems(json_string)); + system = only(PodeSystem(json_string)); simulator = evalsim(system.pode) # open("test_sim.jl", "w") do f From fc1bf10e7752349495939cc1a20c0cf624953412 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Tue, 3 Dec 2024 14:29:18 -0700 Subject: [PATCH 08/32] TST: JSON data for N-S vorticity equation. --- .../algjulia-service/test/ns_vorticity.json | 577 ++++++++++++++++++ 1 file changed, 577 insertions(+) create mode 100644 packages/algjulia-service/test/ns_vorticity.json diff --git a/packages/algjulia-service/test/ns_vorticity.json b/packages/algjulia-service/test/ns_vorticity.json new file mode 100644 index 00000000..2df6e1a1 --- /dev/null +++ b/packages/algjulia-service/test/ns_vorticity.json @@ -0,0 +1,577 @@ +{ + "diagram": [ + { + "tag": "object", + "name": "", + "id": "01938868-6ef8-7096-8a06-ddfe946cb198", + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "01938827-b4c3-7996-850f-2fecb9dc5343" + } + }, + { + "tag": "object", + "name": "", + "id": "01938869-c0ae-7b1f-9179-35d64102bc6e", + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "01938829-293b-726a-8e86-f7462a8f6f07" + } + }, + { + "tag": "object", + "name": "u", + "id": "01938867-e117-7be0-b32a-1e4cd0af5cd9", + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "01938827-ac3d-711a-a508-35087405d2eb" + } + }, + { + "tag": "object", + "name": "", + "id": "0193886f-3fe1-740e-9a0e-2f3d8add2fa1", + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "01938827-b4c3-7996-850f-2fecb9dc5343" + } + }, + { + "tag": "object", + "name": "", + "id": "0193886b-bba3-7b84-9311-4412f4c0718f", + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "01938827-ac3d-711a-a508-35087405d2eb" + } + }, + { + "tag": "object", + "name": "", + "id": "01938868-dfd1-74b2-8b89-32d599ef94e6", + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc" + } + }, + { + "tag": "object", + "name": "", + "id": "01938dad-a5b0-7ce3-a0bf-615e4111ce02", + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "01938827-b4c3-7996-850f-2fecb9dc5343" + } + }, + { + "tag": "object", + "name": "", + "id": "0193886c-f9bd-7872-8d57-1e3c88e7c31b", + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "01938827-ac3d-711a-a508-35087405d2eb" + } + }, + { + "tag": "object", + "name": "", + "id": "0193886c-55b4-71a9-816c-3c30896be5d6", + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "01938829-293b-726a-8e86-f7462a8f6f07" + } + }, + { + "tag": "object", + "name": "ψ", + "id": "01938867-f97d-7189-97e5-e5cd19500108", + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc" + } + }, + { + "tag": "morphism", + "name": "", + "id": "01938868-f03f-7aed-bd6a-e78476e29a99", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "01938828-bdf1-7d20-9dd7-7ad4c8dd78bf" + }, + "dom": { + "tag": "Basic", + "content": "01938867-f97d-7189-97e5-e5cd19500108" + }, + "cod": { + "tag": "Basic", + "content": "01938868-dfd1-74b2-8b89-32d599ef94e6" + } + }, + { + "tag": "morphism", + "name": "", + "id": "01938869-a775-7129-9daa-40b8709365b9", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "01938829-0414-7b82-a0f9-f714d17e6ff4" + }, + "dom": { + "tag": "Basic", + "content": "01938867-f97d-7189-97e5-e5cd19500108" + }, + "cod": { + "tag": "Basic", + "content": "01938869-c0ae-7b1f-9179-35d64102bc6e" + } + }, + { + "tag": "morphism", + "name": "", + "id": "01938868-58bc-7c28-802a-287aa4e7600a", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "01938828-1244-7de4-a00a-e7969c43efef" + }, + "dom": { + "tag": "Basic", + "content": "01938867-e117-7be0-b32a-1e4cd0af5cd9" + }, + "cod": { + "tag": "Basic", + "content": "01938868-6ef8-7096-8a06-ddfe946cb198" + } + }, + { + "tag": "morphism", + "name": "", + "id": "0193886c-3f95-7922-919b-7cd9a582d312", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "0193885a-f4e6-7dc9-9ca7-9b318d93b2c0" + }, + "dom": { + "tag": "Basic", + "content": "0193886b-bba3-7b84-9311-4412f4c0718f" + }, + "cod": { + "tag": "Basic", + "content": "0193886c-55b4-71a9-816c-3c30896be5d6" + } + }, + { + "tag": "morphism", + "name": "", + "id": "0193886a-1fbb-7049-ad9e-5d78097f2045", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "01938829-62c4-7897-b6d6-0a3563ee643d" + }, + "dom": { + "tag": "Basic", + "content": "01938869-c0ae-7b1f-9179-35d64102bc6e" + }, + "cod": { + "tag": "Basic", + "content": "01938867-e117-7be0-b32a-1e4cd0af5cd9" + } + }, + { + "tag": "morphism", + "name": "", + "id": "0193886e-eb24-767e-ab8e-1b346b0c1797", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "0193886b-48c1-788c-9ec4-767eaa555fb6" + }, + "dom": { + "tag": "Basic", + "content": "01938868-6ef8-7096-8a06-ddfe946cb198" + }, + "cod": { + "tag": "Basic", + "content": "01938dad-a5b0-7ce3-a0bf-615e4111ce02" + } + }, + { + "tag": "morphism", + "name": "", + "id": "01938868-8384-7b3c-a56b-d5e4e91d3ee6", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "01938828-8662-7ac6-8e68-58c0cd035384" + }, + "dom": { + "tag": "Basic", + "content": "01938868-6ef8-7096-8a06-ddfe946cb198" + }, + "cod": { + "tag": "Basic", + "content": "01938868-dfd1-74b2-8b89-32d599ef94e6" + } + }, + { + "tag": "morphism", + "name": "", + "id": "0193886b-a079-7129-bf3e-6af6be5907b4", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "01938829-d95c-74be-ba4d-17a19a569a64" + }, + "dom": { + "tag": "Basic", + "content": "01938867-e117-7be0-b32a-1e4cd0af5cd9" + }, + "cod": { + "tag": "Basic", + "content": "0193886b-bba3-7b84-9311-4412f4c0718f" + } + }, + { + "tag": "morphism", + "name": "", + "id": "01938dad-834e-708a-9439-d76797e399ce", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "01938dac-e35f-77ed-9081-6d926d3e366f" + }, + "dom": { + "tag": "Basic", + "content": "0193886f-3fe1-740e-9a0e-2f3d8add2fa1" + }, + "cod": { + "tag": "Basic", + "content": "01938dad-a5b0-7ce3-a0bf-615e4111ce02" + } + }, + { + "tag": "morphism", + "name": "", + "id": "0193886c-9d8a-7c1a-b32a-a7f03283df8d", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "01938829-62c4-7897-b6d6-0a3563ee643d" + }, + "dom": { + "tag": "Basic", + "content": "0193886c-55b4-71a9-816c-3c30896be5d6" + }, + "cod": { + "tag": "Basic", + "content": "0193886c-f9bd-7872-8d57-1e3c88e7c31b" + } + }, + { + "tag": "morphism", + "name": "", + "id": "0193886f-0477-7b28-9f71-ed4ea8014758", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "0193882a-9357-7283-97e7-ad1171ae957e" + }, + "dom": { + "tag": "Basic", + "content": "0193886c-f9bd-7872-8d57-1e3c88e7c31b" + }, + "cod": { + "tag": "Basic", + "content": "0193886f-3fe1-740e-9a0e-2f3d8add2fa1" + } + } + ], + "model": [ + { + "id": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc", + "name": "0-Form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + { + "id": "01938827-ac3d-711a-a508-35087405d2eb", + "name": "Dual 1-Form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + { + "id": "01938827-b4c3-7996-850f-2fecb9dc5343", + "name": "Dual 2-form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + { + "cod": { + "content": "01938827-b4c3-7996-850f-2fecb9dc5343", + "tag": "Basic" + }, + "dom": { + "content": "01938827-ac3d-711a-a508-35087405d2eb", + "tag": "Basic" + }, + "id": "01938828-1244-7de4-a00a-e7969c43efef", + "morType": { + "content": "Nonscalar", + "tag": "Basic" + }, + "name": "d12", + "tag": "morphism" + }, + { + "cod": { + "content": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc", + "tag": "Basic" + }, + "dom": { + "content": "01938827-b4c3-7996-850f-2fecb9dc5343", + "tag": "Basic" + }, + "id": "01938828-8662-7ac6-8e68-58c0cd035384", + "morType": { + "content": "Nonscalar", + "tag": "Basic" + }, + "name": "⋆2", + "tag": "morphism" + }, + { + "cod": { + "content": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc", + "tag": "Basic" + }, + "dom": { + "content": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc", + "tag": "Basic" + }, + "id": "01938828-bdf1-7d20-9dd7-7ad4c8dd78bf", + "morType": { + "content": "Nonscalar", + "tag": "Basic" + }, + "name": "Δ", + "tag": "morphism" + }, + { + "cod": { + "content": "01938829-293b-726a-8e86-f7462a8f6f07", + "tag": "Basic" + }, + "dom": { + "content": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc", + "tag": "Basic" + }, + "id": "01938829-0414-7b82-a0f9-f714d17e6ff4", + "morType": { + "content": "Nonscalar", + "tag": "Basic" + }, + "name": "d01", + "tag": "morphism" + }, + { + "id": "01938829-293b-726a-8e86-f7462a8f6f07", + "name": "Primal 1-Form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + { + "cod": { + "content": "01938827-ac3d-711a-a508-35087405d2eb", + "tag": "Basic" + }, + "dom": { + "content": "01938829-293b-726a-8e86-f7462a8f6f07", + "tag": "Basic" + }, + "id": "01938829-62c4-7897-b6d6-0a3563ee643d", + "morType": { + "content": "Nonscalar", + "tag": "Basic" + }, + "name": "⋆1", + "tag": "morphism" + }, + { + "cod": { + "content": "01938827-ac3d-711a-a508-35087405d2eb", + "tag": "Basic" + }, + "dom": { + "content": "01938827-ac3d-711a-a508-35087405d2eb", + "tag": "Basic" + }, + "id": "01938829-d95c-74be-ba4d-17a19a569a64", + "morType": { + "content": "Nonscalar", + "tag": "Basic" + }, + "name": "∧ᵈᵖ₁₀(-,⋆d(-))", + "tag": "morphism" + }, + { + "cod": { + "content": "01938827-b4c3-7996-850f-2fecb9dc5343", + "tag": "Basic" + }, + "dom": { + "content": "01938827-ac3d-711a-a508-35087405d2eb", + "tag": "Basic" + }, + "id": "0193882a-9357-7283-97e7-ad1171ae957e", + "morType": { + "content": "Nonscalar", + "tag": "Basic" + }, + "name": "d̃₁", + "tag": "morphism" + }, + { + "cod": { + "content": "01938829-293b-726a-8e86-f7462a8f6f07", + "tag": "Basic" + }, + "dom": { + "content": "01938827-ac3d-711a-a508-35087405d2eb", + "tag": "Basic" + }, + "id": "0193885a-f4e6-7dc9-9ca7-9b318d93b2c0", + "morType": { + "content": "Nonscalar", + "tag": "Basic" + }, + "name": "♭♯", + "tag": "morphism" + }, + { + "cod": { + "content": "01938827-b4c3-7996-850f-2fecb9dc5343", + "tag": "Basic" + }, + "dom": { + "content": "01938827-b4c3-7996-850f-2fecb9dc5343", + "tag": "Basic" + }, + "id": "0193886b-48c1-788c-9ec4-767eaa555fb6", + "morType": { + "content": "Nonscalar", + "tag": "Basic" + }, + "name": " ∂ₜ", + "tag": "morphism" + }, + { + "cod": { + "content": "01938827-b4c3-7996-850f-2fecb9dc5343", + "tag": "Basic" + }, + "dom": { + "content": "01938827-b4c3-7996-850f-2fecb9dc5343", + "tag": "Basic" + }, + "id": "01938dac-e35f-77ed-9081-6d926d3e366f", + "morType": { + "content": "Nonscalar", + "tag": "Basic" + }, + "name": "-", + "tag": "morphism" + } + ], + "scalars": {}, + "plotVariables": [ + "01938867-e117-7be0-b32a-1e4cd0af5cd9" + ] +} From 0321a9f47450d30513d6e37a9332b053df1d6611 Mon Sep 17 00:00:00 2001 From: Kevin Carlson Date: Tue, 3 Dec 2024 14:31:41 -0700 Subject: [PATCH 09/32] Decapodes tests passing including with two variables. remove System reference in tsx replace plotvars with uuids in test json trying to get one-plotvar tests running again woo tests pass incl twovar --- packages/algjulia-service/src/decapodes.jl | 11 ++++---- .../algjulia-service/test/diffusion_data.json | 2 +- .../test/diffusion_data_twovars.json | 2 +- .../test/diffusion_long_trip.json | 2 +- .../test/diffusivity_constant.json | 2 +- packages/algjulia-service/test/runtests.jl | 28 +++++++++---------- .../src/stdlib/analyses/decapodes.tsx | 15 +++++----- 7 files changed, 32 insertions(+), 30 deletions(-) diff --git a/packages/algjulia-service/src/decapodes.jl b/packages/algjulia-service/src/decapodes.jl index 7f129315..bb31c671 100644 --- a/packages/algjulia-service/src/decapodes.jl +++ b/packages/algjulia-service/src/decapodes.jl @@ -302,9 +302,12 @@ function PodeSystem(json_string::String) # pode, anons, and vars (UUID => ACSetId) decapode, anons, vars = Decapode(diagram, theory; scalars=scalars); + uuid2symb = Dict( + [key => (subpart(decapode, vars[key], :name)) for key ∈ keys(vars)] + ); # plotting variables - plotvars = Symbol.(json_object[:plotVariables]) - + plotvars = [uuid2symb[uuid] for uuid in json_object[:plotVariables]]; + # mesh and initial conditions s, sd = create_mesh() u0 = init_conditions(plotvars, sd) @@ -324,9 +327,7 @@ function PodeSystem(json_string::String) end # symbol => uuid. we need this to reassociate the var - symb2uuid = Dict( - [(subpart(decapode, vars[key], :name) => key) for key ∈ keys(vars)] - ); + symb2uuid = Dict([v => k for (k,v) in pairs(uuid2symb)]) return PodeSystem(decapode, plotvars, anons, s, sd, u0, sys_generate, symb2uuid) end diff --git a/packages/algjulia-service/test/diffusion_data.json b/packages/algjulia-service/test/diffusion_data.json index fef44cce..4da4dc92 100644 --- a/packages/algjulia-service/test/diffusion_data.json +++ b/packages/algjulia-service/test/diffusion_data.json @@ -1,4 +1,4 @@ -{ "plotVariables": ["C"], +{ "plotVariables": ["01932402-bcf5-7432-8d14-dbae9eabf907"], "diagram": [ { "id": "01932402-bcf5-7432-8d14-dbae9eabf907", diff --git a/packages/algjulia-service/test/diffusion_data_twovars.json b/packages/algjulia-service/test/diffusion_data_twovars.json index 5ccf7219..4db9a27e 100644 --- a/packages/algjulia-service/test/diffusion_data_twovars.json +++ b/packages/algjulia-service/test/diffusion_data_twovars.json @@ -1,4 +1,4 @@ -{ "plotVariables": ["C","dC/dt"], +{ "plotVariables": ["01932402-bcf5-7432-8d14-dbae9eabf907","01932403-5b6c-7231-90d7-d7cece275eb2"], "diagram": [ { "id": "01932402-bcf5-7432-8d14-dbae9eabf907", diff --git a/packages/algjulia-service/test/diffusion_long_trip.json b/packages/algjulia-service/test/diffusion_long_trip.json index af9be5df..aeac9a80 100644 --- a/packages/algjulia-service/test/diffusion_long_trip.json +++ b/packages/algjulia-service/test/diffusion_long_trip.json @@ -1,4 +1,4 @@ -{ "plotVariables": ["C"], +{ "plotVariables": ["01936ee4-8a1d-7bd7-a2c4-0f5291893346"], "diagram": [ { "tag": "object", diff --git a/packages/algjulia-service/test/diffusivity_constant.json b/packages/algjulia-service/test/diffusivity_constant.json index 4bb67cc9..f4dd8b5f 100644 --- a/packages/algjulia-service/test/diffusivity_constant.json +++ b/packages/algjulia-service/test/diffusivity_constant.json @@ -1,4 +1,4 @@ -{ "plotVariables": ["u"], +{ "plotVariables": ["01936f2e-2f39-738d-8af2-5caf75b29f3a"], "diagram": [ { "tag": "object", diff --git a/packages/algjulia-service/test/runtests.jl b/packages/algjulia-service/test/runtests.jl index 74fb77aa..5d665dce 100644 --- a/packages/algjulia-service/test/runtests.jl +++ b/packages/algjulia-service/test/runtests.jl @@ -17,7 +17,7 @@ import OrdinaryDiffEq: ReturnCode #using Plots # load data -data = open(JSON3.read, joinpath(@__DIR__, "test", "diffusion_data.json"), "r") +data = open(JSON3.read, joinpath(@__DIR__, "diffusion_data.json"), "r") diagram = data[:diagram]; model = data[:model]; @@ -82,11 +82,11 @@ end end - +#= @testset "Simulation" begin json_string = read(joinpath(@__DIR__, "diffusion_data.json"), String); - system = only(PodeSystem(json_string)); + system = PodeSystem(json_string); simulator = evalsim(system.pode) f = simulator(system.dualmesh, default_dec_generate, DiagonalHodge()); @@ -103,7 +103,7 @@ end result = SimResult(soln, system); - @test typeof(result.state) == Vector{Matrix{SVector{3, Float64}}} + @test typeof(result.state) == Dict{String, Vector{Matrix{SVector{3, Float64}}}} jv = JsonValue(result); @@ -112,14 +112,14 @@ end @testset "SimulationTwoVar" begin - json_string = read(joinpath(@__DIR__, "diffusion_data_twovars.json"), String); + json_string = read(joinpath(@__DIR__,"diffusion_data_twovars.json"), String); system = PodeSystem(json_string); # DEBUGGING open("test_sim.jl", "w") do f write(f, string(gensim(system.pode))) end - simulator = include("test_sim.jl") + simulator = include("../test_sim.jl") f = simulator(system.dualmesh, system.generate, DiagonalHodge()) @@ -135,16 +135,16 @@ end result = SimResult(soln, system); - @test typeof(result.state) == Vector{Matrix{SVector{3, Float64}}} + @test typeof(result.state) == Dict{String, Vector{Matrix{SVector{3, Float64}}}} - jvs = JsonValue(results); + jvs = JsonValue(result); end - +=# ##### -data = open(JSON3.read, joinpath(@__DIR__, "diffusion_long_trip.json"), "r") +data = open(JSON3.read, joinpath(@__DIR__,"diffusion_long_trip.json"), "r") diagram = data[:diagram]; model = data[:model]; @@ -175,7 +175,7 @@ end @testset "Simulation ..." begin json_string = read(joinpath(@__DIR__, "diffusion_long_trip.json"), String); - system = only(PodeSystem(json_string)); + system = PodeSystem(json_string); simulator = evalsim(system.pode) # open("test_sim.jl", "w") do f @@ -196,7 +196,7 @@ end result = SimResult(soln, system); - @test typeof(result.state) == Vector{Matrix{SVector{3, Float64}}} + @test typeof(result.state) == Dict{String, Vector{Matrix{SVector{3, Float64}}}} jv = JsonValue(result); @@ -236,7 +236,7 @@ end @testset "Simulation ..." begin json_string = read(joinpath(@__DIR__, "diffusivity_constant.json"), String); - system = only(PodeSystem(json_string)); + system = PodeSystem(json_string); simulator = evalsim(system.pode) # open("test_sim.jl", "w") do f @@ -257,7 +257,7 @@ end result = SimResult(soln, system); - @test typeof(result.state) == Vector{Matrix{SVector{3, Float64}}} + @test typeof(result.state) == Dict{String, Vector{Matrix{SVector{3, Float64}}}} jv = JsonValue(result); diff --git a/packages/frontend/src/stdlib/analyses/decapodes.tsx b/packages/frontend/src/stdlib/analyses/decapodes.tsx index e4723553..655de0d6 100644 --- a/packages/frontend/src/stdlib/analyses/decapodes.tsx +++ b/packages/frontend/src/stdlib/analyses/decapodes.tsx @@ -279,16 +279,17 @@ using AlgebraicJuliaService `; /** Julia code run to perform a simulation. */ -const makeSimulationCode = (data: SimulationData) => ` -system = System(raw"""${JSON.stringify(data)}"""); -simulator = evalsim(system.pode); +const makeSimulationCode = (data: SimulationData) => + ` + system = only(PodeSystems(raw"""${JSON.stringify(data)}""")); + simulator = evalsim(system.pode); -f = simulator(system.dualmesh, system.generate, DiagonalHodge()); + f = simulator(system.dualmesh, system.generate, DiagonalHodge()); -soln = run_sim(f, system.init, 100.0, ComponentArray(k=0.5,)); + soln = run_sim(f, system.init, 100.0, ComponentArray(k=0.5,)); -JsonValue(SimResult(soln, system)) -`; + JsonValue(SimResult(soln, system)) + `; /** Create data to send to the Julia kernel. */ const makeSimulationData = ( From 2667495ffb5e4cbaed85cac7769bda3716082709 Mon Sep 17 00:00:00 2001 From: Kevin Carlson Date: Tue, 3 Dec 2024 22:13:34 -0700 Subject: [PATCH 10/32] ns running fine on the julia side --- packages/algjulia-service/Project.toml | 2 + packages/algjulia-service/src/decapodes.jl | 26 +- .../algjulia-service/test/ns_vorticity.json | 1114 ++++++++--------- packages/algjulia-service/test/runtests.jl | 34 +- .../src/stdlib/analyses/decapodes.tsx | 4 +- 5 files changed, 592 insertions(+), 588 deletions(-) diff --git a/packages/algjulia-service/Project.toml b/packages/algjulia-service/Project.toml index f991b4fe..73f49961 100644 --- a/packages/algjulia-service/Project.toml +++ b/packages/algjulia-service/Project.toml @@ -8,6 +8,7 @@ version = "0.1.0" ACSets = "227ef7b5-1206-438b-ac65-934d6da304b8" CombinatorialSpaces = "b1c52339-7909-45ad-8b6a-6e388f7c67f2" ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" +Debugger = "31a5f54b-26ea-5ae9-a837-f05ce5417438" Decapodes = "679ab3ea-c928-4fe6-8d59-fd451142d391" DiagrammaticEquations = "6f00c28b-6bed-4403-80fa-30e0dc12f317" Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" @@ -22,6 +23,7 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" [compat] ACSets = "0.2.21" ComponentArrays = "0.15" +Debugger = "0.7.10" Decapodes = "0.5.6" DiagrammaticEquations = "0.1.7" Distributions = "0.25" diff --git a/packages/algjulia-service/src/decapodes.jl b/packages/algjulia-service/src/decapodes.jl index bb31c671..147f8c08 100644 --- a/packages/algjulia-service/src/decapodes.jl +++ b/packages/algjulia-service/src/decapodes.jl @@ -9,7 +9,7 @@ using ACSets using DiagrammaticEquations using Decapodes -using Decapodes: dec_mat_dual_differential, dec_mat_hodge +using Decapodes: dec_mat_dual_differential, dec_mat_inverse_hodge using CombinatorialSpaces # dependencies @@ -58,12 +58,14 @@ function to_decapode_theory(::Val{:Hom}, name::String) "∂t" => :∂ₜ "∂ₜ" => :∂ₜ "Δ" => :Δ + "Δ⁻¹" => :Δ⁻¹ "d" => :d₀ "d*" => :dual_d₁ "d̃₁" => :dual_d₁ # \star on LHS "⋆" => :⋆₁ "⋆⁻¹" => :⋆₀⁻¹ + "⋆₀⁻¹" => :⋆₀⁻¹ # \bigstar on LHS "★" => :⋆₁ "★⁻¹" => :⋆₀⁻¹ @@ -265,10 +267,10 @@ function create_mesh() end export create_mesh -function init_conditions(plotvars::Vector{Symbol}, sd::HasDeltaSet) +function init_conditions(vars::Vector{Symbol}, sd::HasDeltaSet) c_dist = MvNormal([50, 50], LinearAlgebra.Diagonal(map(abs2, [7.5, 7.5]))) c = [pdf(c_dist, [p[1], p[2]]) for p in sd[:point]] - u0 = ComponentArray(; Dict([plotvar=>c for plotvar in plotvars])...) + u0 = ComponentArray(; Dict([var=>c for var in vars])...) return u0 end @@ -287,7 +289,7 @@ export PodeSystem """ Construct a vector of `PodeSystem` objects from a JSON string. """ -function PodeSystem(json_string::String) +function PodeSystem(json_string::String,hodge=GeometricHodge()) json_object = JSON3.read(json_string); # converts the JSON of (the fragment of) the theory @@ -302,6 +304,7 @@ function PodeSystem(json_string::String) # pode, anons, and vars (UUID => ACSetId) decapode, anons, vars = Decapode(diagram, theory; scalars=scalars); + dot_rename!(decapode) uuid2symb = Dict( [key => (subpart(decapode, vars[key], :name)) for key ∈ keys(vars)] ); @@ -310,17 +313,24 @@ function PodeSystem(json_string::String) # mesh and initial conditions s, sd = create_mesh() - u0 = init_conditions(plotvars, sd) + u0 = init_conditions([infer_state_names(decapode) ; plotvars], sd) ♭♯_m = ♭♯_mat(sd); wedge_dp10 = dec_wedge_product_dp(Tuple{1,0}, sd); dual_d1_m = dec_mat_dual_differential(1, sd); - star1_m = dec_mat_hodge(1, sd, GeometricHodge()); - function sys_generate(s, my_symbol; hodge=GeometricHodge()) + star0_inv_m = dec_mat_inverse_hodge(0, sd, hodge) + Δ0 = Δ(0,sd); + fΔ0 = factorize(Δ0); + + function sys_generate(s, my_symbol,hodge=hodge) op = @match my_symbol begin sym && if sym ∈ keys(anons) end => anons[sym] :♭♯ => x -> ♭♯_m * x # [1] - :dpsw => x -> wedge_dp10(x, star1_m*(dual_d1_m*x)) + :dpsw => x -> wedge_dp10(x, star0_inv_m[1]*(dual_d1_m[1]*x)) + :Δ⁻¹ => x -> begin + y = fΔ0 \ x + y .- minimum(y) + end _ => default_dec_matrix_generate(s, my_symbol, hodge) end return (args...) -> op(args...) diff --git a/packages/algjulia-service/test/ns_vorticity.json b/packages/algjulia-service/test/ns_vorticity.json index 2df6e1a1..ecf6654e 100644 --- a/packages/algjulia-service/test/ns_vorticity.json +++ b/packages/algjulia-service/test/ns_vorticity.json @@ -1,577 +1,539 @@ { - "diagram": [ - { - "tag": "object", - "name": "", - "id": "01938868-6ef8-7096-8a06-ddfe946cb198", - "obType": { - "tag": "Basic", - "content": "Object" - }, - "over": { - "tag": "Basic", - "content": "01938827-b4c3-7996-850f-2fecb9dc5343" - } - }, - { - "tag": "object", - "name": "", - "id": "01938869-c0ae-7b1f-9179-35d64102bc6e", - "obType": { - "tag": "Basic", - "content": "Object" - }, - "over": { - "tag": "Basic", - "content": "01938829-293b-726a-8e86-f7462a8f6f07" - } - }, - { - "tag": "object", - "name": "u", - "id": "01938867-e117-7be0-b32a-1e4cd0af5cd9", - "obType": { - "tag": "Basic", - "content": "Object" - }, - "over": { - "tag": "Basic", - "content": "01938827-ac3d-711a-a508-35087405d2eb" - } - }, - { - "tag": "object", - "name": "", - "id": "0193886f-3fe1-740e-9a0e-2f3d8add2fa1", - "obType": { - "tag": "Basic", - "content": "Object" - }, - "over": { - "tag": "Basic", - "content": "01938827-b4c3-7996-850f-2fecb9dc5343" - } - }, - { - "tag": "object", - "name": "", - "id": "0193886b-bba3-7b84-9311-4412f4c0718f", - "obType": { - "tag": "Basic", - "content": "Object" - }, - "over": { - "tag": "Basic", - "content": "01938827-ac3d-711a-a508-35087405d2eb" - } - }, - { - "tag": "object", - "name": "", - "id": "01938868-dfd1-74b2-8b89-32d599ef94e6", - "obType": { - "tag": "Basic", - "content": "Object" - }, - "over": { - "tag": "Basic", - "content": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc" - } - }, - { - "tag": "object", - "name": "", - "id": "01938dad-a5b0-7ce3-a0bf-615e4111ce02", - "obType": { - "tag": "Basic", - "content": "Object" - }, - "over": { - "tag": "Basic", - "content": "01938827-b4c3-7996-850f-2fecb9dc5343" - } - }, - { - "tag": "object", - "name": "", - "id": "0193886c-f9bd-7872-8d57-1e3c88e7c31b", - "obType": { - "tag": "Basic", - "content": "Object" - }, - "over": { - "tag": "Basic", - "content": "01938827-ac3d-711a-a508-35087405d2eb" - } - }, - { - "tag": "object", - "name": "", - "id": "0193886c-55b4-71a9-816c-3c30896be5d6", - "obType": { - "tag": "Basic", - "content": "Object" - }, - "over": { - "tag": "Basic", - "content": "01938829-293b-726a-8e86-f7462a8f6f07" - } - }, - { - "tag": "object", - "name": "ψ", - "id": "01938867-f97d-7189-97e5-e5cd19500108", - "obType": { - "tag": "Basic", - "content": "Object" - }, - "over": { - "tag": "Basic", - "content": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc" - } - }, - { - "tag": "morphism", - "name": "", - "id": "01938868-f03f-7aed-bd6a-e78476e29a99", - "morType": { - "tag": "Basic", - "content": "Nonscalar" - }, - "over": { - "tag": "Basic", - "content": "01938828-bdf1-7d20-9dd7-7ad4c8dd78bf" - }, - "dom": { - "tag": "Basic", - "content": "01938867-f97d-7189-97e5-e5cd19500108" - }, - "cod": { - "tag": "Basic", - "content": "01938868-dfd1-74b2-8b89-32d599ef94e6" - } - }, - { - "tag": "morphism", - "name": "", - "id": "01938869-a775-7129-9daa-40b8709365b9", - "morType": { - "tag": "Basic", - "content": "Nonscalar" - }, - "over": { - "tag": "Basic", - "content": "01938829-0414-7b82-a0f9-f714d17e6ff4" - }, - "dom": { - "tag": "Basic", - "content": "01938867-f97d-7189-97e5-e5cd19500108" - }, - "cod": { - "tag": "Basic", - "content": "01938869-c0ae-7b1f-9179-35d64102bc6e" - } - }, - { - "tag": "morphism", - "name": "", - "id": "01938868-58bc-7c28-802a-287aa4e7600a", - "morType": { - "tag": "Basic", - "content": "Nonscalar" - }, - "over": { - "tag": "Basic", - "content": "01938828-1244-7de4-a00a-e7969c43efef" - }, - "dom": { - "tag": "Basic", - "content": "01938867-e117-7be0-b32a-1e4cd0af5cd9" - }, - "cod": { - "tag": "Basic", - "content": "01938868-6ef8-7096-8a06-ddfe946cb198" - } - }, - { - "tag": "morphism", - "name": "", - "id": "0193886c-3f95-7922-919b-7cd9a582d312", - "morType": { - "tag": "Basic", - "content": "Nonscalar" - }, - "over": { - "tag": "Basic", - "content": "0193885a-f4e6-7dc9-9ca7-9b318d93b2c0" - }, - "dom": { - "tag": "Basic", - "content": "0193886b-bba3-7b84-9311-4412f4c0718f" - }, - "cod": { - "tag": "Basic", - "content": "0193886c-55b4-71a9-816c-3c30896be5d6" - } - }, - { - "tag": "morphism", - "name": "", - "id": "0193886a-1fbb-7049-ad9e-5d78097f2045", - "morType": { - "tag": "Basic", - "content": "Nonscalar" - }, - "over": { - "tag": "Basic", - "content": "01938829-62c4-7897-b6d6-0a3563ee643d" - }, - "dom": { - "tag": "Basic", - "content": "01938869-c0ae-7b1f-9179-35d64102bc6e" - }, - "cod": { - "tag": "Basic", - "content": "01938867-e117-7be0-b32a-1e4cd0af5cd9" - } - }, - { - "tag": "morphism", - "name": "", - "id": "0193886e-eb24-767e-ab8e-1b346b0c1797", - "morType": { - "tag": "Basic", - "content": "Nonscalar" - }, - "over": { - "tag": "Basic", - "content": "0193886b-48c1-788c-9ec4-767eaa555fb6" - }, - "dom": { - "tag": "Basic", - "content": "01938868-6ef8-7096-8a06-ddfe946cb198" - }, - "cod": { - "tag": "Basic", - "content": "01938dad-a5b0-7ce3-a0bf-615e4111ce02" - } - }, - { - "tag": "morphism", - "name": "", - "id": "01938868-8384-7b3c-a56b-d5e4e91d3ee6", - "morType": { - "tag": "Basic", - "content": "Nonscalar" - }, - "over": { - "tag": "Basic", - "content": "01938828-8662-7ac6-8e68-58c0cd035384" - }, - "dom": { - "tag": "Basic", - "content": "01938868-6ef8-7096-8a06-ddfe946cb198" - }, - "cod": { - "tag": "Basic", - "content": "01938868-dfd1-74b2-8b89-32d599ef94e6" - } - }, - { - "tag": "morphism", - "name": "", - "id": "0193886b-a079-7129-bf3e-6af6be5907b4", - "morType": { - "tag": "Basic", - "content": "Nonscalar" - }, - "over": { - "tag": "Basic", - "content": "01938829-d95c-74be-ba4d-17a19a569a64" - }, - "dom": { - "tag": "Basic", - "content": "01938867-e117-7be0-b32a-1e4cd0af5cd9" - }, - "cod": { - "tag": "Basic", - "content": "0193886b-bba3-7b84-9311-4412f4c0718f" - } - }, - { - "tag": "morphism", - "name": "", - "id": "01938dad-834e-708a-9439-d76797e399ce", - "morType": { - "tag": "Basic", - "content": "Nonscalar" - }, - "over": { - "tag": "Basic", - "content": "01938dac-e35f-77ed-9081-6d926d3e366f" - }, - "dom": { - "tag": "Basic", - "content": "0193886f-3fe1-740e-9a0e-2f3d8add2fa1" - }, - "cod": { - "tag": "Basic", - "content": "01938dad-a5b0-7ce3-a0bf-615e4111ce02" - } - }, - { - "tag": "morphism", - "name": "", - "id": "0193886c-9d8a-7c1a-b32a-a7f03283df8d", - "morType": { - "tag": "Basic", - "content": "Nonscalar" - }, - "over": { - "tag": "Basic", - "content": "01938829-62c4-7897-b6d6-0a3563ee643d" - }, - "dom": { - "tag": "Basic", - "content": "0193886c-55b4-71a9-816c-3c30896be5d6" - }, - "cod": { - "tag": "Basic", - "content": "0193886c-f9bd-7872-8d57-1e3c88e7c31b" - } - }, - { - "tag": "morphism", - "name": "", - "id": "0193886f-0477-7b28-9f71-ed4ea8014758", - "morType": { - "tag": "Basic", - "content": "Nonscalar" - }, - "over": { - "tag": "Basic", - "content": "0193882a-9357-7283-97e7-ad1171ae957e" - }, - "dom": { - "tag": "Basic", - "content": "0193886c-f9bd-7872-8d57-1e3c88e7c31b" - }, - "cod": { - "tag": "Basic", - "content": "0193886f-3fe1-740e-9a0e-2f3d8add2fa1" - } - } - ], - "model": [ - { - "id": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc", - "name": "0-Form", - "obType": { - "content": "Object", - "tag": "Basic" - }, - "tag": "object" - }, - { - "id": "01938827-ac3d-711a-a508-35087405d2eb", - "name": "Dual 1-Form", - "obType": { - "content": "Object", - "tag": "Basic" - }, - "tag": "object" - }, - { - "id": "01938827-b4c3-7996-850f-2fecb9dc5343", - "name": "Dual 2-form", - "obType": { - "content": "Object", - "tag": "Basic" - }, - "tag": "object" - }, - { - "cod": { - "content": "01938827-b4c3-7996-850f-2fecb9dc5343", - "tag": "Basic" - }, - "dom": { - "content": "01938827-ac3d-711a-a508-35087405d2eb", - "tag": "Basic" - }, - "id": "01938828-1244-7de4-a00a-e7969c43efef", - "morType": { - "content": "Nonscalar", - "tag": "Basic" - }, - "name": "d12", - "tag": "morphism" - }, - { - "cod": { - "content": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc", - "tag": "Basic" - }, - "dom": { - "content": "01938827-b4c3-7996-850f-2fecb9dc5343", - "tag": "Basic" - }, - "id": "01938828-8662-7ac6-8e68-58c0cd035384", - "morType": { - "content": "Nonscalar", - "tag": "Basic" - }, - "name": "⋆2", - "tag": "morphism" - }, - { - "cod": { - "content": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc", - "tag": "Basic" - }, - "dom": { - "content": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc", - "tag": "Basic" - }, - "id": "01938828-bdf1-7d20-9dd7-7ad4c8dd78bf", - "morType": { - "content": "Nonscalar", - "tag": "Basic" - }, - "name": "Δ", - "tag": "morphism" - }, - { - "cod": { - "content": "01938829-293b-726a-8e86-f7462a8f6f07", - "tag": "Basic" - }, - "dom": { - "content": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc", - "tag": "Basic" - }, - "id": "01938829-0414-7b82-a0f9-f714d17e6ff4", - "morType": { - "content": "Nonscalar", - "tag": "Basic" - }, - "name": "d01", - "tag": "morphism" - }, - { - "id": "01938829-293b-726a-8e86-f7462a8f6f07", - "name": "Primal 1-Form", - "obType": { - "content": "Object", - "tag": "Basic" - }, - "tag": "object" - }, - { - "cod": { - "content": "01938827-ac3d-711a-a508-35087405d2eb", - "tag": "Basic" - }, - "dom": { - "content": "01938829-293b-726a-8e86-f7462a8f6f07", - "tag": "Basic" - }, - "id": "01938829-62c4-7897-b6d6-0a3563ee643d", - "morType": { - "content": "Nonscalar", - "tag": "Basic" - }, - "name": "⋆1", - "tag": "morphism" - }, - { - "cod": { - "content": "01938827-ac3d-711a-a508-35087405d2eb", - "tag": "Basic" - }, - "dom": { - "content": "01938827-ac3d-711a-a508-35087405d2eb", - "tag": "Basic" - }, - "id": "01938829-d95c-74be-ba4d-17a19a569a64", - "morType": { - "content": "Nonscalar", - "tag": "Basic" - }, - "name": "∧ᵈᵖ₁₀(-,⋆d(-))", - "tag": "morphism" - }, - { - "cod": { - "content": "01938827-b4c3-7996-850f-2fecb9dc5343", - "tag": "Basic" - }, - "dom": { - "content": "01938827-ac3d-711a-a508-35087405d2eb", - "tag": "Basic" - }, - "id": "0193882a-9357-7283-97e7-ad1171ae957e", - "morType": { - "content": "Nonscalar", - "tag": "Basic" - }, - "name": "d̃₁", - "tag": "morphism" - }, - { - "cod": { - "content": "01938829-293b-726a-8e86-f7462a8f6f07", - "tag": "Basic" - }, - "dom": { - "content": "01938827-ac3d-711a-a508-35087405d2eb", - "tag": "Basic" - }, - "id": "0193885a-f4e6-7dc9-9ca7-9b318d93b2c0", - "morType": { - "content": "Nonscalar", - "tag": "Basic" - }, - "name": "♭♯", - "tag": "morphism" - }, - { - "cod": { - "content": "01938827-b4c3-7996-850f-2fecb9dc5343", - "tag": "Basic" - }, - "dom": { - "content": "01938827-b4c3-7996-850f-2fecb9dc5343", - "tag": "Basic" - }, - "id": "0193886b-48c1-788c-9ec4-767eaa555fb6", - "morType": { - "content": "Nonscalar", - "tag": "Basic" - }, - "name": " ∂ₜ", - "tag": "morphism" - }, - { - "cod": { - "content": "01938827-b4c3-7996-850f-2fecb9dc5343", - "tag": "Basic" - }, - "dom": { - "content": "01938827-b4c3-7996-850f-2fecb9dc5343", - "tag": "Basic" - }, - "id": "01938dac-e35f-77ed-9081-6d926d3e366f", - "morType": { - "content": "Nonscalar", - "tag": "Basic" - }, - "name": "-", - "tag": "morphism" - } - ], - "scalars": {}, - "plotVariables": [ - "01938867-e117-7be0-b32a-1e4cd0af5cd9" - ] -} + "diagram": [ + { + "tag": "object", + "name": "", + "id": "01938869-c0ae-7b1f-9179-35d64102bc6e", + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "01938829-293b-726a-8e86-f7462a8f6f07" + } + }, + { + "tag": "object", + "name": "uu", + "id": "01938867-e117-7be0-b32a-1e4cd0af5cd9", + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "01938827-ac3d-711a-a508-35087405d2eb" + } + }, + { + "tag": "object", + "name": "", + "id": "0193886f-3fe1-740e-9a0e-2f3d8add2fa1", + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "01938827-b4c3-7996-850f-2fecb9dc5343" + } + }, + { + "tag": "object", + "name": "", + "id": "0193886b-bba3-7b84-9311-4412f4c0718f", + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "01938827-ac3d-711a-a508-35087405d2eb" + } + }, + { + "tag": "object", + "name": "", + "id": "01938868-dfd1-74b2-8b89-32d599ef94e6", + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc" + } + }, + { + "tag": "object", + "name": "", + "id": "01938dad-a5b0-7ce3-a0bf-615e4111ce02", + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "01938827-b4c3-7996-850f-2fecb9dc5343" + } + }, + { + "tag": "object", + "name": "duu", + "id": "01938fb0-449c-7614-8086-2384cf6bc784", + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "01938827-b4c3-7996-850f-2fecb9dc5343" + } + }, + { + "tag": "object", + "name": "", + "id": "0193886c-f9bd-7872-8d57-1e3c88e7c31b", + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "01938827-ac3d-711a-a508-35087405d2eb" + } + }, + { + "tag": "object", + "name": "", + "id": "0193886c-55b4-71a9-816c-3c30896be5d6", + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "01938829-293b-726a-8e86-f7462a8f6f07" + } + }, + { + "tag": "object", + "name": "ψ", + "id": "01938867-f97d-7189-97e5-e5cd19500108", + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc" + } + }, + { + "tag": "morphism", + "name": "", + "id": "01938868-f03f-7aed-bd6a-e78476e29a99", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "01938828-bdf1-7d20-9dd7-7ad4c8dd78bf" + }, + "dom": { + "tag": "Basic", + "content": "01938868-dfd1-74b2-8b89-32d599ef94e6" + }, + "cod": { + "tag": "Basic", + "content": "01938867-f97d-7189-97e5-e5cd19500108" + } + }, + { + "tag": "morphism", + "name": "", + "id": "01938869-a775-7129-9daa-40b8709365b9", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "01938829-0414-7b82-a0f9-f714d17e6ff4" + }, + "dom": { + "tag": "Basic", + "content": "01938867-f97d-7189-97e5-e5cd19500108" + }, + "cod": { + "tag": "Basic", + "content": "01938869-c0ae-7b1f-9179-35d64102bc6e" + } + }, + { + "tag": "morphism", + "name": "", + "id": "0193886c-3f95-7922-919b-7cd9a582d312", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "0193885a-f4e6-7dc9-9ca7-9b318d93b2c0" + }, + "dom": { + "tag": "Basic", + "content": "0193886b-bba3-7b84-9311-4412f4c0718f" + }, + "cod": { + "tag": "Basic", + "content": "0193886c-55b4-71a9-816c-3c30896be5d6" + } + }, + { + "tag": "morphism", + "name": "", + "id": "0193886a-1fbb-7049-ad9e-5d78097f2045", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "01938829-62c4-7897-b6d6-0a3563ee643d" + }, + "dom": { + "tag": "Basic", + "content": "01938869-c0ae-7b1f-9179-35d64102bc6e" + }, + "cod": { + "tag": "Basic", + "content": "01938867-e117-7be0-b32a-1e4cd0af5cd9" + } + }, + { + "tag": "morphism", + "name": "", + "id": "0193886e-eb24-767e-ab8e-1b346b0c1797", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "0193886b-48c1-788c-9ec4-767eaa555fb6" + }, + "dom": { + "tag": "Basic", + "content": "01938fb0-449c-7614-8086-2384cf6bc784" + }, + "cod": { + "tag": "Basic", + "content": "01938dad-a5b0-7ce3-a0bf-615e4111ce02" + } + }, + { + "tag": "morphism", + "name": "", + "id": "01938868-8384-7b3c-a56b-d5e4e91d3ee6", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "01938828-8662-7ac6-8e68-58c0cd035384" + }, + "dom": { + "tag": "Basic", + "content": "01938fb0-449c-7614-8086-2384cf6bc784" + }, + "cod": { + "tag": "Basic", + "content": "01938868-dfd1-74b2-8b89-32d599ef94e6" + } + }, + { + "tag": "morphism", + "name": "", + "id": "0193886b-a079-7129-bf3e-6af6be5907b4", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "01938829-d95c-74be-ba4d-17a19a569a64" + }, + "dom": { + "tag": "Basic", + "content": "01938867-e117-7be0-b32a-1e4cd0af5cd9" + }, + "cod": { + "tag": "Basic", + "content": "0193886b-bba3-7b84-9311-4412f4c0718f" + } + }, + { + "tag": "morphism", + "name": "", + "id": "01938dad-834e-708a-9439-d76797e399ce", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "01938dac-e35f-77ed-9081-6d926d3e366f" + }, + "dom": { + "tag": "Basic", + "content": "0193886f-3fe1-740e-9a0e-2f3d8add2fa1" + }, + "cod": { + "tag": "Basic", + "content": "01938dad-a5b0-7ce3-a0bf-615e4111ce02" + } + }, + { + "tag": "morphism", + "name": "", + "id": "0193886c-9d8a-7c1a-b32a-a7f03283df8d", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "01938829-62c4-7897-b6d6-0a3563ee643d" + }, + "dom": { + "tag": "Basic", + "content": "0193886c-55b4-71a9-816c-3c30896be5d6" + }, + "cod": { + "tag": "Basic", + "content": "0193886c-f9bd-7872-8d57-1e3c88e7c31b" + } + }, + { + "tag": "morphism", + "name": "", + "id": "0193886f-0477-7b28-9f71-ed4ea8014758", + "morType": { + "tag": "Basic", + "content": "Nonscalar" + }, + "over": { + "tag": "Basic", + "content": "0193882a-9357-7283-97e7-ad1171ae957e" + }, + "dom": { + "tag": "Basic", + "content": "0193886c-f9bd-7872-8d57-1e3c88e7c31b" + }, + "cod": { + "tag": "Basic", + "content": "0193886f-3fe1-740e-9a0e-2f3d8add2fa1" + } + } + ], + "model": [ + { + "id": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc", + "name": "0-Form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + { + "id": "01938827-ac3d-711a-a508-35087405d2eb", + "name": "Dual 1-Form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + { + "id": "01938827-b4c3-7996-850f-2fecb9dc5343", + "name": "Dual 2-form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + { + "cod": { + "content": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc", + "tag": "Basic" + }, + "dom": { + "content": "01938827-b4c3-7996-850f-2fecb9dc5343", + "tag": "Basic" + }, + "id": "01938828-8662-7ac6-8e68-58c0cd035384", + "morType": { + "content": "Nonscalar", + "tag": "Basic" + }, + "name": "⋆₀⁻¹", + "tag": "morphism" + }, + { + "cod": { + "content": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc", + "tag": "Basic" + }, + "dom": { + "content": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc", + "tag": "Basic" + }, + "id": "01938828-bdf1-7d20-9dd7-7ad4c8dd78bf", + "morType": { + "content": "Nonscalar", + "tag": "Basic" + }, + "name": "Δ⁻¹", + "tag": "morphism" + }, + { + "cod": { + "content": "01938829-293b-726a-8e86-f7462a8f6f07", + "tag": "Basic" + }, + "dom": { + "content": "01938827-972c-7fd6-a4f7-bbf1e0ee52fc", + "tag": "Basic" + }, + "id": "01938829-0414-7b82-a0f9-f714d17e6ff4", + "morType": { + "content": "Nonscalar", + "tag": "Basic" + }, + "name": "d01", + "tag": "morphism" + }, + { + "id": "01938829-293b-726a-8e86-f7462a8f6f07", + "name": "Primal 1-Form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + { + "cod": { + "content": "01938827-ac3d-711a-a508-35087405d2eb", + "tag": "Basic" + }, + "dom": { + "content": "01938829-293b-726a-8e86-f7462a8f6f07", + "tag": "Basic" + }, + "id": "01938829-62c4-7897-b6d6-0a3563ee643d", + "morType": { + "content": "Nonscalar", + "tag": "Basic" + }, + "name": "⋆1", + "tag": "morphism" + }, + { + "cod": { + "content": "01938827-ac3d-711a-a508-35087405d2eb", + "tag": "Basic" + }, + "dom": { + "content": "01938827-ac3d-711a-a508-35087405d2eb", + "tag": "Basic" + }, + "id": "01938829-d95c-74be-ba4d-17a19a569a64", + "morType": { + "content": "Nonscalar", + "tag": "Basic" + }, + "name": "∧ᵈᵖ₁₀(-,⋆d(-))", + "tag": "morphism" + }, + { + "cod": { + "content": "01938827-b4c3-7996-850f-2fecb9dc5343", + "tag": "Basic" + }, + "dom": { + "content": "01938827-ac3d-711a-a508-35087405d2eb", + "tag": "Basic" + }, + "id": "0193882a-9357-7283-97e7-ad1171ae957e", + "morType": { + "content": "Nonscalar", + "tag": "Basic" + }, + "name": "d̃₁", + "tag": "morphism" + }, + { + "cod": { + "content": "01938829-293b-726a-8e86-f7462a8f6f07", + "tag": "Basic" + }, + "dom": { + "content": "01938827-ac3d-711a-a508-35087405d2eb", + "tag": "Basic" + }, + "id": "0193885a-f4e6-7dc9-9ca7-9b318d93b2c0", + "morType": { + "content": "Nonscalar", + "tag": "Basic" + }, + "name": "♭♯", + "tag": "morphism" + }, + { + "cod": { + "content": "01938827-b4c3-7996-850f-2fecb9dc5343", + "tag": "Basic" + }, + "dom": { + "content": "01938827-b4c3-7996-850f-2fecb9dc5343", + "tag": "Basic" + }, + "id": "0193886b-48c1-788c-9ec4-767eaa555fb6", + "morType": { + "content": "Nonscalar", + "tag": "Basic" + }, + "name": " ∂ₜ", + "tag": "morphism" + }, + { + "cod": { + "content": "01938827-b4c3-7996-850f-2fecb9dc5343", + "tag": "Basic" + }, + "dom": { + "content": "01938827-b4c3-7996-850f-2fecb9dc5343", + "tag": "Basic" + }, + "id": "01938dac-e35f-77ed-9081-6d926d3e366f", + "morType": { + "content": "Nonscalar", + "tag": "Basic" + }, + "name": "-", + "tag": "morphism" + } + ], + "scalars": {}, + "plotVariables": [ + "01938867-e117-7be0-b32a-1e4cd0af5cd9" + ] +} \ No newline at end of file diff --git a/packages/algjulia-service/test/runtests.jl b/packages/algjulia-service/test/runtests.jl index 5d665dce..5d195d28 100644 --- a/packages/algjulia-service/test/runtests.jl +++ b/packages/algjulia-service/test/runtests.jl @@ -172,7 +172,7 @@ model = data[:model]; end -@testset "Simulation ..." begin +@testset "Simulation 2" begin json_string = read(joinpath(@__DIR__, "diffusion_long_trip.json"), String); system = PodeSystem(json_string); @@ -233,7 +233,7 @@ scalars = data[:scalars]; end -@testset "Simulation ..." begin +@testset "Simulation 3" begin json_string = read(joinpath(@__DIR__, "diffusivity_constant.json"), String); system = PodeSystem(json_string); @@ -263,6 +263,36 @@ end end +@testset "Simulation from real front-end data" begin + + json_string = read(joinpath(@__DIR__, "ns_vorticity.json"), String); + system = PodeSystem(json_string); + + simulator = evalsim(system.pode) + # open("test_sim.jl", "w") do f + # write(f, string(gensim(system.pode))) + # end + # simulator = include("test_sim.jl"); + + f = simulator(system.dualmesh, system.generate, DiagonalHodge()); + + soln = run_sim(f, system.init, 50.0, ComponentArray(k=0.5,)); + # returns ::ODESolution + # - retcode + # - interpolation + # - t + # - u::Vector{ComponentVector} + + @test soln.retcode == ReturnCode.Success + + result = SimResult(soln, system); + + @test typeof(result.state) == Dict{String, Vector{Matrix{SVector{3, Float64}}}} + + jv = JsonValue(result); + +end + # PLOTTING UTILITIES # TODO size fixed diff --git a/packages/frontend/src/stdlib/analyses/decapodes.tsx b/packages/frontend/src/stdlib/analyses/decapodes.tsx index 655de0d6..dcd9a197 100644 --- a/packages/frontend/src/stdlib/analyses/decapodes.tsx +++ b/packages/frontend/src/stdlib/analyses/decapodes.tsx @@ -96,7 +96,7 @@ export function Decapodes(props: DiagramAnalysisProps) { if (!simulationData) { return undefined; } - + console.log(JSON.parse(JSON.stringify(simulationData))); // Request that the kernel run a simulation with the given data. const future = kernel.requestExecute({ code: makeSimulationCode(simulationData), @@ -281,7 +281,7 @@ using AlgebraicJuliaService /** Julia code run to perform a simulation. */ const makeSimulationCode = (data: SimulationData) => ` - system = only(PodeSystems(raw"""${JSON.stringify(data)}""")); + system = PodeSystem(raw"""${JSON.stringify(data)}"""); simulator = evalsim(system.pode); f = simulator(system.dualmesh, system.generate, DiagonalHodge()); From aa13f5af43f83d029dc5552d567c32515875330f Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Tue, 3 Dec 2024 22:18:29 -0700 Subject: [PATCH 11/32] ENH: Set geometric domain and mesh in frontend to Decapodes. --- packages/algjulia-service/src/decapodes.jl | 33 +++- .../src/components/fixed_table_editor.tsx | 111 ++++++++---- .../src/stdlib/analyses/decapodes.css | 6 + .../src/stdlib/analyses/decapodes.tsx | 169 +++++++++++++++--- 4 files changed, 258 insertions(+), 61 deletions(-) create mode 100644 packages/frontend/src/stdlib/analyses/decapodes.css diff --git a/packages/algjulia-service/src/decapodes.jl b/packages/algjulia-service/src/decapodes.jl index 147f8c08..be472210 100644 --- a/packages/algjulia-service/src/decapodes.jl +++ b/packages/algjulia-service/src/decapodes.jl @@ -22,7 +22,8 @@ using Distributions # for initial conditions using GeometryBasics: Point2, Point3 using OrdinaryDiffEq -export infer_types!, evalsim, default_dec_generate, default_dec_matrix_generate, DiagonalHodge, ComponentArray +export infer_types!, evalsim, default_dec_generate, default_dec_matrix_generate, + DiagonalHodge, ComponentArray struct ImplError <: Exception name::String @@ -30,6 +31,36 @@ end Base.showerror(io::IO, e::ImplError) = print(io, "$(e.name) not implemented") +## Geometry + +""" Supported domains. """ +const domain_names = [:Plane, :Sphere] + +""" Mapping from supported domains to meshes for the domain. """ +const mesh_names = Dict( + :Plane => [:Rectangle, :Periodic], + :Sphere => [:Icosphere6, :Icosphere7, :Icosphere8, :UV], +) + +""" Mapping from supported domains to initial/boundary conditions. """ +const initial_condition_names = Dict( + :Plane => [:Gaussian], + :Sphere => [:TaylorVortex, :SixVortex], +) + +""" Supported geometries, in the JSON format expected by the frontend. """ +function supported_decapodes_geometries() + domains = map(domain_names) do domain + Dict( + :name => domain, + :meshes => mesh_names[domain], + :initialConditions => initial_condition_names[domain], + ) + end + Dict(:domains => domains) +end +export supported_decapodes_geometries + ## THEORY BUILDING """ Functions to build a dictionary associating ids in the theory to elements in the model""" diff --git a/packages/frontend/src/components/fixed_table_editor.tsx b/packages/frontend/src/components/fixed_table_editor.tsx index 3f58ce68..765c1731 100644 --- a/packages/frontend/src/components/fixed_table_editor.tsx +++ b/packages/frontend/src/components/fixed_table_editor.tsx @@ -3,7 +3,7 @@ import { For, Match, Show, Switch, createEffect, createSignal } from "solid-js"; import "./fixed_table_editor.css"; -type ContentType = "string" | "boolean"; +type ContentType = "string" | "boolean" | "enum"; type BaseColumnSchema = { /** Type of content displayed in the column. */ @@ -18,12 +18,6 @@ type BaseColumnSchema = { /** Content of the column at the given row. */ content: (row: Row) => Content; - /** Is the text valid as content for the column at the given row? - - If not specified, any content is considered valid. - */ - validate?: (row: Row, content: Content) => boolean; - /** Sets the content for the columns at the given row, if possible. Returns whether setting the content was successful. If not specified, the @@ -32,18 +26,44 @@ type BaseColumnSchema = { setContent?: (row: Row, content: Content) => boolean; }; -/** Schema for a text column in a table editor. */ +/** Schema for a text column in a table editor. + +Each cell editor is a text input field. + */ export type TextColumnSchema = BaseColumnSchema & { contentType: "string"; + + /** Is the text valid as content for the column at the given row? + + If not specified, any content is considered valid. + */ + validate?: (row: Row, text: string) => boolean; }; -/** Schema for a boolean column in a table editor. */ +/** Schema for a boolean column in a table editor. + +Each cell editor is a checkbox. + */ export type BooleanColumnSchema = BaseColumnSchema & { contentType: "boolean"; }; +/** Schema for an enum column in a table editor. + +Each cell editor is a select box. The enum variants are assumed to be strings. + */ +export type EnumColumnSchema = BaseColumnSchema & { + contentType: "enum"; + + /** List of variants comprising the enum. */ + variants: (row: Row) => string[]; +}; + /** Schema for a column in a table editor. */ -export type ColumnSchema = TextColumnSchema | BooleanColumnSchema; +export type ColumnSchema = + | TextColumnSchema + | BooleanColumnSchema + | EnumColumnSchema; /** Create schema for a column with numerical (floating point) data. */ export const createNumericalColumn = (args: { @@ -57,7 +77,7 @@ export const createNumericalColumn = (args: { contentType: "string", name: args.name, header: args.header, - content: (row) => { + content(row) { let value = args.data(row); if (value === undefined) { value = args.default ?? 0; @@ -65,7 +85,7 @@ export const createNumericalColumn = (args: { } return value.toString(); }, - validate: (row, text) => { + validate(row, text) { const parsed = Number(text); return !Number.isNaN(parsed) && (args.validate?.(row, parsed) ?? true); }, @@ -130,18 +150,18 @@ function Cell(props: { row: Row; schema: ColumnSchema; }) { - const { row, schema } = destructure(props); return ( - - - - {(schema) => } - - - {(schema) => } - - - + + + {(schema) => } + + + {(schema) => } + + + {(schema) => } + + ); } @@ -164,17 +184,19 @@ function TextCellEditor(props: { createEffect(() => setIsValid(schema().validate?.(row(), text()) ?? true)); return ( - setText(evt.target.value)} - onChange={(evt) => applyText(evt.target.value)} - /> + + setText(evt.target.value)} + onChange={(evt) => applyText(evt.target.value)} + /> + ); } @@ -189,7 +211,28 @@ function BooleanCellEditor(props: { class="fixed-table-cell-input" type="checkbox" checked={schema().content(row())} + disabled={schema().setContent === undefined} onInput={(evt) => schema().setContent?.(row(), evt.currentTarget.checked)} /> ); } + +function EnumCellEditor(props: { + row: Row; + schema: EnumColumnSchema; +}) { + const { row, schema } = destructure(props); + + return ( + + ); +} diff --git a/packages/frontend/src/stdlib/analyses/decapodes.css b/packages/frontend/src/stdlib/analyses/decapodes.css new file mode 100644 index 00000000..8423fd79 --- /dev/null +++ b/packages/frontend/src/stdlib/analyses/decapodes.css @@ -0,0 +1,6 @@ +.decapodes-domain { + display: flex; + flex-direction: row; + gap: 1ex; + margin-bottom: 1ex; +} diff --git a/packages/frontend/src/stdlib/analyses/decapodes.tsx b/packages/frontend/src/stdlib/analyses/decapodes.tsx index dcd9a197..68b7e015 100644 --- a/packages/frontend/src/stdlib/analyses/decapodes.tsx +++ b/packages/frontend/src/stdlib/analyses/decapodes.tsx @@ -1,5 +1,5 @@ import type { IReplyErrorContent } from "@jupyterlab/services/lib/kernel/messages"; -import { Match, Switch, createMemo, createResource, onCleanup } from "solid-js"; +import { For, Match, Show, Switch, createMemo, createResource, onCleanup } from "solid-js"; import { isMatching } from "ts-pattern"; import type { DiagramAnalysisProps } from "../../analysis"; @@ -20,18 +20,23 @@ import { } from "../../diagram"; import type { ModelJudgment, MorphismDecl } from "../../model"; import type { DiagramAnalysisMeta } from "../../theory"; +import { uniqueIndexArray } from "../../util/indexing"; import { PDEPlot2D, type PDEPlotData2D } from "../../visualization"; import Loader from "lucide-solid/icons/loader"; import RotateCcw from "lucide-solid/icons/rotate-ccw"; import baseStyles from "./base_styles.module.css"; +import "./decapodes.css"; import "./simulation.css"; /** Configuration for a Decapodes analysis of a diagram. */ export type DecapodesContent = JupyterSettings & { - scalars: Record; + domain: string | null; + mesh: string | null; + initialConditions: Record; plotVariables: Record; + scalars: Record; }; type JupyterSettings = { @@ -55,8 +60,11 @@ export function configureDecapodes(options: { description, component: (props) => , initialContent: () => ({ - scalars: {}, + domain: null, + mesh: null, + initialConditions: {}, plotVariables: {}, + scalars: {}, }), }; } @@ -64,6 +72,7 @@ export function configureDecapodes(options: { /** Analyze a DEC diagram by performing a simulation using Decapodes.jl. */ export function Decapodes(props: DiagramAnalysisProps) { + // Step 1: Start the Julia kernel. const [kernel, { refetch: restartKernel }] = createResource(async () => { const jupyter = await import("@jupyterlab/services"); @@ -75,22 +84,45 @@ export function Decapodes(props: DiagramAnalysisProps) { const kernelManager = new jupyter.KernelManager({ serverSettings }); const kernel = await kernelManager.startNew({ name: "julia-1.11" }); + return kernel; + }); + + onCleanup(() => kernel()?.shutdown()); + + // Step 2: Run initialization code in the kernel. + const startedKernel = () => (kernel.error ? undefined : kernel()); + + const [options] = createResource(startedKernel, async (kernel) => { + // Request that the kernel run code to initialize the service. const future = kernel.requestExecute({ code: initCode }); - const reply = await future.done; + // Look for simulation options as output from the kernel. + let options: SimulationOptions | undefined; + future.onIOPub = (msg) => { + if (msg.header.msg_type === "execute_result") { + const content = msg.content as JsonDataContent; + options = content["data"]?.["application/json"]; + } + }; + + const reply = await future.done; if (reply.content.status === "error") { await kernel.shutdown(); throw new Error(formatError(reply.content)); } - - return kernel; + if (!options) { + throw new Error("Allowed options not received after initialization"); + } + return { + domains: uniqueIndexArray(options.domains, (domain) => domain.name), + }; }); - onCleanup(() => kernel()?.shutdown()); - - const maybeKernel = () => (kernel.error ? undefined : kernel()); + // Step 3: Run the simulation in the kernel! + const initedKernel = () => + kernel.error || options.error || options.loading ? undefined : kernel(); - const [result, { refetch: rerunSimulation }] = createResource(maybeKernel, async (kernel) => { + const [result, { refetch: rerunSimulation }] = createResource(initedKernel, async (kernel) => { // Construct the data to send to kernel. const simulationData = makeSimulationData(props.liveDiagram, props.content); if (!simulationData) { @@ -102,7 +134,7 @@ export function Decapodes(props: DiagramAnalysisProps) { code: makeSimulationCode(simulationData), }); - // Handle output from the kernel. + // Look for simulation results as output from the kernel. let result: PDEPlotData2D | undefined; future.onIOPub = (msg) => { if ( @@ -161,7 +193,7 @@ export function Decapodes(props: DiagramAnalysisProps) { }), ]; - const plotVariableSchema: ColumnSchema[] = [ + const variableSchema: ColumnSchema[] = [ { contentType: "string", header: true, @@ -181,17 +213,17 @@ export function Decapodes(props: DiagramAnalysisProps) { }, ]; - const header = () => ( + const Header = () => (
Simulation - + - + ) {
); + const DomainConfig = (domains: Map) => ( +
+ Domain: + + + {(name) => ( + <> + Mesh: + + + )} + +
+ ); + return (
- + + {(options) => DomainConfig(options().domains)}
+ -
- {"Loading the AlgebraicJulia service..."} + + {"Loading the AlgebraicJulia service..."} + {(error) => ( - + +
{error().message}
+
+ )} +
+ + {(error) => ( +
{error().message}
)} @@ -255,7 +334,25 @@ type JsonDataContent = { }; }; -/** Data send to the Julia kernel defining a simulation. */ +/** Options supported by Decapodes, defined by the Julia service. */ +type SimulationOptions = { + /** Geometric domains supported by Decapodes. */ + domains: Domain[]; +}; + +/** A geometric domain and its allow discretizations. */ +type Domain = { + /** Name of the domain. */ + name: string; + + /** Supported meshes that discretize the domain. */ + meshes: string[]; + + /** Initial/boundary conditions supported for the domain. */ + initialConditions: string[]; +}; + +/** Data sent to the Julia kernel defining a simulation. */ type SimulationData = { /** Judgments defining the diagram, including inferred ones. */ diagram: Array; @@ -263,11 +360,20 @@ type SimulationData = { /** Judgments defining the model. */ model: Array; - /** Mapping from IDs of scalar operations to numerical values. */ - scalars: Record; + /** The geometric domain to use for the simulation. */ + domain: string; - /** Variables to plot. */ + /** The mesh to use for the simulation. */ + mesh: string; + + /** Mapping from variable UUIDs to enum variants for initital conditions. */ + initialConditions: Record; + + /** Variables to plot, a list of UUIDs. */ plotVariables: Array; + + /** Mapping from UIIDs of scalar operations to numerical values. */ + scalars: Record; }; /** Julia code run after kernel is started. */ @@ -276,10 +382,12 @@ import IJulia IJulia.register_jsonmime(MIME"application/json"()) using AlgebraicJuliaService + +JsonValue(supported_decapodes_geometries()) `; /** Julia code run to perform a simulation. */ -const makeSimulationCode = (data: SimulationData) => +const makeSimulationCode = (data: SimulationData) => ` system = PodeSystem(raw"""${JSON.stringify(data)}"""); simulator = evalsim(system.pode); @@ -300,12 +408,21 @@ const makeSimulationData = ( if (validatedDiagram?.result.tag !== "Ok") { return undefined; } + + const { domain, mesh, initialConditions, plotVariables, scalars } = content; + if (domain === null || mesh === null) { + return undefined; + } + return { diagram: fromCatlogDiagram(validatedDiagram.diagram, (id) => liveDiagram.objectIndex().map.get(id), ), model: liveDiagram.liveModel.formalJudgments(), - scalars: content.scalars, - plotVariables: Object.keys(content.plotVariables).filter((v) => content.plotVariables[v]), + domain, + mesh, + initialConditions, + plotVariables: Object.keys(plotVariables).filter((v) => plotVariables[v]), + scalars, }; }; From c863426d12887e5c5453fe773eb5966dc0ae68a0 Mon Sep 17 00:00:00 2001 From: Kevin Carlson Date: Tue, 3 Dec 2024 22:36:47 -0700 Subject: [PATCH 12/32] get rid of spurious init thing --- packages/algjulia-service/src/decapodes.jl | 2 +- packages/algjulia-service/test/ns_vorticity.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/algjulia-service/src/decapodes.jl b/packages/algjulia-service/src/decapodes.jl index be472210..ce92bf63 100644 --- a/packages/algjulia-service/src/decapodes.jl +++ b/packages/algjulia-service/src/decapodes.jl @@ -344,7 +344,7 @@ function PodeSystem(json_string::String,hodge=GeometricHodge()) # mesh and initial conditions s, sd = create_mesh() - u0 = init_conditions([infer_state_names(decapode) ; plotvars], sd) + u0 = init_conditions(infer_state_names(decapode), sd) ♭♯_m = ♭♯_mat(sd); wedge_dp10 = dec_wedge_product_dp(Tuple{1,0}, sd); diff --git a/packages/algjulia-service/test/ns_vorticity.json b/packages/algjulia-service/test/ns_vorticity.json index ecf6654e..dcb8205d 100644 --- a/packages/algjulia-service/test/ns_vorticity.json +++ b/packages/algjulia-service/test/ns_vorticity.json @@ -534,6 +534,6 @@ ], "scalars": {}, "plotVariables": [ - "01938867-e117-7be0-b32a-1e4cd0af5cd9" + "01938fb0-449c-7614-8086-2384cf6bc784" ] } \ No newline at end of file From 18c2ea31a8ad9761152de1c43d61119b697752d6 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 4 Dec 2024 08:55:05 -0500 Subject: [PATCH 13/32] wip mesh/initial conditions interop --- packages/algjulia-service/src/decapodes.jl | 58 ++++++++++++++++++---- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/packages/algjulia-service/src/decapodes.jl b/packages/algjulia-service/src/decapodes.jl index ce92bf63..c786d4e4 100644 --- a/packages/algjulia-service/src/decapodes.jl +++ b/packages/algjulia-service/src/decapodes.jl @@ -61,6 +61,54 @@ function supported_decapodes_geometries() end export supported_decapodes_geometries +abstract type Domain end + +# meshes associated with Planes +@data Planar <: Domain begin + Rectangle(max_x::Int, max_y::Int, dx::Float64, dy::Float64) + Periodic +end + +# meshes associated with Spheres +@data Spherical <: Domain begin + Sphere(dim::Int, radius::Float64) # no default arguments in MLStyle + UV +end + +function create_mesh end; export create_mesh + +function create_mesh(r::Rectangle, division::SimplexCenter=Circumcenter()) + s = triangulated_grid(r.max_x, r.max_y, r.dx, r.dy, Point2{Float64}) + sd = EmbeddedDeltaDualComplex2D{Bool, Float64, Point2{Float64}}(s) + subdivide_duals!(sd, division) + return (s, d) +end + +function create_mesh(r::Periodic, division::SimplexCenter=Circumcenter()) end + +function create_mesh(s::Sphere, division::SimplexCenter=Circumcenter()) + s = loadmesh(Icosphere(s.dim, s.radius)); + sd = EmbeddedDeltaDualComplex2D{Bool, Float64, Point2{Float64}}(s) + subdivide_duals!(sd, division) + return (s, sd) +end + +function create_mesh(s::UV, division::SimplexCenter=Circumcenter()) + s, _, _ = makeSphere(0, 180, 2.5, 0, 360, 2.5, RADIUS); + sd = EmbeddedDeltaDualComplex2D{Bool, Float64, Point2{Float64}}(s) + subdivide_duals!(sd, division) + return (s, sd) +end + +# XXX old function to be deprecated +function create_mesh() + s = triangulated_grid(100,100,2,2,Point2{Float64}) + sd = EmbeddedDeltaDualComplex2D{Bool, Float64, Point2{Float64}}(s) + subdivide_duals!(sd, Circumcenter()) + return (s, sd) +end +export create_mesh + ## THEORY BUILDING """ Functions to build a dictionary associating ids in the theory to elements in the model""" @@ -288,16 +336,6 @@ end export Decapode # the proper name for this constructor should be "SummationDecapode" -# TODO we need to make this dynamic -function create_mesh() - s = triangulated_grid(100,100,2,2,Point2{Float64}) - sd = EmbeddedDeltaDualComplex2D{Bool, Float64, Point2{Float64}}(s) - subdivide_duals!(sd, Circumcenter()) - - return (s, sd) -end -export create_mesh - function init_conditions(vars::Vector{Symbol}, sd::HasDeltaSet) c_dist = MvNormal([50, 50], LinearAlgebra.Diagonal(map(abs2, [7.5, 7.5]))) c = [pdf(c_dist, [p[1], p[2]]) for p in sd[:point]] From af81608a0e2b3644cd080dd71094248358b3e5a8 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 4 Dec 2024 11:02:22 -0500 Subject: [PATCH 14/32] wip moved geometry+init to new file, wrote some functions wip haven't tested but precompiles successfully --- packages/algjulia-service/Project.toml | 2 - packages/algjulia-service/src/decapodes.jl | 94 ++-------- .../algjulia-service/src/geometry_and_init.jl | 171 ++++++++++++++++++ 3 files changed, 186 insertions(+), 81 deletions(-) create mode 100644 packages/algjulia-service/src/geometry_and_init.jl diff --git a/packages/algjulia-service/Project.toml b/packages/algjulia-service/Project.toml index 73f49961..f991b4fe 100644 --- a/packages/algjulia-service/Project.toml +++ b/packages/algjulia-service/Project.toml @@ -8,7 +8,6 @@ version = "0.1.0" ACSets = "227ef7b5-1206-438b-ac65-934d6da304b8" CombinatorialSpaces = "b1c52339-7909-45ad-8b6a-6e388f7c67f2" ComponentArrays = "b0b7db55-cfe3-40fc-9ded-d10e2dbeff66" -Debugger = "31a5f54b-26ea-5ae9-a837-f05ce5417438" Decapodes = "679ab3ea-c928-4fe6-8d59-fd451142d391" DiagrammaticEquations = "6f00c28b-6bed-4403-80fa-30e0dc12f317" Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" @@ -23,7 +22,6 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" [compat] ACSets = "0.2.21" ComponentArrays = "0.15" -Debugger = "0.7.10" Decapodes = "0.5.6" DiagrammaticEquations = "0.1.7" Distributions = "0.25" diff --git a/packages/algjulia-service/src/decapodes.jl b/packages/algjulia-service/src/decapodes.jl index c786d4e4..66852184 100644 --- a/packages/algjulia-service/src/decapodes.jl +++ b/packages/algjulia-service/src/decapodes.jl @@ -31,83 +31,8 @@ end Base.showerror(io::IO, e::ImplError) = print(io, "$(e.name) not implemented") -## Geometry - -""" Supported domains. """ -const domain_names = [:Plane, :Sphere] - -""" Mapping from supported domains to meshes for the domain. """ -const mesh_names = Dict( - :Plane => [:Rectangle, :Periodic], - :Sphere => [:Icosphere6, :Icosphere7, :Icosphere8, :UV], -) - -""" Mapping from supported domains to initial/boundary conditions. """ -const initial_condition_names = Dict( - :Plane => [:Gaussian], - :Sphere => [:TaylorVortex, :SixVortex], -) - -""" Supported geometries, in the JSON format expected by the frontend. """ -function supported_decapodes_geometries() - domains = map(domain_names) do domain - Dict( - :name => domain, - :meshes => mesh_names[domain], - :initialConditions => initial_condition_names[domain], - ) - end - Dict(:domains => domains) -end -export supported_decapodes_geometries - -abstract type Domain end - -# meshes associated with Planes -@data Planar <: Domain begin - Rectangle(max_x::Int, max_y::Int, dx::Float64, dy::Float64) - Periodic -end - -# meshes associated with Spheres -@data Spherical <: Domain begin - Sphere(dim::Int, radius::Float64) # no default arguments in MLStyle - UV -end - -function create_mesh end; export create_mesh - -function create_mesh(r::Rectangle, division::SimplexCenter=Circumcenter()) - s = triangulated_grid(r.max_x, r.max_y, r.dx, r.dy, Point2{Float64}) - sd = EmbeddedDeltaDualComplex2D{Bool, Float64, Point2{Float64}}(s) - subdivide_duals!(sd, division) - return (s, d) -end - -function create_mesh(r::Periodic, division::SimplexCenter=Circumcenter()) end - -function create_mesh(s::Sphere, division::SimplexCenter=Circumcenter()) - s = loadmesh(Icosphere(s.dim, s.radius)); - sd = EmbeddedDeltaDualComplex2D{Bool, Float64, Point2{Float64}}(s) - subdivide_duals!(sd, division) - return (s, sd) -end - -function create_mesh(s::UV, division::SimplexCenter=Circumcenter()) - s, _, _ = makeSphere(0, 180, 2.5, 0, 360, 2.5, RADIUS); - sd = EmbeddedDeltaDualComplex2D{Bool, Float64, Point2{Float64}}(s) - subdivide_duals!(sd, division) - return (s, sd) -end - -# XXX old function to be deprecated -function create_mesh() - s = triangulated_grid(100,100,2,2,Point2{Float64}) - sd = EmbeddedDeltaDualComplex2D{Bool, Float64, Point2{Float64}}(s) - subdivide_duals!(sd, Circumcenter()) - return (s, sd) -end -export create_mesh +# funcitons for geometry and initial conditions +include("geometry_and_init.jl") ## THEORY BUILDING @@ -349,7 +274,7 @@ struct PodeSystem scalars::Dict{Symbol, Any} # closures # TODO rename scalars => anons mesh::HasDeltaSet dualmesh::HasDeltaSet - init::Any # TODO specify. Is it always ComponentVector? + init::ComponentArray # TODO Is it always ComponentVector? generate::Any uuiddict::Dict{Symbol, String} end @@ -380,16 +305,27 @@ function PodeSystem(json_string::String,hodge=GeometricHodge()) # plotting variables plotvars = [uuid2symb[uuid] for uuid in json_object[:plotVariables]]; - # mesh and initial conditions + # mesh + # TODO pass the domain, mesh data s, sd = create_mesh() + + # initial conditions + # ic_specs = json_object[:initialConditions]; + # icdata = []; + # u0 = initial_conditions( + # # uuid2symb[u + # Dict([uuid2symb[uuid] => icdata[ic_specs[uuid]] for uuid ∈ keys(ic_specs)]...), + # sd) u0 = init_conditions(infer_state_names(decapode), sd) + # initialize operators ♭♯_m = ♭♯_mat(sd); wedge_dp10 = dec_wedge_product_dp(Tuple{1,0}, sd); dual_d1_m = dec_mat_dual_differential(1, sd); star0_inv_m = dec_mat_inverse_hodge(0, sd, hodge) Δ0 = Δ(0,sd); fΔ0 = factorize(Δ0); + # end initialize function sys_generate(s, my_symbol,hodge=hodge) op = @match my_symbol begin diff --git a/packages/algjulia-service/src/geometry_and_init.jl b/packages/algjulia-service/src/geometry_and_init.jl new file mode 100644 index 00000000..4847c5cf --- /dev/null +++ b/packages/algjulia-service/src/geometry_and_init.jl @@ -0,0 +1,171 @@ +# TODO UUID => :Gaussian w default method + +## INTEROP + +""" Supported domains. """ +const domain_names = [:Plane, :Sphere] + +""" Mapping from supported domains to meshes for the domain. """ +const mesh_names = Dict( + :Plane => [:Rectangle, :Periodic], + :Sphere => [:Icosphere6, :Icosphere7, :Icosphere8, :UV], +) + +""" Mapping from supported domains to initial/boundary conditions. """ +const initial_condition_names = Dict( + :Plane => [:Gaussian], + :Sphere => [:TaylorVortex, :SixVortex], +) + +""" Supported geometries, in the JSON format expected by the frontend. """ +function supported_decapodes_geometries() + domains = map(domain_names) do domain + Dict( + :name => domain, + :meshes => mesh_names[domain], + :initialConditions => initial_condition_names[domain], + ) + end + Dict(:domains => domains) +end +export supported_decapodes_geometries + +## GEOMETRY + +abstract type Domain end + +# meshes associated with Planes +@data Planar <: Domain begin + Rectangle(max_x::Int, max_y::Int, dx::Float64, dy::Float64) + Periodic # TODO +end + +Rectangle() = Rectangle(500, 5000, 1, 1); + +middle(r::Rectangle) = [r.max_x/2, r.max_y/2] + +# meshes associated with Spheres +@data Spherical <: Domain begin + Sphere(dim::Int, radius::Float64) + UV(minlat::Int, maxlat::Int, dlat::Float64, minlong::Int, maxlong::Int, dlong::Float64, radius::Float64) +end + +Sphere(dim) = Sphere(dim, 1.0) +# TODO need default method for UV + +""" helper function for UV """ +function makeSphere(m::UV) + makeSphere(m.minlat, m.maxlat, m.dlat, m.minlong, m.maxlong, m.dlong, m.radius) +end + +function create_mesh end; export create_mesh + +function create_mesh(d::Domain, args...) + throw(ImplError("The mesh ($(d)) is")) +end + +function create_mesh(r::Rectangle, division::SimplexCenter=Circumcenter()) + s = triangulated_grid(r.max_x, r.max_y, r.dx, r.dy, Point2{Float64}) + sd = EmbeddedDeltaDualComplex2D{Bool, Float64, Point2{Float64}}(s) + subdivide_duals!(sd, division) + return (s, d) +end + +# function create_mesh(r::Periodic, division::SimplexCenter=Circumcenter()) +# end + +function create_mesh(m::Sphere, division::SimplexCenter=Circumcenter()) + s = loadmesh(Icosphere(m.dim, m.radius)); + sd = EmbeddedDeltaDualComplex2D{Bool, Float64, Point2{Float64}}(s) + subdivide_duals!(sd, division) + return (s, sd) +end + +function create_mesh(m::UV, division::SimplexCenter=Circumcenter()) + s, _, _ = makeSphere(m); + sd = EmbeddedDeltaDualComplex2D{Bool, Float64, Point3{Float64}}(s) + subdivide_duals!(sd, division) + return (s, sd) +end + +# XXX old function to be deprecated +function create_mesh() + s = triangulated_grid(100,100,2,2,Point2{Float64}) + sd = EmbeddedDeltaDualComplex2D{Bool, Float64, Point2{Float64}}(s) + subdivide_duals!(sd, Circumcenter()) + return (s, sd) +end +export create_mesh + +## INITIAL CONDITIONS + +# TODO we want a mapping between symbols and their initial conditions, which may vary. +# i.e. Dict{Symbol, InitialCondition}. This first pass assumes the inverse; InitialConditions carry a reference to their variables. This is a little inflexible. + +@data InitialConditionsData begin + GaussianData(μ::Vector{Float64}, Σ::Diagonal{Float64, Vector{Float64}}) +end + +function GaussianData(μ::Vector{Float64}, Σ::Vector{Float64}) + GaussianData(μ, LinearAlgebra.Diagonal(abs.(Σ))) +end + +# DEFAULT METHOD. A Moshi-like Default impl. would be nice! +function GaussianData(r::Rectangle) + μ = middle(r) + GaussianData(μ, μ/10) +end + +Distributions.MvNormal(ξ::GaussianData) = MvNormal(ξ.μ, ξ.Σ) + +abstract type InitialConditionData end + +@data PlanarIC <: InitialConditionData begin + GaussianIC(r::Rectangle, ξ::GaussianData) +end + +# DEFAULT METHOD +GaussianIC(r::Rectangle) = GaussianIC(r, GaussianData(r)) + +@data SphericalIC <: InitialConditionData begin + TaylorVortexIC(m::Sphere, data::Any) + SixVortexIC(m::Sphere, data::Any) +end + +function initial_conditions end; export initial_conditions + +# TODO aspirational method +function initial_conditions(initial_conditions::Dict{String, Symbol}, uuid2symb::Dict{String, Symbol}, sd::HasDeltaSet) + # convert to Dict +end + +function initial_conditions(ics::Dict{Symbol, InitialConditionData}, sd::HasDeltaSet) + u0 = ComponentArray(; Dict([ + var => initial_conditions(ics[var], sd) for var ∈ keys(ics) + ])...) + return u0 +end + +function initial_conditions(x::InitialConditionData, args...) + throw(ImplError("These initial conditions ($(x)) are")) +end + +function initial_conditions(ics::GaussianIC, sd::HasDeltaSet) + c_dist = MvNormal(ics) + c = [pdf(c_dist, [p[1], p[2]]) for p ∈ sd[:point]] + return c +end + +function initial_conditions(ics::TaylorVortexIC, sd::HasDeltaSet) end + +function initial_conditions(ics::SixVortexIC, sd::HasDeltaSet) end + + + +# XXX +# function init_conditions(vars::Vector{Symbol}, sd::HasDeltaSet) +# c_dist = MvNormal([50, 50], LinearAlgebra.Diagonal(map(abs2, [7.5, 7.5]))) +# c = [pdf(c_dist, [p[1], p[2]]) for p in sd[:point]] +# u0 = ComponentArray(; Dict([var=>c for var in vars])...) +# return u0 +# end From 43c4dd51c76682dcac89f3019920361136d51a36 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Wed, 4 Dec 2024 10:32:03 -0700 Subject: [PATCH 15/32] ENH: Send choices of initial condition to Decapodes service. --- .../src/components/fixed_table_editor.tsx | 60 +++++++++++++------ .../src/stdlib/analyses/decapodes.tsx | 33 ++++++++-- 2 files changed, 71 insertions(+), 22 deletions(-) diff --git a/packages/frontend/src/components/fixed_table_editor.tsx b/packages/frontend/src/components/fixed_table_editor.tsx index 765c1731..2545d186 100644 --- a/packages/frontend/src/components/fixed_table_editor.tsx +++ b/packages/frontend/src/components/fixed_table_editor.tsx @@ -5,7 +5,7 @@ import "./fixed_table_editor.css"; type ContentType = "string" | "boolean" | "enum"; -type BaseColumnSchema = { +type BaseColumnSchema = { /** Type of content displayed in the column. */ contentType: ContentType; @@ -14,49 +14,67 @@ type BaseColumnSchema = { /** Is the column a header? */ header?: boolean; - - /** Content of the column at the given row. */ - content: (row: Row) => Content; - - /** Sets the content for the columns at the given row, if possible. - - Returns whether setting the content was successful. If not specified, the - column is not editable. - */ - setContent?: (row: Row, content: Content) => boolean; }; /** Schema for a text column in a table editor. Each cell editor is a text input field. */ -export type TextColumnSchema = BaseColumnSchema & { +export type TextColumnSchema = BaseColumnSchema & { contentType: "string"; + /** Text content of the column at the given row. */ + content: (row: Row) => string; + /** Is the text valid as content for the column at the given row? If not specified, any content is considered valid. */ validate?: (row: Row, text: string) => boolean; + + /** Sets the content for the column at the given row, if possible. + + Returns whether setting the content was successful. If not specified, the + column is not editable. + */ + setContent?: (row: Row, content: string) => boolean; }; /** Schema for a boolean column in a table editor. Each cell editor is a checkbox. */ -export type BooleanColumnSchema = BaseColumnSchema & { +export type BooleanColumnSchema = BaseColumnSchema & { contentType: "boolean"; + + /** Content of the column at the given row, if defined. */ + content: (row: Row) => boolean | null; + + /** Sets the content for the column at the given row. + + If not specified, the column is not editable. + */ + setContent?: (row: Row, content: boolean) => void; }; /** Schema for an enum column in a table editor. Each cell editor is a select box. The enum variants are assumed to be strings. */ -export type EnumColumnSchema = BaseColumnSchema & { +export type EnumColumnSchema = BaseColumnSchema & { contentType: "enum"; /** List of variants comprising the enum. */ variants: (row: Row) => string[]; + + /** Content of the column at the given row, if defined. */ + content: (row: Row) => string | null; + + /** Sets the content for the column at the given row. + + If not specified, the column is not editable. + */ + setContent?: (row: Row, content: string | null) => void; }; /** Schema for a column in a table editor. */ @@ -210,7 +228,7 @@ function BooleanCellEditor(props: { schema().setContent?.(row(), evt.currentTarget.checked)} /> @@ -226,10 +244,18 @@ function EnumCellEditor(props: { return (