diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ea6212c..91d9f0e 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -13,7 +13,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - name: Add GLMakie/XFVB dependencies + run: sudo apt-get update && sudo apt-get install -y xorg-dev mesa-utils xvfb libgl1 freeglut3-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxext-dev libcairo2-dev libfreetype6-dev libffi-dev libjpeg-dev libpng-dev libz-dev + - name: Checkout + uses: actions/checkout@v4 - name: Set up Julia uses: julia-actions/setup-julia@v1 with: @@ -33,11 +36,11 @@ jobs: conda activate test pip install -e . - - name: Run tests with coverage + - name: Run tests with xvfb shell: bash -l {0} run: | conda activate test - pytest --cov=battmo --cov-report=term-missing + xvfb-run -s "-screen 0 1024x768x24" pytest --cov=battmo --cov-report=term-missing release: name: Build and publish to PyPI diff --git a/battmo/api/plotting.py b/battmo/api/plotting.py index c936b97..125d592 100644 --- a/battmo/api/plotting.py +++ b/battmo/api/plotting.py @@ -1,27 +1,31 @@ import juliapkg from ..julia_import import jl import juliacall +import time +import asyncio def activate_plotting(): try: - jl.seval("using WGLMakie; WGLMakie.activate!()") + jl.seval("using GLMakie; GLMakie.activate!()") + except: - print("Unable to load WGLMakie. Have you called install_plotting()?") + + print("Unable to load GLMakie. Have you called install_plotting()?") return False return True def install_plotting(): - juliapkg.add("WGLMakie", "276b4fcb-3e11-5398-bf8b-a0c2d153d008") + juliapkg.add("GLMakie", "e9467ef8-e4e7-5192-8a1a-b1aee30e663a") juliapkg.resolve() activate_plotting() return True def uninstall_plotting(): - juliapkg.rm("WGLMakie", "276b4fcb-3e11-5398-bf8b-a0c2d153d008") + juliapkg.rm("GLMakie", "e9467ef8-e4e7-5192-8a1a-b1aee30e663a") juliapkg.resolve() return True @@ -30,43 +34,61 @@ def make_interactive(): juliacall.interactive() -def plot_dashboard(output, plot_type="simple"): +async def keep_plot_dashboard_alive(*arg, **kwargs): + fig = jl.plot_dashboard(*arg, **kwargs) + while True: + jl.seval("yield()") + await asyncio.sleep(0.1) + + +def plot_dashboard(*arg, **kwargs): + if activate_plotting(): - fig = jl.plot_dashboard(output, plot_type=plot_type) - if plot_type == "line": - jl.seval( - """ - println("Press Ctrl+C to stop plotting interactivity") - while true - sleep(0.1) - end - """ - ) + loop = asyncio.get_event_loop() + + if loop.is_running(): + # Notebook or already running loop + loop.create_task(keep_plot_dashboard_alive(*arg, **kwargs)) + else: + # Script or no loop running - start a loop + asyncio.run(keep_plot_dashboard_alive(*arg, **kwargs)) - make_interactive() - return fig +async def keep_plot_output_alive(*arg, **kwargs): + fig = jl.plot_output(*arg, **kwargs) + while True: + jl.seval("yield()") + await asyncio.sleep(0.1) def plot_output(*arg, **kwargs): if activate_plotting(): - fig = jl.plot_output(*arg, **kwargs) - make_interactive() - jl.seval("display(current_figure())") - return fig + try: + loop = asyncio.get_event_loop() + # Notebook or already running loop + loop.create_task(keep_plot_output_alive(*arg, **kwargs)) + + except RuntimeError: + # Script: no loop running, so start one + asyncio.run(keep_plot_output_alive(*arg, **kwargs)) + + +async def keep_plot_interactive_3d_alive(*arg, **kwargs): + fig = jl.plot_interactive_3d(*arg, **kwargs) + while True: + jl.seval("yield()") + await asyncio.sleep(0.1) def plot_interactive_3d(*arg, **kwargs): if activate_plotting(): - fig = jl.plot_interactive_3d(*arg, **kwargs) - make_interactive() - jl.seval( - """ - println("Press Ctrl+C to stop plotting interactivity") - while true - sleep(0.1) - end - """ - ) - return fig + try: + loop = asyncio.get_event_loop() + # Notebook or already running loop + loop.create_task(keep_plot_interactive_3d_alive(*arg, **kwargs)) + + except RuntimeError: + + # Script: no loop running, so start one + asyncio.run(keep_plot_interactive_3d_alive(*arg, **kwargs)) diff --git a/battmo/julia_import.py b/battmo/julia_import.py index 167de6b..975c976 100644 --- a/battmo/julia_import.py +++ b/battmo/julia_import.py @@ -40,9 +40,7 @@ try: jl.seval( """ - using BattMo, - using Jutul:Jutul,get_1d_interpolator - using WGLMakie + using BattMo, Jutul """ ) @@ -53,12 +51,9 @@ import Pkg Pkg.add("BattMo") Pkg.add("Jutul") - Pkg.add("WGLMakie") Pkg.instantiate() - using BattMo - using Jutul:Jutul,get_1d_interpolator - using WGLMakie + using BattMo, Jutul """ ) diff --git a/battmo/juliapkg.json b/battmo/juliapkg.json index 56b04b4..a41c45e 100644 --- a/battmo/juliapkg.json +++ b/battmo/juliapkg.json @@ -10,9 +10,9 @@ "uuid": "2b460a1a-8a2b-45b2-b125-b5c536396eb9", "version": "0.4.6" }, - "WGLMakie": { - "uuid": "276b4fcb-3e11-5398-bf8b-a0c2d153d008", - "version": "0.11.10" + "GLMakie": { + "uuid": "e9467ef8-e4e7-5192-8a1a-b1aee30e663a", + "version": "0.11.11" }, "OpenSSL_jll": { "uuid": "458c3c95-2e84-50aa-8efc-19380b2a3a95", diff --git a/examples/1d_simulation.py b/examples/1d_simulation.py index 01e96dc..3ae8575 100644 --- a/examples/1d_simulation.py +++ b/examples/1d_simulation.py @@ -1,24 +1,30 @@ -# Partial port of https://battmoteam.github.io/BattMo.jl/dev/tutorials/2_run_a_simulation +# %% from battmo import * import plotly.express as px import pandas as pd import numpy as np +import asyncio +# %% # Load parameter sets cell_parameters = load_cell_parameters(from_default_set="chen_2020") cycling_protocol = load_cycling_protocol(from_default_set="cc_discharge") +# %% # Have a quick look into what kind of cell we're dealing with quick_cell_check(cell_parameters) +# %% # Setup model and simulation model = LithiumIonBattery() sim = Simulation(model, cell_parameters, cycling_protocol) output = solve(sim) +# %% # Have a look into which output quantities are available print_info(output) +# %% # Plot voltage curve time_series = output.time_series @@ -26,5 +32,10 @@ fig = px.line(df, x="Time", y="Voltage", title="Voltage curve") fig.show() + +# %% # Plot a dashboard plot_dashboard(output, plot_type="contour") + + +# %% diff --git a/examples/3d_simulation.py b/examples/3d_simulation.py index c734a18..7ea11e5 100644 --- a/examples/3d_simulation.py +++ b/examples/3d_simulation.py @@ -1,8 +1,8 @@ -# Partial port of https://battmoteam.github.io/BattMo.jl/dev/tutorials/2_run_a_simulation +# %% from battmo import * import plotly.express as px -# Load parameter sets +# %% Load parameter sets cell_parameters = load_cell_parameters(from_default_set="chen_2020") cycling_protocol = load_cycling_protocol(from_default_set="cc_discharge") model_settings = load_model_settings(from_default_set="p4d_cylindrical") @@ -13,10 +13,12 @@ cell_parameters["PositiveElectrode"]["CurrentCollector"]["TabWidth"] = 0.002 simulation_settings["AngularGridPoints"] = 8 -# Setup model and simulation +# %% Setup model and simulation model = LithiumIonBattery(model_settings=model_settings) sim = Simulation(model, cell_parameters, cycling_protocol, simulation_settings=simulation_settings) output = solve(sim) -# Plot interative 3D results +# %% Plot interative 3D results plot_interactive_3d(output) + +# %%