Skip to content

V2.0 RC 1 #749

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
Mar 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Change log

## [v2.0.0-rc1](https://github.com/simvue-io/client/releases/tag/v2.0.0rc1) - 2025-03-06
* Add new example notebooks
* Update and refactor examples to work with v2.0
* Fix bug in offline artifacts using wrong file path
* Change names of sustainability metrics
* Fix `Self` being used in typing Generators so that Simvue works with Python 3.10 in Conda

## [v2.0.0-alpha3](https://github.com/simvue-io/client/releases/tag/v2.0.0a3) - 2025-03-04
* Updated codecarbon to work with new API
* Codecarbon now works with offline mode
Expand Down
6 changes: 3 additions & 3 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ keywords:
- alerting
- simulation
license: Apache-2.0
commit: 64ff8a5344232d44fc7da5b6ff601d3023497977
version: 2.0.0a3
date-released: '2025-03-04'
commit: effbd2e88fa12a181bf33721eae599d4245e1484
version: 2.0.0rc1
date-released: '2025-03-06'
references:
- title: mlco2/codecarbon
version: v2.8.2
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Collect metadata, metrics and artifacts from simulations, processing and AI/ML t

<div align="center">
<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>
<img src="https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12-blue">
<img src="https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue">
<a href="https://pypi.org/project/simvue/" target="_blank"><img src="https://img.shields.io/pypi/v/simvue.svg"/></a>
<a href="https://pepy.tech/project/simvue"><img src="https://static.pepy.tech/badge/simvue"/></a>
<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>
Expand Down
21 changes: 21 additions & 0 deletions examples/Bluemira/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Geometry optimisation using Bluemira

[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.


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

Once you have Bluemira installed and are running the `bluemita` conda environment (or similar), install Simvue with the plotting extras:
```
pip install simvue[plot]
```
Then move into the example's directory:
```
cd examples/Bluemira
```
Make a simvue.toml file - click Create New Run on the web UI, copy the contents listed, and paste into a config file.

Finally, run the example:
```
python geometry_optimisation.py
```
197 changes: 197 additions & 0 deletions examples/Bluemira/geometry_optimisation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
# 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.
#
# Copyright (C) 2021 M. Coleman, J. Cook, F. Franza, I.A. Maione, S. McIntosh,
# J. Morris, D. Short
#
# bluemira is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# bluemira is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with bluemira; if not, see <https://www.gnu.org/licenses/>.
"""
Geometry Optimisation

Example taken from: bluemira/examples/optimisation/geometry_optimisation.ex.py

In this example we will go through how to set up a simple geometry
optimisation, including a geometric constraint.

The problem to solve is, minimise the length of our wall boundary,
in the xz-plane, whilst keeping it a minimum distance from our plasma.

We will greatly simplify this problem by working with a circular
plasma, we will use a PrincetonD for the wall shape,
and set the minimum distance to half a meter.
"""

import numpy as np
import os
import sys
from bluemira.display import plot_2d
from bluemira.display.plotter import PlotOptions
from bluemira.geometry.optimisation import optimise_geometry
from bluemira.geometry.parameterisations import GeometryParameterisation, PrincetonD
from bluemira.geometry.tools import distance_to, make_circle
from bluemira.geometry.wire import BluemiraWire

import simvue

def f_objective(geom: GeometryParameterisation) -> float:
"""Objective function to minimise a shape's length."""
return geom.create_shape().length


def distance_constraint(
geom: GeometryParameterisation, boundary: BluemiraWire, min_distance: float, run: simvue.Run
) -> float:
"""
A constraint to keep a minimum distance between two shapes.

The constraint must be in the form f(x) <= 0, i.e., constraint
is satisfied if f(x) <= 0.

Since what we want is 'min_distance <= distance(A, B)', we rewrite
this in the form 'min_distance - distance(A, B) <= 0', and return
the left-hand side from this function.
"""
shape = geom.create_shape()
# Log all variables as metrics after each iteration, giving human readable names:
run.log_metrics(
{
"inboard_limb_radius": float(geom.variables["x1"].value),
"outboard_limb_radius": float(geom.variables["x2"].value),
"vertical_offset": float(geom.variables["dz"].value),
"length_of_wall": float(shape.length),
"distance_to_plasma": float(distance_to(shape, boundary)[0])
}
)
return min_distance - distance_to(shape, boundary)[0]

# The original example prints stuff to the console to track progress
# Instead of changing these lines to log events (since we probably want both),
# We can make a class which intercepts stdout and also sends messages to Simvue
class StdoutToSimvue():
def __init__(self, run: simvue.Run):
self.run = run

def write(self, message: str):
# Log the message as an event (so long as it isnt a blank line)
if message.strip():
run.log_event(message)
# And print to console as normal
sys.__stdout__.write(message)

def flush(self):
sys.__stdout__.flush()

# Here we will start doing our optimisation. First create a Simvue run,
# using the Run class as a context manager:
with simvue.Run() as run:
# Initialise our run:
run.init(
name="bluemira_geometry_optimisation",
folder="/simvue_client_demos",
visibility="tenant" if os.environ.get("CI") else None,
tags=["bluemira", "simvue_client_examples"],
description="Minimise the length of a parameterised geometry using gradient-based optimisation algorithm.",
)

# Redirect stdout so that print statements also get logged as events:
stdout_sender = StdoutToSimvue(run)
sys.stdout = stdout_sender

# Next define the shape of our plasma, and the minimum distance we want between
# our wall boundary and our plasma:
min_distance = 0.5
plasma = make_circle(radius=2, center=(8, 0, 0.25), axis=(0, 1, 0))

# As with any optimisation, it's important to pick a reasonable initial
# parameterisation.
wall_boundary = PrincetonD({
"x1": {"value": 4, "upper_bound": 6},
"x2": {"value": 12, "lower_bound": 10},
})

print("Initial parameterisation:")
print(wall_boundary.variables)
print(f"Length of wall : {wall_boundary.create_shape().length}")
print(f"Distance to plasma: {distance_to(wall_boundary.create_shape(), plasma)[0]}")

# Create metadata for our original parameters:
_metadata = {
var: {
"initial": wall_boundary.variables[var].value,
"lower_bound": wall_boundary.variables[var].lower_bound,
"upper_bound": wall_boundary.variables[var].upper_bound
}
for var in ["x1", "x2", "dz"]
}
run.update_metadata({"bluemira_parameters": _metadata})

# Create and upload an image of the initial design to Simvue
_plot = plot_2d([wall_boundary.create_shape(), plasma])
_fig = _plot.get_figure()
run.save_object(_fig, category="input", name="initial_shape")

# Optimise our geometry using a gradient descent method
result = optimise_geometry(
wall_boundary,
algorithm="SLSQP",
f_objective=f_objective,
opt_conditions={"ftol_abs": 1e-6},
keep_history=True,
ineq_constraints=[
{
"f_constraint": lambda g: distance_constraint(g, plasma, min_distance, run),
"tolerance": np.array([1e-8]),
},
],
)

# Print final results after optimisation
print("Optimised parameterisation:")
print(result.geom.variables)

boundary = result.geom.create_shape()
print(f"Length of wall : {boundary.length}")
print(f"Distance to plasma: {distance_to(boundary, plasma)[0]}")

# Update metadata with final optimised values
_metadata = {
var: {
"final": result.geom.variables[var].value,
}
for var in ["x1", "x2", "dz"]
}
run.update_metadata({"bluemira_parameters": _metadata})

# Create and upload an image of the optimised design to Simvue
_plot = plot_2d([boundary, plasma])
_fig = _plot.get_figure()
run.save_object(_fig, category="output", name="final_shape")

# Use the history to create and upload an image of the design iterations
geom = PrincetonD()
ax = plot_2d(plasma, show=False)
for i, (x, _) in enumerate(result.history):
geom.variables.set_values_from_norm(x)
wire = geom.create_shape()
wire_options = {
"alpha": 0.5 + ((i + 1) / len(result.history)) / 2,
"color": "red",
"linewidth": 0.1,
}
ax = plot_2d(wire, options=PlotOptions(wire_options=wire_options), ax=ax, show=False)
_plot = plot_2d(boundary, ax=ax, show=True)
_fig = _plot.get_figure()
run.save_object(_fig, category="output", name="design_iterations")
56 changes: 0 additions & 56 deletions examples/FDS/activate_vents.fds

This file was deleted.

47 changes: 0 additions & 47 deletions examples/FDS/fds_unlim

This file was deleted.

Loading
Loading