"""Solve and plot a stationary GP vortex in a radial lattice. Tested with Python 3.14.4, NumPy 2.3.5 and Matplotlib 3.10.7. Writes only an opaque PNG basename to caller CWD; honors caller MPLCONFIGDIR. """ from pathlib import Path import numpy as np def tridiagonal(lower, diagonal, upper, rhs): """Thomas elimination for the finite-difference Newton correction.""" b=diagonal.copy(); d=rhs.copy(); c=upper.copy() for j in range(1,len(b)): scale=lower[j]/b[j-1] b[j]-=scale*c[j-1]; d[j]-=scale*d[j-1] answer=np.empty_like(d);answer[-1]=d[-1]/b[-1] for j in range(len(b)-2,-1,-1):answer[j]=(d[j]-c[j]*answer[j+1])/b[j] return answer def solve_profile(depth=.6, period=4., outer=32., step=.025): n=round(outer/step); r=np.linspace(0,outer,n+1); h=r[1] potential=depth*np.sin(np.pi*r/period)**2 lower=np.zeros_like(r);upper=np.zeros_like(r);base=np.zeros_like(r) lower[1:-1]=h**-2-1/(2*r[1:-1]*h) upper[1:-1]=h**-2+1/(2*r[1:-1]*h) base[1:-1]=-2/h**2-1/r[1:-1]**2+1-potential[1:-1] lower[-1]=-1; base[-1]=1; base[0]=1 def residual(f): out=np.zeros_like(f) out[1:-1]=lower[1:-1]*f[:-2]+upper[1:-1]*f[2:]+base[1:-1]*f[1:-1]-f[1:-1]**3 out[0]=f[0];out[-1]=f[-1]-f[-2] return out f=np.tanh(r/np.sqrt(2))*np.sqrt(np.maximum(1-potential,.1)) for iteration in range(50): res=residual(f); norm=np.max(np.abs(res)) if norm<2e-10:break diagonal=base.copy();diagonal[1:-1]-=3*f[1:-1]**2 correction=tridiagonal(lower,diagonal,upper,-res) weight=1. for attempt in range(30): candidate=f+weight*correction if np.min(candidate[1:])>0 and np.max(np.abs(residual(candidate)))