Coverage for python/gsfit/database_readers/st40_spider_mdsplus/setup_coils.py: 0%

138 statements  

« prev     ^ index     » next       coverage.py v7.15.0, created at 2026-07-07 13:12 +0000

1# mypy: ignore-errors 

2# TODO: need to fix mypy errors 

3 

4import typing 

5from typing import TYPE_CHECKING 

6 

7import mdsthin 

8import numpy as np 

9import numpy.typing as npt 

10from gsfit_rs import Coils 

11from scipy.constants import mu_0 

12from st40_database import GetData 

13 

14if TYPE_CHECKING: 

15 from . import DatabaseReader 

16 

17# # The PF coil's are defined in the `P2.3/coil.dat` file, in the `SPIDER` GitLab repository. 

18# # On `smaug2`, this file is at `/home/alexei.dnestrovskij/ASTRA_TE_user/astra_user/exp/equ/P2.3/coil.dat` 

19# spider_coil_description = dict( 

20# DIVT=dict(rc=0.1930, zc=0.7907, wc=0.0566, hc=0.1351, ndi=20), 

21# DIVB=dict(rc=0.1930, zc=-0.7907, wc=0.0566, hc=0.1351, ndi=20), 

22# BVLT=dict(rc=1.3860, zc=0.422, wc=0.11, hc=0.1156, ndi=12), 

23# BVLB=dict(rc=1.3860, zc=-0.422, wc=0.11, hc=0.1156, ndi=12), 

24# BVUT=dict(rc=1.0470, zc=1.0390, wc=0.0552, hc=0.1382, ndi=20), 

25# BVUB=dict(rc=1.0470, zc=-1.0390, wc=0.0552, hc=0.1382, ndi=20), 

26# SOL=dict(rc=0.1295, zc=0.0000, wc=0.0161, hc=1.4711, ndi=64), 

27# MCT=dict(rc=0.7500, zc=0.3550, wc=0.0407, hc=0.0467, ndi=12), 

28# MCB=dict(rc=0.7500, zc=-0.3550, wc=0.0407, hc=0.0467, ndi=12), 

29# PSHT1=dict(rc=0.8518, zc=0.669, wc=0.007, hc=0.0232, ndi=4), 

30# PSHB1=dict(rc=0.8518, zc=-0.669, wc=0.007, hc=0.0232, ndi=4), 

31# PSHT2=dict(rc=0.833, zc=0.7016, wc=0.007, hc=0.0232, ndi=4), 

32# PSHB2=dict(rc=0.833, zc=-0.7016, wc=0.007, hc=0.0232, ndi=4), 

33# ) 

34 

35 

36# spider_coil_description = dict( 

37# DIVT=dict(rc=0.1930, zc=0.7907, wc=0.0566, hc=0.1351, ndi=20), 

38# DIVB=dict(rc=0.1930, zc=-0.7907, wc=0.0566, hc=0.1351, ndi=20), 

39# BVLT=dict(rc=1.3860, zc=0.422, wc=0.11, hc=0.1156, ndi=12), 

40# BVLB=dict(rc=1.3860, zc=-0.422, wc=0.11, hc=0.1156, ndi=12), 

41# BVUT=dict(rc=1.0470, zc=1.0390, wc=0.0552, hc=0.1382, ndi=20), 

42# BVUB=dict(rc=1.0470, zc=-1.0390, wc=0.0552, hc=0.1382, ndi=20), 

43# SOL=dict(rc=0.1295, zc=0.0000, wc=0.0161, hc=1.4711, ndi=64), 

44# MCT=dict(rc=0.7500, zc=0.3550, wc=0.0407, hc=0.0467, ndi=12), 

45# MCB=dict(rc=0.7500, zc=-0.3550, wc=0.0407, hc=0.0467, ndi=12), 

46# PSHT1=dict(rc=0.8518, zc=0.669, wc=0.007, hc=0.0232, ndi=4), 

47# PSHB1=dict(rc=0.8518, zc=-0.669, wc=0.007, hc=0.0232, ndi=4), 

48# PSHT2=dict(rc=0.833, zc=0.7016, wc=0.007, hc=0.0232, ndi=4), 

49# PSHB2=dict(rc=0.833, zc=-0.7016, wc=0.007, hc=0.0232, ndi=4) 

50# ) 

51 

52 

53# Taken from `https://tokamak-devlin.tokamak.local/gitlab/physics/SPIDER/-/blob/main/EFIT/coil.dat?ref_type=heads` 

54spider_coil_description = dict( 

55 DIVT=dict(rc=0.1930, zc=0.7907, wc=0.0566, hc=0.1351, ndi=20), 

56 DIVB=dict(rc=0.1930, zc=-0.7907, wc=0.0566, hc=0.1351, ndi=20), 

57 BVLT=dict(rc=1.3860, zc=0.408, wc=0.11, hc=0.1156, ndi=12), 

58 BVLB=dict(rc=1.3860, zc=-0.43, wc=0.11, hc=0.1156, ndi=12), 

59 BVUT=dict(rc=1.0470, zc=1.0390, wc=0.0552, hc=0.1382, ndi=20), 

60 BVUB=dict(rc=1.0470, zc=-1.0390, wc=0.0552, hc=0.1382, ndi=20), 

61 SOL=dict(rc=0.1295, zc=0.0000, wc=0.0161, hc=1.4711, ndi=64), 

62 MCT=dict(rc=0.7500, zc=0.3550, wc=0.0407, hc=0.0467, ndi=12), 

63 MCB=dict(rc=0.7500, zc=-0.3550, wc=0.0407, hc=0.0467, ndi=12), 

64 PSHT1=dict(rc=0.8518, zc=0.669, wc=0.007, hc=0.0232, ndi=4), 

65 PSHB1=dict(rc=0.8518, zc=-0.669, wc=0.007, hc=0.0232, ndi=4), 

66 PSHT2=dict(rc=0.833, zc=0.7016, wc=0.007, hc=0.0232, ndi=4), 

67 PSHB2=dict(rc=0.833, zc=-0.7016, wc=0.007, hc=0.0232, ndi=4), 

68) 

69 

70# 20 

71# 0.1930 0.7907 0.0566 0.1351 0.0 90.0 0.0 28 1 DIVT 1 

72# 20 

73# 0.1930 -0.7907 0.0566 0.1351 0.0 90.0 0.0 28 1 DIVB 2 

74# 12 

75# 1.3860 0.422 0.11 0.1156 0.0 90.0 0.0 16 2 BVLT 3 

76# 12 

77# 1.3860 -0.422 0.11 0.1156 0.0 90.0 0.0 16 2 BVLB 4 

78# 20 

79# 1.0470 1.0390 0.0552 0.1382 0.0 90.0 0.0 24 3 BVUT 5 

80# 20 

81# 1.0470 -1.0390 0.0552 0.1382 0.0 90.0 0.0 24 4 BVUB 6 

82# 64 

83# 0.1295 0.0000 0.0161 1.4711 0.0 90.0 0.0 190 5 CS 7 

84# 12 

85# 0.7500 0.3550 0.0407 0.0467 0.0 90.0 0.0 11 6 MCT 8 

86# 12 

87# 0.7500 -0.3550 0.0407 0.0467 0.0 90.0 0.0 11 6 MCB 9 

88# 4 

89# 0.8518 0.669 0.007 0.0232 0.0 120.0 0.0 4 7 PSHT1 10 

90# 4 

91# 0.8518 -0.669 0.007 0.0232 0.0 60.0 0.0 4 7 PSHB1 11 

92# 4 

93# 0.833 0.7016 0.007 0.0232 0.0 120.0 0.0 4 7 PSHT2 12 

94# 4 

95# 0.833 -0.7016 0.007 0.0232 0.0 60.0 0.0 4 7 PSHB2 13 

96 

97 

98def dividing_parallograms(coil_dictionary) -> tuple[int, int]: 

99 """ 

100 The PF coils are described as parallograms, and discretized in `SPIDER/SRC/Trecur_2.f`, in the `equil_spider_tepm` GitLab repository. 

101 

102 ``` 

103 SW = SQRT( NDIVA*WSIZE/HSIZE ) 

104 SH = SQRT( NDIVA*HSIZE/WSIZE ) 

105 SW = SW + 0.5 

106 SH = SH + 0.5 

107 C 

108 NDIVW = IDINT(SW) 

109 NDIVH = IDINT(SH) 

110 ``` 

111 """ 

112 ndi = coil_dictionary["ndi"] 

113 wc = coil_dictionary["wc"] 

114 hc = coil_dictionary["hc"] 

115 

116 sw = np.sqrt(ndi * wc / hc) 

117 sh = np.sqrt(ndi * hc / wc) 

118 sw = sw + 0.5 

119 sh = sh + 0.5 

120 ndivw = int(sw) 

121 ndivh = int(sh) 

122 return ndivw, ndivh 

123 

124 

125def calculate_coil_filament_positions(coil_dictionary) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]: 

126 """ 

127 Calculate the coil filament positions from the coil description dictionary. 

128 

129 :param coil_dictionary: Dictionary containing the coil description 

130 :return: Tuple of numpy arrays containing the r and z positions of the coil filaments 

131 

132 ``` 

133 C************************************************************** 

134 C SUBROUTINE FOR DIVIDING OF PARALLELOGRAM 

135 C ( FOR PFC CROSS-SECTION ) 

136 C************************************************************** 

137 C INPUT DATE: 

138 C ---------- 

139 C RC,ZC - CILINDER COORDINATES OF CENTER OF PARALLELOGRAM 

140 C WC - PROJECTION OF THE FIRST SIDE OF PARALLELOGRAM 

141 C ON AXIS "R" (IN METER) 

142 C HC - PROJECTION OF THE SECOND SIDE OF PARALLELOGRAM 

143 C ON AXIS "Z" (IN METER) 

144 C AWC - ANGLE BETWEEN THE FIRST SIDE OF PARAL. AND 

145 C AXIS "R" (IN DEGREES, IT MUST NOT BE EQUAL 90 , 

146 C IT MUST BE : -90 < AWC < 90 ) 

147 C AHC - ANGLE BETWEEN THE SECOND SIDE OF PARAL. AND 

148 C AXIS "R" (IN DEGREES, IT MUST NOT BE EQUAL 0 , 

149 C IT MUST BE : 0 < AHC < 180 ) 

150 C CURC - CURRENT OF PARALLELOGRAM (IN MA) 

151 C NDIV - APPROXIMATE NUMBER OF CELLS OF DIVIDING: 

152 C NDIV=0 - A SPECIAL CASE: AUTOMATICALLY NDIVRE=1, 

153 C RS(1)=RC, ZS(1)=ZC, PS=CURC 

154 C IF NDIV > 0 THEN WE HAVE THE MOST TOTAL ALGORITHM OF 

155 C DIVIDING 

156 C IF NDIV < 0 THEN NDIVW AND NDIVH ARE CUT OFF BY ABS(NDIV) 

157 C 

158 C OUTPUT DATE: 

159 C ---------- 

160 C NDIVRE - REAL NUMBER OF CELLS OF DIVIDING ( = NDIVW*NDIVH ) 

161 C NDIVW - NUMBER OF DIVIDING OF THE FIRST SIDE OF PARAL. 

162 C NDIVH - NUMBER OF DIVIDING OF THE SECOND SIDE OF PARAL. 

163 C RS(L),ZS(L) - CILINDER COORDINATES OF CENTERS OF CELLS OF DIVIDING 

164 C L = 1,2,...,NDIVRE ! (IN METER) 

165 C PS - CURRENT OF EVERY CELL OF DIVIDING (IN MA) 

166 C VERS - VERTICAL (OR LINEAR, OR RADIUS) SIZE OF CELL CROSS-S. 

167 C HORS - HORIZONTAL SIZE OF CELL CROSS-SECTION 

168 C 

169 C************************************************************** 

170 C 

171 SUBROUTINE DIVPAR( RC, ZC, WC, HC, AWC, AHC, CURC, NDIV, 

172 * NDIVRE, NDIVW, NDIVH, RS, ZS, PS, 

173 * VERS, HORS ) 

174 C 

175 C 

176 include 'double.inc' 

177 C 

178 DIMENSION RS(1), ZS(1) 

179 C 

180 C************************************************************** 

181 SIN(X) = DSIN(X) 

182 COS(X) = DCOS(X) 

183 ATAN(X) = DATAN(X) 

184 SQRT(X) = DSQRT(X) 

185 C************************************************************** 

186 C 

187 IF(NDIV.EQ.0) THEN 

188 NDIVW = 1 

189 NDIVH = 1 

190 NDIVRE = 1 

191 RS(1) = RC 

192 ZS(1) = ZC 

193 PS = CURC 

194 VERS = HC 

195 HORS = WC 

196 RETURN 

197 END IF 

198 C*************************************** 

199 C 

200 IF(AHC.LT.0) AHC = AHC + 180. 

201 C 

202 IF(NDIV.GE.0) THEN 

203 NDIVA = NDIV 

204 ELSE 

205 NDIVA = -NDIV 

206 END IF 

207 C*************************************** 

208 C PARAMETERS OF PARALLELOGRAM 

209 C 

210 XX = 1. 

211 PI = 4.*ATAN(XX) 

212 C 

213 AWCR = AWC * PI /180. 

214 AHCR = AHC * PI /180. 

215 C 

216 R0 = RC - 0.5*( WC + HC * COS(AHCR)/SIN(AHCR) ) 

217 Z0 = ZC - 0.5*( HC + WC * SIN(AWCR)/COS(AWCR) ) 

218 C 

219 WSIZE = WC / COS(AWCR) 

220 HSIZE = HC / SIN(AHCR) 

221 C 

222 WR = WC 

223 WZ = WC * SIN(AWCR) / COS(AWCR) 

224 HR = HC * COS(AHCR) / SIN(AHCR) 

225 HZ = HC 

226 C*************************************** 

227 C CALCULATION NDIVW, NDIVH, NDIVRE, PS 

228 C 

229 SW = SQRT( NDIVA*WSIZE/HSIZE ) 

230 SH = SQRT( NDIVA*HSIZE/WSIZE ) 

231 SW = SW + 0.5 

232 SH = SH + 0.5 

233 C 

234 NDIVW = IDINT(SW) 

235 NDIVH = IDINT(SH) 

236 C 

237 IF(NDIVW.EQ.0) NDIVW = 1 

238 IF(NDIVH.EQ.0) NDIVH = 1 

239 C 

240 IF((NDIV.LT.0).AND.(NDIVW.GT.NDIVA)) NDIVW = NDIVA 

241 IF((NDIV.LT.0).AND.(NDIVH.GT.NDIVA)) NDIVH = NDIVA 

242 C 

243 NDIVRE = NDIVW * NDIVH 

244 PS = CURC / NDIVRE 

245 C*************************************** 

246 C CALCULATION RS(L), ZS(L) : L = 1,2,...,NDIVRE 

247 C 

248 WR = WR / NDIVW 

249 WZ = WZ / NDIVW 

250 HR = HR / NDIVH 

251 HZ = HZ / NDIVH 

252 C 

253 HORS = WR 

254 VERS = HZ 

255 C 

256 RS(1) = R0 + 0.5*(WR + HR) 

257 ZS(1) = Z0 + 0.5*(WZ + HZ) 

258 C 

259 DO 1 I=1,NDIVW 

260 DO 1 J=1,NDIVH 

261 L = (I-1)*NDIVH + J 

262 RS(L) = RS(1) + (I-1)*WR + (J-1)*HR 

263 ZS(L) = ZS(1) + (I-1)*WZ + (J-1)*HZ 

264 1 CONTINUE 

265 C*************************************** 

266 C 

267 RETURN 

268 END 

269 ``` 

270 """ 

271 

272 # Extract required parameters 

273 rc = float(coil_dictionary["rc"]) # center R 

274 zc = float(coil_dictionary["zc"]) # center Z 

275 wc = float(coil_dictionary["wc"]) # projection of first side on R 

276 hc = float(coil_dictionary["hc"]) # projection of second side on Z 

277 # Optional angles (degrees). Default to AWC=0, AHC=90 if not provided, 

278 # matching the most common cases in the coil table. 

279 awc = float(coil_dictionary.get("awc", 0.0)) 

280 ahc = float(coil_dictionary.get("ahc", 90.0)) 

281 # Approximate number of cells (NDIV/NDIVA) 

282 ndia = int(coil_dictionary.get("ndi", 1)) 

283 

284 # Handle the special case NDIV == 0 

285 if ndia == 0: 

286 rs = np.array([rc], dtype=np.float64) 

287 zs = np.array([zc], dtype=np.float64) 

288 return rs, zs 

289 

290 # Convert angles to radians, applying the Fortran adjustment for AHC < 0 

291 if ahc < 0.0: 

292 ahc = ahc + 180.0 

293 

294 pi = np.pi 

295 awcr = awc * pi / 180.0 

296 ahcr = ahc * pi / 180.0 

297 

298 # Guard against invalid angles (avoid division by zero) 

299 # In Fortran code: it assumes -90 < AWC < 90 and 0 < AHC < 180 

300 # If cos(awcr) or sin(ahcr) are too close to zero, nudge slightly. 

301 eps = 1e-12 

302 c_aw = np.cos(awcr) 

303 s_aw = np.sin(awcr) 

304 s_ah = np.sin(ahcr) 

305 c_ah = np.cos(ahcr) 

306 if abs(c_aw) < eps: 

307 c_aw = eps if c_aw >= 0 else -eps 

308 if abs(s_ah) < eps: 

309 s_ah = eps if s_ah >= 0 else -eps 

310 

311 # Fortran mapping 

312 r0 = rc - 0.5 * (wc + hc * c_ah / s_ah) 

313 z0 = zc - 0.5 * (hc + wc * s_aw / c_aw) 

314 

315 wsize = wc / c_aw 

316 hsize = hc / s_ah 

317 

318 wr = wc 

319 wz = wc * s_aw / c_aw 

320 hr = hc * c_ah / s_ah 

321 hz = hc 

322 

323 # Calculate NDIVW, NDIVH per Fortran's DIVPAR (NDIV >= 0 path) 

324 sw = np.sqrt(ndia * wsize / hsize) + 0.5 

325 sh = np.sqrt(ndia * hsize / wsize) + 0.5 

326 ndivw = int(sw) 

327 ndivh = int(sh) 

328 if ndivw == 0: 

329 ndivw = 1 

330 if ndivh == 0: 

331 ndivh = 1 

332 

333 # Real number of divided cells 

334 ndivre = ndivw * ndivh 

335 

336 # Cell sizes 

337 wr_cell = wr / ndivw 

338 wz_cell = wz / ndivw 

339 hr_cell = hr / ndivh 

340 hz_cell = hz / ndivh 

341 

342 # First cell center 

343 r1 = r0 + 0.5 * (wr_cell + hr_cell) 

344 z1 = z0 + 0.5 * (wz_cell + hz_cell) 

345 

346 # Generate centers for all cells (Fortran order L = (I-1)*NDIVH + J) 

347 rs = np.empty(ndivre, dtype=np.float64) 

348 zs = np.empty(ndivre, dtype=np.float64) 

349 idx = 0 

350 for i in range(1, ndivw + 1): 

351 for j in range(1, ndivh + 1): 

352 r = r1 + (i - 1) * wr_cell + (j - 1) * hr_cell 

353 z = z1 + (i - 1) * wz_cell + (j - 1) * hz_cell 

354 rs[idx] = r 

355 zs[idx] = z 

356 idx += 1 

357 

358 return rs, zs 

359 

360 

361def setup_coils( 

362 self: "DatabaseReader", 

363 settings: dict[str, typing.Any], 

364 pulseNo: int, 

365) -> Coils: 

366 """ 

367 This method initialises the Rust `Coils` class. 

368 

369 :param pulseNo: Pulse number, used to read from the database 

370 :param settings: Dictionary containing the JSON settings read from the `settings` directory 

371 

372 **This method is specific to ST40's SPIDER stored on MDSplus.** 

373 

374 See `python/gsfit/database_readers/interface.py` for more details on how a new database_reader should be implemented. 

375 """ 

376 

377 # Initialise the Coils Rust class 

378 coils = Coils() 

379 

380 # Extract the spider_run_name from settings 

381 spider_run_name = settings["GSFIT_code_settings.json"]["database_reader"]["st40_spider_mdsplus"]["workflow"]["spider"]["run_name"] 

382 

383 # Connect to MDSplus 

384 conn = mdsthin.Connection("smaug") 

385 conn.openTree("SPIDER", pulseNo) 

386 

387 # SPIDER time 

388 time = conn.get(f"\\SPIDER::TOP.{spider_run_name}:TIME").data().astype(np.float64) 

389 

390 # PF coil currents 

391 # ["IPL", "MC", "PSH", "DIV", "BVL", "BVUT", "BVUB", "CS"] 

392 # [0, 1, 2, 3, 4, 5, 6, 7] 

393 currents = conn.get(f"\\SPIDER::TOP.{spider_run_name}.COILS.PSU2PF:I").data().astype(np.float64) * 1.0e6 

394 

395 # Add BVLB PF coil 

396 coil_name = "BVLB" 

397 coil_r, coil_z = calculate_coil_filament_positions(spider_coil_description[coil_name]) 

398 # Read in BVL PSU current, and normalise by the number of filaments 

399 currents_local = currents[:, 4] * 4.0 * 4.0 / len(coil_r) 

400 coils.add_pf_coil( 

401 coil_name, 

402 coil_r, 

403 coil_z, 

404 d_r=0.0 * coil_r, 

405 d_z=0.0 * coil_z, 

406 time=time, 

407 measured=currents_local, 

408 ) 

409 # Add BVLT PF coil 

410 coil_name = "BVLT" 

411 coil_r, coil_z = calculate_coil_filament_positions(spider_coil_description[coil_name]) 

412 coils.add_pf_coil( 

413 coil_name, 

414 coil_r, 

415 coil_z, 

416 d_r=0.0 * coil_r, 

417 d_z=0.0 * coil_z, 

418 time=time, 

419 measured=currents_local, 

420 ) 

421 

422 # Read in BVUB PSU current, and normalise by the number of filaments 

423 coil_name = "BVUB" 

424 coil_r, coil_z = calculate_coil_filament_positions(spider_coil_description[coil_name]) 

425 currents_local = currents[:, 6] * 4.0 * 6.0 / len(coil_r) 

426 coils.add_pf_coil( 

427 coil_name, 

428 coil_r, 

429 coil_z, 

430 d_r=0.0 * coil_r, 

431 d_z=0.0 * coil_z, 

432 time=time, 

433 measured=currents_local, 

434 ) 

435 

436 # Read in BVUT PSU current, and normalise by the number of filaments 

437 coil_name = "BVUT" 

438 coil_r, coil_z = calculate_coil_filament_positions(spider_coil_description[coil_name]) 

439 currents_local = currents[:, 5] * 4.0 * 6.0 / len(coil_r) 

440 coils.add_pf_coil( 

441 coil_name, 

442 coil_r, 

443 coil_z, 

444 d_r=0.0 * coil_r, 

445 d_z=0.0 * coil_z, 

446 time=time, 

447 measured=currents_local, 

448 ) 

449 

450 # Read in SOL PSU current, and normalise by the number of filaments 

451 coil_name = "SOL" 

452 coil_r, coil_z = calculate_coil_filament_positions(spider_coil_description[coil_name]) 

453 currents_local = currents[:, 7] * 2.0 * 95.0 / len(coil_r) 

454 coils.add_pf_coil( 

455 coil_name, 

456 coil_r, 

457 coil_z, 

458 d_r=0.0 * coil_r, 

459 d_z=0.0 * coil_z, 

460 time=time, 

461 measured=currents_local, 

462 ) 

463 

464 # Add DIVT PF coil, and normalise by the number of filaments 

465 coil_name = "DIVT" 

466 coil_r, coil_z = calculate_coil_filament_positions(spider_coil_description[coil_name]) 

467 currents_local = currents[:, 3] * 4.0 * 7.0 / len(coil_r) 

468 coils.add_pf_coil( 

469 coil_name, 

470 coil_r, 

471 coil_z, 

472 d_r=0.0 * coil_r, 

473 d_z=0.0 * coil_z, 

474 time=time, 

475 measured=currents_local, 

476 ) 

477 # Add DIVB PF coil 

478 coil_name = "DIVB" 

479 coil_r, coil_z = calculate_coil_filament_positions(spider_coil_description[coil_name]) 

480 coils.add_pf_coil( 

481 coil_name, 

482 coil_r, 

483 coil_z, 

484 d_r=0.0 * coil_r, 

485 d_z=0.0 * coil_z, 

486 time=time, 

487 measured=currents_local, 

488 ) 

489 

490 # Add MCT PF coil, and normalise by the number of filaments 

491 coil_name = "MCT" 

492 coil_r, coil_z = calculate_coil_filament_positions(spider_coil_description[coil_name]) 

493 currents_local = currents[:, 1] * 11.0 / len(coil_r) 

494 coils.add_pf_coil( 

495 coil_name, 

496 coil_r, 

497 coil_z, 

498 d_r=0.0 * coil_r, 

499 d_z=0.0 * coil_z, 

500 time=time, 

501 measured=currents_local, 

502 ) 

503 # Add MCB PF coil 

504 coil_name = "MCB" 

505 coil_r, coil_z = calculate_coil_filament_positions(spider_coil_description[coil_name]) 

506 coils.add_pf_coil( 

507 coil_name, 

508 coil_r, 

509 coil_z, 

510 d_r=0.0 * coil_r, 

511 d_z=0.0 * coil_z, 

512 time=time, 

513 measured=currents_local, 

514 ) 

515 

516 # Add PSHT PF coil 

517 coil_name = "PSHT1" 

518 coil_r, coil_z = calculate_coil_filament_positions(spider_coil_description[coil_name]) 

519 currents_local = currents[:, 2] * 4.0 / len(coil_r) 

520 coils.add_pf_coil( 

521 coil_name, 

522 coil_r, 

523 coil_z, 

524 d_r=0.0 * coil_r, 

525 d_z=0.0 * coil_z, 

526 time=time, 

527 measured=currents_local, 

528 ) 

529 # Add PSHB PF coil 

530 coil_name = "PSHB1" 

531 coil_r, coil_z = calculate_coil_filament_positions(spider_coil_description[coil_name]) 

532 coils.add_pf_coil( 

533 coil_name, 

534 coil_r, 

535 coil_z, 

536 d_r=0.0 * coil_r, 

537 d_z=0.0 * coil_z, 

538 time=time, 

539 measured=currents_local, 

540 ) 

541 # Add PSHT2 PF coil 

542 coil_name = "PSHT2" 

543 coil_r, coil_z = calculate_coil_filament_positions(spider_coil_description[coil_name]) 

544 coils.add_pf_coil( 

545 coil_name, 

546 coil_r, 

547 coil_z, 

548 d_r=0.0 * coil_r, 

549 d_z=0.0 * coil_z, 

550 time=time, 

551 measured=currents_local, 

552 ) 

553 # Add PSHB2 PF coil 

554 coil_name = "PSHB2" 

555 coil_r, coil_z = calculate_coil_filament_positions(spider_coil_description[coil_name]) 

556 coils.add_pf_coil( 

557 coil_name, 

558 coil_r, 

559 coil_z, 

560 d_r=0.0 * coil_r, 

561 d_z=0.0 * coil_z, 

562 time=time, 

563 measured=currents_local, 

564 ) 

565 

566 # Add TF coil 

567 bt_vac = conn.get(f"\\SPIDER::TOP.{spider_run_name}.GLOBAL:BTVAC_GEO").data().astype(np.float64) # time-dependent 

568 r_geo = conn.get(f"\\SPIDER::TOP.{spider_run_name}.GLOBAL:RGEO").data().astype(np.float64) # time-dependent 

569 i_rod = bt_vac * (2.0 * np.pi * r_geo) / mu_0 # time-dependent 

570 coils.add_tf_coil( 

571 time=time, 

572 measured=i_rod, 

573 ) 

574 

575 return coils