def test_sin_nlbvp(benchmark, x_basis_class, Nx, dtype): # Parameters ncc_cutoff = 1e-10 tolerance = 1e-10 # Build domain x_basis = x_basis_class('x', Nx, interval=(0, 1), dealias=2) domain = de.Domain([x_basis], np.float64) # Setup problem problem = de.NLBVP(domain, variables=['u'], ncc_cutoff=ncc_cutoff) problem.add_equation("dx(u) = sqrt(1 - u**2)") problem.add_bc("left(u) = 0") # Setup initial guess solver = problem.build_solver() x = domain.grid(0) u = solver.state['u'] u['g'] = x # Iterations pert = solver.perturbations.data pert.fill(1 + tolerance) while np.sum(np.abs(pert)) > tolerance: solver.newton_iteration() logger.info('Perturbation norm: {}'.format(np.sum(np.abs(pert)))) # Check solution u_true = np.sin(x) u.set_scales(1) assert np.allclose(u['g'], u_true)
def build_problem(self, ncc_cutoff=1e-10): """Constructs and initial value problem of the current object's equation set Arguments: ---------- ncc_cutoff : float The largest coefficient magnitude to keep track of when building NCCs """ if self.variables is None: logger.error("BVP variables must be set before problem is built") self.problem = de.NLBVP(self.de_domain.domain, variables=self.variables, ncc_cutoff=ncc_cutoff)
def __init__(self, *args, ncc_cutoff=1e-10, dealias=3 / 2, flux_factor=1, **kwargs): self.flux_factor = flux_factor super(FC_equilibrium_solver, self).__init__(*args, dealias=dealias, **kwargs) self.problem = de.NLBVP( self.domain, variables=['ln_T1', 'ln_rho1', 'ln_T1_z', 'M1'], ncc_cutoff=ncc_cutoff)
def find_rho(domain, rho0, T0, T0z, mass, m, tol=1e-10, n_rho0=3, epsilon=1e-4, gamma=5. / 3): problem = de.NLBVP(domain, variables=['rho', 'ln_rho', 'M']) problem.parameters['T'] = T0 problem.parameters['Tz'] = T0z problem.parameters['tot_M'] = mass problem.parameters['g'] = 1 + m problem.add_equation('dz(M) - rho = 0') problem.add_equation('ln_rho = log(rho)') problem.add_equation('dz(ln_rho) = -(Tz + g)/T') problem.add_bc(' left(M) = 0') problem.add_bc('right(M) = tot_M') solver = problem.build_solver() rho = solver.state['rho'] ln_rho = solver.state['ln_rho'] M = solver.state['M'] rho0.antidifferentiate('z', ('left', 0), out=M) ln_rho['g'] = np.copy(np.log(rho0['g'])) rho['g'] = np.copy(rho0['g']) pert = solver.perturbations.data pert.fill(1 + tol) while np.sum(np.abs(pert)) > tol: solver.newton_iteration() ln_rho_bot = np.mean(ln_rho.interpolate(z=0)['g']) ln_rho_top = np.mean(ln_rho.interpolate(z=Lz)['g']) n_rho = ln_rho_bot - ln_rho_top d_nrho = n_rho - n_rho0 dS_0 = -epsilon * n_rho0 * (gamma - 1) / m / gamma delta_dS = (gamma - 1) * d_nrho / gamma dS = dS_0 + delta_dS print( 'epsilon {} / BL {} / Delta n_rho: {} / dS_0 {}, delta_dS {}, dS/dS_0 {}' .format(epsilon, bl_thickness, d_nrho, dS_0, delta_dS, dS / dS_0)) return rho['g'], ln_rho['g'], d_nrho, dS / dS_0
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
def solve_dedalus(X0, P, domain, tolerance=1e-10, **bvp_kw): """Solve for structure using Dedalus NLBVP.""" # Unpack state vector and parameters p0, pz0 = X0 N2, g, γ = P # Setup buoyancy frequency field z = domain.grid(0) N2f = domain.new_field() N2f['g'] = np.array([N2(zi) for zi in z]) # Setup NLBVP for background problem = de.NLBVP(domain, variables=['p', 'pz'], **bvp_kw) problem.parameters['γ'] = γ problem.parameters['g'] = g problem.parameters['p0'] = p0 problem.parameters['pz0'] = pz0 problem.parameters['N2'] = N2f problem.add_equation("dz(pz) + (N2/g)*pz = pz*pz/p/γ") problem.add_equation("dz(p) - pz = 0") problem.add_bc("left(p) = p0") problem.add_bc("left(pz) = pz0") # Start from scipy solution z0 = domain.bases[0].interval[0] zs = np.concatenate([[z0], z]) p, pz = solve_scipy(X0, P, zs) solver = problem.build_solver() solver.state['p']['g'] = p[1:] solver.state['pz']['g'] = pz[1:] # Solve pert = solver.perturbations.data pert.fill(1 + tolerance) while np.sum(np.abs(pert)) > tolerance: solver.newton_iteration() logger.info('Perturbation norm: {}'.format(np.sum(np.abs(pert)))) p = solver.state['p'] pz = solver.state['pz'] return p, pz
def loop_tasks(self, tolerance=1e-10): """ Perform AE tasks every loop iteration. Arguments: --------- tolerance : float convergence tolerance for AE NLBVP """ # Don't do anything AE related if Pe < 1 if self.flow.grid_average('Pe') < 1 and not self.pe_switch: return elif not self.pe_switch: self.curr_ae_wait_time += self.solver_ivp.sim_time self.pe_switch = True #If first averaging iteration, reset stuff properly first = False if not self.doing_ae and not self.finished_ae and self.solver_ivp.sim_time >= self.curr_ae_wait_time: self._reset_profiles() #set time data properly self.doing_ae = True first = True if self.doing_ae: self._update_avg_profiles() if first: return do_AE = self._check_convergence() if do_AE: #Get averages from global domain avg_fields = OrderedDict() for k, prof in self.avg_profiles.items(): avg_fields[k] = self._communicate_profile( prof / self.elapsed_avg_time) #Solve BVP if self.dist_ivp.comm_cart.rank == 0: problem = de.NLBVP( self.AE_domain, variables=['T1', 'T1_z', 'p', 'delta_T1', 'Xi']) for k, p in (['P', self.P], ['R', self.R]): problem.parameters[k] = p for k, p in avg_fields.items(): f = self.AE_domain.new_field() f['g'] = p problem.parameters[k] = f self._set_AE_equations(problem) solver = problem.build_solver() pert = solver.perturbations.data pert.fill(1 + tolerance) while np.sum(np.abs(pert)) > tolerance: solver.newton_iteration() logger.info('Perturbation norm: {}'.format( np.sum(np.abs(pert)))) else: solver = None # Update fields appropriately ae_structure = self._broadcast_ae_solution(solver) diff = self._update_simulation_state(ae_structure, avg_fields) #communicate diff if diff < self.ivp_convergence_thresh: self.finished_ae = True logger.info('Diff: {:.4e}, finished_ae? {}'.format( diff, self.finished_ae)) self.doing_ae = False self.curr_ae_wait_time = self.solver_ivp.sim_time + self.ae_wait_time self.curr_ae_avg_time = self.ae_avg_time self.curr_ae_avg_thresh = self.ae_avg_thresh
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
def solve_BVP(self, atmosphere_kwargs, diffusivity_args, bc_kwargs, tolerance=1e-10): """ Solves a BVP in a 2D Boussinesq box. The BVP calculates updated temperature / pressure fields, then updates the solver states which are tracked in self.solver_states. This automatically updates the IVP's fields. """ super(BoussinesqBVPSolver, self).solve_BVP() nz = atmosphere_kwargs['nz'] # Create space for the returned profiles on all processes. return_dict = OrderedDict() for v in self.VARS.keys(): return_dict[v] = np.zeros(self.nz, dtype=np.float64) # No need to waste processor power on multiple bvps, only do it on one if self.rank == 0: vel_adjust_factor = 1 atmosphere = self.atmosphere_class(dimensions=1, comm=MPI.COMM_SELF, **atmosphere_kwargs) atmosphere.problem = de.NLBVP(atmosphere.domain, variables=['T1', 'T1_z', 'p1'], ncc_cutoff=tolerance) #Zero out old varables to make atmospheric substitutions happy. old_vars = ['u', 'w', 'dx(A)', 'uz', 'wz'] for sub in old_vars: atmosphere.problem.substitutions[sub] = '0' atmosphere._set_parameters(*diffusivity_args) atmosphere._set_subs() # Create the appropriate enthalpy flux profile based on boundary conditions self._update_profiles_dict(bc_kwargs, atmosphere, vel_adjust_factor) #Add time and horizontally averaged profiles from IVP to the problem as parameters for k in self.FIELDS.keys(): f = atmosphere._new_ncc() f.set_scales( self.nz / nz, keep_data=True ) #If nz(bvp) =/= nz(ivp), this allows interaction between them if len(self.profiles_dict[k].shape) == 2: f['g'] = self.profiles_dict[k].mean(axis=0) else: f['g'] = self.profiles_dict[k] atmosphere.problem.parameters[k] = f self._set_eqns(atmosphere.problem) self._set_BCs(atmosphere, bc_kwargs) # Solve the BVP solver = atmosphere.problem.build_solver() pert = solver.perturbations.data pert.fill(1 + tolerance) while np.sum(np.abs(pert)) > tolerance: solver.newton_iteration() logger.info('Perturbation norm: {}'.format(np.sum( np.abs(pert)))) T1 = solver.state['T1'] T1_z = solver.state['T1_z'] P1 = solver.state['p1'] #Appropriately adjust T1 in IVP T1.set_scales(self.nz / nz, keep_data=True) return_dict['T1_IVP'] += T1['g'] #Appropriately adjust T1_z in IVP T1_z.set_scales(self.nz / nz, keep_data=True) return_dict['T1_z_IVP'] += T1_z['g'] #Appropriately adjust p in IVP P1.set_scales(self.nz / nz, keep_data=True) return_dict['p_IVP'] += P1['g'] else: for v in self.VARS.keys(): return_dict[v] *= 0 logger.info(return_dict) self.comm.Barrier() # Communicate output profiles from proc 0 to all others. for v in self.VARS.keys(): glob = np.zeros(self.nz) self.comm.Allreduce(return_dict[v], glob, op=MPI.SUM) return_dict[v] = glob vel_adj_loc = np.zeros(1) vel_adj_glob = np.zeros(1) if self.rank == 0: vel_adj_loc[0] = vel_adjust_factor self.comm.Allreduce(vel_adj_loc, vel_adj_glob, op=MPI.SUM) # Actually update IVP states for v in self.VARS.keys(): #Subtract out current avg self.solver_states[v].set_scales(1, keep_data=True) self.solver_states[v]['g'] -= self.solver_states[v]['g'].mean( axis=0) #Put in right avg self.solver_states[v].set_scales(1, keep_data=True) self.solver_states[v]['g'] += return_dict[v][ self.n_per_proc * self.rank:self.n_per_proc * (self.rank + 1)] for v in self.VEL_VARS.keys(): self.vel_solver_states[v]['g'] *= vel_adj_glob[0] self._reset_fields()
# Build domain z_basis = de.Chebyshev('z', Nz, interval=(0, Lz), dealias=2) domain = de.Domain([z_basis], grid_dtype=np.float64) phi = domain.new_field() phi_rho_g = domain.new_field() z = domain.grid(0) phi['g'] = phi_0 + (1 - phi_0) * a * z rho_g_init = rho_0 * np.exp(-z * b / rho_0) phi_rho_g['g'] = phi['g'] * rho_g_init # Setup problem problem = de.NLBVP(domain, variables=['w_g', 'w_m', 'wz_m'], ncc_cutoff=ncc_cutoff) problem.parameters['alpha'] = alpha problem.parameters['beta'] = beta problem.parameters['B2'] = 1 problem.parameters['B3'] = a problem.parameters['phi'] = phi problem.parameters['phi_rho_g'] = phi_rho_g problem.add_equation( "alpha*(w_m - w_g) = beta*dz(phi_rho_g) + phi_rho_g - beta*phi_rho_g * dz(phi) / phi", tau=False) problem.add_equation( "(4/3)*alpha*(dz(wz_m)) - alpha*(w_m - w_g) = (phi - phi_rho_g) + (4/3)*alpha*(1+phi**2)*dz(phi)/phi*wz_m - phi*(phi - phi_rho_g)", tau=True) problem.add_equation("wz_m - dz(w_m) = 0", tau=True) problem.add_bc("left(w_m) = B2")
def compute_background(domain, params, plot=False): # Setup problem problem = de.NLBVP(domain, variables=['phi', 'phi_rho_g', 'w_g', 'w_m', 'wz_m'], ncc_cutoff=params.bvp_ncc_cutoff) problem.parameters['alpha'] = params.alpha problem.parameters['beta'] = params.beta problem.parameters['B0'] = params.phi_0 problem.parameters['B1'] = params.phi_0 * params.rho_0 problem.parameters['B2'] = 1 problem.parameters['B3'] = params.a problem.substitutions['dt(A)'] = "0*A" problem.add_equation("- dt(phi) + dz(w_m) = dz(phi * w_m)", tau=True) problem.add_equation( "dt(phi_rho_g) + dz(phi_rho_g) = dz(phi_rho_g) - dz(phi_rho_g * w_g)", tau=True) problem.add_equation( "alpha*(w_m - w_g) - beta*dz(phi_rho_g) - phi_rho_g = - beta*phi_rho_g * dz(phi) / phi", tau=False) problem.add_equation( "(4/3)*alpha*(dz(wz_m)) - alpha*(w_m - w_g) - (phi - phi_rho_g) = (4/3)*alpha*(1+phi**2)*dz(phi)/phi*wz_m - phi*(phi - phi_rho_g)", tau=True) problem.add_equation("wz_m - dz(w_m) = 0", tau=True) problem.add_bc("left(phi) = B0") problem.add_bc("left(phi_rho_g) = B1") problem.add_bc("left(w_m) = B2") problem.add_bc("left(wz_m) = B3") # Setup initial guess solver = problem.build_solver() logger.info('Solver built') # Initial conditions z = domain.grid(0) phi = solver.state['phi'] phi_rho_g = solver.state['phi_rho_g'] w_g = solver.state['w_g'] w_m = solver.state['w_m'] wz_m = solver.state['wz_m'] phi['g'] = params.phi_0 + (1 - params.phi_0) * params.a * z rho_g_init = params.rho_0 * np.exp(-z * params.b / params.rho_0) phi_rho_g['g'] = phi['g'] * rho_g_init w_g['g'] = params.wg_0 * (1 + params.a * z) w_m['g'] = 1 + params.a * z # Iterations pert = solver.perturbations.data pert.fill(1 + params.tolerance) start_time = time.time() while np.sum(np.abs(pert)) > params.tolerance: solver.newton_iteration() logger.info('Perturbation norm: {}'.format(np.sum(np.abs(pert)))) end_time = time.time() logger.info('-' * 20) logger.info('Iterations: {}'.format(solver.iteration)) logger.info('Run time: %.2f sec' % (end_time - start_time)) if plot: rho_g = (phi_rho_g / phi).evaluate() import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt fig, (ax1, ax2, ax3, ax4) = plt.subplots(1, 4) phi.set_scales(1) rho_g.set_scales(1) w_g.set_scales(1) w_m.set_scales(1) ax1.plot(phi['g'], z, '.-') ax1.set_title('phi') ax2.plot(rho_g['g'], z, '.-') ax2.set_title('rho_g') ax3.plot(w_m['g'], z, '.-') ax3.set_title('w_m') ax4.plot(w_g['g'], z, '.-') ax4.set_title('w_g') plt.savefig("background.pdf") fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2) ax1.plot(np.abs(phi['c']), '.-') ax1.set_title('phi') ax1.set_yscale('log') ax2.plot(np.abs(rho_g['c']), '.-') ax2.set_title('rho_g') ax2.set_yscale('log') ax3.plot(np.abs(w_m['c']), '.-') ax3.set_title('w_m') ax3.set_yscale('log') ax4.plot(np.abs(w_g['c']), '.-') ax4.set_title('w_g') ax4.set_yscale('log') plt.savefig("background_coeffs.pdf") return solver
gamma = 5/3 m_ad = 1/(gamma-1) m = m_poly logger.info("m={}, m_ad = {}, m_poly=(3-{})/(1+{})={}".format(m, m_ad, b, a, m_poly)) ε = 0.5 #1e-3 Lz = 5 ; Q = 1-ε tau_0_BB14 = 4e-4*np.array([1e4,1e5,1e6,1e7])*5 F_over_cE_BB14 = 1/4*(np.array([26600, 16300,9300,5200])/38968)**4 Q_BB14 = tau_0_BB14*F_over_cE_BB14 z_basis = de.Chebyshev('z', nz, interval=(0,Lz), dealias=2) domain = de.Domain([z_basis], np.float64, comm=MPI.COMM_SELF) problem = de.NLBVP(domain, variables=['ln_T', 'ln_rho'], ncc_cutoff=ncc_cutoff) problem.parameters['a'] = a problem.parameters['b'] = b problem.parameters['g'] = g = (m+1) problem.parameters['Lz'] = Lz problem.parameters['gamma'] = gamma problem.parameters['ε'] = ε problem.parameters['Q'] = Q problem.parameters['lnT0'] = lnT0 = 0 problem.parameters['lnρ0'] = lnρ0 = m*lnT0 problem.substitutions['ρκ(ln_rho,ln_T)'] = "exp(ln_rho*(a+1)+ln_T*(b))" problem.add_equation("dz(ln_T) = -Q*exp(ln_rho*(a+1)+ln_T*(b-4))") problem.add_equation("dz(ln_T) + dz(ln_rho) = -g*exp(-ln_T)") problem.add_bc("left(ln_T) = lnT0") problem.add_bc("left(ln_rho) = lnρ0")
def solve_BVP(self, atmosphere_kwargs, diffusivity_kwargs, tolerance=1e-13): """ Solves a BVP in a 2D FC_ConstHeating atmosphere under the kappa/mu formulation of the equations. Run parameters are specified, and should be similar to those of the bvp. The BVP calculates updated rho / temperature fields, then updates the solver states which are tracked in self.solver_states. This automatically updates the IVP's fields. """ super(FC_BVP_solver, self).solve_BVP() nz = atmosphere_kwargs['nz'] # No need to waste processor power on multiple bvps, only do it on one if self.rank == 0: atmosphere = self.atmosphere_class(dimensions=1, comm=MPI.COMM_SELF, **atmosphere_kwargs) #Variables are T, dz(T), rho, integrated mass atmosphere.problem = de.NLBVP(atmosphere.domain, variables=['T1', 'T1_z', 'rho1', 'M1'],\ ncc_cutoff=tolerance) #Zero out old varables to make atmospheric substitutions happy. old_vars = ['u', 'w', 'ln_rho1', 'v', 'u_z', 'w_z', 'v_z', 'dx(A)'] for sub in old_vars: atmosphere.problem.substitutions[sub] = '0' atmosphere._set_diffusivities(**diffusivity_kwargs) atmosphere._set_parameters() atmosphere._set_subs() #Add time and horizontally averaged profiles from IVP to the problem as parameters for k in self.FIELDS.keys(): f = atmosphere._new_ncc() f.set_scales( self.nz / nz, keep_data=True ) #If nz(bvp) =/= nz(ivp), this allows interaction between them f['g'] = self.profiles_dict[k] atmosphere.problem.parameters[k] = f self._set_subs(atmosphere.problem) self._set_eqns(atmosphere.problem) self._set_BCs(atmosphere.problem) # Solve the BVP solver = atmosphere.problem.build_solver() pert = solver.perturbations.data pert.fill(1 + tolerance) while np.sum(np.abs(pert)) > tolerance: solver.newton_iteration() logger.info('Perturbation norm: {}'.format(np.sum( np.abs(pert)))) T1 = solver.state['T1'] T1_z = solver.state['T1_z'] rho1 = solver.state['rho1'] # Create space for the returned profiles on all processes. return_dict = dict() for v in self.VARS.keys(): return_dict[v] = np.zeros(self.nz, dtype=np.float64) if self.rank == 0: #Appropriately adjust T1 in IVP T1.set_scales(self.nz / nz, keep_data=True) return_dict['T1_IVP'] = T1['g'] + self.profiles_dict[ 'T1_IVP'] - self.profiles_dict_curr['T1_IVP'] #Appropriately adjust T1_z in IVP T1_z.set_scales(self.nz / nz, keep_data=True) return_dict['T1_z_IVP'] = T1_z['g'] + self.profiles_dict[ 'T1_z_IVP'] - self.profiles_dict_curr['T1_z_IVP'] #Appropriately adjust ln_rho1 in IVP rho1.set_scales(self.nz / nz, keep_data=True) return_dict['ln_rho1_IVP'] = np.log( 1 + (rho1['g'] + self.profiles_dict['rho_full_IVP'] - self.profiles_dict_curr['rho_full_IVP']) / self.profiles_dict_curr['rho_full_IVP']) print('returning the following avg profiles from BVP\n', return_dict) self.comm.Barrier() # Communicate output profiles from proc 0 to all others. for v in self.VARS.keys(): glob = np.zeros(self.nz) self.comm.Allreduce(return_dict[v], glob, op=MPI.SUM) return_dict[v] = glob # Actually update IVP states for v in self.VARS.keys(): self.solver_states[v].set_scales(1, keep_data=True) self.solver_states[v]['g'] += return_dict[v][ self.n_per_proc * self.rank:self.n_per_proc * (self.rank + 1)] self._reset_fields()
from dedalus import public as de import logging logger = logging.getLogger(__name__) # Parameters n = 3.25 # Build domain x_basis = de.Chebyshev('x', 128, interval=(0, 1), dealias=2) domain = de.Domain([x_basis], np.float64) # Setup problem problem = de.NLBVP(domain, variables=['f', 'fx', 'R']) problem.parameters['n'] = n problem.add_equation("x*dx(fx) + 2*fx = -x*(R**2)*(f**n)") problem.add_equation("fx - dx(f) = 0") problem.add_equation("dx(R) = 0") problem.add_bc("left(f) = 1") problem.add_bc("left(fx) = 0") problem.add_bc("right(f) = 0") # Setup initial guess solver = problem.build_solver() x = domain.grid(0, scales=domain.dealias) f = solver.state['f'] fx = solver.state['fx'] R = solver.state['R'] f['g'] = np.cos(np.pi/2 * x)*0.9
def structure_bvp(atmo_class, atmo_args, atmo_kwargs, profiles, scalars): """ Solves a BVP to get the right FT atmospheric structure Inputs: ------- atmo_class : The class type of the atmosphere object atmo_args : tuple args for atmo_class.__init__() atmo_kwargs : dict kwargs for atmo_class.__init__() profiles : OrderedDict A dictionary with profiles required for the BVP scalars : OrderedDict A dictionary with scalar values required for the BVP """ # Grab important raw TT values dS_TT = scalars['s_over_cp_z'] Nu = scalars['Nu'] T1_TT = profiles['T1'] UdotGradw_TT = profiles['UdotGradw'] # Construct Dedalus setup atmosphere = atmo_class(*atmo_args, **atmo_kwargs) Lz = atmosphere.Lz z_basis = de.Chebyshev('z', len(T1_TT), interval=[0, Lz], dealias=1) domain = de.Domain([ z_basis, ], grid_dtype=np.float64, comm=MPI.COMM_SELF) problem = de.NLBVP(domain, variables=['dS', 'S', 'ln_rho1', 'M1']) atmosphere.build_atmosphere(domain, problem) z = domain.grid(0) # Remove the 1/Cp from dS/Cp dS_TT *= atmosphere.Cp # Solve out for Temp profile of the FT case S0 = domain.new_field() T_TT = domain.new_field() T1_FT = domain.new_field() UdotGradw = domain.new_field() grad_ad = -(atmosphere.g / atmosphere.Cp) T_TT['g'] = atmosphere.T0['g'] + T1_TT T_ad = 1 + grad_ad * (z - Lz) dT_ad_TT = np.abs( np.mean(T_TT.interpolate(z=Lz)['g'] - T_TT.interpolate(z=0)['g'])) - np.abs(grad_ad * Lz) dT_ad_FT = dT_ad_TT / Nu T1_FT['g'] = dT_ad_FT * ( (T_TT['g'] - T_ad) / dT_ad_TT) + T_ad - atmosphere.T0['g'] # Setup S0, other constants S0['g'] = atmosphere.Cp * ( (1 / atmosphere.ɣ) * np.log(atmosphere.T0['g']) - ((atmosphere.ɣ - 1) / atmosphere.ɣ) * np.log(atmosphere.rho0['g'])) dS0 = np.mean(S0.interpolate(z=Lz)['g'] - S0.interpolate(z=0)['g']) evolved_Ra_factor = (dS_TT / dS0) UdotGradw['g'] = UdotGradw_TT # Feed parameters into the problem (many are fed in through atmosphere class) problem.parameters['T1_FT'] = T1_FT problem.parameters['dS_TT'] = dS_TT problem.parameters['UdotGradw_TT'] = UdotGradw problem.substitutions['ln_rho0'] = 'log(rho0)' # Hydrostatic equilibrium (modified) + mass conservation. problem.add_equation( "(T0 + T1_FT)*dz(ln_rho1) = -dz(T1_FT) - T1_FT*ln_rho0_z + (dS/dS_TT)*UdotGradw_TT" ) problem.add_equation("dz(M1) = rho0*(exp(ln_rho1) - 1)") problem.add_equation( "S/Cp = (1/ɣ) * log(T0 + T1_FT) - ((ɣ-1)/ɣ)*(ln_rho0 + ln_rho1)") problem.add_equation("dS = right(S) - left(S)") problem.add_bc(" left(M1) = 0") problem.add_bc("right(M1) = 0") # Solve NLBVP tol = 1e-10 solver = problem.build_solver() dS = solver.state['dS'] ln_rho1_FT = solver.state['ln_rho1'] pert = solver.perturbations.data pert.fill(1 + tol) while np.sum(np.abs(pert)) > tol: solver.newton_iteration() print('pert norm: {} / dS: {:.2e}'.format(np.sum(np.abs(pert)), np.mean(dS['g']))) dS_FT = np.mean(dS['g']) FT_Ra_factor = (dS0 / dS_FT) * evolved_Ra_factor return T1_FT['g'], ln_rho1_FT[ 'g'], dS_FT / dS_TT, dT_ad_FT / dT_ad_TT, FT_Ra_factor
import logging logger = logging.getLogger(__name__) # Parameters Nx = 128 n = 3.25 ncc_cutoff = 1e-4 tolerance = 1e-8 # Build domain x_basis = de.Chebyshev('x', Nx, interval=(0, 1), dealias=2) domain = de.Domain([x_basis], np.float64) # Setup problem problem = de.NLBVP(domain, variables=['f', 'fx', 'R'], ncc_cutoff=ncc_cutoff) problem.meta['R']['x']['constant'] = True problem.parameters['n'] = n problem.add_equation("x*dx(fx) + 2*fx = -x*(R**2)*(f**n)", tau=False) problem.add_equation("fx - dx(f) = 0") problem.add_bc("left(f) = 1") problem.add_bc("right(f) = 0") # Setup initial guess solver = problem.build_solver() x = domain.grid(0) f = solver.state['f'] fx = solver.state['fx'] R = solver.state['R'] f['g'] = np.cos(np.pi / 2 * x) * 0.9 f.differentiate('x', out=fx)
dtype = np.float64 # Bases coords = d3.SphericalCoordinates('phi', 'theta', 'r') dist = d3.Distributor(coords, dtype=dtype) basis = d3.BallBasis(coords, (1, 1, Nr), radius=1, dtype=dtype, dealias=dealias) # Fields f = dist.Field(name='f', bases=basis) tau = dist.Field(name='tau', bases=basis.S2_basis(radius=1)) # Substitutions lift = lambda A: d3.Lift(A, basis, -1) # Problem problem = d3.NLBVP([f, tau], namespace=locals()) problem.add_equation("lap(f) + lift(tau) = - f**n") problem.add_equation("f(r=1) = 0") # Initial guess phi, theta, r = dist.local_grids(basis) R0 = 5 f['g'] = R0**(2/(n-1)) * (1 - r**2)**2 # Solver solver = problem.build_solver(ncc_cutoff=ncc_cutoff) pert_norm = np.inf f.change_scales(dealias) steps = [f['g'].ravel().copy()] while pert_norm > tolerance: solver.newton_iteration()