diff --git a/CMakeLists.txt b/CMakeLists.txt index 316f46cde4..b439786e81 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,6 @@ LIST(APPEND PROCESS_SRCS numerics.f90 scan.f90 fw.f90 - current_drive.f90 hcpb.f90 pfcoil.f90 reinke_module.f90 diff --git a/process/current_drive.py b/process/current_drive.py new file mode 100644 index 0000000000..34958c0be4 --- /dev/null +++ b/process/current_drive.py @@ -0,0 +1,1988 @@ +import numpy as np + +from process.fortran import ( + heat_transport_variables, + current_drive_variables, + physics_variables, + cost_variables, + constants, + profiles_module, + process_output as po, + error_handling as eh, +) + + +class CurrentDrive: + def __init__(self): + self.outfile = constants.nout + + def cudriv(self, output: bool): + """Routine to calculate the current drive power requirements + 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 power requirements of the current + drive system, using a choice of models for the current drive + efficiency. + AEA FUS 251: A User's Guide to the PROCESS Systems Code + """ + + current_drive_variables.echpwr = 0.0e0 + current_drive_variables.pnbeam = 0.0e0 + current_drive_variables.plhybd = 0.0e0 + current_drive_variables.cnbeam = 0.0e0 + cnbeamfix = 0.0e0 + current_drive_variables.porbitlossmw = 0.0e0 + porbitlossmwfix = 0.0e0 + + pinjmw1 = 0.0 + pinjmwfix = 0.0 + pinjimw1 = 0.0 + pinjemw1 = 0.0 + pinjemwfix = 0.0 + pinjimwfix = 0.0 + auxiliary_cdfix = 0.0 + faccdfix = 0.0 + gamcdfix = 0.0e0 + + # To stop issues with input file we force + # zero secondary heating if no injection method + if current_drive_variables.iefrffix == 0: + current_drive_variables.pheatfix = 0.0 + + # check for unphysically large heating in + # secondary injected power source + if current_drive_variables.pheatfix > current_drive_variables.pinjfixmw: + current_drive_variables.pheatfix = current_drive_variables.pinjfixmw + + # current_drive_variables.irfcd | switch for current drive calculation + # = 0 | turned off + # = 1 | turned on + if current_drive_variables.irfcd != 0: + # put electron density in desired units (10^-20 m-3) + dene20 = physics_variables.dene * 1.0e-20 + + # If present we must calculate second current drive + # efficiencies in units of Amps/Watt using the fixed + # values from user input + # current_drive_variables.iefrffix | switch for fixed current drive efficiency model + + # Fenstermacher Lower Hybrid model + if current_drive_variables.iefrffix == 1: + effrfssfix = ( + (0.36e0 * (1.0e0 + (physics_variables.te / 25.0e0) ** 1.16e0)) + / (physics_variables.rmajor * dene20) + * current_drive_variables.feffcd + ) + effcdfix = effrfssfix + # Ion-Cyclotron current drive + elif current_drive_variables.iefrffix == 2: + effrfssfix = ( + 0.63e0 + * 0.1e0 + * physics_variables.ten + / (2.0e0 + physics_variables.zeff) + / (physics_variables.rmajor * dene20) + * current_drive_variables.feffcd + ) + effcdfix = effrfssfix + # Fenstermacher Electron Cyclotron Resonance model + elif current_drive_variables.iefrffix == 3: + effrfssfix = ( + 0.21e0 + * physics_variables.ten + / (physics_variables.rmajor * dene20 * physics_variables.dlamee) + * current_drive_variables.feffcd + ) + effcdfix = effrfssfix + # Ehst Lower Hybrid / Fast Wave current drive + elif current_drive_variables.iefrffix == 4: + effrfssfix = ( + physics_variables.te**0.77e0 + * (0.034e0 + 0.196e0 * physics_variables.beta) + / (physics_variables.rmajor * dene20) + * ( + 32.0e0 / (5.0e0 + physics_variables.zeff) + + 2.0e0 + + (12.0e0 * (6.0e0 + physics_variables.zeff)) + / (5.0e0 + physics_variables.zeff) + / (3.0e0 + physics_variables.zeff) + + 3.76e0 / physics_variables.zeff + ) + / 12.507e0 + * current_drive_variables.feffcd + ) + effcdfix = effrfssfix + elif current_drive_variables.iefrffix == 5: + ( + effnbss, + current_drive_variables.fpion, + current_drive_variables.nbshinef, + ) = self.iternb() + effnbssfix = effnbss * current_drive_variables.feffcd + effcdfix = effnbssfix + # Culham Lower Hybrid current drive model + elif current_drive_variables.iefrffix == 6: + effrfss = self.cullhy() + effrfssfix = effrfss * current_drive_variables.feffcd + effcdfix = effrfssfix + # Culham ECCD model + elif current_drive_variables.iefrffix == 7: + effrfss = self.culecd() + effrfssfix = effrfss * current_drive_variables.feffcd + effcdfix = effrfssfix + # Culham Neutral Beam model + elif current_drive_variables.iefrffix == 8: + ( + effnbss, + current_drive_variables.fpion, + current_drive_variables.nbshinef, + ) = self.culnbi() + effnbssfix = effnbss * current_drive_variables.feffcd + effcdfix = effnbssfix + # ECRH user input gamma + elif current_drive_variables.iefrffix == 10: + # Normalised current drive efficiency gamma + current_drive_variables.gamcd = current_drive_variables.gamma_ecrh + + # Absolute current drive efficiency + effrfssfix = current_drive_variables.gamcd / ( + dene20 * physics_variables.rmajor + ) + effcdfix = effrfssfix + # EBW scaling + elif current_drive_variables.iefrffix == 12: + # Scaling author Simon Freethy + # Ref : PROCESS issue 1262 + # Normalised current drive efficiency gamma + current_drive_variables.gamcd = ( + current_drive_variables.xi_ebw / 32.7e0 + ) * physics_variables.te + + # Absolute current drive efficiency + effrfssfix = current_drive_variables.gamcd / ( + dene20 * physics_variables.rmajor + ) + effcdfix = effrfssfix + + # EBWs can only couple to plasma if cyclotron harmonic is above plasma density cut-off; + # this behaviour is captured in the following function (ref issue #1262): + # current_drive_variables.harnum = cyclotron harmonic number (fundamental used as default) + # constant 'a' controls sharpness of transition + a = 0.1e0 + + fc = ( + 1.0e0 + / (2.0e0 * np.pi) + * current_drive_variables.harnum + * constants.echarge + * physics_variables.bt + / constants.emass + ) + fp = ( + 1.0e0 + / (2.0e0 * np.pi) + * np.sqrt( + physics_variables.dene + * constants.echarge**2 + / (constants.emass * constants.epsilon0) + ) + ) + + density_factor = 0.5e0 * ( + 1.0e0 + np.tanh((2.0e0 / a) * ((fp - fc) / fp - a)) + ) + + effcdfix = effcdfix * density_factor + + effrfssfix = effrfssfix * density_factor + elif current_drive_variables.iefrffix != 0: + raise RuntimeError( + f"Current drive switch is invalid: {current_drive_variables.iefrffix = }" + ) + + if current_drive_variables.iefrffix in [1, 2, 4, 6]: + # Injected power + pinjemwfix = current_drive_variables.pinjfixmw + + # Wall plug power + heat_transport_variables.pinjwpfix = ( + current_drive_variables.pinjfixmw / current_drive_variables.etalh + ) + + # Wall plug to injector efficiency + current_drive_variables.etacdfix = current_drive_variables.etalh + + # Normalised current drive efficiency gamma + gamcdfix = effrfssfix * (dene20 * physics_variables.rmajor) + + # the fixed auxiliary current + auxiliary_cdfix = ( + effrfssfix + * ( + current_drive_variables.pinjfixmw + - current_drive_variables.pheatfix + ) + * 1.0e6 + ) + faccdfix = auxiliary_cdfix / physics_variables.plascur + elif current_drive_variables.iefrffix in [3, 7, 10, 11, 12]: + # Injected power + pinjemwfix = current_drive_variables.pinjfixmw + + # Wall plug power + heat_transport_variables.pinjwpfix = ( + current_drive_variables.pinjfixmw / current_drive_variables.etaech + ) + + # Wall plug to injector efficiency + current_drive_variables.etacdfix = current_drive_variables.etaech + + # the fixed auxiliary current + auxiliary_cdfix = ( + effrfssfix + * ( + current_drive_variables.pinjfixmw + - current_drive_variables.pheatfix + ) + * 1.0e6 + ) + faccdfix = auxiliary_cdfix / physics_variables.plascur + elif current_drive_variables.iefrffix in [5, 8]: + # Account for first orbit losses + # (power due to particles that are ionised but not thermalised) [MW]: + # This includes a second order term in shinethrough*(first orbit loss) + current_drive_variables.forbitloss = min( + 0.999, current_drive_variables.forbitloss + ) # Should never be needed + + if physics_variables.ipedestal != 3: # When not using PLASMOD + pnbitotfix = current_drive_variables.pinjfixmw / ( + 1.0e0 + - current_drive_variables.forbitloss + + current_drive_variables.forbitloss + * current_drive_variables.nbshinef + ) + else: + # Netural beam power calculated by PLASMOD + pnbitotfix = current_drive_variables.pinjmw / ( + 1.0e0 + - current_drive_variables.forbitloss + + current_drive_variables.forbitloss + * current_drive_variables.nbshinef + ) + + # Shinethrough power (atoms that are not ionised) [MW]: + nbshinemwfix = pnbitotfix * current_drive_variables.nbshinef + + # First orbit loss + porbitlossmwfix = current_drive_variables.forbitloss * ( + pnbitotfix - nbshinemwfix + ) + + # Power deposited + pinjmwfix = pnbitotfix - nbshinemwfix - porbitlossmwfix + pinjimwfix = pinjmwfix * current_drive_variables.fpion + pinjemwfix = pinjmwfix * (1.0e0 - current_drive_variables.fpion) + + current_drive_variables.pwpnb = ( + pnbitotfix / current_drive_variables.etanbi + ) # neutral beam wall plug power + heat_transport_variables.pinjwpfix = current_drive_variables.pwpnb + current_drive_variables.etacdfix = current_drive_variables.etanbi + gamnb = effnbssfix * (dene20 * physics_variables.rmajor) + gamcdfix = gamnb + cnbeamfix = ( + 1.0e-3 * (pnbitotfix * 1.0e6) / current_drive_variables.enbeam + ) # Neutral beam current (A) + auxiliary_cdfix = ( + effnbssfix + * ( + current_drive_variables.pinjfixmw + - current_drive_variables.pheatfix + ) + * 1.0e6 + ) + faccdfix = auxiliary_cdfix / physics_variables.plascur + + # Fenstermacher Lower Hybrid model + if current_drive_variables.iefrf == 1: + effrfss = ( + (0.36e0 * (1.0e0 + (physics_variables.te / 25.0e0) ** 1.16e0)) + / (physics_variables.rmajor * dene20) + * current_drive_variables.feffcd + ) + current_drive_variables.effcd = effrfss + # Ion-Cyclotron current drive + elif current_drive_variables.iefrf == 2: + effrfss = ( + 0.63e0 + * 0.1e0 + * physics_variables.ten + / (2.0e0 + physics_variables.zeff) + / (physics_variables.rmajor * dene20) + * current_drive_variables.feffcd + ) + current_drive_variables.effcd = effrfss + # Fenstermacher Electron Cyclotron Resonance model + elif current_drive_variables.iefrf == 3: + effrfss = ( + 0.21e0 + * physics_variables.ten + / (physics_variables.rmajor * dene20 * physics_variables.dlamee) + * current_drive_variables.feffcd + ) + current_drive_variables.effcd = effrfss + # Ehst Lower Hybrid / Fast Wave current drive + elif current_drive_variables.iefrf == 4: + effrfss = ( + physics_variables.te**0.77e0 + * (0.034e0 + 0.196e0 * physics_variables.beta) + / (physics_variables.rmajor * dene20) + * ( + 32.0e0 / (5.0e0 + physics_variables.zeff) + + 2.0e0 + + (12.0e0 * (6.0e0 + physics_variables.zeff)) + / (5.0e0 + physics_variables.zeff) + / (3.0e0 + physics_variables.zeff) + + 3.76e0 / physics_variables.zeff + ) + / 12.507e0 + * current_drive_variables.feffcd + ) + current_drive_variables.effcd = effrfss + # ITER Neutral Beam current drive + elif current_drive_variables.iefrf == 5: + ( + effnbss, + current_drive_variables.fpion, + current_drive_variables.nbshinef, + ) = self.iternb() + effnbss = effnbss * current_drive_variables.feffcd + current_drive_variables.effcd = effnbss + # Culham Lower Hybrid current drive model + elif current_drive_variables.iefrf == 6: + effrfss = self.cullhy() + effrfss = effrfss * current_drive_variables.feffcd + current_drive_variables.effcd = effrfss + # Culham ECCD model + elif current_drive_variables.iefrf == 7: + effrfss = self.culecd() + effrfss = effrfss * current_drive_variables.feffcd + current_drive_variables.effcd = effrfss + # Culham Neutral Beam model + elif current_drive_variables.iefrf == 8: + ( + effnbss, + current_drive_variables.fpion, + current_drive_variables.nbshinef, + ) = self.culnbi() + effnbss = effnbss * current_drive_variables.feffcd + current_drive_variables.effcd = effnbss + # ECRH user input gamma + elif current_drive_variables.iefrf == 10: + current_drive_variables.gamcd = current_drive_variables.gamma_ecrh + + # Absolute current drive efficiency + effrfss = current_drive_variables.gamcd / ( + dene20 * physics_variables.rmajor + ) + current_drive_variables.effcd = effrfss + # EBW scaling + elif current_drive_variables.iefrf == 12: + # Scaling author Simon Freethy + # Ref : PROCESS issue 1262 + + # Normalised current drive efficiency gamma + current_drive_variables.gamcd = ( + current_drive_variables.xi_ebw / 32.7e0 + ) * physics_variables.te + + # Absolute current drive efficiency + effrfss = current_drive_variables.gamcd / ( + dene20 * physics_variables.rmajor + ) + current_drive_variables.effcd = effrfss + + # EBWs can only couple to plasma if cyclotron harmonic is above plasma density cut-off; + # this behaviour is captured in the following function (ref issue #1262): + # current_drive_variables.harnum = cyclotron harmonic number (fundamental used as default) + # contant 'a' controls sharpness of transition + else: + raise RuntimeError( + f"Current drive switch is invalid: {current_drive_variables.iefrf = }" + ) + + # Compute current drive wall plug and injected powers (MW) and efficiencies + auxiliary_cd = physics_variables.faccd * physics_variables.plascur + + # LHCD or ICCD + if current_drive_variables.iefrf in [1, 2, 4, 6]: + # Injected power + current_drive_variables.plhybd = ( + 1.0e-6 + * (physics_variables.faccd - faccdfix) + * physics_variables.plascur + / effrfss + + current_drive_variables.pheat + ) + pinjimw1 = 0.0e0 + pinjemw1 = current_drive_variables.plhybd + + # Wall plug power + current_drive_variables.pwplh = ( + current_drive_variables.plhybd / current_drive_variables.etalh + ) + pinjwp1 = current_drive_variables.pwplh + + # Wall plug to injector efficiency + current_drive_variables.etacd = current_drive_variables.etalh + + # Normalised current drive efficiency gamma + gamrf = effrfss * (dene20 * physics_variables.rmajor) + current_drive_variables.gamcd = gamrf + # ECCD + elif current_drive_variables.iefrf in [3, 7, 10, 11, 12]: + # Injected power (set to close to close the Steady-state current equilibrium) + current_drive_variables.echpwr = ( + 1.0e-6 + * (physics_variables.faccd - faccdfix) + * physics_variables.plascur + / effrfss + + current_drive_variables.pheat + ) + pinjemw1 = current_drive_variables.echpwr + + # Wall plug power + current_drive_variables.echwpow = ( + current_drive_variables.echpwr / current_drive_variables.etaech + ) + + # Wall plug to injector efficiency + pinjwp1 = current_drive_variables.echwpow + current_drive_variables.etacd = current_drive_variables.etaech + elif current_drive_variables.iefrf in [5, 8]: + # MDK. See Gitlab issue #248, and scanned note. + power1 = ( + 1.0e-6 + * (physics_variables.faccd - faccdfix) + * physics_variables.plascur + / effnbss + + current_drive_variables.pheat + ) + + # Account for first orbit losses + # (power due to particles that are ionised but not thermalised) [MW]: + # This includes a second order term in shinethrough*(first orbit loss) + current_drive_variables.forbitloss = min( + 0.999, current_drive_variables.forbitloss + ) # Should never be needed + + if physics_variables.ipedestal != 3: # When not using PLASMOD + current_drive_variables.pnbitot = power1 / ( + 1.0e0 + - current_drive_variables.forbitloss + + current_drive_variables.forbitloss + * current_drive_variables.nbshinef + ) + else: + # Neutral beam power calculated by PLASMOD + current_drive_variables.pnbitot = current_drive_variables.pinjmw / ( + 1.0e0 + - current_drive_variables.forbitloss + + current_drive_variables.forbitloss + * current_drive_variables.nbshinef + ) + + # Shinethrough power (atoms that are not ionised) [MW]: + current_drive_variables.nbshinemw = ( + current_drive_variables.pnbitot * current_drive_variables.nbshinef + ) + + # First orbit loss + current_drive_variables.porbitlossmw = ( + current_drive_variables.forbitloss + * ( + current_drive_variables.pnbitot + - current_drive_variables.nbshinemw + ) + ) + + # Power deposited + pinjmw1 = ( + current_drive_variables.pnbitot + - current_drive_variables.nbshinemw + - current_drive_variables.porbitlossmw + ) + pinjimw1 = pinjmw1 * current_drive_variables.fpion + pinjemw1 = pinjmw1 * (1.0e0 - current_drive_variables.fpion) + + current_drive_variables.pwpnb = ( + current_drive_variables.pnbitot / current_drive_variables.etanbi + ) # neutral beam wall plug power + pinjwp1 = current_drive_variables.pwpnb + current_drive_variables.etacd = current_drive_variables.etanbi + gamnb = effnbss * (dene20 * physics_variables.rmajor) + current_drive_variables.gamcd = gamnb + current_drive_variables.cnbeam = ( + 1.0e-3 + * (current_drive_variables.pnbitot * 1.0e6) + / current_drive_variables.enbeam + ) # Neutral beam current (A) + + # Total injected power + # sum contributions from primary and secondary systems + current_drive_variables.pinjmw = ( + pinjemw1 + pinjimw1 + pinjemwfix + pinjimwfix + ) + pinjmw1 = pinjemw1 + pinjimw1 + pinjmwfix = pinjemwfix + pinjimwfix + current_drive_variables.pinjemw = pinjemw1 + pinjemwfix + current_drive_variables.pinjimw = pinjimw1 + pinjimwfix + heat_transport_variables.pinjwp = ( + pinjwp1 + heat_transport_variables.pinjwpfix + ) + + # Reset injected power to zero for ignited plasma (fudge) + if physics_variables.ignite == 1: + heat_transport_variables.pinjwp = 0.0e0 + + # Ratio of fusion to input (injection+ohmic) power + if ( + abs( + current_drive_variables.pinjmw + + current_drive_variables.porbitlossmw + + physics_variables.pohmmw + ) + < 1.0e-6 + ): + current_drive_variables.bigq = 1.0e18 + else: + current_drive_variables.bigq = physics_variables.powfmw / ( + current_drive_variables.pinjmw + + current_drive_variables.porbitlossmw + + physics_variables.pohmmw + ) + + if not output: + return + + po.oheadr(self.outfile, "Current Drive System") + + if current_drive_variables.irfcd == 0: + po.ocmmnt(self.outfile, "No current drive used") + po.oblnkl(self.outfile) + return + + if current_drive_variables.iefrf in [1, 4, 6]: + po.ocmmnt(self.outfile, "Lower Hybrid Current Drive") + elif current_drive_variables.iefrf == 2: + po.ocmmnt(self.outfile, "Ion Cyclotron Current Drive") + elif current_drive_variables.iefrf in [3, 7]: + po.ocmmnt(self.outfile, "Electron Cyclotron Current Drive") + elif current_drive_variables.iefrf in [5, 8]: + po.ocmmnt(self.outfile, "Neutral Beam Current Drive") + elif current_drive_variables.iefrf == 10: + po.ocmmnt( + self.outfile, "Electron Cyclotron Current Drive (user input gamma_CD)" + ) + elif current_drive_variables.iefrf == 12: + po.ocmmnt(self.outfile, "EBW current drive") + + po.ovarin( + self.outfile, + "Current drive efficiency model", + "(iefrf)", + current_drive_variables.iefrf, + ) + + if current_drive_variables.iefrffix in [1, 4, 6]: + po.ocmmnt(self.outfile, "Lower Hybrid Current Drive") + elif current_drive_variables.iefrffix == 2: + po.ocmmnt(self.outfile, "Ion Cyclotron Current Drive") + elif current_drive_variables.iefrffix in [3, 7]: + po.ocmmnt(self.outfile, "Electron Cyclotron Current Drive") + elif current_drive_variables.iefrffix in [5, 8]: + po.ocmmnt(self.outfile, "Neutral Beam Current Drive") + elif current_drive_variables.iefrffix == 10: + po.ocmmnt( + self.outfile, "Electron Cyclotron Current Drive (user input gamma_CD)" + ) + elif current_drive_variables.iefrffix == 12: + po.ocmmnt(self.outfile, "EBW current drive") + + po.ovarin( + self.outfile, + "Secondary current drive efficiency model", + "(iefrffix)", + current_drive_variables.iefrffix, + ) + + if physics_variables.ignite == 1: + po.ocmmnt( + self.outfile, + "Ignited plasma; injected power only used for start-up phase", + ) + + po.oblnkl(self.outfile) + + if abs(physics_variables.facoh) > 1.0e-8: + po.ocmmnt(self.outfile, "Current is driven by both inductive") + po.ocmmnt(self.outfile, "and non-inductive means.") + + po.ovarre( + self.outfile, + "Ratio of power for flat-top to start-up (MW)", + "(startupratio)", + cost_variables.startupratio, + ) + po.ovarre( + self.outfile, + "Auxiliary power used for plasma heating only (MW)", + "(pheat)", + current_drive_variables.pheat + current_drive_variables.pheatfix, + ) + po.ovarre( + self.outfile, + "Power injected for current drive (MW)", + "(pcurrentdrivemw)", + current_drive_variables.pinjmw + - current_drive_variables.pheat + - current_drive_variables.pheatfix, + ) + po.ovarre( + self.outfile, + "Maximum Allowed Bootstrap current fraction", + "(bscfmax)", + current_drive_variables.bscfmax, + ) + if current_drive_variables.iefrffix != 0: + po.ovarre( + self.outfile, + "Power injected for main current drive (MW)", + "(pcurrentdrivemw1)", + pinjmw1 - current_drive_variables.pheat, + ) + po.ovarre( + self.outfile, + "Power injected for secondary current drive (MW)", + "(pcurrentdrivemw2)", + pinjmwfix - current_drive_variables.pheatfix, + ) + + po.ovarre( + self.outfile, + "Fusion gain factor Q", + "(bigq)", + current_drive_variables.bigq, + "OP ", + ) + po.ovarre( + self.outfile, + "Auxiliary current drive (A)", + "(auxiliary_cd)", + auxiliary_cd, + "OP ", + ) + if current_drive_variables.iefrffix != 0: + po.ovarre( + self.outfile, + "Secondary auxiliary current drive (A)", + "(auxiliary_cdfix)", + auxiliary_cdfix, + "OP ", + ) + + po.ovarre( + self.outfile, + "Current drive efficiency (A/W)", + "(effcd)", + current_drive_variables.effcd, + "OP ", + ) + po.ovarre( + self.outfile, + "Normalised current drive efficiency, gamma (10^20 A/W-m2)", + "(gamcd)", + current_drive_variables.gamcd, + "OP ", + ) + po.ovarre( + self.outfile, + "Wall plug to injector efficiency", + "(etacd)", + current_drive_variables.etacd, + ) + + if current_drive_variables.iefrf == 10: + po.ovarre( + self.outfile, + "ECRH plasma heating efficiency", + "(gamma_ecrh)", + current_drive_variables.gamma_ecrh, + ) + elif current_drive_variables.iefrf == 12: + po.ovarre( + self.outfile, + "EBW plasma heating efficiency", + "(xi_ebw)", + current_drive_variables.xi_ebw, + ) + + if current_drive_variables.iefrffix != 0: + po.ovarre( + self.outfile, + "Secondary current drive efficiency (A/W)", + "(effcdfix)", + effcdfix, + "OP ", + ) + po.ovarre( + self.outfile, + "Seconday wall plug to injector efficiency", + "(etacdfix)", + current_drive_variables.etacdfix, + ) + po.ovarre( + self.outfile, + "Normalised secondary current drive efficiency, gamma (10^20 A/W-m2)", + "(gamcdfix)", + gamcdfix, + "OP ", + ) + + po.osubhd(self.outfile, "Fractions of current drive :") + po.ovarrf( + self.outfile, + "Bootstrap fraction", + "(bootipf)", + current_drive_variables.bootipf, + "OP ", + ) + po.ovarrf( + self.outfile, + "Diamagnetic fraction", + "(diaipf)", + current_drive_variables.diaipf, + "OP ", + ) + po.ovarrf( + self.outfile, + "Pfirsch-Schlueter fraction", + "(psipf)", + current_drive_variables.psipf, + "OP ", + ) + po.ovarrf( + self.outfile, + "Auxiliary current drive fraction", + "(faccd)", + physics_variables.faccd, + "OP ", + ) + po.ovarrf( + self.outfile, + "Inductive fraction", + "(facoh)", + physics_variables.facoh, + "OP ", + ) + # Add total error check. + po.ovarrf( + self.outfile, + "Total", + "(current_drive_variables.plasipf+physics_variables.faccd+physics_variables.facoh)", + current_drive_variables.plasipf + + physics_variables.faccd + + physics_variables.facoh, + ) + if ( + abs( + current_drive_variables.plasipf + + physics_variables.faccd + + physics_variables.facoh + - 1.0e0 + ) + > 1.0e-8 + ): + po.ocmmnt(self.outfile, "ERROR: current drive fractions do not add to 1") + + # MDK Add physics_variables.fvsbrnni as it can be an iteration variable + po.ovarrf( + self.outfile, + "Fraction of the plasma current produced by non-inductive means", + "(fvsbrnni)", + physics_variables.fvsbrnni, + ) + + if ( + abs(current_drive_variables.bootipf - current_drive_variables.bscfmax) + < 1.0e-8 + ): + po.ocmmnt(self.outfile, "Warning : bootstrap current fraction is at") + po.ocmmnt(self.outfile, " its prescribed maximum.") + + po.oblnkl(self.outfile) + + if abs(current_drive_variables.plhybd) > 1.0e-8: + po.ovarre(self.outfile, "RF efficiency (A/W)", "(effrfss)", effrfss, "OP ") + po.ovarre(self.outfile, "RF gamma (10^20 A/W-m2)", "(gamrf)", gamrf, "OP ") + po.ovarre( + self.outfile, + "Lower hybrid injected power (MW)", + "(plhybd)", + current_drive_variables.plhybd, + "OP ", + ) + po.ovarre( + self.outfile, + "Lower hybrid wall plug efficiency", + "(etalh)", + current_drive_variables.etalh, + ) + po.ovarre( + self.outfile, + "Lower hybrid wall plug power (MW)", + "(pwplh)", + current_drive_variables.pwplh, + "OP ", + ) + + # MDK rearranged and added current_drive_variables.nbshinemw + # if (abs(current_drive_variables.pnbeam) > 1.0e-8) : + if ( + (current_drive_variables.iefrf == 5) + or (current_drive_variables.iefrf == 8) + or (current_drive_variables.iefrffix == 5) + or (current_drive_variables.iefrffix == 8) + ): + po.ovarre( + self.outfile, + "Neutral beam energy (keV)", + "(enbeam)", + current_drive_variables.enbeam, + ) + if (current_drive_variables.iefrf == 5) or ( + current_drive_variables.iefrf == 8 + ): + po.ovarre( + self.outfile, + "Neutral beam current (A)", + "(cnbeam)", + current_drive_variables.cnbeam, + "OP ", + ) + + if (current_drive_variables.iefrffix == 5) or ( + current_drive_variables.iefrffix == 8 + ): + po.ovarre( + self.outfile, + "Secondary fixed neutral beam current (A)", + "(cnbeamfix)", + cnbeamfix, + "OP ", + ) + + if (current_drive_variables.iefrf == 5) or ( + current_drive_variables.iefrf == 8 + ): + po.ovarre( + self.outfile, "Beam efficiency (A/W)", "(effnbss)", effnbss, "OP " + ) + + if (current_drive_variables.iefrffix == 5) or ( + current_drive_variables.iefrffix == 8 + ): + po.ovarre( + self.outfile, + "Secondary fixed beam efficiency (A/W)", + "(effnbssfix)", + effnbssfix, + "OP ", + ) + + po.ovarre( + self.outfile, "Beam gamma (10^20 A/W-m2)", "(gamnb)", gamnb, "OP " + ) + po.ovarre( + self.outfile, + "Neutral beam wall plug efficiency", + "(etanbi)", + current_drive_variables.etanbi, + ) + po.ovarre( + self.outfile, + "Beam decay lengths to centre", + "(taubeam)", + current_drive_variables.taubeam, + "OP ", + ) + po.ovarre( + self.outfile, + "Beam shine-through fraction", + "(nbshinef)", + current_drive_variables.nbshinef, + "OP ", + ) + po.ovarre( + self.outfile, + "Neutral beam wall plug power (MW)", + "(pwpnb)", + current_drive_variables.pwpnb, + "OP ", + ) + + po.oblnkl(self.outfile) + po.ocmmnt(self.outfile, "Neutral beam power balance :") + po.ocmmnt(self.outfile, "----------------------------") + if (current_drive_variables.iefrf == 5) or ( + current_drive_variables.iefrf == 8 + ): + po.ovarrf( + self.outfile, + "Beam first orbit loss power (MW)", + "(porbitlossmw)", + current_drive_variables.porbitlossmw, + "OP ", + ) + po.ovarrf( + self.outfile, + "Beam shine-through power [MW]", + "(nbshinemw)", + current_drive_variables.nbshinemw, + "OP ", + ) + po.ovarrf( + self.outfile, + "Beam power deposited in plasma (MW)", + "(pinjmw)", + pinjmw1, + "OP ", + ) + po.ovarrf( + self.outfile, + "Maximum allowable beam power (MW)", + "(pinjalw)", + current_drive_variables.pinjalw, + ) + po.ovarrf( + self.outfile, + "Total (MW)", + "(current_drive_variables.porbitlossmw+current_drive_variables.nbshinemw+current_drive_variables.pinjmw)", + current_drive_variables.porbitlossmw + + current_drive_variables.nbshinemw + + pinjmw1, + ) + po.oblnkl(self.outfile) + po.ovarrf( + self.outfile, + "Beam power entering vacuum vessel (MW)", + "(pnbitot)", + current_drive_variables.pnbitot, + "OP ", + ) + + if (current_drive_variables.iefrffix == 5) or ( + current_drive_variables.iefrffix == 8 + ): + po.oblnkl(self.outfile) + po.ocmmnt(self.outfile, "Secondary fixed neutral beam power balance :") + po.ocmmnt(self.outfile, "----------------------------") + po.ovarrf( + self.outfile, + "Secondary fixed beam first orbit loss power (MW)", + "(porbitlossmwfix)", + porbitlossmwfix, + "OP ", + ) + po.ovarrf( + self.outfile, + "Secondary fixed beam shine-through power [MW]", + "(nbshinemwfix)", + nbshinemwfix, + "OP ", + ) + po.ovarrf( + self.outfile, + "Secondary fixed beam power deposited in plasma (MW)", + "(pinjmwfix)", + pinjmwfix, + "OP ", + ) + po.ovarrf( + self.outfile, + "Maximum allowable beam power (MW)", + "(pinjalw)", + current_drive_variables.pinjalw, + ) + po.ovarrf( + self.outfile, + "Secondary fixed total (MW)", + "(porbitlossmwfixed+nbshinemwfix+pinjmwfix)", + porbitlossmwfix + nbshinemwfix + pinjmwfix, + ) + po.oblnkl(self.outfile) + po.ovarrf( + self.outfile, + "Secondary beam power entering vacuum vessel (MW)", + "(pnbitotfix)", + pnbitotfix, + "OP ", + ) + + po.oblnkl(self.outfile) + + po.ovarre( + self.outfile, + "Fraction of beam energy to ions", + "(fpion)", + current_drive_variables.fpion, + "OP ", + ) + po.ovarre( + self.outfile, + "Beam duct shielding thickness (m)", + "(nbshield)", + current_drive_variables.nbshield, + ) + po.ovarre( + self.outfile, + "Beam tangency radius / Plasma major radius", + "(frbeam)", + current_drive_variables.frbeam, + ) + po.ovarre( + self.outfile, + "Beam centreline tangency radius (m)", + "(rtanbeam)", + current_drive_variables.rtanbeam, + "OP ", + ) + po.ovarre( + self.outfile, + "Maximum possible tangency radius (m)", + "(rtanmax)", + current_drive_variables.rtanmax, + "OP ", + ) + + if abs(current_drive_variables.echpwr) > 1.0e-8: + po.ovarre( + self.outfile, + "Electron cyclotron injected power (MW)", + "(echpwr)", + current_drive_variables.echpwr, + "OP ", + ) + po.ovarrf( + self.outfile, + "Maximum allowable ECRH power (MW)", + "(pinjalw)", + current_drive_variables.pinjalw, + ) + po.ovarre( + self.outfile, + "ECH wall plug efficiency", + "(etaech)", + current_drive_variables.etaech, + ) + po.ovarre( + self.outfile, + "ECH wall plug power (MW)", + "(echwpow)", + current_drive_variables.echwpow, + "OP ", + ) + + if abs(current_drive_variables.pinjfixmw) > 1.0e-8: + po.ovarrf( + self.outfile, + "Fixed ECRH power (MW)", + "(pinjmwfix)", + current_drive_variables.pinjmwfix, + ) + po.ovarre( + self.outfile, + "ECH wall plug efficiency", + "(etaech)", + current_drive_variables.etaech, + ) + po.ovarre( + self.outfile, + "Secondary fixed ECH wall plug power (MW)", + "(pinjwpfix)", + current_drive_variables.pinjwpfix, + "OP ", + ) + + def iternb(self): + """Routine to calculate ITER Neutral Beam current drive parameters + author: P J Knight, CCFE, Culham Science Centre + effnbss : output real : neutral beam current drive efficiency (A/W) + fpion : output real : fraction of NB power given to ions + fshine : output real : shine-through fraction of beam + This routine calculates the current drive parameters for a + neutral beam system, based on the 1990 ITER model. + AEA FUS 251: A User's Guide to the PROCESS Systems Code + ITER Physics Design Guidelines: 1989 [IPDG89], N. A. Uckan et al, + ITER Documentation Series No.10, IAEA/ITER/DS/10, IAEA, Vienna, 1990 + """ + # Check argument sanity + if (1 + physics_variables.eps) < current_drive_variables.frbeam: + eh.fdiags[0] = physics_variables.eps + eh.fdiags[1] = current_drive_variables.frbeam + eh.report_error(15) + + # Calculate beam path length to centre + dpath = physics_variables.rmajor * np.sqrt( + (1.0 + physics_variables.eps) ** 2 - current_drive_variables.frbeam**2 + ) + + # Calculate beam stopping cross-section + sigstop = self.sigbeam( + current_drive_variables.enbeam / physics_variables.abeam, + physics_variables.te, + physics_variables.dene, + physics_variables.ralpne, + physics_variables.rncne, + physics_variables.rnone, + physics_variables.rnfene, + ) + + # Calculate number of decay lengths to centre + current_drive_variables.taubeam = dpath * physics_variables.dene * sigstop + + # Shine-through fraction of beam + fshine = np.exp(-2.0 * dpath * physics_variables.dene * sigstop) + fshine = max(fshine, 1e-20) + + # Deuterium and tritium beam densities + dend = physics_variables.deni * (1.0 - current_drive_variables.ftritbm) + dent = physics_variables.deni * current_drive_variables.ftritbm + + # Power split to ions / electrons + fpion = self.cfnbi( + physics_variables.abeam, + current_drive_variables.enbeam, + physics_variables.ten, + physics_variables.dene, + dend, + dent, + physics_variables.zeffai, + physics_variables.dlamie, + ) + + # Current drive efficiency + effnbss = current_drive_variables.frbeam * self.etanb( + physics_variables.abeam, + physics_variables.alphan, + physics_variables.alphat, + physics_variables.aspect, + physics_variables.dene, + current_drive_variables.enbeam, + physics_variables.rmajor, + physics_variables.ten, + physics_variables.zeff, + ) + + return effnbss, fpion, fshine + + def cullhy(self): + """Routine to calculate Lower Hybrid current drive efficiency + author: P J Knight, CCFE, Culham Science Centre + effrfss : output real : lower hybrid current drive efficiency (A/W) + This routine calculates the current drive parameters for a + lower hybrid system, based on the AEA FUS 172 model. + AEA FUS 251: A User's Guide to the PROCESS Systems Code + AEA FUS 172: Physics Assessment for the European Reactor Study + """ + rratio = self.lhrad() + rpenet = rratio * physics_variables.rminor + + # Local density, temperature, toroidal field at this minor radius + + dlocal = 1.0e-19 * profiles_module.nprofile( + rratio, + physics_variables.rhopedn, + physics_variables.ne0, + physics_variables.neped, + physics_variables.nesep, + physics_variables.alphan, + ) + tlocal = profiles_module.tprofile( + rratio, + physics_variables.rhopedt, + physics_variables.te0, + physics_variables.teped, + physics_variables.tesep, + physics_variables.alphat, + physics_variables.tbeta, + ) + blocal = ( + physics_variables.bt + * physics_variables.rmajor + / (physics_variables.rmajor - rpenet) + ) # Calculated on inboard side + + # Parallel refractive index needed for plasma access + + frac = np.sqrt(dlocal) / blocal + nplacc = frac + np.sqrt(1.0e0 + frac * frac) + + # Local inverse aspect ratio + + epslh = rpenet / physics_variables.rmajor + + # LH normalised efficiency (A/W m**-2) + + x = 24.0e0 / (nplacc * np.sqrt(tlocal)) + + term01 = 6.1e0 / (nplacc * nplacc * (physics_variables.zeff + 5.0e0)) + term02 = 1.0e0 + (tlocal / 25.0e0) ** 1.16e0 + term03 = epslh**0.77e0 * np.sqrt(12.25e0 + x * x) + term04 = 3.5e0 * epslh**0.77e0 + x + + if term03 > term04: + eh.fdiags[0] = term03 + eh.fdiags[1] = term04 + eh.report_error(129) + + gamlh = term01 * term02 * (1.0e0 - term03 / term04) + + # Current drive efficiency (A/W) + + return gamlh / ((0.1e0 * dlocal) * physics_variables.rmajor) + + def culecd(self): + """Routine to calculate Electron Cyclotron current drive efficiency + author: M R O'Brien, CCFE, Culham Science Centre + author: P J Knight, CCFE, Culham Science Centre + effrfss : output real : electron cyclotron current drive efficiency (A/W) + This routine calculates the current drive parameters for a + electron cyclotron system, based on the AEA FUS 172 model. + AEA FUS 251: A User's Guide to the PROCESS Systems Code + AEA FUS 172: Physics Assessment for the European Reactor Study + """ + rrr = 1.0e0 / 3.0e0 + + # Temperature + tlocal = profiles_module.tprofile( + rrr, + physics_variables.rhopedt, + physics_variables.te0, + physics_variables.teped, + physics_variables.tesep, + physics_variables.alphat, + physics_variables.tbeta, + ) + + # Density (10**20 m**-3) + dlocal = 1.0e-20 * profiles_module.nprofile( + rrr, + physics_variables.rhopedn, + physics_variables.ne0, + physics_variables.neped, + physics_variables.nesep, + physics_variables.alphan, + ) + + # Inverse aspect ratio + epsloc = rrr * physics_variables.rminor / physics_variables.rmajor + + # Effective charge (use average value) + zlocal = physics_variables.zeff + + # Coulomb logarithm for ion-electron collisions + # (From J. A. Wesson, 'Tokamaks', Clarendon Press, Oxford, p.293) + coulog = 15.2e0 - 0.5e0 * np.log(dlocal) + np.log(tlocal) + + # Calculate normalised current drive efficiency at four different + # poloidal angles, and average. + # cosang = cosine of the poloidal angle at which ECCD takes place + # = +1 outside, -1 inside. + cosang = 1.0e0 + ecgam1 = self.eccdef(tlocal, epsloc, zlocal, cosang, coulog) + cosang = 0.5e0 + ecgam2 = self.eccdef(tlocal, epsloc, zlocal, cosang, coulog) + cosang = -0.5e0 + ecgam3 = self.eccdef(tlocal, epsloc, zlocal, cosang, coulog) + cosang = -1.0e0 + ecgam4 = self.eccdef(tlocal, epsloc, zlocal, cosang, coulog) + + # Normalised current drive efficiency (A/W m**-2) + ecgam = 0.25e0 * (ecgam1 + ecgam2 + ecgam3 + ecgam4) + + # Current drive efficiency (A/W) + return ecgam / (dlocal * physics_variables.rmajor) + + def eccdef(self, tlocal, epsloc, zlocal, cosang, coulog): + """Routine to calculate Electron Cyclotron current drive efficiency + author: M R O'Brien, CCFE, Culham Science Centre + author: P J Knight, CCFE, Culham Science Centre + tlocal : input real : local electron temperature (keV) + epsloc : input real : local inverse aspect ratio + zlocal : input real : local plasma effective charge + cosang : input real : cosine of the poloidal angle at which ECCD takes + place (+1 outside, -1 inside) + coulog : input real : local coulomb logarithm for ion-electron collisions + ecgam : output real : normalised current drive efficiency (A/W m**-2) + This routine calculates the current drive parameters for a + electron cyclotron system, based on the AEA FUS 172 model. + It works out the ECCD efficiency using the formula due to Cohen + quoted in the ITER Physics Design Guidelines : 1989 + (but including division by the Coulomb Logarithm omitted from + IPDG89). We have assumed gamma**2-1 << 1, where gamma is the + relativistic factor. The notation follows that in IPDG89. +
The answer ECGAM is the normalised efficiency nIR/P with n the + local density in 10**20 /m**3, I the driven current in MAmps, + R the major radius in metres, and P the absorbed power in MWatts. + AEA FUS 251: A User's Guide to the PROCESS Systems Code + AEA FUS 172: Physics Assessment for the European Reactor Study + ITER Physics Design Guidelines: 1989 [IPDG89], N. A. Uckan et al, + ITER Documentation Series No.10, IAEA/ITER/DS/10, IAEA, Vienna, 1990 + """ + mcsq = 9.1095e-31 * 2.9979e8**2 / (1.0e3 * 1.6022e-19) # keV + f = 16.0e0 * (tlocal / mcsq) ** 2 + + # fp is the derivative of f with respect to gamma, the relativistic + # factor, taken equal to 1 + 2T/(m c**2) + + fp = 16.0e0 * tlocal / mcsq + + # lam is IPDG89's lambda. LEGEND calculates the Legendre function of + # order alpha and argument lam, palpha, and its derivative, palphap. + # Here alpha satisfies alpha(alpha+1) = -8/(1+zlocal). alpha is of the + # form (-1/2 + ix), with x a real number and i = sqrt(-1). + + lam = 1.0e0 + palpha, palphap = self.legend(zlocal, lam) + + lams = np.sqrt(2.0e0 * epsloc / (1.0e0 + epsloc)) + palphas, _ = self.legend(zlocal, lams) + + # hp is the derivative of IPDG89's h function with respect to lam + + h = -4.0e0 * lam / (zlocal + 5.0e0) * (1.0e0 - lams * palpha / (lam * palphas)) + hp = -4.0e0 / (zlocal + 5.0e0) * (1.0e0 - lams * palphap / palphas) + + # facm is IPDG89's momentum conserving factor + + facm = 1.5e0 + y = mcsq / (2.0e0 * tlocal) * (1.0e0 + epsloc * cosang) + + # We take the negative of the IPDG89 expression to get a positive + # number + + ecgam = ( + -7.8e0 + * facm + * np.sqrt((1.0e0 + epsloc) / (1.0e0 - epsloc)) + / coulog + * (h * fp - 0.5e0 * y * f * hp) + ) + + if ecgam < 0.0e0: + eh.report_error(17) + return ecgam + + def culnbi(self): + """Routine to calculate Neutral Beam current drive parameters + author: P J Knight, CCFE, Culham Science Centre + effnbss : output real : neutral beam current drive efficiency (A/W) + fpion : output real : fraction of NB power given to ions + fshine : output real : shine-through fraction of beam + This routine calculates Neutral Beam current drive parameters + using the corrections outlined in AEA FUS 172 to the ITER method. +
The result cannot be guaranteed for devices with aspect ratios far + from that of ITER (approx. 2.8). + AEA FUS 251: A User's Guide to the PROCESS Systems Code + AEA FUS 172: Physics Assessment for the European Reactor Study + """ + if (1.0e0 + physics_variables.eps) < current_drive_variables.frbeam: + eh.fdiags[0] = physics_variables.eps + eh.fdiags[1] = current_drive_variables.frbeam + eh.report_error(20) + + # Calculate beam path length to centre + + dpath = physics_variables.rmajor * np.sqrt( + (1.0e0 + physics_variables.eps) ** 2 - current_drive_variables.frbeam**2 + ) + + # Calculate beam stopping cross-section + + sigstop = self.sigbeam( + current_drive_variables.enbeam / physics_variables.abeam, + physics_variables.te, + physics_variables.dene, + physics_variables.ralpne, + physics_variables.rncne, + physics_variables.rnone, + physics_variables.rnfene, + ) + + # Calculate number of decay lengths to centre + + current_drive_variables.taubeam = dpath * physics_variables.dnla * sigstop + + # Shine-through fraction of beam + + fshine = np.exp(-2.0e0 * dpath * physics_variables.dnla * sigstop) + fshine = max(fshine, 1.0e-20) + + # Deuterium and tritium beam densities + + dend = physics_variables.deni * (1.0e0 - current_drive_variables.ftritbm) + dent = physics_variables.deni * current_drive_variables.ftritbm + + # Power split to ions / electrons + + fpion = self.cfnbi( + physics_variables.abeam, + current_drive_variables.enbeam, + physics_variables.ten, + physics_variables.dene, + dend, + dent, + physics_variables.zeffai, + physics_variables.dlamie, + ) + + # Current drive efficiency + + effnbss = self.etanb2( + physics_variables.abeam, + physics_variables.alphan, + physics_variables.alphat, + physics_variables.aspect, + physics_variables.dene, + physics_variables.dnla, + current_drive_variables.enbeam, + current_drive_variables.frbeam, + fshine, + physics_variables.rmajor, + physics_variables.rminor, + physics_variables.ten, + physics_variables.zeff, + ) + + return effnbss, fpion, fshine + + def lhrad(self): + """Routine to calculate Lower Hybrid wave absorption radius + author: P J Knight, CCFE, Culham Science Centre + rratio : output real : minor radius of penetration / rminor + This routine determines numerically the minor radius at which the + damping of Lower Hybrid waves occurs, using a Newton-Raphson method. + AEA FUS 251: A User's Guide to the PROCESS Systems Code + AEA FUS 172: Physics Assessment for the European Reactor Study + """ + # Correction to refractive index (kept within valid bounds) + drfind = min(0.7e0, max(0.1e0, 12.5e0 / physics_variables.te0)) + + # Use Newton-Raphson method to establish the correct minor radius + # ratio. g is calculated as a function of r / r_minor, where g is + # the difference between the results of the two formulae for the + # energy E given in AEA FUS 172, p.58. The required minor radius + # ratio has been found when g is sufficiently close to zero. + + # Initial guess for the minor radius ratio + + rat0 = 0.8e0 + + for _ in range(100): + # Minor radius ratios either side of the latest guess + + r1 = rat0 - 1.0e-3 * rat0 + r2 = rat0 + 1.0e-3 * rat0 + + # Evaluate g at rat0, r1, r2 + + g0 = self.lheval(drfind, rat0) + g1 = self.lheval(drfind, r1) + g2 = self.lheval(drfind, r2) + + # Calculate gradient of g with respect to minor radius ratio + + dgdr = (g2 - g1) / (r2 - r1) + + # New approximation + + rat1 = rat0 - g0 / dgdr + + # Force this approximation to lie within bounds + + rat1 = max(0.0001e0, rat1) + rat1 = min(0.9999e0, rat1) + + if abs(g0) <= 0.01e0: + break + rat0 = rat1 + + else: + eh.report_error(16) + rat0 = 0.8e0 + + return rat0 + + def etanb2( + self, + abeam, + alphan, + alphat, + aspect, + dene, + dnla, + enbeam, + frbeam, + fshine, + rmajor, + rminor, + ten, + zeff, + ): + """Routine to find neutral beam current drive efficiency + using the ITER 1990 formulation, plus correction terms + outlined in Culham Report AEA FUS 172 + author: P J Knight, CCFE, Culham Science Centre + abeam : input real : beam ion mass (amu) + alphan : input real : density profile factor + alphat : input real : temperature profile factor + aspect : input real : aspect ratio + dene : input real : volume averaged electron density (m**-3) + dnla : input real : line averaged electron density (m**-3) + enbeam : input real : neutral beam energy (keV) + frbeam : input real : R_tangent / R_major for neutral beam injection + fshine : input real : shine-through fraction of beam + rmajor : input real : plasma major radius (m) + rminor : input real : plasma minor radius (m) + ten : input real : density weighted average electron temperature (keV) + zeff : input real : plasma effective charge + This routine calculates the current drive efficiency in A/W of + a neutral beam system, based on the 1990 ITER model, + plus correction terms outlined in Culham Report AEA FUS 172. +
The formulae are from AEA FUS 172, unless denoted by IPDG89.
+ AEA FUS 251: A User's Guide to the PROCESS Systems Code
+ AEA FUS 172: Physics Assessment for the European Reactor Study
+ ITER Physics Design Guidelines: 1989 [IPDG89], N. A. Uckan et al,
+ ITER Documentation Series No.10, IAEA/ITER/DS/10, IAEA, Vienna, 1990
+ """
+ # Charge of beam ions
+ zbeam = 1.0
+
+ # Fitting factor (IPDG89)
+ bbd = 1.0
+
+ # Volume averaged electron density (10**20 m**-3)
+ dene20 = dene / 1e20
+
+ # Line averaged electron density (10**20 m**-3)
+ dnla20 = dnla / 1e20
+
+ # Critical energy (MeV) (power to electrons = power to ions) (IPDG89)
+ # N.B. ten is in keV
+ ecrit = 0.01 * abeam * ten
+
+ # Beam energy in MeV
+ ebmev = enbeam / 1e3
+
+ # x and y coefficients of function J0(x,y) (IPDG89)
+ xjs = ebmev / (bbd * ecrit)
+ xj = np.sqrt(xjs)
+
+ yj = 0.8 * zeff / abeam
+
+ # Fitting function J0(x,y)
+ j0 = xjs / (4.0 + 3.0 * yj + xjs * (xj + 1.39 + 0.61 * yj**0.7))
+
+ # Effective inverse aspect ratio, with a limit on its maximum value
+ epseff = min(0.2, (0.5 / aspect))
+
+ # Reduction in the reverse electron current
+ # due to neoclassical effects
+ gfac = (1.55 + 0.85 / zeff) * np.sqrt(epseff) - (0.2 + 1.55 / zeff) * epseff
+
+ # Reduction in the net beam driven current
+ # due to the reverse electron current
+ ffac = 1.0 - (zbeam / zeff) * (1.0 - gfac)
+
+ # Normalisation to allow results to be valid for
+ # non-ITER plasma size and density:
+
+ # Line averaged electron density (10**20 m**-3) normalised to ITER
+ nnorm = 1.0
+
+ # Distance along beam to plasma centre
+ r = max(rmajor, rmajor * frbeam)
+ eps1 = rminor / r
+
+ if (1.0 + eps1) < frbeam:
+ eh.fdiags[0] = eps1
+ eh.fdiags[1] = frbeam
+ eh.report_error(21)
+
+ d = rmajor * np.sqrt((1.0 + eps1) ** 2 - frbeam**2)
+
+ # Distance along beam to plasma centre for ITER
+ # assuming a tangency radius equal to the major radius
+ epsitr = 2.15 / 6.0
+ dnorm = 6.0 * np.sqrt(2.0 * epsitr + epsitr**2)
+
+ # Normalisation to beam energy (assumes a simplified formula for
+ # the beam stopping cross-section)
+ ebnorm = ebmev * ((nnorm * dnorm) / (dnla20 * d)) ** (1.0 / 0.78)
+
+ # A_bd fitting coefficient, after normalisation with ebnorm
+ abd = (
+ 0.107
+ * (1.0 - 0.35 * alphan + 0.14 * alphan**2)
+ * (1.0 - 0.21 * alphat)
+ * (1.0 - 0.2 * ebnorm + 0.09 * ebnorm**2)
+ )
+
+ # Normalised current drive efficiency (A/W m**-2) (IPDG89)
+ gamnb = 5.0 * abd * 0.1 * ten * (1.0 - fshine) * frbeam * j0 / 0.2 * ffac
+
+ # Current drive efficiency (A/W)
+ return gamnb / (dene20 * rmajor)
+
+ def lheval(self, drfind, rratio):
+ """Routine to evaluate the difference between electron energy
+ expressions required to find the Lower Hybrid absorption radius
+ author: P J Knight, CCFE, Culham Science Centre
+ drfind : input real : correction to parallel refractive index
+ rratio : input real : guess for radius of penetration / rminor
+ ediff : output real : difference between the E values (keV)
+ This routine evaluates the difference between the values calculated
+ from the two equations for the electron energy E, given in
+ AEA FUS 172, p.58. This difference is used to locate the Lower Hybrid
+ wave absorption radius via a Newton-Raphson method, in calling
+ routine lhrad.
+ AEA FUS 251: A User's Guide to the PROCESS Systems Code
+ AEA FUS 172: Physics Assessment for the European Reactor Study
+ """
+ dlocal = 1.0e-19 * profiles_module.nprofile(
+ rratio,
+ physics_variables.rhopedn,
+ physics_variables.ne0,
+ physics_variables.neped,
+ physics_variables.nesep,
+ physics_variables.alphan,
+ )
+
+ # Local electron temperature
+
+ tlocal = profiles_module.tprofile(
+ rratio,
+ physics_variables.rhopedt,
+ physics_variables.te0,
+ physics_variables.teped,
+ physics_variables.tesep,
+ physics_variables.alphat,
+ physics_variables.tbeta,
+ )
+
+ # Local toroidal field (evaluated at the inboard region of the flux surface)
+
+ blocal = (
+ physics_variables.bt
+ * physics_variables.rmajor
+ / (physics_variables.rmajor - rratio * physics_variables.rminor)
+ )
+
+ # Parallel refractive index needed for plasma access
+
+ frac = np.sqrt(dlocal) / blocal
+ nplacc = frac + np.sqrt(1.0e0 + frac * frac)
+
+ # Total parallel refractive index
+
+ refind = nplacc + drfind
+
+ # First equation for electron energy E
+
+ e1 = 511.0e0 * (np.sqrt(1.0e0 + 1.0e0 / (refind * refind)) - 1.0e0)
+
+ # Second equation for E
+
+ e2 = 7.0e0 * tlocal
+
+ # Difference
+
+ return e1 - e2
+
+ def etanb(self, abeam, alphan, alphat, aspect, dene, ebeam, rmajor, ten, zeff):
+ """Routine to find neutral beam current drive efficiency
+ using the ITER 1990 formulation
+ author: P J Knight, CCFE, Culham Science Centre
+ abeam : input real : beam ion mass (amu)
+ alphan : input real : density profile factor
+ alphat : input real : temperature profile factor
+ aspect : input real : aspect ratio
+ dene : input real : volume averaged electron density (m**-3)
+ enbeam : input real : neutral beam energy (keV)
+ rmajor : input real : plasma major radius (m)
+ ten : input real : density weighted average electron temp. (keV)
+ zeff : input real : plasma effective charge
+ This routine calculates the current drive efficiency of
+ a neutral beam system, based on the 1990 ITER model.
+ AEA FUS 251: A User's Guide to the PROCESS Systems Code
+ ITER Physics Design Guidelines: 1989 [IPDG89], N. A. Uckan et al,
+ ITER Documentation Series No.10, IAEA/ITER/DS/10, IAEA, Vienna, 1990
+ """
+
+ zbeam = 1.0
+ bbd = 1.0
+
+ dene20 = 1e-20 * dene
+
+ # Ratio of E_beam/E_crit
+ xjs = ebeam / (bbd * 10.0 * abeam * ten)
+ xj = np.sqrt(xjs)
+
+ yj = 0.8 * zeff / abeam
+
+ rjfunc = xjs / (4.0 + 3.0 * yj + xjs * (xj + 1.39 + 0.61 * yj**0.7))
+
+ epseff = 0.5 / aspect
+ gfac = (1.55 + 0.85 / zeff) * np.sqrt(epseff) - (0.2 + 1.55 / zeff) * epseff
+ ffac = 1.0 / zbeam - (1.0 - gfac) / zeff
+
+ abd = (
+ 0.107
+ * (1.0 - 0.35 * alphan + 0.14 * alphan**2)
+ * (1.0 - 0.21 * alphat)
+ * (1.0 - 0.2e-3 * ebeam + 0.09e-6 * ebeam**2)
+ )
+
+ return abd * (5.0 / rmajor) * (0.1 * ten / dene20) * rjfunc / 0.2 * ffac
+
+ def cfnbi(self, afast, efast, te, ne, nd, nt, zeffai, xlmbda):
+ """Routine to calculate the fraction of the fast particle energy
+ coupled to the ions
+ author: P J Knight, CCFE, Culham Science Centre
+ afast : input real : mass of fast particle (units of proton mass)
+ efast : input real : energy of fast particle (keV)
+ te : input real : density weighted average electron temp. (keV)
+ ne : input real : volume averaged electron density (m**-3)
+ nd : input real : deuterium beam density (m**-3)
+ nt : input real : tritium beam density (m**-3)
+ zeffai : input real : mass weighted plasma effective charge
+ xlmbda : input real : ion-electron coulomb logarithm
+ fpion : output real : fraction of fast particle energy coupled to ions
+ This routine calculates the fast particle energy coupled to
+ the ions in the neutral beam system.
+ AEA FUS 251: A User's Guide to the PROCESS Systems Code
+ """
+ # atmd = 2.0
+ atmdt = 2.5
+ # atmt = 3.0
+ c = 3.0e8
+ me = 9.1e-31
+ # zd = 1.0
+ # zt = 1.0
+
+ # xlbd = self.xlmbdabi(afast, atmd, efast, te, ne)
+ # xlbt = self.xlmbdabi(afast, atmt, efast, te, ne)
+
+ # sum = nd * zd * zd * xlbd / atmd + nt * zt * zt * xlbt / atmt
+ # ecritfix = 16.0e0 * te * afast * (sum / (ne * xlmbda)) ** (2.0e0 / 3.0e0)
+
+ xlmbdai = self.xlmbdabi(afast, atmdt, efast, te, ne)
+ sumln = zeffai * xlmbdai / xlmbda
+ xlnrat = (3.0e0 * np.sqrt(np.pi) / 4.0e0 * me / constants.mproton * sumln) ** (
+ 2.0e0 / 3.0e0
+ )
+ ve = c * np.sqrt(2.0e0 * te / 511.0e0)
+
+ ecritfi = (
+ afast
+ * constants.mproton
+ * ve
+ * ve
+ * xlnrat
+ / (2.0e0 * constants.echarge * 1.0e3)
+ )
+
+ x = np.sqrt(efast / ecritfi)
+ t1 = np.log((x * x - x + 1.0e0) / ((x + 1.0e0) ** 2))
+ thx = (2.0e0 * x - 1.0e0) / np.sqrt(3.0e0)
+ t2 = 2.0e0 * np.sqrt(3.0e0) * (np.atan(thx) + np.pi / 6.0e0)
+
+ return (t1 + t2) / (3.0e0 * x * x)
+
+ def xlmbdabi(self, mb, mth, eb, t, nelec):
+ """Calculates the Coulomb logarithm for ion-ion collisions
+ author: P J Knight, CCFE, Culham Science Centre
+ mb : input real : mass of fast particle (units of proton mass)
+ mth : input real : mass of background ions (units of proton mass)
+ eb : input real : energy of fast particle (keV)
+ t : input real : density weighted average electron temp. (keV)
+ nelec : input real : volume averaged electron density (m**-3)
+ This function calculates the Coulomb logarithm for ion-ion
+ collisions where the relative velocity may be large compared
+ with the background ('mt') thermal velocity.
+ Mikkelson and Singer, Nuc Tech/Fus, 4, 237 (1983)
+ """
+
+ x1 = (t / 10.0) * (eb / 1000.0) * mb / (nelec / 1e20)
+ x2 = mth / (mth + mb)
+
+ return 23.7 + np.log(x2 * np.sqrt(x1))
+
+ def legend(self, zlocal, arg):
+ """Routine to calculate Legendre function and its derivative
+ author: M R O'Brien, CCFE, Culham Science Centre
+ author: P J Knight, CCFE, Culham Science Centre
+ zlocal : input real : local plasma effective charge
+ arg : input real : argument of Legendre function
+ palpha : output real : value of Legendre function
+ palphap : output real : derivative of Legendre function
+ This routine calculates the Legendre function palpha
+ of argument arg and order
+ alpha = -0.5 + i sqrt(xisq),
+ and its derivative palphap.
+
This Legendre function is a conical function and we use the series
+ in xisq given in Abramowitz and Stegun. The
+ derivative is calculated from the derivative of this series.
+
The derivatives were checked by calculating palpha for
+ neighbouring arguments. The calculation of palpha for zero
+ argument was checked by comparison with the expression
+ palpha(0) = 1/sqrt(pi) * cos(pi*alpha/2) * gam1 / gam2
+ (Abramowitz and Stegun, eqn 8.6.1). Here gam1 and
+ gam2 are the Gamma functions of arguments
+ 0.5*(1+alpha) and 0.5*(2+alpha) respectively.
+ Abramowitz and Stegun, equation 8.12.1
+ """
+ if abs(arg) > (1.0e0 + 1.0e-10):
+ eh.fdiags[0] = arg
+ eh.report_error(18)
+
+ arg2 = min(arg, (1.0e0 - 1.0e-10))
+ sinsq = 0.5e0 * (1.0e0 - arg2)
+ xisq = 0.25e0 * (32.0e0 * zlocal / (zlocal + 1.0e0) - 1.0e0)
+ palpha = 1.0e0
+ pold = 1.0e0
+ pterm = 1.0e0
+ palphap = 0.0e0
+ poldp = 0.0e0
+
+ for n in range(10000):
+ # Check for convergence every 20 iterations
+
+ if (n > 1) and ((n % 20) == 1):
+ term1 = 1.0e-10 * max(abs(pold), abs(palpha))
+ term2 = 1.0e-10 * max(abs(poldp), abs(palphap))
+
+ if (abs(pold - palpha) < term1) and (abs(poldp - palphap) < term2):
+ return palpha, palphap
+
+ pold = palpha
+ poldp = palphap
+
+ pterm = (
+ pterm
+ * (4.0e0 * xisq + (2.0e0 * n - 1.0e0) ** 2)
+ / (2.0e0 * n) ** 2
+ * sinsq
+ )
+ palpha = palpha + pterm
+ palphap = palphap - n * pterm / (1.0e0 - arg2)
+ else:
+ eh.report_error(19)
+
+ def sigbeam(self, eb, te, ne, rnhe, rnc, rno, rnfe):
+ """Calculates the stopping cross-section for a hydrogen
+ beam in a fusion plasma
+ author: P J Knight, CCFE, Culham Science Centre
+ eb : input real : beam energy (kev/amu)
+ te : input real : electron temperature (keV)
+ ne : input real : electron density (10^20m-3)
+ rnhe : input real : alpha density / ne
+ rnc : input real : carbon density /ne
+ rno : input real : oxygen density /ne
+ rnfe : input real : iron density /ne
+ This function calculates the stopping cross-section (m^-2)
+ for a hydrogen beam in a fusion plasma.
+ Janev, Boley and Post, Nuclear Fusion 29 (1989) 2125
+ """
+ a = np.array(
+ [
+ [
+ [4.4, -2.49e-2],
+ [7.46e-2, 2.27e-3],
+ [3.16e-3, -2.78e-5],
+ ],
+ [
+ [2.3e-1, -1.15e-2],
+ [-2.55e-3, -6.2e-4],
+ [1.32e-3, 3.38e-5],
+ ],
+ ]
+ )
+
+ b = np.array(
+ [
+ [
+ [[-2.36, -1.49, -1.41, -1.03], [0.185, -0.0154, -4.08e-4, 0.106]],
+ [
+ [-0.25, -0.119, -0.108, -0.0558],
+ [-0.0381, -0.015, -0.0138, -3.72e-3],
+ ],
+ ],
+ [
+ [
+ [0.849, 0.518, 0.477, 0.322],
+ [-0.0478, 7.18e-3, 1.57e-3, -0.0375],
+ ],
+ [
+ [0.0677, 0.0292, 0.0259, 0.0124],
+ [0.0105, 3.66e-3, 3.33e-3, 8.61e-4],
+ ],
+ ],
+ [
+ [
+ [-0.0588, -0.0336, -0.0305, -0.0187],
+ [4.34e-3, 3.41e-4, 7.35e-4, 3.53e-3],
+ ],
+ [
+ [-4.48e-3, -1.79e-3, -1.57e-3, -7.43e-4],
+ [-6.76e-4, -2.04e-4, -1.86e-4, -5.12e-5],
+ ],
+ ],
+ ]
+ )
+
+ z = np.array([2.0, 6.0, 8.0, 26.0])
+ nn = np.array([rnhe, rnc, rno, rnfe])
+
+ nen = ne * 1e-19
+
+ s1 = 0.0
+ for k in range(2):
+ for j in range(3):
+ for i in range(2):
+ s1 += (
+ a[i, j, k]
+ * (np.log(eb)) ** i
+ * (np.log(nen)) ** j
+ * (np.log(te)) ** k
+ )
+
+ sz = 0.0
+ for l in range(4): # noqa: E741
+ for k in range(2):
+ for j in range(2):
+ for i in range(3):
+ sz += (
+ b[i, j, k, l]
+ * (np.log(eb)) ** i
+ * (np.log(nen)) ** j
+ * (np.log(te)) ** k
+ * nn[l]
+ * z[l]
+ * (z[l] - 1.0)
+ )
+
+ return max(1e-20 * (np.exp(s1) / eb * (1.0 + sz)), 1e-23)
diff --git a/process/main.py b/process/main.py
index 36bff8a94d..c1b3c5f57a 100644
--- a/process/main.py
+++ b/process/main.py
@@ -70,6 +70,7 @@
from process.dcll import DCLL
from process.blanket_library import BlanketLibrary
from process.fw import Fw
+from process.current_drive import CurrentDrive
from process.impurity_radiation import initialise_imprad
from pathlib import Path
@@ -585,6 +586,7 @@ def __init__(self):
self.fw = Fw()
self.blanket_library = BlanketLibrary(fw=self.fw)
self.ccfe_hcpb = CCFE_HCPB(blanket_library=self.blanket_library)
+ self.current_drive = CurrentDrive()
self.stellarator = Stellarator(
availability=self.availability,
buildings=self.buildings,
@@ -593,9 +595,12 @@ def __init__(self):
power=self.power,
plasma_profile=self.plasma_profile,
hcpb=self.ccfe_hcpb,
+ current_drive=self.current_drive,
)
self.costs_2015 = Costs2015()
- self.physics = Physics(plasma_profile=self.plasma_profile)
+ self.physics = Physics(
+ plasma_profile=self.plasma_profile, current_drive=self.current_drive
+ )
self.dcll = DCLL(blanket_library=self.blanket_library)
diff --git a/process/output.py b/process/output.py
index 80c323a5e1..82afe15a3f 100644
--- a/process/output.py
+++ b/process/output.py
@@ -56,7 +56,7 @@ def write(models, outfile):
ft.physics_module.igmarcal(outfile)
# TODO what is this? Not in caller.f90?
- ft.current_drive_module.cudriv(outfile, 1)
+ models.current_drive.cudriv(output=True)
# Pulsed reactor model
models.pulse.run(output=True)
diff --git a/process/physics.py b/process/physics.py
index ed70c0b78e..6daee8682d 100644
--- a/process/physics.py
+++ b/process/physics.py
@@ -1,7 +1,6 @@
import numpy
import math
import process.physics_functions as physics_funcs
-from process.fortran import current_drive_module
from process.fortran import constraint_variables
from process.fortran import reinke_variables
from process.fortran import reinke_module
@@ -22,9 +21,10 @@
class Physics:
- def __init__(self, plasma_profile):
+ def __init__(self, plasma_profile, current_drive):
self.outfile = constants.nout
self.plasma_profile = plasma_profile
+ self.current_drive = current_drive
def physics(self):
"""
@@ -302,7 +302,7 @@ def physics(self):
# Auxiliary current drive power calculations
if current_drive_variables.irfcd != 0:
- current_drive_module.cudriv(constants.nout, 0)
+ self.current_drive.cudriv(False)
# Calculate fusion power + components
diff --git a/process/stellarator.py b/process/stellarator.py
index 6edefb4faf..f37508508c 100644
--- a/process/stellarator.py
+++ b/process/stellarator.py
@@ -29,7 +29,6 @@
physics_functions_module,
neoclassics_module,
impurity_radiation_module,
- current_drive_module,
)
import process.superconductors as superconductors
import process.physics_functions as physics_funcs
@@ -54,7 +53,15 @@ class Stellarator:
"""
def __init__(
- self, availability, vacuum, buildings, costs, power, plasma_profile, hcpb
+ self,
+ availability,
+ vacuum,
+ buildings,
+ costs,
+ power,
+ plasma_profile,
+ hcpb,
+ current_drive,
) -> None:
"""Initialises the Stellarator model's variables
@@ -70,6 +77,8 @@ def __init__(
: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
+ :param current_drive: a pointer to the CurrentDrive model, allowing use of CurrentDrives's variables/methods
+ :type current_drive: process.current_drive.CurrentDrive
"""
self.outfile: int = constants.nout
@@ -82,6 +91,7 @@ def __init__(
self.power = power
self.plasma_profile = plasma_profile
self.hcpb = hcpb
+ self.current_drive = current_drive
def run(self, output: bool):
"""Routine to call the physics and engineering modules
@@ -4637,7 +4647,7 @@ def stheat(self, output: bool):
effnbss,
fpion,
current_drive_variables.nbshinef,
- ) = current_drive_module.culnbi()
+ ) = self.current_drive.culnbi()
current_drive_variables.pnbeam = current_drive_variables.pheat * (
1 - current_drive_variables.forbitloss
)
diff --git a/source/fortran/current_drive.f90 b/source/fortran/current_drive.f90
deleted file mode 100644
index 4d2158099d..0000000000
--- a/source/fortran/current_drive.f90
+++ /dev/null
@@ -1,1738 +0,0 @@
-! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-module current_drive_module
- !! Module containing current drive system routines
- !! author: P J Knight, CCFE, Culham Science Centre
- !! N/A
- !! This module contains routines relevant for calculating the
- !! current drive parameters for a fusion power plant.
- !! AEA FUS 251: A User's Guide to the PROCESS Systems Code
- !
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- ! Import modules
-#ifndef dp
- use, intrinsic :: iso_fortran_env, only: dp=>real64
-#endif
- implicit none
-
-contains
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- subroutine cudriv(outfile,iprint)
-
- !! Routine to calculate the current drive power requirements
- !! 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 power requirements of the current
- !! drive system, using a choice of models for the current drive
- !! efficiency.
- !! AEA FUS 251: A User's Guide to the PROCESS Systems Code
- !
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- use error_handling, only: idiags, report_error
- use profiles_module, only: tprofile, nprofile
- use process_output, only: oblnkl, ocmmnt, ovarin, ovarre, ovarrf, osubhd, &
- oheadr
- use heat_transport_variables, only: pinjwpfix, pinjwp
- use current_drive_variables, only: echpwr, pnbeam, plhybd, cnbeam, porbitlossmw, &
- iefrf, iefrffix, pheat, pheatfix, pinjfixmw, irfcd, feffcd, fpion, nbshinef, &
- gamcd, gamma_ecrh, rho_ecrh, etalh, etacd, etacdfix, etaech, forbitloss, &
- pinjmw, pwpnb, etanbi, enbeam, effcd, pwplh, echwpow, pnbitot, nbshinemw, &
- pinjemw, pinjimw, bigq, bootipf, bscfmax, taubeam, pinjalw, nbshield, &
- frbeam, rtanbeam, rtanmax, diaipf, psipf, plasipf, harnum, xi_ebw
- use physics_variables, only: dene, te, rmajor, ten, zeff, dlamee, beta, &
- rhopedt, rhopedn, te0, teped, tesep, alphat, alphan, ne0, nesep, neped, &
- bt, rminor, tbeta, plascur, ipedestal, faccd, ignite, pohmmw, powfmw, &
- facoh, fvsbrnni
- use constants, only: nout, echarge, emass, pi, epsilon0
- use cost_variables, only: startupratio
-
- implicit none
-
- ! Arguments
- integer, intent(in) :: iprint, outfile
-
- ! Local variables !
- ! !!!!!!!!!!!!!!!!!!
-
- real(dp) :: dene20, effnbss, effrfss, gamnb, gamrf, power1
- real(dp) :: effcdfix, effrfssfix, effnbssfix, pinjwp1
- real(dp) :: pnbitotfix, nbshinemwfix, porbitlossmwfix, cnbeamfix
- real(dp) :: pinjimw1, pinjemw1, pinjimwfix, pinjemwfix, pinjmw1, pinjmwfix
- real(dp) :: auxiliary_cdfix, faccdfix, gamcdfix
- real(dp) :: fshift, xf, enpa,ftherm,fpp,cdeff, ampperwatt
- real(dp) :: dens_at_rho, te_at_rho
- logical :: Temperature_capped
- real(dp) :: auxiliary_cd
- real(dp) :: a, fc, fp, density_factor
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- echpwr = 0.0D0
- pnbeam = 0.0D0
- plhybd = 0.0D0
- cnbeam = 0.0D0
- cnbeamfix = 0.0D0
- porbitlossmw = 0.0D0
- porbitlossmwfix = 0.0D0
-
- pinjmw1 = 0.0
- pinjmwfix = 0.0
- pinjimw1 = 0.0
- pinjemw1 = 0.0
- pinjemwfix = 0.0
- pinjimwfix = 0.0
- auxiliary_cdfix = 0.0
- faccdfix = 0.0
- gamcdfix = 0.0D0
-
- ! To stop issues with input file we force
- ! zero secondary heating if no injection method
- if (iefrffix == 0) then
- pheatfix = 0.0
- end if
-
- ! check for unphysically large heating in
- ! secondary injected power source
- if (pheatfix > pinjfixmw) then
- pheatfix = pinjfixmw
- end if
-
- ! irfcd | switch for current drive calculation
- ! = 0 | turned off
- ! = 1 | turned on
- if (irfcd /= 0) then
-
- ! put electron density in desired units (10^-20 m-3)
- dene20 = dene * 1.0D-20
-
- ! If present we must calculate second current drive
- ! efficiencies in units of Amps/Watt using the fixed
- ! values from user input
- ! iefrffix | switch for fixed current drive efficiency model
- select case (iefrffix)
-
- case(0) ! second current drive not present
-
- case (1) ! Fenstermacher Lower Hybrid model
-
- effrfssfix = (0.36D0 * (1.0D0 + (te/25.0D0)**1.16D0)) / &
- (rmajor*dene20) * feffcd
- effcdfix = effrfssfix
-
- case (2) ! Ion-Cyclotron current drive
-
- effrfssfix = 0.63D0 * 0.1D0*ten / (2.0D0 + zeff) / &
- (rmajor*dene20) * feffcd
- effcdfix = effrfssfix
-
- case (3) ! Fenstermacher Electron Cyclotron Resonance model
-
- effrfssfix = 0.21D0 * ten/ (rmajor * dene20 * dlamee) * feffcd
- effcdfix = effrfssfix
-
- case (4) ! Ehst Lower Hybrid / Fast Wave current drive
-
- effrfssfix = te**0.77D0 * (0.034D0 + 0.196D0 * beta) / &
- (rmajor*dene20) * ( 32.0D0/(5.0D0+zeff) + 2.0D0 + &
- (12.0D0*(6.0D0+zeff))/(5.0D0+zeff)/(3.0D0+zeff) + &
- 3.76D0/zeff) / 12.507D0 * feffcd
- effcdfix = effrfssfix
-
- case (5) ! ITER Neutral Beam current drive
-
- call iternb(effnbss,fpion,nbshinef)
- effnbssfix = effnbss * feffcd
- effcdfix = effnbssfix
-
- case (6) ! Culham Lower Hybrid current drive model
-
- call cullhy(effrfss)
- effrfssfix = effrfss * feffcd
- effcdfix = effrfssfix
-
- case (7) ! Culham ECCD model
-
- call culecd(effrfss)
- effrfssfix = effrfss * feffcd
- effcdfix = effrfssfix
-
- case (8) ! Culham Neutral Beam model
-
- call culnbi(effnbss,fpion,nbshinef)
- effnbssfix = effnbss * feffcd
- effcdfix = effnbssfix
-
- case (9) ! Issue #508 Remove RFP option Oscillating Field CD model
-
- case (10) ! ECRH user input gamma
-
- ! Normalised current drive efficiency gamma
- gamcd = gamma_ecrh
-
- ! Absolute current drive efficiency
- effrfssfix = gamcd / (dene20 * rmajor)
- effcdfix = effrfssfix
-
- case (11) ! ECRH Poli model "HARE", removed in issue #1811
-
- case (12)
- ! EBW scaling
- ! Scaling author Simon Freethy
- ! Ref : PROCESS issue 1262
-
- ! Normalised current drive efficiency gamma
- gamcd = (xi_ebw/32.7D0) * te
-
- ! Absolute current drive efficiency
- effrfssfix = gamcd / (dene20 * rmajor)
- effcdfix = effrfssfix
-
- ! EBWs can only couple to plasma if cyclotron harmonic is above plasma density cut-off;
- ! this behaviour is captured in the following function (ref issue #1262):
- ! harnum = cyclotron harmonic number (fundamental used as default)
- ! constant 'a' controls sharpness of transition
- a = 0.1D0
-
- fc = 1.0D0/(2.0D0*pi) * harnum * echarge * bt / emass
- fp = 1.0D0/(2.0D0*pi) * sqrt( dene * echarge**2 / ( emass * epsilon0 ) )
-
- density_factor = 0.5D0 * ( 1.0D0 + tanh( (2.0D0/a) * ( ( fp - fc )/fp - a) ) )
-
- effcdfix = effcdfix * density_factor
-
- effrfssfix = effrfssfix * density_factor
-
-
- case default
- idiags(1) = iefrffix
- call report_error(126)
-
- end select
-
- ! find the current drive wall plug and injected powers (MW)
- ! and efficiencies for secondary current drive mechanisms
- ! using the fixed injected power from the user input
- select case (iefrffix)
-
- case (1,2,4,6) ! LHCD or ICCD
-
- ! Injected power
- pinjemwfix = pinjfixmw
-
- ! Wall plug power
- pinjwpfix = pinjfixmw / etalh
-
- ! Wall plug to injector efficiency
- etacdfix = etalh
-
- ! Normalised current drive efficiency gamma
- gamcdfix = effrfssfix * (dene20 * rmajor)
-
- ! the fixed auxiliary current
- auxiliary_cdfix = effrfssfix * ( pinjfixmw - pheatfix) * 1.0d6
- faccdfix = auxiliary_cdfix / plascur
-
- case (3,7,10,11,12) ! ECCD
-
- ! Injected power
- pinjemwfix = pinjfixmw
-
- ! Wall plug power
- pinjwpfix = pinjfixmw / etaech
-
- ! Wall plug to injector efficiency
- etacdfix = etaech
-
- ! the fixed auxiliary current
- auxiliary_cdfix = effrfssfix * ( pinjfixmw - pheatfix) * 1.0d6
- faccdfix = auxiliary_cdfix / plascur
-
- case (5,8) ! NBCD
-
- ! Account for first orbit losses
- ! (power due to particles that are ionised but not thermalised) [MW]:
- ! This includes a second order term in shinethrough*(first orbit loss)
- forbitloss = min(0.999,forbitloss) ! Should never be needed
-
- if(ipedestal.ne.3) then ! When not using PLASMOD
- pnbitotfix = pinjfixmw / (1.0D0-forbitloss+forbitloss*nbshinef)
- else
- ! Netural beam power calculated by PLASMOD
- pnbitotfix = pinjmw / (1.0D0-forbitloss+forbitloss*nbshinef)
- endif
-
- ! Shinethrough power (atoms that are not ionised) [MW]:
- nbshinemwfix = pnbitotfix * nbshinef
-
- ! First orbit loss
- porbitlossmwfix = forbitloss * (pnbitotfix - nbshinemwfix)
-
- ! Power deposited
- pinjmwfix = pnbitotfix - nbshinemwfix - porbitlossmwfix
- pinjimwfix = pinjmwfix * fpion
- pinjemwfix = pinjmwfix * (1.0D0-fpion)
-
- pwpnb = pnbitotfix/etanbi ! neutral beam wall plug power
- pinjwpfix = pwpnb
- etacdfix = etanbi
- gamnb = effnbssfix * (dene20 * rmajor)
- gamcdfix = gamnb
- cnbeamfix = 1.0D-3 * (pnbitotfix*1.0D6) / enbeam ! Neutral beam current (A)
- auxiliary_cdfix = effnbssfix * ( pinjfixmw - pheatfix) * 1.0d6
- faccdfix = auxiliary_cdfix / plascur
-
- case (9) ! OFCD - RFP option removed in PROCESS (issue #508)
-
- end select
-
- ! Calculate current drive efficiencies in units of Amps/Watt.
- ! iefrf | switch for current drive efficiency model
- select case (iefrf)
-
- case (1) ! Fenstermacher Lower Hybrid model
-
- effrfss = (0.36D0 * (1.0D0 + (te/25.0D0)**1.16D0)) / &
- (rmajor*dene20) * feffcd
- effcd = effrfss
-
- case (2) ! Ion-Cyclotron current drive
-
- effrfss = 0.63D0 * 0.1D0*ten / (2.0D0 + zeff) / &
- (rmajor*dene20) * feffcd
- effcd = effrfss
-
- case (3) ! Fenstermacher Electron Cyclotron Resonance model
-
- effrfss = 0.21D0 * ten/ (rmajor * dene20 * dlamee) * feffcd
- effcd = effrfss
-
- case (4) ! Ehst Lower Hybrid / Fast Wave current drive
-
- effrfss = te**0.77D0 * (0.034D0 + 0.196D0 * beta) / &
- (rmajor*dene20) * ( 32.0D0/(5.0D0+zeff) + 2.0D0 + &
- (12.0D0*(6.0D0+zeff))/(5.0D0+zeff)/(3.0D0+zeff) + &
- 3.76D0/zeff) / 12.507D0 * feffcd
- effcd = effrfss
-
- case (5) ! ITER Neutral Beam current drive
-
- call iternb(effnbss,fpion,nbshinef)
- effnbss = effnbss * feffcd
- effcd = effnbss
-
- case (6) ! Culham Lower Hybrid current drive model
-
- call cullhy(effrfss)
- effrfss = effrfss * feffcd
- effcd = effrfss
-
- case (7) ! Culham ECCD model
-
- call culecd(effrfss)
- effrfss = effrfss * feffcd
- effcd = effrfss
-
- case (8) ! Culham Neutral Beam model
-
- call culnbi(effnbss,fpion,nbshinef)
- effnbss = effnbss * feffcd
- effcd = effnbss
-
- case (9) ! Issue #508 Remove RFP option Oscillating Field CD model
-
- case (10) ! ECRH user input gamma
-
- gamcd = gamma_ecrh
-
- ! Absolute current drive efficiency
- effrfss = gamcd / (dene20 * rmajor)
- effcd = effrfss
-
- case (11) ! ECRH Poli model "HARE", removed in issue #1811
-
- case (12)
- ! EBW scaling
- ! Scaling author Simon Freethy
- ! Ref : PROCESS issue 1262
-
- ! Normalised current drive efficiency gamma
- gamcd = (xi_ebw/32.7D0) * te
-
- ! Absolute current drive efficiency
- effrfss = gamcd / (dene20 * rmajor)
- effcd = effrfss
-
- ! EBWs can only couple to plasma if cyclotron harmonic is above plasma density cut-off;
- ! this behaviour is captured in the following function (ref issue #1262):
- ! harnum = cyclotron harmonic number (fundamental used as default)
- ! contant 'a' controls sharpness of transition
-
- !TODO is the below needed?
- ! a = 0.1D0
-
- ! fc = 1.0D0/(2.0D0*pi) * harnum * echarge * bt / emass
- ! fp = 1.0D0/(2.0D0*pi) * sqrt( dene * echarge**2 / ( emass * epsilon0 ) )
-
- ! density_factor = 0.5D0 * ( 1.0D0 + tanh( (2.0D0/a) * ( ( fp - fc )/fp - a) ) )
-
- ! effcd = effcd * density_factor
-
- ! effrfss = effrfss * density_factor
-
-
- case default
- idiags(1) = iefrf
- call report_error(126)
-
- end select
-
- ! Compute current drive wall plug and injected powers (MW) and efficiencies
- auxiliary_cd = faccd * plascur
- select case (iefrf)
-
- case (1,2,4,6) ! LHCD or ICCD
-
- ! Injected power
- plhybd = 1.0D-6 * (faccd - faccdfix) * plascur / effrfss + pheat
- pinjimw1 = 0.0D0
- pinjemw1 = plhybd
-
- ! Wall plug power
- pwplh = plhybd / etalh
- pinjwp1 = pwplh
-
- ! Wall plug to injector efficiency
- etacd = etalh
-
- ! Normalised current drive efficiency gamma
- gamrf = effrfss * (dene20 * rmajor)
- gamcd = gamrf
-
- case (3,7,10,11,12) ! ECCD
-
- ! Injected power (set to close to close the Steady-state current equilibrium)
- echpwr = 1.0D-6 * (faccd - faccdfix) * plascur / effrfss + pheat
- pinjemw1 = echpwr
-
- ! Wall plug power
- echwpow = echpwr / etaech
-
- ! Wall plug to injector efficiency
- pinjwp1 = echwpow
- etacd = etaech
-
- case (5,8) ! NBCD
-
- ! MDK. See Gitlab issue #248, and scanned note.
- power1 = 1.0D-6 * (faccd - faccdfix) * plascur / effnbss + pheat
-
- ! Account for first orbit losses
- ! (power due to particles that are ionised but not thermalised) [MW]:
- ! This includes a second order term in shinethrough*(first orbit loss)
- forbitloss = min(0.999,forbitloss) ! Should never be needed
-
- if(ipedestal.ne.3)then ! When not using PLASMOD
- pnbitot = power1 / (1.0D0-forbitloss+forbitloss*nbshinef)
- else
- ! Neutral beam power calculated by PLASMOD
- pnbitot = pinjmw / (1.0D0-forbitloss+forbitloss*nbshinef)
- endif
-
- ! Shinethrough power (atoms that are not ionised) [MW]:
- nbshinemw = pnbitot * nbshinef
-
- ! First orbit loss
- porbitlossmw = forbitloss * (pnbitot - nbshinemw)
-
- ! Power deposited
- pinjmw1 = pnbitot - nbshinemw - porbitlossmw
- pinjimw1 = pinjmw1 * fpion
- pinjemw1 = pinjmw1 * (1.0D0-fpion)
-
- pwpnb = pnbitot/etanbi ! neutral beam wall plug power
- pinjwp1 = pwpnb
- etacd = etanbi
- gamnb = effnbss * (dene20 * rmajor)
- gamcd = gamnb
- cnbeam = 1.0D-3 * (pnbitot*1.0D6) / enbeam ! Neutral beam current (A)
-
-
-
- case (9) ! OFCD - RFP option removed in PROCESS (issue #508)
-
- end select
-
- ! Total injected power
- ! sum contributions from primary and secondary systems
- pinjmw = pinjemw1 + pinjimw1 + pinjemwfix + pinjimwfix
- pinjmw1 = pinjemw1 + pinjimw1
- pinjmwfix = pinjemwfix + pinjimwfix
- pinjemw = pinjemw1 + pinjemwfix
- pinjimw = pinjimw1 + pinjimwfix
- pinjwp = pinjwp1 + pinjwpfix
-
- ! Reset injected power to zero for ignited plasma (fudge)
- if (ignite == 1) then
- pinjwp = 0.0D0
- end if
-
- ! Ratio of fusion to input (injection+ohmic) power
- if (abs(pinjmw + porbitlossmw + pohmmw) < 1.0D-6) then
- bigq = 1.0D18
- else
- bigq = powfmw / (pinjmw + porbitlossmw + pohmmw)
- end if
-
- end if
-
- ! Output !
- ! !!!!!!!!!
-
- if (iprint == 0) return
-
- call oheadr(outfile,'Current Drive System')
-
- if (irfcd == 0) then
- call ocmmnt(outfile,'No current drive used')
- call oblnkl(outfile)
- return
- end if
-
- select case (iefrf)
-
- case (1,4,6)
- call ocmmnt(outfile,'Lower Hybrid Current Drive')
- case (2)
- call ocmmnt(outfile,'Ion Cyclotron Current Drive')
- case (3,7)
- call ocmmnt(outfile,'Electron Cyclotron Current Drive')
- case (5,8)
- call ocmmnt(outfile,'Neutral Beam Current Drive')
- case (9)
- ! RFP option removed in PROCESS (issue #508)
- case (10)
- call ocmmnt(outfile,'Electron Cyclotron Current Drive (user input gamma_CD)')
- case (11)
- call ocmmnt(outfile,'Electron Cyclotron Current Drive (HARE). No longer implemented.')
- case (12)
- call ocmmnt(outfile,'EBW current drive')
- end select
-
- call ovarin(outfile,'Current drive efficiency model','(iefrf)',iefrf)
-
- if (iefrffix.NE.0) then
- select case (iefrffix)
-
- case (1,4,6)
- call ocmmnt(outfile,'Lower Hybrid Current Drive')
- case (2)
- call ocmmnt(outfile,'Ion Cyclotron Current Drive')
- case (3,7)
- call ocmmnt(outfile,'Electron Cyclotron Current Drive')
- case (5,8)
- call ocmmnt(outfile,'Neutral Beam Current Drive')
- case (9)
- ! RFP option removed in PROCESS (issue #508)
- case (10)
- call ocmmnt(outfile,'Electron Cyclotron Current Drive (user input gamma_CD)')
- case(11)
- call ocmmnt(outfile,'Electron Cyclotron Current Drive (HARE). No longer implemented.')
- case (12)
- call ocmmnt(outfile,'EBW current drive')
- end select
-
- call ovarin(outfile,'Secondary current drive efficiency model','(iefrffix)',iefrffix)
- end if
-
- if (ignite == 1) then
- call ocmmnt(outfile, &
- 'Ignited plasma; injected power only used for start-up phase')
- end if
-
- call oblnkl(outfile)
-
- if (abs(facoh) > 1.0D-8) then
- call ocmmnt(outfile,'Current is driven by both inductive')
- call ocmmnt(outfile,'and non-inductive means.')
- end if
-
- call ovarre(outfile,'Ratio of power for flat-top to start-up (MW)', '(startupratio)', startupratio)
- call ovarre(outfile,'Auxiliary power used for plasma heating only (MW)', '(pheat)', pheat + pheatfix)
- call ovarre(outfile,'Power injected for current drive (MW)','(pcurrentdrivemw)', pinjmw - pheat - pheatfix)
- call ovarre(outfile,'Maximum Allowed Bootstrap current fraction', '(bscfmax)', bscfmax)
- if (iefrffix.NE.0) then
- call ovarre(outfile,'Power injected for main current drive (MW)','(pcurrentdrivemw1)', pinjmw1 - pheat)
- call ovarre(outfile,'Power injected for secondary current drive (MW)','(pcurrentdrivemw2)', pinjmwfix - pheatfix)
- end if
- call ovarre(outfile,'Fusion gain factor Q','(bigq)',bigq, 'OP ')
- call ovarre(outfile,'Auxiliary current drive (A)','(auxiliary_cd)',auxiliary_cd, 'OP ')
- if (iefrffix.ne.0) then
- call ovarre(outfile,'Secondary auxiliary current drive (A)','(auxiliary_cdfix)',auxiliary_cdfix, 'OP ')
- end if
- call ovarre(outfile,'Current drive efficiency (A/W)','(effcd)',effcd, 'OP ')
- call ovarre(outfile,'Normalised current drive efficiency, gamma (10^20 A/W-m2)', &
- '(gamcd)',gamcd, 'OP ')
- call ovarre(outfile,'Wall plug to injector efficiency','(etacd)',etacd)
-
- select case(iefrf) ! Select plasma heating choice for output in the mfile
- ! Other heating cases to be added when necessary
-
- case(1,2,3,4)
-
- case(5,8) !NBI case(5) and Case(8) NBI is covered elsewhere
-
- case(6,7,9,11)
-
- case(10) !ECRH/ECCD Heating of Plasma
-
- call ovarre(outfile,'ECRH plasma heating efficiency','(gamma_ecrh)',gamma_ecrh)
-
- case(12) !EBW Heating of Plasma
-
- call ovarre(outfile,'EBW plasma heating efficiency','(xi_ebw)',xi_ebw)
-
- case default
- idiags(1) = iefrf
- call report_error(126)
-
- end select
-
-
- if (iefrffix.NE.0) then
- call ovarre(outfile,'Secondary current drive efficiency (A/W)','(effcdfix)',effcdfix, 'OP ')
- call ovarre(outfile,'Seconday wall plug to injector efficiency','(etacdfix)',etacdfix)
- call ovarre(outfile,'Normalised secondary current drive efficiency, gamma (10^20 A/W-m2)', &
- '(gamcdfix)',gamcdfix, 'OP ')
- end if
-
- call osubhd(outfile,'Fractions of current drive :')
- call ovarrf(outfile,'Bootstrap fraction','(bootipf)',bootipf, 'OP ')
- call ovarrf(outfile,'Diamagnetic fraction','(diaipf)',diaipf, 'OP ')
- call ovarrf(outfile,'Pfirsch-Schlueter fraction','(psipf)',psipf, 'OP ')
- call ovarrf(outfile,'Auxiliary current drive fraction','(faccd)',faccd, 'OP ')
- call ovarrf(outfile,'Inductive fraction','(facoh)',facoh, 'OP ')
- ! Add total error check.
- call ovarrf(outfile,'Total','(plasipf+faccd+facoh)',plasipf+faccd+facoh)
- if (abs(plasipf+faccd+facoh-1.0d0) > 1.0d-8) then
- call ocmmnt(outfile,'ERROR: current drive fractions do not add to 1')
- end if
- ! MDK Add fvsbrnni as it can be an iteration variable
- call ovarrf(outfile,'Fraction of the plasma current produced by non-inductive means','(fvsbrnni)',fvsbrnni)
-
- if (abs(bootipf-bscfmax) < 1.0D-8) then
- call ocmmnt(outfile,'Warning : bootstrap current fraction is at')
- call ocmmnt(outfile,' its prescribed maximum.')
- end if
-
- call oblnkl(outfile)
-
- if (abs(plhybd) > 1.0D-8) then
- !if (iefrffix.NE.0) then ! needs updating
- ! call ovarre(outfile,'Secondary RF efficiency (A/W)','(effrfssfix)',effrfssfix, 'OP ')
- !end if
- call ovarre(outfile,'RF efficiency (A/W)','(effrfss)',effrfss, 'OP ')
- call ovarre(outfile,'RF gamma (10^20 A/W-m2)','(gamrf)',gamrf, 'OP ')
- call ovarre(outfile,'Lower hybrid injected power (MW)','(plhybd)',plhybd, 'OP ')
- call ovarre(outfile,'Lower hybrid wall plug efficiency','(etalh)',etalh)
- call ovarre(outfile,'Lower hybrid wall plug power (MW)','(pwplh)',pwplh, 'OP ')
- end if
-
- ! MDK rearranged and added nbshinemw
- !if (abs(pnbeam) > 1.0D-8) then
- if ((iefrf == 5).or.(iefrf== 8).or.(iefrffix == 5).or.(iefrffix == 8)) then
- call ovarre(outfile,'Neutral beam energy (keV)','(enbeam)',enbeam)
- if ((iefrf == 5).or.(iefrf == 8)) then
- call ovarre(outfile,'Neutral beam current (A)','(cnbeam)',cnbeam, 'OP ')
- end if
- if ((iefrffix == 5).or.(iefrffix == 8)) then
- call ovarre(outfile,'Secondary fixed neutral beam current (A)','(cnbeamfix)',cnbeamfix, 'OP ')
- end if
- if ((iefrf == 5).or.(iefrf == 8)) then
- call ovarre(outfile,'Beam efficiency (A/W)','(effnbss)',effnbss, 'OP ')
- end if
- if ((iefrffix == 5).or.(iefrffix == 8)) then
- call ovarre(outfile,'Secondary fixed beam efficiency (A/W)','(effnbssfix)',effnbssfix, 'OP ')
- end if
- call ovarre(outfile,'Beam gamma (10^20 A/W-m2)','(gamnb)',gamnb, 'OP ')
- call ovarre(outfile,'Neutral beam wall plug efficiency','(etanbi)',etanbi)
- call ovarre(outfile,'Beam decay lengths to centre','(taubeam)',taubeam, 'OP ')
- call ovarre(outfile,'Beam shine-through fraction','(nbshinef)',nbshinef, 'OP ')
- call ovarre(outfile,'Neutral beam wall plug power (MW)','(pwpnb)',pwpnb, 'OP ')
-
- call oblnkl(outfile)
- call ocmmnt(outfile,'Neutral beam power balance :')
- call ocmmnt(outfile,'----------------------------')
- if ((iefrf == 5).or.(iefrf == 8)) then
- call ovarrf(outfile,'Beam first orbit loss power (MW)','(porbitlossmw)', porbitlossmw, 'OP ')
- call ovarrf(outfile,'Beam shine-through power [MW]','(nbshinemw)',nbshinemw, 'OP ')
- call ovarrf(outfile,'Beam power deposited in plasma (MW)','(pinjmw)',pinjmw1, 'OP ')
- call ovarrf(outfile,'Maximum allowable beam power (MW)','(pinjalw)',pinjalw)
- call ovarrf(outfile,'Total (MW)', &
- '(porbitlossmw+nbshinemw+pinjmw)',porbitlossmw+nbshinemw+pinjmw1)
- call oblnkl(outfile)
- call ovarrf(outfile,'Beam power entering vacuum vessel (MW)','(pnbitot)',pnbitot, 'OP ')
- end if
- if ((iefrffix == 5).or.(iefrffix == 8)) then
- call oblnkl(outfile)
- call ocmmnt(outfile,'Secondary fixed neutral beam power balance :')
- call ocmmnt(outfile,'----------------------------')
- call ovarrf(outfile,'Secondary fixed beam first orbit loss power (MW)','(porbitlossmwfix)', porbitlossmwfix, 'OP ')
- call ovarrf(outfile,'Secondary fixed beam shine-through power [MW]','(nbshinemwfix)',nbshinemwfix, 'OP ')
- call ovarrf(outfile,'Secondary fixed beam power deposited in plasma (MW)','(pinjmwfix)',pinjmwfix, 'OP ')
- call ovarrf(outfile,'Maximum allowable beam power (MW)','(pinjalw)',pinjalw)
- call ovarrf(outfile,'Secondary fixed total (MW)', &
- '(porbitlossmwfixed+nbshinemwfix+pinjmwfix)',porbitlossmwfix+nbshinemwfix+pinjmwfix)
- call oblnkl(outfile)
- call ovarrf(outfile,'Secondary beam power entering vacuum vessel (MW)','(pnbitotfix)',pnbitotfix, 'OP ')
- end if
- call oblnkl(outfile)
-
- call ovarre(outfile,'Fraction of beam energy to ions','(fpion)',fpion, 'OP ')
- call ovarre(outfile,'Beam duct shielding thickness (m)','(nbshield)',nbshield)
- call ovarre(outfile,'Beam tangency radius / Plasma major radius','(frbeam)',frbeam)
- call ovarre(outfile,'Beam centreline tangency radius (m)','(rtanbeam)', rtanbeam, 'OP ')
- call ovarre(outfile,'Maximum possible tangency radius (m)','(rtanmax)', rtanmax, 'OP ')
- end if
-
- if (abs(echpwr) > 1.0D-8) then
- call ovarre(outfile,'Electron cyclotron injected power (MW)','(echpwr)',echpwr, 'OP ')
- call ovarrf(outfile,'Maximum allowable ECRH power (MW)','(pinjalw)',pinjalw)
- call ovarre(outfile,'ECH wall plug efficiency','(etaech)',etaech)
- call ovarre(outfile,'ECH wall plug power (MW)','(echwpow)',echwpow, 'OP ')
- end if
- if (abs(pinjfixmw) > 1.0D-8) then
- call ovarrf(outfile,'Fixed ECRH power (MW)','(pinjmwfix)',pinjmwfix)
- call ovarre(outfile,'ECH wall plug efficiency','(etaech)',etaech)
- call ovarre(outfile,'Secondary fixed ECH wall plug power (MW)','(pinjwpfix)',pinjwpfix, 'OP ')
- end if
-
- end subroutine cudriv
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- subroutine iternb(effnbss,fpion,fshine)
-
- !! Routine to calculate ITER Neutral Beam current drive parameters
- !! author: P J Knight, CCFE, Culham Science Centre
- !! effnbss : output real : neutral beam current drive efficiency (A/W)
- !! fpion : output real : fraction of NB power given to ions
- !! fshine : output real : shine-through fraction of beam
- !! This routine calculates the current drive parameters for a
- !! neutral beam system, based on the 1990 ITER model.
- !! AEA FUS 251: A User's Guide to the PROCESS Systems Code
- !! ITER Physics Design Guidelines: 1989 [IPDG89], N. A. Uckan et al,
- !! ITER Documentation Series No.10, IAEA/ITER/DS/10, IAEA, Vienna, 1990
- !
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- use error_handling, only: fdiags, idiags, report_error
- use current_drive_variables, only: frbeam, enbeam, taubeam, ftritbm
- use physics_variables, only: eps, rmajor, abeam, te, dene, ralpne, rncne, &
- rnone, rnfene, deni, ten, zeffai, dlamie, alphan, alphat, aspect, zeff
- use constants, only: rmu0
- implicit none
-
- ! Arguments !
- ! !!!!!!!!!!!!
-
- real(dp), intent(out) :: effnbss,fpion,fshine
-
- ! Local variables !
- ! !!!!!!!!!!!!!!!!!!
-
- real(dp) :: dend,dent,dpath,sigstop
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- ! Check argument sanity
- if ((1.0D0+eps) < frbeam) then
- fdiags(1) = eps ; fdiags(2) = frbeam
- call report_error(15)
- end if
-
- ! Calculate beam path length to centre
- dpath = rmajor * sqrt( (1.0D0 + eps)**2 - frbeam**2)
-
- ! Calculate beam stopping cross-section
- sigstop = sigbeam(enbeam/abeam,te,dene,ralpne,rncne,rnone,rnfene)
-
- ! Calculate number of decay lengths to centre
- taubeam = dpath * dene * sigstop
-
- ! Shine-through fraction of beam
- fshine = exp(-2.0D0 * dpath*dene*sigstop)
- fshine = max(fshine, 1.0D-20)
-
- ! Deuterium and tritium beam densities
- dend = deni * (1.0D0-ftritbm)
- dent = deni * ftritbm
-
- ! Power split to ions / electrons
- call cfnbi(abeam,enbeam,ten,dene,dend,dent,zeffai,dlamie,fpion)
-
- ! Current drive efficiency
- effnbss = frbeam * &
- etanb(abeam,alphan,alphat,aspect,dene,enbeam,rmajor,ten,zeff)
-
- contains
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- function etanb(abeam,alphan,alphat,aspect,dene,ebeam,rmajor,ten,zeff)
-
- !! Routine to find neutral beam current drive efficiency
- !! using the ITER 1990 formulation
- !! author: P J Knight, CCFE, Culham Science Centre
- !! abeam : input real : beam ion mass (amu)
- !! alphan : input real : density profile factor
- !! alphat : input real : temperature profile factor
- !! aspect : input real : aspect ratio
- !! dene : input real : volume averaged electron density (m**-3)
- !! enbeam : input real : neutral beam energy (keV)
- !! rmajor : input real : plasma major radius (m)
- !! ten : input real : density weighted average electron temp. (keV)
- !! zeff : input real : plasma effective charge
- !! This routine calculates the current drive efficiency of
- !! a neutral beam system, based on the 1990 ITER model.
- !! AEA FUS 251: A User's Guide to the PROCESS Systems Code
- !! ITER Physics Design Guidelines: 1989 [IPDG89], N. A. Uckan et al,
- !! ITER Documentation Series No.10, IAEA/ITER/DS/10, IAEA, Vienna, 1990
- !
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- implicit none
-
- real(dp) :: etanb
-
- ! Arguments !
- ! !!!!!!!!!!!!
-
- real(dp), intent(in) :: abeam,alphan,alphat,aspect,dene, &
- ebeam,rmajor,ten,zeff
-
- ! Local variables !
- ! !!!!!!!!!!!!!!!!!!
-
- real(dp) :: abd,bbd,dene20,dum,epseff,ffac,gfac,rjfunc, &
- xj,xjs,yj,zbeam
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- ! TODO comment this subroutine.
-
- zbeam = 1.0D0
- bbd = 1.0D0
-
- dene20 = 1.0D-20*dene
-
- ! Ratio of E_beam/E_crit
- xjs = ebeam / (bbd*10.0D0*abeam*ten)
- xj = sqrt(xjs)
-
- yj = 0.8D0 * zeff/abeam
-
- rjfunc = xjs / (4.0D0 + 3.0D0*yj + xjs * &
- (xj + 1.39D0 + 0.61D0*yj**0.7D0))
-
- epseff = 0.5D0/aspect
- gfac = (1.55D0 + 0.85D0/zeff)*sqrt(epseff) - &
- (0.2D0 + 1.55D0/zeff)*epseff
- ffac = 1.0D0/zbeam - (1.0D0 - gfac)/zeff
-
- abd = 0.107D0 * (1.0D0 - 0.35D0*alphan + 0.14D0*alphan**2) * &
- (1.0D0 - 0.21D0*alphat) * (1.0D0 - 0.2D-3*ebeam + &
- 0.09D-6 * ebeam**2)
- dum = abd *(5.0D0/rmajor) * (0.1D0*ten/dene20) * rjfunc/0.2D0 * ffac
-
- etanb = dum
-
- end function etanb
-
- end subroutine iternb
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- subroutine cfnbi(afast,efast,te,ne,nd,nt,zeffai,xlmbda,fpion)
-
- !! Routine to calculate the fraction of the fast particle energy
- !! coupled to the ions
- !! author: P J Knight, CCFE, Culham Science Centre
- !! afast : input real : mass of fast particle (units of proton mass)
- !! efast : input real : energy of fast particle (keV)
- !! te : input real : density weighted average electron temp. (keV)
- !! ne : input real : volume averaged electron density (m**-3)
- !! nd : input real : deuterium beam density (m**-3)
- !! nt : input real : tritium beam density (m**-3)
- !! zeffai : input real : mass weighted plasma effective charge
- !! xlmbda : input real : ion-electron coulomb logarithm
- !! fpion : output real : fraction of fast particle energy coupled to ions
- !! This routine calculates the fast particle energy coupled to
- !! the ions in the neutral beam system.
- !! AEA FUS 251: A User's Guide to the PROCESS Systems Code
- !
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- use constants, only: mproton, pi, echarge
-
- implicit none
-
- ! Arguments
-
- real(dp), intent(in) :: afast,efast,te,ne,nd,nt,zeffai,xlmbda
- real(dp), intent(out) :: fpion
-
- ! Local variables
-
- real(dp) :: ans,ecritfi,ecritfix,sum,sumln,thx,t1,t2,ve,x, &
- xlbd,xlbt,xlmbdai,xlnrat
-
- real(dp), parameter :: atmd = 2.0D0
- real(dp), parameter :: atmdt = 2.5D0
- real(dp), parameter :: atmt = 3.0D0
- real(dp), parameter :: c = 3.0D8
- real(dp), parameter :: me = 9.1D-31
- real(dp), parameter :: zd = 1.0D0
- real(dp), parameter :: zt = 1.0D0
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- xlbd = xlmbdabi(afast,atmd,efast,te,ne)
- xlbt = xlmbdabi(afast,atmt,efast,te,ne)
-
- sum = nd*zd*zd * xlbd/atmd + nt*zt*zt * xlbt/atmt
- ecritfix = 16.0D0 * te * afast * (sum/(ne*xlmbda))**(2.0D0/3.0D0)
-
- xlmbdai = xlmbdabi(afast,atmdt,efast,te,ne)
- sumln = zeffai * xlmbdai/xlmbda
- xlnrat = (3.0D0*sqrt(pi)/4.0D0 * me/mproton * sumln)**(2.0D0/3.0D0)
- ve = c * sqrt(2.0D0*te/511.0D0)
-
- ecritfi = afast * mproton * ve*ve * xlnrat/(2.0D0 * echarge * 1.0D3)
-
- x = sqrt(efast/ecritfi)
- t1 = log( (x*x - x + 1.0D0) / ((x + 1.0D0)**2) )
- thx = (2.0D0*x - 1.0D0)/sqrt(3.0D0)
- t2 = 2.0D0*sqrt(3.0D0) *(atan(thx) + pi/6.0D0)
-
- ans = (t1 + t2) / (3.0D0 * x*x)
- fpion = ans
-
- contains
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- function xlmbdabi(mb,mth,eb,t,nelec)
-
- !! Calculates the Coulomb logarithm for ion-ion collisions
- !! author: P J Knight, CCFE, Culham Science Centre
- !! mb : input real : mass of fast particle (units of proton mass)
- !! mth : input real : mass of background ions (units of proton mass)
- !! eb : input real : energy of fast particle (keV)
- !! t : input real : density weighted average electron temp. (keV)
- !! nelec : input real : volume averaged electron density (m**-3)
- !! This function calculates the Coulomb logarithm for ion-ion
- !! collisions where the relative velocity may be large compared
- !! with the background ('mt') thermal velocity.
- !! Mikkelson and Singer, Nuc Tech/Fus, 4, 237 (1983)
- !
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- implicit none
-
- real(dp) :: xlmbdabi
-
- ! Arguments
-
- real(dp), intent(in) :: mb,mth,eb,t,nelec
-
- ! Local variables
-
- real(dp) :: ans,x1,x2
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- x1 = (t/10.0D0) * (eb/1000.0D0) * mb/(nelec/1.0D20)
- x2 = mth/(mth + mb)
-
- ans = 23.7D0 + log(x2 * sqrt(x1))
- xlmbdabi = ans
-
- end function xlmbdabi
-
- end subroutine cfnbi
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- function sigbeam(eb,te,ne,rnhe,rnc,rno,rnfe)
-
- !! Calculates the stopping cross-section for a hydrogen
- !! beam in a fusion plasma
- !! author: P J Knight, CCFE, Culham Science Centre
- !! eb : input real : beam energy (kev/amu)
- !! te : input real : electron temperature (keV)
- !! ne : input real : electron density (10^20m-3)
- !! rnhe : input real : alpha density / ne
- !! rnc : input real : carbon density /ne
- !! rno : input real : oxygen density /ne
- !! rnfe : input real : iron density /ne
- !! This function calculates the stopping cross-section (m^-2)
- !! for a hydrogen beam in a fusion plasma.
- !! Janev, Boley and Post, Nuclear Fusion 29 (1989) 2125
- !
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- implicit none
-
- real(dp) :: sigbeam
-
- ! Arguments
-
- real(dp), intent(in) :: eb,te,ne,rnhe,rnc,rno,rnfe
-
- ! Local variables
-
- real(dp) :: ans,nen,sz,s1
- real(dp), dimension(2,3,2) :: a
- real(dp), dimension(3,2,2,4) :: b
- real(dp), dimension(4) :: nn,z
-
- integer :: i,is,j,k
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- data a/ 4.40D0, 2.30D-1, 7.46D-2,-2.55D-3, 3.16D-3, 1.32D-3, &
- -2.49D-2,-1.15D-2, 2.27D-3,-6.20D-4,-2.78D-5, 3.38D-5/
-
- data b/ &
- -2.36D0, 8.49D-1,-5.88D-2,-2.50D-1, 6.77D-2,-4.48D-3, &
- 1.85D-1,-4.78D-2, 4.34D-3,-3.81D-2, 1.05D-2,-6.76D-4, &
- -1.49D0, 5.18D-1,-3.36D-2,-1.19D-1, 2.92D-2,-1.79D-3, &
- -1.54D-2, 7.18D-3, 3.41D-4,-1.50D-2, 3.66D-3,-2.04D-4, &
- -1.41D0, 4.77D-1,-3.05D-2,-1.08D-1, 2.59D-2,-1.57D-3, &
- -4.08D-4, 1.57D-3, 7.35D-4,-1.38D-2, 3.33D-3,-1.86D-4, &
- -1.03D0, 3.22D-1,-1.87D-2,-5.58D-2, 1.24D-2,-7.43D-4, &
- 1.06D-1,-3.75D-2, 3.53D-3,-3.72D-3, 8.61D-4,-5.12D-5/
-
- z(1) = 2.0D0 ; z(2) = 6.0D0 ; z(3) = 8.0D0 ; z(4) = 26.0D0
- nn(1) = rnhe ; nn(2) = rnc ; nn(3) = rno ; nn(4) = rnfe
-
- nen = ne/1.0D19
-
- s1 = 0.0D0
- do k = 1,2
- do j = 1,3
- do i = 1,2
- s1 = s1 + a(i,j,k) * (log(eb))**(i-1) * &
- (log(nen))**(j-1) * (log(te))**(k-1)
- end do
- end do
- end do
-
- ! Impurity term
-
- sz = 0.0D0
- do is = 1,4
- do k = 1,2
- do j = 1,2
- do i = 1,3
- sz = sz + b(i,j,k,is)* (log(eb))**(i-1) * &
- (log(nen))**(j-1) * (log(te))**(k-1) * &
- nn(is) * z(is) * (z(is)-1.0D0)
- end do
- end do
- end do
- end do
-
- ans = 1.0D-20 * ( exp(s1)/eb * (1.0D0 + sz) )
-
- sigbeam = max(ans,1.0D-23)
-
- end function sigbeam
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- subroutine cullhy(effrfss)
-
- !! Routine to calculate Lower Hybrid current drive efficiency
- !! author: P J Knight, CCFE, Culham Science Centre
- !! effrfss : output real : lower hybrid current drive efficiency (A/W)
- !! This routine calculates the current drive parameters for a
- !! lower hybrid system, based on the AEA FUS 172 model.
- !! AEA FUS 251: A User's Guide to the PROCESS Systems Code
- !! AEA FUS 172: Physics Assessment for the European Reactor Study
- !
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- use error_handling, only: fdiags, idiags, report_error
- use profiles_module, only: nprofile, tprofile
- use physics_variables, only: rminor, rhopedn, ne0, neped, nesep, alphan, &
- rhopedt, te0, tesep, teped, alphat, tbeta, bt, rmajor, zeff
-
- implicit none
-
- ! Arguments
-
- real(dp), intent(out) :: effrfss
-
- ! Local variables
-
- real(dp) :: blocal,dlocal,epslh,frac,gamlh,nplacc,rpenet, &
- rratio,term01,term02,term03,term04,tlocal,x
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- ! Calculate the penetration radius of the LH waves
-
- call lhrad(rratio)
- rpenet = rratio*rminor
-
- ! Local density, temperature, toroidal field at this minor radius
-
- dlocal = 1.0D-19 * nprofile(rratio,rhopedn,ne0,neped,nesep,alphan)
- tlocal = tprofile(rratio,rhopedt,te0,teped,tesep,alphat,tbeta)
- blocal = bt*rmajor/(rmajor-rpenet) ! Calculated on inboard side
-
- ! Parallel refractive index needed for plasma access
-
- frac = sqrt(dlocal)/blocal
- nplacc = frac + sqrt(1.0D0 + frac*frac)
-
- ! Local inverse aspect ratio
-
- epslh = rpenet/rmajor
-
- ! LH normalised efficiency (A/W m**-2)
-
- x = 24.0D0 / (nplacc*sqrt(tlocal))
-
- term01 = 6.1D0 / (nplacc*nplacc * (zeff+5.0D0))
- term02 = 1.0D0 + (tlocal/25.0D0)**1.16D0
- term03 = epslh**0.77D0 * sqrt(12.25D0 + x*x)
- term04 = 3.5D0*epslh**0.77D0 + x
-
- if (term03 > term04) then
- fdiags(1) = term03 ; fdiags(2) = term04
- call report_error(129)
- end if
-
- gamlh = term01 * term02 * (1.0D0 - term03/term04)
-
- ! Current drive efficiency (A/W)
-
- effrfss = gamlh / ((0.1D0*dlocal)*rmajor)
-
- contains
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- subroutine lhrad(rratio)
-
- !! Routine to calculate Lower Hybrid wave absorption radius
- !! author: P J Knight, CCFE, Culham Science Centre
- !! rratio : output real : minor radius of penetration / rminor
- !! This routine determines numerically the minor radius at which the
- !! damping of Lower Hybrid waves occurs, using a Newton-Raphson method.
- !! AEA FUS 251: A User's Guide to the PROCESS Systems Code
- !! AEA FUS 172: Physics Assessment for the European Reactor Study
- !
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- implicit none
-
- ! Arguments
-
- real(dp), intent(out) :: rratio
-
- ! Local variables
-
- real(dp) :: dgdr,drfind,g0,g1,g2,rat0,rat1,r1,r2
- integer :: lapno
- integer, parameter :: maxlap = 100
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- ! Correction to refractive index (kept within valid bounds)
-
- drfind = min(0.7D0, max(0.1D0,12.5D0/te0))
-
- ! Use Newton-Raphson method to establish the correct minor radius
- ! ratio. g is calculated as a function of r / r_minor, where g is
- ! the difference between the results of the two formulae for the
- ! energy E given in AEA FUS 172, p.58. The required minor radius
- ! ratio has been found when g is sufficiently close to zero.
-
- ! Initial guess for the minor radius ratio
-
- rat0 = 0.8D0
-
- lapno = 0
- do ; lapno = lapno+1
-
- ! Minor radius ratios either side of the latest guess
-
- r1 = rat0 - 1.0D-3*rat0
- r2 = rat0 + 1.0D-3*rat0
-
- ! Evaluate g at rat0, r1, r2
-
- call lheval(drfind,rat0,g0)
- call lheval(drfind,r1,g1)
- call lheval(drfind,r2,g2)
-
- ! Calculate gradient of g with respect to minor radius ratio
-
- dgdr = (g2-g1)/(r2-r1)
-
- ! New approximation
-
- rat1 = rat0 - g0/dgdr
-
- ! Force this approximation to lie within bounds
-
- rat1 = max(0.0001D0,rat1)
- rat1 = min(0.9999D0,rat1)
-
- ! Check the number of laps for convergence
-
- if (lapno >= maxlap) then
- idiags(1) = lapno ; call report_error(16)
- rat0 = 0.8D0
- exit
- end if
-
- ! Is g sufficiently close to zero?
-
- if (abs(g0) > 0.01D0) then
- ! No, so go around loop again
- rat0 = rat1
- else
- exit
- end if
-
- end do
-
- rratio = rat0
-
- end subroutine lhrad
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- subroutine lheval(drfind,rratio,ediff)
-
- !! Routine to evaluate the difference between electron energy
- !! expressions required to find the Lower Hybrid absorption radius
- !! author: P J Knight, CCFE, Culham Science Centre
- !! drfind : input real : correction to parallel refractive index
- !! rratio : input real : guess for radius of penetration / rminor
- !! ediff : output real : difference between the E values (keV)
- !! This routine evaluates the difference between the values calculated
- !! from the two equations for the electron energy E, given in
- !! AEA FUS 172, p.58. This difference is used to locate the Lower Hybrid
- !! wave absorption radius via a Newton-Raphson method, in calling
- !! routine lhrad.
- !! AEA FUS 251: A User's Guide to the PROCESS Systems Code
- !! AEA FUS 172: Physics Assessment for the European Reactor Study
- !
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- implicit none
-
- ! Arguments
-
- real(dp), intent(in) :: drfind,rratio
- real(dp), intent(out) :: ediff
-
- ! Local variables
-
- real(dp) :: blocal,dlocal,e1,e2,frac,nplacc,refind,tlocal
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- ! Local electron density
-
- dlocal = 1.0D-19 * nprofile(rratio,rhopedn,ne0,neped,nesep,alphan)
-
- ! Local electron temperature
-
- tlocal = tprofile(rratio,rhopedt,te0,teped,tesep,alphat,tbeta)
-
- ! Local toroidal field (evaluated at the inboard region of the flux surface)
-
- blocal = bt * rmajor/(rmajor - rratio*rminor)
-
- ! Parallel refractive index needed for plasma access
-
- frac = sqrt(dlocal)/blocal
- nplacc = frac + sqrt(1.0D0 + frac*frac)
-
- ! Total parallel refractive index
-
- refind = nplacc + drfind
-
- ! First equation for electron energy E
-
- e1 = 511.0D0 * (sqrt(1.0D0 + 1.0D0/(refind*refind)) - 1.0D0)
-
- ! Second equation for E
-
- e2 = 7.0D0 * tlocal
-
- ! Difference
-
- ediff = e1-e2
-
- end subroutine lheval
-
- end subroutine cullhy
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- subroutine culecd(effrfss)
-
- !! Routine to calculate Electron Cyclotron current drive efficiency
- !! author: M R O'Brien, CCFE, Culham Science Centre
- !! author: P J Knight, CCFE, Culham Science Centre
- !! effrfss : output real : electron cyclotron current drive efficiency (A/W)
- !! This routine calculates the current drive parameters for a
- !! electron cyclotron system, based on the AEA FUS 172 model.
- !! AEA FUS 251: A User's Guide to the PROCESS Systems Code
- !! AEA FUS 172: Physics Assessment for the European Reactor Study
- !
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- use profiles_module, only: tprofile, nprofile
- use physics_variables, only: rhopedt, te0, teped, tesep, alphat, tbeta, &
- rhopedn, ne0, nesep, neped, alphan, rminor, rmajor, zeff
-
- implicit none
-
- real(dp), intent(out) :: effrfss
-
- ! Local variables
-
- real(dp) :: cosang,coulog,dlocal,ecgam,ecgam1,ecgam2,ecgam3,ecgam4, &
- epsloc,rrr,tlocal,zlocal
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- ! Local plasma parameters : take r = a/3
- rrr = 1.0D0/3.0D0
-
- ! Temperature
- tlocal = tprofile(rrr,rhopedt,te0,teped,tesep,alphat,tbeta)
-
- ! Density (10**20 m**-3)
- dlocal = 1.0D-20 * nprofile(rrr,rhopedn,ne0,neped,nesep,alphan)
-
- ! Inverse aspect ratio
- epsloc = rrr * rminor/rmajor
-
- ! Effective charge (use average value)
- zlocal = zeff
-
- ! Coulomb logarithm for ion-electron collisions
- ! (From J. A. Wesson, 'Tokamaks', Clarendon Press, Oxford, p.293)
- coulog = 15.2D0 - 0.5D0*log(dlocal) + log(tlocal)
-
- ! Calculate normalised current drive efficiency at four different
- ! poloidal angles, and average.
- ! cosang = cosine of the poloidal angle at which ECCD takes place
- ! = +1 outside, -1 inside.
- cosang = 1.0D0 ; call eccdef(tlocal,epsloc,zlocal,cosang,coulog,ecgam1)
- cosang = 0.5D0 ; call eccdef(tlocal,epsloc,zlocal,cosang,coulog,ecgam2)
- cosang = -0.5D0 ; call eccdef(tlocal,epsloc,zlocal,cosang,coulog,ecgam3)
- cosang = -1.0D0 ; call eccdef(tlocal,epsloc,zlocal,cosang,coulog,ecgam4)
-
- ! Normalised current drive efficiency (A/W m**-2)
- ecgam = 0.25D0 * (ecgam1+ecgam2+ecgam3+ecgam4)
-
- ! Current drive efficiency (A/W)
- effrfss = ecgam/(dlocal*rmajor)
-
- contains
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- subroutine eccdef(tlocal,epsloc,zlocal,cosang,coulog,ecgam)
-
- !! Routine to calculate Electron Cyclotron current drive efficiency
- !! author: M R O'Brien, CCFE, Culham Science Centre
- !! author: P J Knight, CCFE, Culham Science Centre
- !! tlocal : input real : local electron temperature (keV)
- !! epsloc : input real : local inverse aspect ratio
- !! zlocal : input real : local plasma effective charge
- !! cosang : input real : cosine of the poloidal angle at which ECCD takes
- !! place (+1 outside, -1 inside)
- !! coulog : input real : local coulomb logarithm for ion-electron collisions
- !! ecgam : output real : normalised current drive efficiency (A/W m**-2)
- !! This routine calculates the current drive parameters for a
- !! electron cyclotron system, based on the AEA FUS 172 model.
- !! It works out the ECCD efficiency using the formula due to Cohen
- !! quoted in the ITER Physics Design Guidelines : 1989
- !! (but including division by the Coulomb Logarithm omitted from
- !! IPDG89). We have assumed gamma**2-1 << 1, where gamma is the
- !! relativistic factor. The notation follows that in IPDG89.
- !!
The answer ECGAM is the normalised efficiency nIR/P with n the
- !! local density in 10**20 /m**3, I the driven current in MAmps,
- !! R the major radius in metres, and P the absorbed power in MWatts.
- !! AEA FUS 251: A User's Guide to the PROCESS Systems Code
- !! AEA FUS 172: Physics Assessment for the European Reactor Study
- !! ITER Physics Design Guidelines: 1989 [IPDG89], N. A. Uckan et al,
- !! ITER Documentation Series No.10, IAEA/ITER/DS/10, IAEA, Vienna, 1990
- !
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- use error_handling, only: report_error
-
- implicit none
-
- ! Arguments
-
- real(dp), intent(in) :: tlocal,epsloc,zlocal,cosang,coulog
- real(dp), intent(out) :: ecgam
-
- ! Local variables
-
- real(dp) :: f,facm,fp,h,hp,lam,lams,mcsq,palpha,palphap,palphaps, &
- palphas,y
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- mcsq = 9.1095D-31 * 2.9979D8**2 /(1.0D3*1.6022D-19) ! keV
- f = 16.0D0 * (tlocal/mcsq)**2
-
- ! fp is the derivative of f with respect to gamma, the relativistic
- ! factor, taken equal to 1 + 2T/(m c**2)
-
- fp = 16.0D0 * tlocal/mcsq
-
- ! lam is IPDG89's lambda. LEGEND calculates the Legendre function of
- ! order alpha and argument lam, palpha, and its derivative, palphap.
- ! Here alpha satisfies alpha(alpha+1) = -8/(1+zlocal). alpha is of the
- ! form (-1/2 + ix), with x a real number and i = sqrt(-1).
-
- lam = 1.0D0
- call legend(zlocal,lam,palpha,palphap)
-
- lams = sqrt(2.0D0*epsloc/(1.0D0+epsloc))
- call legend(zlocal,lams,palphas,palphaps)
-
- ! hp is the derivative of IPDG89's h function with respect to lam
-
- h = -4.0D0 * lam/(zlocal+5.0D0) * (1.0D0-lams*palpha/(lam*palphas))
- hp = -4.0D0 / (zlocal+5.0D0) * (1.0D0-lams*palphap/palphas)
-
- ! facm is IPDG89's momentum conserving factor
-
- facm = 1.5D0
- y = mcsq/(2.0D0*tlocal) * (1.0D0 + epsloc*cosang)
-
- ! We take the negative of the IPDG89 expression to get a positive
- ! number
-
- ecgam = -7.8D0 * facm * sqrt((1.0D0+epsloc)/(1.0D0-epsloc)) / coulog &
- * (h*fp - 0.5D0*y*f*hp)
-
- if (ecgam < 0.0D0) call report_error(17)
-
- end subroutine eccdef
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- subroutine legend(zlocal,arg,palpha,palphap)
-
- !! Routine to calculate Legendre function and its derivative
- !! author: M R O'Brien, CCFE, Culham Science Centre
- !! author: P J Knight, CCFE, Culham Science Centre
- !! zlocal : input real : local plasma effective charge
- !! arg : input real : argument of Legendre function
- !! palpha : output real : value of Legendre function
- !! palphap : output real : derivative of Legendre function
- !! This routine calculates the Legendre function palpha
- !! of argument arg and order
- !! alpha = -0.5 + i sqrt(xisq),
- !! and its derivative palphap.
- !!
This Legendre function is a conical function and we use the series
- !! in xisq given in Abramowitz and Stegun. The
- !! derivative is calculated from the derivative of this series.
- !!
The derivatives were checked by calculating palpha for
- !! neighbouring arguments. The calculation of palpha for zero
- !! argument was checked by comparison with the expression
- !! palpha(0) = 1/sqrt(pi) * cos(pi*alpha/2) * gam1 / gam2
- !! (Abramowitz and Stegun, eqn 8.6.1). Here gam1 and
- !! gam2 are the Gamma functions of arguments
- !! 0.5*(1+alpha) and 0.5*(2+alpha) respectively.
- !! Abramowitz and Stegun, equation 8.12.1
- !
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- use error_handling, only: fdiags, idiags, report_error
-
- implicit none
-
- real(dp), intent(in) :: zlocal,arg
- real(dp), intent(out) :: palpha,palphap
-
- ! Local variables
-
- real(dp) :: arg2,pold,poldp,pterm,sinsq,term1,term2,xisq
- integer :: n
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- ! Check for invalid argument
-
- if (abs(arg) > (1.0D0+1.0D-10)) then
- fdiags(1) = arg ; call report_error(18)
- end if
-
- arg2 = min(arg, (1.0D0-1.0D-10))
- sinsq = 0.5D0*(1.0D0-arg2)
- xisq = 0.25D0*(32.0D0*zlocal/(zlocal+1.0D0) - 1.0D0)
- palpha = 1.0D0
- pold = 1.0D0
- pterm = 1.0D0
- palphap = 0.0D0
- poldp = 0.0D0
-
- do n = 1,10001
-
- ! Check for convergence every 20 iterations
-
- if ((n > 1).and.(mod(n,20) == 1)) then
- term1 = 1.0D-10 * max(abs(pold),abs(palpha))
- term2 = 1.0D-10 * max(abs(poldp),abs(palphap))
-
- if ( (abs(pold-palpha) < term1) .and. &
- (abs(poldp-palphap) < term2) ) return
-
- pold = palpha
- poldp = palphap
- end if
-
- pterm = pterm * (4.0D0*xisq+(2.0D0*n - 1.0D0)**2) / &
- (2.0D0*n)**2 * sinsq
- palpha = palpha + pterm
- palphap = palphap - n*pterm/(1.0D0-arg2)
-
- end do
-
- ! Code will only get this far if convergence has failed
-
- call report_error(19)
-
- end subroutine legend
-
- end subroutine culecd
-
- ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- subroutine culnbi(effnbss,fpion,fshine)
-
- !! Routine to calculate Neutral Beam current drive parameters
- !! author: P J Knight, CCFE, Culham Science Centre
- !! effnbss : output real : neutral beam current drive efficiency (A/W)
- !! fpion : output real : fraction of NB power given to ions
- !! fshine : output real : shine-through fraction of beam
- !! This routine calculates Neutral Beam current drive parameters
- !! using the corrections outlined in AEA FUS 172 to the ITER method.
- !!
The result cannot be guaranteed for devices with aspect ratios far - !! from that of ITER (approx. 2.8). - !! AEA FUS 251: A User's Guide to the PROCESS Systems Code - !! AEA FUS 172: Physics Assessment for the European Reactor Study - ! - ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - use error_handling, only: fdiags, idiags, report_error - use current_drive_variables, only: enbeam, frbeam, taubeam, ftritbm - use physics_variables, only: eps, rmajor, abeam, te, dene, ralpne, rncne, & - rnone, rnfene, dnla, deni, ten, zeffai, dlamie, alphan, alphat, aspect, & - rminor, zeff - - implicit none - - real(dp), intent(out) :: effnbss,fpion,fshine - - ! Local variables - - real(dp) :: dend,dent,dpath,sigstop - - ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - ! Check argument sanity - - if ((1.0D0+eps) < frbeam) then - fdiags(1) = eps ; fdiags(2) = frbeam - call report_error(20) - end if - - ! Calculate beam path length to centre - - dpath = rmajor * sqrt( (1.0D0 + eps)**2 - frbeam**2) - - ! Calculate beam stopping cross-section - - sigstop = sigbeam(enbeam/abeam,te,dene,ralpne,rncne,rnone,rnfene) - - ! Calculate number of decay lengths to centre - - taubeam = dpath * dnla * sigstop - - ! Shine-through fraction of beam - - fshine = exp(-2.0D0 * dpath*dnla*sigstop) - fshine = max(fshine, 1.0D-20) - - ! Deuterium and tritium beam densities - - dend = deni * (1.0D0-ftritbm) - dent = deni * ftritbm - - ! Power split to ions / electrons - - call cfnbi(abeam,enbeam,ten,dene,dend,dent,zeffai,dlamie,fpion) - - ! Current drive efficiency - - effnbss = etanb2(abeam,alphan,alphat,aspect,dene,dnla,enbeam, & - frbeam,fshine,rmajor,rminor,ten,zeff) - - contains - - ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - function etanb2(abeam,alphan,alphat,aspect,dene,dnla,enbeam,frbeam, & - fshine,rmajor,rminor,ten,zeff) - !! Routine to find neutral beam current drive efficiency - !! using the ITER 1990 formulation, plus correction terms - !! outlined in Culham Report AEA FUS 172 - !! author: P J Knight, CCFE, Culham Science Centre - !! abeam : input real : beam ion mass (amu) - !! alphan : input real : density profile factor - !! alphat : input real : temperature profile factor - !! aspect : input real : aspect ratio - !! dene : input real : volume averaged electron density (m**-3) - !! dnla : input real : line averaged electron density (m**-3) - !! enbeam : input real : neutral beam energy (keV) - !! frbeam : input real : R_tangent / R_major for neutral beam injection - !! fshine : input real : shine-through fraction of beam - !! rmajor : input real : plasma major radius (m) - !! rminor : input real : plasma minor radius (m) - !! ten : input real : density weighted average electron temperature (keV) - !! zeff : input real : plasma effective charge - !! This routine calculates the current drive efficiency in A/W of - !! a neutral beam system, based on the 1990 ITER model, - !! plus correction terms outlined in Culham Report AEA FUS 172. - !!
The formulae are from AEA FUS 172, unless denoted by IPDG89. - !! AEA FUS 251: A User's Guide to the PROCESS Systems Code - !! AEA FUS 172: Physics Assessment for the European Reactor Study - !! ITER Physics Design Guidelines: 1989 [IPDG89], N. A. Uckan et al, - !! ITER Documentation Series No.10, IAEA/ITER/DS/10, IAEA, Vienna, 1990 - ! - ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - implicit none - - real(dp) :: etanb2 - - ! Arguments - - real(dp), intent(in) :: abeam,alphan,alphat,aspect,dene,dnla, & - enbeam,frbeam,fshine,rmajor,rminor,ten,zeff - - ! Local variables - - real(dp) :: abd,bbd,d,dene20,dnla20,dnorm,ebmev,ebnorm, & - ecrit,epseff,epsitr,eps1,ffac,gamnb,gfac,j0,nnorm,r,xj, & - xjs,yj,zbeam - - ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - ! Charge of beam ions - zbeam = 1.0D0 - - ! Fitting factor (IPDG89) - bbd = 1.0D0 - - ! Volume averaged electron density (10**20 m**-3) - dene20 = dene/1.0D20 - - ! Line averaged electron density (10**20 m**-3) - dnla20 = dnla/1.0D20 - - ! Critical energy (MeV) (power to electrons = power to ions) (IPDG89) - ! N.B. ten is in keV - ecrit = 0.01D0 * abeam * ten - - ! Beam energy in MeV - ebmev = enbeam/1.0D3 - - ! x and y coefficients of function J0(x,y) (IPDG89) - xjs = ebmev/(bbd*ecrit) - xj = sqrt(xjs) - - yj = 0.8D0 * zeff/abeam - - ! Fitting function J0(x,y) - j0 = xjs / (4.0D0 + 3.0D0*yj + xjs *(xj + 1.39D0 + & - 0.61D0 * yj**0.7D0)) - - ! Effective inverse aspect ratio, with a limit on its maximum value - epseff = min(0.2D0, (0.5D0/aspect)) - - ! Reduction in the reverse electron current - ! due to neoclassical effects - gfac = (1.55D0 + 0.85D0/zeff)*sqrt(epseff) - & - (0.2D0 + 1.55D0/zeff)*epseff - - ! Reduction in the net beam driven current - ! due to the reverse electron current - ffac = 1.0D0 - (zbeam/zeff) * (1.0D0 - gfac) - - ! Normalisation to allow results to be valid for - ! non-ITER plasma size and density: - - ! Line averaged electron density (10**20 m**-3) normalised to ITER - nnorm = 1.0D0 - - ! Distance along beam to plasma centre - r = max(rmajor,rmajor*frbeam) - eps1 = rminor/r - - if ((1.0D0+eps1) < frbeam) then - fdiags(1) = eps1 ; fdiags(2) = frbeam - call report_error(21) - end if - - d = rmajor * sqrt( (1.0D0+eps1)**2 - frbeam**2) - - ! Distance along beam to plasma centre for ITER - ! assuming a tangency radius equal to the major radius - epsitr = 2.15D0/6.0D0 - dnorm = 6.0D0 * sqrt(2.0D0*epsitr + epsitr**2) - - ! Normalisation to beam energy (assumes a simplified formula for - ! the beam stopping cross-section) - ebnorm = ebmev * ( (nnorm*dnorm)/(dnla20*d) )**(1.0D0/0.78D0) - - ! A_bd fitting coefficient, after normalisation with ebnorm - abd = 0.107D0 * (1.0D0 - 0.35D0*alphan + 0.14D0*alphan**2) * & - (1.0D0 - 0.21D0*alphat) * (1.0D0 - 0.2D0*ebnorm + 0.09D0*ebnorm**2) - - ! Normalised current drive efficiency (A/W m**-2) (IPDG89) - gamnb = 5.0D0 * abd * 0.1D0*ten * (1.0D0-fshine) * frbeam * & - j0/0.2D0 * ffac - - ! Current drive efficiency (A/W) - etanb2 = gamnb / (dene20*rmajor) - - end function etanb2 - - end subroutine culnbi - -end module current_drive_module diff --git a/tests/unit/test_current_drive.py b/tests/unit/test_current_drive.py new file mode 100644 index 0000000000..2783f00cf1 --- /dev/null +++ b/tests/unit/test_current_drive.py @@ -0,0 +1,584 @@ +import pytest +from typing import NamedTuple, Any + + +from process.fortran import ( + current_drive_variables, + cost_variables, + physics_variables, + heat_transport_variables, +) +from process.current_drive import CurrentDrive + + +@pytest.fixture +def current_drive(): + """Provides CurrentDrive object for testing. + + :returns current_drive: initialised CurrentDrive object + :rtype: process.current_drive.CurrentDrive + """ + return CurrentDrive() + + +class CudrivParam(NamedTuple): + pinjwpfix: Any = None + + pinjwp: Any = None + + echpwr: Any = None + + pnbeam: Any = None + + plhybd: Any = None + + cnbeam: Any = None + + porbitlossmw: Any = None + + iefrf: Any = None + + iefrffix: Any = None + + pheat: Any = None + + pheatfix: Any = None + + pinjfixmw: Any = None + + irfcd: Any = None + + feffcd: Any = None + + fpion: Any = None + + nbshinef: Any = None + + gamcd: Any = None + + gamma_ecrh: Any = None + + etalh: Any = None + + etacd: Any = None + + etacdfix: Any = None + + etaech: Any = None + + forbitloss: Any = None + + pinjmw: Any = None + + pwpnb: Any = None + + etanbi: Any = None + + enbeam: Any = None + + effcd: Any = None + + pwplh: Any = None + + echwpow: Any = None + + pnbitot: Any = None + + nbshinemw: Any = None + + pinjemw: Any = None + + pinjimw: Any = None + + bigq: Any = None + + bootipf: Any = None + + bscfmax: Any = None + + taubeam: Any = None + + pinjalw: Any = None + + nbshield: Any = None + + frbeam: Any = None + + rtanbeam: Any = None + + rtanmax: Any = None + + diaipf: Any = None + + psipf: Any = None + + plasipf: Any = None + + harnum: Any = None + + xi_ebw: Any = None + + dene: Any = None + + te: Any = None + + rmajor: Any = None + + ten: Any = None + + zeff: Any = None + + dlamee: Any = None + + beta: Any = None + + rhopedt: Any = None + + rhopedn: Any = None + + te0: Any = None + + teped: Any = None + + tesep: Any = None + + alphat: Any = None + + alphan: Any = None + + ne0: Any = None + + nesep: Any = None + + neped: Any = None + + bt: Any = None + + rminor: Any = None + + tbeta: Any = None + + plascur: Any = None + + ipedestal: Any = None + + faccd: Any = None + + ignite: Any = None + + pohmmw: Any = None + + powfmw: Any = None + + facoh: Any = None + + fvsbrnni: Any = None + + startupratio: Any = None + + iprint: Any = None + + outfile: Any = None + + expected_pinjwp: Any = None + + expected_echpwr: Any = None + + expected_gamcd: Any = None + + expected_etacd: Any = None + + expected_pinjmw: Any = None + + expected_effcd: Any = None + + expected_echwpow: Any = None + + expected_pinjemw: Any = None + + expected_bigq: Any = None + + +@pytest.mark.parametrize( + "cudrivparam", + ( + CudrivParam( + pinjwpfix=0, + pinjwp=0, + echpwr=0, + pnbeam=0, + plhybd=0, + cnbeam=0, + porbitlossmw=0, + iefrf=10, + iefrffix=0, + pheat=75, + pheatfix=0, + pinjfixmw=0, + irfcd=1, + feffcd=1, + fpion=0.5, + nbshinef=0, + gamcd=0, + gamma_ecrh=0.30000000000000004, + etalh=0.29999999999999999, + etacd=0, + etacdfix=0, + etaech=0.5, + forbitloss=0, + pinjmw=0, + pwpnb=0, + etanbi=0.29999999999999999, + enbeam=1000, + effcd=0, + pwplh=0, + echwpow=0, + pnbitot=0, + nbshinemw=0, + pinjemw=0, + pinjimw=0, + bigq=0, + bootipf=0.27635918746616817, + bscfmax=0.95000000000000007, + taubeam=0, + pinjalw=200, + nbshield=0.5, + frbeam=1.05, + rtanbeam=0, + rtanmax=0, + diaipf=0, + psipf=0, + plasipf=0.27635918746616817, + harnum=1, + xi_ebw=0.80000000000000004, + dene=7.5e19, + te=12, + rmajor=8, + ten=12.626131115905864, + zeff=2.0909945616489103, + dlamee=17.510652035055571, + beta=0.030000000000000006, + rhopedt=0.94000000000000006, + rhopedn=0.94000000000000006, + te0=24.402321098330372, + teped=5.5, + tesep=0.10000000000000001, + alphat=1.45, + alphan=1, + ne0=8.515060981068918e19, + nesep=4.1177885154594193e19, + neped=7.000240476281013e19, + bt=5.7000000000000002, + rminor=2.6666666666666665, + tbeta=2, + plascur=18398455.678867526, + ipedestal=1, + faccd=0.12364081253383186, + ignite=0, + pohmmw=0, + powfmw=0, + facoh=0.59999999999999998, + fvsbrnni=0.40000000000000002, + startupratio=1, + iprint=0, + outfile=11, + expected_pinjwp=240.99200038011492, + expected_echpwr=120.49600019005746, + expected_gamcd=0.30000000000000004, + expected_etacd=0.5, + expected_pinjmw=120.49600019005746, + expected_effcd=0.05000000000000001, + expected_echwpow=240.99200038011492, + expected_pinjemw=120.49600019005746, + expected_bigq=0, + ), + CudrivParam( + pinjwpfix=0, + pinjwp=240.99200038011492, + echpwr=120.49600019005746, + pnbeam=0, + plhybd=0, + cnbeam=0, + porbitlossmw=0, + iefrf=10, + iefrffix=0, + pheat=75, + pheatfix=0, + pinjfixmw=0, + irfcd=1, + feffcd=1, + fpion=0.5, + nbshinef=0, + gamcd=0.30000000000000004, + gamma_ecrh=0.30000000000000004, + etalh=0.29999999999999999, + etacd=0.5, + etacdfix=0, + etaech=0.5, + forbitloss=0, + pinjmw=120.49600019005746, + pwpnb=0, + etanbi=0.29999999999999999, + enbeam=1000, + effcd=0.05000000000000001, + pwplh=0, + echwpow=240.99200038011492, + pnbitot=0, + nbshinemw=0, + pinjemw=120.49600019005746, + pinjimw=0, + bigq=0, + bootipf=0.27635918746616817, + bscfmax=0.95000000000000007, + taubeam=0, + pinjalw=200, + nbshield=0.5, + frbeam=1.05, + rtanbeam=8.4000000000000004, + rtanmax=13.179564451855533, + diaipf=0, + psipf=0, + plasipf=0.27635918746616817, + harnum=1, + xi_ebw=0.80000000000000004, + dene=7.5e19, + te=12, + rmajor=8, + ten=12.626131115905864, + zeff=2.0909945616489103, + dlamee=17.510652035055571, + beta=0.030000000000000006, + rhopedt=0.94000000000000006, + rhopedn=0.94000000000000006, + te0=24.402321098330372, + teped=5.5, + tesep=0.10000000000000001, + alphat=1.45, + alphan=1, + ne0=8.515060981068918e19, + nesep=4.1177885154594193e19, + neped=7.000240476281013e19, + bt=5.7000000000000002, + rminor=2.6666666666666665, + tbeta=2, + plascur=18398455.678867526, + ipedestal=1, + faccd=0.12364081253383186, + ignite=0, + pohmmw=0.76707314489379119, + powfmw=1051.6562748933977, + facoh=0.59999999999999998, + fvsbrnni=0.40000000000000002, + startupratio=1, + iprint=0, + outfile=11, + expected_pinjwp=240.99200038011492, + expected_echpwr=120.49600019005746, + expected_gamcd=0.30000000000000004, + expected_etacd=0.5, + expected_pinjmw=120.49600019005746, + expected_effcd=0.05000000000000001, + expected_echwpow=240.99200038011492, + expected_pinjemw=120.49600019005746, + expected_bigq=8.6725187311435423, + ), + ), +) +def test_cudriv(cudrivparam, monkeypatch, current_drive): + """ + Automatically generated Regression Unit Test for cudriv. + + This test was generated using data from tests/regression/scenarios/large-tokamak/IN.DAT. + + :param cudrivparam: the data used to mock and assert in this test. + :type cudrivparam: cudrivparam + + :param monkeypatch: pytest fixture used to mock module/class variables + :type monkeypatch: _pytest.monkeypatch.monkeypatch + """ + + monkeypatch.setattr(heat_transport_variables, "pinjwpfix", cudrivparam.pinjwpfix) + + monkeypatch.setattr(heat_transport_variables, "pinjwp", cudrivparam.pinjwp) + + monkeypatch.setattr(current_drive_variables, "echpwr", cudrivparam.echpwr) + + monkeypatch.setattr(current_drive_variables, "pnbeam", cudrivparam.pnbeam) + + monkeypatch.setattr(current_drive_variables, "plhybd", cudrivparam.plhybd) + + monkeypatch.setattr(current_drive_variables, "cnbeam", cudrivparam.cnbeam) + + monkeypatch.setattr( + current_drive_variables, "porbitlossmw", cudrivparam.porbitlossmw + ) + + monkeypatch.setattr(current_drive_variables, "iefrf", cudrivparam.iefrf) + + monkeypatch.setattr(current_drive_variables, "iefrffix", cudrivparam.iefrffix) + + monkeypatch.setattr(current_drive_variables, "pheat", cudrivparam.pheat) + + monkeypatch.setattr(current_drive_variables, "pheatfix", cudrivparam.pheatfix) + + monkeypatch.setattr(current_drive_variables, "pinjfixmw", cudrivparam.pinjfixmw) + + monkeypatch.setattr(current_drive_variables, "irfcd", cudrivparam.irfcd) + + monkeypatch.setattr(current_drive_variables, "feffcd", cudrivparam.feffcd) + + monkeypatch.setattr(current_drive_variables, "fpion", cudrivparam.fpion) + + monkeypatch.setattr(current_drive_variables, "nbshinef", cudrivparam.nbshinef) + + monkeypatch.setattr(current_drive_variables, "gamcd", cudrivparam.gamcd) + + monkeypatch.setattr(current_drive_variables, "gamma_ecrh", cudrivparam.gamma_ecrh) + + monkeypatch.setattr(current_drive_variables, "etalh", cudrivparam.etalh) + + monkeypatch.setattr(current_drive_variables, "etacd", cudrivparam.etacd) + + monkeypatch.setattr(current_drive_variables, "etacdfix", cudrivparam.etacdfix) + + monkeypatch.setattr(current_drive_variables, "etaech", cudrivparam.etaech) + + monkeypatch.setattr(current_drive_variables, "forbitloss", cudrivparam.forbitloss) + + monkeypatch.setattr(current_drive_variables, "pinjmw", cudrivparam.pinjmw) + + monkeypatch.setattr(current_drive_variables, "pwpnb", cudrivparam.pwpnb) + + monkeypatch.setattr(current_drive_variables, "etanbi", cudrivparam.etanbi) + + monkeypatch.setattr(current_drive_variables, "enbeam", cudrivparam.enbeam) + + monkeypatch.setattr(current_drive_variables, "effcd", cudrivparam.effcd) + + monkeypatch.setattr(current_drive_variables, "pwplh", cudrivparam.pwplh) + + monkeypatch.setattr(current_drive_variables, "echwpow", cudrivparam.echwpow) + + monkeypatch.setattr(current_drive_variables, "pnbitot", cudrivparam.pnbitot) + + monkeypatch.setattr(current_drive_variables, "nbshinemw", cudrivparam.nbshinemw) + + monkeypatch.setattr(current_drive_variables, "pinjemw", cudrivparam.pinjemw) + + monkeypatch.setattr(current_drive_variables, "pinjimw", cudrivparam.pinjimw) + + monkeypatch.setattr(current_drive_variables, "bigq", cudrivparam.bigq) + + monkeypatch.setattr(current_drive_variables, "bootipf", cudrivparam.bootipf) + + monkeypatch.setattr(current_drive_variables, "bscfmax", cudrivparam.bscfmax) + + monkeypatch.setattr(current_drive_variables, "taubeam", cudrivparam.taubeam) + + monkeypatch.setattr(current_drive_variables, "pinjalw", cudrivparam.pinjalw) + + monkeypatch.setattr(current_drive_variables, "nbshield", cudrivparam.nbshield) + + monkeypatch.setattr(current_drive_variables, "frbeam", cudrivparam.frbeam) + + monkeypatch.setattr(current_drive_variables, "rtanbeam", cudrivparam.rtanbeam) + + monkeypatch.setattr(current_drive_variables, "rtanmax", cudrivparam.rtanmax) + + monkeypatch.setattr(current_drive_variables, "diaipf", cudrivparam.diaipf) + + monkeypatch.setattr(current_drive_variables, "psipf", cudrivparam.psipf) + + monkeypatch.setattr(current_drive_variables, "plasipf", cudrivparam.plasipf) + + monkeypatch.setattr(current_drive_variables, "harnum", cudrivparam.harnum) + + monkeypatch.setattr(current_drive_variables, "xi_ebw", cudrivparam.xi_ebw) + + monkeypatch.setattr(physics_variables, "dene", cudrivparam.dene) + + monkeypatch.setattr(physics_variables, "te", cudrivparam.te) + + monkeypatch.setattr(physics_variables, "rmajor", cudrivparam.rmajor) + + monkeypatch.setattr(physics_variables, "ten", cudrivparam.ten) + + monkeypatch.setattr(physics_variables, "zeff", cudrivparam.zeff) + + monkeypatch.setattr(physics_variables, "dlamee", cudrivparam.dlamee) + + monkeypatch.setattr(physics_variables, "beta", cudrivparam.beta) + + monkeypatch.setattr(physics_variables, "rhopedt", cudrivparam.rhopedt) + + monkeypatch.setattr(physics_variables, "rhopedn", cudrivparam.rhopedn) + + monkeypatch.setattr(physics_variables, "te0", cudrivparam.te0) + + monkeypatch.setattr(physics_variables, "teped", cudrivparam.teped) + + monkeypatch.setattr(physics_variables, "tesep", cudrivparam.tesep) + + monkeypatch.setattr(physics_variables, "alphat", cudrivparam.alphat) + + monkeypatch.setattr(physics_variables, "alphan", cudrivparam.alphan) + + monkeypatch.setattr(physics_variables, "ne0", cudrivparam.ne0) + + monkeypatch.setattr(physics_variables, "nesep", cudrivparam.nesep) + + monkeypatch.setattr(physics_variables, "neped", cudrivparam.neped) + + monkeypatch.setattr(physics_variables, "bt", cudrivparam.bt) + + monkeypatch.setattr(physics_variables, "rminor", cudrivparam.rminor) + + monkeypatch.setattr(physics_variables, "tbeta", cudrivparam.tbeta) + + monkeypatch.setattr(physics_variables, "plascur", cudrivparam.plascur) + + monkeypatch.setattr(physics_variables, "ipedestal", cudrivparam.ipedestal) + + monkeypatch.setattr(physics_variables, "faccd", cudrivparam.faccd) + + monkeypatch.setattr(physics_variables, "ignite", cudrivparam.ignite) + + monkeypatch.setattr(physics_variables, "pohmmw", cudrivparam.pohmmw) + + monkeypatch.setattr(physics_variables, "powfmw", cudrivparam.powfmw) + + monkeypatch.setattr(physics_variables, "facoh", cudrivparam.facoh) + + monkeypatch.setattr(physics_variables, "fvsbrnni", cudrivparam.fvsbrnni) + + monkeypatch.setattr(cost_variables, "startupratio", cudrivparam.startupratio) + + current_drive.cudriv(output=False) + + assert heat_transport_variables.pinjwp == pytest.approx(cudrivparam.expected_pinjwp) + + assert current_drive_variables.echpwr == pytest.approx(cudrivparam.expected_echpwr) + + assert current_drive_variables.gamcd == pytest.approx(cudrivparam.expected_gamcd) + + assert current_drive_variables.etacd == pytest.approx(cudrivparam.expected_etacd) + + assert current_drive_variables.pinjmw == pytest.approx(cudrivparam.expected_pinjmw) + + assert current_drive_variables.effcd == pytest.approx(cudrivparam.expected_effcd) + + assert current_drive_variables.echwpow == pytest.approx( + cudrivparam.expected_echwpow + ) + + assert current_drive_variables.pinjemw == pytest.approx( + cudrivparam.expected_pinjemw + ) + + assert current_drive_variables.bigq == pytest.approx(cudrivparam.expected_bigq) + + +def test_sigbeam(current_drive): + assert current_drive.sigbeam( + 1e3, 13.07, 8.0e-1, 0.1, 1e-4, 1e-4, 1e-4 + ) == pytest.approx(2.013589662302492e-11) diff --git a/tests/unit/test_stellarator.py b/tests/unit/test_stellarator.py index 4ebee6ad7a..3ffcfd038c 100644 --- a/tests/unit/test_stellarator.py +++ b/tests/unit/test_stellarator.py @@ -23,6 +23,7 @@ from process.hcpb import CCFE_HCPB from process.blanket_library import BlanketLibrary from process.fw import Fw +from process.current_drive import CurrentDrive @pytest.fixture @@ -40,6 +41,7 @@ def stellarator(): Power(), PlasmaProfile(), CCFE_HCPB(BlanketLibrary(Fw())), + CurrentDrive(), )