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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/source/actor_visualization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Available Settings

* Optional

- ***port***: (int) The port on which the visualization server will run. Defaults to `5006`.
- ***port***: (int) The port on which the visualization server will run. Defaults to `0`, indicating a random available port.
- ***throttle_interval***: (float) The minimum time in seconds between plot updates.
This can be used to prevent the visualization from slowing down if data arrives very quickly. Defaults to `0.1`.
- ***keep_alive***: (bool) If `True`, the visualization server will remain active after
Expand Down
Binary file added docs/source/images/automatic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/source/images/ff_prime.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/source/images/ip_curve.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/source/images/outline.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Manual
installing
usage
tips_and_tricks
training

.. toctree::
:caption: API docs
Expand Down
480 changes: 480 additions & 0 deletions docs/source/training.rst

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion imas_muscle3/actors/visualization_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ def main() -> None:
while instance.reuse_instance():
if first_run:
plot_file_path = instance.get_setting("plot_file_path", "str")
port = get_setting_optional(instance, "port", 5006)
# If port is not specified, use a random available port
port = get_setting_optional(instance, "port", 0)
# FIXME: there is an issue when the plotting takes much longer
# than it takes for data to arrive from the MUSCLE actor. As a
# remedy, set a plotting throttle interval.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"""
Example that plots the following:
- First wall and divertor from machine description IDS.
- Boundary outline from an equilibrium IDS
"""

import holoviews as hv
import numpy as np
import panel as pn
import xarray as xr

from imas_muscle3.visualization.base_plotter import BasePlotter
from imas_muscle3.visualization.base_state import BaseState


class State(BaseState):
def extract(self, ids):
if ids.metadata.name == "equilibrium":
self.extract_equilibrium(ids)

def extract_equilibrium(self, ids):
ts = ids.time_slice[0]
outline = ts.boundary.outline

boundary_data = xr.Dataset(
{
"r": (("time", "point"), [outline.r]),
"z": (("time", "point"), [outline.z]),
},
coords={
"time": [ids.time[0]],
"point": np.arange(len(outline.r)),
},
)

current_data = self.data.get("equilibrium")
if current_data is None:
self.data["equilibrium"] = boundary_data
else:
self.data["equilibrium"] = xr.concat(
[current_data, boundary_data], dim="time", join="outer"
)


class Plotter(BasePlotter):
DEFAULT_OPTS = hv.opts.Overlay(
xlim=(0, 13),
ylim=(-10, 10),
title="Wall and equilibrium boundary outline",
xlabel="r [m]",
ylabel="z [m]",
)

def get_dashboard(self):
elements = [
hv.DynamicMap(self._plot_boundary_outline),
hv.DynamicMap(self._plot_wall),
]
overlay = hv.Overlay(elements).collate().opts(self.DEFAULT_OPTS)
return pn.pane.HoloViews(overlay, width=800, height=1000)

@pn.depends("time")
def _plot_boundary_outline(self):
state = self.active_state.data.get("equilibrium")

if state is not None and "r" in state and "z" in state:
selected_data = state.sel(time=self.time)
r = selected_data.r.values
z = selected_data.z.values
else:
r, z = [], [], "Waiting for data..."

return hv.Curve((r, z)).opts(self.DEFAULT_OPTS)

def _plot_wall(self):
"""Generates path for limiter and divertor."""
paths = []
wall = self.active_state.md.get("wall")
if wall is not None:
for unit in wall.description_2d[0].limiter.unit:
name = str(unit.name)
r_vals = unit.outline.r
z_vals = unit.outline.z
paths.append((r_vals, z_vals, name))
return hv.Path(paths, vdims=["name"]).opts(
color="black",
line_width=2,
hover_tooltips=[("", "@name")],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
ymmsl_version: v0.1
model:
name: test_model
components:
source_component:
implementation: source_component
ports:
o_i: [equilibrium_out]
source_component_md:
implementation: source_component
ports:
o_i: [wall_out, pf_active_out]
visualization_component:
implementation: visualization_component
ports:
s: [equilibrium_in, pf_active_md_in, wall_md_in]
conduits:
source_component.equilibrium_out: visualization_component.equilibrium_in
source_component_md.wall_out: visualization_component.wall_md_in
source_component_md.pf_active_out: visualization_component.pf_active_md_in
settings:
source_component.source_uri: imas:hdf5?path=/home/ITER/blokhus/public/imasdb/ITER/4/666666/3/
source_component_md.source_uri: imas:hdf5?path=/home/ITER/blokhus/public/imasdb/ITER/4/666666/3/
visualization_component.plot_file_path: /home/ITER/blokhus/projects/IMAS-MUSCLE3/imas_muscle3/visualization/examples/machine_description/machine_description.py
visualization_component.keep_alive: true
implementations:
visualization_component:
executable: python
args: -u -m imas_muscle3.actors.visualization_component
source_component:
executable: python
args: -u -m imas_muscle3.actors.source_component
resources:
source_component:
threads: 1
source_component_md:
threads: 1
visualization_component:
threads: 1
1 change: 0 additions & 1 deletion imas_muscle3/visualization/examples/pds/pds.ymmsl
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ settings:
source_component.source_uri: imas:hdf5?path=/home/sebbe/projects/iter_python/IMAS-MUSCLE3/data/pds_example
source_component_md.source_uri: imas:hdf5?path=/home/sebbe/projects/iter_python/Waveform-Editor/data/nice-input-dd4
visualization_component.plot_file_path: /home/sebbe/projects/iter_python/IMAS-MUSCLE3/imas_muscle3/visualization/examples/pds/pds.py
visualization_component.port: 5007
visualization_component.keep_alive: true
implementations:
visualization_component:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ model:
conduits:
source_component.equilibrium_out: visualization_component.equilibrium_in
settings:
source_component.source_uri: imas:hdf5?path=/home/sebbe/projects/iter_python/IMAS-MUSCLE3/data/striped_eq
visualization_component.plot_file_path: /home/sebbe/projects/iter_python/IMAS-MUSCLE3/imas_muscle3/visualization/examples/simple_1d_plot/simple_1d_plot.py
visualization_component.port: 5007
source_component.source_uri: imas:hdf5?path=/home/ITER/blokhus/public/imasdb/ITER/4/666666/3/
visualization_component.plot_file_path: /home/ITER/blokhus/projects/IMAS-MUSCLE3/imas_muscle3/visualization/examples/simple_1d_plot/simple_1d_plot.py
visualization_component.throttle_interval: 0
visualization_component.keep_alive: true
implementations:
Expand Down
14 changes: 1 addition & 13 deletions imas_muscle3/visualization/visualization_actor.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import logging
import runpy
import webbrowser
from typing import Dict

import panel as pn
Expand Down Expand Up @@ -92,18 +91,7 @@ def _start_server(self) -> None:
self.server = pn.serve( # type: ignore[no-untyped-call]
self.dynamic_panel,
port=self.port,
show=False,
show=self.open_browser_on_start,
threaded=True,
start=True,
)
if self.open_browser_on_start:
self._open_browser()

def _open_browser(self) -> None:
"""Open the dashboard in the system web browser."""
url = f"http://localhost:{self.port}"
try:
webbrowser.open(url)
except Exception as e:
logger.warning(f"Could not open browser automatically: {e}")
logger.info(f"Dashboard is available at {url}")