diff --git a/examples/introduction.ex.py b/examples/introduction.ex.py index db216070b4..acaaae5669 100644 --- a/examples/introduction.ex.py +++ b/examples/introduction.ex.py @@ -14,6 +14,8 @@ # name: python3 # --- +# %% +"""An example to introduce running PROCESS""" # %% [markdown] # # Introduction to running PROCESS # diff --git a/examples/optimum_solutions_comparison.ex.py b/examples/optimum_solutions_comparison.ex.py index 540783db03..5d10a23e6f 100644 --- a/examples/optimum_solutions_comparison.ex.py +++ b/examples/optimum_solutions_comparison.ex.py @@ -13,7 +13,8 @@ # language: python # name: python3 # --- - +# %% +"""An example to compare optimum solutions from PROCESS""" # %% [markdown] # # Optimum solutions comparison notebook # diff --git a/examples/scan.ex.py b/examples/scan.ex.py index 267a1d6f63..814e608939 100644 --- a/examples/scan.ex.py +++ b/examples/scan.ex.py @@ -14,6 +14,8 @@ # name: python3 # --- +# %% +"""An example to run and visualise a PROCESS scan""" # %% [markdown] slideshow={"slide_type": "slide"} # # Running and visualising a PROCESS scan # diff --git a/examples/single_model_evaluation.ex.py b/examples/single_model_evaluation.ex.py index 873ed41ba4..18fa0506a1 100644 --- a/examples/single_model_evaluation.ex.py +++ b/examples/single_model_evaluation.ex.py @@ -14,6 +14,8 @@ # name: python3 # --- +# %% +"""An example to evaluate a single PROCESS model""" # %% [markdown] # # Evaluating a single PROCESS model # When understanding or investigating an individual model within Process, @@ -54,6 +56,7 @@ # Doesn't crash after running a once-through # Print initial values of interest def print_values(): + """Function to print values of some variables""" print( "W frac = " f"{single_run.data.impurity_radiation.f_nd_impurity_electron_array[13]:.3e}" diff --git a/examples/vary_run_example.ex.py b/examples/vary_run_example.ex.py index 3bb663421c..6e4d1b3018 100644 --- a/examples/vary_run_example.ex.py +++ b/examples/vary_run_example.ex.py @@ -14,6 +14,8 @@ # name: python3 # --- +# %% +"""An example to demonstrate VaryRun""" # %% [markdown] # # Demonstration of VaryRun diff --git a/process/core/caller.py b/process/core/caller.py index 149291bc2a..e618e51262 100644 --- a/process/core/caller.py +++ b/process/core/caller.py @@ -1,3 +1,5 @@ +"""Module to call physics and engineering models""" + from __future__ import annotations import logging diff --git a/process/core/constants.py b/process/core/constants.py index 5d709d273e..6ce3319a13 100644 --- a/process/core/constants.py +++ b/process/core/constants.py @@ -1,3 +1,5 @@ +"""Library of constants used in PROCESS.""" + IOTTY = 6 """Standard output unit identifier""" diff --git a/process/core/exceptions.py b/process/core/exceptions.py index c468e416dc..529e947c7c 100644 --- a/process/core/exceptions.py +++ b/process/core/exceptions.py @@ -1,3 +1,6 @@ +"""Base exceptions from which other PROCESS exceptions are derived""" + + class ProcessError(Exception): """A base Exception to derive other PROCESS exceptions from""" diff --git a/process/core/init.py b/process/core/init.py index 5aba77d345..a96251dc01 100644 --- a/process/core/init.py +++ b/process/core/init.py @@ -1,3 +1,5 @@ +"""Routines for PROCESS initialisation.""" + from __future__ import annotations import datetime @@ -238,6 +240,12 @@ def check_process(inputs, data): # noqa: ARG001 This routine performs a sanity check of the input variables and ensures other dependent variables are given suitable values. + + Raises + ------ + ProcessValidationError + If there is a problem with the contents of the input file. + See individual ProcessValidationError instances for more details. """ # Check that there are sufficient iteration variables if data.numerics.nvar < data.numerics.neqns: diff --git a/process/core/solver/iteration_variables.py b/process/core/solver/iteration_variables.py index ac79dc2789..c40f932ec1 100644 --- a/process/core/solver/iteration_variables.py +++ b/process/core/solver/iteration_variables.py @@ -1,3 +1,5 @@ +"""Module containing iteration variable initialisation routines""" + from __future__ import annotations import logging @@ -17,6 +19,8 @@ @dataclass class IterationVariable: + """Dataclass holding information about iteration variables""" + name: str """The name of the variable""" module: str | Any @@ -250,6 +254,12 @@ def check_iteration_variable(iteration_variable_value, name: str = ""): name: str : (Default value = "") + + Raises + ------ + ProcessValueError + If an iteration variable is 0 or very close, or if an iteration + variable is NaN or infinity """ if abs(iteration_variable_value) <= 1e-12: error_msg = f"Iteration variable {name} is 0 (or very close)" @@ -267,6 +277,11 @@ def check_iteration_variable(iteration_variable_value, name: str = ""): def load_iteration_variables(data): """ Loads the physics and engineering variables into the optimisation variable array. + + Raises + ------ + ProcessValueError + If iteration variable is missing """ for i in range(data.numerics.nvar): variable_index = data.numerics.ixc[i] diff --git a/process/core/solver/objectives.py b/process/core/solver/objectives.py index 52fcc96b88..40c00f6aa0 100644 --- a/process/core/solver/objectives.py +++ b/process/core/solver/objectives.py @@ -33,6 +33,11 @@ def objective_function(minmax: int, data: DataStructure) -> float: data: DataStructure data structure object for providing data to the objective function + + Raises + ------ + ProcessValueError + If minmax=15 not used with i_plant_availability=1 """ try: figure_of_merit = FiguresOfMerit(abs(minmax)) diff --git a/process/core/solver/solver.py b/process/core/solver/solver.py index a38e6aecf6..4409d3dee9 100644 --- a/process/core/solver/solver.py +++ b/process/core/solver/solver.py @@ -359,6 +359,11 @@ def get_solver(data: DataStructure, solver_name: str = "vmcon") -> _Solver: ------- _Solver solver to use for optimisation + + Raises + ------ + ProcessValueError + If solver name is not an inbuilt PROCESS solver or recognised package """ solver: _Solver @@ -390,6 +395,11 @@ def load_external_solver(package: str): ---------- package: str : + Raises + ------ + AttributeError + If module does not have a '__process_solver__' attribute + """ module = importlib.import_module(package) diff --git a/process/core/solver/solver_handler.py b/process/core/solver/solver_handler.py index b457e25ca9..c7c14c3af5 100644 --- a/process/core/solver/solver_handler.py +++ b/process/core/solver/solver_handler.py @@ -1,3 +1,5 @@ +"""Module containing solver handler routines""" + from process.core.solver.evaluators import Evaluators from process.core.solver.iteration_variables import ( load_iteration_variables, diff --git a/process/data/__init__.py b/process/data/__init__.py index e69de29bb2..4819830bb7 100644 --- a/process/data/__init__.py +++ b/process/data/__init__.py @@ -0,0 +1 @@ +"""Module containing impurity data""" diff --git a/process/data_structure/__init__.py b/process/data_structure/__init__.py index 97722324db..a2f6351e01 100644 --- a/process/data_structure/__init__.py +++ b/process/data_structure/__init__.py @@ -1,3 +1,5 @@ +"""Module containing data structure variables""" + from process.data_structure import ( blanket_variables, build_variables, diff --git a/process/main.py b/process/main.py index 7d2715c2c9..009317ab68 100644 --- a/process/main.py +++ b/process/main.py @@ -290,6 +290,7 @@ def __init__( @property def mfile_path(self): + """Mfile path""" return self.config.outfile def run(self): @@ -383,7 +384,15 @@ def set_filenames(self, filepath_out): self.set_mfile() def set_input(self): - """Validate and set the input file path.""" + """Validate and set the input file path. + + Raises + ------ + ValueError + If input filename doesn't end in 'IN.DAT' + FileNotFoundError + If input file not found + """ # Check input file ends in "IN.DAT", then save prefix # (the part before the IN.DAT) if not self.input_file.name.endswith("IN.DAT"): @@ -433,7 +442,13 @@ def initialise(self): self.data.numerics.ixc[:n].sort() def run_scan(self): - """Create scan object if required.""" + """Create scan object if required. + + Raises + ------ + ValueError + If invalid ioptimiz value selected + """ # TODO Move this solver logic up to init? # ioptimz == 1: optimisation if self.data.numerics.ioptimz == PROCESSRunMode.OPTIMISATION: @@ -488,6 +503,11 @@ def validate_input(self, replace_obsolete: bool = False): If obsolete variables are found, and if `replace_obsolete` is set to True, they are either removed or replaced by their updated names as specified in the OBS_VARS dictionary. + + Raises + ------ + ValueError + If obsolete variables are present in the input file. """ obsolete_variables = ov.OBS_VARS obsolete_vars_help_message = ov.OBS_VARS_HELP @@ -600,6 +620,11 @@ def validate_user_model(self): Ensures that the corresponding model variable in Models is defined and that any relevant switches are set correctly. + + Raises + ------ + ValueError + If user-created model not injected correctly """ # try and get costs model try: @@ -719,6 +744,14 @@ def __init__(self, data: DataStructure): @property def costs(self) -> Model: + """Set up cost model parameters + + Raises + ------ + ValueError + If custom costs not initialised, or if costs model + is unknown + """ if CostModels(self.data.costs.i_cost_model) == CostModels.PROCESS_1990: return self._costs_1990 if CostModels(self.data.costs.i_cost_model) == CostModels.KOVARI_2014: @@ -737,6 +770,7 @@ def costs(self, value: Model): @property def models(self) -> tuple[Model, ...]: + """Set up the models""" # At the moment, this property just returns models # that implement the Model interface. # Eventually every Model will comply and then @@ -796,6 +830,7 @@ def models(self) -> tuple[Model, ...]: ) def setup_data_structure(self): + """Set up the data structure""" # This Models class should be replaced with a dataclass so we can # iterate over the `fields`. # This can be a disgusting temporary measure :( diff --git a/process/models/__init__.py b/process/models/__init__.py index e69de29bb2..d1683e931a 100644 --- a/process/models/__init__.py +++ b/process/models/__init__.py @@ -0,0 +1 @@ +"""Module containing physics and engineering models""" diff --git a/process/models/availability.py b/process/models/availability.py index 06d953b928..a43102ef80 100644 --- a/process/models/availability.py +++ b/process/models/availability.py @@ -1,3 +1,5 @@ +"""Module containing plant availability routines""" + import logging import math @@ -40,6 +42,7 @@ def __init__(self): self.outfile = constants.NOUT # output file unit def output(self): + """Output availability information""" self.run(output=True) def run(self, output: bool = False): @@ -57,6 +60,12 @@ def run(self, output: bool = False): ---------- output : indicate whether output should be written to the output file, or not + + Raises + ------ + ProcessValueError + If i_plant_availability == 3, as this is for a spherical tokamak and + so need to use itart=1 for that model """ if self.data.costs.i_plant_availability == 3: if self.data.physics.itart != 1: diff --git a/process/models/blankets/__init__.py b/process/models/blankets/__init__.py index e69de29bb2..a98a6c0c2d 100644 --- a/process/models/blankets/__init__.py +++ b/process/models/blankets/__init__.py @@ -0,0 +1 @@ +"""Module containing blanket library routines""" diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index 1a6ab4a411..b381985142 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -1,4 +1,4 @@ -"""This library contains routines that can be shared by the blanket modules.""" +"""Library containing routines that can be shared by the blanket modules.""" import logging from enum import IntEnum @@ -54,16 +54,18 @@ class FWBlktCoolantLoopTypes(IntEnum): class BlanketLibrary(Model): + """Module containing blanket library routines""" + def __init__(self, fw): self.outfile = constants.NOUT self.fw = fw def output(self): - """This model doesn't have any output""" + """BlanketLibrary model doesn't have any output""" def run(self): - """This model doesn't need to be run""" + """BlanketLibrary model doesn't need to be run""" def component_volumes(self): """Calculate the blanket, shield, vacuum vessel and cryostat volumes @@ -697,6 +699,12 @@ def primary_coolant_properties(self, output: bool): ---------- output: bool + Raises + ------ + ProcessValueError + If there is an error in primary_coolant_properties. + See + """ # Make sure that, if the inputs for the FW and blanket inputs are different, # the i_fw_blkt_shared_coolant variable is @@ -2077,6 +2085,11 @@ def flow_velocity(self, i_channel_shape, mass_flow_rate, flow_density): Coolant mass flow rate per pipe (kg/s) flow_density : Coolant density + + Raises + ------ + ProcessValueError + If i_channel_shape is an invalid option (not in [1,2]) """ if i_channel_shape == 1: return mass_flow_rate / ( @@ -3330,6 +3343,11 @@ def pipe_hydraulic_diameter(self, i_channel_shape): i_channel_shape : switch for circular or rectangular channel crossection. Shape depends on whether primary or secondary coolant + + Raises + ------ + ProcessValueError + If i_channel_shape is an invalid option (not in [1,2]) """ # If primary coolant then circular channels assumed if i_channel_shape == 1: @@ -3373,6 +3391,12 @@ def elbow_coeff( float Elbow coefficient for pressure drop calculation + Raises + ------ + ProcessValueError + If 70 <= elbow angle(deg) <= 100 as there is no formula + for this range + References ---------- - [Ide1969] Idel'Cik, I. E. (1969), Memento des pertes de charge, @@ -3458,6 +3482,11 @@ def coolant_pumping_power( float Pumping power in MW. + Raises + ------ + ProcessValueError + If calculated pressure drops in the coolant are too large to be feasible + References ---------- - Idel'Cik, I. E. (1969), Memento des pertes de charge @@ -3589,7 +3618,10 @@ def coolant_pumping_power( class OutboardBlanket(BlanketLibrary): + """Outboard blanket routines""" + def calculate_basic_geometry(self): + """Calculate basic outboard blanket geometry""" self.component_volumes() dia_blkt_channel = self.pipe_hydraulic_diameter(i_channel_shape=1) @@ -3717,7 +3749,10 @@ def calculate_blanket_outboard_module_geometry( class InboardBlanket(BlanketLibrary): + """Inboard blanket routines""" + def calculate_basic_geometry(self): + """Calculate basic inboard blanket geometry""" self.component_volumes() dia_blkt_channel = self.pipe_hydraulic_diameter(i_channel_shape=1) diff --git a/process/models/blankets/dcll.py b/process/models/blankets/dcll.py index ea92e5c36d..82616d4293 100644 --- a/process/models/blankets/dcll.py +++ b/process/models/blankets/dcll.py @@ -1,3 +1,5 @@ +"""Module containing Dual Coolant Lead Lithium (DCLL) routines""" + from process.core import constants from process.core import ( process_output as po, @@ -12,8 +14,8 @@ class DCLL(InboardBlanket, OutboardBlanket): - """This module contains the Dual Coolant Lead Lithium (DCLL). - + """Module containing the Dual Coolant Lead Lithium (DCLL) + routines. Acronyms for this module: @@ -92,9 +94,11 @@ class DCLL(InboardBlanket, OutboardBlanket): """ def output(self): + """Output DCLL information""" self.run(output=True) def run(self, output: bool = False): + """Run DCLL routines""" self.component_volumes() # If Shafranov shift is added, the angle formula can be used where the shift is @@ -158,7 +162,7 @@ def run(self, output: bool = False): self.write_output() def dcll_neutronics_and_power(self, output: bool): - """This is a temporary module that will use results from + """Temporary module that will use results from CCFE Bluemira neutronics work (once completed). Database will provide values for power deposition in FW & BB, BB TBR, and neutron fluence at TF coil for different thicknesses of BB @@ -334,6 +338,7 @@ def dcll_neutronics_and_power(self, output: bool): ) def dcll_power_and_heating(self, output: bool): + """DCLL power and heating calculations""" # Mechanical Pumping # For i_p_coolant_pumping == 0: @@ -977,6 +982,7 @@ def dcll_masses(self, output: bool): ) def write_output(self): + """Output DCLL information""" # Component Volumes po.osubhd(self.outfile, "Component Volumes :") diff --git a/process/models/blankets/hcpb.py b/process/models/blankets/hcpb.py index cfebd974d3..8941f8de72 100644 --- a/process/models/blankets/hcpb.py +++ b/process/models/blankets/hcpb.py @@ -1,3 +1,5 @@ +"""Module containing the PROCESS CCFE HCPB blanket model""" + import logging import numpy as np @@ -21,7 +23,7 @@ class CCFE_HCPB(OutboardBlanket, InboardBlanket): - """This module contains the PROCESS CCFE HCPB blanket model + """Module containing the PROCESS CCFE HCPB blanket model based on CCFE HCPB model from the PROCESS engineering paper PROCESS Engineering paper (M. Kovari et al.) @@ -34,9 +36,11 @@ class CCFE_HCPB(OutboardBlanket, InboardBlanket): """ def output(self): + """Output CCFE HCPB information""" self.run(output=True) def run(self, output: bool = False): + """Run CCFE HCPB routines""" # Coolant type self.data.fwbs.i_blkt_coolant_type = CoolantType.HELIUM # Note that the first wall coolant is now input separately. @@ -1289,6 +1293,7 @@ def st_centrepost_nuclear_heating(self, pneut, sh_width): return pnuc_cp_tf, p_cp_shield_nuclear_heat_mw, pnuc_cp def write_output(self): + """Output CCFE HCPB information""" po.oheadr(self.outfile, "First wall and blanket : CCFE HCPB model") self.output_blkt_volumes_and_areas() diff --git a/process/models/build.py b/process/models/build.py index fa39c75274..7fa2b7b3e3 100644 --- a/process/models/build.py +++ b/process/models/build.py @@ -1,3 +1,5 @@ +"""Module containing routines for build calculations""" + import logging from enum import IntEnum @@ -29,11 +31,14 @@ class FwBlktVVShape(IntEnum): class Build(Model): + """Routines for build calculations""" + def __init__(self): self.outfile = constants.NOUT self.mfile = constants.MFILE def output(self): + """Output the build information""" # Radial build self.calculate_radial_build(output=True) @@ -41,6 +46,7 @@ def output(self): self.calculate_vertical_build(output=True) def run(self): + """Run the build routines""" self.calculate_radial_build(output=False) self.calculate_vertical_build(output=False) @@ -1630,7 +1636,7 @@ def plasma_outboard_edge_toroidal_ripple( return ripple, r_tf_outboard_midmin, flag def calculate_radial_build(self, output: bool): - """This method determines the radial build of the machine. + """Method determining the radial build of the machine. It calculates various parameters related to the build of the machine, such as thicknesses, radii, and areas. Results can be outputted with the `output` flag. diff --git a/process/models/buildings.py b/process/models/buildings.py index 48137e9cc0..e0cb9d6640 100644 --- a/process/models/buildings.py +++ b/process/models/buildings.py @@ -1,3 +1,5 @@ +"""Module containing routines for buildings calculations""" + import logging import numpy as np @@ -17,20 +19,22 @@ class Buildings(Model): """ - This module contains routines for calculating the + Module containing routines for buildings calculations """ def __init__(self): """ - This routine calls the buildings calculations. + Routine calling the buildings calculations. """ self.outfile = constants.NOUT # output file unit def output(self): + """Output buildings information""" self.run(output=True) def run(self, output: bool = False): + """Run the buildings models""" # Find TF coil radial positions # outboard edge: outboard mid-leg radial position + half-thickness # of outboard leg diff --git a/process/models/costs/__init__.py b/process/models/costs/__init__.py index e69de29bb2..2cac66828a 100644 --- a/process/models/costs/__init__.py +++ b/process/models/costs/__init__.py @@ -0,0 +1 @@ +"""Module containing cost calculations""" diff --git a/process/models/costs/costs.py b/process/models/costs/costs.py index cee4ff9c35..9f7bf7186c 100644 --- a/process/models/costs/costs.py +++ b/process/models/costs/costs.py @@ -1,3 +1,5 @@ +"""Module containing cost routines""" + import logging import numpy as np @@ -13,6 +15,8 @@ class Costs(Model): + """Cost accounting calculations""" + def __init__(self): self.outfile = constants.NOUT @@ -79,6 +83,7 @@ def run(self): self.coelc() def output(self): + """Output costs information""" self.run() if self.data.costs.output_costs == 0: return @@ -2589,6 +2594,11 @@ def acc9(self): def acc2253(self): """Account 225.3 : Energy storage This routine evaluates the Account 225.3 (energy storage) costs. + + Raises + ------ + ProcessValueError + If illegal value used for istore", """ self.data.costs.c2253 = 0.0e0 diff --git a/process/models/costs/costs_2015.py b/process/models/costs/costs_2015.py index e9ba8d25b2..eca6e69bf9 100644 --- a/process/models/costs/costs_2015.py +++ b/process/models/costs/costs_2015.py @@ -1,3 +1,5 @@ +"""Module containing cost 2015 routines""" + import logging import numpy as np @@ -10,6 +12,8 @@ class Costs2015(Model): + """Cost 2015 accounting calculations""" + def __init__(self): self.outfile = constants.NOUT diff --git a/process/models/cryostat.py b/process/models/cryostat.py index edc6aaec4a..58fc72d940 100644 --- a/process/models/cryostat.py +++ b/process/models/cryostat.py @@ -1,3 +1,5 @@ +"""Module containing cryostat routines""" + import numpy as np from process.core import constants @@ -6,6 +8,8 @@ class Cryostat(Model): + """Calculate cryostat parameters""" + def __init__(self): self.outfile = constants.NOUT diff --git a/process/models/cs_fatigue.py b/process/models/cs_fatigue.py index 52c723b465..9252217d4e 100644 --- a/process/models/cs_fatigue.py +++ b/process/models/cs_fatigue.py @@ -1,3 +1,5 @@ +"""Module containing CS fatigue routines""" + import numpy as np from numba import njit @@ -6,14 +8,16 @@ class CsFatigue(Model): + """Calculate CS fatigue model parameters""" + def __init__(self): self.outfile = constants.NOUT def output(self): - """This model doesn't have any output""" + """CsFatigue model doesn't have any output""" def run(self): - """This model doesn't need to be run""" + """CsFatigue model doesn't need to be run""" def ncycle( self, diff --git a/process/models/divertor.py b/process/models/divertor.py index 37d1b32fc3..a9563e4383 100644 --- a/process/models/divertor.py +++ b/process/models/divertor.py @@ -1,3 +1,5 @@ +"""Module containing divertor routines""" + import math import numpy as np @@ -21,6 +23,7 @@ def __init__(self): self.outfile = constants.NOUT # output file unit def output(self): + """Output divertor information""" self.run(output=True) def run(self, output: bool = False): @@ -154,6 +157,10 @@ def divtart( float Divertor heat load for a tight aspect ratio machine (MW/m2) + Raises + ------ + ProcessValueError + If dz_xpoint_divertor is non-positive Notes ----- @@ -451,6 +458,7 @@ class LowerDivertor(Divertor): """Module containing lower divertor routines""" def run(self, output: bool): + """Run the LowerDivertor routines""" super().run(output=output) self.data.divertor.p_div_lower_nuclear_heat_mw = self.incident_neutron_power( @@ -470,6 +478,7 @@ class UpperDivertor(Divertor): """Module containing upper divertor routines""" def run(self, output: bool): + """Run the UpperDivertor routine""" super().run(output=output) self.data.divertor.p_div_upper_nuclear_heat_mw = self.incident_neutron_power( diff --git a/process/models/fw.py b/process/models/fw.py index 68121fd33f..733c75fb38 100644 --- a/process/models/fw.py +++ b/process/models/fw.py @@ -1,3 +1,5 @@ +"""Module containing first wall routines""" + import logging import numpy as np @@ -23,10 +25,13 @@ class FirstWall(Model): + """Calculate the first wall parameters""" + def __init__(self): self.outfile = constants.NOUT def output(self): + """Output first wall information""" # First wall geometry self.output_fw_geometry() @@ -37,6 +42,7 @@ def output(self): self.output_fw_pumping() def run(self): + """Run the first wall model routines""" self.data.fwbs.dz_fw_half = self.calculate_first_wall_half_height( z_plasma_xpoint_lower=self.data.build.z_plasma_xpoint_lower, dz_xpoint_divertor=self.data.build.dz_xpoint_divertor, @@ -200,6 +206,7 @@ def calculate_dshaped_first_wall_areas( dr_fw_plasma_gap_inboard: float, dr_fw_plasma_gap_outboard: float, ) -> tuple[float, float, float]: + """Calculate d-shaped first wall areas""" # D-shaped # Major radius to outer edge of inboard section r1 = rmajor - rminor - dr_fw_plasma_gap_inboard @@ -303,6 +310,12 @@ def apply_first_wall_coverage_factors( ------- tuple[float, float, float] Contains first wall inboard area, outboard area, and total area (m^2). + + Raises + ------ + ProcessValueError + If fhole+f_ster_div_single+f_a_fw_outboard_hcd is too high + for a credible outboard wall area """ if n_divertors == 2: # Double null configuration @@ -332,6 +345,7 @@ def apply_first_wall_coverage_factors( return a_fw_inboard, a_fw_outboard, a_fw_total def set_fw_geometry(self): + """Set dr_fw_inboard and dr_fw_outboard""" self.data.build.dr_fw_inboard = ( 2 * self.data.fwbs.radius_fw_channel + 2 * self.data.fwbs.dr_fw_wall ) diff --git a/process/models/geometry/__init__.py b/process/models/geometry/__init__.py index e69de29bb2..1bc88bd2c9 100644 --- a/process/models/geometry/__init__.py +++ b/process/models/geometry/__init__.py @@ -0,0 +1 @@ +"""Module containing geometry calculations""" diff --git a/process/models/ife.py b/process/models/ife.py index aa4f23ac85..ea3993bd1d 100644 --- a/process/models/ife.py +++ b/process/models/ife.py @@ -50,6 +50,7 @@ def __init__(self, availability, costs): self.costs = costs def output(self): + """Outputs IFE information""" self.run(output=True) def run(self, output: bool = False): @@ -584,6 +585,10 @@ def sombld(self): ) def hylbld(self): + """Routine to create the build of an inertial fusion energy + device, based on the design of the HYLIFE-II study, + and to calculate the material volumes for the device core + """ # Radial build self.data.ife.r1 = self.data.ife.chrad self.data.ife.r2 = self.data.ife.r1 + self.data.ife.fwdr @@ -810,6 +815,11 @@ def bld2019(self): and top corners and with a lower shield at the centre. See diagram attached to Issue #907. Issue #907 + + Raises + ------ + ProcessValueError + If inputs are not appropriate for 2019 IFE build """ # Check input if self.data.ife.fwdr > 0 or self.data.ife.v1dr > 0: @@ -1336,6 +1346,11 @@ def ifephy(self, output: bool = False): ---------- output: bool (Default value = False) + + Raises + ------ + ProcessValueError + If selected ifedrv is an invalid option """ match self.data.ife.ifedrv: case -1: @@ -1678,6 +1693,11 @@ def ifefbs(self, output: bool = False): ---------- output: bool (Default value = False) + + Raises + ------ + ProcessValueError + If illegal fbreed value used """ # Material densities # 0 = void diff --git a/process/models/pfcoil.py b/process/models/pfcoil.py index c1843a3e34..686c6d6d02 100644 --- a/process/models/pfcoil.py +++ b/process/models/pfcoil.py @@ -1,3 +1,5 @@ +"""Module containing PF coil and CS coil models.""" + import logging import math from dataclasses import dataclass @@ -92,6 +94,12 @@ def pfcoil(self): This subroutine performs the calculations for the PF and Central Solenoid coils, to determine their size, location, current waveforms, stresses etc. + + Raises + ------ + ProcessValueError + If there are errors with PF coils. + See individual ProcessValueError instances for more details. """ lrow1 = 2 * NPTSMX + N_PF_GROUPS_MAX lcol1 = N_PF_GROUPS_MAX @@ -1518,6 +1526,7 @@ def efc( return ssq, ccls def tf_pf_collision_detector(self): + """Determine if TF and PF coil positions are colliding.""" # Collision test between TF and PF coils for picture frame TF # See issue 1612 # https://git.ccfe.ac.uk/process/process/-/issues/1612 @@ -2999,10 +3008,10 @@ def __init__(self, cs_fatigue): self.cs_fatigue = cs_fatigue def output(self): - """This model doesn't have any output""" + """CSCoil model doesn't have any output""" def run(self): - """This model doesn't need to be run""" + """CSCoil model doesn't need to be run""" @staticmethod def calculate_cs_geometry( @@ -4225,6 +4234,9 @@ def calculate_cs_self_axial_stress( def calculate_cs_self_midplane_axial_stress_time_profile( self, ) -> None: + """ + Calculate profile for axial stress and axial force for the central solenoid. + """ for time in range(6): stress_value, _ = self.calculate_cs_self_peak_midplane_axial_stress( r_cs_outer=self.data.pf_coil.r_pf_coil_outer[ @@ -4710,6 +4722,11 @@ def superconpf( Critical cable current density [A/m²] (j_crit_cable) Superconducting strand non-copper critical current density [A/m²] (j_crit_sc) Temperature margin [K] (tmarg) + + Raises + ------ + ProcessValueError + If i_pf_superconductor not a valid SuperconductorModel """ def j_crit_cable_frac(j_crit_sc, fcu, fhe): diff --git a/process/models/physics/__init__.py b/process/models/physics/__init__.py index e69de29bb2..de9629aa0f 100644 --- a/process/models/physics/__init__.py +++ b/process/models/physics/__init__.py @@ -0,0 +1 @@ +"""Module containing physics models""" diff --git a/process/models/physics/bootstrap_current.py b/process/models/physics/bootstrap_current.py index 729b732bb6..d77bcbedb0 100644 --- a/process/models/physics/bootstrap_current.py +++ b/process/models/physics/bootstrap_current.py @@ -1448,10 +1448,10 @@ class SauterBootstrapCurrent(Model): """Class to calculate the bootstrap current using the Sauter et al formula.""" def run(self): - """This model isn't run""" + """SauterBootstrapCurrent model isn't run""" def output(self): - """This model doesn't have any output""" + """SauterBootstrapCurrent model doesn't have any output""" def bootstrap_fraction_sauter(self, plasma_profile: PlasmaProfile) -> float: """Calculate the bootstrap current fraction from the Sauter et al scaling. diff --git a/process/models/physics/confinement_time.py b/process/models/physics/confinement_time.py index 2e9dd2ae8a..967fa47909 100644 --- a/process/models/physics/confinement_time.py +++ b/process/models/physics/confinement_time.py @@ -50,10 +50,10 @@ def __init__(self): self.mfile = constants.MFILE def output(self): - """This model doesn't have any output""" + """PlasmaConfinementTime model doesn't have any output""" def run(self): - """This model doesn't need to be run""" + """PlasmaConfinementTime model doesn't need to be run""" def calculate_confinement_time( self, diff --git a/process/models/physics/current_drive.py b/process/models/physics/current_drive.py index a86e78fbfc..ca9c086e67 100644 --- a/process/models/physics/current_drive.py +++ b/process/models/physics/current_drive.py @@ -138,10 +138,10 @@ def __init__(self, plasma_profile: PlasmaProfile): self.plasma_profile = plasma_profile def output(self): - """This model doesn't have any output""" + """NeutralBeam model doesn't have any output""" def run(self): - """This model doesn't need to be run""" + """NeutralBeam model doesn't need to be run""" def iternb(self): """Routine to calculate ITER Neutral Beam current drive parameters @@ -792,10 +792,10 @@ def __init__(self, plasma_profile: PlasmaProfile): self.plasma_profile = plasma_profile def run(self): - """This model isn't run""" + """ElectronCyclotron model isn't run""" def output(self): - """This has no output""" + """ElectronCyclotron model has no output""" def culecd(self): """Routine to calculate Electron Cyclotron current drive efficiency @@ -1315,10 +1315,10 @@ def __init__(self, plasma_profile: PlasmaProfile): self.plasma_profile = plasma_profile def run(self): - """This isn't run""" + """LowerHybrid model isn't run""" def output(self): - """This has no output""" + """LowerHybrid model has no output""" def cullhy(self): """Calculate Culham Lower Hybrid current drive efficiency. diff --git a/process/models/physics/exhaust.py b/process/models/physics/exhaust.py index 81237cb122..c61f9c66aa 100644 --- a/process/models/physics/exhaust.py +++ b/process/models/physics/exhaust.py @@ -17,7 +17,7 @@ def __init__(self): self.mfile = constants.MFILE def run(self): - """This model isn't run.""" + """PlasmaExhaust model isn't run.""" def output(self): """Output plasma exhaust results to the output file.""" diff --git a/process/models/physics/impurity_radiation.py b/process/models/physics/impurity_radiation.py index e4400364ac..3d0e9e22aa 100644 --- a/process/models/physics/impurity_radiation.py +++ b/process/models/physics/impurity_radiation.py @@ -669,10 +669,10 @@ def __init__(self, plasma_profile: PlasmaProfile, data_structure: DataStructure) self.pden_impurity_rad_edge_total_mw = 0.0 def run(self): - """This model isn't run""" + """ImpurityRadiation model isn't run""" def output(self): - """This model has no output""" + """ImpurityRadiation model has no output""" def map_imprad_profile(self): """Map imprad_profile() over each impurity element index.""" diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index 413e545d4c..945bede5c2 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -1162,6 +1162,11 @@ def plasma_composition(self): beams. - Calculates the density weighted mass and mass weighted plasma effective charge. + + Raises + ------ + ProcessValueError + If znfuel is negative """ # Alpha ash portion self.data.physics.nd_plasma_alphas_thermal_vol_avg = ( @@ -3283,7 +3288,7 @@ def __init__(self): self.mfile = constants.MFILE def output(self): - """This model doesn't have any output""" + """PlasmaBeta model doesn't have any output""" @staticmethod def get_beta_norm_max_value( @@ -4273,7 +4278,7 @@ def __init__(self): self.mfile = constants.MFILE def output(self): - """This model has no output""" + """PlasmaInductance model has no output""" def run(self): """Calculate plasma inductance parameters. diff --git a/process/models/physics/plasma_current.py b/process/models/physics/plasma_current.py index b1a272f545..cc1c12da37 100644 --- a/process/models/physics/plasma_current.py +++ b/process/models/physics/plasma_current.py @@ -73,7 +73,7 @@ def __init__(self): self.mfile = constants.MFILE def run(self): - """This model doesn't need to be run""" + """PlasmaCurrent model doesn't need to be run""" def output(self) -> None: """Output plasma current and safety factor information.""" diff --git a/process/models/physics/plasma_profiles.py b/process/models/physics/plasma_profiles.py index f54f6167f2..c27ac81917 100644 --- a/process/models/physics/plasma_profiles.py +++ b/process/models/physics/plasma_profiles.py @@ -47,7 +47,7 @@ def run(self): self.parameterise_plasma() def output(self): - """This model doesn't have any output""" + """PlasmaProfile model doesn't have any output""" def parameterise_plasma(self): """Initializes the density and temperature diff --git a/process/models/physics/profiles.py b/process/models/physics/profiles.py index f8ef9c1a73..2a655789b2 100644 --- a/process/models/physics/profiles.py +++ b/process/models/physics/profiles.py @@ -57,13 +57,14 @@ def __init__(self): self.profile_dx = 0 def run(self): + """Initialise profile_x and profile_y""" self.profile_x = np.arange( self.data.physics.n_plasma_profile_elements, dtype=float ) self.profile_y = np.zeros(self.data.physics.n_plasma_profile_elements) def output(self): - """This model doesn't have any output""" + """Profile model doesn't have any output""" def normalise_profile_x(self): """Normalizes the x-dimension of the profile. @@ -410,6 +411,11 @@ def calculate_profile_y( tbeta : float Second temperature exponent. + Raises + ------ + ProcessValueError + If negative temperature in plasma profile + References ---------- Jean, J. (2011). HELIOS: A Zero-Dimensional Tool for Next Step and Reactor diff --git a/process/models/stellarator/__init__.py b/process/models/stellarator/__init__.py index e69de29bb2..24cdd36fa8 100644 --- a/process/models/stellarator/__init__.py +++ b/process/models/stellarator/__init__.py @@ -0,0 +1 @@ +"""Module containing stellarator models""" diff --git a/process/models/stellarator/build.py b/process/models/stellarator/build.py index e4d933de25..eb43d88bd3 100644 --- a/process/models/stellarator/build.py +++ b/process/models/stellarator/build.py @@ -1,3 +1,5 @@ +"""Module containing stellarator build routines""" + from process.core import process_output as po from process.core.model import DataStructure @@ -184,6 +186,7 @@ def st_build(stellarator, f_output: bool, data: DataStructure): def output(stellarator, data): + """Output stellarator information""" po.oheadr(stellarator.outfile, "Radial Build") po.ovarre( diff --git a/process/models/stellarator/coils/__init__.py b/process/models/stellarator/coils/__init__.py index e69de29bb2..9ce682dd2a 100644 --- a/process/models/stellarator/coils/__init__.py +++ b/process/models/stellarator/coils/__init__.py @@ -0,0 +1 @@ +"""Module containing stellarator coil models""" diff --git a/process/models/stellarator/coils/calculate.py b/process/models/stellarator/coils/calculate.py index 129ae460f2..cefdf71da7 100644 --- a/process/models/stellarator/coils/calculate.py +++ b/process/models/stellarator/coils/calculate.py @@ -1,3 +1,5 @@ +"""Module containing stellarator coil routines""" + import logging import numpy as np @@ -17,7 +19,7 @@ def st_coil(stellarator, output: bool, data: DataStructure): - """This routine calculates the properties of the coils for + """Routine to calculate the properties of the coils for a stellarator device. Some precalculated effective parameters for a stellarator power @@ -166,6 +168,7 @@ def st_coil(stellarator, output: bool, data: DataStructure): def calculate_coil_toroidal_thickness(data: DataStructure): + """Calculate coil toroidal thickness""" data.tfcoil.dx_tf_inboard_out_toroidal = ( data.tfcoil.dx_tf_wp_primary_toroidal + 2.0e0 * data.tfcoil.dx_tf_side_case_min @@ -187,6 +190,7 @@ def calculate_coil_radial_thickness(data): def calculate_coil_cross_sectional_area(a_tf_wp_with_insulation, data): + """Calculate coil cross-sectional area""" # [m^2] overall coil cross-sectional area # (assuming inboard and outboard leg are the same) data.tfcoil.a_tf_leg_outboard = ( @@ -199,6 +203,7 @@ def calculate_coil_cross_sectional_area(a_tf_wp_with_insulation, data): def calculate_coil_half_widths(data: DataStructure): + """Calculate coil half widths""" # [m] Half-width of side of coil nearest torus centreline data.tfcoil.tfocrn = 0.5e0 * data.tfcoil.dx_tf_inboard_out_toroidal # [m] Half-width of side of coil nearest plasma @@ -206,6 +211,7 @@ def calculate_coil_half_widths(data: DataStructure): def calculate_plasma_facing_coil_area(data: DataStructure): + """Calculate plasma facing coil area""" # [m^2] Total surface area of coil side facing plasma: inboard region data.tfcoil.tfsai = ( data.tfcoil.n_tf_coils @@ -279,7 +285,9 @@ def calculate_coils_summary_variables( def calculate_inductance(r_coil_minor, data: DataStructure): - """This uses the reference value for the inductance and scales it with a^2/R + """Calculate inductance + + This uses the reference value for the inductance and scales it with a^2/R (toroid inductance scaling) Parameters @@ -373,6 +381,7 @@ def calculate_current(data: DataStructure): def winding_pack_total_size( r_coil_major: float, r_coil_minor: float, coilcurrent: float, data: DataStructure ): + """Calculate winding pack total size""" # Winding Pack total size: n_it = 200 # number of iterations @@ -546,6 +555,7 @@ def calculate_casing(data: DataStructure): def calculate_vertical_ports(data: DataStructure): + """Calculate vertical ports""" # Maximal toroidal port size (vertical ports) (m) # The maximal distance is correct # but the vertical extension of this port is not clear @@ -565,6 +575,7 @@ def calculate_vertical_ports(data: DataStructure): def calculate_horizontal_ports(data: DataStructure): + """Calculate horizontal ports""" # Maximal toroidal port size (horizontal ports) (m) data.stellarator.hporttmax = ( 0.8e0 diff --git a/process/models/stellarator/coils/coils.py b/process/models/stellarator/coils/coils.py index 5b16e1122c..fadbe9d32f 100644 --- a/process/models/stellarator/coils/coils.py +++ b/process/models/stellarator/coils/coils.py @@ -1,3 +1,5 @@ +"""Module containing stellarator coil routines""" + import logging import numpy as np @@ -30,6 +32,13 @@ def jcrit_from_material( f_a_tf_turn_cable_space_extra_void, j_wp, ): + """Calculate critical current density + + Raises + ------ + ProcessValueError + If illegal value for i_pf_superconductor + """ strain = -0.005 # for now a small value # this is helium fraction in the superconductor # (set it to the fixed global variable here) diff --git a/process/models/stellarator/coils/output.py b/process/models/stellarator/coils/output.py index 91d6a95e32..4b0280625c 100644 --- a/process/models/stellarator/coils/output.py +++ b/process/models/stellarator/coils/output.py @@ -1,3 +1,5 @@ +"""Module to output stellarator modular coil results.""" + from process.core import process_output as po diff --git a/process/models/stellarator/density_limits.py b/process/models/stellarator/density_limits.py index 749def0b7e..eecdbaac83 100644 --- a/process/models/stellarator/density_limits.py +++ b/process/models/stellarator/density_limits.py @@ -1,3 +1,5 @@ +"""Module containing stellarator density limit routines.""" + import copy import logging @@ -74,6 +76,10 @@ def st_sudo_density_limit(b_plasma_toroidal_on_axis, powht, rmajor, rminor, data dlimit : Maximum volume-averaged plasma density (/m3) + Raises + ------ + ProcessValueError + If negative square root imminent """ arg = powht * b_plasma_toroidal_on_axis / (rmajor * rminor * rminor) @@ -212,6 +218,7 @@ def power_at_ignition_point(stellarator, gyro_frequency_max, te0_available): def output(stellarator, bt_ecrh, ne0_max_ECRH, data): + """Outputs stellarator ECRH information to output file""" po.oheadr(stellarator.outfile, "ECRH Ignition at lower values. Information:") po.ovarre( diff --git a/process/models/stellarator/divertor.py b/process/models/stellarator/divertor.py index 42c86a2917..ae1047f4db 100644 --- a/process/models/stellarator/divertor.py +++ b/process/models/stellarator/divertor.py @@ -1,3 +1,5 @@ +"""Module to call the stellarator divertor model.""" + import numpy as np from process.core import constants diff --git a/process/models/stellarator/heating.py b/process/models/stellarator/heating.py index fd4bd43aab..b81e930973 100644 --- a/process/models/stellarator/heating.py +++ b/process/models/stellarator/heating.py @@ -1,3 +1,5 @@ +"""Routine to calculate the auxiliary heating power in a stellarator""" + import logging from process.core import process_output as po @@ -24,6 +26,11 @@ def st_heat(stellarator, f_output: bool, data: DataStructure): data: DataStructure data structure object + Raises + ------ + ProcessValueError + If illegal value used for isthtr + """ f_p_beam_injected_ions = None if data.stellarator.isthtr == 1: @@ -134,6 +141,7 @@ def st_heat(stellarator, f_output: bool, data: DataStructure): def output(stellarator, data: DataStructure, f_p_beam_injected_ions=None): + """Outputs the stellarator heating parameters to the output file.""" po.oheadr(stellarator.outfile, "Auxiliary Heating System") if data.stellarator.isthtr == 1: diff --git a/process/models/stellarator/initialization.py b/process/models/stellarator/initialization.py index f9a1e61792..ec91ef6d52 100644 --- a/process/models/stellarator/initialization.py +++ b/process/models/stellarator/initialization.py @@ -1,3 +1,5 @@ +"""Module to initialise variables relevant to stellarators.""" + from process.core.model import DataStructure diff --git a/process/models/stellarator/neoclassics.py b/process/models/stellarator/neoclassics.py index bd41fcaef4..7bea4c360a 100644 --- a/process/models/stellarator/neoclassics.py +++ b/process/models/stellarator/neoclassics.py @@ -1,3 +1,5 @@ +"""Module containing neoclassics routines""" + import logging import numpy as np @@ -10,15 +12,18 @@ class Neoclassics(Model): + """Module containing neoclassics routines""" + @property def no_roots(self): + """Obtain number of Gauss Laguerre roots""" return self.data.neoclassics.roots.shape[0] def output(self): - """This model doesn't have any output""" + """Neoclassics model doesn't have any output""" def run(self): - """This model doesn't need to be run""" + """Neoclassics model doesn't need to be run""" def init_neoclassics(self, r_effin, eps_effin, iotain): """Constructor of the neoclassics object from the effective radius, @@ -275,6 +280,7 @@ def init_profile_values_from_PROCESS(self, rho): return dens, temp, dr_dens, dr_temp def calc_neoclassics(self): + """Calculate neoclassics parameters""" if self.data.stellarator_config.stella_config_epseff < 0: logger.error( "epseff value lower than 0: " @@ -610,6 +616,7 @@ def neoclassics_calc_nu_star_fromT(self, iota): return neoclassics_calc_nu_star_fromT def neoclassics_calc_vd(self): + """Calculates the drift velocity on GL roots""" vde = ( self.data.neoclassics.roots * self.data.neoclassics.temperatures[0] diff --git a/process/models/stellarator/preset_config.py b/process/models/stellarator/preset_config.py index e9d4438d1c..6a75ac4178 100644 --- a/process/models/stellarator/preset_config.py +++ b/process/models/stellarator/preset_config.py @@ -1,3 +1,5 @@ +"""Load the appropriate Stellarator machine configuration""" + import json from pathlib import Path @@ -227,6 +229,11 @@ def load_stellarator_config(istell: int, config_file: Path | None, data: DataStr data: DataStructure data structure object + Raises + ------ + ProcessValueError + If stellarator config file is None but istell=6, or if + istell is not an integer in the range [1, 6] """ match istell: case 1: diff --git a/process/models/stellarator/stellarator.py b/process/models/stellarator/stellarator.py index 4d23487128..e75fc106c8 100644 --- a/process/models/stellarator/stellarator.py +++ b/process/models/stellarator/stellarator.py @@ -1,3 +1,5 @@ +"""Module containing stellarator routines""" + from __future__ import annotations import logging @@ -106,6 +108,7 @@ def __init__( self.bootstrap = plasma_bootstrap def output(self): + """Routine to output stellarator parameters""" self.run(output=True) def run(self, output: bool = False): @@ -501,6 +504,12 @@ def st_fwbs(self, output: bool): ---------- output: + Raises + ------ + ProcessValueError + If i_p_coolant_pumping not 0 or 1 + (can only use i_p_coolant_pumping = 0 or 1 for stellarator) + """ self.data.fwbs.life_fw_fpy = min( @@ -1884,6 +1893,11 @@ def st_phys(self, output): ---------- output : + Raises + ------ + ProcessValueError + If beta is in ixc and istell>0 + References ---------- AEA FUS 172: Physics Assessment for the European Reactor Study @@ -2463,6 +2477,7 @@ def st_phys_output( nd_plasma_electron_line, nd_plasma_electrons_max, ): + """Routine to output stellarator physics paramaters""" po.oheadr(self.outfile, "Stellarator Specific Physics:") po.ovarre( diff --git a/process/models/superconductors.py b/process/models/superconductors.py index 7b37a1c247..20a93374fd 100644 --- a/process/models/superconductors.py +++ b/process/models/superconductors.py @@ -1090,16 +1090,28 @@ def bottura_scaling( @dataclass class CroCoCableGeometry: + """Dataclass holding CroCo cable geometry parameters""" + dia_croco_strand_tape_region: float + """Inner diameter of CroCo strand tape region (m)""" n_croco_strand_hts_tapes: float + """Number of HTS tapes in CroCo strand""" a_croco_strand_copper_total: float + """Total copper area in CroCo strand (m²)""" a_croco_strand_hastelloy: float + """ Total Hastelloy area in CroCo strand (m²)""" a_croco_strand_solder: float + """ Total solder area in CroCo strand (m²)""" a_croco_strand_rebco: float + """Total REBCO area in CroCo strand (m²)""" a_croco_strand: float + """Total area of CroCo strand (m²)""" dr_hts_tape: float + """Width of the tape (m)""" dx_hts_tape_total: float + """thickness of tape, inc. all layers (m)""" dx_croco_strand_tape_stack: float + """Height of the tape stack in the CroCo strand (m)""" def calculate_croco_cable_geometry( @@ -1127,15 +1139,7 @@ def calculate_croco_cable_geometry( Returns ------- CroCoCableGeometry - - dia_croco_strand_tape_region: Inner diameter of CroCo strand tape region (m) - - n_croco_strand_hts_tapes: Number of HTS tapes in CroCo strand - - a_croco_strand_copper_total: Total copper area in CroCo strand (m²) - - a_croco_strand_hastelloy: Total Hastelloy area in CroCo strand (m²) - - a_croco_strand_solder: Total solder area in CroCo strand (m²) - - a_croco_strand_rebco: Total REBCO area in CroCo strand (m²) - - a_croco_strand: Total area of CroCo strand (m²) - - dr_hts_tape: Width of the tape (m) - - dx_croco_strand_tape_stack: Height of the tape stack in the CroCo strand (m) + Dataclass holding CroCo cable geometry parameters """ # Calculate the inner diameter of the CroCo strand tape region dia_croco_strand_tape_region = dia_croco_strand - 2.0 * dx_croco_strand_copper diff --git a/process/models/tfcoil/__init__.py b/process/models/tfcoil/__init__.py index e69de29bb2..96c24d51cd 100644 --- a/process/models/tfcoil/__init__.py +++ b/process/models/tfcoil/__init__.py @@ -0,0 +1 @@ +"""Module containing TF coil models""" diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index fdc25e2030..c061443d0a 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -86,20 +86,33 @@ class TFWPGeometry: """ r_tf_wp_inboard_inner: float + """WP inboard inner radius [m]""" r_tf_wp_inboard_outer: float + """WP inboard outer radius [m]""" r_tf_wp_inboard_centre: float + """WP inboard centre radius [m]""" dx_tf_wp_toroidal_min: float + """Minimal toroidal thickness of WP [m]""" dr_tf_wp_no_insulation: float + """Radial thickness of winding pack without insulation [m]""" dx_tf_wp_primary_toroidal: float + """Primary toroidal thickness [m]""" dx_tf_wp_secondary_toroidal: float + """Secondary toroidal thickness [m]""" dx_tf_wp_toroidal_average: float + """Averaged toroidal thickness [m]""" a_tf_wp_with_insulation: float + """WP cross-sectional area with insulation [m²]""" a_tf_wp_no_insulation: float + """WP cross-sectional area without insulation [m²]""" a_tf_wp_ground_insulation: float + """WP ground insulation cross-sectional area [m²]""" @dataclass class TFSuperconductorLimits: + """Dataclass holding superconducting TF coil limits""" + j_tf_wp_critical: float j_superconductor_critical: float f_c_tf_turn_operating_critical: float @@ -135,7 +148,6 @@ def run_base_superconducting_tf(self): d_sc_tf = self.data.superconducting_tfcoil # Calculating the WP / ground insulation areas - wp_geometry = TFWPGeometry wp_geometry = self.superconducting_tf_wp_geometry( i_tf_wp_geom=self.data.tfcoil.i_tf_wp_geom, r_tf_inboard_in=self.data.build.r_tf_inboard_in, @@ -1477,29 +1489,13 @@ def superconducting_tf_wp_geometry( Returns ------- TFWPGeometry - A TFWPGeometry dataclass containing the following attributes: - - r_tf_wp_inboard_inner (float): WP inboard inner radius [m] - - r_tf_wp_inboard_outer (float): WP inboard outer radius [m] - - r_tf_wp_inboard_centre (float): WP inboard centre radius [m] - - dx_tf_wp_toroidal_min (float): Minimal toroidal thickness of WP [m] - - dr_tf_wp_no_insulation (float): Radial thickness of winding pack without - insulation [m] - - dx_tf_wp_primary_toroidal (float): Primary toroidal thickness [m] - - dx_tf_wp_secondary_toroidal (float): Secondary toroidal thickness [m] - - dx_tf_wp_toroidal_average (float): Averaged toroidal thickness [m] - - a_tf_wp_with_insulation (float): WP cross-sectional area with - insulation [m²] - - a_tf_wp_no_insulation (float): WP cross-sectional area without - insulation [m²] - - a_tf_wp_ground_insulation (float): WP ground insulation cross-sectional - area [m²] + Data class for storing the geometry of the TF coil winding pack and + ground insulation. Raises ------ - ValueError - If calculated winding pack area (with or without insulation) is - non-positive, or if i_tf_wp_geom is not a valid - SuperconductingTFWPShapeType. + ProcessValueError + If i_tf_wp_geom is not a valid SuperconductingTFWPShapeType. """ r_tf_wp_inboard_inner = r_tf_inboard_in + dr_tf_nose_case @@ -2757,7 +2753,12 @@ def tf_cable_in_conduit_superconductor_properties( Returns ------- : - Superconcting TF coil limits dataclass + Superconducting TF coil limits dataclass + + Raises + ------ + ProcessValueError + If sctfcoil.supercon has been called but data.tfcoil.i_tf_sc_mat=6 Notes ----- @@ -3736,6 +3737,8 @@ def output_cable_in_conduit_cable_info(self) -> None: @dataclass class CROCOAveragedTurnGeometry: + """Dataclass holding CroCo averaged turn geometry parameters""" + a_tf_turn_cable_space_no_void: float a_tf_turn_steel: float a_tf_turn_insulation: float @@ -3750,6 +3753,8 @@ class CROCOAveragedTurnGeometry: @dataclass class CroCoCableSpaceGeometry: + """Dataclass holding CroCo cable space geometry parameters""" + dia_tf_turn_croco_cable: float a_tf_turn_cable_space_no_void: float a_tf_turn_cable_space_effective: float @@ -3769,6 +3774,11 @@ def run(self, output: bool = False): ---------- output : bool If True, print the results of the calculations. + + Raises + ------ + ProcessValueError + if integer turn geometry used for CroCo conductor """ self.run_base_superconducting_tf() @@ -4494,6 +4504,7 @@ def tf_turn_croco_cable_space_properties( ) def croco_voltage(self) -> float: + """Calculates CROCO voltage""" d_sc_tf = self.data.superconducting_tfcoil if self.data.tfcoil.quench_model == "linear": diff --git a/tests/unit/models/test_superconductors.py b/tests/unit/models/test_superconductors.py index c76720ca52..1af200c21b 100644 --- a/tests/unit/models/test_superconductors.py +++ b/tests/unit/models/test_superconductors.py @@ -127,8 +127,6 @@ def test_jcrit_nbti(jcritnbtiparam): :param jcritnbtiparam: the data used to mock and assert in this test. :type jcritnbtiparam: jcritnbtiparam - :param monkeypatch: pytest fixture used to mock module/class variables - :type monkeypatch: _pytest.monkeypatch.monkeypatch """ jcrit, tcrit = superconductors.jcrit_nbti( @@ -270,7 +268,7 @@ def test_calculate_croco_cable_geometry( dx_hts_tape_hastelloy, expected, ): - result: CroCoCableGeometry = superconductors.calculate_croco_cable_geometry( + result = superconductors.calculate_croco_cable_geometry( dia_croco_strand, dx_croco_strand_copper, dx_hts_tape_rebco, diff --git a/tracking/tracking_data.py b/tracking/tracking_data.py index 16525cf40e..b3456d2805 100644 --- a/tracking/tracking_data.py +++ b/tracking/tracking_data.py @@ -171,6 +171,9 @@ def __init__(self): # tracking data that shows the value of an important variable as a key-value pair def asdict(self) -> dict: + """ + Returns a dict of metadata and tracking data + """ return {"meta": self.meta, "tracking": self.tracking} @@ -197,6 +200,11 @@ def __init__( the path to an mfile to create tracking data for. database: the folder (acting as a database) that stores all tracking JSON files + + Raises + ------ + RuntimeError + if PROCESS has failed to converge """ self.mfile = mf.MFile(mfile) @@ -558,6 +566,11 @@ def track_entrypoint(arguments): Entrypoint if we run in track mode. Generates a tracking JSOn file for the provided MFile. + + Raises + ------ + ValueError + if '--mfile' argument is not set """ if not arguments.mfile: raise ValueError("track requires --mfile be set") @@ -576,6 +589,11 @@ def plot_entrypoint(arguments): Entrypoint if we run in plot mode. Plots all tracking data into a single tracking.html file + + Raises + ------ + ValueError + if '--out' argument is not set """ if not arguments.out: raise ValueError("plot requires --out be set")