Skip to content

Commit 68d5fdb

Browse files
authored
Merge pull request #750 from simvue-io/dev
v2.0.0 RC 1
2 parents f7a6261 + 6c5bfce commit 68d5fdb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1395
-2457
lines changed

.github/workflows/examples.yml

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -60,32 +60,6 @@ jobs:
6060
export SIMVUE_URL=${{ secrets.SIMVUE_URL }}
6161
export SIMVUE_TOKEN=${{ secrets.SIMVUE_TOKEN }}
6262
python3 examples/Logging/logging-to-simvue.py --ci
63-
OpenFOAM:
64-
runs-on: ubuntu-latest
65-
if: false # deactivate for now
66-
container:
67-
image: openfoam/openfoam10-paraview56
68-
options: --user root
69-
steps:
70-
- uses: actions/checkout@v4
71-
- name: Manual Python Install
72-
run: |
73-
add-apt-repository ppa:deadsnakes/ppa -y
74-
apt-get update && \
75-
apt-get install -y git wget curl python3.11-full
76-
77-
- name: Install dependencies
78-
run: |
79-
python3.11 -m ensurepip --upgrade
80-
python3.11 -m pip install ukaea-multiparser
81-
- name: Install Package
82-
run: python3.11 -m pip install .
83-
- name: Run Example
84-
shell: bash
85-
run: |
86-
export SIMVUE_URL=${{ secrets.SIMVUE_URL }}
87-
export SIMVUE_TOKEN=${{ secrets.SIMVUE_TOKEN }}
88-
python3.11 ./examples/OpenFOAM/simvue_openfoam.py /opt/openfoam10/tutorials/incompressible/pimpleFoam/laminar/movingCone/Allrun --ci
8963
Optuna:
9064
runs-on: ubuntu-latest
9165
if: false # deactivate for now
@@ -166,25 +140,6 @@ jobs:
166140
export SIMVUE_URL=${{ secrets.SIMVUE_URL }}
167141
export SIMVUE_TOKEN=${{ secrets.SIMVUE_TOKEN }}
168142
python3.11 ./examples/Tensorflow/dynamic_rnn.py --ci
169-
FDS:
170-
runs-on: ubuntu-latest
171-
container:
172-
image: openbcl/fds:6.9.1
173-
steps:
174-
- uses: actions/checkout@v4
175-
- name: Setup Python
176-
uses: actions/setup-python@v5
177-
with:
178-
python-version: "3.11"
179-
- name: Install Simvue
180-
run: python3 -m pip install .
181-
- name: Install Dependencies
182-
run: python3 -m pip install -r examples/FDS/requirements.txt
183-
- name: Run Example
184-
run: |
185-
export SIMVUE_URL=${{ secrets.SIMVUE_URL }}
186-
export SIMVUE_TOKEN=${{ secrets.SIMVUE_TOKEN }}
187-
python3.11 ./examples/FDS/minimal_fds.py examples/FDS/activate_vents.fds . --ci
188143
GEANT4:
189144
if: false # deactivate for now
190145
runs-on: ubuntu-latest

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ repos:
2323
args: [--branch, main, --branch, dev]
2424
- id: check-added-large-files
2525
- repo: https://github.com/astral-sh/ruff-pre-commit
26-
rev: v0.9.7
26+
rev: v0.9.9
2727
hooks:
2828
- id: ruff
2929
args: [ --fix, --exit-non-zero-on-fix, "--ignore=C901" ]

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Change log
22

3+
## [v2.0.0-rc1](https://github.com/simvue-io/client/releases/tag/v2.0.0rc1) - 2025-03-06
4+
* Add new example notebooks
5+
* Update and refactor examples to work with v2.0
6+
* Fix bug in offline artifacts using wrong file path
7+
* Change names of sustainability metrics
8+
* Fix `Self` being used in typing Generators so that Simvue works with Python 3.10 in Conda
9+
310
## [v2.0.0-alpha3](https://github.com/simvue-io/client/releases/tag/v2.0.0a3) - 2025-03-04
411
* Updated codecarbon to work with new API
512
* Codecarbon now works with offline mode

CITATION.cff

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ keywords:
4242
- alerting
4343
- simulation
4444
license: Apache-2.0
45-
commit: 64ff8a5344232d44fc7da5b6ff601d3023497977
46-
version: 2.0.0a3
47-
date-released: '2025-03-04'
45+
commit: effbd2e88fa12a181bf33721eae599d4245e1484
46+
version: 2.0.0rc1
47+
date-released: '2025-03-06'
4848
references:
4949
- title: mlco2/codecarbon
5050
version: v2.8.2
@@ -72,18 +72,12 @@ references:
7272
family-names: Connell
7373
- given-names: Amine
7474
family-names: Saboni
75-
- given-names: Inimaz
76-
family-names:
77-
- given-names: supatomic
78-
family-names:
7975
- given-names: Mathilde
8076
family-names: Léval
8177
- given-names: Luis
8278
family-names: Blanche
8379
- given-names: Alexis
8480
family-names: Cruveiller
85-
- given-names: ouminasara
86-
family-names:
8781
- given-names: Franklin
8882
family-names: Zhao
8983
- given-names: Aditya
@@ -110,7 +104,5 @@ references:
110104
family-names: Bauer
111105
- given-names: Lucas Otávio N.
112106
family-names: de Araújo
113-
- given-names: JPW
114-
family-names:
115107
- given-names: Minerva
116108
family-names: Books

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Collect metadata, metrics and artifacts from simulations, processing and AI/ML t
1616

1717
<div align="center">
1818
<a href="https://github.com/simvue-io/client/blob/main/LICENSE" target="_blank"><img src="https://img.shields.io/github/license/simvue-io/client"/></a>
19-
<img src="https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12-blue">
19+
<img src="https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue">
2020
<a href="https://pypi.org/project/simvue/" target="_blank"><img src="https://img.shields.io/pypi/v/simvue.svg"/></a>
2121
<a href="https://pepy.tech/project/simvue"><img src="https://static.pepy.tech/badge/simvue"/></a>
2222
<a href="https://github.com/astral-sh/ruff"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json"></a>

examples/Bluemira/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Geometry optimisation using Bluemira
2+
3+
[Bluemira](https://github.com/Fusion-Power-Plant-Framework/bluemira) is an integrated inter-disciplinary design tool for future fusion reactors. It incorporates several modules, some of which rely on other codes, to carry out a range of typical conceptual fusion reactor design activities. This example uses Simvue to track the optimisation of the geometry of a Princeton-D shaped magnet, while maintaining a safe minimum distance to the plasma of 0.5m.
4+
5+
6+
To run this example, you will need to install Bluemira. For details of installation of Bluemira please refer to https://bluemira.readthedocs.io/en/develop/installation.html
7+
8+
Once you have Bluemira installed and are running the `bluemita` conda environment (or similar), install Simvue with the plotting extras:
9+
```
10+
pip install simvue[plot]
11+
```
12+
Then move into the example's directory:
13+
```
14+
cd examples/Bluemira
15+
```
16+
Make a simvue.toml file - click Create New Run on the web UI, copy the contents listed, and paste into a config file.
17+
18+
Finally, run the example:
19+
```
20+
python geometry_optimisation.py
21+
```
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
# bluemira is an integrated inter-disciplinary design tool for future fusion
2+
# reactors. It incorporates several modules, some of which rely on other
3+
# codes, to carry out a range of typical conceptual fusion reactor design
4+
# activities.
5+
#
6+
# Copyright (C) 2021 M. Coleman, J. Cook, F. Franza, I.A. Maione, S. McIntosh,
7+
# J. Morris, D. Short
8+
#
9+
# bluemira is free software; you can redistribute it and/or
10+
# modify it under the terms of the GNU Lesser General Public
11+
# License as published by the Free Software Foundation; either
12+
# version 2.1 of the License, or (at your option) any later version.
13+
#
14+
# bluemira is distributed in the hope that it will be useful,
15+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17+
# Lesser General Public License for more details.
18+
#
19+
# You should have received a copy of the GNU Lesser General Public
20+
# License along with bluemira; if not, see <https://www.gnu.org/licenses/>.
21+
"""
22+
Geometry Optimisation
23+
24+
Example taken from: bluemira/examples/optimisation/geometry_optimisation.ex.py
25+
26+
In this example we will go through how to set up a simple geometry
27+
optimisation, including a geometric constraint.
28+
29+
The problem to solve is, minimise the length of our wall boundary,
30+
in the xz-plane, whilst keeping it a minimum distance from our plasma.
31+
32+
We will greatly simplify this problem by working with a circular
33+
plasma, we will use a PrincetonD for the wall shape,
34+
and set the minimum distance to half a meter.
35+
"""
36+
37+
import numpy as np
38+
import os
39+
import sys
40+
from bluemira.display import plot_2d
41+
from bluemira.display.plotter import PlotOptions
42+
from bluemira.geometry.optimisation import optimise_geometry
43+
from bluemira.geometry.parameterisations import GeometryParameterisation, PrincetonD
44+
from bluemira.geometry.tools import distance_to, make_circle
45+
from bluemira.geometry.wire import BluemiraWire
46+
47+
import simvue
48+
49+
def f_objective(geom: GeometryParameterisation) -> float:
50+
"""Objective function to minimise a shape's length."""
51+
return geom.create_shape().length
52+
53+
54+
def distance_constraint(
55+
geom: GeometryParameterisation, boundary: BluemiraWire, min_distance: float, run: simvue.Run
56+
) -> float:
57+
"""
58+
A constraint to keep a minimum distance between two shapes.
59+
60+
The constraint must be in the form f(x) <= 0, i.e., constraint
61+
is satisfied if f(x) <= 0.
62+
63+
Since what we want is 'min_distance <= distance(A, B)', we rewrite
64+
this in the form 'min_distance - distance(A, B) <= 0', and return
65+
the left-hand side from this function.
66+
"""
67+
shape = geom.create_shape()
68+
# Log all variables as metrics after each iteration, giving human readable names:
69+
run.log_metrics(
70+
{
71+
"inboard_limb_radius": float(geom.variables["x1"].value),
72+
"outboard_limb_radius": float(geom.variables["x2"].value),
73+
"vertical_offset": float(geom.variables["dz"].value),
74+
"length_of_wall": float(shape.length),
75+
"distance_to_plasma": float(distance_to(shape, boundary)[0])
76+
}
77+
)
78+
return min_distance - distance_to(shape, boundary)[0]
79+
80+
# The original example prints stuff to the console to track progress
81+
# Instead of changing these lines to log events (since we probably want both),
82+
# We can make a class which intercepts stdout and also sends messages to Simvue
83+
class StdoutToSimvue():
84+
def __init__(self, run: simvue.Run):
85+
self.run = run
86+
87+
def write(self, message: str):
88+
# Log the message as an event (so long as it isnt a blank line)
89+
if message.strip():
90+
run.log_event(message)
91+
# And print to console as normal
92+
sys.__stdout__.write(message)
93+
94+
def flush(self):
95+
sys.__stdout__.flush()
96+
97+
# Here we will start doing our optimisation. First create a Simvue run,
98+
# using the Run class as a context manager:
99+
with simvue.Run() as run:
100+
# Initialise our run:
101+
run.init(
102+
name="bluemira_geometry_optimisation",
103+
folder="/simvue_client_demos",
104+
visibility="tenant" if os.environ.get("CI") else None,
105+
tags=["bluemira", "simvue_client_examples"],
106+
description="Minimise the length of a parameterised geometry using gradient-based optimisation algorithm.",
107+
)
108+
109+
# Redirect stdout so that print statements also get logged as events:
110+
stdout_sender = StdoutToSimvue(run)
111+
sys.stdout = stdout_sender
112+
113+
# Next define the shape of our plasma, and the minimum distance we want between
114+
# our wall boundary and our plasma:
115+
min_distance = 0.5
116+
plasma = make_circle(radius=2, center=(8, 0, 0.25), axis=(0, 1, 0))
117+
118+
# As with any optimisation, it's important to pick a reasonable initial
119+
# parameterisation.
120+
wall_boundary = PrincetonD({
121+
"x1": {"value": 4, "upper_bound": 6},
122+
"x2": {"value": 12, "lower_bound": 10},
123+
})
124+
125+
print("Initial parameterisation:")
126+
print(wall_boundary.variables)
127+
print(f"Length of wall : {wall_boundary.create_shape().length}")
128+
print(f"Distance to plasma: {distance_to(wall_boundary.create_shape(), plasma)[0]}")
129+
130+
# Create metadata for our original parameters:
131+
_metadata = {
132+
var: {
133+
"initial": wall_boundary.variables[var].value,
134+
"lower_bound": wall_boundary.variables[var].lower_bound,
135+
"upper_bound": wall_boundary.variables[var].upper_bound
136+
}
137+
for var in ["x1", "x2", "dz"]
138+
}
139+
run.update_metadata({"bluemira_parameters": _metadata})
140+
141+
# Create and upload an image of the initial design to Simvue
142+
_plot = plot_2d([wall_boundary.create_shape(), plasma])
143+
_fig = _plot.get_figure()
144+
run.save_object(_fig, category="input", name="initial_shape")
145+
146+
# Optimise our geometry using a gradient descent method
147+
result = optimise_geometry(
148+
wall_boundary,
149+
algorithm="SLSQP",
150+
f_objective=f_objective,
151+
opt_conditions={"ftol_abs": 1e-6},
152+
keep_history=True,
153+
ineq_constraints=[
154+
{
155+
"f_constraint": lambda g: distance_constraint(g, plasma, min_distance, run),
156+
"tolerance": np.array([1e-8]),
157+
},
158+
],
159+
)
160+
161+
# Print final results after optimisation
162+
print("Optimised parameterisation:")
163+
print(result.geom.variables)
164+
165+
boundary = result.geom.create_shape()
166+
print(f"Length of wall : {boundary.length}")
167+
print(f"Distance to plasma: {distance_to(boundary, plasma)[0]}")
168+
169+
# Update metadata with final optimised values
170+
_metadata = {
171+
var: {
172+
"final": result.geom.variables[var].value,
173+
}
174+
for var in ["x1", "x2", "dz"]
175+
}
176+
run.update_metadata({"bluemira_parameters": _metadata})
177+
178+
# Create and upload an image of the optimised design to Simvue
179+
_plot = plot_2d([boundary, plasma])
180+
_fig = _plot.get_figure()
181+
run.save_object(_fig, category="output", name="final_shape")
182+
183+
# Use the history to create and upload an image of the design iterations
184+
geom = PrincetonD()
185+
ax = plot_2d(plasma, show=False)
186+
for i, (x, _) in enumerate(result.history):
187+
geom.variables.set_values_from_norm(x)
188+
wire = geom.create_shape()
189+
wire_options = {
190+
"alpha": 0.5 + ((i + 1) / len(result.history)) / 2,
191+
"color": "red",
192+
"linewidth": 0.1,
193+
}
194+
ax = plot_2d(wire, options=PlotOptions(wire_options=wire_options), ax=ax, show=False)
195+
_plot = plot_2d(boundary, ax=ax, show=True)
196+
_fig = _plot.get_figure()
197+
run.save_object(_fig, category="output", name="design_iterations")

0 commit comments

Comments
 (0)