phystota

tokamak

Feb 2nd, 2026
159
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.41 KB | None | 0 0
  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3.  
  4. """
  5.    Explicit Euler integrator for guiding-center poloidal motion model
  6.  
  7.    Model equations integrated (Euler):
  8.      dx/dt = -omega * z
  9.      dz/dt =  omega * x + vdrift    
  10.  
  11.    Input:
  12.      R0       : reference major radius
  13.      x0       : initial x(0) = R(0) - R0
  14.      z0       : initial z(0)
  15.      dt       : time step for explicit Euler
  16.      t_end    : final simulation time
  17.  
  18.    omega_mode : selects angular frequency model:
  19.                -- "const": omega = omega0 (constant angular frequency)
  20.                -- "var"  : omega = vpar / (R0 + x) = vpar / R
  21.    omega0     : constant angular frequency used if omega_mode == "const"
  22.    vpar0      : initial parallel speed v_par(0)
  23.    vdrift     : constant drift velocity added to dz/dt equation
  24.  
  25.    mirror_mode: Evolves vpar using a magnetic-mirror force from 2.20 Poli's notes
  26.                -- "True" : dvpar/dt = - mu/m * B0 * R0 * z / (x + R0) ^ 2
  27.                -- "False": vpar     = const
  28.    mu_m       : mu/m = magnetic moment divided by mass
  29.    B0         : magnetic field scale at R0 [T]
  30.  
  31.    Output:
  32.      t[n]     : time
  33.      z[n]     : poloidal-plane vertical coordinate Z  
  34.      R[n]     : major radius, R = R0 + x    
  35.      vpar[n]  : B-parallel guiding-center speed
  36.  
  37. """
  38.  
  39. # ----------------------- Explicit Euler integrator  --------------------------------------- #
  40. def euler_solution(R0, x0, z0, dt, t_end, omega_mode = "const", omega0=1.0, vpar0=1.0, vdrift=0.0, mirror = False, mu_m = 1.0, B0 = 1.0):
  41.  
  42.     N = int(t_end / dt) + 1                                     # number of steps
  43.     t = np.linspace(0.0, (N - 1) * dt, N)                       # time array
  44.     x = np.empty(N); z = np.empty(N); vpar = np.empty(N);       # solution arrays for x and z
  45.     x[0] = x0; z[0] = z0; vpar[0] = vpar0;                      # initial conditions
  46.  
  47.     r0 = np.hypot(x0, z0)
  48.     alpha = r0/(R0+x0)
  49.  
  50.     eps = 1e-14
  51.  
  52.     # system of equations to solve:
  53.     for n in range(N - 1):
  54.  
  55.         # mirror force: update vpar
  56.         if mirror:
  57.             R = R0 + x[n]
  58.             dvpar_dt = - mu_m * (B0 * R0) * z[n] / (R**2)
  59.             vpar[n+1] = vpar[n] + dt * dvpar_dt
  60.         else:
  61.             vpar[n+1] = vpar[n]
  62.  
  63.         # drift velocity influence
  64.         if omega_mode == "const":
  65.             omega = omega0
  66.         else:  # omega(R)=vpar/R with R=R0+x
  67.             # omega = vpar[n] / (R0 + x[n])
  68.             r = np.hypot(x[n], z[n])
  69.             omega = (alpha * vpar[n]) / max(r, eps)
  70.                
  71.         x[n+1] = x[n] - dt * ( omega * z[n])                    
  72.         z[n+1] = z[n] + dt * ( omega * x[n] + vdrift )
  73.  
  74.     # return to original variables for better plotting
  75.     R = R0 + x
  76.     return t, R, z, vpar
  77.  
  78.  
  79. # ----------------------- Analytical solution (omega,vd are constant) ----------------------- #
  80.  
  81. def analytic_solution(R0, omega, vd, x0, z0, t):
  82.     # Shift to remove constant vd term as from 1.3
  83.     x_sh0 = x0 + vd / omega
  84.  
  85.     # constant amplitude/phase for the solution
  86.     A = np.sqrt(x_sh0**2 + z0**2)
  87.     phi = np.arctan2(z0, x_sh0)
  88.  
  89.     # solution
  90.     x = A * np.cos(omega * t + phi) - vd / omega
  91.     z = A * np.sin(omega * t + phi)
  92.  
  93.     # return to original variables
  94.     R = R0 + x
  95.     return R, z
  96.  
  97.  
  98. # ----------------------- Main function: conditions, plotting -------------------------------- #
  99.  
  100.  
  101. # ----------------------- A) No drift - compare Euler + Analytical solution ------------------ #
  102. R0 = 1.0
  103. vpar = 1.0
  104. x0, z0 = 1.0, 0.0
  105. vd0 = 0.0
  106.  
  107. omega0 = vpar/R0
  108. T0 = 2*np.pi/omega0
  109. t_end = 10*T0
  110. dt_list = [T0/100, T0/500, T0/2000]
  111.  
  112. # Plot in (x,z)
  113. plt.figure()
  114. plt.title("Orbit in (x,z) for different dt")
  115. plt.xlabel("x = R - R0")
  116. plt.ylabel("z")
  117. plt.axis("equal")
  118.  
  119. # analytical solution
  120. t_an = np.linspace(0.0, t_end, 5000)
  121. R_an, z_an = analytic_solution(R0, omega0, vd0, x0, z0, t_an)
  122. plt.plot(R_an - R0, z_an, label="analytic")
  123.  
  124. # Euler solutions for each timestep dt
  125.  
  126. for dt in dt_list:
  127.     t, R_eu, z_eu, vpar_eu = euler_solution(R0, x0, z0, dt, t_end, "const", omega0, vpar, vd0, mirror = False)
  128.     plt.plot(R_eu - R0, z_eu, label=f"Euler dt={dt:.3g}")
  129.  
  130. plt.legend()
  131. plt.show()
  132.  
  133. # ----------------------- B) Drift scan: different v_drift --------------------------------- #
  134. x0, z0 = 0.1, 0.0
  135. dt = T0/20000  
  136. vdrift_list = [0.01, 0.05]
  137.  
  138. plt.figure()
  139. plt.title(f"Drift scan (dt={dt:.3g}): omega=vpar/(R0+x)")
  140. plt.xlabel("x = R - R0")
  141. plt.ylabel("z")
  142. plt.axis("equal")
  143.  
  144. for vdrift in vdrift_list:
  145.     t, R_eu, z_eu, vpar_eu = euler_solution(R0, x0, z0, dt, t_end, omega_mode="var", vpar0=vpar, vdrift=vdrift, mirror = False)
  146.     plt.plot(R_eu - R0, z_eu, label=f"v_drift={vdrift}")
  147.  
  148. plt.legend()
  149. plt.show()
  150.  
  151.  
  152. # ----------------------- C) Mirror force  ------------------------------------------------- #
  153. x0, z0 = 0.3, 0.0
  154. vpar0 = 1.0
  155. vdrift = 0.1
  156. dt = T0/20000
  157. t_end = 10*T0
  158.  
  159. mu_m = 2.0
  160. B0 = 1.0  
  161.  
  162. t, R_eu, z_eu, vpar_eu= euler_solution(R0, x0, z0, dt, t_end, omega_mode="var", vpar0=vpar0, vdrift=vdrift, mirror=True, mu_m=mu_m, B0=B0)
  163.  
  164. plt.figure()
  165. plt.title("Mirror force induced trapped-like motion")
  166. plt.xlabel("x = R - R0")
  167. plt.ylabel("z")
  168. plt.axis("equal")
  169. plt.plot(R_eu - R0, z_eu)
  170. plt.show()
  171.  
  172. ## let's plot vpar changing with time to see the trapping itself
  173. plt.figure()
  174. plt.plot(t, vpar_eu)
  175. plt.title("v_par(t)")
  176. plt.xlabel("t"); plt.ylabel("v_par")
  177. plt.show()
Advertisement
Add Comment
Please, Sign In to add comment