def lane_emden(Nr, m=1.5, n_rho=3, radius=1, ncc_cutoff = 1e-10, tolerance = 1e-10, dtype=np.complex128, comm=None): # TO-DO: clean this up and make work for ncc ingestion in main script in np.float64 rather than np.complex128 c = de.SphericalCoordinates('phi', 'theta', 'r') d = de.Distributor((c,), comm=comm, dtype=dtype) b = de.BallBasis(c, (1, 1, Nr), radius=radius, dtype=dtype) br = b.radial_basis phi, theta, r = b.local_grids() # Fields f = d.Field(name='f', bases=b) R = d.Field(name='R') τ = d.Field(name='τ', bases=b.S2_basis(radius=radius)) # Parameters and operators lap = lambda A: de.Laplacian(A, c) lift_basis = b.clone_with(k=2) # match laplacian lift = lambda A: de.LiftTau(A, lift_basis, -1) problem = de.NLBVP([f, R, τ]) problem.add_equation((lap(f) + lift(τ), - R**2 * f**m)) problem.add_equation((f(r=0), 1)) problem.add_equation((f(r=radius), np.exp(-n_rho/m, dtype=dtype))) # explicit typing to match domain # Solver solver = problem.build_solver(ncc_cutoff=ncc_cutoff) # Initial guess f['g'] = np.cos(np.pi/2 * r)**2 R['g'] = 5 # Iterations logger.debug('beginning Lane-Emden NLBVP iterations') pert_norm = np.inf while pert_norm > tolerance: solver.newton_iteration() pert_norm = sum(pert.allreduce_data_norm('c', 2) for pert in solver.perturbations) logger.debug(f'Perturbation norm: {pert_norm:.3e}') T = d.Field(name='T', bases=br) ρ = d.Field(name='ρ', bases=br) lnρ = d.Field(name='lnρ', bases=br) T['g'] = f['g'] ρ['g'] = f['g']**m lnρ['g'] = np.log(ρ['g']) structure = {'T':T,'lnρ':lnρ} for key in structure: structure[key].require_scales(1) structure['r'] = r structure['problem'] = {'c':c, 'b':b, 'problem':problem} return structure
s = d.Field(name='s', bases=b) u = d.VectorField(c, name='u', bases=b) # Taus zb1 = zb.clone_with(a=zb.a + 1, b=zb.b + 1) zb2 = zb.clone_with(a=zb.a + 2, b=zb.b + 2) lift_basis = zb.clone_with(a=1 / 2, b=1 / 2) # First derivative basis lift = lambda A, n: de.LiftTau(A, lift_basis, n) τs1 = d.Field(name='τs1', bases=xb) τs2 = d.Field(name='τs2', bases=xb) τu1 = d.VectorField(c, name='τu1', bases=(xb, )) τu2 = d.VectorField(c, name='τu2', bases=(xb, )) # Parameters and operators div = lambda A: de.Divergence(A, index=0) lap = lambda A: de.Laplacian(A, c) grad = lambda A: de.Gradient(A, c) #curl = lambda A: de.operators.Curl(A) dot = lambda A, B: de.DotProduct(A, B) cross = lambda A, B: de.CrossProduct(A, B) trace = lambda A: de.Trace(A) trans = lambda A: de.TransposeComponents(A) dt = lambda A: de.TimeDerivative(A) integ = lambda A: de.Integrate(de.Integrate(A, 'x'), 'z') avg = lambda A: integ(A) / (Lx * Lz) #x_avg = lambda A: de.Integrate(A, 'x')/(Lx) x_avg = lambda A: de.Integrate(A, 'x') / (Lx) from dedalus.core.operators import Skew skew = lambda A: Skew(A)
def heated_polytrope(nz, γ, ε, n_h, tolerance = 1e-8, ncc_cutoff = 1e-10, dealias = 2, verbose=False): import dedalus.public as de cP = γ/(γ-1) m_ad = 1/(γ-1) s_c_over_c_P = scrS = 1 # s_c/c_P = 1 logger.info("γ = {:.3g}, ε={:.3g}".format(γ, ε)) # this assumes h_bot=1, grad_φ = (γ-1)/γ (or L=Hρ) h_bot = 1 # generally, h_slope = -1/(1+m) # start in an adibatic state, heat from there h_slope = -1/(1+m_ad) grad_φ = (γ-1)/γ Lz = -1/h_slope*(1-np.exp(-n_h)) print(n_h, Lz, h_slope) c = de.CartesianCoordinates('z') d = de.Distributor(c, dtype=np.float64) zb = de.ChebyshevT(c.coords[-1], size=nz, bounds=(0, Lz), dealias=dealias) b = zb z = zb.local_grid(1) zd = zb.local_grid(dealias) # Fields θ = d.Field(name='θ', bases=b) Υ = d.Field(name='Υ', bases=b) s = d.Field(name='s', bases=b) u = d.VectorField(c, name='u', bases=b) # Taus lift_basis = zb.clone_with(a=zb.a+2, b=zb.b+2) lift = lambda A, n: de.Lift(A, lift_basis, n) lift_basis1 = zb.clone_with(a=zb.a+1, b=zb.b+1) lift1 = lambda A, n: de.Lift(A, lift_basis1, n) τ_h1 = d.VectorField(c,name='τ_h1') τ_s1 = d.Field(name='τ_s1') τ_s2 = d.Field(name='τ_s2') # Parameters and operators lap = lambda A: de.Laplacian(A, c) grad = lambda A: de.Gradient(A, c) ez, = c.unit_vector_fields(d) # NLBVP goes here # intial guess h0 = d.Field(name='h0', bases=zb) θ0 = d.Field(name='θ0', bases=zb) Υ0 = d.Field(name='Υ0', bases=zb) s0 = d.Field(name='s0', bases=zb) structure = {'h':h0,'s':s0,'θ':θ0,'Υ':Υ0} for key in structure: structure[key].change_scales(dealias) h0['g'] = h_bot + zd*h_slope #(Lz+1)-z θ0['g'] = np.log(h0).evaluate()['g'] Υ0['g'] = (m_ad*θ0).evaluate()['g'] s0['g'] = 0 problem = de.NLBVP([h0, s0, Υ0, τ_s1, τ_s2, τ_h1]) problem.add_equation((grad(h0) + lift1(τ_h1,-1), -grad_φ*ez + h0*grad(s0))) problem.add_equation((-lap(h0) + lift(τ_s1,-1) + lift(τ_s2,-2), ε)) problem.add_equation(((γ-1)*Υ0 + s_c_over_c_P*γ*s0, np.log(h0))) problem.add_equation((Υ0(z=0), 0)) problem.add_equation((h0(z=0), 1)) problem.add_equation((h0(z=Lz), np.exp(-n_h))) # Solver solver = problem.build_solver(ncc_cutoff=ncc_cutoff) pert_norm = np.inf while pert_norm > tolerance: solver.newton_iteration() pert_norm = sum(pert.allreduce_data_norm('c', 2) for pert in solver.perturbations) logger.info('current perturbation norm = {:.3g}'.format(pert_norm)) if verbose: import matplotlib.pyplot as plt fig, ax = plt.subplots() ax2 = ax.twinx() ax.plot(zd, h0['g'], linestyle='dashed', color='xkcd:dark grey', label='h') ax2.plot(zd, np.log(h0).evaluate()['g'], label=r'$\ln h$') ax2.plot(zd, Υ0['g'], label=r'$\ln \rho$') ax2.plot(zd, s0['g'], color='xkcd:brick red', label=r'$s$') ax.legend() ax2.legend() fig.savefig('heated_polytrope_nh{}_eps{}_gamma{:.3g}.pdf'.format(n_h,ε,γ)) for key in structure: structure[key].change_scales(1) return structure