diff --git a/CMakeLists.txt b/CMakeLists.txt index d6367e52..ec123813 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,6 +123,10 @@ LIST(APPEND PROCESS_SRCS reinke_variables.f90 neoclassics_module.f90 blanket_library.f90 + stellarator_variables.f90 + stellarator.f90 + stellarator_configuration.f90 + stellarator_fwbs.f90 ) PREPROCESS() diff --git a/documentation/proc-pages/fusion-devices/stellarator.md b/documentation/proc-pages/fusion-devices/stellarator.md index 45ab6970..525e7eea 100644 --- a/documentation/proc-pages/fusion-devices/stellarator.md +++ b/documentation/proc-pages/fusion-devices/stellarator.md @@ -1,8 +1,5 @@ # Stellarator Model -!!! danger inline end "Stellarator removed" - The Stellarator model was removed in issue #1853 because of concerns around licensing issues with a number of sections of code used in the model. Issue #1854 aims to reintroduce the model after these licensing issues are addressed. - The code has the ability to perform calculations based on the physics and engineering of a stellarator, which, although being a toroidal device, is radically different in a number of ways from a tokamak. The model is largely based on W7-X and the HELIAS 5-B stellarator power plant design[^1] (Figure 1) and related modelling that has been performed by IPP Greifswald[^2] [^3] [^4] [^5] diff --git a/process/caller.py b/process/caller.py index c91e2793..9b7d6528 100644 --- a/process/caller.py +++ b/process/caller.py @@ -37,6 +37,13 @@ def call_models(self, xc): # Convert variables ft.define_iteration_variables.convxc(xc, nvars) + # Perform the various function calls + # Stellarator caller + if ft.stellarator_variables.istell != 0: + self.models.stellarator.run(output=False) + # TODO Is this return safe? + return + # Inertial Fusion Energy calls if ft.ife_variables.ife != 0: self.models.ife.run(output=False) diff --git a/process/evaluators.py b/process/evaluators.py index 73c9a4b8..677a9176 100644 --- a/process/evaluators.py +++ b/process/evaluators.py @@ -4,6 +4,7 @@ from process.fortran import cost_variables as cv from process.fortran import numerics from process.fortran import physics_variables as pv +from process.fortran import stellarator_variables as sv from process.fortran import times_variables as tv from process.fortran import function_evaluator import numpy as np @@ -56,22 +57,22 @@ def fcnvmc1(self, n, m, xv, ifail): self.caller.call_models(xv) # Convergence loop to ensure burn time consistency - - loop = 0 - while (loop < 10) and ( - abs((tv.tburn - tv.tburn0) / max(tv.tburn, 0.01)) > 0.001 - ): - loop += 1 - self.caller.call_models(xv) - if gv.verbose == 1: - print("Internal tburn consistency check: ", tv.tburn, tv.tburn0) - - if loop >= 10: - print( - "Burn time values are not consistent in iteration: ", - numerics.nviter, - ) - print("tburn, tburn0: ", tv.tburn, tv.tburn0) + if sv.istell == 0: + loop = 0 + while (loop < 10) and ( + abs((tv.tburn - tv.tburn0) / max(tv.tburn, 0.01)) > 0.001 + ): + loop += 1 + self.caller.call_models(xv) + if gv.verbose == 1: + print("Internal tburn consistency check: ", tv.tburn, tv.tburn0) + + if loop >= 10: + print( + "Burn time values are not consistent in iteration: ", + numerics.nviter, + ) + print("tburn, tburn0: ", tv.tburn, tv.tburn0) # Evaluate figure of merit (objective function) objf = function_evaluator.funfom() diff --git a/process/io/sankey_funcs.py b/process/io/sankey_funcs.py index 62710c57..da42e08f 100644 --- a/process/io/sankey_funcs.py +++ b/process/io/sankey_funcs.py @@ -17,7 +17,6 @@ def plot_full_sankey( mfilename="MFILE.DAT", ): # Plots the power flow from PROCESS as a Sankey Diagram - # ------------------------------- Pulling values from the MFILE ------------------------------- m_file = MFile(mfilename) @@ -98,7 +97,6 @@ def plot_full_sankey( # Loop 1 to get 'Plasma Heating' branch tip coords; loop 2 to match 'PLASMA' branch for _ in range(2): - # The visual settings of the Sankey Plot plt.rcParams.update({"font.size": 9}) fig = plt.figure() @@ -451,7 +449,6 @@ def plot_full_sankey( def plot_sankey(mfilename="MFILE.DAT"): # Plot simplified power flow Sankey Diagram - # ------------------------------- Pulling values from the MFILE ------------------------------- m_file = MFile(mfilename) @@ -522,7 +519,7 @@ def plot_sankey(mfilename="MFILE.DAT"): # Plot simplified power flow Sankey Dia pthermfw_blkt = m_file.data["pthermfw_blkt"].get_scan( -1 ) # Heat for electricity (MW) - htpmw_fw_blkt = m_file.data["htpmw_blkt_tot"].get_scan( + htpmw_fw_blkt = m_file.data["htpmw_fw_blkt"].get_scan( -1 ) # 1st wall & blanket pumping (MW) pthermmw_p = pthermfw_blkt - htpmw_fw_blkt # Heat - pumping power (MW) @@ -563,7 +560,6 @@ def plot_sankey(mfilename="MFILE.DAT"): # Plot simplified power flow Sankey Dia # Loop 1 to get 'Plasma Heating' branch tip coords; loop 2 to match 'PLASMA' branch for _ in range(2): - # ------------------------------------ Visual Settings ------------------------------------ plt.rcParams.update({"font.size": 9}) # Setting font size to 9 diff --git a/process/main.py b/process/main.py index 44bca33f..f52e37c0 100644 --- a/process/main.py +++ b/process/main.py @@ -49,6 +49,7 @@ from process.pulse import Pulse from process.scan import Scan from process import final +from process.stellarator import Stellarator from process.structure import Structure from process.build import Build from process.utilities.f2py_string_patch import string_to_f2py_compatible @@ -580,11 +581,20 @@ def __init__(self): self.costs = Costs() self.ife = IFE(availability=self.availability, costs=self.costs) self.plasma_profile = PlasmaProfile() - self.costs_2015 = Costs2015() - self.physics = Physics(plasma_profile=self.plasma_profile) self.fw = Fw() self.blanket_library = BlanketLibrary(fw=self.fw) self.ccfe_hcpb = CCFE_HCPB(blanket_library=self.blanket_library) + self.stellarator = Stellarator( + availability=self.availability, + buildings=self.buildings, + vacuum=self.vacuum, + costs=self.costs, + power=self.power, + plasma_profile=self.plasma_profile, + hcpb=self.ccfe_hcpb, + ) + self.costs_2015 = Costs2015() + self.physics = Physics(plasma_profile=self.plasma_profile) self.dcll = DCLL(blanket_library=self.blanket_library) diff --git a/process/output.py b/process/output.py index 6f56cb72..80c323a5 100644 --- a/process/output.py +++ b/process/output.py @@ -17,6 +17,11 @@ def write(models, outfile): # during the solution process) ft.error_handling.errors_on = True + # Call stellarator output routine instead if relevant + if ft.stellarator_variables.istell != 0: + models.stellarator.run(output=True) + return + # Call IFE output routine instead if relevant if ft.ife_variables.ife != 0: models.ife.run(output=True) diff --git a/process/solver.py b/process/solver.py index 69aec2c4..5e9bec18 100644 --- a/process/solver.py +++ b/process/solver.py @@ -169,7 +169,11 @@ def solve(self) -> int: B = np.identity(numerics.nvar) * self.b def _solver_callback(i: int, _x, _result, convergence_param: float): - print(f"{i+1} | Convergence Parameter: {convergence_param:.3E}", end="\r") + print( + f"{i+1} | Convergence Parameter: {convergence_param:.3E}", + end="\r", + flush=True, + ) try: x, _, _, res = solve( diff --git a/process/stellarator.py b/process/stellarator.py new file mode 100644 index 00000000..e359cffc --- /dev/null +++ b/process/stellarator.py @@ -0,0 +1,4782 @@ +import logging +from copy import copy +import numpy as np + +from process.fortran import ( + constants, + physics_module as ph, + stellarator_module as st, + process_output as po, + physics_variables, + physics_module, + current_drive_variables, + tfcoil_variables, + stellarator_configuration, + stellarator_variables, + numerics, + build_variables, + fwbs_variables, + heat_transport_variables, + structure_variables, + divertor_variables, + cost_variables, + fwbs_module, + error_handling, + constraint_variables, + rebco_variables, + maths_library, + superconductors, + profiles_module, + physics_functions_module, + neoclassics_module, + impurity_radiation_module, + current_drive_module, +) +import process.physics_functions as physics_funcs +from process.coolprop_interface import FluidProperties + +logger = logging.getLogger(__name__) +# Logging handler for console output +s_handler = logging.StreamHandler() +s_handler.setLevel(logging.ERROR) +logger.addHandler(s_handler) + + +class Stellarator: + """Module containing stellarator routines + author: P J Knight, CCFE, Culham Science Centre + N/A + This module contains routines for calculating the + parameters of the first wall, blanket and shield components + of a fusion power plant. + + AEA FUS 251: A User's Guide to the PROCESS Systems Code + """ + + def __init__( + self, availability, vacuum, buildings, costs, power, plasma_profile, hcpb + ) -> None: + """Initialises the Stellarator model's variables + + :param availability: a pointer to the availability model, allowing use of availability's variables/methods + :type availability: process.availability.Availability + :param buildings: a pointer to the buildings model, allowing use of buildings's variables/methods + :type buildings: process.buildings.Buildings + :param Vacuum: a pointer to the vacuum model, allowing use of vacuum's variables/methods + :type Vacuum: process.vacuum.Vacuum + :param costs: a pointer to the costs model, allowing use of costs' variables/methods + :type costs: process.costs.Costs + :param plasma_profile: a pointer to the plasma_profile model, allowing use of plasma_profile's variables/methods + :type plasma_profile: process.plasma_profile.PlasmaProfile + :param hcpb: a pointer to the ccfe_hcpb model, allowing use of ccfe_hcpb's variables/methods + :type hcpb: process.hcpb.CCFE_HCPB + """ + + self.outfile: int = constants.nout + self.first_call_stfwbs = True + + self.availability = availability + self.buildings = buildings + self.vacuum = vacuum + self.costs = costs + self.power = power + self.plasma_profile = plasma_profile + self.hcpb = hcpb + + def run(self, output: bool): + """Routine to call the physics and engineering modules + relevant to stellarators + author: P J Knight, CCFE, Culham Science Centre + author: F Warmer, IPP Greifswald + + This routine is the caller for the stellarator models. + AEA FUS 251: A User's Guide to the PROCESS Systems Code + + :param output: indicate whether output should be written to the output file, or not + :type output: boolean + """ + + if output: + self.costs.costs(output=True) + # TODO: should availability.run be called + # rather than availability.avail? + self.availability.avail(output=True) + ph.outplas(self.outfile) + self.stigma() + self.stheat(True) + self.stphys(True) + self.stopt(True) + + # As stopt changes dene, te and bt, stphys needs two calls + # to correct for larger changes (it is only consistent after + # two or three fix point iterations) call stphys here again, just to be sure. + # This can be removed once the bad practice in stopt is removed! + self.stphys(False) + + self.stdiv(True) + self.stbild(True) + self.stcoil(True) + self.ststrc(True) + self.stfwbs(True) + + self.power.tfpwr(output=True) + self.buildings.run(output=True) + self.vacuum.run(output=True) + self.power.acpow(output=True) + self.power.power2(output=True) + + return + + self.stnewconfig() + self.stgeom() + self.stphys(False) + self.stopt(False) + self.stcoil(False) + self.stbild(False) + self.ststrc(False) + self.stfwbs(False) + self.stdiv(False) + + self.power.tfpwr(output=False) + self.power.power1() + self.buildings.run(output=False) + self.vacuum.run(output=False) + self.power.acpow(output=False) + self.power.power2(output=False) + # TODO: should availability.run be called + # rather than availability.avail? + self.availability.avail(output=False) + self.costs.costs(output=False) + + if any(numerics.icc == 91): + # This call is comparably time consuming.. + # If the respective constraint equation is not called, do not set the values + ( + stellarator_variables.powerht_constraint, + stellarator_variables.powerscaling_constraint, + ) = self.power_at_ignition_point( + stellarator_variables.max_gyrotron_frequency, + stellarator_variables.te0_ecrh_achievable, + ) + + st.first_call = False + + def stigma(self): + """Routine to calculate ignition margin at the final point + with different stellarator confinement time scaling laws + author: P J Knight, CCFE, Culham Science Centre + outfile : input integer : output file unit + This routine calculates the ignition margin at the final + point with different stellarator confinement time scaling laws + AEA FUS 251: A User's Guide to the PROCESS Systems Code + """ + po.osubhd(self.outfile, "Confinement times, and required H-factors :") + + po.write( + self.outfile, + f"{' '*5}scaling law{' '*30}confinement time (s){' '*55}H-factor for", + ) + po.write(self.outfile, f"{' '*34}for H = 2{' '*54}power balance") + + # Label stellarator scaling laws (update if more are added) + + istlaw = [21, 22, 23, 37, 38] + + # Calculate power balances for all stellarator scaling laws + # assuming H = 2 + + for iisc, i in enumerate(istlaw): + ( + physics_variables.kappaa, + physics_variables.ptrepv, + physics_variables.ptripv, + physics_variables.tauee, + physics_variables.tauei, + physics_variables.taueff, + physics_variables.powerht, + ) = physics_module.pcond( + physics_variables.afuel, + physics_variables.palpmw, + physics_variables.aspect, + physics_variables.bt, + physics_variables.dnitot, + physics_variables.dene, + physics_variables.dnla, + physics_variables.eps, + 2.0, + physics_variables.iinvqd, + physics_variables.isc, + physics_variables.ignite, + physics_variables.kappa, + physics_variables.kappa95, + physics_variables.pchargemw, + current_drive_variables.pinjmw, + physics_variables.plascur, + physics_variables.pcoreradpv, + physics_variables.rmajor, + physics_variables.rminor, + physics_variables.te, + physics_variables.ten, + physics_variables.tin, + physics_variables.q, + physics_variables.qstar, + physics_variables.vol, + physics_variables.xarea, + physics_variables.zeff, + ) + + physics_variables.hfac[iisc] = physics_module.fhfac(i) + + def stnewconfig(self): + """author: J Lion, IPP Greifswald + Routine to initialise the stellarator configuration + + Routine to initialise the stellarator configuration. + This routine is called right before the calculation and could + in principle overwrite variables from the input file. + It overwrites rminor with rmajor and aspect ratio e.g. + """ + + stellarator_configuration.new_stella_config(stellarator_variables.istell) + + # If physics_variables.aspect ratio is not in numerics.ixc set it to default value + # Or when you call it the first time + if 1 not in numerics.ixc: + physics_variables.aspect = ( + stellarator_configuration.stella_config_aspect_ref + ) + + # Set the physics_variables.rminor radius as result here. + physics_variables.rminor = physics_variables.rmajor / physics_variables.aspect + physics_variables.eps = 1.0e0 / physics_variables.aspect + + tfcoil_variables.n_tf = ( + stellarator_configuration.stella_config_coilspermodule + * stellarator_configuration.stella_config_symmetry + ) # This overwrites tfcoil_variables.n_tf in input file. + + # Factors used to scale the reference point. + st.f_r = ( + physics_variables.rmajor + / stellarator_configuration.stella_config_rmajor_ref + ) # Size scaling factor with respect to the reference calculation + st.f_a = ( + physics_variables.rminor + / stellarator_configuration.stella_config_rminor_ref + ) # Size scaling factor with respect to the reference calculation + + st.f_aspect = ( + physics_variables.aspect + / stellarator_configuration.stella_config_aspect_ref + ) + st.f_n = tfcoil_variables.n_tf / ( + stellarator_configuration.stella_config_coilspermodule + * stellarator_configuration.stella_config_symmetry + ) # Coil number factor + st.f_b = ( + physics_variables.bt / stellarator_configuration.stella_config_bt_ref + ) # B-field scaling factor + + def stgeom(self): + """ + author: J Lion, IPP Greifswald + Routine to calculate the plasma volume and surface area for + a stellarator using precalculated effective values + + This routine calculates the plasma volume and surface area for + a stellarator configuration. + It is simple scaling based on a Fourier representation based on + that described in Geiger documentation. + + J. Geiger, IPP Greifswald internal document: 'Darstellung von + ineinandergeschachtelten toroidal geschlossenen Flaechen mit + Fourierkoeffizienten' ('Representation of nested, closed + surfaces with Fourier coefficients') + """ + physics_variables.vol = ( + st.f_r * st.f_a**2 * stellarator_configuration.stella_config_plasma_volume + ) + + # Plasma surface scaled from effective parameter: + physics_variables.sarea = ( + st.f_r * st.f_a * stellarator_configuration.stella_config_plasma_surface + ) + + # Plasma cross section area. Approximated + physics_variables.xarea = ( + np.pi * physics_variables.rminor * physics_variables.rminor + ) # average, could be calculated for every toroidal angle if desired + + # physics_variables.sareao is retained only for obsolescent fispact calculation... + + # Cross-sectional area, averaged over toroidal angle + physics_variables.sareao = ( + 0.5e0 * physics_variables.sarea + ) # Used only in the divertor model; approximate as for tokamaks + + def stopt(self, output: bool): + """Routine to reiterate the physics loop + author: J Lion, IPP Greifswald + None + This routine reiterates some physics modules. + """ + + physics_variables.dnelimt = self.stdlim( + physics_variables.bt, + physics_variables.powerht, + physics_variables.rmajor, + physics_variables.rminor, + ) + + # Calculates the ECRH parameters + + ne0_max_ECRH, bt_ecrh = self.stdlim_ecrh( + stellarator_variables.max_gyrotron_frequency, physics_variables.bt + ) + + ne0_max_ECRH = min(physics_variables.ne0, ne0_max_ECRH) + bt_ecrh = min(physics_variables.bt, bt_ecrh) + + if output: + self.stopt_output( + stellarator_variables.max_gyrotron_frequency, + physics_variables.bt, + bt_ecrh, + ne0_max_ECRH, + stellarator_variables.te0_ecrh_achievable, + ) + + def stbild(self, output: bool): + """ + Routine to determine the build of a stellarator machine + author: P J Knight, CCFE, Culham Science Centre + author: F Warmer, IPP Greifswald + outfile : input integer : output file unit + iprint : input integer : switch for writing to output file (1=yes) + This routine determines the build of the stellarator machine. + The values calculated are based on the mean minor radius, etc., + as the actual radial and vertical build thicknesses vary with + toroidal angle. + AEA FUS 251: A User's Guide to the PROCESS Systems Code + """ + if fwbs_variables.blktmodel > 0: + build_variables.blnkith = ( + build_variables.blbuith + + build_variables.blbmith + + build_variables.blbpith + ) + build_variables.blnkoth = ( + build_variables.blbuoth + + build_variables.blbmoth + + build_variables.blbpoth + ) + build_variables.shldtth = 0.5e0 * ( + build_variables.shldith + build_variables.shldoth + ) + + # Top/bottom blanket thickness + + build_variables.blnktth = 0.5e0 * ( + build_variables.blnkith + build_variables.blnkoth + ) + + # First Wall + build_variables.fwith = ( + 2.0e0 * fwbs_variables.afw + 2.0e0 * fwbs_variables.fw_wall + ) + build_variables.fwoth = build_variables.fwith + + build_variables.bore = physics_variables.rmajor - ( + build_variables.ohcth + + build_variables.gapoh + + build_variables.tfcth + + build_variables.gapds + + build_variables.d_vv_in + + build_variables.shldith + + build_variables.blnkith + + build_variables.fwith + + build_variables.scrapli + + physics_variables.rminor + ) + + # Radial build to centre of plasma (should be equal to physics_variables.rmajor) + build_variables.rbld = ( + build_variables.bore + + build_variables.ohcth + + build_variables.gapoh + + build_variables.tfcth + + build_variables.gapds + + build_variables.d_vv_in + + build_variables.shldith + + build_variables.blnkith + + build_variables.fwith + + build_variables.scrapli + + physics_variables.rminor + ) + + # Bc stellarators cannot scale physics_variables.rminor reasonably well an additional constraint equation is required, + # that ensures that there is enough space between coils and plasma. + build_variables.required_radial_space = ( + build_variables.tfcth / 2.0e0 + + build_variables.gapds + + build_variables.d_vv_in + + build_variables.shldith + + build_variables.blnkith + + build_variables.fwith + + build_variables.scrapli + ) + + # derivative_min_LCFS_coils_dist for how strong the stellarator shape changes wrt to aspect ratio + build_variables.available_radial_space = st.f_r * ( + stellarator_configuration.stella_config_derivative_min_lcfs_coils_dist + * stellarator_configuration.stella_config_rminor_ref + * (1 / st.f_aspect - 1) + + stellarator_configuration.stella_config_min_plasma_coil_distance + ) + + # Radius to inner edge of inboard shield + build_variables.rsldi = ( + physics_variables.rmajor + - physics_variables.rminor + - build_variables.scrapli + - build_variables.fwith + - build_variables.blnkith + - build_variables.shldith + ) + + # Radius to outer edge of outboard shield + build_variables.rsldo = ( + physics_variables.rmajor + + physics_variables.rminor + + build_variables.scraplo + + build_variables.fwoth + + build_variables.blnkoth + + build_variables.shldoth + ) + + # Thickness of outboard TF coil legs + build_variables.tfthko = build_variables.tfcth + + # Radius to centre of outboard TF coil legs + + build_variables.gapsto = build_variables.gapomin + build_variables.r_tf_outboard_mid = ( + build_variables.rsldo + + build_variables.d_vv_out + + build_variables.gapsto + + 0.5e0 * build_variables.tfthko + ) + + # Height to inside edge of TF coil + # Roughly equal to average of (inboard build from TF coil to plasma + # centre) and (outboard build from plasma centre to TF coil) + + build_variables.hmax = 0.5e0 * ( + ( + build_variables.gapds + + build_variables.d_vv_in + + build_variables.shldith + + build_variables.blnkith + + build_variables.fwith + + build_variables.scrapli + + physics_variables.rminor + ) + + ( + physics_variables.rminor + + build_variables.scraplo + + build_variables.fwoth + + build_variables.blnkoth + + build_variables.shldoth + + build_variables.d_vv_out + + build_variables.gapsto + ) + ) + + # Outer divertor strike point radius, set equal to major radius + + build_variables.rspo = physics_variables.rmajor + + # First wall area: scales with minor radius + + # Average minor radius of the first wall + awall = physics_variables.rminor + 0.5e0 * ( + build_variables.scrapli + build_variables.scraplo + ) + build_variables.fwarea = ( + physics_variables.sarea * awall / physics_variables.rminor + ) + + if heat_transport_variables.ipowerflow == 0: + build_variables.fwarea = ( + 1.0e0 - fwbs_variables.fhole + ) * build_variables.fwarea + else: + build_variables.fwarea = ( + 1.0e0 - fwbs_variables.fhole - fwbs_variables.fdiv - fwbs_variables.fhcd + ) * build_variables.fwarea + + if output: + # Print out device build + + po.oheadr(self.outfile, "Radial Build") + + po.ovarre( + self.outfile, + "Avail. Space (m)", + "(available_radial_space)", + build_variables.available_radial_space, + ) + po.ovarre( + self.outfile, + "Req. Space (m)", + "(required_radial_space)", + build_variables.required_radial_space, + ) + po.ovarre( + self.outfile, "f value: ", "(f_avspace)", build_variables.f_avspace + ) + + # po.write(self.outfile,10) + # 10 format(t43,'Thickness (m)',t60,'Radius (m)') + + radius = 0.0e0 + po.obuild(self.outfile, "Device centreline", 0.0e0, radius) + + drbild = ( + build_variables.bore + build_variables.ohcth + build_variables.gapoh + ) + radius = radius + drbild + po.obuild(self.outfile, "Machine bore", drbild, radius, "(bore)") + po.ovarre( + self.outfile, "Machine build_variables.bore (m)", "(bore)", drbild + ) + + radius = radius + build_variables.tfcth + po.obuild( + self.outfile, + "Coil inboard leg", + build_variables.tfcth, + radius, + "(tfcth)", + ) + po.ovarre( + self.outfile, "Coil inboard leg (m)", "(deltf)", build_variables.tfcth + ) + + radius = radius + build_variables.gapds + po.obuild(self.outfile, "Gap", build_variables.gapds, radius, "(gapds)") + po.ovarre(self.outfile, "Gap (m)", "(gapds)", build_variables.gapds) + + radius = radius + build_variables.d_vv_in + po.obuild( + self.outfile, + "Vacuum vessel", + build_variables.d_vv_in, + radius, + "(d_vv_in)", + ) + po.ovarre( + self.outfile, + "Vacuum vessel radial thickness (m)", + "(d_vv_in)", + build_variables.d_vv_in, + ) + + radius = radius + build_variables.shldith + po.obuild( + self.outfile, + "Inboard shield", + build_variables.shldith, + radius, + "(shldith)", + ) + po.ovarre( + self.outfile, + "Inner radiation shield radial thickness (m)", + "(shldith)", + build_variables.shldith, + ) + + radius = radius + build_variables.blnkith + po.obuild( + self.outfile, + "Inboard blanket", + build_variables.blnkith, + radius, + "(blnkith)", + ) + po.ovarre( + self.outfile, + "Inboard blanket radial thickness (m)", + "(blnkith)", + build_variables.blnkith, + ) + + radius = radius + build_variables.fwith + po.obuild( + self.outfile, + "Inboard first wall", + build_variables.fwith, + radius, + "(fwith)", + ) + po.ovarre( + self.outfile, + "Inboard first wall radial thickness (m)", + "(fwith)", + build_variables.fwith, + ) + + radius = radius + build_variables.scrapli + po.obuild( + self.outfile, + "Inboard scrape-off", + build_variables.scrapli, + radius, + "(scrapli)", + ) + po.ovarre( + self.outfile, + "Inboard scrape-off radial thickness (m)", + "(scrapli)", + build_variables.scrapli, + ) + + radius = radius + physics_variables.rminor + po.obuild( + self.outfile, + "Plasma geometric centre", + physics_variables.rminor, + radius, + "(rminor)", + ) + + radius = radius + physics_variables.rminor + po.obuild( + self.outfile, + "Plasma outboard edge", + physics_variables.rminor, + radius, + "(rminor)", + ) + + radius = radius + build_variables.scraplo + po.obuild( + self.outfile, + "Outboard scrape-off", + build_variables.scraplo, + radius, + "(scraplo)", + ) + po.ovarre( + self.outfile, + "Outboard scrape-off radial thickness (m)", + "(scraplo)", + build_variables.scraplo, + ) + + radius = radius + build_variables.fwoth + po.obuild( + self.outfile, + "Outboard first wall", + build_variables.fwoth, + radius, + "(fwoth)", + ) + po.ovarre( + self.outfile, + "Outboard first wall radial thickness (m)", + "(fwoth)", + build_variables.fwoth, + ) + + radius = radius + build_variables.blnkoth + po.obuild( + self.outfile, + "Outboard blanket", + build_variables.blnkoth, + radius, + "(blnkoth)", + ) + po.ovarre( + self.outfile, + "Outboard blanket radial thickness (m)", + "(blnkoth)", + build_variables.blnkoth, + ) + + radius = radius + build_variables.shldoth + po.obuild( + self.outfile, + "Outboard shield", + build_variables.shldoth, + radius, + "(shldoth)", + ) + po.ovarre( + self.outfile, + "Outer radiation shield radial thickness (m)", + "(shldoth)", + build_variables.shldoth, + ) + + radius = radius + build_variables.d_vv_out + po.obuild( + self.outfile, + "Vacuum vessel", + build_variables.d_vv_out, + radius, + "(d_vv_out)", + ) + + radius = radius + build_variables.gapsto + po.obuild(self.outfile, "Gap", build_variables.gapsto, radius, "(gapsto)") + po.ovarre(self.outfile, "Gap (m)", "(gapsto)", build_variables.gapsto) + + radius = radius + build_variables.tfthko + po.obuild( + self.outfile, + "Coil outboard leg", + build_variables.tfthko, + radius, + "(tfthko)", + ) + po.ovarre( + self.outfile, + "Coil outboard leg radial thickness (m)", + "(tfthko)", + build_variables.tfthko, + ) + + def ststrc(self, output): + """ + Routine to calculate the structural masses for a stellarator + author: P J Knight, CCFE, Culham Science Centre + outfile : input integer : output file unit + iprint : input integer : switch for writing to output file (1=yes) + This routine calculates the structural masses for a stellarator. + This is the stellarator version of routine + STRUCT. In practice, many of the masses + are simply set to zero to avoid double-counting of structural + components that are specified differently for tokamaks. + AEA FUS 251: A User's Guide to the PROCESS Systems Code + """ + structure_variables.fncmass = 0.0e0 + + # Reactor core gravity support mass + structure_variables.gsmass = 0.0e0 # ? Not sure about this. + + # This is the previous scaling law for intercoil structure + # We keep is here as a reference to the new model, which + # we do not really trust yet. + # Mass of support structure (includes casing) (tonnes) + # Scaling for required structure mass (Steel) from: + # F.C. Moon, J. Appl. Phys. 53(12) (1982) 9112 + # + # Values based on regression analysis by Greifswald, March 2014 + M_struc = 1.3483e0 * (1000.0e0 * tfcoil_variables.estotftgj) ** 0.7821e0 + msupstr = 1000.0e0 * M_struc # kg + + ################################################################ + # Intercoil support structure calculation: + # Calculate the intercoil bolted plates structure from the coil surface + # which needs to be precalculated (or calculated in PROCESS but this not done here) + # The coil width is subtracted from that: + # total_coil_width = b + 2* d_ic + 2* case_thickness_constant + # total_coil_thickness = h + 2* d_ic + 2* case_thickness_constant + + # The following line is correct AS LONG AS we do not scale the coil sizes + intercoil_surface = ( + stellarator_configuration.stella_config_coilsurface * st.f_r**2 + - tfcoil_variables.tftort + * stellarator_configuration.stella_config_coillength + * st.f_r + * st.f_n + ) + + # This 0.18 m is an effective thickness which is scaled with empirial 1.5 law. 5.6 T is reference point of Helias + # The thickness 0.18m was obtained as a measured value from Schauer, F. and Bykov, V. design of Helias 5-B. (Nucl Fus. 2013) + structure_variables.aintmass = ( + 0.18e0 * st.f_b**2 * intercoil_surface * fwbs_variables.denstl + ) + + structure_variables.clgsmass = ( + 0.2e0 * structure_variables.aintmass + ) # Very simple approximation for the gravity support. + # This fits for the Helias 5b reactor design point ( F. and Bykov, V. design of Helias 5-B. (nucl Fus. 2013)). + + # Total mass of cooled components + structure_variables.coldmass = ( + tfcoil_variables.whttf + + structure_variables.aintmass + + fwbs_variables.dewmkg + ) + + # Output section + + if output: + po.oheadr(self.outfile, "Support Structure") + po.ovarre( + self.outfile, + "Intercoil support structure mass (from intercoil calculation) (kg)", + "(aintmass)", + structure_variables.aintmass, + ) + po.ovarre( + self.outfile, + "Intercoil support structure mass (scaling, for comparison) (kg)", + "(empiricalmass)", + msupstr, + ) + po.ovarre( + self.outfile, + "Gravity support structure mass (kg)", + "(clgsmass)", + structure_variables.clgsmass, + ) + po.ovarre( + self.outfile, + "Mass of cooled components (kg)", + "(coldmass)", + structure_variables.coldmass, + ) + + def stdiv(self, output: bool): + """Routine to call the stellarator divertor model + author: P J Knight, CCFE, Culham Science Centre + author: F Warmer, IPP Greifswald + outfile : input integer : output file unit + iprint : input integer : switch for writing to output file (1=yes) + This routine calls the divertor model for a stellarator, + developed by Felix Warmer. + Stellarator Divertor Model for the Systems + Code PROCESS, F. Warmer, 21/06/2013 + """ + Theta = stellarator_variables.flpitch # ~bmn [rad] field line pitch + R = physics_variables.rmajor + P_div = physics_variables.pdivt + alpha = divertor_variables.anginc + xi_p = divertor_variables.xpertin + T_scrape = divertor_variables.tdiv + + # Scrape-off temperature in Joules + + E = T_scrape * constants.echarge + + # Sound speed of particles (m/s) + + c_s = np.sqrt(E / (physics_variables.afuel * constants.umass)) + + # Island size (m) + + w_r = 4.0e0 * np.sqrt( + stellarator_variables.bmn + * R + / (stellarator_variables.shear * stellarator_variables.n_res) + ) + + # Perpendicular (to plate) distance from X-point to divertor plate (m) + + Delta = stellarator_variables.f_w * w_r + + # Length 'along' plasma (m) + + l_p = ( + 2 * np.pi * R * (stellarator_variables.m_res) / stellarator_variables.n_res + ) + + # Connection length from X-point to divertor plate (m) + + l_x_t = Delta / Theta + + # Power decay length (m) + + l_q = np.sqrt(xi_p * (l_x_t / c_s)) + + # Channel broadening length (m) + + l_b = np.sqrt(xi_p * l_p / (c_s)) + + # Channel broadening factor + + f_x = 1.0e0 + (l_b / (l_p * Theta)) + + # Length of a single divertor plate (m) + + l_d = f_x * l_p * (Theta / alpha) + + # Total length of divertor plates (m) + + l_t = 2.0e0 * stellarator_variables.n_res * l_d + + # Wetted area (m2) + + a_eff = l_t * l_q + + # Divertor plate width (m): assume total area is wetted area/stellarator_variables.fdivwet + + darea = a_eff / stellarator_variables.fdivwet + l_w = darea / l_t + + # Divertor heat load (MW/m2) + + q_div = stellarator_variables.f_asym * (P_div / a_eff) + + # Transfer to global variables + + divertor_variables.hldiv = q_div + divertor_variables.divsur = darea + + fwbs_variables.fdiv = darea / build_variables.fwarea + + if output: + po.oheadr(self.outfile, "Divertor") + + po.ovarre( + self.outfile, + "Power to divertor (MW)", + "(pdivt.)", + physics_variables.pdivt, + ) + po.ovarre( + self.outfile, + "Angle of incidence (deg)", + "(anginc)", + divertor_variables.anginc * 180.0e0 / np.pi, + ) + po.ovarre( + self.outfile, + "Perp. heat transport coefficient (m2/s)", + "(xpertin)", + divertor_variables.xpertin, + ) + po.ovarre( + self.outfile, + "Divertor plasma temperature (eV)", + "(tdiv)", + divertor_variables.tdiv, + ) + po.ovarre( + self.outfile, + "Radiated power fraction in SOL", + "(f_rad)", + stellarator_variables.f_rad, + ) + po.ovarre( + self.outfile, + "Heat load peaking factor", + "(f_asym)", + stellarator_variables.f_asym, + ) + po.ovarin( + self.outfile, + "Poloidal resonance number", + "(m_res)", + stellarator_variables.m_res, + ) + po.ovarin( + self.outfile, + "Toroidal resonance number", + "(n_res)", + stellarator_variables.n_res, + ) + po.ovarre( + self.outfile, + "Relative radial field perturbation", + "(bmn)", + stellarator_variables.bmn, + ) + po.ovarre( + self.outfile, + "Field line pitch (rad)", + "(flpitch)", + stellarator_variables.flpitch, + ) + po.ovarre( + self.outfile, + "Island size fraction factor", + "(f_w)", + stellarator_variables.f_w, + ) + po.ovarre( + self.outfile, + "Magnetic stellarator_variables.shear (/m)", + "(shear)", + stellarator_variables.shear, + ) + po.ovarre(self.outfile, "Divertor wetted area (m2)", "(A_eff)", a_eff) + po.ovarre( + self.outfile, + "Wetted area fraction of total plate area", + "(fdivwet)", + stellarator_variables.fdivwet, + ) + po.ovarre(self.outfile, "Divertor plate length (m)", "(L_d)", l_d) + po.ovarre(self.outfile, "Divertor plate width (m)", "(L_w)", l_w) + po.ovarre(self.outfile, "Flux channel broadening factor", "(F_x)", f_x) + po.ovarre( + self.outfile, "Power decay width (cm)", "(100*l_q)", 100.0e0 * l_q + ) + po.ovarre(self.outfile, "Island width (m)", "(w_r)", w_r) + po.ovarre( + self.outfile, + "Perp. distance from X-point to plate (m)", + "(Delta)", + Delta, + ) + po.ovarre( + self.outfile, + "Peak heat load (MW/m2)", + "(hldiv)", + divertor_variables.hldiv, + ) + + def blanket_neutronics(self): + # heating of the blanket + if fwbs_variables.breedmat == 1: + fwbs_variables.breeder = "Orthosilicate" + fwbs_variables.densbreed = 1.50e3 + elif fwbs_variables.breedmat == 2: + fwbs_variables.breeder = "Metatitanate" + fwbs_variables.densbreed = 1.78e3 + else: + fwbs_variables.breeder = ( + "Zirconate" # (In reality, rarely used - activation problems) + ) + fwbs_variables.densbreed = 2.12e3 + + fwbs_variables.whtblkt = fwbs_variables.volblkt * fwbs_variables.densbreed + self.hcpb.nuclear_heating_blanket() + + # Heating of the magnets + self.hcpb.nuclear_heating_magnets(False) + + # Rough estimate of TF coil volume used, assuming 25% of the total + # TF coil perimeter is inboard, 75% outboard + tf_volume = ( + 0.25 * tfcoil_variables.tfleng * tfcoil_variables.tfareain + + 0.75 + * tfcoil_variables.tfleng + * tfcoil_variables.arealeg + * tfcoil_variables.n_tf + ) + + fwbs_variables.ptfnucpm3 = fwbs_variables.ptfnuc / tf_volume + + # heating of the shield + self.hcpb.nuclear_heating_shield() + + # Energy multiplication factor + fwbs_variables.emult = 1.269 + + # Tritium breeding ratio + fwbs_variables.tbr = self.hcpb.tbr_shimwell( + fwbs_variables.volblkt, fwbs_variables.li6enrich, 1 + ) + + # Use older model to calculate neutron fluence since it + # is not calculated in the CCFE blanket model + ( + _, + _, + _, + fwbs_variables.nflutf, + _, + _, + _, + _, + _, + _, + ) = fwbs_module.sctfcoil_nuclear_heating_iter90() + + # blktlife calculation left entierly to availability + # Cannot find calculation for vvhemax in CCFE blanket + + def stfwbs(self, output: bool): + """Routine to calculate first wall, blanket and shield properties + for a stellarator + author: P J Knight, CCFE, Culham Science Centre + author: F Warmer, IPP Greifswald + outfile : input integer : Fortran output unit identifier + iprint : input integer : Switch to write output to file (1=yes) + This routine calculates a stellarator's first wall, blanket and + shield properties. + It calculates the nuclear heating in the blanket / shield, and + estimates the volume and masses of the first wall, + blanket, shield and vacuum vessel. +

The arrays coef(i,j) and decay(i,j) + are used for exponential decay approximations of the + (superconducting) TF coil nuclear parameters. +

+ Note: Costing and mass calculations elsewhere assume + stainless steel only. +

The method is the same as for tokamaks (as performed via + fwbs), except for the volume calculations, + which scale the surface area of the components from that + of the plasma. + AEA FUS 251: A User's Guide to the PROCESS Systems Code + """ + fwbs_variables.fwlife = min( + cost_variables.abktflnc / physics_variables.wallmw, cost_variables.tlife + ) + + # First wall inboard, outboard areas (assume 50% of total each) + build_variables.fwareaib = 0.5e0 * build_variables.fwarea + build_variables.fwareaob = 0.5e0 * build_variables.fwarea + + # Blanket volume; assume that its surface area is scaled directly from the + # plasma surface area. + # Uses fwbs_variables.fhole etc. to take account of gaps due to ports etc. + + r1 = physics_variables.rminor + 0.5e0 * ( + build_variables.scrapli + + build_variables.fwith + + build_variables.scraplo + + build_variables.fwoth + ) + if heat_transport_variables.ipowerflow == 0: + build_variables.blarea = ( + physics_variables.sarea + * r1 + / physics_variables.rminor + * (1.0e0 - fwbs_variables.fhole) + ) + else: + build_variables.blarea = ( + physics_variables.sarea + * r1 + / physics_variables.rminor + * ( + 1.0e0 + - fwbs_variables.fhole + - fwbs_variables.fdiv + - fwbs_variables.fhcd + ) + ) + + build_variables.blareaib = 0.5e0 * build_variables.blarea + build_variables.blareaob = 0.5e0 * build_variables.blarea + + fwbs_variables.volblkti = build_variables.blareaib * build_variables.blnkith + fwbs_variables.volblkto = build_variables.blareaob * build_variables.blnkoth + fwbs_variables.volblkt = fwbs_variables.volblkti + fwbs_variables.volblkto + + # Shield volume + # Uses fvolsi, fwbs_variables.fvolso as area coverage factors + + r1 = r1 + 0.5e0 * (build_variables.blnkith + build_variables.blnkoth) + build_variables.sharea = physics_variables.sarea * r1 / physics_variables.rminor + build_variables.shareaib = ( + 0.5e0 * build_variables.sharea * fwbs_variables.fvolsi + ) + build_variables.shareaob = ( + 0.5e0 * build_variables.sharea * fwbs_variables.fvolso + ) + + volshldi = build_variables.shareaib * build_variables.shldith + volshldo = build_variables.shareaob * build_variables.shldoth + fwbs_variables.volshld = volshldi + volshldo + + fwbs_variables.whtshld = ( + fwbs_variables.volshld + * fwbs_variables.denstl + * (1.0e0 - fwbs_variables.vfshld) + ) + + # Neutron power lost through holes in first wall (eventually absorbed by + # shield) + + fwbs_variables.pnucloss = physics_variables.pneutmw * fwbs_variables.fhole + + # The peaking factor, obtained as precalculated parameter + fwbs_variables.wallpf = ( + stellarator_configuration.stella_config_neutron_peakfactor + ) + + # Blanket neutronics calculations + if fwbs_variables.blktmodel == 1: + self.blanket_neutronics() + + if heat_transport_variables.ipowerflow == 1: + fwbs_variables.pnucdiv = physics_variables.pneutmw * fwbs_variables.fdiv + fwbs_variables.pnuchcd = physics_variables.pneutmw * fwbs_variables.fhcd + fwbs_variables.pnucfw = ( + physics_variables.pneutmw + - fwbs_variables.pnucdiv + - fwbs_variables.pnucloss + - fwbs_variables.pnuchcd + ) + + fwbs_variables.pradloss = ( + physics_variables.pradmw * fwbs_variables.fhole + ) + fwbs_variables.praddiv = physics_variables.pradmw * fwbs_variables.fdiv + fwbs_variables.pradhcd = physics_variables.pradmw * fwbs_variables.fhcd + fwbs_variables.pradfw = ( + physics_variables.pradmw + - fwbs_variables.praddiv + - fwbs_variables.pradloss + - fwbs_variables.pradhcd + ) + + heat_transport_variables.htpmw_fw = heat_transport_variables.fpumpfw * ( + fwbs_variables.pnucfw + + fwbs_variables.pradfw + + current_drive_variables.porbitlossmw + ) + heat_transport_variables.htpmw_blkt = ( + heat_transport_variables.fpumpblkt * fwbs_variables.pnucblkt + ) + heat_transport_variables.htpmw_shld = ( + heat_transport_variables.fpumpshld * fwbs_variables.pnucshld + ) + heat_transport_variables.htpmw_div = ( + heat_transport_variables.fpumpdiv + * ( + physics_variables.pdivt + + fwbs_variables.pnucdiv + + fwbs_variables.praddiv + ) + ) + + # Void fraction in first wall / breeding zone, + # for use in fwbs_variables.fwmass and coolvol calculation below + + vffwi = ( + 1.0e0 + - fwbs_variables.fblbe + - fwbs_variables.fblbreed + - fwbs_variables.fblss + ) + vffwo = vffwi + + else: + fwbs_variables.pnuc_cp = 0.0e0 + + if heat_transport_variables.ipowerflow == 0: + # Energy-multiplied neutron power + + pneut2 = ( + physics_variables.pneutmw + - fwbs_variables.pnucloss + - fwbs_variables.pnuc_cp + ) * fwbs_variables.emult + + fwbs_variables.emultmw = pneut2 - ( + physics_variables.pneutmw + - fwbs_variables.pnucloss + - fwbs_variables.pnuc_cp + ) + + # Nuclear heating in the blanket + + decaybl = 0.075e0 / ( + 1.0e0 + - fwbs_variables.vfblkt + - fwbs_variables.fblli2o + - fwbs_variables.fblbe + ) + + fwbs_variables.pnucblkt = pneut2 * ( + 1.0e0 - np.exp(-build_variables.blnkoth / decaybl) + ) + + # Nuclear heating in the shield + fwbs_variables.pnucshld = pneut2 - fwbs_variables.pnucblkt + + # Superconducting coil shielding calculations + ( + coilhtmx, + dpacop, + htheci, + fwbs_variables.nflutf, + pheci, + pheco, + ptfiwp, + ptfowp, + raddose, + fwbs_variables.ptfnuc, + ) = fwbs_module.sctfcoil_nuclear_heating_iter90() + + else: # heat_transport_variables.ipowerflow == 1 + # Neutron power incident on divertor (MW) + + fwbs_variables.pnucdiv = physics_variables.pneutmw * fwbs_variables.fdiv + + # Neutron power incident on HCD apparatus (MW) + + fwbs_variables.pnuchcd = physics_variables.pneutmw * fwbs_variables.fhcd + + # Neutron power deposited in first wall, blanket and shield (MW) + + pnucfwbs = ( + physics_variables.pneutmw + - fwbs_variables.pnucdiv + - fwbs_variables.pnucloss + - fwbs_variables.pnuc_cp + - fwbs_variables.pnuchcd + ) + + # Split between inboard and outboard by first wall area fractions + + pnucfwbsi = pnucfwbs * build_variables.fwareaib / build_variables.fwarea + pnucfwbso = pnucfwbs * build_variables.fwareaob / build_variables.fwarea + + # Radiation power incident on divertor (MW) + + fwbs_variables.praddiv = physics_variables.pradmw * fwbs_variables.fdiv + + # Radiation power incident on HCD apparatus (MW) + + fwbs_variables.pradhcd = physics_variables.pradmw * fwbs_variables.fhcd + + # Radiation power lost through holes (eventually hits shield) (MW) + + fwbs_variables.pradloss = ( + physics_variables.pradmw * fwbs_variables.fhole + ) + + # Radiation power incident on first wall (MW) + + fwbs_variables.pradfw = ( + physics_variables.pradmw + - fwbs_variables.praddiv + - fwbs_variables.pradloss + - fwbs_variables.pradhcd + ) + + # Calculate the power deposited in the first wall, blanket and shield, + # and the required coolant pumping power + + # If we have chosen pressurised water as the coolant, set the + # coolant outlet temperature as 20 deg C below the boiling point + + if fwbs_variables.coolwh == 2: + if fwbs_variables.irefprop: + fwbs_variables.outlet_temp = ( + FluidProperties.of( + "Water", + pressure=fwbs_variables.coolp, + vapor_quality=0, + ) + - 20 + ) + else: + fwbs_variables.outlet_temp = ( + 273.15 + + 168.396 + + 0.314653 / fwbs_variables.coolp + + -0.000728 / fwbs_variables.coolp**2 + + 31.588979 * np.log(fwbs_variables.coolp) + + 11.473141 * fwbs_variables.coolp + + -0.575335 * fwbs_variables.coolp**2 + + 0.013165 * fwbs_variables.coolp**3 + ) - 20 + + bfwi = 0.5e0 * build_variables.fwith + bfwo = 0.5e0 * build_variables.fwoth + + vffwi = ( + fwbs_variables.afwi * fwbs_variables.afwi / (bfwi * bfwi) + ) # inboard FW coolant void fraction + vffwo = ( + fwbs_variables.afwo * fwbs_variables.afwo / (bfwo * bfwo) + ) # outboard FW coolant void fraction + + # First wall decay length (m) - improved calculation required + + decayfwi = fwbs_variables.declfw + decayfwo = fwbs_variables.declfw + + # Surface heat flux on first wall (MW) (sum = fwbs_variables.pradfw) + + psurffwi = ( + fwbs_variables.pradfw + * build_variables.fwareaib + / build_variables.fwarea + ) + psurffwo = ( + fwbs_variables.pradfw + * build_variables.fwareaob + / build_variables.fwarea + ) + + # Simple blanket model (fwbs_variables.primary_pumping = 0 or 1) is assumed for stellarators + + # The power deposited in the first wall, breeder zone and shield is + # calculated according to their dimensions and materials assuming + # an exponential attenuation of nuclear heating with increasing + # radial distance. The pumping power for the coolant is calculated + # as a fraction of the total thermal power deposited in the + # coolant. + + pnucfwi = pnucfwbsi * (1.0e0 - np.exp(-2.0e0 * bfwi / decayfwi)) + pnucfwo = pnucfwbso * (1.0e0 - np.exp(-2.0e0 * bfwo / decayfwo)) + + # Neutron power reaching blanket and shield (MW) + + pnucbsi = pnucfwbsi - pnucfwi + pnucbso = pnucfwbso - pnucfwo + + # Blanket decay length (m) - improved calculation required + + decaybzi = fwbs_variables.declblkt + decaybzo = fwbs_variables.declblkt + + # Neutron power deposited in breeder zone (MW) + + pnucbzi = pnucbsi * ( + 1.0e0 - np.exp(-build_variables.blnkith / decaybzi) + ) + pnucbzo = pnucbso * ( + 1.0e0 - np.exp(-build_variables.blnkoth / decaybzo) + ) + + # Calculate coolant pumping powers from input fraction. + # The pumping power is assumed to be a fraction, fpump, of the + # incident thermal power to each component so that + # htpmw_i = fpump_i*C, where C is the non-pumping thermal power + # deposited in the coolant + + # First wall and Blanket pumping power (MW) + + if fwbs_variables.primary_pumping == 0: + # Use input + pass + elif fwbs_variables.primary_pumping == 1: + heat_transport_variables.htpmw_fw = ( + heat_transport_variables.fpumpfw + * ( + pnucfwi + + pnucfwo + + psurffwi + + psurffwo + + current_drive_variables.porbitlossmw + ) + ) + heat_transport_variables.htpmw_blkt = ( + heat_transport_variables.fpumpblkt + * ( + pnucbzi * fwbs_variables.emult + + pnucbzo * fwbs_variables.emult + ) + ) + else: + error_handling.report_error(215) + + fwbs_variables.emultmw = ( + heat_transport_variables.fpumpblkt + * (pnucbzi * fwbs_variables.emult + pnucbzo) + * (fwbs_variables.emult - 1.0e0) + ) + + # Total nuclear heating of first wall (MW) + + fwbs_variables.pnucfw = pnucfwi + pnucfwo + + # Total nuclear heating of blanket (MW) + + fwbs_variables.pnucblkt = (pnucbzi + pnucbzo) * fwbs_variables.emult + + fwbs_variables.emultmw = fwbs_variables.emultmw + ( + pnucbzi + pnucbzo + ) * (fwbs_variables.emult - 1.0e0) + + # Calculation of shield and divertor powers + # Shield and divertor powers and pumping powers are calculated using the same + # simplified method as the first wall and breeder zone when fwbs_variables.primary_pumping = 1. + # i.e. the pumping power is a fraction of the total thermal power deposited in the + # coolant. + + # Neutron power reaching the shield (MW) + # The power lost from the fwbs_variables.fhole area fraction is assumed to be incident upon the shield + + pnucsi = ( + pnucbsi + - pnucbzi + + (fwbs_variables.pnucloss + fwbs_variables.pradloss) + * build_variables.fwareaib + / build_variables.fwarea + ) + pnucso = ( + pnucbso + - pnucbzo + + (fwbs_variables.pnucloss + fwbs_variables.pradloss) + * build_variables.fwareaob + / build_variables.fwarea + ) + + # Improved calculation of shield power decay lengths required + + decayshldi = fwbs_variables.declshld + decayshldo = fwbs_variables.declshld + + # Neutron power deposited in the shield (MW) + + pnucshldi = pnucsi * ( + 1.0e0 - np.exp(-build_variables.shldith / decayshldi) + ) + pnucshldo = pnucso * ( + 1.0e0 - np.exp(-build_variables.shldoth / decayshldo) + ) + + fwbs_variables.pnucshld = pnucshldi + pnucshldo + + # Calculate coolant pumping powers from input fraction. + # The pumping power is assumed to be a fraction, fpump, of the incident + # thermal power to each component so that, + # htpmw_i = fpump_i*C + # where C is the non-pumping thermal power deposited in the coolant + + if fwbs_variables.primary_pumping == 1: + # Shield pumping power (MW) + heat_transport_variables.htpmw_shld = ( + heat_transport_variables.fpumpshld * (pnucshldi + pnucshldo) + ) + + # Divertor pumping power (MW) + heat_transport_variables.htpmw_div = ( + heat_transport_variables.fpumpdiv + * ( + physics_variables.pdivt + + fwbs_variables.pnucdiv + + fwbs_variables.praddiv + ) + ) + + # Remaining neutron power to coils and else:where. This is assumed + # (for superconducting coils at least) to be absorbed by the + # coils, and so contributes to the cryogenic load + + if tfcoil_variables.i_tf_sup == 1: + fwbs_variables.ptfnuc = pnucsi + pnucso - pnucshldi - pnucshldo + else: # resistive coils + fwbs_variables.ptfnuc = 0.0e0 + + # heat_transport_variables.ipowerflow + + # fwbs_variables.blktmodel + + # Divertor mass + # N.B. divertor_variables.divsur is calculated in stdiv after this point, so will + # be zero on first lap, hence the initial approximation + + if self.first_call_stfwbs: + divertor_variables.divsur = 50.0e0 + self.first_call_stfwbs = False + + divertor_variables.divmas = ( + divertor_variables.divsur + * divertor_variables.divdens + * (1.0e0 - divertor_variables.divclfr) + * divertor_variables.divplt + ) + + # Start adding components of the coolant mass: + # Divertor coolant volume (m3) + + coolvol = ( + divertor_variables.divsur + * divertor_variables.divclfr + * divertor_variables.divplt + ) + + # Blanket mass, excluding coolant + + if fwbs_variables.blktmodel == 0: + if (fwbs_variables.blkttype == 1) or ( + fwbs_variables.blkttype == 2 + ): # liquid breeder (WCLL or HCLL) + fwbs_variables.wtbllipb = ( + fwbs_variables.volblkt * fwbs_variables.fbllipb * 9400.0e0 + ) + fwbs_variables.whtblli = ( + fwbs_variables.volblkt * fwbs_variables.fblli * 534.0e0 + ) + fwbs_variables.whtblkt = ( + fwbs_variables.wtbllipb + fwbs_variables.whtblli + ) + else: # solid breeder (HCPB); always for ipowerflow=0 + fwbs_variables.wtblli2o = ( + fwbs_variables.volblkt * fwbs_variables.fblli2o * 2010.0e0 + ) + fwbs_variables.whtblbe = ( + fwbs_variables.volblkt * fwbs_variables.fblbe * 1850.0e0 + ) + fwbs_variables.whtblkt = ( + fwbs_variables.wtblli2o + fwbs_variables.whtblbe + ) + + fwbs_variables.whtblss = ( + fwbs_variables.volblkt * fwbs_variables.denstl * fwbs_variables.fblss + ) + fwbs_variables.whtblvd = ( + fwbs_variables.volblkt * 5870.0e0 * fwbs_variables.fblvd + ) + + fwbs_variables.whtblkt = ( + fwbs_variables.whtblkt + fwbs_variables.whtblss + fwbs_variables.whtblvd + ) + + else: # volume fractions proportional to sub-assembly thicknesses + fwbs_variables.whtblss = fwbs_variables.denstl * ( + fwbs_variables.volblkti + / build_variables.blnkith + * ( + build_variables.blbuith * fwbs_variables.fblss + + build_variables.blbmith * (1.0e0 - fwbs_variables.fblhebmi) + + build_variables.blbpith * (1.0e0 - fwbs_variables.fblhebpi) + ) + + fwbs_variables.volblkto + / build_variables.blnkoth + * ( + build_variables.blbuoth * fwbs_variables.fblss + + build_variables.blbmoth * (1.0e0 - fwbs_variables.fblhebmo) + + build_variables.blbpoth * (1.0e0 - fwbs_variables.fblhebpo) + ) + ) + fwbs_variables.whtblbe = ( + 1850.0e0 + * fwbs_variables.fblbe + * ( + ( + fwbs_variables.volblkti + * build_variables.blbuith + / build_variables.blnkith + ) + + ( + fwbs_variables.volblkto + * build_variables.blbuoth + / build_variables.blnkoth + ) + ) + ) + fwbs_variables.whtblbreed = ( + fwbs_variables.densbreed + * fwbs_variables.fblbreed + * ( + ( + fwbs_variables.volblkti + * build_variables.blbuith + / build_variables.blnkith + ) + + ( + fwbs_variables.volblkto + * build_variables.blbuoth + / build_variables.blnkoth + ) + ) + ) + fwbs_variables.whtblkt = ( + fwbs_variables.whtblss + + fwbs_variables.whtblbe + + fwbs_variables.whtblbreed + ) + + fwbs_variables.vfblkt = ( + fwbs_variables.volblkti + / fwbs_variables.volblkt + * ( # inboard portion + (build_variables.blbuith / build_variables.blnkith) + * ( + 1.0e0 + - fwbs_variables.fblbe + - fwbs_variables.fblbreed + - fwbs_variables.fblss + ) + + (build_variables.blbmith / build_variables.blnkith) + * fwbs_variables.fblhebmi + + (build_variables.blbpith / build_variables.blnkith) + * fwbs_variables.fblhebpi + ) + ) + fwbs_variables.vfblkt = ( + fwbs_variables.vfblkt + + fwbs_variables.volblkto + / fwbs_variables.volblkt + * ( # outboard portion + (build_variables.blbuoth / build_variables.blnkoth) + * ( + 1.0e0 + - fwbs_variables.fblbe + - fwbs_variables.fblbreed + - fwbs_variables.fblss + ) + + (build_variables.blbmoth / build_variables.blnkoth) + * fwbs_variables.fblhebmo + + (build_variables.blbpoth / build_variables.blnkoth) + * fwbs_variables.fblhebpo + ) + ) + + # When fwbs_variables.blktmodel > 0, although the blanket is by definition helium-cooled + # in this case, the shield etc. are assumed to be water-cooled, and since + # water is heavier the calculation for fwbs_variables.coolmass is better done with + # coolwh=2 if fwbs_variables.blktmodel > 0; thus we can ignore the helium coolant mass + # in the blanket. + + if fwbs_variables.blktmodel == 0: + coolvol = coolvol + fwbs_variables.volblkt * fwbs_variables.vfblkt + + coolvol = coolvol + fwbs_variables.volshld * fwbs_variables.vfshld + + # Penetration shield (set = internal shield) + + fwbs_variables.wpenshld = fwbs_variables.whtshld + + if heat_transport_variables.ipowerflow == 0: + # First wall mass + # (first wall area is calculated else:where) + + fwbs_variables.fwmass = ( + build_variables.fwarea + * (build_variables.fwith + build_variables.fwoth) + / 2.0e0 + * fwbs_variables.denstl + * (1.0e0 - fwbs_variables.fwclfr) + ) + + # Surface areas adjacent to plasma + + coolvol = ( + coolvol + + build_variables.fwarea + * (build_variables.fwith + build_variables.fwoth) + / 2.0e0 + * fwbs_variables.fwclfr + ) + + else: + fwbs_variables.fwmass = fwbs_variables.denstl * ( + build_variables.fwareaib * build_variables.fwith * (1.0e0 - vffwi) + + build_variables.fwareaob * build_variables.fwoth * (1.0e0 - vffwo) + ) + coolvol = ( + coolvol + + build_variables.fwareaib * build_variables.fwith * vffwi + + build_variables.fwareaob * build_variables.fwoth * vffwo + ) + + # Average first wall coolant fraction, only used by old routines + # in fispact.f90, safety.f90 + + fwbs_variables.fwclfr = ( + build_variables.fwareaib * build_variables.fwith * vffwi + + build_variables.fwareaob * build_variables.fwoth * vffwo + ) / ( + build_variables.fwarea + * 0.5e0 + * (build_variables.fwith + build_variables.fwoth) + ) + + # Mass of coolant = volume * density at typical coolant + # temperatures and pressures + # N.B. for fwbs_variables.blktmodel > 0, mass of *water* coolant in the non-blanket + # structures is used (see comment above) + + if (fwbs_variables.blktmodel > 0) or ( + fwbs_variables.coolwh == 2 + ): # pressurised water coolant + fwbs_variables.coolmass = coolvol * 806.719e0 + else: # gaseous helium coolant + fwbs_variables.coolmass = coolvol * 1.517e0 + + # Assume external cryostat is a torus with circular cross-section, + # centred on plasma major radius. + # N.B. No check made to see if coils etc. lie wholly within cryostat... + + # External cryostat outboard major radius (m) + + fwbs_variables.rdewex = ( + build_variables.r_tf_outboard_mid + + 0.5e0 * build_variables.tfthko + + fwbs_variables.rpf2dewar + ) + adewex = fwbs_variables.rdewex - physics_variables.rmajor + + # External cryostat volume + + fwbs_variables.vdewex = ( + 4.0e0 + * (np.pi**2) + * physics_variables.rmajor + * adewex + * build_variables.ddwex + ) + + # Internal vacuum vessel volume + # fwbs_variables.fvoldw accounts for ports, support, etc. additions + + r1 = physics_variables.rminor + 0.5e0 * ( + build_variables.scrapli + + build_variables.fwith + + build_variables.blnkith + + build_variables.shldith + + build_variables.scraplo + + build_variables.fwoth + + build_variables.blnkoth + + build_variables.shldoth + ) + fwbs_variables.vdewin = ( + (build_variables.d_vv_in + build_variables.d_vv_out) + / 2.0e0 + * physics_variables.sarea + * r1 + / physics_variables.rminor + * fwbs_variables.fvoldw + ) + + # Vacuum vessel mass + + fwbs_variables.vvmass = fwbs_variables.vdewin * fwbs_variables.denstl + + # Sum of internal vacuum vessel and external cryostat masses + + fwbs_variables.dewmkg = ( + fwbs_variables.vdewin + fwbs_variables.vdewex + ) * fwbs_variables.denstl + + if output: + # Output section + + po.oheadr(self.outfile, "First Wall / Blanket / Shield") + po.ovarre( + self.outfile, + "Average neutron wall load (MW/m2)", + "(wallmw)", + physics_variables.wallmw, + ) + if fwbs_variables.blktmodel > 0: + po.ovarre( + self.outfile, + "Neutron wall load peaking factor", + "(wallpf)", + fwbs_variables.wallpf, + ) + + po.ovarre( + self.outfile, + "First wall full-power lifetime (years)", + "(fwlife)", + fwbs_variables.fwlife, + ) + + po.ovarre( + self.outfile, + "Inboard shield thickness (m)", + "(shldith)", + build_variables.shldith, + ) + po.ovarre( + self.outfile, + "Outboard shield thickness (m)", + "(shldoth)", + build_variables.shldoth, + ) + po.ovarre( + self.outfile, + "Top shield thickness (m)", + "(shldtth)", + build_variables.shldtth, + ) + + if fwbs_variables.blktmodel > 0: + po.ovarre( + self.outfile, + "Inboard breeding zone thickness (m)", + "(blbuith)", + build_variables.blbuith, + ) + po.ovarre( + self.outfile, + "Inboard box manifold thickness (m)", + "(blbmith)", + build_variables.blbmith, + ) + po.ovarre( + self.outfile, + "Inboard back plate thickness (m)", + "(blbpith)", + build_variables.blbpith, + ) + + po.ovarre( + self.outfile, + "Inboard blanket thickness (m)", + "(blnkith)", + build_variables.blnkith, + ) + if fwbs_variables.blktmodel > 0: + po.ovarre( + self.outfile, + "Outboard breeding zone thickness (m)", + "(blbuoth)", + build_variables.blbuoth, + ) + po.ovarre( + self.outfile, + "Outboard box manifold thickness (m)", + "(blbmoth)", + build_variables.blbmoth, + ) + po.ovarre( + self.outfile, + "Outboard back plate thickness (m)", + "(blbpoth)", + build_variables.blbpoth, + ) + + po.ovarre( + self.outfile, + "Outboard blanket thickness (m)", + "(blnkoth)", + build_variables.blnkoth, + ) + po.ovarre( + self.outfile, + "Top blanket thickness (m)", + "(blnktth)", + build_variables.blnktth, + ) + + if (heat_transport_variables.ipowerflow == 0) and ( + fwbs_variables.blktmodel == 0 + ): + po.osubhd(self.outfile, "Coil nuclear parameters :") + po.ovarre( + self.outfile, "Peak magnet heating (MW/m3)", "(coilhtmx)", coilhtmx + ) + po.ovarre( + self.outfile, + "Inboard coil winding pack heating (MW)", + "(ptfiwp)", + ptfiwp, + ) + po.ovarre( + self.outfile, + "Outboard coil winding pack heating (MW)", + "(ptfowp)", + ptfowp, + ) + po.ovarre( + self.outfile, "Peak coil case heating (MW/m3)", "(htheci)", htheci + ) + po.ovarre( + self.outfile, "Inboard coil case heating (MW)", "(pheci)", pheci + ) + po.ovarre( + self.outfile, "Outboard coil case heating (MW)", "(pheco)", pheco + ) + po.ovarre(self.outfile, "Insulator dose (rad)", "(raddose)", raddose) + po.ovarre( + self.outfile, + "Maximum neutron fluence (n/m2)", + "(nflutf)", + fwbs_variables.nflutf, + ) + po.ovarre( + self.outfile, + "Copper stabiliser displacements/atom", + "(dpacop)", + dpacop, + ) + + if fwbs_variables.blktmodel == 0: + po.osubhd(self.outfile, "Nuclear heating :") + po.ovarre( + self.outfile, + "Blanket heating (including energy multiplication) (MW)", + "(pnucblkt)", + fwbs_variables.pnucblkt, + ) + po.ovarre( + self.outfile, + "Shield nuclear heating (MW)", + "(pnucshld)", + fwbs_variables.pnucshld, + ) + po.ovarre( + self.outfile, + "Coil nuclear heating (MW)", + "(ptfnuc)", + fwbs_variables.ptfnuc, + ) + else: + po.osubhd(self.outfile, "Blanket neutronics :") + po.ovarre( + self.outfile, + "Blanket heating (including energy multiplication) (MW)", + "(pnucblkt)", + fwbs_variables.pnucblkt, + ) + po.ovarre( + self.outfile, + "Shield heating (MW)", + "(pnucshld)", + fwbs_variables.pnucshld, + ) + po.ovarre( + self.outfile, + "Energy multiplication in blanket", + "(emult)", + fwbs_variables.emult, + ) + po.ovarin( + self.outfile, + "Number of divertor ports assumed", + "(npdiv)", + fwbs_variables.npdiv, + ) + po.ovarin( + self.outfile, + "Number of inboard H/CD ports assumed", + "(nphcdin)", + fwbs_variables.nphcdin, + ) + po.ovarin( + self.outfile, + "Number of outboard H/CD ports assumed", + "(nphcdout)", + fwbs_variables.nphcdout, + ) + if fwbs_variables.hcdportsize == 1: + po.ocmmnt( + self.outfile, " (small heating/current drive ports assumed)" + ) + else: + po.ocmmnt( + self.outfile, " (large heating/current drive ports assumed)" + ) + + if fwbs_variables.breedmat == 1: + po.ocmmnt( + self.outfile, + "Breeder material: Lithium orthosilicate (Li4Si04)", + ) + elif fwbs_variables.breedmat == 2: + po.ocmmnt( + self.outfile, + "Breeder material: Lithium methatitanate (Li2TiO3)", + ) + elif fwbs_variables.breedmat == 3: + po.ocmmnt( + self.outfile, "Breeder material: Lithium zirconate (Li2ZrO3)" + ) + else: # shouldn't get here... + po.ocmmnt(self.outfile, "Unknown breeder material...") + + po.ovarre( + self.outfile, + "Lithium-6 enrichment (%)", + "(li6enrich)", + fwbs_variables.li6enrich, + ) + po.ovarre( + self.outfile, "Tritium breeding ratio", "(tbr)", fwbs_variables.tbr + ) + po.ovarre( + self.outfile, + "Tritium production rate (g/day)", + "(tritprate)", + fwbs_variables.tritprate, + ) + po.ovarre( + self.outfile, + "Nuclear heating on i/b coil (MW/m3)", + "(pnuctfi)", + fwbs_variables.pnuctfi, + ) + po.ovarre( + self.outfile, + "Nuclear heating on o/b coil (MW/m3)", + "(pnuctfo)", + fwbs_variables.pnuctfo, + ) + po.ovarre( + self.outfile, + "Total nuclear heating on coil (MW)", + "(ptfnuc)", + fwbs_variables.ptfnuc, + ) + po.ovarre( + self.outfile, + "Fast neut. fluence on i/b coil (n/m2)", + "(nflutfi)", + fwbs_variables.nflutfi * 1.0e4, + ) + po.ovarre( + self.outfile, + "Fast neut. fluence on o/b coil (n/m2)", + "(nflutfo)", + fwbs_variables.nflutfo * 1.0e4, + ) + po.ovarre( + self.outfile, + "Minimum final He conc. in IB VV (appm)", + "(vvhemini)", + fwbs_variables.vvhemini, + ) + po.ovarre( + self.outfile, + "Minimum final He conc. in OB VV (appm)", + "(vvhemino)", + fwbs_variables.vvhemino, + ) + po.ovarre( + self.outfile, + "Maximum final He conc. in IB VV (appm)", + "(vvhemaxi)", + fwbs_variables.vvhemaxi, + ) + po.ovarre( + self.outfile, + "Maximum final He conc. in OB VV (appm)", + "(vvhemaxo)", + fwbs_variables.vvhemaxo, + ) + po.ovarre( + self.outfile, + "Blanket lifetime (full power years)", + "(bktlife)", + fwbs_variables.bktlife, + ) + po.ovarre( + self.outfile, + "Blanket lifetime (calendar years)", + "(t_bl_y)", + fwbs_variables.t_bl_y, + ) + + if (heat_transport_variables.ipowerflow == 1) and ( + fwbs_variables.blktmodel == 0 + ): + po.oblnkl(self.outfile) + po.ovarin( + self.outfile, + "First wall / blanket thermodynamic model", + "(secondary_cycle)", + fwbs_variables.secondary_cycle, + ) + if fwbs_variables.secondary_cycle == 0: + po.ocmmnt(self.outfile, " (Simple calculation)") + + po.osubhd(self.outfile, "Blanket / shield volumes and weights :") + + # if (fwbs_variables.blktmodel == 0) : + # if ((fwbs_variables.blkttype == 1)or(fwbs_variables.blkttype == 2)) : + # po.write(self.outfile,601) volblkti, volblkto, volblkt, whtblkt, vfblkt, fbllipb, wtbllipb, fblli, whtblli, fblss, whtblss, fblvd, whtblvd, volshldi, volshldo, volshld, whtshld, vfshld, fwbs_variables.wpenshld + # else: # (also if ipowerflow=0) + # po.write(self.outfile,600) volblkti, volblkto, volblkt, whtblkt, vfblkt, fblbe, whtblbe, fblli2o, wtblli2o, fblss, whtblss, fblvd, whtblvd, volshldi, volshldo, volshld, whtshld, vfshld, fwbs_variables.wpenshld + + # else: + # po.write(self.outfile,602) volblkti, volblkto, volblkt, whtblkt, vfblkt, (fwbs_variables.volblkti/fwbs_variables.volblkt * build_variables.blbuith/build_variables.blnkith + fwbs_variables.volblkto/fwbs_variables.volblkt * build_variables.blbuoth/build_variables.blnkoth) * fblbe, whtblbe, (fwbs_variables.volblkti/fwbs_variables.volblkt * build_variables.blbuith/build_variables.blnkith + fwbs_variables.volblkto/fwbs_variables.volblkt * build_variables.blbuoth/build_variables.blnkoth) * fblbreed, whtblbreed, fwbs_variables.volblkti/fwbs_variables.volblkt/build_variables.blnkith * (build_variables.blbuith * fwbs_variables.fblss + build_variables.blbmith * (1.0e0-fwbs_variables.fblhebmi) + build_variables.blbpith * (1.0e0-fwbs_variables.fblhebpi)) + fwbs_variables.volblkto/fwbs_variables.volblkt/build_variables.blnkoth * (build_variables.blbuoth * fwbs_variables.fblss + build_variables.blbmoth * (1.0e0-fwbs_variables.fblhebmo) + build_variables.blbpoth * (1.0e0-fwbs_variables.fblhebpo)), whtblss, volshldi, volshldo, volshld, whtshld, vfshld, fwbs_variables.wpenshld + + # 600 format( t32,'volume (m3)',t45,'vol fraction',t62,'weight (kg)'/ t32,'-----------',t45,'------------',t62,'-----------'/ ' Inboard blanket' ,t32,1pe10.3,/ ' Outboard blanket' ,t32,1pe10.3,/ ' Total blanket' ,t32,1pe10.3,t62,1pe10.3/ ' Void fraction' ,t45,1pe10.3,/ ' Blanket Be ',t45,1pe10.3,t62,1pe10.3/ ' Blanket Li2O ',t45,1pe10.3,t62,1pe10.3/ ' Blanket ss ',t45,1pe10.3,t62,1pe10.3/ ' Blanket Vd ',t45,1pe10.3,t62,1pe10.3/ ' Inboard shield' ,t32,1pe10.3,/ ' Outboard shield' ,t32,1pe10.3,/ ' Primary shield',t32,1pe10.3,t62,1pe10.3/ ' Void fraction' ,t45,1pe10.3,/ ' Penetration shield' ,t62,1pe10.3) + + # 601 format( t32,'volume (m3)',t45,'vol fraction',t62,'weight (kg)'/ t32,'-----------',t45,'------------',t62,'-----------'/ ' Inboard blanket' ,t32,1pe10.3,/ ' Outboard blanket' ,t32,1pe10.3,/ ' Total blanket' ,t32,1pe10.3,t62,1pe10.3/ ' Void fraction' ,t45,1pe10.3,/ ' Blanket LiPb ',t45,1pe10.3,t62,1pe10.3/ ' Blanket Li ',t45,1pe10.3,t62,1pe10.3/ ' Blanket ss ',t45,1pe10.3,t62,1pe10.3/ ' Blanket Vd ',t45,1pe10.3,t62,1pe10.3/ ' Inboard shield' ,t32,1pe10.3,/ ' Outboard shield' ,t32,1pe10.3,/ ' Primary shield',t32,1pe10.3,t62,1pe10.3/ ' Void fraction' ,t45,1pe10.3,/ ' Penetration shield' ,t62,1pe10.3) + + # 602 format( t32,'volume (m3)',t45,'vol fraction',t62,'weight (kg)'/ t32,'-----------',t45,'------------',t62,'-----------'/ ' Inboard blanket' ,t32,1pe10.3,/ ' Outboard blanket' ,t32,1pe10.3,/ ' Total blanket' ,t32,1pe10.3,t62,1pe10.3/ ' Void fraction' ,t45,1pe10.3,/ ' Blanket Be ',t45,1pe10.3,t62,1pe10.3/ ' Blanket breeder',t45,1pe10.3,t62,1pe10.3/ ' Blanket steel',t45,1pe10.3,t62,1pe10.3/ ' Inboard shield' ,t32,1pe10.3,/ ' Outboard shield' ,t32,1pe10.3,/ ' Primary shield',t32,1pe10.3,t62,1pe10.3/ ' Void fraction' ,t45,1pe10.3,/ ' Penetration shield' ,t62,1pe10.3) + + po.osubhd(self.outfile, "Other volumes, masses and areas :") + po.ovarre( + self.outfile, "First wall area (m2)", "(fwarea)", build_variables.fwarea + ) + po.ovarre( + self.outfile, "First wall mass (kg)", "(fwmass)", fwbs_variables.fwmass + ) + po.ovarre( + self.outfile, + "External cryostat inner radius (m)", + "", + fwbs_variables.rdewex - 2.0e0 * adewex, + ) + po.ovarre( + self.outfile, + "External cryostat outer radius (m)", + "(rdewex)", + fwbs_variables.rdewex, + ) + po.ovarre( + self.outfile, "External cryostat minor radius (m)", "(adewex)", adewex + ) + po.ovarre( + self.outfile, + "External cryostat shell volume (m3)", + "(vdewex)", + fwbs_variables.vdewex, + ) + po.ovarre( + self.outfile, + "External cryostat mass (kg)", + "", + fwbs_variables.dewmkg - fwbs_variables.vvmass, + ) + po.ovarre( + self.outfile, + "Internal vacuum vessel shell volume (m3)", + "(vdewin)", + fwbs_variables.vdewin, + ) + po.ovarre( + self.outfile, + "Vacuum vessel mass (kg)", + "(vvmass)", + fwbs_variables.vvmass, + ) + po.ovarre( + self.outfile, + "Total cryostat + vacuum vessel mass (kg)", + "(dewmkg)", + fwbs_variables.dewmkg, + ) + po.ovarre( + self.outfile, + "Divertor area (m2)", + "(divsur)", + divertor_variables.divsur, + ) + po.ovarre( + self.outfile, + "Divertor mass (kg)", + "(divmas)", + divertor_variables.divmas, + ) + + def stcoil(self, output: bool): + """Routine that performs the calculations for stellarator coils + author: J Lion, IPP Greifswald + outfile : input integer : output file unit + iprint : input integer : switch for writing to output file (1=yes) + This routine calculates the properties of the coils for + a stellarator device. +

Some precalculated effective parameters for a stellarator power + plant design are used as the basis for the calculations. The coils + are assumed to be a fixed shape, but are scaled in size + appropriately for the machine being modelled. + """ + r_coil_major = stellarator_configuration.stella_config_coil_rmajor * st.f_r + r_coil_minor = stellarator_configuration.stella_config_coil_rminor * st.f_r + + ######################################################################################## + # Winding Pack Geometry: for one conductor + # + # This one conductor will just be multiplied later to fit the winding pack size. + # + # [m] Dimension of square cable space inside insulation + # and case of the conduit of each turn + t_cable = tfcoil_variables.t_turn_tf - 2.0e0 * ( + tfcoil_variables.thwcndut + tfcoil_variables.thicndut + ) # t_cable = t_w + if t_cable < 0: + print( + "t_cable is negative. Check t_turn, tfcoil_variables.thwcndut and thicndut." + ) + # [m^2] Cross-sectional area of cable space per turn + tfcoil_variables.acstf = ( + 0.9e0 * t_cable**2 + ) # 0.9 to include some rounded corners. (tfcoil_variables.acstf = pi (t_cable/2)**2 = pi/4 *t_cable**2 for perfect round conductor). This factor depends on how round the corners are. + # [m^2] Cross-sectional area of conduit case per turn + tfcoil_variables.acndttf = ( + t_cable + 2.0e0 * tfcoil_variables.thwcndut + ) ** 2 - tfcoil_variables.acstf + ####################################################################################### + + ####################################################################################### + # Winding Pack total size: + # + # Total coil current (MA) + coilcurrent = ( + st.f_b * stellarator_configuration.stella_config_i0 * st.f_r / st.f_n + ) + st.f_i = coilcurrent / stellarator_configuration.stella_config_i0 + + n_it = 200 # number of iterations + + rhs = np.zeros((n_it,)) + lhs = np.zeros((n_it,)) + jcrit_vector = np.zeros((n_it,)) + wp_width_r = np.zeros((n_it,)) + b_max_k = np.zeros((n_it,)) + + for k in range(n_it): + # Sample coil winding pack + wp_width_r[k] = (r_coil_minor / 40.0e0) + (k / (n_it - 1e0)) * ( + r_coil_minor / 1.0e0 - r_coil_minor / 40.0e0 + ) + if tfcoil_variables.i_tf_sc_mat == 6: + wp_width_r[k] = (r_coil_minor / 150.0e0) + (k / (n_it - 1e0)) * ( + r_coil_minor / 1.0e0 - r_coil_minor / 150.0e0 + ) + + # B-field calculation + b_max_k[k] = self.bmax_from_awp( + wp_width_r[k], + coilcurrent, + tfcoil_variables.n_tf, + r_coil_major, + r_coil_minor, + ) + + # jcrit for this bmax: + jcrit_vector[k] = self.jcrit_frommaterial( + b_max_k[k], + tfcoil_variables.tftmp + 1.5, + tfcoil_variables.i_tf_sc_mat, + tfcoil_variables.b_crit_upper_nbti, + tfcoil_variables.bcritsc, + tfcoil_variables.fcutfsu, + tfcoil_variables.fhts, + tfcoil_variables.t_crit_nbti, + tfcoil_variables.tcritsc, + tfcoil_variables.vftf, + ) # Get here a temperature margin of 1.5K. + + # The operation current density weighted with the global iop/icrit fraction + lhs[:] = constraint_variables.fiooic * jcrit_vector + + # Conduct fraction of conduit * Superconductor fraction in conductor + f_scu = ( + (tfcoil_variables.acstf * (1.0e0 - tfcoil_variables.vftf)) + / (tfcoil_variables.t_turn_tf**2) + * (1.0e0 - tfcoil_variables.fcutfsu) + ) # fraction that is SC of wp. + # print *, "f_scu. ",f_scu,"Awp min: ",Awp(1) + + rhs[:] = coilcurrent / ( + wp_width_r**2 / stellarator_configuration.stella_config_wp_ratio * f_scu + ) # f_scu should be the fraction of the sc that is in the winding pack. + + wp_width_r_min = ( + r_coil_minor / 10.0e0 + ) ** 2 # Initial guess for intersection routine + if tfcoil_variables.i_tf_sc_mat == 6: + wp_width_r_min = ( + r_coil_minor / 20.0e0 + ) ** 2 # If REBCO, : start at smaller winding pack ratios + + # Find the intersection between LHS and RHS (or: how much awp do I need to get to the desired coil current) + wp_width_r_min = self.intersect( + wp_width_r, lhs, wp_width_r, rhs, wp_width_r_min + ) + + # Maximum field at superconductor surface (T) + wp_width_r_min = max(tfcoil_variables.t_turn_tf**2, wp_width_r_min) + + # Recalculate tfcoil_variables.bmaxtf at the found awp_min: + tfcoil_variables.bmaxtf = self.bmax_from_awp( + wp_width_r_min, + coilcurrent, + tfcoil_variables.n_tf, + r_coil_major, + r_coil_minor, + ) + + # Winding pack toroidal, radial cross-sections (m) + awp_tor = ( + wp_width_r_min / stellarator_configuration.stella_config_wp_ratio + ) # Toroidal dimension + awp_rad = wp_width_r_min # Radial dimension + + tfcoil_variables.wwp1 = awp_tor # [m] toroidal thickness of winding pack + tfcoil_variables.wwp2 = ( + awp_tor # [m] toroidal thickness of winding pack (region in front) + ) + tfcoil_variables.dr_tf_wp = awp_rad # [m] radial thickness of winding pack + + # [m^2] winding-pack cross sectional area including insulation (not global) + awpc = (tfcoil_variables.dr_tf_wp + 2.0e0 * tfcoil_variables.tinstf) * ( + tfcoil_variables.wwp1 + 2.0e0 * tfcoil_variables.tinstf + ) + + awptf = awp_tor * awp_rad # [m^2] winding-pack cross sectional area + tfcoil_variables.jwptf = ( + coilcurrent * 1.0e6 / awptf + ) # [A/m^2] winding pack current density + tfcoil_variables.n_tf_turn = awptf / ( + tfcoil_variables.t_turn_tf**2 + ) # estimated number of turns for a given turn size (not global). Take at least 1. + tfcoil_variables.cpttf = ( + coilcurrent * 1.0e6 / tfcoil_variables.n_tf_turn + ) # [A] current per turn - estimation + # [m^2] Total conductor cross-sectional area, taking account of void area + tfcoil_variables.acond = ( + tfcoil_variables.acstf + * tfcoil_variables.n_tf_turn + * (1.0e0 - tfcoil_variables.vftf) + ) + # [m^2] Void area in cable, for He + tfcoil_variables.avwp = ( + tfcoil_variables.acstf * tfcoil_variables.n_tf_turn * tfcoil_variables.vftf + ) + # [m^2] Insulation area (not including ground-wall) + tfcoil_variables.aiwp = tfcoil_variables.n_tf_turn * ( + tfcoil_variables.t_turn_tf**2 + - tfcoil_variables.acndttf + - tfcoil_variables.acstf + ) + # [m^2] Structure area for cable + tfcoil_variables.aswp = tfcoil_variables.n_tf_turn * tfcoil_variables.acndttf + # End of winding pack calculations + ####################################################################################### + + ####################################################################################### + # Casing calculations + # + # Coil case thickness (m). Here assumed to be constant + # until something better comes up. + # case_thickness_constant = tfcoil_variables.thkcas #0.2e0 # #? Leave this constant for now... Check this## Should be scaled with forces I think. + # For now assumed to be constant in a bolted plate model. + # + tfcoil_variables.casthi = ( + tfcoil_variables.thkcas + ) # [m] coil case thickness outboard distance (radial) + # thkcas = case_thickness_constant/2.0e0 # [m] coil case thickness inboard distance (radial). + tfcoil_variables.casths = ( + tfcoil_variables.thkcas + ) # [m] coil case thickness toroidal distance (toroidal) + + # End of casing calculations + ####################################################################################### + + ####################################################################################### + # Port calculations + # + # Maximal toroidal port size (vertical ports) (m) + # The maximal distance is correct but the vertical extension of this port is not clear# + # This is simplified for now and can be made more accurate in the future# + stellarator_variables.vporttmax = ( + 0.4e0 + * stellarator_configuration.stella_config_max_portsize_width + * st.f_r + / st.f_n + ) # This is not accurate yet. Needs more insight# + + # Maximal poloidal port size (vertical ports) (m) + stellarator_variables.vportpmax = ( + 2.0 * stellarator_variables.vporttmax + ) # Simple approximation + + # Maximal vertical port clearance area (m2) + stellarator_variables.vportamax = ( + stellarator_variables.vporttmax * stellarator_variables.vportpmax + ) + + # Horizontal ports + # Maximal toroidal port size (horizontal ports) (m) + stellarator_variables.hporttmax = ( + 0.8e0 + * stellarator_configuration.stella_config_max_portsize_width + * st.f_r + / st.f_n + ) # Factor 0.8 to take the variation with height into account + + # Maximal poloidal port size (horizontal ports) (m) + stellarator_variables.hportpmax = ( + 2.0e0 * stellarator_variables.hporttmax + ) # Simple approximation + + # Maximal horizontal port clearance area (m2) + stellarator_variables.hportamax = ( + stellarator_variables.hporttmax * stellarator_variables.hportpmax + ) + # End of port calculations + ####################################################################################### + + ####################################################################################### + # General Coil Geometry values + # + tfcoil_variables.tftort = ( + tfcoil_variables.wwp1 + + 2.0e0 * tfcoil_variables.casths + + 2.0e0 * tfcoil_variables.tinstf + ) # [m] Thickness of inboard leg in toroidal direction + + build_variables.tfcth = ( + tfcoil_variables.thkcas + + tfcoil_variables.dr_tf_wp + + tfcoil_variables.casthi + + 2.0e0 * tfcoil_variables.tinstf + ) # [m] Thickness of inboard leg in radial direction + build_variables.tfthko = ( + tfcoil_variables.thkcas + + tfcoil_variables.dr_tf_wp + + tfcoil_variables.casthi + + 2.0e0 * tfcoil_variables.tinstf + ) # [m] Thickness of outboard leg in radial direction (same as inboard) + tfcoil_variables.arealeg = ( + build_variables.tfcth * tfcoil_variables.tftort + ) # [m^2] overall coil cross-sectional area (assuming inboard and + # outboard leg are the same) + tfcoil_variables.acasetf = ( + build_variables.tfcth * tfcoil_variables.tftort + ) - awpc # [m^2] Cross-sectional area of surrounding case + + tfcoil_variables.tfocrn = ( + 0.5e0 * tfcoil_variables.tftort + ) # [m] Half-width of side of coil nearest torus centreline + tfcoil_variables.tficrn = ( + 0.5e0 * tfcoil_variables.tftort + ) # [m] Half-width of side of coil nearest plasma + + # [m^2] Total surface area of coil side facing plasma: inboard region + tfcoil_variables.tfsai = ( + tfcoil_variables.n_tf + * tfcoil_variables.tftort + * 0.5e0 + * tfcoil_variables.tfleng + ) + # [m^2] Total surface area of coil side facing plasma: outboard region + tfcoil_variables.tfsao = ( + tfcoil_variables.tfsai + ) # depends, how 'inboard' and 'outboard' are defined + + # [m] Minimal distance in toroidal direction between two stellarator coils (from mid to mid) + # Consistency with coil width is checked in constraint equation 82 + tfcoil_variables.toroidalgap = ( + stellarator_configuration.stella_config_dmin + * (r_coil_major - r_coil_minor) + / ( + stellarator_configuration.stella_config_coil_rmajor + - stellarator_configuration.stella_config_coil_rminor + ) + ) + # Left-Over coil gap between two coils (m) + coilcoilgap = tfcoil_variables.toroidalgap - tfcoil_variables.tftort + + # Variables for ALL coils. + tfcoil_variables.tfareain = ( + tfcoil_variables.n_tf * tfcoil_variables.arealeg + ) # [m^2] Total area of all coil legs (midplane) + tfcoil_variables.ritfc = ( + tfcoil_variables.n_tf * coilcurrent * 1.0e6 + ) # [A] Total current in ALL coils + tfcoil_variables.oacdcp = ( + tfcoil_variables.ritfc / tfcoil_variables.tfareain + ) # [A / m^2] overall current density + tfcoil_variables.rbmax = ( + r_coil_major - r_coil_minor + awp_rad + ) # [m] radius of peak field occurrence, average + # jlion: not sure what this will be used for. Not very + # useful for stellarators + + # This uses the reference value for the inductance and scales it with a^2/R (toroid inductance scaling) + inductance = ( + stellarator_configuration.stella_config_inductance + / st.f_r + * (r_coil_minor / stellarator_configuration.stella_config_coil_rminor) ** 2 + * st.f_n**2 + ) + tfcoil_variables.estotftgj = ( + 0.5e0 + * ( + stellarator_configuration.stella_config_inductance + / st.f_r + * (r_coil_minor / stellarator_configuration.stella_config_coil_rminor) + ** 2 + * st.f_n**2 + ) + * (tfcoil_variables.ritfc / tfcoil_variables.n_tf) ** 2 + * 1.0e-9 + ) # [GJ] Total magnetic energy + + # Coil dimensions + build_variables.hmax = ( + 0.5e0 + * stellarator_configuration.stella_config_maximal_coil_height + * (r_coil_minor / stellarator_configuration.stella_config_coil_rminor) + ) # [m] maximum half-height of coil + r_tf_inleg_mid = ( + r_coil_major - r_coil_minor + ) # This is not very well defined for a stellarator. + # Though, this is taken as an average value. + tf_total_h_width = ( + r_coil_minor # ? not really sure what this is supposed to be. Estimated as + ) + # the average minor coil radius + + tfborev = 2.0e0 * build_variables.hmax # [m] estimated vertical coil bore + + tfcoil_variables.tfleng = ( + stellarator_configuration.stella_config_coillength + * (r_coil_minor / stellarator_configuration.stella_config_coil_rminor) + / tfcoil_variables.n_tf + ) # [m] estimated average length of a coil + + # [m^2] Total surface area of toroidal shells covering coils + tfcoil_variables.tfcryoarea = ( + stellarator_configuration.stella_config_coilsurface + * (r_coil_minor / stellarator_configuration.stella_config_coil_rminor) ** 2 + * 1.1e0 + ) # 1.1 to scale it out a bit. Should be coupled to winding pack maybe. + + # Minimal bending radius: + min_bending_radius = ( + stellarator_configuration.stella_config_min_bend_radius + * st.f_r + * 1.0 + / (1.0 - tfcoil_variables.dr_tf_wp / (2.0 * r_coil_minor)) + ) + + # End of general coil geometry values + ####################################################################################### + + ####################################################################################### + # Masses of conductor constituents + # + # [kg] Mass of case + # (no need for correction factors as is the case for tokamaks) + # This is only correct if the winding pack is 'thin' (tfleng>>sqrt(tfcoil_variables.acasetf)). + tfcoil_variables.whtcas = ( + tfcoil_variables.tfleng * tfcoil_variables.acasetf * tfcoil_variables.dcase + ) + # Mass of ground-wall insulation [kg] + # (assumed to be same density/material as conduit insulation) + tfcoil_variables.whtgw = ( + tfcoil_variables.tfleng * (awpc - awptf) * tfcoil_variables.dcondins + ) + # [kg] mass of Superconductor + tfcoil_variables.whtconsc = ( + tfcoil_variables.tfleng + * tfcoil_variables.n_tf_turn + * tfcoil_variables.acstf + * (1.0e0 - tfcoil_variables.vftf) + * (1.0e0 - tfcoil_variables.fcutfsu) + - tfcoil_variables.tfleng * tfcoil_variables.awphec + ) * tfcoil_variables.dcond[ + tfcoil_variables.i_tf_sc_mat - 1 + ] # awphec is 0 for a stellarator. but keep this term for now. + # [kg] mass of Copper in conductor + tfcoil_variables.whtconcu = ( + tfcoil_variables.tfleng + * tfcoil_variables.n_tf_turn + * tfcoil_variables.acstf + * (1.0e0 - tfcoil_variables.vftf) + * tfcoil_variables.fcutfsu + - tfcoil_variables.tfleng * tfcoil_variables.awphec + ) * constants.dcopper + # [kg] mass of Steel conduit (sheath) + tfcoil_variables.whtconsh = ( + tfcoil_variables.tfleng + * tfcoil_variables.n_tf_turn + * tfcoil_variables.acndttf + * fwbs_variables.denstl + ) + # if (i_tf_sc_mat==6) tfcoil_variables.whtconsh = fcondsteel * awptf *tfcoil_variables.tfleng* fwbs_variables.denstl + # Conduit insulation mass [kg] + # (tfcoil_variables.aiwp already contains tfcoil_variables.n_tf_turn) + tfcoil_variables.whtconin = ( + tfcoil_variables.tfleng * tfcoil_variables.aiwp * tfcoil_variables.dcondins + ) + # [kg] Total conductor mass + tfcoil_variables.whtcon = ( + tfcoil_variables.whtconsc + + tfcoil_variables.whtconcu + + tfcoil_variables.whtconsh + + tfcoil_variables.whtconin + ) + # [kg] Total coil mass + tfcoil_variables.whttf = ( + tfcoil_variables.whtcas + tfcoil_variables.whtcon + tfcoil_variables.whtgw + ) * tfcoil_variables.n_tf + # End of general coil geometry values + ####################################################################################### + + ####################################################################################### + # Quench protection: + # + # This copied from the tokamak module: + # Radial position of vacuum vessel [m] + radvv = ( + physics_variables.rmajor + - physics_variables.rminor + - build_variables.scrapli + - build_variables.fwith + - build_variables.blnkith + - build_variables.vvblgap + - build_variables.shldith + ) + + # Actual VV force density + # Based on reference values from W-7X: + # Bref = 3; + # Iref = 1.3*50; + # aref = 0.92; + # \[Tau]ref = 1.; + # Rref = 5.2; + # dref = 14*10^-3; + + # NOTE: original implementation used taucq which used a EUROfusion + # constant in the calculation. This was the minimum allowed quench time. + # Replacing with the actual quench time. + f_vv_actual = ( + 2.54e6 + * (3e0 * 1.3e0 * 50e0 * 0.92e0**2e0) + / (1e0 * 5.2e0 * 0.014e0) + * ( + physics_variables.bt + * tfcoil_variables.ritfc + * physics_variables.rminor**2 + / ( + (build_variables.d_vv_in + build_variables.d_vv_out) + / 2 + * tfcoil_variables.tdmptf + * radvv + ) + ) + ** (-1) + ) + + # the conductor fraction is meant of the cable space# + # This is the old routine which is being replaced for now by the new one below + # protect(aio, tfes, acs, aturn, tdump, fcond, fcu, tba, tmax ,ajwpro, vd) + # call protect(cpttf,estotftgj/tfcoil_variables.n_tf*1.0e9,acstf, tfcoil_variables.t_turn_tf**2 ,tdmptf,1-vftf,fcutfsu,tftmp,tmaxpro,jwdgpro2,vd) + + vd = self.u_max_protect_v( + tfcoil_variables.estotftgj / tfcoil_variables.n_tf * 1.0e9, + tfcoil_variables.tdmptf, + tfcoil_variables.cpttf, + ) + + # comparison + # the new quench protection routine, see #1047 + tfcoil_variables.jwdgpro = self.j_max_protect_am2( + tfcoil_variables.tdmptf, + 0.0e0, + tfcoil_variables.fcutfsu, + 1 - tfcoil_variables.vftf, + tfcoil_variables.tftmp, + tfcoil_variables.acstf, + tfcoil_variables.t_turn_tf**2, + ) + + # print *, "Jmax, comparison: ", jwdgpro, " ", jwdgpro2," ",jwptf/jwdgpro, " , tfcoil_variables.tdmptf: ",tdmptf, " tfcoil_variables.fcutfsu: ",fcutfsu + # print *, "acstf: ", tfcoil_variables.acstf + # Also give the copper area for REBCO quench calculations: + rebco_variables.coppera_m2 = ( + coilcurrent * 1.0e6 / (tfcoil_variables.acond * tfcoil_variables.fcutfsu) + ) + tfcoil_variables.vtfskv = vd / 1.0e3 # Dump voltage + # + ####################################################################################### + + # Forces scaling # + tfcoil_variables.max_force_density = ( + stellarator_configuration.stella_config_max_force_density + * st.f_i + / st.f_n + * tfcoil_variables.bmaxtf + / stellarator_configuration.stella_config_wp_bmax + * stellarator_configuration.stella_config_wp_area + / awptf + ) + + # Approximate, very simple maxiumum stress: (needed for limitation of icc 32) + tfcoil_variables.sig_tf_wp = ( + tfcoil_variables.max_force_density * tfcoil_variables.dr_tf_wp * 1.0e6 + ) # in Pa + + # Units: MN/m + max_force_density_mnm = ( + stellarator_configuration.stella_config_max_force_density_mnm + * st.f_i + / st.f_n + * tfcoil_variables.bmaxtf + / stellarator_configuration.stella_config_wp_bmax + ) + # + max_lateral_force_density = ( + stellarator_configuration.stella_config_max_lateral_force_density + * st.f_i + / st.f_n + * tfcoil_variables.bmaxtf + / stellarator_configuration.stella_config_wp_bmax + * stellarator_configuration.stella_config_wp_area + / awptf + ) + max_radial_force_density = ( + stellarator_configuration.stella_config_max_radial_force_density + * st.f_i + / st.f_n + * tfcoil_variables.bmaxtf + / stellarator_configuration.stella_config_wp_bmax + * stellarator_configuration.stella_config_wp_area + / awptf + ) + # + # F = f*V = B*j*V \propto B/B0 * I/I0 * A0/A * A/A0 * len/len0 + centering_force_max_mn = ( + stellarator_configuration.stella_config_centering_force_max_mn + * st.f_i + / st.f_n + * tfcoil_variables.bmaxtf + / stellarator_configuration.stella_config_wp_bmax + * stellarator_configuration.stella_config_coillength + / tfcoil_variables.n_tf + / tfcoil_variables.tfleng + ) + centering_force_min_mn = ( + stellarator_configuration.stella_config_centering_force_min_mn + * st.f_i + / st.f_n + * tfcoil_variables.bmaxtf + / stellarator_configuration.stella_config_wp_bmax + * stellarator_configuration.stella_config_coillength + / tfcoil_variables.n_tf + / tfcoil_variables.tfleng + ) + centering_force_avg_mn = ( + stellarator_configuration.stella_config_centering_force_avg_mn + * st.f_i + / st.f_n + * tfcoil_variables.bmaxtf + / stellarator_configuration.stella_config_wp_bmax + * stellarator_configuration.stella_config_coillength + / tfcoil_variables.n_tf + / tfcoil_variables.tfleng + ) + # + #################################### + + if output: + self.stcoil_output( + awptf, + centering_force_avg_mn, + centering_force_max_mn, + centering_force_min_mn, + coilcoilgap, + rebco_variables.coppera_m2, + rebco_variables.coppera_m2_max, + f_scu, + f_vv_actual, + constraint_variables.fiooic, + inductance, + tfcoil_variables.max_force_density, + max_force_density_mnm, + max_lateral_force_density, + max_radial_force_density, + min_bending_radius, + r_coil_major, + r_coil_minor, + r_tf_inleg_mid, + tfcoil_variables.sig_tf_wp, + tfcoil_variables.t_turn_tf, + tfcoil_variables.tdmptf, + tf_total_h_width, + tfborev, + tfcoil_variables.toroidalgap, + tfcoil_variables.vdalw, + tfcoil_variables.vtfskv, + ) + + def u_max_protect_v(self, tfes, tdump, aio): + """tfes : input real : Energy stored in one TF coil (J) + tdump : input real : Dump time (sec) + aio : input real : Operating current (A) + """ + return 2 * tfes / (tdump * aio) + + def j_max_protect_am2(self, tau_quench, t_detect, fcu, fcond, temp, acs, aturn): + temp_k = [4, 14, 24, 34, 44, 54, 64, 74, 84, 94, 104, 114, 124] + q_cu_array_sA2m4 = [ + 1.08514e17, + 1.12043e17, + 1.12406e17, + 1.05940e17, + 9.49741e16, + 8.43757e16, + 7.56346e16, + 6.85924e16, + 6.28575e16, + 5.81004e16, + 5.40838e16, + 5.06414e16, + 4.76531e16, + ] + q_he_array_sA2m4 = [ + 3.44562e16, + 9.92398e15, + 4.90462e15, + 2.41524e15, + 1.26368e15, + 7.51617e14, + 5.01632e14, + 3.63641e14, + 2.79164e14, + 2.23193e14, + 1.83832e14, + 1.54863e14, + 1.32773e14, + ] + + q_he = maths_library.find_y_nonuniform_x(temp, temp_k, q_he_array_sA2m4, 13) + q_cu = maths_library.find_y_nonuniform_x(temp, temp_k, q_cu_array_sA2m4, 13) + + # This leaves out the contribution from the superconductor fraction for now + return (acs / aturn) * np.sqrt( + 1 + / (0.5 * tau_quench + t_detect) + * (fcu**2 * fcond**2 * q_cu + fcu * fcond * (1 - fcond) * q_he) + ) + + def jcrit_frommaterial( + self, + bmax, + thelium, + i_tf_sc_mat, + b_crit_upper_nbti, + bcritsc, + fcutfsu, + fhts, + t_crit_nbti, + tcritsc, + vftf, + ): + strain = -0.005 # for now a small value + # fhe = vftf # this is helium fraction in the superconductor (set it to the fixed global variable here) + + fcu = fcutfsu # fcutfsu is a global variable. Is the copper fraction + # of a cable conductor. + + if i_tf_sc_mat == 1: # ITER Nb3Sn critical surface parameterization + bc20m = 32.97 # these are values taken from sctfcoil.f90 + tc0m = 16.06 + + # jcritsc returned by itersc is the critical current density in the + # superconductor - not the whole strand, which contains copper + if bmax > bc20m: + jcritsc = 1.0e-9 # Set to a small nonzero value + else: + ( + jcritsc, + bcrit, + tcrit, + ) = superconductors.itersc(thelium, bmax, strain, bc20m, tc0m) + + jcritstr = jcritsc * (1.0 - fcu) + + # This is needed right now. Can we change it later? + jcritsc = max(1.0e-9, jcritsc) + jcritstr = max(1.0e-9, jcritstr) + + elif ( + i_tf_sc_mat == 2 + ): # Bi-2212 high temperature superconductor parameterization + # Current density in a strand of Bi-2212 conductor + # N.B. jcrit returned by bi2212 is the critical current density + # in the strand, not just the superconducting portion. + # The parameterization for jcritstr assumes a particular strand + # composition that does not require a user-defined copper fraction, + # so this is irrelevant in this model + + # jstrand = jwp / (1 - fhe) + jstrand = 0 # as far as I can tell this will always be 0 + # because jwp was never set in fortran (so 0) + + jcritstr, tmarg = superconductors.bi2212( + bmax, jstrand, thelium, fhts + ) # bi2212 outputs jcritstr + jcritsc = jcritstr / (1 - fcu) + tcrit = thelium + tmarg + elif i_tf_sc_mat == 3: # NbTi data + bc20m = 15.0 + tc0m = 9.3 + c0 = 1.0 + + if bmax > bc20m: + jcritsc = 1.0e-9 # Set to a small nonzero value + else: + jcritsc, tcrit = superconductors.jcrit_nbti( + thelium, + bmax, + c0, + bc20m, + tc0m, + ) + # I dont need tcrit here so dont use it. + + jcritstr = jcritsc * (1 - fcu) + + # This is needed right now. Can we change it later? + jcritsc = max(1.0e-9, jcritsc) + jcritstr = max(1.0e-9, jcritstr) + elif i_tf_sc_mat == 4: # As (1), but user-defined parameters + bc20m = bcritsc + tc0m = tcritsc + jcritsc, bcrit, tcrit = superconductors.itersc( + thelium, bmax, strain, bc20m, tc0m + ) + jcritstr = jcritsc * (1 - fcu) + elif i_tf_sc_mat == 5: # WST Nb3Sn parameterisation + bc20m = 32.97 + tc0m = 16.06 + + # jcritsc returned by itersc is the critical current density in the + # superconductor - not the whole strand, which contains copper + + jcritsc, bcrit, tcrit = superconductors.wstsc( + thelium, + bmax, + strain, + bc20m, + tc0m, + ) + jcritstr = jcritsc * (1 - fcu) + elif ( + i_tf_sc_mat == 6 + ): # ! "REBCO" 2nd generation HTS superconductor in CrCo strand + jcritsc, validity = superconductors.jcrit_rebco(thelium, bmax, 0) + jcritsc = max(1.0e-9, jcritsc) + jcritstr = jcritsc * (1 - fcu) + + elif i_tf_sc_mat == 7: # Durham Ginzburg-Landau Nb-Ti parameterisation + bc20m = b_crit_upper_nbti + tc0m = t_crit_nbti + jcritsc, bcrit, tcrit = superconductors.gl_nbti( + thelium, bmax, strain, bc20m, tc0m + ) + jcritstr = jcritsc * (1 - fcu) + elif i_tf_sc_mat == 8: + bc20m = 429 + tc0m = 185 + jcritsc, bcrit, tcrit = superconductors.gl_rebco( + thelium, bmax, strain, bc20m, tc0m + ) + # A0 calculated for tape cross section already + jcritstr = jcritsc * (1 - fcu) + else: + error_handling.idiags[0] = i_tf_sc_mat + error_handling.report_error(156) + + return jcritsc * 1e-6 + + def bmax_from_awp(self, wp_width_radial, current, n_tf, r_coil_major, r_coil_minor): + """Returns a fitted function for bmax for stellarators + + author: J Lion, IPP Greifswald + Returns a fitted function for bmax in dependece + of the winding pack. The stellarator type config + is taken from the parent scope. + """ + + return ( + 2e-1 + * current + * n_tf + / (r_coil_major - r_coil_minor) + * ( + stellarator_configuration.stella_config_a1 + + stellarator_configuration.stella_config_a2 + * r_coil_major + / wp_width_radial + ) + ) + + def intersect(self, x1, y1, x2, y2, xin): + """Routine to find the x (abscissa) intersection point of two curves + each defined by tabulated (x,y) values + author: P J Knight, CCFE, Culham Science Centre + x1(1:n1) : input real array : x values for first curve + y1(1:n1) : input real array : y values for first curve + n1 : input integer : length of arrays x1, y1 + x2(1:n2) : input real array : x values for first curve + y2(1:n2) : input real array : y values for first curve + n2 : input integer : length of arrays x2, y2 + x : input/output real : initial x value guess on entry; + x value at point of intersection on exit + This routine estimates the x point (abscissa) at which two curves + defined by tabulated (x,y) values intersect, using simple + linear interpolation and the Newton-Raphson method. + The routine will stop with an error message if no crossing point + is found within the x ranges of the two curves. + None + """ + x = xin + n1 = len(x1) + n2 = len(x2) + + xmin = max(np.amin(x1), np.amin(x2)) + xmax = min(np.max(x1), np.amax(x2)) + + if xmin >= xmax: + error_handling.fdiags[0] = np.amin(x1) + error_handling.fdiags[1] = np.amin(x2) + error_handling.fdiags[2] = np.amax(x1) + error_handling.fdiags[3] = np.amax(x2) + error_handling.report_error(111) + + # Ensure input guess for x is within this range + + if x < xmin: + x = xmin + elif x > xmax: + x = xmax + + # Find overall y range, and set tolerance + # in final difference in y values + + ymin = min(np.amin(y1), np.amin(y2)) + ymax = max(np.max(y1), np.max(y2)) + + epsy = 1.0e-6 * (ymax - ymin) + + # Finite difference dx + + dx = 0.01e0 / max(n1, n2) * (xmax - xmin) + + for i in range(100): + # Find difference in y values at x + + y01 = maths_library.find_y_nonuniform_x(x, x1, y1, n1) + y02 = maths_library.find_y_nonuniform_x(x, x2, y2, n2) + y = y01 - y02 + + if abs(y) < epsy: + break + + # Find difference in y values at x+dx + + y01 = maths_library.find_y_nonuniform_x(x + dx, x1, y1, n1) + y02 = maths_library.find_y_nonuniform_x(x + dx, x2, y2, n2) + yright = y01 - y02 + + # Find difference in y values at x-dx + + y01 = maths_library.find_y_nonuniform_x(x - dx, x1, y1, n1) + y02 = maths_library.find_y_nonuniform_x(x - dx, x2, y2, n2) + yleft = y01 - y02 + + # Adjust x using Newton-Raphson method + + x = x - 2.0e0 * dx * y / (yright - yleft) + + if x < xmin: + error_handling.fdiags[0] = x + error_handling.fdiags[1] = xmin + error_handling.report_error(112) + x = xmin + break + + if x > xmax: + error_handling.fdiags[0] = x + error_handling.fdiags[1] = xmax + error_handling.report_error(113) + x = xmax + break + else: + error_handling.report_error(114) + + return x + + def stopt_output( + self, max_gyrotron_frequency, bt, bt_ecrh, ne0_max_ECRH, te0_ecrh_achievable + ): + po.oheadr(self.outfile, "ECRH Ignition at lower values. Information:") + + po.ovarre( + self.outfile, + "Maximal available gyrotron freq (input)", + "(max_gyro_frequency)", + max_gyrotron_frequency, + ) + + po.ovarre(self.outfile, "Operating point: bfield", "(bt)", bt) + po.ovarre( + self.outfile, + "Operating point: Peak density", + "(ne0)", + physics_variables.ne0, + ) + po.ovarre( + self.outfile, + "Operating point: Peak temperature", + "(te0)", + physics_variables.te0, + ) + + po.ovarre(self.outfile, "Ignition point: bfield (T)", "(bt_ecrh)", bt_ecrh) + po.ovarre( + self.outfile, + "Ignition point: density (/m3)", + "(ne0_max_ECRH)", + ne0_max_ECRH, + ) + po.ovarre( + self.outfile, + "Maximum reachable ECRH temperature (pseudo) (keV)", + "(te0_ecrh_achievable)", + te0_ecrh_achievable, + ) + + powerht_local, pscalingmw_local = self.power_at_ignition_point( + max_gyrotron_frequency, te0_ecrh_achievable + ) + po.ovarre( + self.outfile, + "Ignition point: Heating Power (MW)", + "(powerht_ecrh)", + powerht_local, + ) + po.ovarre( + self.outfile, + "Ignition point: Loss Power (MW)", + "(pscalingmw_ecrh)", + pscalingmw_local, + ) + + if powerht_local >= pscalingmw_local: + po.ovarin(self.outfile, "Operation point ECRH ignitable?", "(ecrh_bool)", 1) + else: + po.ovarin(self.outfile, "Operation point ECRH ignitable?", "(ecrh_bool)", 0) + + def power_at_ignition_point(self, gyro_frequency_max, te0_available): + """Routine to calculate if the plasma is ignitable with the current values for the B field. Assumes + current ECRH achievable peak temperature (which is inaccurate as the cordey pass should be calculated) + author: J Lion, IPP Greifswald + gyro_frequency_max : input real : Maximal available Gyrotron frequency (1/s) NOT (rad/s) + te0_available : input real : Reachable peak electron temperature, reached by ECRH (keV) + powerht_out : output real: Heating Power at ignition point (MW) + pscalingmw_out : output real: Heating Power loss at ignition point (MW) + This routine calculates the density limit due to an ECRH heating scheme on axis + Assumes current peak temperature (which is inaccurate as the cordey pass should be calculated) + Maybe use this: https://doi.org/10.1088/0029-5515/49/8/085026 + """ + + te_old = copy(physics_variables.te) + # Volume averaged physics_variables.te from te0_achievable + physics_variables.te = te0_available / (1.0e0 + physics_variables.alphat) + ne0_max, bt_ecrh_max = self.stdlim_ecrh( + gyro_frequency_max, physics_variables.bt + ) + # Now go to point where ECRH is still available + # In density.. + dene_old = copy(physics_variables.dene) + physics_variables.dene = min( + dene_old, ne0_max / (1.0e0 + physics_variables.alphan) + ) + + # And B-field.. + bt_old = copy(physics_variables.bt) + physics_variables.bt = min(bt_ecrh_max, physics_variables.bt) + + self.stphys(False) + self.stphys( + False + ) # The second call seems to be necessary for all values to "converge" (and is sufficient) + + powerht_out = max( + copy(physics_variables.powerht), 0.00001e0 + ) # the radiation module sometimes returns negative heating power + pscalingmw_out = copy(physics_variables.pscalingmw) + + # Reverse it and do it again because anything more efficiently isn't suitable with the current implementation + # This is bad practice but seems to be necessary as of now: + physics_variables.te = te_old + physics_variables.dene = dene_old + physics_variables.bt = bt_old + + self.stphys(False) + self.stphys(False) + + return powerht_out, pscalingmw_out + + def stdlim(self, bt, powht, rmajor, rminor): + """Routine to calculate the Sudo density limit in a stellarator + author: P J Knight, CCFE, Culham Science Centre + bt : input real : Toroidal field on axis (T) + powht : input real : Absorbed heating power (MW) + rmajor : input real : Plasma major radius (m) + rminor : input real : Plasma minor radius (m) + dlimit : output real : Maximum volume-averaged plasma density (/m3) + This routine calculates the density limit for a stellarator. + S.Sudo, Y.Takeiri, H.Zushi et al., Scalings of Energy Confinement + and Density Limit in Stellarator/Heliotron Devices, Nuclear Fusion + vol.30, 11 (1990). + AEA FUS 251: A User's Guide to the PROCESS Systems Code + """ + arg = powht * bt / (rmajor * rminor * rminor) + + if arg <= 0.0e0: + error_handling.fdiags[0] = arg + error_handling.fdiags[1] = powht + error_handling.fdiags[2] = bt + error_handling.fdiags[3] = rmajor + error_handling.fdiags[4] = rminor + error_handling.report_error(108) + + # Maximum line-averaged electron density + + dnlamx = 0.25e20 * np.sqrt(arg) + + # Scale the result so that it applies to the volume-averaged + # electron density + + dlimit = dnlamx * physics_variables.dene / physics_variables.dnla + + # Set the required value for icc=5 + + physics_variables.dnelimt = dlimit + + return dlimit + + def stcoil_output( + self, + awptf, + centering_force_avg_mn, + centering_force_max_mn, + centering_force_min_mn, + coilcoilgap, + coppera_m2, + coppera_m2_max, + f_scu, + f_vv_actual, + fiooic, + inductance, + max_force_density, + max_force_density_mnm, + max_lateral_force_density, + max_radial_force_density, + min_bending_radius, + r_coil_major, + r_coil_minor, + r_tf_inleg_mid, + sig_tf_wp, + t_turn_tf, + tdmptf, + tf_total_h_width, + tfborev, + toroidalgap, + vdalw, + vtfskv, + ): + """Writes stellarator modular coil output to file + author: P J Knight, CCFE, Culham Science Centre + outfile : input integer : output file unit + This routine writes the stellarator modular coil results + to the output file. + None + """ + po.oheadr(self.outfile, "Modular Coils") + + po.osubhd(self.outfile, "General Coil Parameters :") + + po.ovarre( + self.outfile, "Number of modular coils", "(n_tf)", tfcoil_variables.n_tf + ) + po.ovarre(self.outfile, "Av. coil major radius", "(coil_r)", r_coil_major) + po.ovarre(self.outfile, "Av. coil minor radius", "(coil_a)", r_coil_minor) + po.ovarre( + self.outfile, + "Av. coil aspect ratio", + "(coil_aspect)", + r_coil_major / r_coil_minor, + ) + + po.ovarre( + self.outfile, + "Cross-sectional area per coil (m2)", + "(tfarea/n_tf)", + tfcoil_variables.tfareain / tfcoil_variables.n_tf, + ) + po.ovarre( + self.outfile, + "Total inboard leg radial thickness (m)", + "(tfcth)", + build_variables.tfcth, + ) + po.ovarre( + self.outfile, + "Total outboard leg radial thickness (m)", + "(tfthko)", + build_variables.tfthko, + ) + po.ovarre( + self.outfile, + "Inboard leg outboard half-width (m)", + "(tficrn)", + tfcoil_variables.tficrn, + ) + po.ovarre( + self.outfile, + "Inboard leg inboard half-width (m)", + "(tfocrn)", + tfcoil_variables.tfocrn, + ) + po.ovarre( + self.outfile, + "Outboard leg toroidal thickness (m)", + "(tftort)", + tfcoil_variables.tftort, + ) + po.ovarre( + self.outfile, "Minimum coil distance (m)", "(toroidalgap)", toroidalgap + ) + po.ovarre( + self.outfile, + "Minimal left gap between coils (m)", + "(coilcoilgap)", + coilcoilgap, + ) + po.ovarre( + self.outfile, + "Minimum coil bending radius (m)", + "(min_bend_radius)", + min_bending_radius, + ) + po.ovarre( + self.outfile, + "Mean coil circumference (m)", + "(tfleng)", + tfcoil_variables.tfleng, + ) + po.ovarre( + self.outfile, + "Total current (MA)", + "(ritfc)", + 1.0e-6 * tfcoil_variables.ritfc, + ) + po.ovarre( + self.outfile, + "Current per coil(MA)", + "(ritfc/n_tf)", + 1.0e-6 * tfcoil_variables.ritfc / tfcoil_variables.n_tf, + ) + po.ovarre( + self.outfile, + "Winding pack current density (A/m2)", + "(jwptf)", + tfcoil_variables.jwptf, + ) + po.ovarre( + self.outfile, + "Max allowable current density as restricted by quench (A/m2)", + "(jwdgpro)", + tfcoil_variables.jwdgpro, + ) + po.ovarre( + self.outfile, + "Overall current density (A/m2)", + "(oacdcp)", + tfcoil_variables.oacdcp, + ) + po.ovarre( + self.outfile, + "Maximum field on superconductor (T)", + "(bmaxtf)", + tfcoil_variables.bmaxtf, + ) + po.ovarre( + self.outfile, + "Total Stored energy (GJ)", + "(estotftgj)", + tfcoil_variables.estotftgj, + ) + po.ovarre( + self.outfile, "Inductance of TF Coils (H)", "(inductance)", inductance + ) + po.ovarre( + self.outfile, "Total mass of coils (kg)", "(whttf)", tfcoil_variables.whttf + ) + + po.osubhd(self.outfile, "Coil Geometry :") + po.ovarre( + self.outfile, + "Inboard leg centre radius (m)", + "(r_tf_inleg_mid)", + r_tf_inleg_mid, + ) + po.ovarre( + self.outfile, + "Outboard leg centre radius (m)", + "(r_tf_outboard_mid)", + build_variables.r_tf_outboard_mid, + ) + po.ovarre( + self.outfile, + "Maximum inboard edge height (m)", + "(hmax)", + build_variables.hmax, + ) + po.ovarre( + self.outfile, + "Clear horizontal bore (m)", + "(tf_total_h_width)", + tf_total_h_width, + ) + po.ovarre(self.outfile, "Clear vertical bore (m)", "(tfborev)", tfborev) + + po.osubhd(self.outfile, "Conductor Information :") + po.ovarre( + self.outfile, + "Superconductor mass per coil (kg)", + "(whtconsc)", + tfcoil_variables.whtconsc, + ) + po.ovarre( + self.outfile, + "Copper mass per coil (kg)", + "(whtconcu)", + tfcoil_variables.whtconcu, + ) + po.ovarre( + self.outfile, + "Steel conduit mass per coil (kg)", + "(whtconsh)", + tfcoil_variables.whtconsh, + ) + po.ovarre( + self.outfile, + "Total conductor cable mass per coil (kg)", + "(whtcon)", + tfcoil_variables.whtcon, + ) + po.ovarre( + self.outfile, + "Cable conductor + void area (m2)", + "(acstf)", + tfcoil_variables.acstf, + ) + po.ovarre( + self.outfile, + "Cable space coolant fraction", + "(vftf)", + tfcoil_variables.vftf, + ) + po.ovarre( + self.outfile, + "Conduit case thickness (m)", + "(thwcndut)", + tfcoil_variables.thwcndut, + ) + po.ovarre( + self.outfile, + "Cable insulation thickness (m)", + "(thicndut)", + tfcoil_variables.thicndut, + ) + + ap = awptf + po.osubhd(self.outfile, "Winding Pack Information :") + po.ovarre(self.outfile, "Winding pack area", "(ap)", ap) + po.ovarre( + self.outfile, + "Conductor fraction of winding pack", + "(acond/ap)", + tfcoil_variables.acond / ap, + ) + po.ovarre( + self.outfile, + "Copper fraction of conductor", + "(fcutfsu)", + tfcoil_variables.fcutfsu, + ) + po.ovarre( + self.outfile, + "Structure fraction of winding pack", + "(aswp/ap)", + tfcoil_variables.aswp / ap, + ) + po.ovarre( + self.outfile, + "Insulator fraction of winding pack", + "(aiwp/ap)", + tfcoil_variables.aiwp / ap, + ) + po.ovarre( + self.outfile, + "Helium fraction of winding pack", + "(avwp/ap)", + tfcoil_variables.avwp / ap, + ) + po.ovarre( + self.outfile, + "Winding radial thickness (m)", + "(dr_tf_wp)", + tfcoil_variables.dr_tf_wp, + ) + po.ovarre( + self.outfile, + "Winding toroidal thickness (m)", + "(wwp1)", + tfcoil_variables.wwp1, + ) + po.ovarre( + self.outfile, + "Ground wall insulation thickness (m)", + "(tinstf)", + tfcoil_variables.tinstf, + ) + po.ovarre( + self.outfile, + "Number of turns per coil", + "(n_tf_turn)", + tfcoil_variables.n_tf_turn, + ) + po.ovarre( + self.outfile, + "Width of each turn (incl. insulation) (m)", + "(t_turn_tf)", + t_turn_tf, + ) + po.ovarre( + self.outfile, "Current per turn (A)", "(cpttf)", tfcoil_variables.cpttf + ) + po.ovarre(self.outfile, "jop/jcrit", "(fiooic)", fiooic) + po.ovarre( + self.outfile, + "Current density in conductor area (A/m2)", + "(ritfc/acond)", + 1.0e-6 + * tfcoil_variables.ritfc + / tfcoil_variables.n_tf + / tfcoil_variables.acond, + ) + po.ovarre( + self.outfile, + "Current density in SC area (A/m2)", + "(ritfc/acond/f_scu)", + 1.0e-6 * tfcoil_variables.ritfc / tfcoil_variables.n_tf / ap / f_scu, + ) + po.ovarre(self.outfile, "Superconductor faction of WP (1)", "(f_scu)", f_scu) + + po.osubhd(self.outfile, "Forces and Stress :") + po.ovarre( + self.outfile, + "Maximal toroidally and radially av. force density (MN/m3)", + "(max_force_density)", + max_force_density, + ) + po.ovarre( + self.outfile, + "Maximal force density (MN/m)", + "(max_force_density_Mnm)", + max_force_density_mnm, + ) + po.ovarre( + self.outfile, + "Maximal stress (approx.) (MPa)", + "(sig_tf_wp)", + sig_tf_wp * 1.0e-6, + ) + + po.ovarre( + self.outfile, + "Maximal lateral force density (MN/m3)", + "(max_lateral_force_density)", + max_lateral_force_density, + ) + po.ovarre( + self.outfile, + "Maximal radial force density (MN/m3)", + "(max_radial_force_density)", + max_radial_force_density, + ) + + po.ovarre( + self.outfile, + "Max. centering force (coil) (MN)", + "(centering_force_max_MN)", + centering_force_max_mn, + ) + po.ovarre( + self.outfile, + "Min. centering force (coil) (MN)", + "(centering_force_min_MN)", + centering_force_min_mn, + ) + po.ovarre( + self.outfile, + "Avg. centering force per coil (MN)", + "(centering_force_avg_MN)", + centering_force_avg_mn, + ) + + po.osubhd(self.outfile, "Quench Restrictions :") + po.ovarre( + self.outfile, + "Actual quench time (or time constant) (s)", + "(tdmptf)", + tdmptf, + ) + po.ovarre( + self.outfile, + "Actual quench vaccuum vessel force density (MN/m^3)", + "(f_vv_actual)", + f_vv_actual, + ) + po.ovarre( + self.outfile, + "Maximum allowed voltage during quench due to insulation (kV)", + "(vdalw)", + vdalw, + ) + po.ovarre(self.outfile, "Actual quench voltage (kV)", "(vtfskv)", vtfskv, "OP ") + po.ovarre( + self.outfile, + "Current (A) per mm^2 copper (A/mm2)", + "(coppera_m2)", + coppera_m2 * 1.0e-6, + ) + po.ovarre( + self.outfile, + "Max Copper current fraction:", + "(coppera_m2/coppera_m2_max)", + coppera_m2 / coppera_m2_max, + ) + + po.osubhd(self.outfile, "External Case Information :") + + po.ovarre( + self.outfile, + "Case thickness, plasma side (m)", + "(casthi)", + tfcoil_variables.casthi, + ) + po.ovarre( + self.outfile, + "Case thickness, outer side (m)", + "(thkcas)", + tfcoil_variables.thkcas, + ) + po.ovarre( + self.outfile, + "Case toroidal thickness (m)", + "(casths)", + tfcoil_variables.casths, + ) + po.ovarre( + self.outfile, + "Case area per coil (m2)", + "(acasetf)", + tfcoil_variables.acasetf, + ) + po.ovarre( + self.outfile, + "External case mass per coil (kg)", + "(whtcas)", + tfcoil_variables.whtcas, + ) + + po.osubhd(self.outfile, "Available Space for Ports :") + + po.ovarre( + self.outfile, + "Max toroidal size of vertical ports (m)", + "(vporttmax)", + stellarator_variables.vporttmax, + ) + po.ovarre( + self.outfile, + "Max poloidal size of vertical ports (m)", + "(vportpmax)", + stellarator_variables.vportpmax, + ) + po.ovarre( + self.outfile, + "Max area of vertical ports (m2)", + "(vportamax)", + stellarator_variables.vportamax, + ) + po.ovarre( + self.outfile, + "Max toroidal size of horizontal ports (m)", + "(hporttmax)", + stellarator_variables.hporttmax, + ) + po.ovarre( + self.outfile, + "Max poloidal size of horizontal ports (m)", + "(hportpmax)", + stellarator_variables.hportpmax, + ) + po.ovarre( + self.outfile, + "Max area of horizontal ports (m2)", + "(hportamax)", + stellarator_variables.hportamax, + ) + + def stdlim_ecrh(self, gyro_frequency_max, bt_input): + """Routine to calculate the density limit due to an ECRH heating scheme on axis + depending on an assumed maximal available gyrotron frequency. + author: J Lion, IPP Greifswald + gyro_frequency_max : input real : Maximal available Gyrotron frequency (1/s) NOT (rad/s) + bt : input real : Maximal magnetic field on axis (T) + dlimit_ecrh : output real : Maximum peak plasma density by ECRH constraints (/m3) + bt_max : output real : Maximum allowable b field for ecrh heating (T) + This routine calculates the density limit due to an ECRH heating scheme on axis + """ + gyro_frequency = min(1.76e11 * bt_input, gyro_frequency_max * 2.0e0 * np.pi) + + # Restrict b field to the maximal available gyrotron frequency + bt_max = (gyro_frequency_max * 2.0e0 * np.pi) / 1.76e11 + + # me*e0/e^2 * w^2 + ne0_max = max(0.0e0, 3.142077e-4 * gyro_frequency**2) + + # Check if parabolic profiles are used: + if physics_variables.ipedestal == 0: + # Parabolic profiles used, use analytical formula: + dlimit_ecrh = ne0_max + else: + logger.warning( + "It was used physics_variables.ipedestal = 1 in a stellarator routine. PROCESS will pretend it got parabolic profiles (physics_variables.ipedestal = 0)." + ) + dlimit_ecrh = ne0_max + + return dlimit_ecrh, bt_max + + def stphys(self, output): + """Routine to calculate stellarator plasma physics information + author: P J Knight, CCFE, Culham Science Centre + author: F Warmer, IPP Greifswald + None + This routine calculates the physics quantities relevant to + a stellarator device. + AEA FUS 251: A User's Guide to the PROCESS Systems Code + AEA FUS 172: Physics Assessment for the European Reactor Study + """ + # ############################################### + # Calculate plasma composition + # Issue #261 Remove old radiation model + + physics_module.plasma_composition() + + # Calculate density and temperature profile quantities + profiles_module.plasma_profiles() + + # Total field + physics_variables.btot = np.sqrt( + physics_variables.bt**2 + physics_variables.bp**2 + ) + + if ( + 5 in numerics.ixc + ): # Check if physics_variables.beta (iteration variable 5) is an iteration variable + error_handling.report_error(251) + + # Set physics_variables.beta as a consequence: + # This replaces constraint equation 1 as it is just an equality. + physics_variables.beta = ( + physics_variables.betaft + + physics_variables.betanb + + 2.0e3 + * constants.rmu0 + * constants.echarge + * ( + physics_variables.dene * physics_variables.ten + + physics_variables.dnitot * physics_variables.tin + ) + / physics_variables.btot**2 + ) + physics_module.total_plasma_internal_energy = ( + 1.5e0 + * physics_variables.beta + * physics_variables.btot + * physics_variables.btot + / (2.0e0 * constants.rmu0) + * physics_variables.vol + ) + + physics_module.rho_star = np.sqrt( + 2.0e0 + * constants.mproton + * physics_variables.aion + * physics_module.total_plasma_internal_energy + / (3.0e0 * physics_variables.vol * physics_variables.dnla) + ) / ( + constants.echarge + * physics_variables.bt + * physics_variables.eps + * physics_variables.rmajor + ) + + physics_variables.q95 = physics_variables.q + + # Calculate poloidal field using rotation transform + physics_variables.bp = ( + physics_variables.rminor + * physics_variables.bt + / physics_variables.rmajor + * stellarator_variables.iotabar + ) + + # Poloidal physics_variables.beta + + # betap = physics_variables.beta * ( physics_variables.btot/physics_variables.bp )**2 # Dont need this I think. + + # Perform auxiliary power calculations + + self.stheat(False) + + # Calculate fusion power + + ( + physics_variables.palppv, + physics_variables.pchargepv, + physics_variables.pneutpv, + sigvdt, + physics_variables.fusionrate, + physics_variables.alpharate, + physics_variables.protonrate, + pdtpv, + pdhe3pv, + pddpv, + ) = physics_functions_module.palph( + physics_variables.alphan, + physics_variables.alphat, + physics_variables.deni, + physics_variables.fdeut, + physics_variables.fhe3, + physics_variables.ftrit, + physics_variables.ti, + ) + + physics_variables.pdt = pdtpv * physics_variables.vol + physics_variables.pdhe3 = pdhe3pv * physics_variables.vol + physics_variables.pdd = pddpv * physics_variables.vol + + # Calculate neutral beam slowing down effects + # If ignited, then ignore beam fusion effects + + if (current_drive_variables.pnbeam != 0.0e0) and ( + physics_variables.ignite == 0 + ): + ( + physics_variables.betanb, + physics_variables.dnbeam2, + physics_variables.palpnb, + ) = physics_functions_module.beamfus( + physics_variables.beamfus0, + physics_variables.betbm0, + physics_variables.bp, + physics_variables.bt, + current_drive_variables.cnbeam, + physics_variables.dene, + physics_variables.deni, + physics_variables.dlamie, + physics_variables.ealphadt, + current_drive_variables.enbeam, + physics_variables.fdeut, + physics_variables.ftrit, + current_drive_variables.ftritbm, + sigvdt, + physics_variables.ten, + physics_variables.tin, + physics_variables.vol, + physics_variables.zeffai, + ) + physics_variables.fusionrate = ( + physics_variables.fusionrate + + 1.0e6 + * physics_variables.palpnb + / (1.0e3 * physics_variables.ealphadt * constants.echarge) + / physics_variables.vol + ) + physics_variables.alpharate = ( + physics_variables.alpharate + + 1.0e6 + * physics_variables.palpnb + / (1.0e3 * physics_variables.ealphadt * constants.echarge) + / physics_variables.vol + ) + + physics_variables.pdt = physics_variables.pdt + 5.0e0 * physics_variables.palpnb + + ( + physics_variables.palpmw, + physics_variables.pneutmw, + physics_variables.pchargemw, + physics_variables.betaft, + physics_variables.palpipv, + physics_variables.palpepv, + physics_variables.pfuscmw, + physics_variables.powfmw, + ) = physics_functions_module.palph2( + physics_variables.bt, + physics_variables.bp, + physics_variables.dene, + physics_variables.deni, + physics_variables.dnitot, + physics_variables.falpe, + physics_variables.falpi, + physics_variables.palpnb, + physics_variables.ifalphap, + physics_variables.pchargepv, + physics_variables.pneutpv, + physics_variables.ten, + physics_variables.tin, + physics_variables.vol, + physics_variables.palppv, + ) + + # Neutron wall load + + if physics_variables.iwalld == 1: + physics_variables.wallmw = ( + physics_variables.ffwal + * physics_variables.pneutmw + / physics_variables.sarea + ) + else: + if heat_transport_variables.ipowerflow == 0: + physics_variables.wallmw = ( + (1.0e0 - fwbs_variables.fhole) + * physics_variables.pneutmw + / build_variables.fwarea + ) + else: + physics_variables.wallmw = ( + ( + 1.0e0 + - fwbs_variables.fhole + - fwbs_variables.fhcd + - fwbs_variables.fdiv + ) + * physics_variables.pneutmw + / build_variables.fwarea + ) + + # Calculate ion/electron equilibration power + + physics_variables.piepv = physics_module.rether( + physics_variables.alphan, + physics_variables.alphat, + physics_variables.dene, + physics_variables.dlamie, + physics_variables.te, + physics_variables.ti, + physics_variables.zeffai, + ) + + # Calculate radiation power + radpwr_data = physics_funcs.radpwr(self.plasma_profile) + physics_variables.pbrempv = radpwr_data.pbrempv + physics_variables.plinepv = radpwr_data.plinepv + physics_variables.psyncpv = radpwr_data.psyncpv + physics_variables.pcoreradpv = radpwr_data.pcoreradpv + physics_variables.pedgeradpv = radpwr_data.pedgeradpv + physics_variables.pradpv = radpwr_data.pradpv + + physics_variables.pcoreradpv = max(physics_variables.pcoreradpv, 0.0e0) + physics_variables.pedgeradpv = max(physics_variables.pedgeradpv, 0.0e0) + + physics_variables.pinnerzoneradmw = ( + physics_variables.pcoreradpv * physics_variables.vol + ) # Should probably be vol_core + physics_variables.pouterzoneradmw = ( + physics_variables.pedgeradpv * physics_variables.vol + ) + + physics_variables.pradmw = physics_variables.pradpv * physics_variables.vol + + # Heating power to plasma (= Psol in divertor model) + # Ohmic power is zero in a stellarator + # physics_variables.pradmw here is core + edge (no SOL) + + powht = ( + physics_variables.falpha * physics_variables.palpmw + + physics_variables.pchargemw + + physics_variables.pohmmw + - physics_variables.pradpv * physics_variables.vol + ) + powht = max( + 0.00001e0, powht + ) # To avoid negative heating power. This line is VERY important + + if physics_variables.ignite == 0: + powht = ( + powht + current_drive_variables.pinjmw + ) # if not ignited add the auxiliary power + + # Here the implementation sometimes leaves the accessible regime when pradmw> powht which is unphysical and + # is not taken care of by the rad module. We restrict the radiation power here by the heating power: + physics_variables.pradmw = max(0.0e0, physics_variables.pradmw) + + # Power to divertor, = (1-stellarator_variables.f_rad)*Psol + + # The SOL radiation needs to be smaller than the physics_variables.pradmw + physics_variables.psolradmw = stellarator_variables.f_rad * powht + physics_variables.pdivt = powht - physics_variables.psolradmw + + # Add SOL Radiation to total + physics_variables.pradmw = ( + physics_variables.pradmw + physics_variables.psolradmw + ) + # pradpv = physics_variables.pradmw / physics_variables.vol # this line OVERWRITES the original definition of pradpv, probably shouldn't be defined like that as the core does not lose SOL power. + + # The following line is unphysical, but prevents -ve sqrt argument + # Should be obsolete if constraint eqn 17 is turned on (but beware - + # this may not be quite correct for stellarators) + physics_variables.pdivt = max(0.001e0, physics_variables.pdivt) + + # Power transported to the first wall by escaped alpha particles + + physics_variables.palpfwmw = physics_variables.palpmw * ( + 1.0e0 - physics_variables.falpha + ) + + # Nominal mean photon wall load + if physics_variables.iwalld == 1: + physics_variables.photon_wall = ( + physics_variables.ffwal + * physics_variables.pradmw + / physics_variables.sarea + ) + else: + if heat_transport_variables.ipowerflow == 0: + physics_variables.photon_wall = ( + (1.0e0 - fwbs_variables.fhole) + * physics_variables.pradmw + / build_variables.fwarea + ) + else: + physics_variables.photon_wall = ( + ( + 1.0e0 + - fwbs_variables.fhole + - fwbs_variables.fhcd + - fwbs_variables.fdiv + ) + * physics_variables.pradmw + / build_variables.fwarea + ) + + constraint_variables.peakradwallload = ( + physics_variables.photon_wall * constraint_variables.peakfactrad + ) + + physics_variables.rad_fraction_total = physics_variables.pradmw / ( + physics_variables.falpha * physics_variables.palpmw + + physics_variables.pchargemw + + physics_variables.pohmmw + + current_drive_variables.pinjmw + ) + + # Calculate transport losses and energy confinement time using the + # chosen scaling law + # N.B. stellarator_variables.iotabar replaces tokamak physics_variables.q95 in argument list + + ( + physics_variables.kappaa, + physics_variables.ptrepv, + physics_variables.ptripv, + physics_variables.tauee, + physics_variables.tauei, + physics_variables.taueff, + physics_variables.powerht, + ) = physics_module.pcond( + physics_variables.afuel, + physics_variables.palpmw, + physics_variables.aspect, + physics_variables.bt, + physics_variables.dnitot, + physics_variables.dene, + physics_variables.dnla, + physics_variables.eps, + physics_variables.hfact, + physics_variables.iinvqd, + physics_variables.isc, + physics_variables.ignite, + physics_variables.kappa, + physics_variables.kappa95, + physics_variables.pchargemw, + current_drive_variables.pinjmw, + physics_variables.plascur, + physics_variables.pcoreradpv, + physics_variables.rmajor, + physics_variables.rminor, + physics_variables.te, + physics_variables.ten, + physics_variables.tin, + stellarator_variables.iotabar, + physics_variables.qstar, + physics_variables.vol, + physics_variables.xarea, + physics_variables.zeff, + ) + + physics_variables.ptremw = physics_variables.ptrepv * physics_variables.vol + physics_variables.ptrimw = physics_variables.ptripv * physics_variables.vol + + physics_variables.pscalingmw = ( + physics_variables.ptremw + physics_variables.ptrimw + ) + + # Calculate auxiliary physics related information + # for the rest of the code + + sbar = 1.0e0 + ( + physics_variables.burnup, + physics_variables.dntau, + physics_variables.figmer, + fusrat, + physics_variables.qfuel, + physics_variables.rndfuel, + physics_variables.taup, + ) = physics_module.phyaux( + physics_variables.aspect, + physics_variables.dene, + physics_variables.deni, + physics_variables.fusionrate, + physics_variables.alpharate, + physics_variables.plascur, + sbar, + physics_variables.dnalp, + physics_variables.taueff, + physics_variables.vol, + ) + + # Calculate physics_variables.beta limit. Does nothing atm so commented out + # call stblim(physics_variables.betalim) + + # Calculate the neoclassical sanity check with PROCESS parameters + ( + q_PROCESS, + q_PROCESS_r1, + q_neo, + gamma_neo, + total_q_neo, + total_q_neo_e, + q_neo_e, + q_neo_D, + q_neo_a, + q_neo_T, + g_neo_e, + g_neo_D, + g_neo_a, + g_neo_T, + dndt_neo_e, + dndt_neo_D, + dndt_neo_a, + dndt_neo_T, + dndt_neo_fuel, + dmdt_neo_fuel, + dmdt_neo_fuel_from_e, + chi_neo_e, + chi_PROCESS_e, + nu_star_e, + nu_star_d, + nu_star_T, + nu_star_He, + ) = self.calc_neoclassics() + + if output: + self.stphys_output( + q_PROCESS, + total_q_neo_e, + dmdt_neo_fuel_from_e, + q_PROCESS_r1, + chi_PROCESS_e, + chi_neo_e, + q_neo_e, + g_neo_e, + dndt_neo_e, + physics_variables.rho_ne_max, + physics_variables.rho_te_max, + physics_variables.gradient_length_ne, + physics_variables.gradient_length_te, + physics_module.rho_star, + nu_star_e, + nu_star_d, + nu_star_T, + nu_star_He, + physics_variables.dnla, + physics_variables.dnelimt, + ) + + def stphys_output( + self, + q_PROCESS, + total_q_neo_e, + dmdt_neo_fuel_from_e, + q_PROCESS_r1, + chi_PROCESS_e, + chi_neo_e, + q_neo_e, + g_neo_e, + dndt_neo_e, + rho_ne_max, + rho_te_max, + gradient_length_ne, + gradient_length_te, + rho_star, + nu_star_e, + nu_star_D, + nu_star_T, + nu_star_He, + dnla, + dnelimt, + ): + po.oheadr(self.outfile, "Stellarator Specific Physics:") + + po.ovarre( + self.outfile, + "Total 0D heat flux (r=rhocore) (MW/m2)", + "(q_PROCESS)", + q_PROCESS, + ) + po.ovarre( + self.outfile, + "Total neoclassical flux from 4*q_e (r=rhocore) (MW/m2)", + "(total_q_neo_e)", + total_q_neo_e, + ) + + po.ovarre( + self.outfile, + "Total fuel (DT) mass flux by using 4 * neoclassical e transport (mg/s): ", + "(dmdt_neo_fuel_from_e)", + dmdt_neo_fuel_from_e, + ) + po.ovarre( + self.outfile, + "Considered Heatflux by LCFS heat flux ratio (1)", + "(q_PROCESS/q_PROCESS_r1)", + q_PROCESS / q_PROCESS_r1, + ) + + po.ovarre( + self.outfile, + "Resulting electron effective chi (0D) (r=rhocore): ", + "(chi_PROCESS_e)", + chi_PROCESS_e, + ) + po.ovarre( + self.outfile, + "Neoclassical electron effective chi (r=rhocore): ", + "(chi_neo_e)", + chi_neo_e, + ) + + po.ovarre( + self.outfile, + "Heat flux due to neoclassical energy transport (e) (MW/m2): ", + "(q_neo_e)", + q_neo_e, + ) + po.ovarre( + self.outfile, + "Heat flux due to neoclassical particle transport (e) (MW/m2): ", + "(g_neo_e)", + g_neo_e, + ) + po.ovarre( + self.outfile, + "Particle flux due to neoclassical particle transport (e) (1/m2/s): ", + "(dndt_neo_e)", + dndt_neo_e, + ) + + po.ovarre( + self.outfile, "r/a of maximum ne gradient (m)", "(rho_ne_max)", rho_ne_max + ) + po.ovarre( + self.outfile, "r/a of maximum te gradient (m)", "(rho_te_max)", rho_te_max + ) + po.ovarre( + self.outfile, + "Maxium ne gradient length (1)", + "(gradient_length_ne)", + gradient_length_ne, + ) + po.ovarre( + self.outfile, + "Maxium te gradient length (1)", + "(gradient_length_te)", + gradient_length_te, + ) + po.ovarre( + self.outfile, + "Gradient Length Ratio (T/n) (1)", + "(gradient_length_ratio)", + gradient_length_te / gradient_length_ne, + ) + + po.ovarre(self.outfile, "Normalized ion Larmor radius", "(rho_star)", rho_star) + po.ovarre( + self.outfile, + "Normalized collisionality (electrons)", + "(nu_star_e)", + nu_star_e, + ) + po.ovarre( + self.outfile, "Normalized collisionality (D)", "(nu_star_D)", nu_star_D + ) + po.ovarre( + self.outfile, "Normalized collisionality (T)", "(nu_star_T)", nu_star_T + ) + po.ovarre( + self.outfile, "Normalized collisionality (He)", "(nu_star_He)", nu_star_He + ) + + po.ovarre( + self.outfile, + "Obtained line averaged density at op. point (/m3)", + "(dnla)", + dnla, + ) + po.ovarre(self.outfile, "Sudo density limit (/m3)", "(dnelimt)", dnelimt) + po.ovarre( + self.outfile, + "Ratio density to sudo limit (1)", + "(dnla/dnelimt)", + dnla / dnelimt, + ) + + def calc_neoclassics(self): + neoclassics_module.init_neoclassics( + 0.6, + stellarator_configuration.stella_config_epseff, + stellarator_variables.iotabar, + ) + + q_PROCESS = ( + ( + physics_variables.falpha * physics_variables.palppv + - physics_variables.pcoreradpv + ) + * physics_variables.vol + / physics_variables.sarea + * impurity_radiation_module.coreradius + ) + q_PROCESS_r1 = ( + ( + physics_variables.falpha * physics_variables.palppv + - physics_variables.pcoreradpv + ) + * physics_variables.vol + / physics_variables.sarea + ) + + q_neo = sum(neoclassics_module.q_flux * 1e-6) + gamma_neo = sum( + neoclassics_module.gamma_flux * neoclassics_module.temperatures * 1e-6 + ) + + total_q_neo = sum( + neoclassics_module.q_flux * 1e-6 + + neoclassics_module.gamma_flux * neoclassics_module.temperatures * 1e-6 + ) + + total_q_neo_e = ( + 2 + * 2 + * ( + neoclassics_module.q_flux[0] * 1e-6 + + neoclassics_module.gamma_flux[0] + * neoclassics_module.temperatures[0] + * 1e-6 + ) + ) + + q_neo_e = neoclassics_module.q_flux[0] * 1e-6 + q_neo_D = neoclassics_module.q_flux[1] * 1e-6 + q_neo_a = neoclassics_module.q_flux[3] * 1e-6 + q_neo_T = neoclassics_module.q_flux[2] * 1e-6 + + g_neo_e = ( + neoclassics_module.gamma_flux[0] * 1e-6 * neoclassics_module.temperatures[0] + ) + g_neo_D = ( + neoclassics_module.gamma_flux[1] * 1e-6 * neoclassics_module.temperatures[1] + ) + g_neo_a = ( + neoclassics_module.gamma_flux[3] * 1e-6 * neoclassics_module.temperatures[3] + ) + g_neo_T = ( + neoclassics_module.gamma_flux[2] * 1e-6 * neoclassics_module.temperatures[2] + ) + + dndt_neo_e = neoclassics_module.gamma_flux[0] + dndt_neo_D = neoclassics_module.gamma_flux[1] + dndt_neo_a = neoclassics_module.gamma_flux[3] + dndt_neo_T = neoclassics_module.gamma_flux[2] + + dndt_neo_fuel = ( + (dndt_neo_D + dndt_neo_T) + * physics_variables.sarea + * impurity_radiation_module.coreradius + ) + dmdt_neo_fuel = ( + dndt_neo_fuel * physics_variables.afuel * constants.mproton * 1.0e6 + ) # mg + dmdt_neo_fuel_from_e = ( + 4 + * dndt_neo_e + * physics_variables.sarea + * impurity_radiation_module.coreradius + * physics_variables.afuel + * constants.mproton + * 1.0e6 + ) # kg + + chi_neo_e = -( + neoclassics_module.q_flux[0] + + neoclassics_module.gamma_flux[0] * neoclassics_module.temperatures[0] + ) / ( + neoclassics_module.densities[0] * neoclassics_module.dr_temperatures[0] + + neoclassics_module.temperatures[0] * neoclassics_module.dr_densities[0] + ) + + chi_PROCESS_e = self.st_calc_eff_chi() + + nu_star_e = neoclassics_module.nu_star_averaged[0] + nu_star_d = neoclassics_module.nu_star_averaged[1] + nu_star_T = neoclassics_module.nu_star_averaged[2] + nu_star_He = neoclassics_module.nu_star_averaged[3] + + return ( + q_PROCESS, + q_PROCESS_r1, + q_neo, + gamma_neo, + total_q_neo, + total_q_neo_e, + q_neo_e, + q_neo_D, + q_neo_a, + q_neo_T, + g_neo_e, + g_neo_D, + g_neo_a, + g_neo_T, + dndt_neo_e, + dndt_neo_D, + dndt_neo_a, + dndt_neo_T, + dndt_neo_fuel, + dmdt_neo_fuel, + dmdt_neo_fuel_from_e, + chi_neo_e, + chi_PROCESS_e, + nu_star_e, + nu_star_d, + nu_star_T, + nu_star_He, + ) + + def st_calc_eff_chi(self): + volscaling = ( + physics_variables.vol + * st.f_r + * ( + impurity_radiation_module.coreradius + * physics_variables.rminor + / stellarator_configuration.stella_config_rminor_ref + ) + ** 2 + ) + surfacescaling = ( + physics_variables.sarea + * st.f_r + * ( + impurity_radiation_module.coreradius + * physics_variables.rminor + / stellarator_configuration.stella_config_rminor_ref + ) + ) + + nominator = ( + physics_variables.falpha * physics_variables.palppv + - physics_variables.pcoreradpv + ) * volscaling + + # in fortran there was a 0d0*alphan term which I have removed for obvious reasons + # the following comment seems to describe this? + # "include alphan if chi should be incorporate density gradients too" + # but the history can be consulted if required (23/11/22 TN) + denominator = ( + ( + 3 + * physics_variables.ne0 + * constants.echarge + * physics_variables.te0 + * 1e3 + * physics_variables.alphat + * impurity_radiation_module.coreradius + * (1 - impurity_radiation_module.coreradius**2) + ** (physics_variables.alphan + physics_variables.alphat - 1) + ) + * surfacescaling + * 1e-6 + ) + + return nominator / denominator + + def stheat(self, output: bool): + """Routine to calculate the auxiliary heating power + in a stellarator + author: P J Knight, CCFE, Culham Science Centre + outfile : input integer : output file unit + iprint : input integer : switch for writing to output file (1=yes) + This routine calculates the auxiliary heating power for + a stellarator device. + AEA FUS 251: A User's Guide to the PROCESS Systems Code + AEA FUS 172: Physics Assessment for the European Reactor Study + """ + if stellarator_variables.isthtr == 1: + current_drive_variables.echpwr = current_drive_variables.pheat + current_drive_variables.pinjimw = 0 + current_drive_variables.pinjemw = current_drive_variables.echpwr + current_drive_variables.etacd = current_drive_variables.etaech + current_drive_variables.pinjwp = ( + current_drive_variables.pinjimw + current_drive_variables.pinjemw + ) / current_drive_variables.etacd + elif stellarator_variables.isthtr == 2: + current_drive_variables.plhybd = current_drive_variables.pheat + current_drive_variables.pinjimw = 0 + current_drive_variables.pinjemw = current_drive_variables.plhybd + current_drive_variables.etacd = current_drive_variables.etalh + current_drive_variables.pinjwp = ( + current_drive_variables.pinjimw + current_drive_variables.pinjemw + ) / current_drive_variables.etacd + elif stellarator_variables.isthtr == 3: + ( + effnbss, + fpion, + current_drive_variables.nbshinef, + ) = current_drive_module.culnbi() + current_drive_variables.pnbeam = current_drive_variables.pheat * ( + 1 - current_drive_variables.forbitloss + ) + current_drive_variables.porbitlossmw = ( + current_drive_variables.pheat * current_drive_variables.forbitloss + ) + current_drive_variables.pinjimw = current_drive_variables.pnbeam * fpion + current_drive_variables.pinjemw = current_drive_variables.pnbeam * ( + 1 - fpion + ) + current_drive_variables.etacd = current_drive_variables.etanbi + current_drive_variables.pinjwp = ( + current_drive_variables.pinjimw + current_drive_variables.pinjemw + ) / current_drive_variables.etacd + else: + error_handling.idiags[0] = stellarator_variables.isthtr + error_handling.report_error(107) + + # Total injected power + + current_drive_variables.pinjmw = ( + current_drive_variables.pinjemw + current_drive_variables.pinjimw + ) + + # Calculate neutral beam current + + if abs(current_drive_variables.pnbeam) > 1e-8: + current_drive_variables.cnbeam = ( + 1e-3 + * (current_drive_variables.pnbeam * 1e6) + / current_drive_variables.enbeam + ) + else: + current_drive_variables.cnbeam = 0 + + # Ratio of fusion to input (injection+ohmic) power + + if ( + abs( + current_drive_variables.pinjmw + + current_drive_variables.porbitlossmw + + physics_variables.pohmmw + ) + < 1e-6 + ): + current_drive_variables.bigq = 1e18 + else: + current_drive_variables.bigq = physics_variables.powfmw / ( + current_drive_variables.pinjmw + + current_drive_variables.porbitlossmw + + physics_variables.pohmmw + ) + + if output: + po.oheadr(self.outfile, "Auxiliary Heating System") + + if stellarator_variables.isthtr == 1: + po.ocmmnt(self.outfile, "Electron Cyclotron Resonance Heating") + elif stellarator_variables.isthtr == 2: + po.ocmmnt(self.outfile, "Lower Hybrid Heating") + elif stellarator_variables.isthtr == 3: + po.ocmmnt(self.outfile, "Neutral Beam Injection Heating") + + if physics_variables.ignite == 1: + po.ocmmnt( + self.outfile, + "Ignited plasma; injected power only used for start-up phase", + ) + + po.oblnkl(self.outfile) + + po.ovarre( + self.outfile, + "Auxiliary power supplied to plasma (MW)", + "(pheat)", + current_drive_variables.pheat, + ) + po.ovarre( + self.outfile, + "Fusion gain factor Q", + "(bigq)", + current_drive_variables.bigq, + ) + + if abs(current_drive_variables.pnbeam) > 1e-8: + po.ovarre( + self.outfile, + "Neutral beam energy (keV)", + "(enbeam)", + current_drive_variables.enbeam, + ) + po.ovarre( + self.outfile, + "Neutral beam current (A)", + "(cnbeam)", + current_drive_variables.cnbeam, + ) + po.ovarre( + self.outfile, "Fraction of beam energy to ions", "(fpion)", fpion + ) + po.ovarre( + self.outfile, + "Neutral beam shine-through fraction", + "(nbshinef)", + current_drive_variables.nbshinef, + ) + po.ovarre( + self.outfile, + "Neutral beam orbit loss power (MW)", + "(porbitlossmw)", + current_drive_variables.porbitlossmw, + ) + po.ovarre( + self.outfile, + "Beam duct shielding thickness (m)", + "(nbshield)", + current_drive_variables.nbshield, + ) + po.ovarre( + self.outfile, + "R injection tangent / R-major", + "(frbeam)", + current_drive_variables.frbeam, + ) + po.ovarre( + self.outfile, + "Beam centreline tangency radius (m)", + "(rtanbeam)", + current_drive_variables.rtanbeam, + ) + po.ovarre( + self.outfile, + "Maximum possible tangency radius (m)", + "(rtanmax)", + current_drive_variables.rtanmax, + ) + po.ovarre( + self.outfile, + "Beam decay lengths to centre", + "(taubeam)", + current_drive_variables.taubeam, + ) diff --git a/requirements.txt b/requirements.txt index 39ab315a..81b00033 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ mkdocs-material>=4.6.3 mkdocs-git-revision-date-localized-plugin >= 1.2 mkdocs-glightbox >= 0.3.4 pymdown-extensions>=6.3 -numpy>=1.19.0,<=1.21.5 +numpy>=1.19.0,!=1.22,!=1.24,>=1.23.0 scipy>=0.19.1 matplotlib>=2.1.1 pillow>=5.1.0 @@ -21,3 +21,4 @@ mkdocstrings==0.18.0 PyVMCON>=2.1.0,<3.0.0 CoolProp>=6.4 Jinja2>=3.0 +cvxpy!=1.3.0,!=1.3.1 diff --git a/setup.py b/setup.py index 983ae4e1..c02f45d2 100644 --- a/setup.py +++ b/setup.py @@ -35,8 +35,9 @@ }, "test_suite": "pytest", "install_requires": [ - "numpy>=1.19.0,<1.22.1", + "numpy>=1.19.0,!=1.22,!=1.24,>=1.23.0", "scipy>=0.19.1", + "cvxpy!=1.3.0,!=1.3.1", "importlib-resources ; python_version<'3.9'", "pandas", "tables", diff --git a/source/fortran/constraint_equations.f90 b/source/fortran/constraint_equations.f90 index 9ce5534d..4cb14cb4 100755 --- a/source/fortran/constraint_equations.f90 +++ b/source/fortran/constraint_equations.f90 @@ -1276,6 +1276,7 @@ subroutine constraint_eqn_024(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) !! bt : input real : toroidal field !! btot : input real : total field use physics_variables, only: iculbl, betalim, beta, betanb, betaft, bt, btot + use stellarator_variables, only: istell use constraint_variables, only: fbetatry implicit none real(dp), intent(out) :: tmp_cc @@ -1285,7 +1286,7 @@ subroutine constraint_eqn_024(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) character(len=10), intent(out) :: tmp_units ! Include all beta components: relevant for both tokamaks and stellarators - if (iculbl == 0) then + if ((iculbl == 0).or.(istell /= 0)) then tmp_cc = 1.0D0 - fbetatry * betalim/beta tmp_con = betalim tmp_err = betalim - beta / fbetatry @@ -2271,47 +2272,16 @@ subroutine constraint_eqn_054(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) end subroutine constraint_eqn_054 subroutine constraint_eqn_055(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for helium concentration in vacuum vessel upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for helium concentration in vacuum vessel upper limit - !! #=# fwbs - !! #=#=# fvvhe, vvhemax - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fvvhe : input real : f-value for vacuum vessel He concentration limit - !! vvhealw : input real : allowed maximum helium concentration in vacuum vessel at end of plant life (appm) (iblanket =2) - !! vvhemax : ivvhemaxnput real : maximum helium concentration in vacuum vessel at end of plant life (appm) (iblanket=2 (KIT HCPB)) - !! iblanket : input integer : switch for blanket model:

- use constraint_variables, only: fvvhe, vvhealw - use fwbs_variables, only: vvhemax, iblanket + !! vvhemax is no longer calculated in PROCESS and this constraint is disabled implicit none - real(dp), intent(out) :: tmp_cc + + real(dp), intent(out) :: tmp_cc real(dp), intent(out) :: tmp_con real(dp), intent(out) :: tmp_err character(len=1), intent(out) :: tmp_symbol character(len=10), intent(out) :: tmp_units - if (iblanket == 2) then - tmp_cc = 1.0D0 - fvvhe * vvhealw/vvhemax - tmp_con = vvhealw * (1.0D0 - tmp_cc) - tmp_err = vvhemax * tmp_cc - tmp_symbol = '<' - tmp_units = 'appm' - else ! iblanket /= 2 - tmp_cc = 0 - tmp_con = 0 - tmp_err = 0 - tmp_symbol = '' - tmp_units = '' - call report_error(173) - end if - + call report_error(173) end subroutine constraint_eqn_055 subroutine constraint_eqn_056(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) @@ -3391,6 +3361,10 @@ subroutine constraint_eqn_091(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) !! fecrh_ignition : input real : f-value for constraint powerht_local > powerscaling !! max_gyrotron_frequency : input real : Max. av. gyrotron frequency !! te0_ecrh_achievable : input real : Max. achievable electron temperature at ignition point + use constraint_variables, only: fecrh_ignition + use stellarator_variables, only: max_gyrotron_frequency, te0_ecrh_achievable, powerscaling_constraint, powerht_constraint + use physics_variables, only: ignite + use current_drive_variables, only: pheat implicit none real(dp), intent(out) :: tmp_cc real(dp), intent(out) :: tmp_con @@ -3398,8 +3372,17 @@ subroutine constraint_eqn_091(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) character(len=1), intent(out) :: tmp_symbol character(len=10), intent(out) :: tmp_units - ! Constraint equation 91 is reserved for when Stellarator is added back to PROCESS + ! Achievable ECRH te needs to be larger than needed te for igntion + if(ignite==0) then + tmp_cc = 1.0D0 - fecrh_ignition* (powerht_constraint+pheat)/powerscaling_constraint + else + tmp_cc = 1.0D0 - fecrh_ignition* powerht_constraint/powerscaling_constraint + endif + tmp_con = powerscaling_constraint * (1.0D0 - tmp_cc) + tmp_err = powerht_constraint * tmp_cc + tmp_symbol = '<' + tmp_units = 'MW' end subroutine constraint_eqn_091 diff --git a/source/fortran/fwbs_variables.f90 b/source/fortran/fwbs_variables.f90 index 019029be..ef039bd2 100644 --- a/source/fortran/fwbs_variables.f90 +++ b/source/fortran/fwbs_variables.f90 @@ -247,10 +247,6 @@ module fwbs_variables real(dp) :: tritprate !! tritium production rate [g day^-1] (`iblanket=2` (KIT HCPB)) - real(dp) :: vvhemax - !! maximum helium concentration in vacuum vessel at end of plant life (appm) - !! (`iblanket=2` (KIT HCPB)) - real(dp) :: wallpf !! neutron wall load peaking factor (`iblanket=2` (KIT HCPB)) @@ -731,7 +727,6 @@ subroutine init_fwbs_variables nphcdout = 2 tbr = 0.0D0 tritprate = 0.0D0 - vvhemax = 0.0D0 wallpf = 1.21D0 whtblbreed = 0.0D0 whtblbe = 0.0D0 diff --git a/source/fortran/init_module.f90 b/source/fortran/init_module.f90 index e46977b6..bc5feb1e 100644 --- a/source/fortran/init_module.f90 +++ b/source/fortran/init_module.f90 @@ -32,6 +32,8 @@ subroutine init_all_module_vars use physics_variables, only: init_physics_variables use scan_module, only: init_scan_module use sctfcoil_module, only: init_sctfcoil_module + use stellarator_module, only: init_stellarator_module + use stellarator_variables, only: init_stellarator_variables use tfcoil_variables, only: init_tfcoil_variables use times_variables, only: init_times_variables use constants, only: init_constants @@ -73,6 +75,8 @@ subroutine init_all_module_vars call init_physics_variables call init_scan_module call init_sctfcoil_module + call init_stellarator_module + call init_stellarator_variables call init_tfcoil_variables call init_times_variables call init_constants @@ -118,6 +122,7 @@ subroutine init use error_handling, only: initialise_error_list use numerics, only: ixc , lablxc, nvar use process_input, only: nin, input + use stellarator_module, only: stinit implicit none ! Arguments @@ -151,6 +156,10 @@ subroutine init ! Input any desired new initial values call input + ! Initialise stellarator parameters if necessary + ! This overrides some of the bounds of the tokamak parameters + call stinit + ! Check input data for errors/ambiguities call check diff --git a/source/fortran/initial.f90 b/source/fortran/initial.f90 index 37587076..7ecf2bdc 100755 --- a/source/fortran/initial.f90 +++ b/source/fortran/initial.f90 @@ -273,6 +273,7 @@ subroutine check t_turn_tf, tftmp, t_cable_tf, t_cable_tf_is_input, tftmp, tmpcry, & i_tf_cond_eyoung_axial, eyoung_cond_axial, eyoung_cond_trans, & i_tf_cond_eyoung_trans, i_str_wp + use stellarator_variables, only: istell use sctfcoil_module, only: initialise_cables use vacuum_variables, only: vacuum_model use, intrinsic :: iso_fortran_env, only: dp=>real64 @@ -897,20 +898,20 @@ subroutine check ! Ensure that blanket material fractions allow non-zero space for steel ! CCFE HCPB Model - - if ((iblanket == 1).or.(iblanket == 3)) then - fsum = breeder_multiplier + vfcblkt + vfpblkt - if (fsum >= 1.0D0) then - idiags(1) = iblanket - fdiags(2) = breeder_multiplier - fdiags(3) = vfcblkt - fdiags(4) = vfpblkt - fdiags(5) = fsum - call report_error(165) + if (istell == 0) then + if ((iblanket == 1).or.(iblanket == 3)) then + fsum = breeder_multiplier + vfcblkt + vfpblkt + if (fsum >= 1.0D0) then + idiags(1) = iblanket + fdiags(2) = breeder_multiplier + fdiags(3) = vfcblkt + fdiags(4) = vfpblkt + fdiags(5) = fsum + call report_error(165) + end if end if end if - ! Initialise superconductor cable parameters if(i_tf_sup==1)then call initialise_cables() diff --git a/source/fortran/input.f90 b/source/fortran/input.f90 index 958e0a7e..05d8221a 100644 --- a/source/fortran/input.f90 +++ b/source/fortran/input.f90 @@ -154,10 +154,13 @@ subroutine devtyp !! Set icase description based on device type use global_variables, only: icase use ife_variables, only: ife + use stellarator_variables, only: istell implicit none if (ife == 1) then icase = 'Inertial Fusion model' + else if (istell /= 0) then + icase = 'Stellarator model' end if end subroutine devtyp @@ -317,6 +320,9 @@ subroutine parse_input_file(in_file,out_file,show_changes) use scan_module, only: isweep_2, nsweep, isweep, scan_dim, nsweep_2, & sweep_2, sweep, ipnscns, ipnscnv + use stellarator_variables, only: f_asym, isthtr, n_res, iotabar, fdivwet, & + f_w, bmn, shear, m_res, f_rad, flpitch, istell, max_gyrotron_frequency, & + te0_ecrh_achievable use tfcoil_variables, only: fcoolcp, tfinsgap, vftf, & quench_detection_ef, fhts, dr_tf_wp, rcool, rhotfleg, thkcas, & casthi, n_pancake, bcritsc, i_tf_sup, str_pf_con_res, thwcndut, farc4tf, & @@ -3412,117 +3418,47 @@ subroutine parse_input_file(in_file,out_file,show_changes) ! Stellarator settings case ('istell') - write(outfile,*) ' ' - write(outfile,*) '**********' - write(outfile,*) 'The Stellarator model is currently not included in PROCESS.' - write(outfile,*) 'See issue #1853 for more information on the use of Stellarators.' - write(outfile,*) '**********' - write(outfile,*) ' ' - obsolete_var = .true. + call parse_int_variable('istell', istell, 0, 6, & + 'Stellarator machine specification (1=Helias5, 2=Helias4, 3=Helias3, 4=W7X50, 5=W7X30, 6=jsoninput)') case ('bmn') - write(outfile,*) ' ' - write(outfile,*) '**********' - write(outfile,*) 'The Stellarator model is currently not included in PROCESS.' - write(outfile,*) 'See issue #1853 for more information on the use of Stellarators.' - write(outfile,*) '**********' - write(outfile,*) ' ' - obsolete_var = .true. + call parse_real_variable('bmn', bmn, 1.0D-4, 1.0D-2, & + 'Relative radial field perturbation') case ('max_gyrotron_frequency') - write(outfile,*) ' ' - write(outfile,*) '**********' - write(outfile,*) 'The Stellarator model is currently not included in PROCESS.' - write(outfile,*) 'See issue #1853 for more information on the use of Stellarators.' - write(outfile,*) '**********' - write(outfile,*) ' ' - obsolete_var = .true. + call parse_real_variable('max_gyrotron_frequency', max_gyrotron_frequency, 1.0d9, 1.0d14, & + 'Maximum avail. gyrotron frequency') case ('te0_ecrh_achievable') - write(outfile,*) ' ' - write(outfile,*) '**********' - write(outfile,*) 'The Stellarator model is currently not included in PROCESS.' - write(outfile,*) 'See issue #1853 for more information on the use of Stellarators.' - write(outfile,*) '**********' - write(outfile,*) ' ' - obsolete_var = .true. + call parse_real_variable('te0_ecrh_achievable', te0_ecrh_achievable, 1.0d0, 1.0d3, & + 'Maximum achievable ecrh temperature (peak value)') case ('f_asym') - write(outfile,*) ' ' - write(outfile,*) '**********' - write(outfile,*) 'The Stellarator model is currently not included in PROCESS.' - write(outfile,*) 'See issue #1853 for more information on the use of Stellarators.' - write(outfile,*) '**********' - write(outfile,*) ' ' - obsolete_var = .true. + call parse_real_variable('f_asym', f_asym, 0.9D0, 2.0D0, & + 'Heat load peaking factor') case ('f_rad') - write(outfile,*) ' ' - write(outfile,*) '**********' - write(outfile,*) 'The Stellarator model is currently not included in PROCESS.' - write(outfile,*) 'See issue #1853 for more information on the use of Stellarators.' - write(outfile,*) '**********' - write(outfile,*) ' ' - obsolete_var = .true. + call parse_real_variable('f_rad', f_rad, 0.0D0, 1.0D0, & + 'Radiated power fraction in SOL') case ('f_w') - write(outfile,*) ' ' - write(outfile,*) '**********' - write(outfile,*) 'The Stellarator model is currently not included in PROCESS.' - write(outfile,*) 'See issue #1853 for more information on the use of Stellarators.' - write(outfile,*) '**********' - write(outfile,*) ' ' - obsolete_var = .true. + call parse_real_variable('f_w', f_w, 0.1D0, 1.0D0, & + 'Island size fraction factor') case ('fdivwet') - write(outfile,*) ' ' - write(outfile,*) '**********' - write(outfile,*) 'The Stellarator model is currently not included in PROCESS.' - write(outfile,*) 'See issue #1853 for more information on the use of Stellarators.' - write(outfile,*) '**********' - write(outfile,*) ' ' - obsolete_var = .true. + call parse_real_variable('fdivwet', fdivwet, 0.01D0, 1.0D0, & + 'Wetted area fraction of divertor plates') case ('flpitch') - write(outfile,*) ' ' - write(outfile,*) '**********' - write(outfile,*) 'The Stellarator model is currently not included in PROCESS.' - write(outfile,*) 'See issue #1853 for more information on the use of Stellarators.' - write(outfile,*) '**********' - write(outfile,*) ' ' - obsolete_var = .true. + call parse_real_variable('flpitch', flpitch, 1.0D-4, 1.0D-2, & + 'Field line pitch (rad)') case ('iotabar') - write(outfile,*) ' ' - write(outfile,*) '**********' - write(outfile,*) 'The Stellarator model is currently not included in PROCESS.' - write(outfile,*) 'See issue #1853 for more information on the use of Stellarators.' - write(outfile,*) '**********' - write(outfile,*) ' ' - obsolete_var = .true. + call parse_real_variable('iotabar', iotabar, 0.1D0, 10.0D0, & + 'Stellarator rotational transform (at s=2/3)') case ('isthtr') - write(outfile,*) ' ' - write(outfile,*) '**********' - write(outfile,*) 'The Stellarator model is currently not included in PROCESS.' - write(outfile,*) 'See issue #1853 for more information on the use of Stellarators.' - write(outfile,*) '**********' - write(outfile,*) ' ' - obsolete_var = .true. + call parse_int_variable('isthtr', isthtr, 1, 3, & + 'Stellarator method of auxiliary heating') case ('m_res') - write(outfile,*) ' ' - write(outfile,*) '**********' - write(outfile,*) 'The Stellarator model is currently not included in PROCESS.' - write(outfile,*) 'See issue #1853 for more information on the use of Stellarators.' - write(outfile,*) '**********' - write(outfile,*) ' ' - obsolete_var = .true. + call parse_int_variable('m_res', m_res, 1, 10, & + 'Poloidal resonance number') case ('n_res') - write(outfile,*) ' ' - write(outfile,*) '**********' - write(outfile,*) 'The Stellarator model is currently not included in PROCESS.' - write(outfile,*) 'See issue #1853 for more information on the use of Stellarators.' - write(outfile,*) '**********' - write(outfile,*) ' ' - obsolete_var = .true. + call parse_int_variable('n_res', n_res, 3, 6, & + 'Toroidal resonance number') case ('shear') - write(outfile,*) ' ' - write(outfile,*) '**********' - write(outfile,*) 'The Stellarator model is currently not included in PROCESS.' - write(outfile,*) 'See issue #1853 for more information on the use of Stellarators.' - write(outfile,*) '**********' - write(outfile,*) ' ' - obsolete_var = .true. + call parse_real_variable('shear', shear, 0.1D0, 10.0D0, & + 'Magnetic shear') ! Inertial Fusion Energy plant settings diff --git a/source/fortran/iteration_variables.f90 b/source/fortran/iteration_variables.f90 index f089ab4c..1d280046 100755 --- a/source/fortran/iteration_variables.f90 +++ b/source/fortran/iteration_variables.f90 @@ -323,8 +323,12 @@ end subroutine init_itv_13 real(kind(1.d0)) function itv_13() use build_variables, only: tfcth use error_handling, only: report_error + use stellarator_variables, only: istell implicit none itv_13 = tfcth + if (istell /= 0) then + call report_error(46) + end if end function itv_13 subroutine set_itv_13(ratio) @@ -1315,9 +1319,13 @@ end subroutine init_itv_57 real(kind(1.d0)) function itv_57() use error_handling, only: report_error + use stellarator_variables, only: istell use tfcoil_variables, only: thkcas implicit none itv_57 = thkcas + if (istell /= 0) then + call report_error(48) + end if end function itv_57 subroutine set_itv_57(ratio) @@ -1388,9 +1396,13 @@ end subroutine init_itv_60 real(kind(1.d0)) function itv_60() use error_handling, only: report_error + use stellarator_variables, only: istell use tfcoil_variables, only: i_tf_sup, cpttf implicit none itv_60 = cpttf + if ((istell /= 0).or.(i_tf_sup /= 1)) then + call report_error(49) + end if end function itv_60 subroutine set_itv_60(ratio) @@ -3798,14 +3810,16 @@ subroutine init_itv_169 end subroutine init_itv_169 real(kind(1.d0)) function itv_169() - ! Iteration variable 169 reserved for when Stellarator is added back to PROCESS + use stellarator_variables, only: te0_ecrh_achievable + implicit none + itv_169 = te0_ecrh_achievable end function itv_169 subroutine set_itv_169(ratio) + use stellarator_variables, only: te0_ecrh_achievable implicit none real(kind(1.d0)) :: ratio - - ! Iteration variable 169 reserved for when Stellarator is added back to PROCESS + te0_ecrh_achievable = ratio end subroutine set_itv_169 subroutine init_itv_170 diff --git a/source/fortran/physics.f90 b/source/fortran/physics.f90 index 9e074c0a..ecafe9d1 100644 --- a/source/fortran/physics.f90 +++ b/source/fortran/physics.f90 @@ -3221,7 +3221,8 @@ subroutine outplas(outfile) use numerics, only: active_constraints, boundu, icc, & boundl, ioptimz use reinke_variables, only: fzactual, impvardiv, fzmin - use constants, only: rmu0, mproton, mfile, echarge, pi, epsilon0 + use constants, only: rmu0, mproton, mfile, echarge, pi, epsilon0 + use stellarator_variables, only: iotabar, istell implicit none ! Arguments @@ -3249,8 +3250,9 @@ subroutine outplas(outfile) beta_mcdonald = 4.d0/3.d0 *rmu0 * total_plasma_internal_energy / (vol * bt**2) - call oheadr(outfile,'Plasma') + call oheadr(outfile,'Plasma') + if (istell == 0) then select case (idivrt) case (0) call ocmmnt(outfile,'Plasma configuration = limiter') @@ -3261,15 +3263,18 @@ subroutine outplas(outfile) case default idiags(1) = idivrt ; call report_error(85) end select + else + call ocmmnt(outfile,'Plasma configuration = stellarator') + end if - - - if (itart == 0) then - itart_r = itart - call ovarrf(outfile,'Tokamak aspect ratio = Conventional, itart = 0','(itart)',itart_r) - else if (itart == 1) then - itart_r = itart - call ovarrf(outfile,'Tokamak aspect ratio = Spherical, itart = 1','(itart)',itart_r) + if (istell == 0) then + if (itart == 0) then + itart_r = itart + call ovarrf(outfile,'Tokamak aspect ratio = Conventional, itart = 0','(itart)',itart_r) + else if (itart == 1) then + itart_r = itart + call ovarrf(outfile,'Tokamak aspect ratio = Spherical, itart = 1','(itart)',itart_r) + end if end if call osubhd(outfile,'Plasma Geometry :') @@ -3277,63 +3282,65 @@ subroutine outplas(outfile) call ovarrf(outfile,'Minor radius (m)','(rminor)',rminor, 'OP ') call ovarrf(outfile,'Aspect ratio','(aspect)',aspect) - select case (ishape) - case (0,6,8) - call ovarrf(outfile,'Elongation, X-point (input value used)', '(kappa)',kappa, 'IP ') - case (1) - call ovarrf(outfile,'Elongation, X-point (TART scaling)', '(kappa)',kappa, 'OP ') - case (2,3) - call ovarrf(outfile,'Elongation, X-point (Zohm scaling)', '(kappa)',kappa, 'OP ') - call ovarrf(outfile,'Zohm scaling adjustment factor', '(fkzohm)',fkzohm) - case (4,5,7) - call ovarrf(outfile,'Elongation, X-point (calculated from kappa95)', '(kappa)',kappa, 'OP ') - case (9) - call ovarrf(outfile,'Elongation, X-point (calculated from aspect ratio and li(3))', & + if (istell == 0) then + + select case (ishape) + case (0,6,8) + call ovarrf(outfile,'Elongation, X-point (input value used)', '(kappa)',kappa, 'IP ') + case (1) + call ovarrf(outfile,'Elongation, X-point (TART scaling)', '(kappa)',kappa, 'OP ') + case (2,3) + call ovarrf(outfile,'Elongation, X-point (Zohm scaling)', '(kappa)',kappa, 'OP ') + call ovarrf(outfile,'Zohm scaling adjustment factor', '(fkzohm)',fkzohm) + case (4,5,7) + call ovarrf(outfile,'Elongation, X-point (calculated from kappa95)', '(kappa)',kappa, 'OP ') + case (9) + call ovarrf(outfile,'Elongation, X-point (calculated from aspect ratio and li(3))', & + '(kappa)',kappa, 'OP ') + case (10) + call ovarrf(outfile,'Elongation, X-point (calculated from aspect ratio and stability margin)', & '(kappa)',kappa, 'OP ') - case (10) - call ovarrf(outfile,'Elongation, X-point (calculated from aspect ratio and stability margin)', & - '(kappa)',kappa, 'OP ') - case (11) - call ovarrf(outfile,'Elongation, X-point (calculated from aspect ratio via Menard 2016)', & - '(kappa)',kappa, 'OP ') - case default - idiags(1) = ishape ; call report_error(86) - end select - - select case (ishape) - case (4,5,7) - call ovarrf(outfile,'Elongation, 95% surface (input value used)', & - '(kappa95)',kappa95, 'IP ') - case default - call ovarrf(outfile,'Elongation, 95% surface (calculated from kappa)', & - '(kappa95)',kappa95, 'OP ') - end select - - call ovarrf(outfile,'Elongation, area ratio calc.','(kappaa)',kappaa, 'OP ') - - select case (ishape) - case (0,2,6,8,9,10,11) - call ovarrf(outfile,'Triangularity, X-point (input value used)', & - '(triang)',triang, 'IP ') - case (1) - call ovarrf(outfile,'Triangularity, X-point (TART scaling)', & - '(triang)',triang, 'OP ') - case (3,4,5,7) - call ovarrf(outfile,'Triangularity, X-point (calculated from triang95)', & - '(triang)',triang, 'OP ') - end select - - select case (ishape) - case (3,4,5,7) - call ovarrf(outfile,'Triangularity, 95% surface (input value used)', & - '(triang95)',triang95, 'IP ') - case default - call ovarrf(outfile,'Triangularity, 95% surface (calculated from triang)', & - '(triang95)',triang95, 'OP ') - end select - - call ovarrf(outfile,'Plasma poloidal perimeter (m)','(pperim)',pperim, 'OP ') - + case (11) + call ovarrf(outfile,'Elongation, X-point (calculated from aspect ratio via Menard 2016)', & + '(kappa)',kappa, 'OP ') + case default + idiags(1) = ishape ; call report_error(86) + end select + + select case (ishape) + case (4,5,7) + call ovarrf(outfile,'Elongation, 95% surface (input value used)', & + '(kappa95)',kappa95, 'IP ') + case default + call ovarrf(outfile,'Elongation, 95% surface (calculated from kappa)', & + '(kappa95)',kappa95, 'OP ') + end select + + call ovarrf(outfile,'Elongation, area ratio calc.','(kappaa)',kappaa, 'OP ') + + select case (ishape) + case (0,2,6,8,9,10,11) + call ovarrf(outfile,'Triangularity, X-point (input value used)', & + '(triang)',triang, 'IP ') + case (1) + call ovarrf(outfile,'Triangularity, X-point (TART scaling)', & + '(triang)',triang, 'OP ') + case (3,4,5,7) + call ovarrf(outfile,'Triangularity, X-point (calculated from triang95)', & + '(triang)',triang, 'OP ') + end select + + select case (ishape) + case (3,4,5,7) + call ovarrf(outfile,'Triangularity, 95% surface (input value used)', & + '(triang95)',triang95, 'IP ') + case default + call ovarrf(outfile,'Triangularity, 95% surface (calculated from triang)', & + '(triang95)',triang95, 'OP ') + end select + + call ovarrf(outfile,'Plasma poloidal perimeter (m)','(pperim)',pperim, 'OP ') + end if call ovarrf(outfile,'Plasma cross-sectional area (m2)','(xarea)',xarea, 'OP ') call ovarre(outfile,'Plasma surface area (m2)','(sarea)',sarea, 'OP ') @@ -3342,50 +3349,53 @@ subroutine outplas(outfile) call osubhd(outfile,'Current and Field :') - if (iprofile == 0) then - call ocmmnt(outfile, & - 'Consistency between q0,q,alphaj,rli,dnbeta is not enforced') - else - call ocmmnt(outfile, & - 'Consistency between q0,q,alphaj,rli,dnbeta is enforced') - end if - call oblnkl(outfile) - call ovarin(outfile,'Plasma current scaling law used','(icurr)',icurr) + if (istell == 0) then + if (iprofile == 0) then + call ocmmnt(outfile, & + 'Consistency between q0,q,alphaj,rli,dnbeta is not enforced') + else + call ocmmnt(outfile, & + 'Consistency between q0,q,alphaj,rli,dnbeta is enforced') + end if + call oblnkl(outfile) + call ovarin(outfile,'Plasma current scaling law used','(icurr)',icurr) + call ovarrf(outfile,'Plasma current (MA)','(plascur/1D6)',plascur/1.0D6, 'OP ') + !call ovarrf(outfile,'Plasma current (A)','(plascur)',plascur, 'OP ') + if (iprofile == 1) then + call ovarrf(outfile,'Current density profile factor','(alphaj)',alphaj, 'OP ') + else + call ovarrf(outfile,'Current density profile factor','(alphaj)',alphaj) + end if - call ovarrf(outfile,'Plasma current (MA)','(plascur/1D6)',plascur/1.0D6, 'OP ') - !call ovarrf(outfile,'Plasma current (A)','(plascur)',plascur, 'OP ') - if (iprofile == 1) then - call ovarrf(outfile,'Current density profile factor','(alphaj)',alphaj, 'OP ') - else - call ovarrf(outfile,'Current density profile factor','(alphaj)',alphaj) + call ovarrf(outfile,'Plasma internal inductance, li','(rli)',rli, 'OP ') + call ovarrf(outfile,'Vertical field at plasma (T)','(bvert)',bvert, 'OP ') end if - call ovarrf(outfile,'Plasma internal inductance, li','(rli)',rli, 'OP ') - call ovarrf(outfile,'Vertical field at plasma (T)','(bvert)',bvert, 'OP ') - - call ovarrf(outfile,'Vacuum toroidal field at R (T)','(bt)',bt) call ovarrf(outfile,'Average poloidal field (T)','(bp)',bp, 'OP ') call ovarrf(outfile,'Total field (sqrt(bp^2 + bt^2)) (T)','(btot)',btot, 'OP ') - call ovarrf(outfile,'Safety factor on axis','(q0)',q0) - - if (icurr == 2) then - call ovarrf(outfile,'Mean edge safety factor','(q)',q) - end if + if (istell == 0) then + call ovarrf(outfile,'Safety factor on axis','(q0)',q0) - call ovarrf(outfile,'Safety factor at 95% flux surface','(q95)',q95) + if (icurr == 2) then + call ovarrf(outfile,'Mean edge safety factor','(q)',q) + end if - call ovarrf(outfile,'Cylindrical safety factor (qcyl)','(qstar)',qstar, 'OP ') + call ovarrf(outfile,'Safety factor at 95% flux surface','(q95)',q95) - if (ishape == 1) then - call ovarrf(outfile,'Lower limit for edge safety factor q', '(qlim)',qlim, 'OP ') - end if + call ovarrf(outfile,'Cylindrical safety factor (qcyl)','(qstar)',qstar, 'OP ') + if (ishape == 1) then + call ovarrf(outfile,'Lower limit for edge safety factor q', '(qlim)',qlim, 'OP ') + end if + else + call ovarrf(outfile,'Rotational transform','(iotabar)',iotabar) + end if call osubhd(outfile,'Beta Information :') @@ -3407,18 +3417,20 @@ subroutine outplas(outfile) call ovarrf(outfile,'2nd stability beta upper limit','(epbetmax)', epbetmax) - if (iprofile == 1) then - call ovarrf(outfile,'Beta g coefficient','(dnbeta)',dnbeta, 'OP ') - else - call ovarrf(outfile,'Beta g coefficient','(dnbeta)',dnbeta) - end if + if (istell == 0) then + if (iprofile == 1) then + call ovarrf(outfile,'Beta g coefficient','(dnbeta)',dnbeta, 'OP ') + else + call ovarrf(outfile,'Beta g coefficient','(dnbeta)',dnbeta) + end if - call ovarrf(outfile,'Normalised thermal beta',' ',1.0D8*betath*rminor*bt/plascur, 'OP ') - !call ovarrf(outfile,'Normalised total beta',' ',1.0D8*beta*rminor*bt/plascur, 'OP ') - call ovarrf(outfile,'Normalised total beta',' ',normalised_total_beta, 'OP ') - !call ovarrf(outfile,'Normalised toroidal beta',' ',normalised_total_beta*(btot/bt)**2, 'OP ') - normalised_toroidal_beta=normalised_total_beta*(btot/bt)**2 - call ovarrf(outfile,'Normalised toroidal beta','(normalised_toroidal_beta)',normalised_toroidal_beta, 'OP ') + call ovarrf(outfile,'Normalised thermal beta',' ',1.0D8*betath*rminor*bt/plascur, 'OP ') + !call ovarrf(outfile,'Normalised total beta',' ',1.0D8*beta*rminor*bt/plascur, 'OP ') + call ovarrf(outfile,'Normalised total beta',' ',normalised_total_beta, 'OP ') + !call ovarrf(outfile,'Normalised toroidal beta',' ',normalised_total_beta*(btot/bt)**2, 'OP ') + normalised_toroidal_beta=normalised_total_beta*(btot/bt)**2 + call ovarrf(outfile,'Normalised toroidal beta','(normalised_toroidal_beta)',normalised_toroidal_beta, 'OP ') + end if if (iculbl == 0) then @@ -3443,8 +3455,10 @@ subroutine outplas(outfile) call ovarre(outfile,'Electron density on axis (/m3)','(ne0)',ne0, 'OP ') call ovarre(outfile,'Line-averaged electron density (/m3)','(dnla)',dnla, 'OP ') - call ovarre(outfile,'Line-averaged electron density / Greenwald density', & - '(dnla_gw)',dnla/dlimit(7), 'OP ') + if (istell == 0) then + call ovarre(outfile,'Line-averaged electron density / Greenwald density', & + '(dnla_gw)',dnla/dlimit(7), 'OP ') + end if call ovarre(outfile,'Ion density (/m3)','(dnitot)',dnitot, 'OP ') @@ -3555,14 +3569,16 @@ subroutine outplas(outfile) call ovarrf(outfile,'Temperature profile index','(alphat)',alphat) call ovarrf(outfile,'Temperature profile index beta','(tbeta)',tbeta) - call osubhd(outfile,'Density Limit using different models :') - call ovarre(outfile,'Old ASDEX model','(dlimit(1))',dlimit(1), 'OP ') - call ovarre(outfile,'Borrass ITER model I','(dlimit(2))',dlimit(2), 'OP ') - call ovarre(outfile,'Borrass ITER model II','(dlimit(3))',dlimit(3), 'OP ') - call ovarre(outfile,'JET edge radiation model','(dlimit(4))',dlimit(4), 'OP ') - call ovarre(outfile,'JET simplified model','(dlimit(5))',dlimit(5), 'OP ') - call ovarre(outfile,'Hugill-Murakami Mq model','(dlimit(6))',dlimit(6), 'OP ') - call ovarre(outfile,'Greenwald model','(dlimit(7))',dlimit(7), 'OP ') + if (istell == 0) then + call osubhd(outfile,'Density Limit using different models :') + call ovarre(outfile,'Old ASDEX model','(dlimit(1))',dlimit(1), 'OP ') + call ovarre(outfile,'Borrass ITER model I','(dlimit(2))',dlimit(2), 'OP ') + call ovarre(outfile,'Borrass ITER model II','(dlimit(3))',dlimit(3), 'OP ') + call ovarre(outfile,'JET edge radiation model','(dlimit(4))',dlimit(4), 'OP ') + call ovarre(outfile,'JET simplified model','(dlimit(5))',dlimit(5), 'OP ') + call ovarre(outfile,'Hugill-Murakami Mq model','(dlimit(6))',dlimit(6), 'OP ') + call ovarre(outfile,'Greenwald model','(dlimit(7))',dlimit(7), 'OP ') + end if call osubhd(outfile,'Fuel Constituents :') call ovarrf(outfile,'Deuterium fuel fraction','(fdeut)',fdeut) @@ -3593,7 +3609,9 @@ subroutine outplas(outfile) call ovarre(outfile,'Radiation power from inner zone (MW)', '(pinnerzoneradmw)',pinnerzoneradmw, 'OP ') call ovarre(outfile,'Radiation power from outer zone (MW)','(pouterzoneradmw)', pouterzoneradmw, 'OP ') - call ovarre(outfile,'SOL radiation power as imposed by f_rad (MW)','(psolradmw)', psolradmw, 'OP ') + if (istell/=0) then + call ovarre(outfile,'SOL radiation power as imposed by f_rad (MW)','(psolradmw)', psolradmw, 'OP ') + end if call ovarre(outfile,'Total radiation power from inside LCFS (MW)','(pradmw)',pradmw, 'OP ') call ovarre(outfile,'LCFS radiation fraction = total radiation in LCFS / total power deposited in plasma', & @@ -3610,40 +3628,43 @@ subroutine outplas(outfile) '(palpfwmw)', palpfwmw, 'OP ') call ovarre(outfile,'Nominal mean neutron load on inside surface of reactor (MW/m2)', & '(wallmw)', wallmw, 'OP ') - call oblnkl(outfile) - call ovarre(outfile,'Power incident on the divertor targets (MW)', & - '(ptarmw)',ptarmw, 'OP ') - call ovarre(outfile, 'Fraction of power to the lower divertor', & - '(ftar)', ftar, 'IP ') - call ovarre(outfile,'Outboard side heat flux decay length (m)', & - '(lambdaio)',lambdaio, 'OP ') - if (idivrt == 2) then - call ovarre(outfile,'Midplane seperation of the two magnetic closed flux surfaces (m)', & - '(drsep)',drsep, 'OP ') - end if - call ovarre(outfile,'Fraction of power on the inner targets', & - '(fio)',fio, 'OP ') - call ovarre(outfile,'Fraction of power incident on the lower inner target', & - '(fLI)',fLI, 'OP ') - call ovarre(outfile,'Fraction of power incident on the lower outer target', & - '(fLO)',fLO, 'OP ') - if (idivrt == 2 ) then - call ovarre(outfile,'Fraction of power incident on the upper inner target', & - '(fUI)',fUI, 'OP ') - call ovarre(outfile,'Fraction of power incident on the upper outer target', & - '(fUO)',fUO, 'OP ') - end if - call ovarre(outfile,'Power incident on the lower inner target (MW)', & - '(pLImw)',pLImw, 'OP ') - call ovarre(outfile,'Power incident on the lower outer target (MW)', & - '(pLOmw)',pLOmw, 'OP ') - if (idivrt == 2) then - call ovarre(outfile,'Power incident on the upper innner target (MW)', & - '(pUImw)',pUImw, 'OP ') - call ovarre(outfile,'Power incident on the upper outer target (MW)', & - '(pUOmw)',pUOmw, 'OP ') + if (istell == 0) then + call oblnkl(outfile) + call ovarre(outfile,'Power incident on the divertor targets (MW)', & + '(ptarmw)',ptarmw, 'OP ') + call ovarre(outfile, 'Fraction of power to the lower divertor', & + '(ftar)', ftar, 'IP ') + call ovarre(outfile,'Outboard side heat flux decay length (m)', & + '(lambdaio)',lambdaio, 'OP ') + if (idivrt == 2) then + call ovarre(outfile,'Midplane seperation of the two magnetic closed flux surfaces (m)', & + '(drsep)',drsep, 'OP ') + end if + call ovarre(outfile,'Fraction of power on the inner targets', & + '(fio)',fio, 'OP ') + call ovarre(outfile,'Fraction of power incident on the lower inner target', & + '(fLI)',fLI, 'OP ') + call ovarre(outfile,'Fraction of power incident on the lower outer target', & + '(fLO)',fLO, 'OP ') + if (idivrt == 2 ) then + call ovarre(outfile,'Fraction of power incident on the upper inner target', & + '(fUI)',fUI, 'OP ') + call ovarre(outfile,'Fraction of power incident on the upper outer target', & + '(fUO)',fUO, 'OP ') + end if + call ovarre(outfile,'Power incident on the lower inner target (MW)', & + '(pLImw)',pLImw, 'OP ') + call ovarre(outfile,'Power incident on the lower outer target (MW)', & + '(pLOmw)',pLOmw, 'OP ') + if (idivrt == 2) then + call ovarre(outfile,'Power incident on the upper innner target (MW)', & + '(pUImw)',pUImw, 'OP ') + call ovarre(outfile,'Power incident on the upper outer target (MW)', & + '(pUOmw)',pUOmw, 'OP ') + end if end if + call oblnkl(outfile) call ovarre(outfile,'Ohmic heating power (MW)','(pohmmw)',pohmmw, 'OP ') call ovarrf(outfile,'Fraction of alpha power deposited in plasma','(falpha)',falpha, 'OP ') @@ -3681,68 +3702,69 @@ subroutine outplas(outfile) call ovarre(outfile,'Psep Bt / qAR ratio (MWT/m)','(pdivtbt/qar)', ((pdivt*bt)/(q95*aspect*rmajor)), 'OP ') end if - - call osubhd(outfile,'H-mode Power Threshold Scalings :') - - call ovarre(outfile,'ITER 1996 scaling: nominal (MW)','(pthrmw(1))', pthrmw(1), 'OP ') - call ovarre(outfile,'ITER 1996 scaling: upper bound (MW)','(pthrmw(2))', pthrmw(2), 'OP ') - call ovarre(outfile,'ITER 1996 scaling: lower bound (MW)','(pthrmw(3))', pthrmw(3), 'OP ') - call ovarre(outfile,'ITER 1997 scaling (1) (MW)','(pthrmw(4))',pthrmw(4), 'OP ') - call ovarre(outfile,'ITER 1997 scaling (2) (MW)','(pthrmw(5))',pthrmw(5), 'OP ') - call ovarre(outfile,'Martin 2008 scaling: nominal (MW)', '(pthrmw(6))',pthrmw(6), 'OP ') - call ovarre(outfile,'Martin 2008 scaling: 95% upper bound (MW)', '(pthrmw(7))',pthrmw(7), 'OP ') - call ovarre(outfile,'Martin 2008 scaling: 95% lower bound (MW)', '(pthrmw(8))',pthrmw(8), 'OP ') - call ovarre(outfile,'Snipes 2000 scaling: nominal (MW)', '(pthrmw(9))',pthrmw(9), 'OP ') - call ovarre(outfile,'Snipes 2000 scaling: upper bound (MW)', '(pthrmw(10))',pthrmw(10), 'OP ') - call ovarre(outfile,'Snipes 2000 scaling: lower bound (MW)', '(pthrmw(11))',pthrmw(11), 'OP ') - call ovarre(outfile,'Snipes 2000 scaling (closed divertor): nominal (MW)', '(pthrmw(12))',pthrmw(12), 'OP ') - call ovarre(outfile,'Snipes 2000 scaling (closed divertor): upper bound (MW)', '(pthrmw(13))',pthrmw(13), 'OP ') - call ovarre(outfile,'Snipes 2000 scaling (closed divertor): lower bound (MW)', '(pthrmw(14))',pthrmw(14), 'OP ') - call ovarre(outfile,'Hubbard 2012 L-I threshold - nominal (MW)', '(pthrmw(15))',pthrmw(15), 'OP ') - call ovarre(outfile,'Hubbard 2012 L-I threshold - lower bound (MW)', '(pthrmw(16))',pthrmw(16), 'OP ') - call ovarre(outfile,'Hubbard 2012 L-I threshold - upper bound (MW)', '(pthrmw(17))',pthrmw(17), 'OP ') - call ovarre(outfile,'Hubbard 2017 L-I threshold', '(pthrmw(18))',pthrmw(18), 'OP ') - call ovarre(outfile,'Martin 2008 aspect ratio corrected scaling: nominal (MW)', '(pthrmw(19))',pthrmw(19), 'OP ') - call ovarre(outfile,'Martin 2008 aspect ratio corrected scaling: 95% upper bound (MW)', '(pthrmw(20))',pthrmw(20), 'OP ') - call ovarre(outfile,'Martin 2008 aspect ratio corrected scaling: 95% lower bound (MW)', '(pthrmw(21))',pthrmw(21), 'OP ') - call oblnkl(outfile) - if ((ilhthresh.eq.9).or.(ilhthresh.eq.10).or.(ilhthresh.eq.11)) then - if ((bt < 0.78D0).or.(bt > 7.94D0)) then - call ocmmnt(outfile,'(bt outside Snipes 2000 fitted range)') - call report_error(201) - end if - if ((rminor < 0.15D0).or.(rminor > 1.15D0)) then - call ocmmnt(outfile,'(rminor outside Snipes 2000 fitted range)') - call report_error(202) - end if - if ((rmajor < 0.55D0).or.(rmajor > 3.37D0)) then - call ocmmnt(outfile,'(rmajor outside Snipes 2000 fitted range)') - call report_error(203) - end if - if ((dnla < 0.09D20).or.(dnla > 3.16D20)) then - call ocmmnt(outfile,'(dnla outside Snipes 2000 fitted range)') - call report_error(204) + if (istell == 0) then + call osubhd(outfile,'H-mode Power Threshold Scalings :') + + call ovarre(outfile,'ITER 1996 scaling: nominal (MW)','(pthrmw(1))', pthrmw(1), 'OP ') + call ovarre(outfile,'ITER 1996 scaling: upper bound (MW)','(pthrmw(2))', pthrmw(2), 'OP ') + call ovarre(outfile,'ITER 1996 scaling: lower bound (MW)','(pthrmw(3))', pthrmw(3), 'OP ') + call ovarre(outfile,'ITER 1997 scaling (1) (MW)','(pthrmw(4))',pthrmw(4), 'OP ') + call ovarre(outfile,'ITER 1997 scaling (2) (MW)','(pthrmw(5))',pthrmw(5), 'OP ') + call ovarre(outfile,'Martin 2008 scaling: nominal (MW)', '(pthrmw(6))',pthrmw(6), 'OP ') + call ovarre(outfile,'Martin 2008 scaling: 95% upper bound (MW)', '(pthrmw(7))',pthrmw(7), 'OP ') + call ovarre(outfile,'Martin 2008 scaling: 95% lower bound (MW)', '(pthrmw(8))',pthrmw(8), 'OP ') + call ovarre(outfile,'Snipes 2000 scaling: nominal (MW)', '(pthrmw(9))',pthrmw(9), 'OP ') + call ovarre(outfile,'Snipes 2000 scaling: upper bound (MW)', '(pthrmw(10))',pthrmw(10), 'OP ') + call ovarre(outfile,'Snipes 2000 scaling: lower bound (MW)', '(pthrmw(11))',pthrmw(11), 'OP ') + call ovarre(outfile,'Snipes 2000 scaling (closed divertor): nominal (MW)', '(pthrmw(12))',pthrmw(12), 'OP ') + call ovarre(outfile,'Snipes 2000 scaling (closed divertor): upper bound (MW)', '(pthrmw(13))',pthrmw(13), 'OP ') + call ovarre(outfile,'Snipes 2000 scaling (closed divertor): lower bound (MW)', '(pthrmw(14))',pthrmw(14), 'OP ') + call ovarre(outfile,'Hubbard 2012 L-I threshold - nominal (MW)', '(pthrmw(15))',pthrmw(15), 'OP ') + call ovarre(outfile,'Hubbard 2012 L-I threshold - lower bound (MW)', '(pthrmw(16))',pthrmw(16), 'OP ') + call ovarre(outfile,'Hubbard 2012 L-I threshold - upper bound (MW)', '(pthrmw(17))',pthrmw(17), 'OP ') + call ovarre(outfile,'Hubbard 2017 L-I threshold', '(pthrmw(18))',pthrmw(18), 'OP ') + call ovarre(outfile,'Martin 2008 aspect ratio corrected scaling: nominal (MW)', '(pthrmw(19))',pthrmw(19), 'OP ') + call ovarre(outfile,'Martin 2008 aspect ratio corrected scaling: 95% upper bound (MW)', '(pthrmw(20))',pthrmw(20), 'OP ') + call ovarre(outfile,'Martin 2008 aspect ratio corrected scaling: 95% lower bound (MW)', '(pthrmw(21))',pthrmw(21), 'OP ') + call oblnkl(outfile) + if ((ilhthresh.eq.9).or.(ilhthresh.eq.10).or.(ilhthresh.eq.11)) then + if ((bt < 0.78D0).or.(bt > 7.94D0)) then + call ocmmnt(outfile,'(bt outside Snipes 2000 fitted range)') + call report_error(201) + end if + if ((rminor < 0.15D0).or.(rminor > 1.15D0)) then + call ocmmnt(outfile,'(rminor outside Snipes 2000 fitted range)') + call report_error(202) + end if + if ((rmajor < 0.55D0).or.(rmajor > 3.37D0)) then + call ocmmnt(outfile,'(rmajor outside Snipes 2000 fitted range)') + call report_error(203) + end if + if ((dnla < 0.09D20).or.(dnla > 3.16D20)) then + call ocmmnt(outfile,'(dnla outside Snipes 2000 fitted range)') + call report_error(204) + end if + if ((kappa < 1.0D0).or.(kappa > 2.04D0)) then + call ocmmnt(outfile,'(kappa outside Snipes 2000 fitted range)') + call report_error(205) + end if + if ((triang < 0.07D0).or.(triang > 0.74D0)) then + call ocmmnt(outfile,'(triang outside Snipes 2000 fitted range)') + call report_error(206) + end if + call oblnkl(outfile) end if - if ((kappa < 1.0D0).or.(kappa > 2.04D0)) then - call ocmmnt(outfile,'(kappa outside Snipes 2000 fitted range)') - call report_error(205) + if ((ilhthresh.eq.12).or.(ilhthresh.eq.13).or.(ilhthresh.eq.14)) then + call ocmmnt(outfile,'(L-H threshold for closed divertor only. Limited data used in Snipes fit)') + call oblnkl(outfile) + call report_error(207) end if - if ((triang < 0.07D0).or.(triang > 0.74D0)) then - call ocmmnt(outfile,'(triang outside Snipes 2000 fitted range)') - call report_error(206) + if ((ioptimz > 0).and.(active_constraints(15))) then + call ovarre(outfile,'L-H threshold power (enforced) (MW)', '(boundl(103)*plhthresh)',boundl(103)*plhthresh, 'OP ') + call ovarre(outfile,'L-H threshold power (MW)', '(plhthresh)',plhthresh, 'OP ') + else + call ovarre(outfile,'L-H threshold power (NOT enforced) (MW)', '(plhthresh)',plhthresh, 'OP ') end if - call oblnkl(outfile) - end if - if ((ilhthresh.eq.12).or.(ilhthresh.eq.13).or.(ilhthresh.eq.14)) then - call ocmmnt(outfile,'(L-H threshold for closed divertor only. Limited data used in Snipes fit)') - call oblnkl(outfile) - call report_error(207) - end if - if ((ioptimz > 0).and.(active_constraints(15))) then - call ovarre(outfile,'L-H threshold power (enforced) (MW)', '(boundl(103)*plhthresh)',boundl(103)*plhthresh, 'OP ') - call ovarre(outfile,'L-H threshold power (MW)', '(plhthresh)',plhthresh, 'OP ') - else - call ovarre(outfile,'L-H threshold power (NOT enforced) (MW)', '(plhthresh)',plhthresh, 'OP ') end if call osubhd(outfile,'Confinement :') @@ -3792,16 +3814,16 @@ subroutine outplas(outfile) '(total_energy_conf_time)', total_energy_conf_time, 'OP ') call ocmmnt(outfile,' (= stored energy including fast particles / loss power including radiation') - - ! Issues 363 Output dimensionless plasma parameters MDK - call osubhd(outfile,'Dimensionless plasma parameters') - call ocmmnt(outfile,'For definitions see') - call ocmmnt(outfile,'Recent progress on the development and analysis of the ITPA global H-mode confinement database') - call ocmmnt(outfile,'D.C. McDonald et al, 2007 Nuclear Fusion v47, 147. (nu_star missing 1/mu0)') - call ovarre(outfile,'Normalized plasma pressure beta as defined by McDonald et al', '(beta_mcdonald)',beta_mcdonald,'OP ') - call ovarre(outfile,'Normalized ion Larmor radius', '(rho_star)', rho_star,'OP ') - call ovarre(outfile,'Normalized collisionality', '(nu_star)',nu_star,'OP ') - call ovarre(outfile,'Volume measure of elongation','(kappaa_IPB)',kappaa_IPB,'OP ') + if (istell == 0) then + ! Issues 363 Output dimensionless plasma parameters MDK + call osubhd(outfile,'Dimensionless plasma parameters') + call ocmmnt(outfile,'For definitions see') + call ocmmnt(outfile,'Recent progress on the development and analysis of the ITPA global H-mode confinement database') + call ocmmnt(outfile,'D.C. McDonald et al, 2007 Nuclear Fusion v47, 147. (nu_star missing 1/mu0)') + call ovarre(outfile,'Normalized plasma pressure beta as defined by McDonald et al', '(beta_mcdonald)',beta_mcdonald,'OP ') + call ovarre(outfile,'Normalized ion Larmor radius', '(rho_star)', rho_star,'OP ') + call ovarre(outfile,'Normalized collisionality', '(nu_star)',nu_star,'OP ') + call ovarre(outfile,'Volume measure of elongation','(kappaa_IPB)',kappaa_IPB,'OP ') call osubhd(outfile,'Plasma Volt-second Requirements :') @@ -3818,42 +3840,42 @@ subroutine outplas(outfile) call ovarrf(outfile,'Bootstrap fraction (Sauter et al)', '(bscf_sauter)',bscf_sauter, 'OP ') - call ovarrf(outfile,'Bootstrap fraction (Nevins et al)', '(bscf_nevins)',bscf_nevins, 'OP ') - call ovarrf(outfile,'Bootstrap fraction (Wilson)', '(bscf_wilson)',bscf_wilson, 'OP ') - call ovarrf(outfile,'Diamagnetic fraction (Hender)', '(diacf_hender)',diacf_hender, 'OP ') - call ovarrf(outfile,'Diamagnetic fraction (SCENE)', '(diacf_scene)',diacf_scene, 'OP ') - call ovarrf(outfile,'Pfirsch-Schlueter fraction (SCENE)', '(pscf_scene)',pscf_scene, 'OP ') - ! Error to catch if bootstap fraction limit has been enforced - if (err242==1)then - call report_error(242) - end if - ! Error to catch if self-driven current fraction limit has been enforced - if (err243==1)then - call report_error(243) - end if + call ovarrf(outfile,'Bootstrap fraction (Nevins et al)', '(bscf_nevins)',bscf_nevins, 'OP ') + call ovarrf(outfile,'Bootstrap fraction (Wilson)', '(bscf_wilson)',bscf_wilson, 'OP ') + call ovarrf(outfile,'Diamagnetic fraction (Hender)', '(diacf_hender)',diacf_hender, 'OP ') + call ovarrf(outfile,'Diamagnetic fraction (SCENE)', '(diacf_scene)',diacf_scene, 'OP ') + call ovarrf(outfile,'Pfirsch-Schlueter fraction (SCENE)', '(pscf_scene)',pscf_scene, 'OP ') + ! Error to catch if bootstap fraction limit has been enforced + if (err242==1)then + call report_error(242) + end if + ! Error to catch if self-driven current fraction limit has been enforced + if (err243==1)then + call report_error(243) + end if - if (bscfmax < 0.0D0) then - call ocmmnt(outfile,' (User-specified bootstrap current fraction used)') - else if (ibss == 1) then - call ocmmnt(outfile,' (ITER 1989 bootstrap current fraction model used)') - else if (ibss == 2) then - call ocmmnt(outfile,' (Nevins et al bootstrap current fraction model used)') - else if (ibss == 3) then - call ocmmnt(outfile,' (Wilson bootstrap current fraction model used)') - else if (ibss == 4) then - call ocmmnt(outfile,' (Sauter et al bootstrap current fraction model used)') - end if + if (bscfmax < 0.0D0) then + call ocmmnt(outfile,' (User-specified bootstrap current fraction used)') + else if (ibss == 1) then + call ocmmnt(outfile,' (ITER 1989 bootstrap current fraction model used)') + else if (ibss == 2) then + call ocmmnt(outfile,' (Nevins et al bootstrap current fraction model used)') + else if (ibss == 3) then + call ocmmnt(outfile,' (Wilson bootstrap current fraction model used)') + else if (ibss == 4) then + call ocmmnt(outfile,' (Sauter et al bootstrap current fraction model used)') + end if - if (idia == 0) then - call ocmmnt(outfile,' (Diamagnetic current fraction not calculated)') - ! Error to show if diamagnetic current is above 1% but not used - if (diacf_scene.gt.0.01D0) then - call report_error(244) - end if - else if (idia == 1) then - call ocmmnt(outfile,' (Hender diamagnetic current fraction scaling used)') - else if (idia == 2) then - call ocmmnt(outfile,' (SCENE diamagnetic current fraction scaling used)') + if (idia == 0) then + call ocmmnt(outfile,' (Diamagnetic current fraction not calculated)') + ! Error to show if diamagnetic current is above 1% but not used + if (diacf_scene.gt.0.01D0) then + call report_error(244) + end if + else if (idia == 1) then + call ocmmnt(outfile,' (Hender diamagnetic current fraction scaling used)') + else if (idia == 2) then + call ocmmnt(outfile,' (SCENE diamagnetic current fraction scaling used)') if (ips == 0) then call ocmmnt(outfile,' (Pfirsch-Schlüter current fraction not calculated)') @@ -3873,7 +3895,7 @@ subroutine outplas(outfile) call ovarre(outfile,'Resistive diffusion time (s)','(res_time)',res_time, 'OP ') call ovarre(outfile,'Plasma inductance (H)','(rlp)',rlp, 'OP ') call ovarrf(outfile,'Coefficient for sawtooth effects on burn V-s requirement','(csawth)',csawth) - + end if call osubhd(outfile,'Fuelling :') call ovarre(outfile,'Ratio of He and pellet particle confinement times','(tauratio)',tauratio) diff --git a/source/fortran/stellarator.f90 b/source/fortran/stellarator.f90 new file mode 100644 index 00000000..a3e2f7e5 --- /dev/null +++ b/source/fortran/stellarator.f90 @@ -0,0 +1,123 @@ +module stellarator_module + + !! Module containing stellarator routines + !! author: P J Knight, CCFE, Culham Science Centre + !! N/A + !! This module contains routines for calculating the + !! parameters of the first wall, blanket and shield components + !! of a fusion power plant. + + !! AEA FUS 251: A User's Guide to the PROCESS Systems Code + ! + ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +#ifndef dp + use, intrinsic :: iso_fortran_env, only: dp=>real64 +#endif + + implicit none + + ! scaling parameters to reference point. + real(dp) :: f_n, f_r, f_aspect, f_b, f_i, f_a + + + logical :: first_call = .true. + logical :: first_call_stfwbs = .true. + +contains + + subroutine init_stellarator_module + !! Initialise module variables + implicit none + + first_call = .true. + first_call_stfwbs = .true. + f_n = 0.0D0 + f_r = 0.0D0 + f_a = 0.0D0 + f_b = 0.0D0 + f_i = 0.0D0 + end subroutine init_stellarator_module + + ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + subroutine stinit + + !! Routine to initialise the variables relevant to stellarators + !! author: P J Knight, CCFE, Culham Science Centre + !! author: F Warmer, IPP Greifswald + !! None + !! This routine initialises the variables relevant to stellarators. + !! Many of these may override the values set in routine + !! initial. + !! AEA FUS 251: A User's Guide to the PROCESS Systems Code + ! + ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + use build_variables, only: gapoh, iohcl, ohcth, tfootfi + use current_drive_variables, only: irfcd + use pfcoil_variables, only: ohhghf + use physics_variables, only: aspect, dnbeta, kappa, kappa95, q, rmajor, & + triang, hfac, tauscl + use numerics, only: boundl, boundu + use stellarator_variables, only: istell + use tfcoil_variables, only: n_tf + use times_variables, only: tburn, tcycle, tdown, tdwell, theat, tohs, & + tpulse, tqnch, tramp + use global_variables, only: icase + use constants, only: pi, rmu0, nout + implicit none + + ! Arguments + + ! Local variables + + !real(dp) :: fsum + + ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + ! This routine is called before (!!!) the input file. put everything that depends on the input file in stcaller + if (istell == 0) return + + boundu(1) = 40.0D0 ! allow higher aspect ratio + ! Numerics quantities + + !boundl(1) = 5.0D0 + + + !boundu(3) = 30.0D0 + !boundu(29) = 20.0D0 + + ! These lines switch off tokamak specifics (solenoid, pf coils, pulses etc.). + ! Are they still up to date? (26/07/22 JL) + + ! Build quantities + + ohcth = 0.0D0 + iohcl = 0 + ohhghf = 0.0D0 + gapoh = 0.0D0 + tfootfi = 1.0D0 + + ! Physics quantities + + dnbeta = 0.0D0 + kappa95 = 1.0D0 + triang = 0.0D0 + q = 1.03D0 + + ! Turn off current drive + + irfcd = 0 + + ! Times for different phases + + tramp = 0.0D0 + tohs = 0.0D0 + tburn = 3.15576D7 ! one year + tqnch = 0.0D0 + tpulse = tohs + theat + tburn + tqnch + tdown = tramp + tohs + tqnch + tdwell + tcycle = tramp + tohs + theat + tburn + tqnch + tdwell + + end subroutine stinit +end module stellarator_module diff --git a/source/fortran/stellarator_configuration.f90 b/source/fortran/stellarator_configuration.f90 new file mode 100644 index 00000000..37e1b2d9 --- /dev/null +++ b/source/fortran/stellarator_configuration.f90 @@ -0,0 +1,595 @@ +module stellarator_configuration + !! author: J Lion, IPP Greifswald + !! Module containing defining parameters for a stellarator + !! + !! This module contains a set of constants that defines a + !! stellarator configuration. These parameters are based on external + !! calculations and are hardcoded right now into this module. There will be + !! the possibiltiy to set them via an input file in the future. + !! The list below will be modified in further commits. + ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +#ifndef dp + use, intrinsic :: iso_fortran_env, only: dp=>real64 +#endif + + implicit none + + character (len = 20) :: stella_config_name + ! Name of the configuration + + integer stella_config_symmetry + ! Number of coils [1] + + integer stella_config_coilspermodule + ! Coils per module [1] + + real(dp) stella_config_rmajor_ref + ! Reference Point for major radius where all the other variables are determined [m] + + real(dp) stella_config_rminor_ref + ! Reference Point for minor radius where all the other variables are determined [m] + + real(dp) stella_config_coil_rmajor + ! Reference Point for coil major radius [m] + + real(dp) stella_config_coil_rminor + ! Reference Point for coil minor radius [m] + + real(dp) stella_config_aspect_ref + ! Reference Point for aspect ratio where all the other variables are determined [1] + + real(dp) stella_config_bt_ref + ! Reference Point for toroidal b where all the other variables are determined [T] + + real(dp) stella_config_WP_area + ! Winding pack area at the reference point [m^2] + + real(dp) stella_config_WP_bmax + ! The maximal magnetic field in the winding pack at the reference size of the winding pack [T] + + real(dp) stella_config_i0 + ! Coil current needed for b0 at the reference point [MA] + + real(dp) stella_config_a1 + ! Magnetic field fit parameter a1 (for the maximal field on the coils) [1] + + real(dp) stella_config_a2 + ! Magnetic field fit parameter a2 [1] + + real(dp) stella_config_dmin + ! Minimal intercoil distance at the reference point [m] + + real(dp) stella_config_inductance + ! inductance at the reference point [H] + + real(dp) stella_config_coilsurface + ! Coil surface at the reference point [m2] + + real(dp) stella_config_coillength + ! Total coil length at the reference point [m] + + real(dp) stella_config_max_portsize_width + ! Port size in toroidal direction at the reference point [m] + + real(dp) stella_config_maximal_coil_height + ! The maximal coil height at reference point. [m] + + real(dp) stella_config_min_plasma_coil_distance + ! The minimal distance between coil and plasma at the reference point [m] + + real(dp) stella_config_derivative_min_LCFS_coils_dist + ! The derivative of min_plasma_coil_distance wrt to the minor plasma radius at the reference point [1] + + real(dp) stella_config_plasma_volume + ! The plasma volume at the reference point. Scales as a*R^2. [m^3] + + real(dp) stella_config_plasma_surface + ! The plasma surface a the reference point. [m^2] + + real(dp) stella_config_WP_ratio + ! Ratio radial to toroidal length of the winding pack. (a1 and a2 should be calculated using this value) [1] + + real(dp) stella_config_max_force_density + ! Maximal toroidal and radially averaged force density at reference point in a WP cross section [MN/m^3] + + real(dp) stella_config_max_force_density_MNm + ! Maximal integrated force density at reference point in a WP cross section [MN/m] + + real(dp) stella_config_min_bend_radius + ! Minimal bending radius at reference point [m] + + real(dp) stella_config_epseff + ! Maximal epsilon effective in the core region [1] + + real(dp) stella_config_max_lateral_force_density + ! Maximal lateral force density of the coil set [MN/m] + + real(dp) stella_config_max_radial_force_density + ! Maximal radial force density of the coil set [MN/m] + + real(dp) stella_config_centering_force_max_MN + ! Maximal centering force of a coil in the coil set [MN] + + real(dp) stella_config_centering_force_min_MN + ! Minimal centering force of a coil in the coil set (negative means pointing outwards) [MN] + + real(dp) stella_config_centering_force_avg_MN + ! Average centering force the coils in the coil set [MN/coil] + + real(dp) :: stella_config_neutron_peakfactor + ! The neutron peaking factor determined through inhomogeneities on the stellarator wall (qmax/qavg) [1] + + real(dp), dimension(:), allocatable :: sc_D11_star_mono_input + ! The monoenergetic radial transport coefficients normalized by the plateau value. + + real(dp), dimension(:), allocatable :: sc_nu_star_mono_input + ! The monoenergetic radial transport coefficients normalized by the plateau value. + + contains + + subroutine new_stella_config(index) + integer, intent(in) :: index + + select case (index) + + ! This is the istell case switch: + ! istell = 1: Helias5 machine + ! istell = 2: Helias4 machine + ! istell = 3: Helias3 machine + ! istell = 4: w7x30 machine + ! istell = 5: w7x50 machine + ! istell = 6: Init from json + + ! All parameters set here are prelimnirary versions and might be changed in further commits + + case(1) + ! Helias5 Machine + ! The values are given at the reference point + + stella_config_name = "Helias 5b" + + stella_config_rmajor_ref = 22.2D0 + stella_config_rminor_ref = 1.80D0 + stella_config_aspect_ref = 12.33D0 + + ! Coil radii + stella_config_coil_rmajor = 22.44D0 + stella_config_coil_rminor = 4.76D0 + + stella_config_bt_ref = 5.6D0 + stella_config_WP_area = 0.8d0*0.6d0 + stella_config_WP_bmax = 11.44d0 + + stella_config_symmetry = 5 + stella_config_coilspermodule = 10 + + stella_config_a1 = 0.688D0 + stella_config_a2 = 0.025D0 + + stella_config_plasma_volume = 1422.63D0 ! This value is for Helias 5 + stella_config_dmin = 0.84D0 + stella_config_max_portsize_width = 2.12D0 + + stella_config_plasma_surface = 1960.0D0 ! Plasma Surface + + stella_config_maximal_coil_height = 12.7D0 ! [m] Full height max point to min point + + stella_config_coilsurface = 4817.7D0 ! Coil surface, dimensionfull. At reference point + + stella_config_coillength = 1680.0D0 ! Central filament length of machine with outer radius 1m. + + stella_config_I0 = 13.06D0 ! Coil Current needed to produce 1T on axis in [MA] at outer radius 1m + stella_config_inductance = 1655.76D-6 ! inductance in muH + + stella_config_WP_ratio = 1.2D0 ! The fit values in stellarator config class should be calculated using this value. + + stella_config_max_force_density = 120.0d0 ! [MN/m^3] + stella_config_max_force_density_MNm = 98.0d0 ! [MN/m] + + stella_config_max_lateral_force_density = 92.4d0 ! [MN/m^3] + stella_config_max_radial_force_density = 113.5d0 ! [MN/m^3] + + stella_config_centering_force_max_MN = 189.5d0 + stella_config_centering_force_min_MN = -55.7d0 + stella_config_centering_force_avg_MN = 93.0d0 + + stella_config_min_plasma_coil_distance = 1.9d0 + stella_config_derivative_min_LCFS_coils_dist = -1.0d0 ! this is approximated for now + + stella_config_min_bend_radius = 1.0d0 ! [m] + + stella_config_neutron_peakfactor = 1.6d0 + + stella_config_epseff = 0.015d0 + + + if (allocated(sc_D11_star_mono_input)) deallocate(sc_D11_star_mono_input) + if (allocated(sc_nu_star_mono_input)) deallocate(sc_nu_star_mono_input) + allocate(sc_D11_star_mono_input(10)) + allocate(sc_nu_star_mono_input(10)) + + sc_D11_star_mono_input = (/1,1,1,1,1,1,1,1,1,1/) + sc_nu_star_mono_input = (/1d-8,1d-7,1d-6,1d-5,1d-4,1d-3,1d-2,1d-1,1d0,1d1/) + + + + + case(2) + ! Helias4 Machine + stella_config_name = "Helias 4" + ! Reference point where all the other variables are determined from + ! Plasma outer radius + stella_config_rmajor_ref = 17.6D0 + stella_config_rminor_ref = 2.0D0 + stella_config_aspect_ref = 8.8D0 + + ! Coil radii + stella_config_coil_rmajor = 18.39D0 + stella_config_coil_rminor = 4.94D0 + + stella_config_bt_ref = 5.6D0 + stella_config_WP_area = 0.8d0*0.6d0 + stella_config_WP_bmax = 11.51d0 + + stella_config_symmetry = 4 + stella_config_coilspermodule = 10 + stella_config_a1 = 0.676D0 + stella_config_a2 = 0.029D0 + stella_config_plasma_volume = 1380.0D0 + stella_config_dmin = 1.08D0 + stella_config_max_portsize_width = 3.24D0 + + stella_config_plasma_surface = 1900.0D0 + stella_config_maximal_coil_height = 13.34D0 ! [m] Full height max point to min point + + stella_config_coilsurface = 4100.0D0! Coil surface, dimensionfull. At reference point + + stella_config_coillength = 1435.07D0 ! Central filament length of machine with outer radius 1m. + + stella_config_I0 = 13.146D0 ! Coil Current needed to produce b0 on axis in [MA] at reference point + stella_config_inductance = 1290.4D-6 ! inductance/R*A^2 in muH + + stella_config_WP_ratio = 1.3D0 + + stella_config_max_force_density = 120.0d0 ! [MN/m^3] + stella_config_max_force_density_MNm = 98.0d0 ! [MN/m] + + + stella_config_max_lateral_force_density = 87.9d0 ! [MN/m^3] + stella_config_max_radial_force_density = 109.9d0 ! [MN/m^3] + + stella_config_centering_force_max_MN = 226.0d0 + stella_config_centering_force_min_MN = -35.3d0 + stella_config_centering_force_avg_MN = 125.8d0 + + stella_config_min_plasma_coil_distance = 1.7d0 + stella_config_derivative_min_LCFS_coils_dist = -1.0d0 ! this is approximated for now + + stella_config_min_bend_radius = 0.86d0 ! [m] + + stella_config_neutron_peakfactor = 1.6d0 + + stella_config_epseff = 0.015d0 + + + case(3) + ! Helias 3 Machine + stella_config_name = "Helias 3" + ! Reference point where all the other variables are determined from + ! Plasma outer radius + stella_config_rmajor_ref = 13.86d0 + stella_config_rminor_ref = 2.18d0 + stella_config_aspect_ref = 6.36d0 + + ! Coil radii + stella_config_coil_rmajor = 14.53D0 + stella_config_coil_rminor = 6.12D0 + + stella_config_bt_ref = 5.6D0 + stella_config_WP_bmax = 12.346d0 + stella_config_WP_area = 0.8d0*0.6d0 + + stella_config_symmetry = 3 + stella_config_coilspermodule = 10 + + ! Bmax fit parameters + stella_config_a1 = 0.56D0 + stella_config_a2 = 0.030D0 + + stella_config_plasma_volume = 1300.8D0 + stella_config_dmin = 1.145D0 + stella_config_max_portsize_width = 3.24D0 !??? guess. not ready yet + + stella_config_plasma_surface = 1600.00D0 + + stella_config_maximal_coil_height = 17.74D0! [m] Full height max point to min point + + stella_config_coilsurface = 4240.0D0 ! Coil surface, dimensionfull. At reference point + + stella_config_coillength = 1287.3D0 ! Central filament length of machine with outer radius 1m. + + stella_config_I0 = 14.23D0 ! Coil Current needed to produce 1T on axis in [MA] at outer radius 1m + stella_config_inductance = 1250.7D-6 ! inductance in muH + + stella_config_WP_ratio = 1.3D0 + + stella_config_max_force_density = 120.0d0 + stella_config_max_force_density_MNm = 98.0d0 ! [MN/m] + + + stella_config_max_lateral_force_density = 96.6d0 ! [MN/m^3] + stella_config_max_radial_force_density = 130.5d0 ! [MN/m^3] + + stella_config_centering_force_max_MN = 428.1d0 + stella_config_centering_force_min_MN = -70.3d0 + stella_config_centering_force_avg_MN = 240.9d0 + + stella_config_min_plasma_coil_distance = 1.78d0 + stella_config_derivative_min_LCFS_coils_dist = -1.0d0 ! this is approximated for now + + stella_config_min_bend_radius = 1.145d0 ! [m] + + stella_config_neutron_peakfactor = 1.6d0 + + stella_config_epseff = 0.015d0 + + + case(4) + ! w7x30 Machine + stella_config_name = "W7X-30" + ! Reference point where all the other variables are determined from + ! Plasma outer radius + stella_config_rmajor_ref = 5.50D0 + stella_config_rminor_ref = 0.49D0 + stella_config_aspect_ref = 11.2D0 + + ! Coil radii + stella_config_coil_rmajor = 5.62D0 + stella_config_coil_rminor = 1.36D0 + + + stella_config_bt_ref = 3.0D0 + stella_config_WP_area = 0.18d0*0.15d0 + stella_config_WP_bmax = 10.6d0 + + stella_config_symmetry = 5 + stella_config_coilspermodule = 6 + stella_config_a1 = 0.98D0 + stella_config_a2 = 0.041D0 + stella_config_plasma_volume = 26.4D0 + stella_config_dmin = 0.21D0 + stella_config_max_portsize_width = 0.5D0 + + stella_config_plasma_surface = 128.3D0 + stella_config_maximal_coil_height = 3.6D0 ! [m] Full height max point to min point + + stella_config_coilsurface = 370.0D0! Coil surface, dimensionfull. At reference point + + stella_config_coillength = 303.4D0 ! Central filament length of machine with outer radius 1m. + + stella_config_I0 = 2.9D0 ! Coil Current needed to produce b0 on axis in [MA] at reference point + stella_config_inductance = 252.7D-6 ! inductance/R*A^2 in muH + + stella_config_WP_ratio = 1.2D0 + + stella_config_max_force_density = 350.0d0 ! [MN/m^3] + stella_config_max_force_density_MNm = 98.0d0 ! [MN/m] + + + stella_config_max_lateral_force_density = 271.1d0 ! [MN/m^3] + stella_config_max_radial_force_density = 305.2d0 ! [MN/m^3] + + stella_config_centering_force_max_MN = 7.95d0 + stella_config_centering_force_min_MN = -2.15d0 + stella_config_centering_force_avg_MN = 3.46d0 + + stella_config_min_plasma_coil_distance = 0.45D0 + stella_config_derivative_min_LCFS_coils_dist = -1.0d0 ! this is approximated for now + + stella_config_min_bend_radius = 0.186d0 ! [m] + + stella_config_neutron_peakfactor = 1.6d0 + + stella_config_epseff = 0.015d0 + + case(5) + ! w7x50 Machine + stella_config_name = "W7X-50" + ! Reference point where all the other variables are determined from + ! Plasma outer radius + stella_config_rmajor_ref = 5.5D0 + stella_config_rminor_ref = 0.49D0 + stella_config_aspect_ref = 11.2D0 + + ! Coil radii + stella_config_coil_rmajor = 5.62d0 + stella_config_coil_rminor = 1.18D0 + + + stella_config_bt_ref = 3.0D0 + stella_config_WP_area = 0.18d0*0.15d0 + stella_config_WP_bmax = 6.3d0 + + stella_config_symmetry = 5 + stella_config_coilspermodule = 10 + stella_config_a1 = 0.66D0 + stella_config_a2 = 0.025D0 + stella_config_plasma_volume = 26.4D0 + stella_config_dmin = 0.28D0 + stella_config_max_portsize_width = 0.3D0 + + stella_config_plasma_surface = 128.3D0 + stella_config_maximal_coil_height = 3.1D0 ! [m] Full height max point to min point + + stella_config_coilsurface = 299.85D0! Coil surface, dimensionfull. At reference point + + stella_config_coillength = 420.67D0 ! Central filament length of machine with outer radius 1m. + + stella_config_I0 = 1.745D0 ! Coil Current needed to produce b0 on axis in [MA] at reference point + stella_config_inductance = 412.4D-6 ! inductance/R*A^2 in muH + + stella_config_WP_ratio = 1.2D0 + + stella_config_max_force_density = 250.0d0 ! [MN/m^3] + stella_config_max_force_density_MNm = 98.0d0 ! [MN/m] + + + stella_config_max_lateral_force_density = 116.4d0 ! [MN/m^3] + stella_config_max_radial_force_density = 148.d0 ! [MN/m^3] + + stella_config_centering_force_max_MN = 2.99d0 + stella_config_centering_force_min_MN = -1.29d0 + stella_config_centering_force_avg_MN = 1.61d0 + + stella_config_min_plasma_coil_distance = 0.39D0 + stella_config_derivative_min_LCFS_coils_dist = -1.0d0 ! this is approximated for now + + stella_config_min_bend_radius = 0.39d0 ! [m] + + stella_config_neutron_peakfactor = 1.6d0 + + stella_config_epseff = 0.015d0 + + + + case(6) + ! Init from json + ! This requires a file called stella_config.json in the working directory. + ! It can either be prepared manually or it can be produced automatically based on a VMEC netcdf + ! file and a coils file + ! by the pre-sPROCESS Code, https://gitlab.mpcdf.mpg.de/jtl/sprocess/ by jorrit.lion@ipp.mpg.de + call stella_config_json() + case default + ! Return some error here. The index is not implemented yet. + write(*,*)'ERROR in initialization of stellarator config. No such istell: ',index + end select + + end subroutine new_stella_config + + + + subroutine stella_config_json() + + !! Initialises the effective stellarator values using a json input file + !! author: J Lion, IPP Greifswald + !! None + !! This routine reads in all effective variables that + !! are given needed by the 'constructor' stella_config + !!

The effective values are read in from a JSON-format file. + !! None + ! + ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + use fson_library, only: fson_parse, fson_value, fson_get, fson_destroy + use global_variables, only: output_prefix + + + + ! Arguments + + ! Local variables + + integer :: n_values + character(len=180) :: filename + type(fson_value), pointer :: stellafile + + real(dp), dimension(:), allocatable :: nustar,d11,d13 + !type(stella_config), allocatable, dimension(:) :: stella_json + + ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + ! Parse the json file + + filename = trim(output_prefix) // 'stella_conf.json' + stellafile => fson_parse(trim(filename)) + + ! Extract information arrays from the file + + call fson_get(stellafile, "name", stella_config_name) + call fson_get(stellafile, "symmetry", stella_config_symmetry) + + call fson_get(stellafile, "coilspermodule", stella_config_coilspermodule) + call fson_get(stellafile, "rmajor_ref", stella_config_rmajor_ref) + call fson_get(stellafile, "rminor_ref", stella_config_rminor_ref) + call fson_get(stellafile, "coil_rmajor", stella_config_coil_rmajor) + call fson_get(stellafile, "coil_rminor", stella_config_coil_rminor) + call fson_get(stellafile, "aspect_ref", stella_config_aspect_ref) + call fson_get(stellafile, "bt_ref", stella_config_bt_ref) + call fson_get(stellafile, "WP_area", stella_config_WP_area) + call fson_get(stellafile, "WP_bmax", stella_config_WP_bmax) + call fson_get(stellafile, "i0", stella_config_i0) + call fson_get(stellafile, "a1", stella_config_a1) + call fson_get(stellafile, "a2", stella_config_a2) + call fson_get(stellafile, "dmin", stella_config_dmin) + call fson_get(stellafile, "inductance", stella_config_inductance) + call fson_get(stellafile, "coilsurface", stella_config_coilsurface) + call fson_get(stellafile, "coillength", stella_config_coillength) + call fson_get(stellafile, "max_portsize_width", stella_config_max_portsize_width) + call fson_get(stellafile, "maximal_coil_height", stella_config_maximal_coil_height) + call fson_get(stellafile, "min_plasma_coil_distance", stella_config_min_plasma_coil_distance) + call fson_get(stellafile, "derivative_min_LCFS_coils_dist", stella_config_derivative_min_LCFS_coils_dist) + call fson_get(stellafile, "plasma_volume", stella_config_plasma_volume) + call fson_get(stellafile, "plasma_surface", stella_config_plasma_surface) + call fson_get(stellafile, "WP_ratio", stella_config_WP_ratio) + call fson_get(stellafile, "max_force_density", stella_config_max_force_density) + call fson_get(stellafile, "max_force_density_MNm", stella_config_max_force_density_MNm) + call fson_get(stellafile, "min_bend_radius", stella_config_min_bend_radius) + call fson_get(stellafile, "epseff", stella_config_epseff) + + call fson_get(stellafile, "max_lateral_force_density", stella_config_max_lateral_force_density) + call fson_get(stellafile, "max_radial_force_density", stella_config_max_radial_force_density) + + call fson_get(stellafile, "centering_force_max_MN", stella_config_centering_force_max_MN) + call fson_get(stellafile, "centering_force_min_MN", stella_config_centering_force_min_MN) + call fson_get(stellafile, "centering_force_avg_MN", stella_config_centering_force_avg_MN) + + call fson_get(stellafile, "neutron_peakfactor", stella_config_neutron_peakfactor) + + + call fson_get(stellafile, "number_nu_star", n_values) + + if (allocated(sc_D11_star_mono_input)) deallocate(sc_D11_star_mono_input) + if (allocated(sc_nu_star_mono_input)) deallocate(sc_nu_star_mono_input) + allocate(sc_D11_star_mono_input(n_values)) + allocate(sc_nu_star_mono_input(n_values)) + + + call fson_get(stellafile, "D11_star_mono_input", sc_D11_star_mono_input) + call fson_get(stellafile, "nu_star_mono_input", sc_nu_star_mono_input) + + + + + ! Clean up + call fson_destroy(stellafile) + + end subroutine stella_config_json + + + subroutine stella_error(index,keyname) + ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ! Gives the errors of a the stellarator module + + integer, intent(in) :: index + character, intent(in) :: keyname + + + select case (index) + case(1) + ! Error reading in a json attribute + ! Not used yet because I don't know how. + write(*,*)'ERROR in initialization of stellarator config. Missing json key: ',keyname + + + case default + ! Return some error here. The index is not implemented yet. + write(*,*)'ERROR in stella_error! No such error index: ',index + + end select + + + end subroutine stella_error + + + end module stellarator_configuration diff --git a/source/fortran/stellarator_fwbs.f90 b/source/fortran/stellarator_fwbs.f90 new file mode 100644 index 00000000..d103c7cd --- /dev/null +++ b/source/fortran/stellarator_fwbs.f90 @@ -0,0 +1,196 @@ +module fwbs_module + + !! Module containing first wall, blanket and shield routines + !! author: P J Knight, CCFE, Culham Science Centre + !! N/A + !! This module contains routines for calculating the + !! parameters of the first wall, blanket and shield components + !! of a fusion power plant. + + !! AEA FUS 251: A User's Guide to the PROCESS Systems Code + ! + ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +#ifndef dp + use, intrinsic :: iso_fortran_env, only: dp=>real64 +#endif + implicit none + + private + public :: sctfcoil_nuclear_heating_iter90 !, blanket_neutronics + +contains + +! ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + subroutine sctfcoil_nuclear_heating_iter90(coilhtmx,dpacop,htheci,nflutf, & + pheci,pheco,ptfiwp,ptfowp,raddose,ptfnuc) + + !! Superconducting TF coil nuclear heating estimate + !! author: P J Knight, CCFE, Culham Science Centre + !! coilhtmx : output real : peak magnet heating (MW/m3) + !! dpacop : output real : copper stabiliser displacements/atom + !! htheci : output real : peak TF coil case heating (MW/m3) + !! nflutf : output real : maximum neutron fluence (n/m2) + !! pheci : output real : inboard coil case heating (MW) + !! pheco : output real : outboard coil case heating (MW) + !! ptfiwp : output real : inboard TF coil winding pack heating (MW) + !! ptfowp : output real : outboard TF coil winding pack heating (MW) + !! raddose : output real : insulator dose (rad) + !! ptfnuc : output real : TF coil nuclear heating (MW) + !! This subroutine calculates the nuclear heating in the + !! superconducting TF coils, assuming an exponential neutron + !! attenuation through the blanket and shield materials. + !! The estimates are based on 1990 ITER data. + !!

The arrays coef(i,j) and decay(i,j) + !! are used for exponential decay approximations of the + !! (superconducting) TF coil nuclear parameters. + !!

+ !! Note: Costing and mass calculations elsewhere assume + !! stainless steel only. + !! AEA FUS 251: A User's Guide to the PROCESS Systems Code + ! + ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + use build_variables, only: blnkith, blnkoth, fwith, fwoth, shldith, shldoth + use cost_variables, only: cfactr, tlife + use physics_variables, only: wallmw + use tfcoil_variables, only: casthi, i_tf_sup, tfsai, tfsao, dr_tf_wp, & + tinstf + + use maths_library, only: tril + implicit none + + ! Arguments + + real(dp), intent(out) :: coilhtmx,dpacop,htheci,nflutf, & + pheci,pheco,ptfiwp,ptfowp,raddose,ptfnuc + + ! Local variables + + integer, parameter :: ishmat = 1 ! stainless steel coil casing is assumed + + real(dp), dimension(5) :: fact + real(dp), dimension(5,2) :: coef + real(dp), dimension(7,2) :: decay + real(dp) :: dshieq,dshoeq,fpsdt,fpydt,ptfi,ptfo,wpthk + + ! Global shared variables + + ! Input: blnkith,blnkoth,casthi,cfactr,fwith,fwoth,i_tf_sup,shldith + ! Input: shldoth,tfsai,tfsao,dr_tf_wp,tinstf,tlife,wallmw + + ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if (i_tf_sup /= 1) then ! Resistive coils + coilhtmx = 0.0D0 + ptfiwp = 0.0D0 + ptfowp = 0.0D0 + htheci = 0.0D0 + pheci = 0.0D0 + pheco = 0.0D0 + raddose = 0.0D0 + nflutf = 0.0D0 + dpacop = 0.0D0 + + ptfnuc = 0.0D0 + + else + + ! TF coil nuclear heating coefficients in region i (first element), + ! assuming shield material j (second element where present) + + fact(1) = 8.0D0 + fact(2) = 8.0D0 + fact(3) = 6.0D0 + fact(4) = 4.0D0 + fact(5) = 4.0D0 + + coef(1,1) = 10.3D0 + coef(2,1) = 11.6D0 + coef(3,1) = 7.08D5 + coef(4,1) = 2.19D18 + coef(5,1) = 3.33D-7 + coef(1,2) = 8.32D0 + coef(2,2) = 10.6D0 + coef(3,2) = 7.16D5 + coef(4,2) = 2.39D18 + coef(5,2) = 3.84D-7 + + decay(1,1) = 10.05D0 + decay(2,1) = 17.61D0 + decay(3,1) = 13.82D0 + decay(4,1) = 13.24D0 + decay(5,1) = 14.31D0 + decay(6,1) = 13.26D0 + decay(7,1) = 13.25D0 + decay(1,2) = 10.02D0 + decay(2,2) = 3.33D0 + decay(3,2) = 15.45D0 + decay(4,2) = 14.47D0 + decay(5,2) = 15.87D0 + decay(6,2) = 15.25D0 + decay(7,2) = 17.25D0 + + ! N.B. The vacuum vessel appears to be ignored + + dshieq = shldith + fwith + blnkith + dshoeq = shldoth + fwoth + blnkoth + + ! Winding pack radial thickness, including groundwall insulation + + wpthk = dr_tf_wp + 2.0D0*tinstf + + ! Nuclear heating rate in inboard TF coil (MW/m**3) + + coilhtmx = fact(1) * wallmw * coef(1,ishmat) * & + exp(-decay(6,ishmat) * (dshieq + casthi)) + + ! Total nuclear heating (MW) + + ptfiwp = coilhtmx * tfsai * & + (1.0D0-exp(-decay(1,ishmat)*wpthk)) / decay(1,ishmat) + ptfowp = fact(1) * wallmw * coef(1,ishmat) * & + exp(-decay(6,ishmat) * (dshoeq + casthi)) * tfsao * & + (1.0D0 - exp(-decay(1,ishmat)*wpthk)) / decay(1,ishmat) + + ! Nuclear heating in plasma-side TF coil case (MW) + + htheci = fact(2) * wallmw * coef(2,ishmat) * & + exp(-decay(7,ishmat) * dshieq) + pheci = htheci * tfsai * (1.0D0-exp(-decay(2,ishmat)*casthi))/ & + decay(2,ishmat) + pheco = fact(2) * wallmw * coef(2,ishmat) * & + exp(-decay(7,ishmat) * dshoeq) * tfsao * & + (1.0D0-exp(-decay(2,ishmat)*casthi))/decay(2,ishmat) + ptfi = ptfiwp + pheci + ptfo = ptfowp + pheco + + ptfnuc = ptfi + ptfo + + ! Full power DT operation years for replacement of TF Coil + ! (or plant life) + + fpydt = cfactr * tlife + fpsdt = fpydt * 3.154D7 ! seconds + + ! Insulator dose (rad) + + raddose = coef(3,ishmat) * fpsdt * fact(3) * wallmw * & + exp(-decay(3,ishmat) * (dshieq+casthi)) + + ! Maximum neutron fluence in superconductor (n/m**2) + + nflutf = fpsdt * fact(4) * wallmw * coef(4,ishmat) * & + exp(-decay(4,ishmat) * (dshieq+casthi)) + + ! Atomic displacement in copper stabilizer + + dpacop = fpsdt * fact(5) * wallmw * coef(5,ishmat) * & + exp(-decay(5,ishmat) * (dshieq + casthi) ) + + end if + + end subroutine sctfcoil_nuclear_heating_iter90 + + +end module fwbs_module diff --git a/source/fortran/stellarator_variables.f90 b/source/fortran/stellarator_variables.f90 new file mode 100644 index 00000000..3635743a --- /dev/null +++ b/source/fortran/stellarator_variables.f90 @@ -0,0 +1,119 @@ +module stellarator_variables + !! author: S. Muldrew (UKAEA), F. Warmer, J. Lion (IPP Greifswald) + !! + !! Module containing global variables relating to the stellarator model + !! + !!### References + !! + !! - A general stellarator version of the Systems code PROCESS, J. Lion et al, Nucl. Fus. (2021), https://doi.org/10.1088/1741-4326/ac2dbf + !! - Stellarator Divertor Model for the Systems Code PROCESS, F. Warmer, 21/06/2013 + +#ifndef dp + use, intrinsic :: iso_fortran_env, only: dp=>real64 +#endif + + implicit none + + public + + integer :: istell + !! Switch for stellarator option (set via `device.dat`): + !! + !! - =0 use tokamak model + !! - =1 use stellarator model: Helias5 + !! - =2 use stellarator model: Helias4 + !! - =3 use stellarator model: Helias3 + !! - =4 use stellarator model: Wendelstein 7-X with 50 Coils + !! - =5 use stellarator model: Wendelstein 7-X with 30 Coils + !! - =6 use stellarator model: Use stella_conf.json file (any modulear stellarator, see documentation) + + real(dp) :: bmn + !! relative radial field perturbation + + real(dp) :: f_asym + !! divertor heat load peaking factor + + real(dp) :: f_rad + !! radiated power fraction in SOL + + real(dp) :: f_w + !! island size fraction factor + + real(dp) :: fdivwet + !! wetted fraction of the divertor area + + real(dp) :: flpitch + !! field line pitch (rad) + + real(dp) :: hportamax + !! maximum available area for horizontal ports (m2) + + real(dp) :: hportpmax + !! maximum available poloidal extent for horizontal ports (m) + + real(dp) :: hporttmax + !! maximum available toroidal extent for horizontal ports (m) + + real(dp) :: iotabar + !! rotational transform (reciprocal of tokamak q) for stellarator confinement time scaling laws + + integer :: isthtr + !! Switch for stellarator auxiliary heating method: + !! + !! - = 1electron cyclotron resonance heating + !! - = 2lower hybrid heating + !! - = 3neutral beam injection + + integer :: m_res + !! poloidal resonance number (1) + + real(dp) :: max_gyrotron_frequency + !! Maximal available gyrotron frequency (input parameter) (Hz) + + integer :: n_res + !! toroidal resonance number (1) + + real(dp) :: shear + !! magnetic shear, derivative of iotabar (1) + + real(dp) :: te0_ecrh_achievable + !! maximal central electron temperature as achievable by the ECRH, input. (keV) + + real(dp) :: vportamax + !! maximum available area for vertical ports (m2) + + real(dp) :: vportpmax + !! maximum available poloidal extent for vertical ports (m) + + real(dp) :: vporttmax + !! maximum available toroidal extent for vertical ports (m) + + real(dp) :: powerht_constraint + real(dp) :: powerscaling_constraint + + contains + + subroutine init_stellarator_variables + !! Initialise module variables + implicit none + + istell = 0 + bmn = 1.0D-3 + f_asym = 1.0D0 + f_rad = 0.85D0 + f_w = 0.5D0 + fdivwet = 0.333333333333333D0 + flpitch = 1.0D-3 + hportamax = 0.0D0 + hportpmax = 0.0D0 + hporttmax = 0.0D0 + iotabar = 1.0D0 + isthtr = 3 + m_res = 5 + n_res = 5 + shear = 0.5D0 + vportamax = 0.0D0 + vportpmax = 0.0D0 + vporttmax = 0.0D0 + end subroutine init_stellarator_variables +end module stellarator_variables diff --git a/tests/unit/test_stellarator.py b/tests/unit/test_stellarator.py new file mode 100644 index 00000000..4ebee6ad --- /dev/null +++ b/tests/unit/test_stellarator.py @@ -0,0 +1,2767 @@ +import pytest +from typing import NamedTuple, Any +import numpy + +from process.fortran import ( + physics_variables, + stellarator_configuration, + stellarator_module, + build_variables, + fwbs_variables, + heat_transport_variables, + structure_variables, + tfcoil_variables, + impurity_radiation_module, +) +from process.power import Power +from process.stellarator import Stellarator +from process.vacuum import Vacuum +from process.availability import Availability +from process.buildings import Buildings +from process.costs import Costs +from process.plasma_profiles import PlasmaProfile +from process.hcpb import CCFE_HCPB +from process.blanket_library import BlanketLibrary +from process.fw import Fw + + +@pytest.fixture +def stellarator(): + """Provides Stellarator object for testing. + + :returns: initialised Stellarator object + :rtype: process.stellerator.Stellarator + """ + return Stellarator( + Availability(), + Vacuum(), + Buildings(), + Costs(), + Power(), + PlasmaProfile(), + CCFE_HCPB(BlanketLibrary(Fw())), + ) + + +class StgeomParam(NamedTuple): + aspect: Any = None + + rmajor: Any = None + + rminor: Any = None + + sarea: Any = None + + sareao: Any = None + + vol: Any = None + + xarea: Any = None + + bt: Any = None + + stella_config_plasma_volume: Any = None + + stella_config_plasma_surface: Any = None + + f_r: Any = None + + f_a: Any = None + + expected_sarea: Any = None + + expected_sareao: Any = None + + expected_vol: Any = None + + expected_xarea: Any = None + + +@pytest.mark.parametrize( + "stgeomparam", + ( + StgeomParam( + aspect=12.33, + rmajor=22, + rminor=1.7842660178426601, + sarea=0, + sareao=0, + vol=0, + xarea=0, + bt=5.5, + stella_config_plasma_volume=1422.6300000000001, + stella_config_plasma_surface=1960, + f_r=0.99099099099099097, + f_a=0.99125889880147788, + expected_sarea=1925.3641313657533, + expected_sareao=962.68206568287667, + expected_vol=1385.2745877380669, + expected_xarea=10.001590778710231, + ), + StgeomParam( + aspect=12.33, + rmajor=22, + rminor=1.7842660178426601, + sarea=1925.3641313657533, + sareao=962.68206568287667, + vol=1385.2745877380669, + xarea=10.001590778710231, + bt=5.5, + stella_config_plasma_volume=1422.6300000000001, + stella_config_plasma_surface=1960, + f_r=0.99099099099099097, + f_a=0.99125889880147788, + expected_sarea=1925.3641313657533, + expected_sareao=962.68206568287667, + expected_vol=1385.2745877380669, + expected_xarea=10.001590778710231, + ), + ), +) +def test_stgeom(stgeomparam, monkeypatch, stellarator): + """ + Automatically generated Regression Unit Test for stgeom. + + This test was generated using data from tests/regression/scenarios/stellarator/IN.DAT. + + :param stgeomparam: the data used to mock and assert in this test. + :type stgeomparam: stgeomparam + + :param monkeypatch: pytest fixture used to mock module/class variables + :type monkeypatch: _pytest.monkeypatch.monkeypatch + """ + + monkeypatch.setattr(physics_variables, "aspect", stgeomparam.aspect) + + monkeypatch.setattr(physics_variables, "rmajor", stgeomparam.rmajor) + + monkeypatch.setattr(physics_variables, "rminor", stgeomparam.rminor) + + monkeypatch.setattr(physics_variables, "sarea", stgeomparam.sarea) + + monkeypatch.setattr(physics_variables, "sareao", stgeomparam.sareao) + + monkeypatch.setattr(physics_variables, "vol", stgeomparam.vol) + + monkeypatch.setattr(physics_variables, "xarea", stgeomparam.xarea) + + monkeypatch.setattr(physics_variables, "bt", stgeomparam.bt) + + monkeypatch.setattr( + stellarator_configuration, + "stella_config_plasma_volume", + stgeomparam.stella_config_plasma_volume, + ) + + monkeypatch.setattr( + stellarator_configuration, + "stella_config_plasma_surface", + stgeomparam.stella_config_plasma_surface, + ) + + monkeypatch.setattr(stellarator_module, "f_r", stgeomparam.f_r) + + monkeypatch.setattr(stellarator_module, "f_a", stgeomparam.f_a) + + stellarator.stgeom() + + assert physics_variables.sarea == pytest.approx(stgeomparam.expected_sarea) + + assert physics_variables.sareao == pytest.approx(stgeomparam.expected_sareao) + + assert physics_variables.vol == pytest.approx(stgeomparam.expected_vol) + + assert physics_variables.xarea == pytest.approx(stgeomparam.expected_xarea) + + +class StbildParam(NamedTuple): + blbmith: Any = None + + blbmoth: Any = None + + blbpith: Any = None + + blbpoth: Any = None + + blbuith: Any = None + + blbuoth: Any = None + + blnkith: Any = None + + blnkoth: Any = None + + blnktth: Any = None + + bore: Any = None + + d_vv_in: Any = None + + d_vv_out: Any = None + + fwarea: Any = None + + fwith: Any = None + + fwoth: Any = None + + gapds: Any = None + + gapoh: Any = None + + gapomin: Any = None + + gapsto: Any = None + + hmax: Any = None + + ohcth: Any = None + + r_tf_outboard_mid: Any = None + + rbld: Any = None + + rsldi: Any = None + + rsldo: Any = None + + rspo: Any = None + + scrapli: Any = None + + scraplo: Any = None + + shldith: Any = None + + shldoth: Any = None + + shldtth: Any = None + + tfcth: Any = None + + tfthko: Any = None + + available_radial_space: Any = None + + f_avspace: Any = None + + required_radial_space: Any = None + + afw: Any = None + + blktmodel: Any = None + + fdiv: Any = None + + fhcd: Any = None + + fhole: Any = None + + fw_wall: Any = None + + ipowerflow: Any = None + + rmajor: Any = None + + rminor: Any = None + + sarea: Any = None + + stella_config_derivative_min_lcfs_coils_dist: Any = None + + stella_config_rminor_ref: Any = None + + stella_config_min_plasma_coil_distance: Any = None + + f_r: Any = None + + f_aspect: Any = None + + f_a: Any = None + + iprint: Any = None + + outfile: Any = None + + expected_blnktth: Any = None + + expected_bore: Any = None + + expected_fwarea: Any = None + + expected_fwith: Any = None + + expected_fwoth: Any = None + + expected_gapsto: Any = None + + expected_hmax: Any = None + + expected_r_tf_outboard_mid: Any = None + + expected_rbld: Any = None + + expected_rsldi: Any = None + + expected_rsldo: Any = None + + expected_rspo: Any = None + + expected_available_radial_space: Any = None + + expected_required_radial_space: Any = None + + +@pytest.mark.parametrize( + "stbildparam", + ( + StbildParam( + blbmith=0.17000000000000001, + blbmoth=0.27000000000000002, + blbpith=0.29999999999999999, + blbpoth=0.34999999999999998, + blbuith=0.36499999999999999, + blbuoth=0.46500000000000002, + blnkith=0.70000000000000007, + blnkoth=0.80000000000000004, + blnktth=0, + bore=1.4199999999999999, + d_vv_in=0.35000000000000003, + d_vv_out=0.35000000000000003, + fwarea=0, + fwith=0, + fwoth=0, + gapds=0.025000000000000005, + gapoh=0, + gapomin=0.025000000000000005, + gapsto=0, + hmax=6.2927927927927927, + ohcth=0, + r_tf_outboard_mid=0, + rbld=0, + rsldi=0, + rsldo=0, + rspo=0, + scrapli=0.15000000000000002, + scraplo=0.30000000000000004, + shldith=0.40000000000000002, + shldoth=0.70000000000000007, + shldtth=0.70000000000000007, + tfcth=0.78058448071757114, + tfthko=0.78058448071757114, + available_radial_space=0, + f_avspace=1, + required_radial_space=0, + afw=0.0060000000000000001, + blktmodel=0, + fdiv=0.115, + fhcd=0, + fhole=0, + fw_wall=0.0030000000000000001, + ipowerflow=1, + rmajor=22, + rminor=1.7842660178426601, + sarea=1925.3641313657533, + stella_config_derivative_min_lcfs_coils_dist=-1, + stella_config_rminor_ref=1.8, + stella_config_min_plasma_coil_distance=1.8999999999999999, + f_r=0.99099099099099097, + f_aspect=1, + f_a=0.99125889880147788, + iprint=0, + outfile=11, + expected_blnktth=0.75, + expected_bore=17.79214950143977, + expected_fwarea=1918.8188778803135, + expected_fwith=0.018000000000000002, + expected_fwoth=0.018000000000000002, + expected_gapsto=0.025000000000000005, + expected_hmax=3.7022660178426601, + expected_r_tf_outboard_mid=26.367558258201448, + expected_rbld=22, + expected_rsldi=18.947733982157342, + expected_rsldo=25.602266017842663, + expected_rspo=22, + expected_available_radial_space=1.8828828828828827, + expected_required_radial_space=2.0332922403587861, + ), + StbildParam( + blbmith=0.17000000000000001, + blbmoth=0.27000000000000002, + blbpith=0.29999999999999999, + blbpoth=0.34999999999999998, + blbuith=0.36499999999999999, + blbuoth=0.46500000000000002, + blnkith=0.70000000000000007, + blnkoth=0.80000000000000004, + blnktth=0.75, + bore=17.79214950143977, + d_vv_in=0.35000000000000003, + d_vv_out=0.35000000000000003, + fwarea=1918.8188778803135, + fwith=0.018000000000000002, + fwoth=0.018000000000000002, + gapds=0.025000000000000005, + gapoh=0, + gapomin=0.025000000000000005, + gapsto=0.025000000000000005, + hmax=6.2927927927927927, + ohcth=0, + r_tf_outboard_mid=26.367558258201448, + rbld=22, + rsldi=18.947733982157342, + rsldo=25.602266017842663, + rspo=22, + scrapli=0.15000000000000002, + scraplo=0.30000000000000004, + shldith=0.40000000000000002, + shldoth=0.70000000000000007, + shldtth=0.70000000000000007, + tfcth=0.78058448071757114, + tfthko=0.78058448071757114, + available_radial_space=1.8828828828828827, + f_avspace=1, + required_radial_space=2.0332922403587861, + afw=0.0060000000000000001, + blktmodel=0, + fdiv=0.021924555536480182, + fhcd=0, + fhole=0, + fw_wall=0.0030000000000000001, + ipowerflow=1, + rmajor=22, + rminor=1.7842660178426601, + sarea=1925.3641313657533, + stella_config_derivative_min_lcfs_coils_dist=-1, + stella_config_rminor_ref=1.8, + stella_config_min_plasma_coil_distance=1.8999999999999999, + f_r=0.99099099099099097, + f_aspect=1, + f_a=0.99125889880147788, + iprint=0, + outfile=11, + expected_blnktth=0.75, + expected_bore=17.79214950143977, + expected_fwarea=2120.6210472630282, + expected_fwith=0.018000000000000002, + expected_fwoth=0.018000000000000002, + expected_gapsto=0.025000000000000005, + expected_hmax=3.7022660178426601, + expected_r_tf_outboard_mid=26.367558258201448, + expected_rbld=22, + expected_rsldi=18.947733982157342, + expected_rsldo=25.602266017842663, + expected_rspo=22, + expected_available_radial_space=1.8828828828828827, + expected_required_radial_space=2.0332922403587861, + ), + ), +) +def test_stbild(stbildparam, monkeypatch, stellarator): + """ + Automatically generated Regression Unit Test for stbild. + + This test was generated using data from tests/regression/scenarios/stellarator/IN.DAT. + + :param stbildparam: the data used to mock and assert in this test. + :type stbildparam: stbildparam + + :param monkeypatch: pytest fixture used to mock module/class variables + :type monkeypatch: _pytest.monkeypatch.monkeypatch + """ + + monkeypatch.setattr(build_variables, "blbmith", stbildparam.blbmith) + + monkeypatch.setattr(build_variables, "blbmoth", stbildparam.blbmoth) + + monkeypatch.setattr(build_variables, "blbpith", stbildparam.blbpith) + + monkeypatch.setattr(build_variables, "blbpoth", stbildparam.blbpoth) + + monkeypatch.setattr(build_variables, "blbuith", stbildparam.blbuith) + + monkeypatch.setattr(build_variables, "blbuoth", stbildparam.blbuoth) + + monkeypatch.setattr(build_variables, "blnkith", stbildparam.blnkith) + + monkeypatch.setattr(build_variables, "blnkoth", stbildparam.blnkoth) + + monkeypatch.setattr(build_variables, "blnktth", stbildparam.blnktth) + + monkeypatch.setattr(build_variables, "bore", stbildparam.bore) + + monkeypatch.setattr(build_variables, "d_vv_in", stbildparam.d_vv_in) + + monkeypatch.setattr(build_variables, "d_vv_out", stbildparam.d_vv_out) + + monkeypatch.setattr(build_variables, "fwarea", stbildparam.fwarea) + + monkeypatch.setattr(build_variables, "fwith", stbildparam.fwith) + + monkeypatch.setattr(build_variables, "fwoth", stbildparam.fwoth) + + monkeypatch.setattr(build_variables, "gapds", stbildparam.gapds) + + monkeypatch.setattr(build_variables, "gapoh", stbildparam.gapoh) + + monkeypatch.setattr(build_variables, "gapomin", stbildparam.gapomin) + + monkeypatch.setattr(build_variables, "gapsto", stbildparam.gapsto) + + monkeypatch.setattr(build_variables, "hmax", stbildparam.hmax) + + monkeypatch.setattr(build_variables, "ohcth", stbildparam.ohcth) + + monkeypatch.setattr( + build_variables, "r_tf_outboard_mid", stbildparam.r_tf_outboard_mid + ) + + monkeypatch.setattr(build_variables, "rbld", stbildparam.rbld) + + monkeypatch.setattr(build_variables, "rsldi", stbildparam.rsldi) + + monkeypatch.setattr(build_variables, "rsldo", stbildparam.rsldo) + + monkeypatch.setattr(build_variables, "rspo", stbildparam.rspo) + + monkeypatch.setattr(build_variables, "scrapli", stbildparam.scrapli) + + monkeypatch.setattr(build_variables, "scraplo", stbildparam.scraplo) + + monkeypatch.setattr(build_variables, "shldith", stbildparam.shldith) + + monkeypatch.setattr(build_variables, "shldoth", stbildparam.shldoth) + + monkeypatch.setattr(build_variables, "shldtth", stbildparam.shldtth) + + monkeypatch.setattr(build_variables, "tfcth", stbildparam.tfcth) + + monkeypatch.setattr(build_variables, "tfthko", stbildparam.tfthko) + + monkeypatch.setattr( + build_variables, "available_radial_space", stbildparam.available_radial_space + ) + + monkeypatch.setattr(build_variables, "f_avspace", stbildparam.f_avspace) + + monkeypatch.setattr( + build_variables, "required_radial_space", stbildparam.required_radial_space + ) + + monkeypatch.setattr(fwbs_variables, "afw", stbildparam.afw) + + monkeypatch.setattr(fwbs_variables, "blktmodel", stbildparam.blktmodel) + + monkeypatch.setattr(fwbs_variables, "fdiv", stbildparam.fdiv) + + monkeypatch.setattr(fwbs_variables, "fhcd", stbildparam.fhcd) + + monkeypatch.setattr(fwbs_variables, "fhole", stbildparam.fhole) + + monkeypatch.setattr(fwbs_variables, "fw_wall", stbildparam.fw_wall) + + monkeypatch.setattr(heat_transport_variables, "ipowerflow", stbildparam.ipowerflow) + + monkeypatch.setattr(physics_variables, "rmajor", stbildparam.rmajor) + + monkeypatch.setattr(physics_variables, "rminor", stbildparam.rminor) + + monkeypatch.setattr(physics_variables, "sarea", stbildparam.sarea) + + monkeypatch.setattr( + stellarator_configuration, + "stella_config_derivative_min_lcfs_coils_dist", + stbildparam.stella_config_derivative_min_lcfs_coils_dist, + ) + + monkeypatch.setattr( + stellarator_configuration, + "stella_config_rminor_ref", + stbildparam.stella_config_rminor_ref, + ) + + monkeypatch.setattr( + stellarator_configuration, + "stella_config_min_plasma_coil_distance", + stbildparam.stella_config_min_plasma_coil_distance, + ) + + monkeypatch.setattr(stellarator_module, "f_r", stbildparam.f_r) + + monkeypatch.setattr(stellarator_module, "f_aspect", stbildparam.f_aspect) + + monkeypatch.setattr(stellarator_module, "f_a", stbildparam.f_a) + + stellarator.stbild(False) + assert build_variables.blnktth == pytest.approx(stbildparam.expected_blnktth) + + assert build_variables.bore == pytest.approx(stbildparam.expected_bore) + + assert build_variables.fwarea == pytest.approx(stbildparam.expected_fwarea) + + assert build_variables.fwith == pytest.approx(stbildparam.expected_fwith) + + assert build_variables.fwoth == pytest.approx(stbildparam.expected_fwoth) + + assert build_variables.gapsto == pytest.approx(stbildparam.expected_gapsto) + + assert build_variables.hmax == pytest.approx(stbildparam.expected_hmax) + + assert build_variables.r_tf_outboard_mid == pytest.approx( + stbildparam.expected_r_tf_outboard_mid + ) + + assert build_variables.rbld == pytest.approx(stbildparam.expected_rbld) + + assert build_variables.rsldi == pytest.approx(stbildparam.expected_rsldi) + + assert build_variables.rsldo == pytest.approx(stbildparam.expected_rsldo) + + assert build_variables.rspo == pytest.approx(stbildparam.expected_rspo) + + assert build_variables.available_radial_space == pytest.approx( + stbildparam.expected_available_radial_space + ) + + assert build_variables.required_radial_space == pytest.approx( + stbildparam.expected_required_radial_space + ) + + +class StstrcParam(NamedTuple): + dewmkg: Any = None + + denstl: Any = None + + aintmass: Any = None + + clgsmass: Any = None + + coldmass: Any = None + + fncmass: Any = None + + gsmass: Any = None + + whttf: Any = None + + tcritsc: Any = None + + estotftgj: Any = None + + vtfskv: Any = None + + tftort: Any = None + + stella_config_coilsurface: Any = None + + stella_config_coillength: Any = None + + f_n: Any = None + + f_r: Any = None + + f_b: Any = None + + iprint: Any = None + + outfile: Any = None + + expected_aintmass: Any = None + + expected_clgsmass: Any = None + + expected_coldmass: Any = None + + +@pytest.mark.parametrize( + "ststrcparam", + ( + StstrcParam( + dewmkg=0, + denstl=7800, + aintmass=0, + clgsmass=0, + coldmass=0, + fncmass=0, + gsmass=0, + whttf=5204872.8206625767, + tcritsc=16, + estotftgj=132.55990646265246, + vtfskv=4.3242392290600487, + tftort=0.67648706726464258, + stella_config_coilsurface=4817.6999999999998, + stella_config_coillength=1680, + f_n=1, + f_r=0.99099099099099097, + f_b=0.98214285714285721, + iprint=0, + outfile=11, + expected_aintmass=4882304.266547408, + expected_clgsmass=976460.85330948164, + expected_coldmass=10087177.087209985, + ), + StstrcParam( + dewmkg=22397931.480129492, + denstl=7800, + aintmass=4882304.266547408, + clgsmass=976460.85330948164, + coldmass=10087177.087209985, + fncmass=0, + gsmass=0, + whttf=5204872.8206625767, + tcritsc=16, + estotftgj=132.55990646265246, + vtfskv=4.3242392290600487, + tftort=0.67648706726464258, + stella_config_coilsurface=4817.6999999999998, + stella_config_coillength=1680, + f_n=1, + f_r=0.99099099099099097, + f_b=0.98214285714285721, + iprint=0, + outfile=11, + expected_aintmass=4882304.266547408, + expected_clgsmass=976460.85330948164, + expected_coldmass=32485108.567339476, + ), + ), +) +def test_ststrc(ststrcparam, monkeypatch, stellarator): + """ + Automatically generated Regression Unit Test for ststrc. + + This test was generated using data from tests/regression/scenarios/stellarator/IN.DAT. + + :param ststrcparam: the data used to mock and assert in this test. + :type ststrcparam: ststrcparam + + :param monkeypatch: pytest fixture used to mock module/class variables + :type monkeypatch: _pytest.monkeypatch.monkeypatch + """ + + monkeypatch.setattr(fwbs_variables, "dewmkg", ststrcparam.dewmkg) + + monkeypatch.setattr(fwbs_variables, "denstl", ststrcparam.denstl) + + monkeypatch.setattr(structure_variables, "aintmass", ststrcparam.aintmass) + + monkeypatch.setattr(structure_variables, "clgsmass", ststrcparam.clgsmass) + + monkeypatch.setattr(structure_variables, "coldmass", ststrcparam.coldmass) + + monkeypatch.setattr(structure_variables, "fncmass", ststrcparam.fncmass) + + monkeypatch.setattr(structure_variables, "gsmass", ststrcparam.gsmass) + + monkeypatch.setattr(tfcoil_variables, "whttf", ststrcparam.whttf) + + monkeypatch.setattr(tfcoil_variables, "tcritsc", ststrcparam.tcritsc) + + monkeypatch.setattr(tfcoil_variables, "estotftgj", ststrcparam.estotftgj) + + monkeypatch.setattr(tfcoil_variables, "vtfskv", ststrcparam.vtfskv) + + monkeypatch.setattr(tfcoil_variables, "tftort", ststrcparam.tftort) + + monkeypatch.setattr( + stellarator_configuration, + "stella_config_coilsurface", + ststrcparam.stella_config_coilsurface, + ) + + monkeypatch.setattr( + stellarator_configuration, + "stella_config_coillength", + ststrcparam.stella_config_coillength, + ) + + monkeypatch.setattr(stellarator_module, "f_n", ststrcparam.f_n) + + monkeypatch.setattr(stellarator_module, "f_r", ststrcparam.f_r) + + monkeypatch.setattr(stellarator_module, "f_b", ststrcparam.f_b) + + stellarator.ststrc(False) + + assert structure_variables.aintmass == pytest.approx(ststrcparam.expected_aintmass) + + assert structure_variables.clgsmass == pytest.approx(ststrcparam.expected_clgsmass) + + assert structure_variables.coldmass == pytest.approx(ststrcparam.expected_coldmass) + + +def test_u_max_protect_v(stellarator): + assert stellarator.u_max_protect_v( + tfes=2651198129.2530489, tdump=10, aio=122620.32643505408 + ) == pytest.approx(4324.2392290600483) + + +def test_j_max_protect_am2(stellarator): + assert stellarator.j_max_protect_am2( + tau_quench=10, + t_detect=0, + fcu=0.69000000000000017, + fcond=0.69999999999999996, + temp=4.2000000000000002, + acs=0.0022141440000000008, + aturn=0.0031360000000000008, + ) == pytest.approx(54919989.379449144) + + +def test_bmax_from_awp(stellarator, monkeypatch): + monkeypatch.setattr(stellarator_configuration, "stella_config_a1", 0.688) + monkeypatch.setattr(stellarator_configuration, "stella_config_a2", 0.025) + + assert stellarator.bmax_from_awp( + wp_width_radial=0.11792792792792792, + current=12.711229086229087, + n_tf=50, + r_coil_major=22.237837837837837, + r_coil_minor=4.7171171171171169, + ) == pytest.approx(39.193416982177489) + + +class IntersectParam(NamedTuple): + x1: Any = None + + y1: Any = None + + x2: Any = None + + y2: Any = None + + xin: Any = None + + expected_x: Any = None + + +@pytest.mark.parametrize( + "intersectparam", + ( + IntersectParam( + x1=numpy.array( + numpy.array( + ( + 0.11792792792792792, + 0.14103943139119018, + 0.16415093485445242, + 0.18726243831771466, + 0.21037394178097696, + 0.2334854452442392, + 0.25659694870750144, + 0.27970845217076368, + 0.30281995563402597, + 0.32593145909728821, + 0.34904296256055045, + 0.37215446602381275, + 0.39526596948707493, + 0.41837747295033723, + 0.44148897641359952, + 0.46460047987686171, + 0.487711983340124, + 0.51082348680338618, + 0.53393499026664848, + 0.55704649372991077, + 0.58015799719317307, + 0.60326950065643525, + 0.62638100411969755, + 0.64949250758295984, + 0.67260401104622203, + 0.69571551450948421, + 0.71882701797274662, + 0.7419385214360088, + 0.7650500248992711, + 0.78816152836253328, + 0.81127303182579558, + 0.83438453528905787, + 0.85749603875232017, + 0.88060754221558235, + 0.90371904567884453, + 0.92683054914210694, + 0.94994205260536912, + 0.97305355606863142, + 0.9961650595318936, + 1.0192765629951557, + 1.0423880664584182, + 1.0654995699216803, + 1.0886110733849426, + 1.1117225768482049, + 1.1348340803114672, + 1.1579455837747294, + 1.1810570872379917, + 1.2041685907012538, + 1.2272800941645161, + 1.2503915976277784, + 1.2735031010910405, + 1.296614604554303, + 1.3197261080175653, + 1.3428376114808274, + 1.3659491149440897, + 1.389060618407352, + 1.4121721218706142, + 1.4352836253338765, + 1.4583951287971386, + 1.4815066322604009, + 1.5046181357236632, + 1.5277296391869255, + 1.5508411426501878, + 1.5739526461134499, + 1.5970641495767124, + 1.6201756530399747, + 1.6432871565032368, + 1.6663986599664991, + 1.6895101634297611, + 1.7126216668930236, + 1.7357331703562859, + 1.758844673819548, + 1.7819561772828103, + 1.8050676807460724, + 1.8281791842093349, + 1.8512906876725972, + 1.8744021911358593, + 1.8975136945991216, + 1.9206251980623836, + 1.9437367015256461, + 1.9668482049889084, + 1.9899597084521705, + 2.0130712119154328, + 2.0361827153786947, + 2.0592942188419574, + 2.0824057223052197, + 2.1055172257684815, + 2.1286287292317438, + 2.1517402326950061, + 2.1748517361582684, + 2.1979632396215307, + 2.221074743084793, + 2.2441862465480553, + 2.2672977500113176, + 2.2904092534745795, + 2.3135207569378422, + 2.3366322604011041, + 2.3597437638643664, + 2.3828552673276286, + 2.4059667707908909, + 2.4290782742541528, + 2.4521897777174155, + 2.4753012811806778, + 2.4984127846439401, + 2.5215242881072024, + 2.5446357915704643, + 2.5677472950337266, + 2.5908587984969889, + 2.6139703019602512, + 2.6370818054235139, + 2.6601933088867757, + 2.683304812350038, + 2.7064163158133003, + 2.7295278192765626, + 2.7526393227398249, + 2.7757508262030868, + 2.7988623296663491, + 2.8219738331296114, + 2.8450853365928737, + 2.8681968400561364, + 2.8913083435193982, + 2.9144198469826605, + 2.9375313504459228, + 2.9606428539091851, + 2.9837543573724474, + 3.0068658608357097, + 3.0299773642989716, + 3.0530888677622339, + 3.0762003712254966, + 3.0993118746887589, + 3.1224233781520212, + 3.1455348816152831, + 3.1686463850785453, + 3.1917578885418076, + 3.2148693920050699, + 3.2379808954683322, + 3.2610923989315941, + 3.2842039023948564, + 3.3073154058581191, + 3.3304269093213814, + 3.3535384127846437, + 3.3766499162479056, + 3.3997614197111679, + 3.4228729231744301, + 3.4459844266376924, + 3.4690959301009547, + 3.4922074335642166, + 3.5153189370274789, + 3.5384304404907416, + 3.5615419439540039, + 3.5846534474172662, + 3.6077649508805281, + 3.6308764543437904, + 3.6539879578070527, + 3.677099461270315, + 3.7002109647335772, + 3.7233224681968391, + 3.7464339716601018, + 3.7695454751233641, + 3.7926569785866264, + 3.8157684820498887, + 3.8388799855131506, + 3.8619914889764129, + 3.8851029924396752, + 3.9082144959029375, + 3.9313259993661998, + 3.9544375028294616, + 3.9775490062927243, + 4.0006605097559866, + 4.0237720132192489, + 4.0468835166825112, + 4.0699950201457735, + 4.0931065236090358, + 4.1162180270722981, + 4.1393295305355604, + 4.1624410339988227, + 4.185552537462085, + 4.2086640409253473, + 4.2317755443886096, + 4.2548870478518719, + 4.2779985513151342, + 4.3011100547783965, + 4.3242215582416588, + 4.3473330617049211, + 4.3704445651681834, + 4.3935560686314457, + 4.4166675720947079, + 4.4397790755579694, + 4.4628905790212317, + 4.4860020824844939, + 4.5091135859477571, + 4.5322250894110194, + 4.5553365928742808, + 4.5784480963375431, + 4.6015595998008054, + 4.6246711032640677, + 4.64778260672733, + 4.6708941101905923, + 4.6940056136538546, + 4.7171171171171169, + ), + order="F", + ), + order="F", + ).transpose(), + y1=( + 5.0000000000000004e-16, + 5.0000000000000004e-16, + 2.86227506204928e-06, + 3.1894470530728806e-06, + 3.501769913275426e-06, + 3.8002321316979702e-06, + 1.2911174666617371, + 8.5056437504875237, + 20.310566119714178, + 35.08860867240201, + 51.70918610389198, + 69.396748622632828, + 87.620394571705575, + 106.01674628541932, + 124.33769120996891, + 142.41502100154381, + 160.13633420240603, + 177.42845527181157, + 194.24590993730428, + 210.56283761600093, + 226.36726601523148, + 241.65702673605452, + 256.43682253220271, + 270.71611044750449, + 284.50756794064279, + 297.82597881823477, + 310.68742355249833, + 323.10869161811542, + 335.10685659501451, + 346.69897109328275, + 357.90185016614276, + 368.73192020856345, + 379.20511636351574, + 389.33681584460641, + 399.14179779875741, + 408.63422270274015, + 417.82762604428638, + 426.73492234723324, + 435.36841657939482, + 443.73982071745854, + 451.86027379765881, + 459.74036420016017, + 467.39015323269564, + 474.81919932015148, + 482.03658229003128, + 489.05092738310611, + 495.87042872446028, + 502.50287207059142, + 508.95565670912913, + 515.23581643372211, + 521.3500395511744, + 527.30468790362568, + 533.10581490756374, + 538.75918262525659, + 544.2702778940095, + 549.64432754547875, + 554.88631275174816, + 560.0009825376078, + 564.99286649991768, + 569.86628677533872, + 574.62536929745033, + 579.2740543834276, + 583.81610668926919, + 588.25512457110517, + 592.59454888849484, + 596.83767128391446, + 600.98764197086132, + 605.04747706123726, + 609.02006546092423, + 612.90817536075372, + 616.71446034842711, + 620.44146516535295, + 624.09163113086288, + 627.66730125482525, + 631.17072505831391, + 634.60406312072689, + 637.96939137051618, + 641.26870513558185, + 644.50392296830967, + 647.67689025924653, + 650.7893826524753, + 653.84310927488525, + 656.83971579073147, + 659.78078729211506, + 662.66785103531993, + 665.50237903228447, + 668.2857905058803, + 671.01945421709422, + 673.70469067169677, + 676.34277421347099, + 678.93493501062949, + 681.48236094161882, + 683.98619938610727, + 686.4475589265902, + 688.86751096569674, + 691.24709126396783, + 693.58730140256318, + 695.88911017509122, + 698.15345491248911, + 700.3812427446336, + 702.57335180214466, + 704.73063236162898, + 706.85390793741499, + 708.94397632264361, + 711.0016105824119, + 713.0275600015043, + 715.02255098909779, + 716.98728794267436, + 718.92245407327232, + 720.82871219404842, + 722.70670547403643, + 724.55705815886756, + 726.38037626011544, + 728.17724821484387, + 729.94824551683462, + 731.69392332090069, + 733.41482102160455, + 735.11146280763103, + 736.78435819299762, + 738.43400252621257, + 740.06087747844401, + 741.66545151169328, + 743.24818032791723, + 744.80950729999677, + 746.34986388539869, + 747.86967002332915, + 749.36933451614414, + 750.84925539573658, + 752.30982027558071, + 753.75140668908614, + 755.17438241487491, + 756.57910578956728, + 757.96592600862562, + 759.33518341579338, + 760.6872097816165, + 762.02232857153524, + 763.34085520398844, + 764.64309729896991, + 765.92935491743606, + 767.19992079195924, + 768.45508054900256, + 769.69511292315394, + 770.92028996367185, + 772.13087723365243, + 773.32713400212515, + 774.5093134293686, + 775.67766274572489, + 776.83242342417338, + 777.97383134691734, + 779.10211696622468, + 780.21750545975488, + 781.32021688058262, + 782.41046630213918, + 783.48846395826172, + 784.55441537854597, + 785.60852151918687, + 786.65097888947412, + 787.68197967412061, + 788.70171185157085, + 789.71035930845301, + 790.70810195031345, + 791.6951158087769, + 792.67157314526526, + 793.63764255140313, + 794.5934890462313, + 795.53927417035038, + 796.47515607710181, + 797.40128962089454, + 798.31782644278928, + 799.22491505342578, + 800.12270091340042, + 801.01132651117723, + 801.89093143862772, + 802.76165246427263, + 803.62362360431916, + 804.47697619156065, + 805.32183894221828, + 806.15833802079419, + 806.98659710300694, + 807.80673743687316, + 808.61887790199683, + 809.42313506713379, + 810.21962324608114, + 811.00845455195702, + 811.78973894991509, + 812.5635843083561, + 813.33009644867627, + 814.08937919361006, + 814.84153441420722, + 815.58666207549129, + 816.32486028084327, + 817.05622531514791, + 817.78085168675068, + 818.49883216825299, + 819.21025783619109, + 819.91521810963047, + 820.61380078771219, + 821.30609208618284, + 821.99217667293931, + 822.67213770261992, + ), + x2=numpy.array( + numpy.array( + ( + 0.11792792792792792, + 0.14103943139119018, + 0.16415093485445242, + 0.18726243831771466, + 0.21037394178097696, + 0.2334854452442392, + 0.25659694870750144, + 0.27970845217076368, + 0.30281995563402597, + 0.32593145909728821, + 0.34904296256055045, + 0.37215446602381275, + 0.39526596948707493, + 0.41837747295033723, + 0.44148897641359952, + 0.46460047987686171, + 0.487711983340124, + 0.51082348680338618, + 0.53393499026664848, + 0.55704649372991077, + 0.58015799719317307, + 0.60326950065643525, + 0.62638100411969755, + 0.64949250758295984, + 0.67260401104622203, + 0.69571551450948421, + 0.71882701797274662, + 0.7419385214360088, + 0.7650500248992711, + 0.78816152836253328, + 0.81127303182579558, + 0.83438453528905787, + 0.85749603875232017, + 0.88060754221558235, + 0.90371904567884453, + 0.92683054914210694, + 0.94994205260536912, + 0.97305355606863142, + 0.9961650595318936, + 1.0192765629951557, + 1.0423880664584182, + 1.0654995699216803, + 1.0886110733849426, + 1.1117225768482049, + 1.1348340803114672, + 1.1579455837747294, + 1.1810570872379917, + 1.2041685907012538, + 1.2272800941645161, + 1.2503915976277784, + 1.2735031010910405, + 1.296614604554303, + 1.3197261080175653, + 1.3428376114808274, + 1.3659491149440897, + 1.389060618407352, + 1.4121721218706142, + 1.4352836253338765, + 1.4583951287971386, + 1.4815066322604009, + 1.5046181357236632, + 1.5277296391869255, + 1.5508411426501878, + 1.5739526461134499, + 1.5970641495767124, + 1.6201756530399747, + 1.6432871565032368, + 1.6663986599664991, + 1.6895101634297611, + 1.7126216668930236, + 1.7357331703562859, + 1.758844673819548, + 1.7819561772828103, + 1.8050676807460724, + 1.8281791842093349, + 1.8512906876725972, + 1.8744021911358593, + 1.8975136945991216, + 1.9206251980623836, + 1.9437367015256461, + 1.9668482049889084, + 1.9899597084521705, + 2.0130712119154328, + 2.0361827153786947, + 2.0592942188419574, + 2.0824057223052197, + 2.1055172257684815, + 2.1286287292317438, + 2.1517402326950061, + 2.1748517361582684, + 2.1979632396215307, + 2.221074743084793, + 2.2441862465480553, + 2.2672977500113176, + 2.2904092534745795, + 2.3135207569378422, + 2.3366322604011041, + 2.3597437638643664, + 2.3828552673276286, + 2.4059667707908909, + 2.4290782742541528, + 2.4521897777174155, + 2.4753012811806778, + 2.4984127846439401, + 2.5215242881072024, + 2.5446357915704643, + 2.5677472950337266, + 2.5908587984969889, + 2.6139703019602512, + 2.6370818054235139, + 2.6601933088867757, + 2.683304812350038, + 2.7064163158133003, + 2.7295278192765626, + 2.7526393227398249, + 2.7757508262030868, + 2.7988623296663491, + 2.8219738331296114, + 2.8450853365928737, + 2.8681968400561364, + 2.8913083435193982, + 2.9144198469826605, + 2.9375313504459228, + 2.9606428539091851, + 2.9837543573724474, + 3.0068658608357097, + 3.0299773642989716, + 3.0530888677622339, + 3.0762003712254966, + 3.0993118746887589, + 3.1224233781520212, + 3.1455348816152831, + 3.1686463850785453, + 3.1917578885418076, + 3.2148693920050699, + 3.2379808954683322, + 3.2610923989315941, + 3.2842039023948564, + 3.3073154058581191, + 3.3304269093213814, + 3.3535384127846437, + 3.3766499162479056, + 3.3997614197111679, + 3.4228729231744301, + 3.4459844266376924, + 3.4690959301009547, + 3.4922074335642166, + 3.5153189370274789, + 3.5384304404907416, + 3.5615419439540039, + 3.5846534474172662, + 3.6077649508805281, + 3.6308764543437904, + 3.6539879578070527, + 3.677099461270315, + 3.7002109647335772, + 3.7233224681968391, + 3.7464339716601018, + 3.7695454751233641, + 3.7926569785866264, + 3.8157684820498887, + 3.8388799855131506, + 3.8619914889764129, + 3.8851029924396752, + 3.9082144959029375, + 3.9313259993661998, + 3.9544375028294616, + 3.9775490062927243, + 4.0006605097559866, + 4.0237720132192489, + 4.0468835166825112, + 4.0699950201457735, + 4.0931065236090358, + 4.1162180270722981, + 4.1393295305355604, + 4.1624410339988227, + 4.185552537462085, + 4.2086640409253473, + 4.2317755443886096, + 4.2548870478518719, + 4.2779985513151342, + 4.3011100547783965, + 4.3242215582416588, + 4.3473330617049211, + 4.3704445651681834, + 4.3935560686314457, + 4.4166675720947079, + 4.4397790755579694, + 4.4628905790212317, + 4.4860020824844939, + 4.5091135859477571, + 4.5322250894110194, + 4.5553365928742808, + 4.5784480963375431, + 4.6015595998008054, + 4.6246711032640677, + 4.64778260672733, + 4.6708941101905923, + 4.6940056136538546, + 4.7171171171171169, + ), + order="F", + ), + order="F", + ).transpose(), + y2=numpy.array( + numpy.array( + ( + 7158.8937047628706, + 5004.9316715329842, + 3694.8135594405553, + 2839.0817737773841, + 2249.5484991256844, + 1826.2474529253161, + 1512.0852402131027, + 1272.530117074451, + 1085.7010719257139, + 937.18793256963431, + 817.18705296685539, + 718.84090024522891, + 637.23614115501732, + 568.77783627650172, + 510.786630516309, + 461.23254243400288, + 418.55486713593308, + 381.53776842598086, + 349.2227154220239, + 320.84580081746759, + 295.79217667265311, + 273.56246656674404, + 253.74768704408271, + 236.01030089701965, + 220.06974682397441, + 205.69127634981271, + 192.67726151788867, + 180.86036756672672, + 170.09814691732512, + 160.26872610227511, + 151.26734021373531, + 143.00352975000524, + 135.39885901833182, + 128.3850480674335, + 121.90243465834536, + 115.8987012784103, + 110.32781625621425, + 105.14914879151058, + 100.32672600489369, + 95.828606544860236, + 91.626350312843897, + 87.694567812436034, + 84.010535746139567, + 80.553867959068427, + 77.306232806090279, + 74.251109605447908, + 71.373578121094255, + 68.660136052082635, + 66.098540350235496, + 63.677668875882041, + 61.387399466223862, + 59.218503955912581, + 57.162555073740741, + 55.211844458103641, + 53.35931029918256, + 51.598473337326723, + 49.923380132688088, + 48.328552677103559, + 46.808943550648046, + 45.359895936370307, + 43.977107900883965, + 42.656600428510146, + 41.39468876485914, + 40.187956683990443, + 39.033233343175098, + 37.927572432105393, + 36.868233360237589, + 35.852664257722239, + 34.878486592828409, + 33.943481232542041, + 33.045575793648069, + 32.182833149542383, + 31.353440973645949, + 30.555702213931628, + 29.788026404998764, + 29.048921734577537, + 28.336987790509955, + 27.650908922311828, + 26.989448158512342, + 26.351441627222858, + 25.73579343291075, + 25.141470947240073, + 24.567500476169894, + 24.012963269341064, + 23.476991841193676, + 22.958766576292955, + 22.457512594044672, + 21.972496850393419, + 21.503025456251216, + 21.048441194330234, + 20.608121217778795, + 20.181474915566099, + 19.767941930949156, + 19.366990320602941, + 18.978114843116536, + 18.600835366568806, + 18.234695385808415, + 17.879260640884635, + 17.53411782881939, + 17.198873401581842, + 16.873152443735915, + 16.55659762378199, + 16.248868213714225, + 15.949639171768748, + 15.658600283750749, + 15.375455358703663, + 15.099921475025205, + 14.831728273446556, + 14.570617293574582, + 14.316341350956414, + 14.068663951862252, + 13.827358743198703, + 13.592208995163054, + 13.363007114430141, + 13.139554185829615, + 12.921659540623899, + 12.709140349636851, + 12.501821239611756, + 12.299533931295143, + 12.102116897851683, + 11.909415042315253, + 11.721279392873315, + 11.537566814866656, + 11.358139738464688, + 11.182865901048743, + 11.011618103402494, + 10.844273978870199, + 10.680715774700444, + 10.520830144845792, + 10.364507953537444, + 10.211644088999289, + 10.062137286707557, + 9.91588996164114, + 9.7728080490036398, + 9.6328008529317479, + 9.4957809027354614, + 9.3616638162447625, + 9.2303681698639899, + 9.101815374960319, + 8.9759295602358886, + 8.8526374597548152, + 8.7318683063165654, + 8.6135537298858829, + 8.4976276608070904, + 8.3840262375469035, + 8.2726877187252423, + 8.1635523992077417, + 8.0565625300470547, + 7.9516622420725014, + 7.848797472939288, + 7.7479158974594009, + 7.648966861046592, + 7.5519013161173332, + 7.456671761298697, + 7.3632321833024221, + 7.2715380013323854, + 7.1815460139000722, + 7.0932143479295533, + 7.0065024100400572, + 6.9213708399002529, + 6.8377814655542144, + 6.7556972606243528, + 6.6750823033017541, + 6.5959017370391155, + 6.5181217328659393, + 6.4417094532499517, + 6.3666330174326555, + 6.2928614681706652, + 6.2203647398180992, + 6.1491136276885232, + 6.0790797586382022, + 6.0102355628153186, + 5.9425542465226266, + 5.8760097661436923, + 5.8105768030853397, + 5.7462307396912662, + 5.6829476360840934, + 5.6207042078951268, + 5.5594778048431799, + 5.4992463901256654, + 5.439988520586919, + 5.3816833276304639, + 5.3243104988434826, + 5.2678502603032751, + 5.2122833595369489, + 5.157591049106899, + 5.1037550707959838, + 5.0507576403674586, + 4.9985814328759144, + 4.9472095685066089, + 4.8966255989215437, + 4.8468134940916663, + 4.797757629595548, + 4.7494427743657095, + 4.7018540788646925, + 4.6549770636737042, + 4.6087976084774764, + 4.5633019414297058, + 4.5184766288841098, + 4.4743085654767931, + ), + order="F", + ), + order="F", + ).transpose(), + xin=0.22251193896599297, + expected_x=0.624584480717571, + ), + IntersectParam( + x1=numpy.array( + numpy.array( + ( + 0.11792792792792792, + 0.14103943139119018, + 0.16415093485445242, + 0.18726243831771466, + 0.21037394178097696, + 0.2334854452442392, + 0.25659694870750144, + 0.27970845217076368, + 0.30281995563402597, + 0.32593145909728821, + 0.34904296256055045, + 0.37215446602381275, + 0.39526596948707493, + 0.41837747295033723, + 0.44148897641359952, + 0.46460047987686171, + 0.487711983340124, + 0.51082348680338618, + 0.53393499026664848, + 0.55704649372991077, + 0.58015799719317307, + 0.60326950065643525, + 0.62638100411969755, + 0.64949250758295984, + 0.67260401104622203, + 0.69571551450948421, + 0.71882701797274662, + 0.7419385214360088, + 0.7650500248992711, + 0.78816152836253328, + 0.81127303182579558, + 0.83438453528905787, + 0.85749603875232017, + 0.88060754221558235, + 0.90371904567884453, + 0.92683054914210694, + 0.94994205260536912, + 0.97305355606863142, + 0.9961650595318936, + 1.0192765629951557, + 1.0423880664584182, + 1.0654995699216803, + 1.0886110733849426, + 1.1117225768482049, + 1.1348340803114672, + 1.1579455837747294, + 1.1810570872379917, + 1.2041685907012538, + 1.2272800941645161, + 1.2503915976277784, + 1.2735031010910405, + 1.296614604554303, + 1.3197261080175653, + 1.3428376114808274, + 1.3659491149440897, + 1.389060618407352, + 1.4121721218706142, + 1.4352836253338765, + 1.4583951287971386, + 1.4815066322604009, + 1.5046181357236632, + 1.5277296391869255, + 1.5508411426501878, + 1.5739526461134499, + 1.5970641495767124, + 1.6201756530399747, + 1.6432871565032368, + 1.6663986599664991, + 1.6895101634297611, + 1.7126216668930236, + 1.7357331703562859, + 1.758844673819548, + 1.7819561772828103, + 1.8050676807460724, + 1.8281791842093349, + 1.8512906876725972, + 1.8744021911358593, + 1.8975136945991216, + 1.9206251980623836, + 1.9437367015256461, + 1.9668482049889084, + 1.9899597084521705, + 2.0130712119154328, + 2.0361827153786947, + 2.0592942188419574, + 2.0824057223052197, + 2.1055172257684815, + 2.1286287292317438, + 2.1517402326950061, + 2.1748517361582684, + 2.1979632396215307, + 2.221074743084793, + 2.2441862465480553, + 2.2672977500113176, + 2.2904092534745795, + 2.3135207569378422, + 2.3366322604011041, + 2.3597437638643664, + 2.3828552673276286, + 2.4059667707908909, + 2.4290782742541528, + 2.4521897777174155, + 2.4753012811806778, + 2.4984127846439401, + 2.5215242881072024, + 2.5446357915704643, + 2.5677472950337266, + 2.5908587984969889, + 2.6139703019602512, + 2.6370818054235139, + 2.6601933088867757, + 2.683304812350038, + 2.7064163158133003, + 2.7295278192765626, + 2.7526393227398249, + 2.7757508262030868, + 2.7988623296663491, + 2.8219738331296114, + 2.8450853365928737, + 2.8681968400561364, + 2.8913083435193982, + 2.9144198469826605, + 2.9375313504459228, + 2.9606428539091851, + 2.9837543573724474, + 3.0068658608357097, + 3.0299773642989716, + 3.0530888677622339, + 3.0762003712254966, + 3.0993118746887589, + 3.1224233781520212, + 3.1455348816152831, + 3.1686463850785453, + 3.1917578885418076, + 3.2148693920050699, + 3.2379808954683322, + 3.2610923989315941, + 3.2842039023948564, + 3.3073154058581191, + 3.3304269093213814, + 3.3535384127846437, + 3.3766499162479056, + 3.3997614197111679, + 3.4228729231744301, + 3.4459844266376924, + 3.4690959301009547, + 3.4922074335642166, + 3.5153189370274789, + 3.5384304404907416, + 3.5615419439540039, + 3.5846534474172662, + 3.6077649508805281, + 3.6308764543437904, + 3.6539879578070527, + 3.677099461270315, + 3.7002109647335772, + 3.7233224681968391, + 3.7464339716601018, + 3.7695454751233641, + 3.7926569785866264, + 3.8157684820498887, + 3.8388799855131506, + 3.8619914889764129, + 3.8851029924396752, + 3.9082144959029375, + 3.9313259993661998, + 3.9544375028294616, + 3.9775490062927243, + 4.0006605097559866, + 4.0237720132192489, + 4.0468835166825112, + 4.0699950201457735, + 4.0931065236090358, + 4.1162180270722981, + 4.1393295305355604, + 4.1624410339988227, + 4.185552537462085, + 4.2086640409253473, + 4.2317755443886096, + 4.2548870478518719, + 4.2779985513151342, + 4.3011100547783965, + 4.3242215582416588, + 4.3473330617049211, + 4.3704445651681834, + 4.3935560686314457, + 4.4166675720947079, + 4.4397790755579694, + 4.4628905790212317, + 4.4860020824844939, + 4.5091135859477571, + 4.5322250894110194, + 4.5553365928742808, + 4.5784480963375431, + 4.6015595998008054, + 4.6246711032640677, + 4.64778260672733, + 4.6708941101905923, + 4.6940056136538546, + 4.7171171171171169, + ), + order="F", + ), + order="F", + ).transpose(), + y1=( + 5.0000000000000004e-16, + 5.0000000000000004e-16, + 2.86227506204928e-06, + 3.1894470530728806e-06, + 3.501769913275426e-06, + 3.8002321316979702e-06, + 1.2911174666617371, + 8.5056437504875237, + 20.310566119714178, + 35.08860867240201, + 51.70918610389198, + 69.396748622632828, + 87.620394571705575, + 106.01674628541932, + 124.33769120996891, + 142.41502100154381, + 160.13633420240603, + 177.42845527181157, + 194.24590993730428, + 210.56283761600093, + 226.36726601523148, + 241.65702673605452, + 256.43682253220271, + 270.71611044750449, + 284.50756794064279, + 297.82597881823477, + 310.68742355249833, + 323.10869161811542, + 335.10685659501451, + 346.69897109328275, + 357.90185016614276, + 368.73192020856345, + 379.20511636351574, + 389.33681584460641, + 399.14179779875741, + 408.63422270274015, + 417.82762604428638, + 426.73492234723324, + 435.36841657939482, + 443.73982071745854, + 451.86027379765881, + 459.74036420016017, + 467.39015323269564, + 474.81919932015148, + 482.03658229003128, + 489.05092738310611, + 495.87042872446028, + 502.50287207059142, + 508.95565670912913, + 515.23581643372211, + 521.3500395511744, + 527.30468790362568, + 533.10581490756374, + 538.75918262525659, + 544.2702778940095, + 549.64432754547875, + 554.88631275174816, + 560.0009825376078, + 564.99286649991768, + 569.86628677533872, + 574.62536929745033, + 579.2740543834276, + 583.81610668926919, + 588.25512457110517, + 592.59454888849484, + 596.83767128391446, + 600.98764197086132, + 605.04747706123726, + 609.02006546092423, + 612.90817536075372, + 616.71446034842711, + 620.44146516535295, + 624.09163113086288, + 627.66730125482525, + 631.17072505831391, + 634.60406312072689, + 637.96939137051618, + 641.26870513558185, + 644.50392296830967, + 647.67689025924653, + 650.7893826524753, + 653.84310927488525, + 656.83971579073147, + 659.78078729211506, + 662.66785103531993, + 665.50237903228447, + 668.2857905058803, + 671.01945421709422, + 673.70469067169677, + 676.34277421347099, + 678.93493501062949, + 681.48236094161882, + 683.98619938610727, + 686.4475589265902, + 688.86751096569674, + 691.24709126396783, + 693.58730140256318, + 695.88911017509122, + 698.15345491248911, + 700.3812427446336, + 702.57335180214466, + 704.73063236162898, + 706.85390793741499, + 708.94397632264361, + 711.0016105824119, + 713.0275600015043, + 715.02255098909779, + 716.98728794267436, + 718.92245407327232, + 720.82871219404842, + 722.70670547403643, + 724.55705815886756, + 726.38037626011544, + 728.17724821484387, + 729.94824551683462, + 731.69392332090069, + 733.41482102160455, + 735.11146280763103, + 736.78435819299762, + 738.43400252621257, + 740.06087747844401, + 741.66545151169328, + 743.24818032791723, + 744.80950729999677, + 746.34986388539869, + 747.86967002332915, + 749.36933451614414, + 750.84925539573658, + 752.30982027558071, + 753.75140668908614, + 755.17438241487491, + 756.57910578956728, + 757.96592600862562, + 759.33518341579338, + 760.6872097816165, + 762.02232857153524, + 763.34085520398844, + 764.64309729896991, + 765.92935491743606, + 767.19992079195924, + 768.45508054900256, + 769.69511292315394, + 770.92028996367185, + 772.13087723365243, + 773.32713400212515, + 774.5093134293686, + 775.67766274572489, + 776.83242342417338, + 777.97383134691734, + 779.10211696622468, + 780.21750545975488, + 781.32021688058262, + 782.41046630213918, + 783.48846395826172, + 784.55441537854597, + 785.60852151918687, + 786.65097888947412, + 787.68197967412061, + 788.70171185157085, + 789.71035930845301, + 790.70810195031345, + 791.6951158087769, + 792.67157314526526, + 793.63764255140313, + 794.5934890462313, + 795.53927417035038, + 796.47515607710181, + 797.40128962089454, + 798.31782644278928, + 799.22491505342578, + 800.12270091340042, + 801.01132651117723, + 801.89093143862772, + 802.76165246427263, + 803.62362360431916, + 804.47697619156065, + 805.32183894221828, + 806.15833802079419, + 806.98659710300694, + 807.80673743687316, + 808.61887790199683, + 809.42313506713379, + 810.21962324608114, + 811.00845455195702, + 811.78973894991509, + 812.5635843083561, + 813.33009644867627, + 814.08937919361006, + 814.84153441420722, + 815.58666207549129, + 816.32486028084327, + 817.05622531514791, + 817.78085168675068, + 818.49883216825299, + 819.21025783619109, + 819.91521810963047, + 820.61380078771219, + 821.30609208618284, + 821.99217667293931, + 822.67213770261992, + ), + x2=numpy.array( + numpy.array( + ( + 0.11792792792792792, + 0.14103943139119018, + 0.16415093485445242, + 0.18726243831771466, + 0.21037394178097696, + 0.2334854452442392, + 0.25659694870750144, + 0.27970845217076368, + 0.30281995563402597, + 0.32593145909728821, + 0.34904296256055045, + 0.37215446602381275, + 0.39526596948707493, + 0.41837747295033723, + 0.44148897641359952, + 0.46460047987686171, + 0.487711983340124, + 0.51082348680338618, + 0.53393499026664848, + 0.55704649372991077, + 0.58015799719317307, + 0.60326950065643525, + 0.62638100411969755, + 0.64949250758295984, + 0.67260401104622203, + 0.69571551450948421, + 0.71882701797274662, + 0.7419385214360088, + 0.7650500248992711, + 0.78816152836253328, + 0.81127303182579558, + 0.83438453528905787, + 0.85749603875232017, + 0.88060754221558235, + 0.90371904567884453, + 0.92683054914210694, + 0.94994205260536912, + 0.97305355606863142, + 0.9961650595318936, + 1.0192765629951557, + 1.0423880664584182, + 1.0654995699216803, + 1.0886110733849426, + 1.1117225768482049, + 1.1348340803114672, + 1.1579455837747294, + 1.1810570872379917, + 1.2041685907012538, + 1.2272800941645161, + 1.2503915976277784, + 1.2735031010910405, + 1.296614604554303, + 1.3197261080175653, + 1.3428376114808274, + 1.3659491149440897, + 1.389060618407352, + 1.4121721218706142, + 1.4352836253338765, + 1.4583951287971386, + 1.4815066322604009, + 1.5046181357236632, + 1.5277296391869255, + 1.5508411426501878, + 1.5739526461134499, + 1.5970641495767124, + 1.6201756530399747, + 1.6432871565032368, + 1.6663986599664991, + 1.6895101634297611, + 1.7126216668930236, + 1.7357331703562859, + 1.758844673819548, + 1.7819561772828103, + 1.8050676807460724, + 1.8281791842093349, + 1.8512906876725972, + 1.8744021911358593, + 1.8975136945991216, + 1.9206251980623836, + 1.9437367015256461, + 1.9668482049889084, + 1.9899597084521705, + 2.0130712119154328, + 2.0361827153786947, + 2.0592942188419574, + 2.0824057223052197, + 2.1055172257684815, + 2.1286287292317438, + 2.1517402326950061, + 2.1748517361582684, + 2.1979632396215307, + 2.221074743084793, + 2.2441862465480553, + 2.2672977500113176, + 2.2904092534745795, + 2.3135207569378422, + 2.3366322604011041, + 2.3597437638643664, + 2.3828552673276286, + 2.4059667707908909, + 2.4290782742541528, + 2.4521897777174155, + 2.4753012811806778, + 2.4984127846439401, + 2.5215242881072024, + 2.5446357915704643, + 2.5677472950337266, + 2.5908587984969889, + 2.6139703019602512, + 2.6370818054235139, + 2.6601933088867757, + 2.683304812350038, + 2.7064163158133003, + 2.7295278192765626, + 2.7526393227398249, + 2.7757508262030868, + 2.7988623296663491, + 2.8219738331296114, + 2.8450853365928737, + 2.8681968400561364, + 2.8913083435193982, + 2.9144198469826605, + 2.9375313504459228, + 2.9606428539091851, + 2.9837543573724474, + 3.0068658608357097, + 3.0299773642989716, + 3.0530888677622339, + 3.0762003712254966, + 3.0993118746887589, + 3.1224233781520212, + 3.1455348816152831, + 3.1686463850785453, + 3.1917578885418076, + 3.2148693920050699, + 3.2379808954683322, + 3.2610923989315941, + 3.2842039023948564, + 3.3073154058581191, + 3.3304269093213814, + 3.3535384127846437, + 3.3766499162479056, + 3.3997614197111679, + 3.4228729231744301, + 3.4459844266376924, + 3.4690959301009547, + 3.4922074335642166, + 3.5153189370274789, + 3.5384304404907416, + 3.5615419439540039, + 3.5846534474172662, + 3.6077649508805281, + 3.6308764543437904, + 3.6539879578070527, + 3.677099461270315, + 3.7002109647335772, + 3.7233224681968391, + 3.7464339716601018, + 3.7695454751233641, + 3.7926569785866264, + 3.8157684820498887, + 3.8388799855131506, + 3.8619914889764129, + 3.8851029924396752, + 3.9082144959029375, + 3.9313259993661998, + 3.9544375028294616, + 3.9775490062927243, + 4.0006605097559866, + 4.0237720132192489, + 4.0468835166825112, + 4.0699950201457735, + 4.0931065236090358, + 4.1162180270722981, + 4.1393295305355604, + 4.1624410339988227, + 4.185552537462085, + 4.2086640409253473, + 4.2317755443886096, + 4.2548870478518719, + 4.2779985513151342, + 4.3011100547783965, + 4.3242215582416588, + 4.3473330617049211, + 4.3704445651681834, + 4.3935560686314457, + 4.4166675720947079, + 4.4397790755579694, + 4.4628905790212317, + 4.4860020824844939, + 4.5091135859477571, + 4.5322250894110194, + 4.5553365928742808, + 4.5784480963375431, + 4.6015595998008054, + 4.6246711032640677, + 4.64778260672733, + 4.6708941101905923, + 4.6940056136538546, + 4.7171171171171169, + ), + order="F", + ), + order="F", + ).transpose(), + y2=numpy.array( + numpy.array( + ( + 7158.8937047628706, + 5004.9316715329842, + 3694.8135594405553, + 2839.0817737773841, + 2249.5484991256844, + 1826.2474529253161, + 1512.0852402131027, + 1272.530117074451, + 1085.7010719257139, + 937.18793256963431, + 817.18705296685539, + 718.84090024522891, + 637.23614115501732, + 568.77783627650172, + 510.786630516309, + 461.23254243400288, + 418.55486713593308, + 381.53776842598086, + 349.2227154220239, + 320.84580081746759, + 295.79217667265311, + 273.56246656674404, + 253.74768704408271, + 236.01030089701965, + 220.06974682397441, + 205.69127634981271, + 192.67726151788867, + 180.86036756672672, + 170.09814691732512, + 160.26872610227511, + 151.26734021373531, + 143.00352975000524, + 135.39885901833182, + 128.3850480674335, + 121.90243465834536, + 115.8987012784103, + 110.32781625621425, + 105.14914879151058, + 100.32672600489369, + 95.828606544860236, + 91.626350312843897, + 87.694567812436034, + 84.010535746139567, + 80.553867959068427, + 77.306232806090279, + 74.251109605447908, + 71.373578121094255, + 68.660136052082635, + 66.098540350235496, + 63.677668875882041, + 61.387399466223862, + 59.218503955912581, + 57.162555073740741, + 55.211844458103641, + 53.35931029918256, + 51.598473337326723, + 49.923380132688088, + 48.328552677103559, + 46.808943550648046, + 45.359895936370307, + 43.977107900883965, + 42.656600428510146, + 41.39468876485914, + 40.187956683990443, + 39.033233343175098, + 37.927572432105393, + 36.868233360237589, + 35.852664257722239, + 34.878486592828409, + 33.943481232542041, + 33.045575793648069, + 32.182833149542383, + 31.353440973645949, + 30.555702213931628, + 29.788026404998764, + 29.048921734577537, + 28.336987790509955, + 27.650908922311828, + 26.989448158512342, + 26.351441627222858, + 25.73579343291075, + 25.141470947240073, + 24.567500476169894, + 24.012963269341064, + 23.476991841193676, + 22.958766576292955, + 22.457512594044672, + 21.972496850393419, + 21.503025456251216, + 21.048441194330234, + 20.608121217778795, + 20.181474915566099, + 19.767941930949156, + 19.366990320602941, + 18.978114843116536, + 18.600835366568806, + 18.234695385808415, + 17.879260640884635, + 17.53411782881939, + 17.198873401581842, + 16.873152443735915, + 16.55659762378199, + 16.248868213714225, + 15.949639171768748, + 15.658600283750749, + 15.375455358703663, + 15.099921475025205, + 14.831728273446556, + 14.570617293574582, + 14.316341350956414, + 14.068663951862252, + 13.827358743198703, + 13.592208995163054, + 13.363007114430141, + 13.139554185829615, + 12.921659540623899, + 12.709140349636851, + 12.501821239611756, + 12.299533931295143, + 12.102116897851683, + 11.909415042315253, + 11.721279392873315, + 11.537566814866656, + 11.358139738464688, + 11.182865901048743, + 11.011618103402494, + 10.844273978870199, + 10.680715774700444, + 10.520830144845792, + 10.364507953537444, + 10.211644088999289, + 10.062137286707557, + 9.91588996164114, + 9.7728080490036398, + 9.6328008529317479, + 9.4957809027354614, + 9.3616638162447625, + 9.2303681698639899, + 9.101815374960319, + 8.9759295602358886, + 8.8526374597548152, + 8.7318683063165654, + 8.6135537298858829, + 8.4976276608070904, + 8.3840262375469035, + 8.2726877187252423, + 8.1635523992077417, + 8.0565625300470547, + 7.9516622420725014, + 7.848797472939288, + 7.7479158974594009, + 7.648966861046592, + 7.5519013161173332, + 7.456671761298697, + 7.3632321833024221, + 7.2715380013323854, + 7.1815460139000722, + 7.0932143479295533, + 7.0065024100400572, + 6.9213708399002529, + 6.8377814655542144, + 6.7556972606243528, + 6.6750823033017541, + 6.5959017370391155, + 6.5181217328659393, + 6.4417094532499517, + 6.3666330174326555, + 6.2928614681706652, + 6.2203647398180992, + 6.1491136276885232, + 6.0790797586382022, + 6.0102355628153186, + 5.9425542465226266, + 5.8760097661436923, + 5.8105768030853397, + 5.7462307396912662, + 5.6829476360840934, + 5.6207042078951268, + 5.5594778048431799, + 5.4992463901256654, + 5.439988520586919, + 5.3816833276304639, + 5.3243104988434826, + 5.2678502603032751, + 5.2122833595369489, + 5.157591049106899, + 5.1037550707959838, + 5.0507576403674586, + 4.9985814328759144, + 4.9472095685066089, + 4.8966255989215437, + 4.8468134940916663, + 4.797757629595548, + 4.7494427743657095, + 4.7018540788646925, + 4.6549770636737042, + 4.6087976084774764, + 4.5633019414297058, + 4.5184766288841098, + 4.4743085654767931, + ), + order="F", + ), + order="F", + ).transpose(), + xin=0.22251193896599297, + expected_x=0.624584480717571, + ), + ), +) +def test_intersect(intersectparam, stellarator): + """ + Automatically generated Regression Unit Test for intersect. + + This test was generated using data from tests/regression/scenarios/stellarator/IN.DAT. + + :param intersectparam: the data used to mock and assert in this test. + :type intersectparam: intersectparam + """ + + x = stellarator.intersect( + x1=intersectparam.x1, + y1=intersectparam.y1, + x2=intersectparam.x2, + y2=intersectparam.y2, + xin=intersectparam.xin, + ) + + assert x == pytest.approx(intersectparam.expected_x) + + +class StdlimParam(NamedTuple): + dene: Any = None + + dnla: Any = None + + dnelimt: Any = None + + bt: Any = None + + powht: Any = None + + rmajor: Any = None + + rminor: Any = None + + expected_dnelimt: Any = None + + expected_dlimit: Any = None + + +@pytest.mark.parametrize( + "stdlimparam", + ( + StdlimParam( + dene=2.0914e20, + dnla=2.357822619799476e20, + dnelimt=0, + bt=5.5, + powht=432.20449197454559, + rmajor=22, + rminor=1.7842660178426601, + expected_dnelimt=1.2918765671497731e20, + expected_dlimit=1.2918765671497731e20, + ), + StdlimParam( + dene=2.0914e20, + dnla=2.357822619799476e20, + dnelimt=1.2918765671497731e20, + bt=5.5, + powht=431.98698920075435, + rmajor=22, + rminor=1.7842660178426601, + expected_dnelimt=1.2915514639846759e20, + expected_dlimit=1.2915514639846759e20, + ), + ), +) +def test_stdlim(stdlimparam, monkeypatch, stellarator): + """ + Automatically generated Regression Unit Test for stdlim. + + This test was generated using data from tests/regression/scenarios/stellarator/IN.DAT. + + :param stdlimparam: the data used to mock and assert in this test. + :type stdlimparam: stdlimparam + + :param monkeypatch: pytest fixture used to mock module/class variables + :type monkeypatch: _pytest.monkeypatch.monkeypatch + """ + + monkeypatch.setattr(physics_variables, "dene", stdlimparam.dene) + + monkeypatch.setattr(physics_variables, "dnla", stdlimparam.dnla) + + monkeypatch.setattr(physics_variables, "dnelimt", stdlimparam.dnelimt) + + dlimit = stellarator.stdlim( + bt=stdlimparam.bt, + powht=stdlimparam.powht, + rmajor=stdlimparam.rmajor, + rminor=stdlimparam.rminor, + ) + + assert physics_variables.dnelimt == pytest.approx(stdlimparam.expected_dnelimt) + + assert dlimit == pytest.approx(stdlimparam.expected_dlimit) + + +class StdlimEcrhParam(NamedTuple): + ipedestal: Any = None + + bt_input: Any = None + + gyro_frequency_max: Any = None + + expected_dlimit_ecrh: Any = None + + expected_bt_max: Any = None + + +@pytest.mark.parametrize( + "stdlimecrhparam", + ( + StdlimEcrhParam( + ipedestal=0, + bt_input=6.9100000000000001, + gyro_frequency_max=400000000000, + expected_dlimit_ecrh=4.6472737339514113e20, + expected_bt_max=14.279966607226331, + ), + ), +) +def test_stdlim_ecrh(stdlimecrhparam, monkeypatch, stellarator): + """ + Automatically generated Regression Unit Test for stdlim_ecrh. + + This test was generated using data from tests/regression/scenarios/stellarator_config/IN.DAT. + + :param stdlimecrhparam: the data used to mock and assert in this test. + :type stdlimecrhparam: stdlimecrhparam + + :param monkeypatch: pytest fixture used to mock module/class variables + :type monkeypatch: _pytest.monkeypatch.monkeypatch + """ + + monkeypatch.setattr(physics_variables, "ipedestal", stdlimecrhparam.ipedestal) + + dlimit_ecrh, bt_max = stellarator.stdlim_ecrh( + bt_input=stdlimecrhparam.bt_input, + gyro_frequency_max=stdlimecrhparam.gyro_frequency_max, + ) + + assert dlimit_ecrh == pytest.approx(stdlimecrhparam.expected_dlimit_ecrh) + assert bt_max == pytest.approx(stdlimecrhparam.expected_bt_max) + + +class StCalcEffChiParam(NamedTuple): + te0: Any = None + + ne0: Any = None + + falpha: Any = None + + palppv: Any = None + + pcoreradpv: Any = None + + alphan: Any = None + + alphat: Any = None + + vol: Any = None + + sarea: Any = None + + rminor: Any = None + + coreradius: Any = None + + stella_config_rminor_ref: Any = None + + f_r: Any = None + + expected_output: Any = None + + +@pytest.mark.parametrize( + "stcalceffchiparam", + ( + StCalcEffChiParam( + te0=19.108573496973477, + ne0=3.4479000000000007e20, + falpha=0.95000000000000007, + palppv=1.2629524018077414, + pcoreradpv=0.10762698429338043, + alphan=0.35000000000000003, + alphat=1.2, + vol=1385.8142655379029, + sarea=1926.0551116585129, + rminor=1.7863900994187722, + coreradius=0.60000000000000009, + stella_config_rminor_ref=1.80206932, + f_r=0.99129932482229, + expected_output=0.2620230359599852, + # expected_output=0.26206561772729992, used old e_ + ), + StCalcEffChiParam( + te0=17.5, + ne0=3.4479000000000007e20, + falpha=0.95000000000000007, + palppv=1.0570658694225301, + pcoreradpv=0.1002475669217598, + alphan=0.35000000000000003, + alphat=1.2, + vol=1385.8142655379029, + sarea=1926.0551116585129, + rminor=1.7863900994187722, + coreradius=0.60000000000000009, + stella_config_rminor_ref=1.80206932, + f_r=0.99129932482229, + expected_output=0.2368034193234161, + # expected_output=0.23684190261197124, used old e_ + ), + ), +) +def test_st_calc_eff_chi(stcalceffchiparam, monkeypatch, stellarator): + """ + Automatically generated Regression Unit Test for st_calc_eff_chi. + + This test was generated using data from tests/regression/scenarios/stellarator_config/IN.DAT. + + :param stcalceffchiparam: the data used to mock and assert in this test. + :type stcalceffchiparam: stcalceffchiparam + + :param monkeypatch: pytest fixture used to mock module/class variables + :type monkeypatch: _pytest.monkeypatch.monkeypatch + """ + + monkeypatch.setattr(physics_variables, "te0", stcalceffchiparam.te0) + + monkeypatch.setattr(physics_variables, "ne0", stcalceffchiparam.ne0) + + monkeypatch.setattr(physics_variables, "falpha", stcalceffchiparam.falpha) + + monkeypatch.setattr(physics_variables, "palppv", stcalceffchiparam.palppv) + + monkeypatch.setattr(physics_variables, "pcoreradpv", stcalceffchiparam.pcoreradpv) + + monkeypatch.setattr(physics_variables, "alphan", stcalceffchiparam.alphan) + + monkeypatch.setattr(physics_variables, "alphat", stcalceffchiparam.alphat) + + monkeypatch.setattr(physics_variables, "vol", stcalceffchiparam.vol) + + monkeypatch.setattr(physics_variables, "sarea", stcalceffchiparam.sarea) + + monkeypatch.setattr(physics_variables, "rminor", stcalceffchiparam.rminor) + + monkeypatch.setattr( + impurity_radiation_module, "coreradius", stcalceffchiparam.coreradius + ) + + monkeypatch.setattr( + stellarator_configuration, + "stella_config_rminor_ref", + stcalceffchiparam.stella_config_rminor_ref, + ) + + monkeypatch.setattr(stellarator_module, "f_r", stcalceffchiparam.f_r) + + output = stellarator.st_calc_eff_chi() + + assert output == pytest.approx(stcalceffchiparam.expected_output)