try:
import SBGeom
import SBGeom.VMEC as VMEC
import SBGeom.Mesh as Mesh
import SBGeom.Coils as Coils
except:
import StellBlanket.SBGeom as SBGeom
import StellBlanket.SBGeom.VMEC as VMEC
import StellBlanket.SBGeom.Mesh as Mesh
import StellBlanket.SBGeom.Coils as Coils
import numpy as np
import plotly.graph_objects as go
R = 1.0
w = 0.2
t = np.linspace(0,2 * np.pi,10, endpoint=False)
coil = SBGeom.Discrete_Coil(np.stack([R * np.cos(t),w * np.cos(t), R * np.sin(t) ], axis=-1))
This Coil can be plotted (note that it linearly interpolates between the discrete points)
def create_figure():
fig = go.Figure()
fig.update_layout(autosize=False, width=600, height=600,margin=dict(l=0, r=0, b=0, t=0), scene=dict(aspectmode='data'))
return fig
fig = create_figure()
SBGeom.Coils.Plot(fig, coil, line=dict(color='red'), marker=dict(color='red', size=2.4), mode='lines+markers')
fig.show()
Coil sets can be created by collecting a list of coils:
n_coils = 20
coils = []
Rmaj = 5
Rmin = 1
for i in range(n_coils):
centre = np.array([Rmaj, 0.0, i / n_coils * 2 * np.pi ])
Ri = centre[0] + Rmin * np.cos(t)
Zi = centre[1] + Rmin * np.sin(t)
coils.append(SBGeom.Discrete_Coil(np.stack([ Ri * np.cos(centre[2]) , Ri * np.sin(centre[2]), Zi ], axis=-1)))
coilset = SBGeom.Coil_Set(coils)
fig = create_figure()
SBGeom.Coils.Plot_Set(fig,coilset, line=dict(width=5))
fig.show()
Now, we can use an actual stellarator coilset:
helias5_coils = Coils.Discrete_Coil_Set_From_HDF5("HELIAS5_coils_all_corr.h5")
SYMM = 5
fig = create_figure()
SBGeom.Coils.Plot_Set(fig,helias5_coils, line=dict(width=5))
fig.show()
Of course, there are just thin lines, unsuitable for transport simulations or further analysis. Rectangular finite sizes are supported in multiple ways.
Easiest is the Centroid method, where you take the radial unit vector as the vector from the point to the centre of the coil. The tangent vector is just to the next point. Together with the cross product between these two vectors, this defines an orthonormal frame, which can be used to create a finite size.
width_phi = 0.2
width_R = 0.2
fig = create_figure()
colors = ['red','green', 'blue','purple','olive']
for i in range(int(helias5_coils.Number_of_Coils() / SYMM / 2)):
centroid_lines = helias5_coils[i].Finite_Size_Lines_Centroid(width_phi = width_phi, width_R = width_R, number_of_points = 200)
central_line = helias5_coils[i].Position(np.linspace(0,1,200))
fig.add_trace(go.Scatter3d(x =centroid_lines[:,0], y = centroid_lines[:,1], z= centroid_lines[:,2], line=dict(color= colors[i]), mode="lines", showlegend=False))
fig.add_trace(go.Scatter3d(x =central_line[:,0], y = central_line[:,1], z= central_line[:,2] , line=dict(color= colors[i], dash='dash'), mode="lines", name="Coil "+str(i)))
fig.show()
These finite lines can be meshed as well:
fig = create_figure()
colors = ['red','green', 'blue','purple','olive']
for i in range(int(helias5_coils.Number_of_Coils() / SYMM / 2)):
centroid = helias5_coils[i].Mesh_Triangles_Centroid(width_phi = width_phi, width_R = width_R, number_of_vertices = 200 )
SBGeom.Mesh.Plot(fig, centroid, wireframe=True, color=colors[i], opacity=1.0, name="Coil" +str(i))
fig.show()
Although these coils can be used in transport simulations, they are not ideal because of the twisting that happens in some coils, especially 3 and 4 in this example.
fig.update_layout(scene_camera= dict(up = dict(x=0, y=0, z=1),eye=dict(x=-0.5, y=0.5, z= 0.5), center =dict(x = -0.2,y = 0.1, z=0.25)))
fig.show()
Another option is to use a rotation minimized frame, in which the rotation of these unit vectors along the curve are minimized. This eliminates this twisting:
fig = create_figure()
colors = ['red','green', 'blue','purple','olive']
for i in range(int(helias5_coils.Number_of_Coils() / SYMM / 2)):
centroid = helias5_coils[i].Mesh_Triangles_RMF(width_phi = width_phi, width_R = width_R, number_of_vertices = 200 )
SBGeom.Mesh.Plot(fig, centroid, wireframe=True, color=colors[i%5], opacity=1.0, name="Coil" +str(i))
fig.update_layout(scene_camera= dict(up = dict(x=0, y=0, z=1),eye=dict(x=-0.5, y=0.5, z= 0.5), center =dict(x = -0.2,y = 0.1, z=0.25)))
fig.show()
The same meshing function works for an entire coilset if desired:
(this takes a while because of Plotly)
coilmesh = helias5_coils.Mesh_Triangles_RMF(width_phi = width_phi, width_R = width_R, number_of_vertices = 100 )
fig = create_figure()
SBGeom.Mesh.Plot(fig, coilmesh, wireframe=True)
fig.show()
Fourier Coils¶
Since the output from stellarator coil optimization codes can be Fourier coils as well, the same functions work for Fourier coefficients:
$$ x = \sum_{n=1}^N x^c \cos(n 2\pi t) + x^s \sin(n 2\pi t)$$ $$ y = \sum_{n=1}^N y^c \cos(n 2\pi t) + y^s \sin(n 2\pi t)$$ $$ z = \sum_{n=1}^N z^c \cos(n 2\pi t) + z^s \sin(n 2\pi t)$$
fourier_coil = SBGeom.Fourier_Coil( xyz_cos = np.array([[2.0,0.5,0.3], [0.2,0.0,0.1]]), xyz_sin = np.array([[0.0,0.0,1.3], [0.5,0.0,0.0]]), centre = np.array([0.0,0.0,0.0]))
fig = create_figure()
SBGeom.Coils.Plot(fig, fourier_coil, line=dict(color='red'), marker=dict(color='red', size=2.4), mode='lines+markers')
centroid = fourier_coil.Mesh_Triangles_RMF(width_phi = width_phi, width_R = width_R, number_of_vertices = 200 )
SBGeom.Mesh.Plot(fig, centroid, opacity=0.5)
fig.show()
Discrete coil sets can be converted to Fourier coilsets as well:
fourier_coilset = Coils.Convert_to_Fourier_Coils(helias5_coils)
print(fourier_coilset[0])
Fourier_Coil(50 harmonics)
fig = create_figure()
coilmesh = helias5_coils.Mesh_Triangles_RMF( width_phi = width_phi, width_R = width_R, number_of_vertices = 100 )
coilmesh_fourier = fourier_coilset.Mesh_Triangles_RMF(width_phi = width_phi, width_R = width_R, number_of_vertices = 100 )
SBGeom.Mesh.Plot(fig, coilmesh_fourier, wireframe=True, name="Fourier")
SBGeom.Mesh.Plot(fig, coilmesh , wireframe=True, color='red', name='Original', opacity=0.8)
fig.show()