Skip to content

Commit

Permalink
Trame Dashboard (BLAST-ImpactX#651)
Browse files Browse the repository at this point in the history
* Added trame dashboard `impactx-dashboard`

Co-authored-by: Axel Huebl <[email protected]>
  • Loading branch information
proy30 and ax3l authored Aug 9, 2024
1 parent 77f7525 commit 3da80a9
Show file tree
Hide file tree
Showing 29 changed files with 2,119 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,9 @@ cmake-build-*/
.DS_Store
.AppleDouble
.LSOverride

#####################
# Trame Dashboard (output files are temporary) #
#####################
src/python/impactx/diags*
src/python/impactx/*.png
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ Usage
usage/examples
usage/python
usage/parameters
usage/dashboard
usage/workflows

Data Analysis
Expand Down
15 changes: 15 additions & 0 deletions docs/source/install/dependencies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ Optional dependencies include:
- `quantiphy <https://quantiphy.readthedocs.io/>`__
- `openPMD-api <https://github.com/openPMD/openPMD-api>`__
- see our ``requirements.txt`` file for compatible versions
- web browser/Jupyter Dashboard: `trame <https://kitware.github.io/trame/>`__

- see our ``src/python/impactx/dashboard/requirements.txt`` file for all packages

If you are on a high-performance computing (HPC) system, then :ref:`please see our separate HPC documentation <install-hpc>`.

Expand Down Expand Up @@ -102,6 +105,12 @@ For OpenMP support, you will further need:
conda install -c conda-forge llvm-openmp
For the ImpactX browser/Jupyter dashboard dependencies, install from the ImpactX source directory:

.. code-block:: bash
python3 -m pip install -r src/python/impactx/dashboard/requirements.txt
Spack (Linux/macOS)
-------------------
Expand Down Expand Up @@ -144,6 +153,12 @@ Now install the WarpX/ImpactX dependencies in a new development environment:
spack install
python3 -m pip install jupyter matplotlib numpy openpmd-api openpmd-viewer pandas quantiphy scipy virtualenv yt
For the ImpactX browser/Jupyter dashboard dependencies, install from the ImpactX source directory:

.. code-block:: bash
python3 -m pip install -r src/python/impactx/dashboard/requirements.txt
In new terminal sessions, re-activate the environment with

.. code-block:: bash
Expand Down
87 changes: 87 additions & 0 deletions docs/source/usage/dashboard.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
.. _usage-dashboard:

Dashboard
=========

ImpactX Dashboard is a browser-based interface to ImpactX.
It provides a graphical interface to a subset of ImpactX functionality.

.. note::

ImpactX Dashboard is provided as a preview and continues to be developed.
Let us know in GitHub `discussions <https://github.com/ECP-WarpX/impactx/discussions>`__ and `issues <https://github.com/ECP-WarpX/impactx/issues>`__ how it works for you and what kind of workflows you would like to run in it.


Launching the Dashboard
-----------------------

The ImpactX Dashboard can be run in two modes, as a standalone browser application or inside a Jupyter notebook.

1. **Standalone browser application:**
After installation of ImpactX including the Python modules, launch:

.. code-block:: bash
impactx-dashboard
2. **JupyterLab:**
Start `JupyterLab <https://jupyter.org/install>`__, e.g., logging into a Jupyter service at an HPC center or locally on your computer using:

.. code-block:: bash
jupyter-lab
Inside JupyterLab, run the following Python code in a notebook to initialize and display the dashboard:

.. code-block:: python
from impactx.dashboard import JupyterApp
# Create new application instance
app = JupyterApp()
# Start the server and wait for the UI to be ready
await app.ui.ready
# Display the UI in the JupyterLab notebook
app.ui
Navigation
----------

The user-friendly interface includes multiple tabs and menu options, intended to be navigated from top to bottom:

- **Input Tab**: Allows to adjust simulation input parameters.
- **Run Tab**: Enables to run simulations and monitor their progress.
- **Analyze Tab**: Provides tools to visualize and analyze simulation results.

.. figure:: https://gist.githubusercontent.com/ax3l/b56aa3c3261f9612e276f3198b34f771/raw/11bfe461a24e1daa7fd2d663c686b0fcc2b6e305/dashboard.png
:align: center
:width: 75%
:alt: phase space ellipse

Input section in the dashboard.


Developers
----------

Additional Dependencies
"""""""""""""""""""""""

Additional dependencies to ImpactX for the dashboard are included relative ImpactX source directory:

.. code-block:: bash
python -m pip install -r src/python/impactx/dashboard/requirements.txt
Python Module
"""""""""""""

After installing only the ImpactX Python bindings, one can directly run the dashboard modules from the source tree during development, too.
For this, navigate in the ImpactX source directory to the ``src/python/impactx`` directory and run:

.. code-block:: bash
python -m dashboard
5 changes: 5 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,4 +282,9 @@ def build_extension(self, ext):
# new PEP 639 format
license="BSD-3-Clause-LBNL",
license_files=["LICENSE"],
entry_points={
"console_scripts": [
"impactx-dashboard=impactx.dashboard.__main__:main",
],
},
)
Empty file.
101 changes: 101 additions & 0 deletions src/python/impactx/dashboard/Analyze/analyzeFunctions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
"""
This file is part of ImpactX
Copyright 2024 ImpactX contributors
Authors: Parthib Roy, Axel Huebl
License: BSD-3-Clause-LBNL
"""

import pandas as pd

from ..trame_setup import setup_server

server, state, ctrl = setup_server()

# -----------------------------------------------------------------------------
# Content
# -----------------------------------------------------------------------------


class AnalyzeFunctions:
"""
Helper functions for
preparing the contents for the 'Analyze' page
"""

# -----------------------------------------------------------------------------
# Functions for Beam Characteristic and Ref particle data table
# -----------------------------------------------------------------------------

@staticmethod
def load_data(file_path):
"""
Reads data from the provided file path.
:param file_path: The path to the file to be read.
:return: A DataFrame containing the data from the file.
"""

df = pd.read_csv(file_path, sep=" ")
return df

@staticmethod
def convert_to_dict(combined_data):
"""
Converts data to dictionary format
for Vuetify data table.
:param combined_data: The DataFrame to be converted.
:return: A tuple containing the data as a list of dictionaries and the headers.
"""

dictionary = combined_data.to_dict(orient="records")
columns = combined_data.columns
headers = [
{"text": column.strip(), "value": column.strip()} for column in columns
]
return dictionary, headers

@staticmethod
def combine_files(file1_name, file2_name):
"""
Merges two files together.
:param file1_name: The name of the first file.
:param file2_name: The name of the second file.
:return: A DataFrame containing the merged data from the two files.
"""

file1 = AnalyzeFunctions.load_data(file1_name)
file2 = AnalyzeFunctions.load_data(file2_name)
return pd.merge(file1, file2, how="outer")

@staticmethod
def filter_headers(allHeaders, selected_headers):
"""
Retrieves only user-selected headers.
:param allHeaders: The list of all headers.
:param selected_headers: The list of headers selected by the user.
:return: A list of filtered headers based on user selection.
"""

filtered_headers = []
for selectedHeader in allHeaders:
if selectedHeader["value"] in selected_headers:
filtered_headers.append(selectedHeader)
return filtered_headers

@staticmethod
def filter_data(allData, selected_headers):
"""
Retrieves data for user-selected headers.
:param allData: The list of all data.
:param selected_headers: The list of headers selected by the user.
:return: A list of filtered data based on user selection.
"""

filtered_data = []
for row in allData:
filtered_row = {}
for key, value in row.items():
if key in selected_headers:
filtered_row[key] = value
filtered_data.append(filtered_row)
return filtered_data
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""
This file is part of ImpactX
Copyright 2024 ImpactX contributors
Authors: Parthib Roy, Axel Huebl
License: BSD-3-Clause-LBNL
"""

import plotly.graph_objects as go


def line_plot_1d(selected_headers, filtered_data):
"""
Generates a 1D line plot using Plotly based on selected headers and filtered data.
"""

x_axis = selected_headers[0] if len(selected_headers) > 1 else None
y_axis = selected_headers[1:] if len(selected_headers) > 2 else None

x = [row[x_axis] for row in filtered_data] if x_axis else []

figure_data = []
if y_axis:
for column in y_axis:
y = [row[column] for row in filtered_data]
trace = go.Scatter(
x=x,
y=y,
mode="lines+markers",
name=column,
line=dict(width=2),
marker=dict(size=8),
)
figure_data.append(trace)

return go.Figure(
data=figure_data,
layout=go.Layout(
title="Plot Over S",
xaxis=dict(title="s"),
yaxis=dict(title=""),
margin=dict(l=20, r=20, t=25, b=30),
),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""
This file is part of ImpactX
Copyright 2024 ImpactX contributors
Authors: Parthib Roy, Axel Huebl
License: BSD-3-Clause-LBNL
"""

from ...trame_setup import setup_server

server, state, ctrl = setup_server()

import base64
import io

from impactx import Config, ImpactX

from ...Input.distributionParameters.distributionMain import distribution_parameters
from ...Input.latticeConfiguration.latticeMain import lattice_elements
from ..plot_PhaseSpaceProjections.phaseSpaceSettings import adjusted_settings_plot

# Call MPI_Init and MPI_Finalize only once:
if Config.have_mpi:
from mpi4py import MPI # noqa


def fig_to_base64(fig):
"""
Puts png in trame-compatible form
"""
buf = io.BytesIO()
fig.savefig(buf, format="png")
buf.seek(0)
return base64.b64encode(buf.read()).decode("utf-8")


def run_simulation():
"""
This tests using ImpactX and Pandas Dataframes
"""
sim = ImpactX()

sim.particle_shape = state.particle_shape
sim.space_charge = False
sim.slice_step_diagnostics = True
sim.init_grids()

# init particle beam
kin_energy_MeV = state.kin_energy_MeV
bunch_charge_C = state.bunch_charge_C
npart = state.npart

# reference particle
pc = sim.particle_container()
ref = pc.ref_particle()
ref.set_charge_qe(-1.0).set_mass_MeV(0.510998950).set_kin_energy_MeV(kin_energy_MeV)

distribution = distribution_parameters()
sim.add_particles(bunch_charge_C, distribution, npart)

lattice_configuration = lattice_elements()

sim.lattice.extend(lattice_configuration)

# simulate
sim.evolve()

fig = adjusted_settings_plot(pc)
fig_original = pc.plot_phasespace()

if fig_original is not None:
image_base64 = fig_to_base64(fig_original)
state.image_data = f"data:image/png;base64, {image_base64}"

sim.finalize()

return fig
Loading

0 comments on commit 3da80a9

Please sign in to comment.