Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
98ab79e
Add initial version of react explorer
davidanthoff Jun 23, 2020
314a544
Add build outputs
davidanthoff Jun 23, 2020
dd17ee4
Merge branch 'master' into reactexplorer
lrennels Jul 11, 2020
85f323b
Merge branch 'master' into reactexplorer
lrennels Jul 30, 2020
96902f7
Update Electron compat bounds
lrennels Jul 31, 2020
1850931
displaying the tabs with mimi UI styling
arnavgautam Aug 2, 2020
d164c26
recreating full functionality of old explorer
arnavgautam Aug 2, 2020
90df9af
adding a menu item for subcomponents, which prints to console the com…
arnavgautam Aug 3, 2020
a46e216
nothing renders, but nothing errors when using the ExplorerTreeView
arnavgautam Aug 11, 2020
c0cd2b5
Merge master
lrennels Aug 13, 2020
dc078a2
Edit Electron compat bounds
lrennels Aug 13, 2020
528ad13
Update Project toml
lrennels Aug 13, 2020
da7945c
Add WIP access file and some methods
lrennels Aug 15, 2020
862f2b2
Add comments
lrennels Aug 15, 2020
3ba0118
Separate Subcomponent and Values views
arnavgautam Sep 4, 2020
f8702b3
a working dynamic tree view and data view for the subcomponents
arnavgautam Sep 4, 2020
13f1493
fixed subcomponent path issues, added some styling
arnavgautam Oct 2, 2020
d6017ac
A bit more styling
arnavgautam Oct 2, 2020
a694b27
Merge branch 'master' into reactexplorer
lrennels Oct 7, 2020
97c1cb9
Various small updates and bug fixes
lrennels Oct 7, 2020
cb32340
Fix tests
lrennels Oct 8, 2020
61a5dd6
Merge msater
lrennels Nov 21, 2020
e4b661e
Various improvements to explorer
lrennels Nov 21, 2020
f8bd259
Fix test failures for warnings
lrennels Nov 21, 2020
f46332a
More stylistic improvements
lrennels Nov 21, 2020
01c094c
Make data items buttons
lrennels Nov 21, 2020
747c622
Update the documentation
lrennels Nov 23, 2020
16b9db2
Remove title keyword argument
lrennels Nov 23, 2020
e55357a
Fix broken test
lrennels Nov 23, 2020
9ceb00f
Remove title keyword arg from tests
lrennels Nov 23, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,9 @@ benchmark/tune.json
docs/Manifest.toml
types-old.jl
.vscode
src/explorer/mimiexplorer-app/node_modules
src/explorer/mimiexplorer-app/.pnp
src/explorer/mimiexplorer-app/.pnp.js

# testing
src/explorer/mimiexplorer-app/coverage
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ MetaGraphs = "0.6"
VegaLite = "1, 2"
TableTraits = "0.4.1, 1"
Classes = "1.2"
Electron = "3.1"
IterTools = "1.3"
NamedArrays = "0.9"
IteratorInterfaceExtensions = "0.1.1, 1"
Expand All @@ -70,7 +71,6 @@ FileIO = "1"
julia = "1.4"
GlobalSensitivityAnalysis = "1.0"
DataFrames = "0.19.1, 0.20, 0.21"
Electron = "1.1, 2.0, 3.0"
FilePaths = "0.8"

[targets]
Expand Down
Binary file modified docs/src/figs/explorer_model_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/src/howto/howto_2.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ Mimi provides support for plotting using [VegaLite](https://github.com/vega/vega

Plotting support is provided by the **Explorer UI**, rooted in `VegaLite`. The `explore` function allows the user to view and explore the variables and parameters of a model run. The explorer can be used in two primary ways.

In order to invoke the explorer UI and explore all of the variables and parameters in a model, simply call the function `explore` with the model run as the required argument, and a window title as an optional keyword argument, as shown below. This will produce a new browser window containing a selectable list of parameters and variables, organized by component, each of which produces a graphic. The exception here being that if the parameter or variable is a single scalar value, the value will appear alongside the name in the left-hand list.
In order to invoke the explorer UI and explore all of the variables and parameters in a model, simply call the function `explore` with the model run as the required argument as shown below. This will produce a new browser window containing a selectable list of parameters and variables, organized by component, each of which produces a graphic. The exception here being that if the parameter or variable is a single scalar value, the value will appear alongside the name in the left-hand list.

```julia
run(m)
explore(m, title = "run1 results")
explore(m)
```

![Explorer Model Example](../figs/explorer_model_example.png)
Expand Down
4 changes: 2 additions & 2 deletions docs/src/tutorials/tutorial_2.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,10 @@ Mimi provides support for plotting using [VegaLite](https://github.com/vega/vega

If you wish to explore the results graphically, use the explorer UI. This functionality is described in more detail in the second how-to guide, How-to Guide 2: View and Explore Model Results. For now, however, you don't need this level of detail and can simply follow the steps below.

To explore all variables and parameters of FUND in a dynamic UI app window, use the [`explore`](@ref) function called with the model as the required first argument, and the optional argument of the `title` The menu on the left hand side will list each element in a label formatted as `component: variable/parameter`.
To explore all variables and parameters of FUND in a dynamic UI app window, use the [`explore`](@ref) function called with the model as the required first argument. The menu on the left hand side will list each element in a label formatted as `component: variable/parameter`.

```julia
explore(m, title = "My Window")
explore(m)
```

Alternatively, in order to view just one parameter or variable, call the function [`explore`](@ref) as below to return a plot object and automatically display the plot in a viewer, assuming [`explore`](@ref) is the last command executed. This call will return the type `VegaLite.VLSpec`, which you may interact with using the API described in the [VegaLite.jl](https://github.com/fredo-dedup/VegaLite.jl) documentation. For example, [VegaLite.jl](https://github.com/fredo-dedup/VegaLite.jl) plots can be saved as [PNG](https://en.wikipedia.org/wiki/Portable_Network_Graphics), [SVG](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics), [PDF](https://en.wikipedia.org/wiki/PDF) and [EPS](https://en.wikipedia.org/wiki/Encapsulated_PostScript) files. You may save a plot using the `save` function.
Expand Down
3 changes: 2 additions & 1 deletion src/core/defs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ istype(T::DataType) = (pair -> pair.second isa T)

# Namespace filter functions return dicts of values for the given type.
# N.B. only composites hold other comps in the namespace.
components(obj::AbstractCompositeComponentDef) = filter(istype(AbstractComponentDef), obj.namespace)
components(model::Model, comp_name::Symbol) = components(compdef(model, comp_name))
components(obj::AbstractComponentDef) = filter(istype(AbstractComponentDef), obj.namespace)

param_dict(obj::ComponentDef) = filter(istype(ParameterDef), obj.namespace)
param_dict(obj::AbstractCompositeComponentDef) = filter(istype(CompositeParameterDef), obj.namespace)
Expand Down
6 changes: 6 additions & 0 deletions src/core/instances.jl
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,12 @@ function Base.getindex(mi::ModelInstance, key::AbstractString, datum::Symbol)
_get_datum(mi[key], datum)
end

function Base.getindex(mi::ModelInstance, comp_path::ComponentPath, datum::Symbol)
_get_datum(mi[comp_path], datum)
end

@delegate Base.getindex(m::Model, comp_path::ComponentPath, datum::Symbol) => mi

function Base.getindex(obj::AbstractCompositeComponentInstance, comp_name::Symbol, datum::Symbol)
ci = obj[comp_name]
return _get_datum(ci, datum)
Expand Down
11 changes: 10 additions & 1 deletion src/core/model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,16 @@ datumdef(m::Model, comp_name::Symbol, item::Symbol) = datumdef(compdef(m.md, com
Return the dimension names for the variable or parameter `datum_name`
in the given component `comp_name` in model `m`.
"""
dim_names(m::Model, comp_name::Symbol, datum_name::Symbol) = dim_names(compdef(m, comp_name), datum_name)
function dim_names(m::Model, comp_name::Symbol, datum_name::Symbol)
# the line below would work if the comp_name is in the top level of components in m's component structure
# return dim_names(compdef(m, comp_name), datum_name)

paths = _get_all_paths(m)
comp_path = paths[comp_name]
comp_def = find_comp(m, comp_path)
return dim_names(comp_def, datum_name)
end

dim_names(mm::MarginalModel, comp_name::Symbol, datum_name::Symbol) = dim_names(mm.base, comp_name, datum_name)

@delegate dimension(m::Model, dim_name::Symbol) => md
Expand Down
25 changes: 25 additions & 0 deletions src/core/paths.jl
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ end

find_comp(cr::AbstractComponentReference) = find_comp(parent(cr), pathof(cr))

@delegate find_comp(m::Model, path::ComponentPath) => md

"""
Return the relative path of `descendant` if is within the path of composite `ancestor` or
or nothing otherwise.
Expand Down Expand Up @@ -194,3 +196,26 @@ gensym("ModelDef") names look like Symbol("##ModelDef#123")
function is_abspath(path::ComponentPath)
return ! isempty(path) && match(r"^##ModelDef#\d+$", string(path.names[1])) !== nothing
end

# Returns a dictionary of the paths associated with all components, including composite components
function _get_all_paths(m::Model)
all_paths = Dict{Symbol, ComponentPath}()
for comp in components(m) # iterate over top level ComponentInstances
_add_paths(m, comp, all_paths)
end
return all_paths
end

# a helper function to perform a preorder traversal of a given top-level component
# in model m and add that path, and all sub-component paths, to the paths array
function _add_paths(m::Model, comp::Union{CompositeComponentInstance, LeafComponentInstance}, paths::Dict{Symbol, ComponentPath})
if isa(comp, CompositeComponentInstance)
paths[comp.comp_name] = comp.comp_path
for subcomp in values(comp.comps_dict)
_add_paths(m, subcomp, paths)
end
else # LeafComponentInstance
paths[comp.comp_name] = comp.comp_path
end
return paths
end
4 changes: 4 additions & 0 deletions src/core/types/model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ function Base.getindex(mm::MarginalModel, comp_name::Symbol, name::Symbol)
return (mm.modified[comp_name, name] .- mm.base[comp_name, name]) ./ mm.delta
end

function Base.getindex(mm::MarginalModel, comp_path::ComponentPath, name::Symbol)
return (mm.modified.mi[comp_path, name] .- mm.base.mi[comp_path, name]) ./ mm.delta
end

# DEPRECATION - EVENTUALLY REMOVE (and go back to default getproperty behavior)
function Base.getproperty(base::MarginalModel, s::Symbol)
if (s == :marginal)
Expand Down
86 changes: 61 additions & 25 deletions src/explorer/buildspecs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,24 @@ function _spec_for_item(m::Model, comp_name::Symbol, item_name::Symbol; interact
# Drop references to singleton dimensions
dims = tuple([dim for dim in dims if dim_count(m, dim) != 1]...)
end

# Control flow logic selects the correct plot type based on dimensions
# and dataframe fields
if length(dims) == 0
value = m[comp_name, item_name]
paths = _get_all_paths(m)
comp_path = paths[comp_name];
value = m[comp_path, item_name] === nothing ? m[comp_name, item_name] : m[comp_path, item_name]
name = "$comp_name : $item_name = $value"
spec = createspec_singlevalue(name)
elseif length(dims) > 2
@warn("$comp_name.$item_name has > 2 indexed dimensions, not yet implemented in explorer")
return nothing
name = "$comp_name : $item_name (CANNOT DISPLAY)"
spec = createspec_singlevalue(name)
else
name = "$comp_name : $item_name"
df = getdataframe(m, comp_name, item_name)
dffields = map(string, names(df)) # convert to string once before creating specs

# a 'time' field necessitates a line plot
if "time" in dffields

Expand Down Expand Up @@ -79,7 +82,8 @@ function _spec_for_sim_item(sim_inst::SimulationInstance, comp_name::Symbol, ite
spec = createspec_histogram(name, results, dffields; interactive = interactive)
elseif length(dims) > 2
@warn("$name has > 2 indexed dimensions, not yet implemented in explorer")
return nothing
name = "$comp_name : $item_name (CANNOT DISPLAY)"
spec = createspec_singlevalue(name)
else

# check if there are too many dimensions to map and if so, error
Expand All @@ -103,36 +107,63 @@ function _spec_for_sim_item(sim_inst::SimulationInstance, comp_name::Symbol, ite

end

function tree_view_values(model::Model)
all_subcomps = []
for comp_def in compdefs(model)
subcomp = tree_view_values(model, nameof(comp_def), comp_def)
push!(all_subcomps, subcomp)
end

# Return sorted list so that the UI list of items will be in lexicographic order
return sort(all_subcomps, by = x -> lowercase(x["name"]))
end

function tree_view_values(model::Model, comp_name::Symbol, comp_def::AbstractComponentDef)
sub_comp_item = _tree_view_node(comp_name)
for subcomp in compdefs(comp_def)
push!(sub_comp_item["children"], tree_view_values(model, nameof(subcomp), subcomp));
end
return sub_comp_item
end

function _tree_view_node(comp_name::Symbol)
return Dict("name" => "$comp_name", "children" => Dict[])
end

# Create the list of variables and parameters
function menu_item_list(model::Model)
all_menuitems = []
var_menuitems = []
par_menuitems = []

for comp_name in map(nameof, compdefs(model))
items = vcat(variable_names(model, comp_name), parameter_names(model, comp_name))

for item_name in items
menu_item = _menu_item(model, comp_name, item_name)
if menu_item !== nothing
push!(all_menuitems, menu_item)
end
end
for comp_def in compdefs(model)
all_subcomp_values = menu_item_list(model, nameof(comp_def), comp_def)
append!(var_menuitems, all_subcomp_values["vars"])
append!(par_menuitems, all_subcomp_values["pars"])
end

# Return sorted list so that the UI list of items will be in alphabetical order
return sort(all_menuitems, by = x -> lowercase(x["name"]))
# Return sorted list so that the UI list of items will be in lexicographic order
return Dict("vars" => sort(var_menuitems, by = x -> lowercase(x["name"])),"pars" => sort(par_menuitems, by = x -> lowercase(x["name"])))
end

# Create the list of variables and parameters
function menu_item_list(m::Model, comp_name::Symbol, comp_def::AbstractComponentDef)
var_menu_items = map(var_name -> _menu_item(m, Symbol(comp_name), var_name), variable_names(comp_def));
par_menu_items = map(par_name -> _menu_item(m, Symbol(comp_name), par_name), parameter_names(comp_def));

# Return sorted list so that the UI list of items will be in lexicographic order
return Dict("vars" => sort(var_menu_items, by = x -> lowercase(x["name"])),"pars" => sort(par_menu_items, by = x -> lowercase(x["name"])))
end

function menu_item_list(sim_inst::SimulationInstance)
all_menuitems = []
for datum_key in sim_inst.sim_def.savelist

menu_item = _menu_item(sim_inst, datum_key)
if menu_item !== nothing
push!(all_menuitems, menu_item)
end
end

# Return sorted list so that the UI list of items will be in alphabetical order
# Return sorted list so that the UI list of items will be in lexicographic order
return sort(all_menuitems, by = x -> lowercase(x["name"]))
end

Expand All @@ -144,11 +175,13 @@ function _menu_item(m::Model, comp_name::Symbol, item_name::Symbol)
end

if length(dims) == 0
value = m[comp_name, item_name]
paths = _get_all_paths(m)
comp_path = paths[comp_name];
value = m[comp_path, item_name]
name = "$comp_name : $item_name = $value"
elseif length(dims) > 2
@warn("$comp_name.$item_name has > 2 indexed dimensions, not yet implemented in explorer")
return nothing
name = "$comp_name : $item_name (CANNOT DISPLAY)"
else
name = "$comp_name : $item_name" # the name is needed for the list label
end
Expand All @@ -167,7 +200,7 @@ function _menu_item(sim_inst::SimulationInstance, datum_key::Tuple{Symbol, Symbo

if length(dims) > 2
@warn("$comp_name.$item_name has >2 graphing dims, not yet implemented in explorer")
return nothing
name = "$comp_name : $item_name (CANNOT DISPLAY)"
else
name = "$comp_name : $item_name" # the name is needed for the list label
end
Expand All @@ -193,10 +226,10 @@ function createspec_lineplot_interactive(name, df, dffields)
"VLspec" => Dict(
"\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
"description" => "plot for a specific component variable pair",
"title" => "$name (use bottom plot for interactive selection)",
"data"=> Dict("values" => datapart),
"vconcat" => [
Dict(
"title" => "$name",
# "transform" => [Dict("filter" => Dict("selection" => "brush"))],
"width" => _plot_width,
"height" => _plot_height,
Expand All @@ -215,6 +248,7 @@ function createspec_lineplot_interactive(name, df, dffields)
)
)
), Dict(
"title" => "INTERACTIVE PLOT",
"width" => _plot_width,
"height" => _slider_height,
"mark" => Dict("type" => "line", "point" => true),
Expand Down Expand Up @@ -280,10 +314,10 @@ function createspec_multilineplot_interactive(name, df, dffields, strmultidims)
"VLspec" => Dict(
"\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
"description" => "plot for a specific component variable pair",
"title" => "$name (use bottom plot for interactive selection)",
"data" => Dict("values" => datapart),
"vconcat" => [
Dict(
"title" => "$name",
# "transform" => [Dict("filter" => Dict("selection" => "brush"))],
"mark" => Dict("type" => "line", "point" => true),
"encoding" => Dict(
Expand All @@ -305,6 +339,7 @@ function createspec_multilineplot_interactive(name, df, dffields, strmultidims)
"width" => _plot_width,
"height" => _plot_height
), Dict(
"title" => "INTERACTIVE PLOT",
"width" => _plot_width,
"height" => _slider_height,
"mark" => Dict("type" => "line", "point" => true),
Expand Down Expand Up @@ -467,10 +502,10 @@ function createspec_trumpet_interactive(name, df, dffields)
"VLspec" => Dict(
"\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
"description" => "plot for a specific component variable pair",
"title" => "$name (use bottom plot for interactive selection)",
"data"=> Dict("values" => datapart),
"vconcat" => [
Dict(
"title" => "$name",
"width" => _plot_width,
"height" => _plot_height,
"encoding" => Dict(
Expand Down Expand Up @@ -514,6 +549,7 @@ function createspec_trumpet_interactive(name, df, dffields)
]
),
Dict(
"title" => "INTERACTIVE PLOT",
"width" => _plot_width,
"height" => _slider_height,
"encoding" => Dict(
Expand Down
Loading