Skip to content
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

Papercut: enumify text based choices #3021

Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
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
22 changes: 20 additions & 2 deletions bluemira/builders/pf_coil.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"""

from dataclasses import dataclass
from enum import Enum, auto
from typing import Dict, List, Union

from bluemira.base.builder import Builder
Expand All @@ -24,6 +25,22 @@
from bluemira.geometry.wire import BluemiraWire


class CoilType(Enum):
"""Enumification of text based choice of coil type"""

PF = auto()
CS = auto()

@classmethod
def _missing_(cls, value):
try:
return cls[value.upper()]
except KeyError:
raise ValueError(

Check warning on line 39 in bluemira/builders/pf_coil.py

View check run for this annotation

Codecov / codecov/patch

bluemira/builders/pf_coil.py#L38-L39

Added lines #L38 - L39 were not covered by tests
f"{value} is not a valid CoilType. Choose from: PF or CS"
) from None


@dataclass
class PFCoilBuilderParams(ParameterFrame):
"""
Expand Down Expand Up @@ -79,7 +96,8 @@
c2 = make_circle(r_in)

wp = PhysicalComponent(self.WINDING_PACK, BluemiraFace([c1, c2]))
idx = 0 if self.params.ctype.value == "CS" else 1

idx = CoilType(self.params.ctype.value.upper()).value - 1
apply_component_display_options(wp, color=BLUE_PALETTE["PF"][idx])

r_in -= self.params.tk_insulation.value
Expand Down Expand Up @@ -113,7 +131,7 @@
Build the xz cross-section of the PF coil.
"""
wp = PhysicalComponent(self.WINDING_PACK, BluemiraFace(shape))
idx = 0 if self.params.ctype.value == "CS" else 1
idx = CoilType(self.params.ctype.value.upper()).value - 1
apply_component_display_options(wp, color=BLUE_PALETTE["PF"][idx])

ins_shape = offset_wire(shape, self.params.tk_insulation.value)
Expand Down
42 changes: 32 additions & 10 deletions bluemira/equilibria/equilibrium.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"""

from copy import deepcopy
from enum import Enum
from enum import Enum, auto
from pathlib import Path
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union

Expand Down Expand Up @@ -59,6 +59,26 @@
from bluemira.utilities.tools import abs_rel_difference


class VControlType(Enum):
oliverfunk marked this conversation as resolved.
Show resolved Hide resolved
"""
Enumification of text based numerical stabilisation strategy
choices for vertical position controller
"""

VIRTUAL = auto()
FEEDBACK = auto()

@classmethod
def _missing_(cls, value):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typing of value is missing def _missing_(cls, value: str):

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in PR #3036, commit 3c9ff6d.

try:
return cls[value.upper()]
except KeyError:
raise ValueError(

Check warning on line 76 in bluemira/equilibria/equilibrium.py

View check run for this annotation

Codecov / codecov/patch

bluemira/equilibria/equilibrium.py#L73-L76

Added lines #L73 - L76 were not covered by tests
"Please select a numerical stabilisation strategy"
' from: 1) "virtual" \n 2) "feedback" 3) None.'
Copy link
Contributor

@je-cook je-cook Feb 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please autofill the options with the cls._member_names_ attribute eg

f"{*cls._member_names_,}"

also the error message sounds like the user can input something (which they can't). Maybe something like:
f"{cls.__name__} has no strategy {value} please select from {*cls._member_names_,}"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in PR #3036, commit 3c9ff6d.

) from None


class MHDState:
"""
Base class for magneto-hydrodynamic states
Expand Down Expand Up @@ -1048,17 +1068,19 @@
vcontrol:
Vertical control strategy
"""
if vcontrol == "virtual":
self.controller = VirtualController(self, gz=2.2)
elif vcontrol == "feedback":
raise NotImplementedError
elif vcontrol is None:
if vcontrol is None:
oliverfunk marked this conversation as resolved.
Show resolved Hide resolved
self.controller = DummyController(self.plasma.psi())
else:
raise ValueError(
"Please select a numerical stabilisation strategy"
' from: 1) "virtual" \n 2) "feedback" 3) None.'
)
vcontrol_str = VControlType(vcontrol.upper())
if vcontrol_str is VControlType.VIRTUAL:
self.controller = VirtualController(self, gz=2.2)
elif vcontrol_str is VControlType.FEEDBACK:
raise NotImplementedError

Check warning on line 1078 in bluemira/equilibria/equilibrium.py

View check run for this annotation

Codecov / codecov/patch

bluemira/equilibria/equilibrium.py#L1074-L1078

Added lines #L1074 - L1078 were not covered by tests
else:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this error message will never be hit as it will error on L1073 on the initialisation of the enum, please remove the else

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in PR #3036, commit 3c9ff6d. removed else, put two ifs (instead of if-elif without else)

raise ValueError(

Check warning on line 1080 in bluemira/equilibria/equilibrium.py

View check run for this annotation

Codecov / codecov/patch

bluemira/equilibria/equilibrium.py#L1080

Added line #L1080 was not covered by tests
"Please select a numerical stabilisation strategy"
' from: 1) "virtual" \n 2) "feedback" 3) None.'
)

def solve(self, jtor: Optional[np.ndarray] = None, psi: Optional[np.ndarray] = None):
"""
Expand Down
27 changes: 23 additions & 4 deletions bluemira/equilibria/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from __future__ import annotations

import warnings
from enum import Enum, auto
from itertools import cycle
from typing import TYPE_CHECKING

Expand Down Expand Up @@ -95,6 +96,22 @@
}


class CoilType(Enum):
oliverfunk marked this conversation as resolved.
Show resolved Hide resolved
"""Enumification of text based choice of coil type"""

PF = auto()
CS = auto()

@classmethod
def _missing_(cls, value):
try:
return cls[value.upper()]
except KeyError:
raise ValueError(

Check warning on line 110 in bluemira/equilibria/plotting.py

View check run for this annotation

Codecov / codecov/patch

bluemira/equilibria/plotting.py#L107-L110

Added lines #L107 - L110 were not covered by tests
f"{value} is not a valid CoilType. Choose from: PF or CS"
) from None


class Plotter:
"""
Utility plotter abstract object
Expand Down Expand Up @@ -235,9 +252,10 @@
for i, (x, z, dx, x_b, z_b, ct, n, cur, ctrl) in enumerate(zip(*arrays)):
if ctrl:
if self.colors is not None:
if ct.name == "PF":
ctype = CoilType(ct.name.upper())
if ctype is CoilType.PF:

Check warning on line 256 in bluemira/equilibria/plotting.py

View check run for this annotation

Codecov / codecov/patch

bluemira/equilibria/plotting.py#L255-L256

Added lines #L255 - L256 were not covered by tests
kwargs["facecolor"] = self.colors[0]
elif ct.name == "CS":
elif ctype is CoilType.CS:

Check warning on line 258 in bluemira/equilibria/plotting.py

View check run for this annotation

Codecov / codecov/patch

bluemira/equilibria/plotting.py#L258

Added line #L258 was not covered by tests
kwargs["facecolor"] = self.colors[1]

self._plot_coil(
Expand Down Expand Up @@ -307,7 +325,8 @@
Single coil annotation utility function
"""
off = max(0.2, dx + 0.02)
if ctype.name == "CS":
ctype_name = CoilType(ctype.name.upper())
if ctype_name is CoilType.CS:

Check warning on line 329 in bluemira/equilibria/plotting.py

View check run for this annotation

Codecov / codecov/patch

bluemira/equilibria/plotting.py#L328-L329

Added lines #L328 - L329 were not covered by tests
drs = -1.5 * off
ha = "right"
else:
Expand All @@ -318,7 +337,7 @@
text = "\n".join([text, f"{raw_uc(force[1], 'N', 'MN'):.2f} MN"])
x = float(x) + drs
z = float(z)
if centre is not None and ctype.name == "PF":
if centre is not None and ctype_name is CoilType.PF:

Check warning on line 340 in bluemira/equilibria/plotting.py

View check run for this annotation

Codecov / codecov/patch

bluemira/equilibria/plotting.py#L340

Added line #L340 was not covered by tests
v = np.array([x - centre[0], z - centre[1]])
v /= np.sqrt(sum(v**2))
d = 1 + np.sqrt(2) * dx
Expand Down
43 changes: 32 additions & 11 deletions bluemira/equilibria/positioner.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import re
from copy import deepcopy
from typing import Dict, List, Tuple, Union

from enum import Enum,auto
import numpy as np
from scipy.interpolate import InterpolatedUnivariateSpline, interp1d
from scipy.optimize import minimize_scalar
Expand Down Expand Up @@ -40,6 +40,30 @@
from bluemira.geometry.tools import boolean_cut, boolean_fuse, make_polygon, offset_wire
from bluemira.utilities import tools

class ReactorType(Enum):
"""Enumification of text based choices of the type of reactor"""

NORMAL = auto()
ST = auto()
Comment on lines +46 to +47
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont like these names, they're quite restrictive and not very descriptive.
possible replacements

CONVENTIONAL_TOKAMAK = auto()
SPHERICAL_TOKAMAK = auto()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in #3036, commit b4e1ad1


@classmethod
def _missing_(cls, value):
try:
return cls[value.upper()]
except KeyError:
raise ValueError(f"{value} is a wrong ReactorType. Choose from: ST or Normal") from None

Check warning on line 54 in bluemira/equilibria/positioner.py

View check run for this annotation

Codecov / codecov/patch

bluemira/equilibria/positioner.py#L53-L54

Added lines #L53 - L54 were not covered by tests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as previous enum comments, no typing on value and error message:
f"{cls.__name__} has no entry {value} please select from {*cls._member_names_,}"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in #3036, commit b4e1ad1

class CoilsetLayoutType(Enum):
"""Enumification of text based choices for the layout of the CS modules"""

ITER = auto()
DEMO = auto()

@classmethod
def _missing_(cls, value):
try:
return cls[value.upper()]
except KeyError:
raise ValueError(f"{value} is not a valid coilset Layout. Choose from: 'ITER' and 'DEMO'") from None

Check warning on line 66 in bluemira/equilibria/positioner.py

View check run for this annotation

Codecov / codecov/patch

bluemira/equilibria/positioner.py#L65-L66

Added lines #L65 - L66 were not covered by tests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typing of value and message f"{value} is not a valid coilset Layout. please select from {*cls._member_names_,}"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in #3036, commit b4e1ad1


class CoilPositioner:
"""
Expand Down Expand Up @@ -99,8 +123,8 @@
self.n_PF = n_PF
self.n_CS = n_CS
self.csgap = csgap
self.rtype = rtype
self.cslayout = cslayout
self.rtype = ReactorType(rtype.upper())
self.cslayout = CoilsetLayoutType(cslayout.upper())

def equispace_PF(self, track: Coordinates, n_PF: int) -> List[Coil]:
"""
Expand All @@ -109,10 +133,11 @@
based on plasma shape considerations (mirror about X-points)
"""
a = np.rad2deg(np.arctan(abs(self.delta) / self.kappa))
if self.rtype == "Normal":

if self.rtype is ReactorType.NORMAL:
angle_upper = 90 + a * 1.6
angle_lower = -90 - a * 1.6
elif self.rtype == "ST":
elif self.rtype is ReactorType.ST:

Check warning on line 140 in bluemira/equilibria/positioner.py

View check run for this annotation

Codecov / codecov/patch

bluemira/equilibria/positioner.py#L140

Added line #L140 was not covered by tests
angle_upper = 90 + a * 1.2
angle_lower = -90 - a * 1.0

Expand Down Expand Up @@ -224,18 +249,14 @@
z_max = max(self.track.z)
z_min = -z_max
if self.n_CS != 0:
if self.cslayout == "ITER":
if self.cslayout is CoilsetLayoutType.ITER:
coils.append(
self.equispace_CS(self.x_cs, self.tk_cs, z_min, z_max, self.n_CS)
)
elif self.cslayout == "DEMO":
elif self.cslayout is CoilsetLayoutType.DEMO:
coils.extend(
self.demospace_CS(self.x_cs, self.tk_cs, z_min, z_max, self.n_CS)
)
else:
raise ValueError(
f"Valid options are 'ITER' and 'DEMO', not '{self.cslayout}'"
)
cset = CoilSet(*coils)
cset.discretisation = d_coil
return cset
Expand Down
36 changes: 30 additions & 6 deletions bluemira/fuel_cycle/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

if TYPE_CHECKING:
from bluemira.fuel_cycle.cycle import EUDEMOFuelCycleModel
from enum import Enum, auto

Check warning on line 17 in bluemira/fuel_cycle/analysis.py

View check run for this annotation

Codecov / codecov/patch

bluemira/fuel_cycle/analysis.py#L17

Added line #L17 was not covered by tests

import matplotlib.pyplot as plt
import numpy as np
Expand All @@ -24,6 +25,25 @@
__all__ = ["FuelCycleAnalysis"]


class QueryType(Enum):

Check warning on line 28 in bluemira/fuel_cycle/analysis.py

View check run for this annotation

Codecov / codecov/patch

bluemira/fuel_cycle/analysis.py#L28

Added line #L28 was not covered by tests
"""Enumification of text based choices for the type of statistical value to return"""

MIN = auto()
MAX = auto()
MEAN = auto()
MEDIAN = auto()
P95TH = auto() # identifier should start with a letter

Check warning on line 35 in bluemira/fuel_cycle/analysis.py

View check run for this annotation

Codecov / codecov/patch

bluemira/fuel_cycle/analysis.py#L31-L35

Added lines #L31 - L35 were not covered by tests

@classmethod
def _missing_(cls, value):
try:
return cls[value.upper()]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can also deal with the 95th here eg

if value == "95th":
    return cls.P95TH

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in #3036, commit f564879

except KeyError:
raise ValueError(

Check warning on line 42 in bluemira/fuel_cycle/analysis.py

View check run for this annotation

Codecov / codecov/patch

bluemira/fuel_cycle/analysis.py#L37-L42

Added lines #L37 - L42 were not covered by tests
f"Invalid query: {value}. Choose from: min, max, mean, median, 95th"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix typing and message f"Invalid query: {value}. Choose from: {*cls._member_names_,}"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in #3036, commit f564879

) from None


class FuelCycleAnalysis:
"""
Analysis class for compiling and analysing fuel cycle statistics.
Expand Down Expand Up @@ -117,17 +137,21 @@
return self._query("t_d", s=query)

def _query(self, p: str, s: str) -> float:
if s == "min":
inp_s = QueryType.P95TH if s == "95th" else QueryType(s.upper())

Check warning on line 140 in bluemira/fuel_cycle/analysis.py

View check run for this annotation

Codecov / codecov/patch

bluemira/fuel_cycle/analysis.py#L140

Added line #L140 was not covered by tests

if inp_s is QueryType.MIN:

Check warning on line 142 in bluemira/fuel_cycle/analysis.py

View check run for this annotation

Codecov / codecov/patch

bluemira/fuel_cycle/analysis.py#L142

Added line #L142 was not covered by tests
return min(self.__dict__[p])
if s == "max":
if inp_s is QueryType.MAX:

Check warning on line 144 in bluemira/fuel_cycle/analysis.py

View check run for this annotation

Codecov / codecov/patch

bluemira/fuel_cycle/analysis.py#L144

Added line #L144 was not covered by tests
return max(self.__dict__[p])
if s == "mean":
if inp_s is QueryType.MEAN:

Check warning on line 146 in bluemira/fuel_cycle/analysis.py

View check run for this annotation

Codecov / codecov/patch

bluemira/fuel_cycle/analysis.py#L146

Added line #L146 was not covered by tests
return np.mean(self.__dict__[p])
if s == "median":
if inp_s is QueryType.MEDIAN:

Check warning on line 148 in bluemira/fuel_cycle/analysis.py

View check run for this annotation

Codecov / codecov/patch

bluemira/fuel_cycle/analysis.py#L148

Added line #L148 was not covered by tests
return np.median(self.__dict__[p])
if s == "95th":
if inp_s is QueryType.P95TH:

Check warning on line 150 in bluemira/fuel_cycle/analysis.py

View check run for this annotation

Codecov / codecov/patch

bluemira/fuel_cycle/analysis.py#L150

Added line #L150 was not covered by tests
return np.percentile(self.__dict__[p], 95)
raise ValueError(f"Unknown query: '{s}'")
raise ValueError(

Check warning on line 152 in bluemira/fuel_cycle/analysis.py

View check run for this annotation

Codecov / codecov/patch

bluemira/fuel_cycle/analysis.py#L152

Added line #L152 was not covered by tests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this error will never be hit it will have already errored at L140

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed in PR #3036, followed if-elif-else approach, commit 22ee7a0

f"Unknown query: '{s}'. Choose from: min, max, mean, median, 95th"
)

def plot(self, figsize=(12, 6), bins=20, **kwargs):
"""
Expand Down
22 changes: 20 additions & 2 deletions bluemira/fuel_cycle/lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import json
from dataclasses import dataclass, field
from enum import Enum, auto

Check warning on line 14 in bluemira/fuel_cycle/lifecycle.py

View check run for this annotation

Codecov / codecov/patch

bluemira/fuel_cycle/lifecycle.py#L14

Added line #L14 was not covered by tests
from typing import TYPE_CHECKING, Dict, List, Optional, Union

import matplotlib.pyplot as plt
Expand All @@ -31,6 +32,22 @@
__all__ = ["LifeCycle"]


class PlotType(Enum):

Check warning on line 35 in bluemira/fuel_cycle/lifecycle.py

View check run for this annotation

Codecov / codecov/patch

bluemira/fuel_cycle/lifecycle.py#L35

Added line #L35 was not covered by tests
"""Enumification of text based choices for the type of plot"""

PIE = auto()
BAR = auto()

Check warning on line 39 in bluemira/fuel_cycle/lifecycle.py

View check run for this annotation

Codecov / codecov/patch

bluemira/fuel_cycle/lifecycle.py#L38-L39

Added lines #L38 - L39 were not covered by tests

@classmethod
def _missing_(cls, value):
try:
return cls[value.upper()]
except KeyError:
raise ValueError(

Check warning on line 46 in bluemira/fuel_cycle/lifecycle.py

View check run for this annotation

Codecov / codecov/patch

bluemira/fuel_cycle/lifecycle.py#L41-L46

Added lines #L41 - L46 were not covered by tests
f"{value} is not a valid plot type. Choose from: pie or bar"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typing and error message

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in PR #3036, commit 878ddf4

) from None


class LifeCycle:
"""
A life cycle object for a fusion reactor.
Expand Down Expand Up @@ -412,7 +429,8 @@
self.total_planned_maintenance,
self.t_unplanned_m,
]
if typ == "pie":
plt_typ = PlotType(typ.upper())
if plt_typ is PlotType.PIE:

Check warning on line 433 in bluemira/fuel_cycle/lifecycle.py

View check run for this annotation

Codecov / codecov/patch

bluemira/fuel_cycle/lifecycle.py#L432-L433

Added lines #L432 - L433 were not covered by tests
plt.pie(
sizes,
labels=labels,
Expand All @@ -422,7 +440,7 @@
counterclock=False,
)
plt.axis("equal")
elif typ == "bar":
elif plt_typ is PlotType.BAR:

Check warning on line 443 in bluemira/fuel_cycle/lifecycle.py

View check run for this annotation

Codecov / codecov/patch

bluemira/fuel_cycle/lifecycle.py#L443

Added line #L443 was not covered by tests
bottom = 0
for i, s in enumerate(sizes):
ax.bar(1, s / sum(sizes) * 100, bottom=bottom, label=labels[i])
Expand Down
Loading