# Converted from etaordern_small.m - eta vs phi for small orders (n<=1)importnumpyasnpfromscipy.integrateimportsolve_ivpfromscipy.specialimportivasbesselifrommiscimportoctave_saveordervec=[0.,0.25,0.5,0.75,1.]p=dict(q=2.,thiele=10.,k=2.)c0=0.01der=1e-8start=1.eps_sq=np.sqrt(np.finfo(float).eps)defivp(t,x,order,p):xdot=np.zeros(2)xdot[0]=x[1]c=max(x[0],0.)ift==0:xdot[1]=p['k']*c**orderelse:xdot[1]=-p['q']/t*x[1]+p['k']*c**orderreturnxdotdefivp_shifted(t,x,order,p,r0):"""ODE in shifted coords: t=0 corresponds to r=r0 (dead zone boundary)."""xdot=np.zeros(2)xdot[0]=x[1]c=max(x[0],0.)r=t+r0xdot[1]=-p['q']/r*x[1]+p['k']*c**orderreturnxdotdefmake_g(order,p):defg(t,x):return(np.sqrt((order+1)/2*x[0]**(order-1)*p['k'])*t/(p['q']+1)-p['thiele'])g.terminal=Trueg.direction=0returngtable_list=[]nord=len(ordervec)forjinrange(nord):order=ordervec[j]iforder<1:# Stage 1tsteps=np.concatenate([[0.],np.logspace(-2,5,200)])x0=np.array([c0,0.])sol=solve_ivp(lambdat,x:ivp(t,x,order,p),[tsteps[0],tsteps[-1]],x0,method='Radau',t_eval=tsteps,rtol=eps_sq,atol=1e-18)ts=sol.t;xs=sol.y.Tphi=np.sqrt((order+1)/2*xs[:,0]**(order-1)*p['k'])*ts/(p['q']+1)eta=xs[:,1]/(p['k']*ts*xs[:,0]**order)*(p['q']+1)phi[0]=0.;eta[0]=1.# Stage 2: shifted coordinates, tau=0 at dead zone boundary (r=start=1).# Use asymptotic dead zone IC: c ~ A*tau^m, dc/dtau ~ m*A*tau^(m-1)# where A is from dominant balance of full ODE (incl. curvature term q/r):# A^(1-n) = k / (m*(m-1+q)), m = 2/(1-n).# Start at tau=eps_start chosen so phi_start slightly exceeds plot range.iforder==0:# Zeroth order: exact solution available, start from dead zone boundary# directly; source term k*c^0=k nonzero even at c=0, so [0,0] works.eps_start=0.c_eps=0.dc_eps=0.t2max=40.tsteps2=np.concatenate([[eps_start],np.logspace(-2,np.log10(t2max),300)])else:m=2.0/(1.0-order)A_coef=(p['k']/(m*(m-1+p['q'])))**(1.0/(1.0-order))# phi_crit ~ sqrt((n+1)/2 * A^(n-1) * k) / (q+1) = sqrt(m*(m-1+q)*(n+1)/2) / (q+1)phi_crit_est=np.sqrt((order+1)/2*m*(m-1+p['q']))/(p['q']+1)# Choose eps_start so phi_start = phi_crit*(1 + 1/eps_start) > 12phi_target=12.0eps_start=phi_crit_est/(phi_target-phi_crit_est)c_eps=A_coef*eps_start**mdc_eps=m*A_coef*eps_start**(m-1)t2max=200.tsteps2=np.concatenate([[eps_start],np.logspace(np.log10(eps_start*1.05),np.log10(t2max),400)])x0_s2=np.array([c_eps,dc_eps])sol2=solve_ivp(lambdat,x:ivp_shifted(t,x,order,p,start),[tsteps2[0],tsteps2[-1]],x0_s2,method='Radau',t_eval=tsteps2,rtol=eps_sq,atol=1e-25)ts2=sol2.t;xs2=np.asarray(sol2.y).Tphidry=np.sqrt((order+1)/2*xs2[:,0]**(order-1)*p['k'])*(ts2+start)/(p['q']+1)etadry=xs2[:,1]/(p['k']*(ts2+start)*xs2[:,0]**order)*(p['q']+1)tab=np.vstack([np.column_stack([phi,eta]),np.column_stack([phidry[1:][::-1],etadry[1:][::-1]])])eliforder==1:phi_v=np.logspace(-2,2,250)ifp['q']==0:eta_v=np.tanh(phi_v)/phi_velifp['q']==1:eta_v=besseli(1,phi_v)/(phi_v*besseli(0,phi_v))else:eta_v=(1./phi_v)*(1./np.tanh(3*phi_v)-1./(3*phi_v))tab=np.column_stack([phi_v,eta_v])else:x0_=np.array([c0,0.])t_out=np.logspace(-1,10,2000)ev=make_g(order,p)sol=solve_ivp(lambdat,x:ivp(t,x,order,p),[t_out[0],t_out[-1]],x0_,method='Radau',t_eval=t_out,events=ev,rtol=eps_sq,atol=eps_sq)ts=sol.t;xs=sol.y.Tphi=np.sqrt((order+1)/2*xs[:,0]**(order-1)*p['k'])*ts/(p['q']+1)eta=xs[:,1]/(p['k']*ts*xs[:,0]**order)*(p['q']+1)tab=np.column_stack([phi,eta])table_list.append(tab)octave_save('etaordern_small.dat',*[('table%d'%j,table_list[j])forjinrange(nord)])