@@ -180,26 +180,86 @@ end
180
180
181
181
182
182
"""
183
- find_psi_2nd_separatrix(eqt::IMAS.equilibrium__time_slice, PSI_interpolant::Interpolations.AbstractInterpolation )
183
+ find_psi_2nd_separatrix(eqt::IMAS.equilibrium__time_slice)
184
184
185
- Returns psi of the second magentic separatrix. This relies on the fact that find_x_points! saves the x points in such a way
186
- that the last one is the null with Z opposite to the first x point which is the closest in psi to the lcfs.
185
+ Returns psi of the second magentic separatrix. This relies only on eqt and finds the 2nd sep geometrically.
187
186
"""
188
- function find_psi_2nd_separatrix (eqt:: IMAS.equilibrium__time_slice , PSI_interpolant:: Interpolations.AbstractInterpolation )
189
- psi2nd = PSI_interpolant .(eqt. boundary. x_point[end ]. r, eqt. boundary. x_point[end ]. z)
190
- return psi2nd * 0.999 .+ eqt. profiles_1d. psi[end ] * 0.001
187
+ function find_psi_2nd_separatrix (eqt:: IMAS.equilibrium__time_slice )
188
+
189
+ ZA = eqt. global_quantities. magnetic_axis. z
190
+
191
+ _, psi_separatrix = find_psi_boundary (eqt; raise_error_on_not_open= true ) # psi first open flux surfce up to precision
192
+ surface, _ = flux_surface (eqt, psi_separatrix, :open )
193
+
194
+ # First check if we are in a double null configuration
195
+ for (r,z) in surface
196
+ if isempty (r) || all (z .> ZA) || all (z .< ZA)
197
+ continue
198
+ end
199
+ if z[1 ]* z[end ] < 0
200
+ # if double null, all open surfaces in the SOL start and finish in opposite sides of the midplane
201
+ return psi_separatrix
202
+ end
203
+ end
204
+
205
+ # Single null case
206
+ eqt2d = findfirst (:rectangular , eqt. profiles_2d)
207
+ psi_axis = eqt. profiles_1d. psi[1 ] # psi value on axis
208
+ psi_sign = sign (psi_separatrix - psi_axis) # +1 for increasing psi / -1 for decreasing psi
209
+ psi_low = psi_separatrix
210
+ if psi_sign > 0
211
+ # increasing psi
212
+ psi_up = maximum (eqt2d. psi)
213
+ else
214
+ # decresing psi
215
+ psi_up = minimum (eqt2d. psi)
216
+ end
217
+ psi = (psi_up + psi_low) / 2.0
218
+ err = Inf
219
+ counter = 0
220
+ counter_max = 50
221
+ while abs (err) > 1E-7 && counter < counter_max
222
+ surface, _ = flux_surface (eqt, psi, :open )
223
+ for (r, z) in surface
224
+
225
+ if isempty (r) || all (z .> ZA) || all (z .< ZA)
226
+ continue
227
+ end
228
+
229
+ if z[end ] * z[1 ] > 0
230
+ # the surface starts and finishes on the same side of the midplane; aka is diverted
231
+ psi_low = psi
232
+ else
233
+ # the surface starts and finishes on opposite sides of the midplane
234
+ psi_up = psi
235
+ end
236
+
237
+ end
238
+
239
+ if err == abs (psi_up - psi_low)/ abs (psi_low)
240
+ # neither psi_up nor psi_low were updated; all surfaces in surface were discarded
241
+ # update psi_up, because psi_low is intrinsically safe for the algorithm
242
+ psi_up = psi
243
+ end
244
+
245
+ # update new error
246
+ err = abs (psi_up - psi_low)/ abs (psi_low)
247
+ psi = (psi_up + psi_low) / 2.0
248
+
249
+ counter = counter + 1
250
+ end
251
+
252
+ return psi_up
191
253
end
192
254
193
255
"""
194
256
find_psi_2nd_separatrix(dd::IMAS.dd)
195
257
196
- Returns psi of the second magentic separatrix. This relies on the fact that find_x_points! saves the x points in such a way
197
- that the last one is the null with Z opposite to the first x point which is the closest in psi to the lcfs
258
+ Returns psi of the second magentic separatrix. This relies only on eqt and finds the 2nd sep geometrically.
198
259
"""
199
260
200
261
function find_psi_2nd_separatrix (dd:: IMAS.dd )
201
- r, z, PSI_interpolant = ψ_interpolant (dd. equilibrium. time_slice[]. profiles_2d)
202
- return find_psi_2nd_separatrix (dd. equilibrium. time_slice[], PSI_interpolant)
262
+ return find_psi_2nd_separatrix (dd. equilibrium. time_slice[])
203
263
end
204
264
205
265
"""
@@ -235,7 +295,7 @@ function find_psi_last_diverted(
235
295
sign_z = sign (Xpoint2[2 ]) # sign of Z coordinate of 2nd null
236
296
237
297
_, psi_separatrix = find_psi_boundary (eqt; raise_error_on_not_open= true ) # psi LCFS
238
- psi_2ndseparatrix = find_psi_2nd_separatrix (eqt, PSI_interpolant ) # psi second magnetic separatrix
298
+ psi_2ndseparatrix = find_psi_2nd_separatrix (eqt) # psi second magnetic separatrix
239
299
crossings = intersection ([RA, maximum (wall_r)], [ZA, ZA], wall_r, wall_z)[2 ] # (r,z) point of intersection btw outer midplane (OMP) with wall
240
300
r_wall_midplane = [cr[1 ] for cr in crossings] # R coordinate of the wall at OMP
241
301
psi_wall_midplane = PSI_interpolant .(r_wall_midplane, ZA)[1 ] # psi at the intersection between wall and omp
@@ -360,6 +420,124 @@ function find_psi_last_diverted(dd::IMAS.dd; precision::Float64=1e-7)
360
420
return find_psi_last_diverted (dd. equilibrium. time_slice[], dd. wall, PSI_interpolant; precision)
361
421
end
362
422
423
+ """
424
+ find_psi_max(
425
+ eqt::IMAS.equilibrium__time_slice,
426
+ precision::Float64=1e-3)
427
+
428
+ Returns the max psi useful for an ofl in the SOL with no wall.
429
+ """
430
+ function find_psi_max (
431
+ eqt:: IMAS.equilibrium__time_slice ;
432
+ precision:: Float64 = 1e-2 )
433
+
434
+ eqt2d = findfirst (:rectangular , eqt. profiles_2d)
435
+ RA = eqt. global_quantities. magnetic_axis. r
436
+ ZA = eqt. global_quantities. magnetic_axis. z
437
+
438
+ psi_2ndseparatrix = find_psi_2nd_separatrix (eqt)
439
+ psi_axis = eqt. profiles_1d. psi[1 ] # psi value on axis
440
+ psi_sign = sign (psi_2ndseparatrix - psi_axis) # +1 for increasing psi / -1 for decreasing psi
441
+
442
+ # find the two surfaces `psi_first_lfs_far` and `psi_last_lfs` around the last diverted flux surface
443
+ counter_max = 50
444
+ counter = 0
445
+ if psi_sign > 0
446
+ # increasing psi
447
+ psi_up = maximum (eqt2d. psi)
448
+ else
449
+ # decresing psi
450
+ psi_up = minimum (eqt2d. psi)
451
+ end
452
+
453
+ # check first if psi_up is already the psi we want
454
+ surface, _ = flux_surface (eqt, psi_up, :open )
455
+ for (r,z) in surface
456
+
457
+ if isempty (r) || all (z .> ZA) || all (z .< ZA)
458
+ continue
459
+ end
460
+
461
+ _, crossings = intersection (r, z, [1 , 10 ]* RA, [1 , 1 ]* ZA) # find intersection with midplane
462
+
463
+ if isempty (crossings)
464
+ continue
465
+ end
466
+
467
+ rr = crossings[1 ][1 ] # R coordiante of intersection
468
+ zz = crossings[1 ][2 ] # Z coordiante of intersection
469
+
470
+ if rr < RA
471
+ # surface is in :hfs
472
+ continue
473
+ end
474
+
475
+ if z[1 ]* z[end ] < 0
476
+ # surface starts and finishes in opposite sides of the midplane is the surface we want
477
+ return psi_up
478
+ end
479
+
480
+ end
481
+
482
+ # psi_up corresponds to a surface that does not start and finish on opposite sides of the midplane
483
+ psi_low = psi_2ndseparatrix # psi_low starts and finishes on opposite sides of the midlpane
484
+ psi = (psi_up + psi_low) / 2.0
485
+ zmax = maximum (eqt2d. grid. dim2)
486
+ zmin = minimum (eqt2d. grid. dim2)
487
+
488
+ err = Inf
489
+ while abs (err) > precision && counter < counter_max
490
+ surface, _ = flux_surface (eqt, psi, :open )
491
+ for (r, z) in surface
492
+
493
+ if isempty (r) || all (z .> ZA) || all (z .< ZA)
494
+ continue
495
+ end
496
+
497
+ _, crossings = intersection (r, z, [1 , 10 ]* RA, [1 , 1 ]* ZA) # find intersection of surface with midplane
498
+
499
+ if isempty (crossings)
500
+ continue
501
+ end
502
+
503
+ rr = crossings[1 ][1 ] # R coordiante of intersection
504
+ zz = crossings[1 ][2 ] # Z coordiante of intersection
505
+
506
+ if rr < RA
507
+ # surface is in :hfs
508
+ continue
509
+ end
510
+
511
+ if z[end ] * z[1 ] < 0
512
+ # psi starts and finishes on opposite sides of the midplane
513
+ if abs (z[end ] * z[1 ] - zmax * zmin)/ (zmax^ 2 ) < 0.1
514
+ # make sure that indeed the surface goes from zmin to zmax, or close to that
515
+ psi_low = psi
516
+ else
517
+ psi_up = psi
518
+ end
519
+ else
520
+ # psi does not start and finish on opposite sides of the midplane
521
+ psi_up = psi
522
+ end
523
+ end
524
+
525
+ if err == abs (psi_up - psi_low)/ abs (psi_low)
526
+ # neither psi_up nor psi_low were updated; all surfaces in surface were discarded
527
+ # update psi_up, because psi_low is intrinsically safe for the algorithm
528
+ psi_up = psi
529
+ end
530
+
531
+ err = abs (psi_up - psi_low)/ abs (psi_low)
532
+ psi = (psi_up + psi_low) / 2.0
533
+
534
+ counter = counter + 1
535
+ end
536
+
537
+
538
+ return psi_low
539
+ end
540
+
363
541
364
542
"""
365
543
interp_rmid_at_psi(eqt::IMAS.equilibrium__time_slice, PSI_interpolant::Interpolations.AbstractInterpolation, R::AbstractVector{<:Real})
@@ -736,7 +914,7 @@ function flux_surfaces(eqt::equilibrium__time_slice{T}, B0::T, R0::T; upsample_f
736
914
737
915
# secondary separatrix
738
916
if length (eqt. boundary. x_point) > 1
739
- psi2nd = find_psi_2nd_separatrix (eqt, PSI_interpolant )
917
+ psi2nd = find_psi_2nd_separatrix (eqt)
740
918
tmp, _ = flux_surface (r, z, PSI, eqt. profiles_1d. psi, eqt. global_quantities. magnetic_axis. r, eqt. global_quantities. magnetic_axis. z, psi2nd, :encircling )
741
919
if ! isempty (tmp)
742
920
(pr2nd, pz2nd) = tmp[1 ]
0 commit comments