When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change.
This project and everyone participating in it is governed by the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to matti.coleman@ukaea.uk and/or ivan.maione@kit.edu.
In order to help us address your issue effectively, we ask that you follow this procedure when creating an issue:
- Check that the issue is not already described elsewhere in Issues
- Write a fairly complete description of the bug/feature/problem in the issue, using the provided templates and adding the relevant tags
- Tag the
bluemira
module(s) the issue concerns - If the issue is linked to a Project, please tag it
Bluemira
is software in development and is therefore likely to contain bugs. If you
discover bugs, please follow this procedure:
- Raise an issue using the bug template with a
bug
flag - Include a way to reproduce the bug in the issue, let us know the expected result and what actually happens
Please discuss any feature ideas you have with the developers before submitting them, as you may not be aware of parallel development work taking place, or implementation decisions / subtleties which are relevant. The ideal workflow for submitting a pull request is as follows:
- Discuss the feature with the developers first
- Submit an issue documenting the intent of the feature you wish to develop
- Fork our repository (see the GitHub documentation on forking)
- Make a branch of the
develop
branch which bears a similar name as your issue (e.g.new_feature
) - Develop your feature(s) in your
new_feature
branch - Discuss any problems that arise in the associated issue, perhaps documenting your progress
- Finally, as the author of the
new_feature
branch, you must submit a pull request onto thedevelop
branch- Link the relevant issue(s) and project(s)
- Add an assignee, who will be responsible for reviewing and merging the PR. This should be the person you feel has the most relevant technical background and/or the most familiar with the underlying issue or code.
- The reviewers will be automatically selected based on which module(s) of the code your pull request affects.
The merge request will be reviewed by the core development team before potentially being accepted .
Bluemira
is an object-oriented code and is designed to run relatively fast. We use
objects to represent physical entities, as well as for certain solvers where it is
useful to persist the state of some stored calculations in order to save run-time.
When writing objects, make sure all attributes are initialised in the __init__
method,
and that all methods and variables that are not user-facing are made protected with a
leading underscore: _protected_method
.
We try to keep functions as single-purposes as possible, with as few arguments and keyword arguments as possible. Similarly, with classes, we try to keep the number of attributes and methods relatively low.
Python can run relatively slowly when compared with compiled languages, and in key areas we use a couple of tricks to speed things up:
- Using
__slots__
on classes to save memory - Using
numba
just-in-time compilation to speed up certain bottleneck calculations
Please try and follow these relatively loose guidelines when developing bluemira
.
We also recommend you use an integrated development environment with appropriate code
linting to improve the code you contribute.
Bluemira
is strictly auto-formatted using the ruff
format module which is very similar to black.
In both cases this is an opinionated subset of the
Python PEP8 style guide.
If you don't like how ruff
formats your code, join the club... the loss of
aesthetics is the price to pay for uniformity and consistency!
The point of using it is that all code should more or less look the same, regardless of who writes it.
Please read up on this if you need more details. For bluemira
,
ruff
has the potential to generate very ugly code, especially if:
- you are playing code golf
- you are writing data tables of some form
- you are coding out long mathematical formulae
For the first case, it is probably best you don't do this...
If ruff
breaks your one-line or chain calls into multiple lines, consider breaking these down for readability.
In some cases, it is nicer to turn off the auto-formatting. You can do this as follows:
# fmt: off
default_params = [
['shaf_shift', 'Shafranov shift of plasma (geometric=>magnetic)', 0.5, 'm', None, 'Input'],
['lambda_q', 'Scrape-off layer power decay length', 0.003, 'm', None, 'Input']
]
# fmt: on
Bluemira is also checked for quality using the ruff linter which implements flake8 rules and various extensions to it. This is more a question of code style, which just the formatter doesn't cover in full.
Code that is committed to a branch is automatically checked for quality using pre-commit.
Violations detected by our code quality checks are printed to the console for information.
To avoid checks for a single commit you can use the --no-verify
flag when committing.
The checks do not prevent you from pushing code, but the continuous integration checks will fail.
Please address issues raised by the checks prior to pushing your code.
When writing code for bluemira
, please adhere to the following Python naming
conventions:
-
ALL_CAPITALS_SNAKE_CASE
for global constants -
CapitalisedWords
for classes -
lower_case_snake_case
for functions and methods -
lower_case_snake_case
for arguments and variables
Abbreviations are allowed to be in capitals, e.g.:
show_CAD
build_TF_coils
A full list of accepted abbreviations can be found in the pyproject.toml
file under
ignore_names
.
We try to stick to descriptive lower_case_snake_case
argument and local variable names with the general rule that names less than three characters should not be used.
This is not a hard rule, and there are some notable exceptions:
i
,j
,k
for integers (e.g. looping, indexing)m
,n
for integers (e.g. array sizes)x
,y
,z
,r
for floats or arrays describing coordinates- Where it makes sense to reflect mathematical notation (e.g.
R_0
)
-
All imports should be absolute imports, eg:
from bluemira.base.components import Component
not this
from ..base.components import Component
We enforce this with the
flake8
rules inruff
. -
Imports between bluemira modules should access individual methods directly.
-
Wildcard imports should not be used as it pollutes the namespace and makes it hard to work out where a method originates.
-
Some external modules such as
numpy
andmatplotlib.pyplot
have specific import styles widely used elsewhere, please look for examples inbluemira
if unsure:import numpy as np import matplotlib.pyplot as plt
If only a few methods are used from these types of modules directly importing the method is preferred.
-
Formatting of imports is automatically organised by the rules provided by
isort
andruff
.isort
organises imports into three sections; builtin modules, external modules and internal modules.
All of the above means that this is bad:
import bluemira.base as base
from ..geometry import tools
import os
from scipy import *
and this is good:
from os import chdir
from scipy.interpolate import interp1d
import numpy as np
from bluemira.base.builder import Builder
from bluemira.geometry.tools import make_circle
Please write all written documentation in .rst.
format.
Please write all Python docstrings in numpydoc
style, see details here. Typing should be specified
with type-hints. An example is shown below:
def IPB98y2(
I_p: float, B_t: float, P_sep: float, n: float, R_0: float, A: float, kappa: float
) -> float:
"""
ITER IPB98(y, 2) Confinement time scaling [2]
Parameters
----------
I_p:
Plasma current [A]
B_t:
Toroidal field at R_0 [T]
P_sep:
Separatrix power [W]
n:
Line average plasma density [1/m^3]
R_0:
Major radius [m]
A:
Aspect ratio
kappa:
Plasma elongation
Returns
-------
Energy confinement time [s]
Notes
-----
[2] :doi:`ITER Physics Expert Group, Nucl. Fus. 39, 12 <10.1088/0029-5515/39/12/302>`
\t:math:`\\tau_{E}=0.0562I_p^{0.93}B_t^{0.15}P_{sep}^{-0.69}n^{0.41}M^{0.19}R_0^{1.97}A^{-0.58}\\kappa^{0.78}`
"""
The API documentation is built automatically using sphinx
which reads docstrings and argument types
and processes them accordingly.
Pytest is used for all testing of bluemira. We aim for around 80% coverage of our code as a rough metric.
Bluemira
tests are grouped into classes for testing multiple pieces of functionality of a single class or function.
There are 3 user available extra command line flags to modify the running style of the tests:
--reactor
--longrun
--plotting-on
The plotting-on
flag will enable visualising any plots that are created in the tests. By default these are not displayed.
There are a lot of figures in the full test suite so it is recommended to only do this for a subset of the tests.
For information about reactor
and longrun
tests see below.
A further --private
flag is available where data is not public but tests against that data are useful.
If you have some private data that you would like to test against please contact a maintainer for more information.
Warning
Do not commit private data to the repository there is no simple way to remove that data once pushed. You will have to contact github support to fully remove the data as noted here.
Bluemira
has 4 custom pytest marks which can be used to decorate tests (@pytest.mark.*
):
pytest.mark.reactor
Test only run with the--reactor
commandline flag. This is allocated for intergration tests or large systemspytest.mark.longrun
Test only run with the--longrun
commandline flag. If a test cannot be sped up and takes more than 20s consider flagging it.pytest.mark.private
Test only run with the--private
commandline flag. These tests require private data to runpytest.mark.classplot
These tests allow the production of a figure after all tests in a class have been run. Without this mark figures are shown and closed after every test. Useful for visual comparison of similar parameterisations of a test.
Release versions of bluemira
are generated from git tags, and setuptools-scm is used so that the most recent
tag is dynamically pulled into the bluemira
itself to set __version__
correctly. The main
branch will always contain the newest release.