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

133 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-14 15:25 +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 st40_database import GetData 

12 

13if TYPE_CHECKING: 

14 from . import DatabaseReader 

15 

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

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

18# spider_coil_description = dict( 

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

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

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

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

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

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

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

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

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

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

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

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

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

32# ) 

33 

34 

35# spider_coil_description = dict( 

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

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

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

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

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

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

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

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

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

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

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

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

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

49# ) 

50 

51 

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

53spider_coil_description = dict( 

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

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

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

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

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

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

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

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

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

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

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

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

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

67) 

68 

69# 20 

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

71# 20 

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

73# 12 

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

75# 12 

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

77# 20 

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

79# 20 

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

81# 64 

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

83# 12 

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

85# 12 

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

87# 4 

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

89# 4 

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

91# 4 

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

93# 4 

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

95 

96 

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

98 """ 

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

100 

101 ``` 

102 SW = SQRT( NDIVA*WSIZE/HSIZE ) 

103 SH = SQRT( NDIVA*HSIZE/WSIZE ) 

104 SW = SW + 0.5 

105 SH = SH + 0.5 

106 C 

107 NDIVW = IDINT(SW) 

108 NDIVH = IDINT(SH) 

109 ``` 

110 """ 

111 ndi = coil_dictionary["ndi"] 

112 wc = coil_dictionary["wc"] 

113 hc = coil_dictionary["hc"] 

114 

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

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

117 sw = sw + 0.5 

118 sh = sh + 0.5 

119 ndivw = int(sw) 

120 ndivh = int(sh) 

121 return ndivw, ndivh 

122 

123 

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

125 """ 

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

127 

128 :param coil_dictionary: Dictionary containing the coil description 

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

130 

131 ``` 

132 C************************************************************** 

133 C SUBROUTINE FOR DIVIDING OF PARALLELOGRAM 

134 C ( FOR PFC CROSS-SECTION ) 

135 C************************************************************** 

136 C INPUT DATE: 

137 C ---------- 

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

139 C WC - PROJECTION OF THE FIRST SIDE OF PARALLELOGRAM 

140 C ON AXIS "R" (IN METER) 

141 C HC - PROJECTION OF THE SECOND SIDE OF PARALLELOGRAM 

142 C ON AXIS "Z" (IN METER) 

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

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

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

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

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

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

149 C CURC - CURRENT OF PARALLELOGRAM (IN MA) 

150 C NDIV - APPROXIMATE NUMBER OF CELLS OF DIVIDING: 

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

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

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

154 C DIVIDING 

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

156 C 

157 C OUTPUT DATE: 

158 C ---------- 

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

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

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

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

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

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

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

166 C HORS - HORIZONTAL SIZE OF CELL CROSS-SECTION 

167 C 

168 C************************************************************** 

169 C 

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

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

172 * VERS, HORS ) 

173 C 

174 C 

175 include 'double.inc' 

176 C 

177 DIMENSION RS(1), ZS(1) 

178 C 

179 C************************************************************** 

180 SIN(X) = DSIN(X) 

181 COS(X) = DCOS(X) 

182 ATAN(X) = DATAN(X) 

183 SQRT(X) = DSQRT(X) 

184 C************************************************************** 

185 C 

186 IF(NDIV.EQ.0) THEN 

187 NDIVW = 1 

188 NDIVH = 1 

189 NDIVRE = 1 

190 RS(1) = RC 

191 ZS(1) = ZC 

192 PS = CURC 

193 VERS = HC 

194 HORS = WC 

195 RETURN 

196 END IF 

197 C*************************************** 

198 C 

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

200 C 

201 IF(NDIV.GE.0) THEN 

202 NDIVA = NDIV 

203 ELSE 

204 NDIVA = -NDIV 

205 END IF 

206 C*************************************** 

207 C PARAMETERS OF PARALLELOGRAM 

208 C 

209 XX = 1. 

210 PI = 4.*ATAN(XX) 

211 C 

212 AWCR = AWC * PI /180. 

213 AHCR = AHC * PI /180. 

214 C 

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

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

217 C 

218 WSIZE = WC / COS(AWCR) 

219 HSIZE = HC / SIN(AHCR) 

220 C 

221 WR = WC 

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

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

224 HZ = HC 

225 C*************************************** 

226 C CALCULATION NDIVW, NDIVH, NDIVRE, PS 

227 C 

228 SW = SQRT( NDIVA*WSIZE/HSIZE ) 

229 SH = SQRT( NDIVA*HSIZE/WSIZE ) 

230 SW = SW + 0.5 

231 SH = SH + 0.5 

232 C 

233 NDIVW = IDINT(SW) 

234 NDIVH = IDINT(SH) 

235 C 

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

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

238 C 

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

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

241 C 

242 NDIVRE = NDIVW * NDIVH 

243 PS = CURC / NDIVRE 

244 C*************************************** 

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

246 C 

247 WR = WR / NDIVW 

248 WZ = WZ / NDIVW 

249 HR = HR / NDIVH 

250 HZ = HZ / NDIVH 

251 C 

252 HORS = WR 

253 VERS = HZ 

254 C 

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

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

257 C 

258 DO 1 I=1,NDIVW 

259 DO 1 J=1,NDIVH 

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

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

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

263 1 CONTINUE 

264 C*************************************** 

265 C 

266 RETURN 

267 END 

268 ``` 

269 """ 

270 

271 # Extract required parameters 

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

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

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

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

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

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

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

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

280 # Approximate number of cells (NDIV/NDIVA) 

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

282 

283 # Handle the special case NDIV == 0 

284 if ndia == 0: 

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

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

287 return rs, zs 

288 

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

290 if ahc < 0.0: 

291 ahc = ahc + 180.0 

292 

293 pi = np.pi 

294 awcr = awc * pi / 180.0 

295 ahcr = ahc * pi / 180.0 

296 

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

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

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

300 eps = 1e-12 

301 c_aw = np.cos(awcr) 

302 s_aw = np.sin(awcr) 

303 s_ah = np.sin(ahcr) 

304 c_ah = np.cos(ahcr) 

305 if abs(c_aw) < eps: 

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

307 if abs(s_ah) < eps: 

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

309 

310 # Fortran mapping 

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

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

313 

314 wsize = wc / c_aw 

315 hsize = hc / s_ah 

316 

317 wr = wc 

318 wz = wc * s_aw / c_aw 

319 hr = hc * c_ah / s_ah 

320 hz = hc 

321 

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

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

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

325 ndivw = int(sw) 

326 ndivh = int(sh) 

327 if ndivw == 0: 

328 ndivw = 1 

329 if ndivh == 0: 

330 ndivh = 1 

331 

332 # Real number of divided cells 

333 ndivre = ndivw * ndivh 

334 

335 # Cell sizes 

336 wr_cell = wr / ndivw 

337 wz_cell = wz / ndivw 

338 hr_cell = hr / ndivh 

339 hz_cell = hz / ndivh 

340 

341 # First cell center 

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

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

344 

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

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

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

348 idx = 0 

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

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

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

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

353 rs[idx] = r 

354 zs[idx] = z 

355 idx += 1 

356 

357 return rs, zs 

358 

359 

360def setup_coils( 

361 self: "DatabaseReader", 

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

363 pulseNo: int, 

364) -> Coils: 

365 """ 

366 This method initialises the Rust `Coils` class. 

367 

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

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

370 

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

372 

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

374 """ 

375 

376 # Initialise the Coils Rust class 

377 coils = Coils() 

378 

379 # Extract the spider_run_name from settings 

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

381 

382 # Connect to MDSplus 

383 conn = mdsthin.Connection("smaug") 

384 conn.openTree("SPIDER", pulseNo) 

385 

386 # SPIDER time 

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

388 

389 # PF coil currents 

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

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

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

393 

394 # Add BVLB PF coil 

395 coil_name = "BVLB" 

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

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

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

399 coils.add_pf_coil( 

400 coil_name, 

401 coil_r, 

402 coil_z, 

403 d_r=0.0 * coil_r, 

404 d_z=0.0 * coil_z, 

405 time=time, 

406 measured=currents_local, 

407 ) 

408 # Add BVLT PF coil 

409 coil_name = "BVLT" 

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

411 coils.add_pf_coil( 

412 coil_name, 

413 coil_r, 

414 coil_z, 

415 d_r=0.0 * coil_r, 

416 d_z=0.0 * coil_z, 

417 time=time, 

418 measured=currents_local, 

419 ) 

420 

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

422 coil_name = "BVUB" 

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

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

425 coils.add_pf_coil( 

426 coil_name, 

427 coil_r, 

428 coil_z, 

429 d_r=0.0 * coil_r, 

430 d_z=0.0 * coil_z, 

431 time=time, 

432 measured=currents_local, 

433 ) 

434 

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

436 coil_name = "BVUT" 

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

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

439 coils.add_pf_coil( 

440 coil_name, 

441 coil_r, 

442 coil_z, 

443 d_r=0.0 * coil_r, 

444 d_z=0.0 * coil_z, 

445 time=time, 

446 measured=currents_local, 

447 ) 

448 

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

450 coil_name = "SOL" 

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

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

453 coils.add_pf_coil( 

454 coil_name, 

455 coil_r, 

456 coil_z, 

457 d_r=0.0 * coil_r, 

458 d_z=0.0 * coil_z, 

459 time=time, 

460 measured=currents_local, 

461 ) 

462 

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

464 coil_name = "DIVT" 

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

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

467 coils.add_pf_coil( 

468 coil_name, 

469 coil_r, 

470 coil_z, 

471 d_r=0.0 * coil_r, 

472 d_z=0.0 * coil_z, 

473 time=time, 

474 measured=currents_local, 

475 ) 

476 # Add DIVB PF coil 

477 coil_name = "DIVB" 

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

479 coils.add_pf_coil( 

480 coil_name, 

481 coil_r, 

482 coil_z, 

483 d_r=0.0 * coil_r, 

484 d_z=0.0 * coil_z, 

485 time=time, 

486 measured=currents_local, 

487 ) 

488 

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

490 coil_name = "MCT" 

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

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

493 coils.add_pf_coil( 

494 coil_name, 

495 coil_r, 

496 coil_z, 

497 d_r=0.0 * coil_r, 

498 d_z=0.0 * coil_z, 

499 time=time, 

500 measured=currents_local, 

501 ) 

502 # Add MCB PF coil 

503 coil_name = "MCB" 

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

505 coils.add_pf_coil( 

506 coil_name, 

507 coil_r, 

508 coil_z, 

509 d_r=0.0 * coil_r, 

510 d_z=0.0 * coil_z, 

511 time=time, 

512 measured=currents_local, 

513 ) 

514 

515 # Add PSHT PF coil 

516 coil_name = "PSHT1" 

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

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

519 coils.add_pf_coil( 

520 coil_name, 

521 coil_r, 

522 coil_z, 

523 d_r=0.0 * coil_r, 

524 d_z=0.0 * coil_z, 

525 time=time, 

526 measured=currents_local, 

527 ) 

528 # Add PSHB PF coil 

529 coil_name = "PSHB1" 

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

531 coils.add_pf_coil( 

532 coil_name, 

533 coil_r, 

534 coil_z, 

535 d_r=0.0 * coil_r, 

536 d_z=0.0 * coil_z, 

537 time=time, 

538 measured=currents_local, 

539 ) 

540 # Add PSHT2 PF coil 

541 coil_name = "PSHT2" 

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

543 coils.add_pf_coil( 

544 coil_name, 

545 coil_r, 

546 coil_z, 

547 d_r=0.0 * coil_r, 

548 d_z=0.0 * coil_z, 

549 time=time, 

550 measured=currents_local, 

551 ) 

552 # Add PSHB2 PF coil 

553 coil_name = "PSHB2" 

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

555 coils.add_pf_coil( 

556 coil_name, 

557 coil_r, 

558 coil_z, 

559 d_r=0.0 * coil_r, 

560 d_z=0.0 * coil_z, 

561 time=time, 

562 measured=currents_local, 

563 ) 

564 

565 return coils