5.1. Refinement of points to correct ψ

PsiContour and FineContour objects represent flux surfaces – surfaces of constant \(\psi\) – so all points beloning to them should be at positions where \(\psi(R,Z)=\psi_\mathrm{C}\), where \(\psi_\mathrm{C}\) is the nominal \(\psi\)-value of the contour and \(\psi(R,Z)\) is defined by the psi() method provided by Equilibrum. However, the initial placement of the points at each stage of the grid generation process is given by the result of either an ODE integration or an interpolation or extrapolation from the positions of some other points, so the point will not (initially) lie exactly on the flux surface – there will be some small error in the \(\psi\)-value.

To reduce the difference between the initial \(\psi\) for each point and the nominal \(\psi_\mathrm{C}\) below a tolerance (set by refine_atol), PsiContour.refinePoint() uses one of several methods to refine the position of the point:

  • refinePointNewton() - uses a Newton iteration. Usually converges quickly if the initial guess is close to the final point.

  • refinePointIntegrate() - integrates along a vector \((dR/d\psi,dZ/d\psi)\) using scipy.integrate.solve_ivp

PsiContour.refinePointNewton(p, tangent, *, psi, width, atol)

Use Newton iteration to refine point. This should converge quickly if the original point is sufficiently close.

PsiContour.refinePointIntegrate(p, tangent, *, psi, width, atol)

Integrates across flux surfaces from p

Integrates this:

\[\begin{split}\begin{eqnarray} \frac{dR}{d\psi} &=& \frac{d\psi}{dR} \frac{1}{((d\psi/dZ)^2 + (d\psi/dR)^2)} \\ \frac{dZ}{d\psi} &=& \frac{d\psi}{dZ} \frac{1}{((d\psi/dZ)^2 + (d\psi/dR)^2)} \end{eqnarray}\end{split}\]

Usually quick but does not respect atol, so final result may not be as accurate as desired.

Note: This is the method used in the original IDL Hypnotoad

PsiContour.refinePointLinesearch(p, tangent, *, psi, width, atol)

Refine the location of a point p, using a line search method.

A line of length width is constructed perpendicular to the tangent direction, and scipy.optimize.brentq is used to search the line for the point where \(\psi\) takes its nominal value. If the search fails (for example because \(\psi\) is not monotonically varying along the line), it is retried recursively, using half the width each time.

Usually robust, but can be slow.

The initial width is set by the refine_width setting.

A final option is available, 'integrate+newton' which first uses refinePointIntegrate() to quickly improve the initial guess, and then uses refinePointNewton() to find an accurate final position.

The choice of which method to use is set by the refine_method option ('newton', 'integrate', 'line', or 'integrate+newton'). Multiple options can be passed in a list, in which case they will be tried in order until one of them converges without error. This can be useful in case different methods are more robust in different parts of the grid.