Skip to content

Commit d3f3793

Browse files
committed
Update conversion logic to fill equilibrium contour_tree
- Handle multiple x-points in boundary_[secondary_]separatrix - Determine if O-point at magnetic axis is a local minimum or maximum of the psi map
1 parent a09186a commit d3f3793

File tree

2 files changed

+43
-11
lines changed

2 files changed

+43
-11
lines changed

imas/ids_convert.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,26 +1098,36 @@ def _equilibrium_boundary_3to4(eq3: IDSToplevel, eq4: IDSToplevel, deepcopy: boo
10981098
and ts3.boundary_secondary_separatrix.psi.has_value
10991099
):
11001100
n_nodes = 3
1101-
ts4.contour_tree.node.resize(n_nodes)
1102-
# Magnetic axis (primary O-point)
11031101
node = ts4.contour_tree.node
1104-
node[0].critical_type = 0 # minimum (?)
1102+
node.resize(n_nodes)
1103+
# Magnetic axis (primary O-point)
1104+
axis_is_psi_minimum = (
1105+
# Note the sign flip for psi due to the COCOS change between DD3 and DD4!
1106+
-ts3.global_quantities.psi_axis < -ts3.global_quantities.psi_boundary
1107+
)
1108+
node[0].critical_type = 0 if axis_is_psi_minimum else 2
11051109
node[0].r = ts3.global_quantities.magnetic_axis.r
11061110
node[0].z = ts3.global_quantities.magnetic_axis.z
11071111
node[0].psi = -ts3.global_quantities.psi_axis # COCOS change
11081112

11091113
# X-points
11101114
if n_nodes >= 2:
11111115
if ts3.boundary_separatrix.type == 0: # limiter plasma
1112-
node[1].critical_type = 2 # maximum (?)
1116+
node[1].critical_type = 2 if axis_is_psi_minimum else 0
11131117
node[1].r = ts3.boundary_separatrix.active_limiter_point.r
11141118
node[1].z = ts3.boundary_separatrix.active_limiter_point.z
11151119
else:
11161120
node[1].critical_type = 1 # saddle-point (x-point)
11171121
if len(ts3.boundary_separatrix.x_point):
11181122
node[1].r = ts3.boundary_separatrix.x_point[0].r
11191123
node[1].z = ts3.boundary_separatrix.x_point[0].z
1120-
# TODO: what if there are multiple x-points?
1124+
# Additional x-points. N.B. levelset is only stored on the first node
1125+
for i in range(1, len(ts3.boundary_separatrix.x_point)):
1126+
node.resize(len(node) + 1, keep=True)
1127+
node[-1].critical_type = 1
1128+
node[-1].r = ts3.boundary_separatrix.x_point[i].r
1129+
node[-1].z = ts3.boundary_separatrix.x_point[i].z
1130+
node[-1].psi = -ts3.boundary_separatrix.psi
11211131
node[1].psi = -ts3.boundary_separatrix.psi # COCOS change
11221132
node[1].levelset.r = copy(ts3.boundary_separatrix.outline.r)
11231133
node[1].levelset.z = copy(ts3.boundary_separatrix.outline.z)
@@ -1127,7 +1137,13 @@ def _equilibrium_boundary_3to4(eq3: IDSToplevel, eq4: IDSToplevel, deepcopy: boo
11271137
if len(ts3.boundary_secondary_separatrix.x_point):
11281138
node[2].r = ts3.boundary_secondary_separatrix.x_point[0].r
11291139
node[2].z = ts3.boundary_secondary_separatrix.x_point[0].z
1130-
# TODO: what if there are multiple x-points?
1140+
# Additional x-points. N.B. levelset is only stored on the first node
1141+
for i in range(1, len(ts3.boundary_secondary_separatrix.x_point)):
1142+
node.resize(len(node) + 1, keep=True)
1143+
node[-1].critical_type = 1
1144+
node[-1].r = ts3.boundary_secondary_separatrix.x_point[i].r
1145+
node[-1].z = ts3.boundary_secondary_separatrix.x_point[i].z
1146+
node[-1].psi = -ts3.boundary_secondary_separatrix.psi
11311147
node[2].psi = -ts3.boundary_secondary_separatrix.psi # COCOS change
11321148
node[2].levelset.r = copy(ts3.boundary_secondary_separatrix.outline.r)
11331149
node[2].levelset.z = copy(ts3.boundary_secondary_separatrix.outline.z)

imas/test/test_ids_convert.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ def test_3to4_equilibrium_boundary():
569569
ts.boundary_separatrix.gap.resize(1)
570570
if i == 3:
571571
# Fill second_separatrix
572-
ts.boundary_secondary_separatrix.psi = -1.0
572+
ts.boundary_secondary_separatrix.psi = -1.1
573573
# Use limiter for time_slice[1], otherwise divertor:
574574
ts.boundary_secondary_separatrix.outline.r = [0.9, 3.1, 2.1, 0.9]
575575
ts.boundary_secondary_separatrix.outline.z = [0.9, 2.1, 3.1, 0.9]
@@ -588,10 +588,11 @@ def test_3to4_equilibrium_boundary():
588588
assert len(eq4.time_slice) == 5
589589
for i, ts in enumerate(eq4.time_slice):
590590
node = ts.contour_tree.node
591-
assert len(node) == [1, 2, 2, 3, 2][i]
591+
assert len(node) == [1, 2, 2, 3, 3][i]
592592
# Test magnetic axis
593593
assert node[0].critical_type == 0
594594
assert node[0].r == node[0].z == 2.0
595+
assert node[0].psi == -1.0
595596
assert len(node[0].levelset.r) == len(node[0].levelset.z) == 0
596597
# boundary_separatrix
597598
if i == 1: # node[1] is boundary for limiter plasma
@@ -602,18 +603,33 @@ def test_3to4_equilibrium_boundary():
602603
assert node[1].critical_type == 1
603604
assert node[1].r == node[1].z == 1.0
604605
if i > 0:
606+
assert node[1].psi == 1.0
605607
assert numpy.array_equal(node[1].levelset.r, [1.0, 3.0, 2.0, 1.0])
606608
assert numpy.array_equal(node[1].levelset.z, [1.0, 2.0, 3.0, 1.0])
607609
# boundary_secondary_separatrix
608610
if i == 3:
609611
assert node[2].critical_type == 1
610612
assert node[2].r == 2.1
611613
assert node[2].z == 3.1
614+
assert node[2].psi == 1.1
612615
assert numpy.array_equal(node[2].levelset.r, [0.9, 3.1, 2.1, 0.9])
613616
assert numpy.array_equal(node[2].levelset.z, [0.9, 2.1, 3.1, 0.9])
617+
# Second x-point from boundary_separatrix
618+
if i == 4:
619+
assert node[2].critical_type == 1
620+
assert node[2].r == 2.0
621+
assert node[2].z == 3.0
622+
assert node[2].psi == node[1].psi == 1.0
623+
# Levelset is only filled for the main x-point (node[1])
624+
assert not node[2].levelset.r.has_value
625+
assert not node[2].levelset.z.has_value
614626

615627
# not deepcopied, should share numpy arrays
616-
assert (
617-
eq342.time_slice[1].boundary_separatrix.outline.r.value
618-
is eq4.time_slice[1].contour_tree.node[1].levelset.r.value
628+
slice1_outline_r = eq342.time_slice[1].boundary_separatrix.outline.r.value
629+
assert slice1_outline_r is eq4.time_slice[1].contour_tree.node[1].levelset.r.value
630+
631+
# deepcopy should create a copy of the numpy arrays
632+
eq4_cp = convert_ids(eq342, "4.0.0", deepcopy=True)
633+
assert not numpy.may_share_memory(
634+
slice1_outline_r, eq4_cp.time_slice[1].contour_tree.node[1].levelset.r.value
619635
)

0 commit comments

Comments
 (0)