def test_LP_model(): alpha = 5e-3 M = 1e-2 * Msun Rd = 30. T0 = (2 * np.pi) N = 100 grid = Grid(1.0, 1000, N, spacing='natural') star = SimpleStar() eos = LocallyIsothermalEOS(star, 1 / 30., -0.25, alpha) eos.set_grid(grid) nud = np.interp(Rd, grid.Rc, eos.nu) sol = LBP_Solution(M, Rd, nud, 1) Sigma = sol(grid.Rc, 0.) disc = AccretionDisc(grid, star, eos, Sigma) visc = ViscousEvolution() yr = 2*np.pi # Integrate to specfi times = np.logspace(0, 6, 7)*yr max_err = [3.6e-4, 3.6e-3, 3.6e-2, 3.6e-1, 2.2, 7.9, 17.5] t = 0 for i, ti in enumerate(times): while t < ti: dt = visc.max_timestep(disc) dti = min(dt, ti - t) visc(dti, disc) t = min(t + dt, ti) # Check L1, L2 and max norm exact = sol(grid.Rc, t) error = (exact - disc.Sigma) / exact.mean() L1 = np.linalg.norm(error, ord=1) assert(L1 < max_err[i])
class DustDynamicsModel(object): """ """ def __init__(self, disc, diffusion=False, radial_drift=False, viscous_evo=False, evaporation=False, settling=False, Sc=1, t0=0): self._disc = disc self._visc = None if viscous_evo: bound = 'power_law' # Power law extrapolation fails with zero-density, use simple # boundary condition instead if evaporation: bound = 'Zero' self._visc = ViscousEvolution(boundary=bound) self._diffusion = None if diffusion: diffusion = TracerDiffusion(Sc) # Diffusion can be handled by the radial drift object, without dust we # include it ourself. self._radial_drift = None if radial_drift: self._radial_drift = SingleFluidDrift(diffusion, settling) else: self._diffusion = diffusion self._evaporation = False if evaporation: self._evaporation = evaporation self._t = t0 def __call__(self, tmax): """Evolve the disc for a single timestep args: dtmax : Upper limit to time-step returns: dt : Time step taken """ # Compute the maximum time-step dt = tmax - self.t if self._visc: dt = min(dt, self._visc.max_timestep(self._disc)) if self._radial_drift: dt = min(dt, self._radial_drift.max_timestep(self._disc)) disc = self._disc # Do Advection-diffusion update if self._visc: dust = None size = None try: dust = disc.dust_frac size = disc.grain_size except AttributeError: pass self._visc(dt, disc, [dust, size]) if self._radial_drift: self._radial_drift(dt, disc) # Pin the values to >= 0: disc.Sigma[:] = np.maximum(disc.Sigma, 0) disc.dust_frac[:] = np.maximum(disc.dust_frac, 0) # Apply any photo-evaporation: if self._evaporation: self._evaporation(disc, dt) # Now we should update the auxillary properties, do grain growth etc disc.update(dt) self._t += dt return dt @property def disc(self): return self._disc @property def t(self): return self._t def dump(self, filename): """Write the current state to a file, including header information""" # Put together a header containing information about the physics # included head = self.disc.header() + '\n' if self._visc: head += self._visc.header() + '\n' if self._radial_drift: head += self._radial_drift.header() + '\n' if self._diffusion: head += self._diffusion.header() + '\n' if self._chem: head += self._chem.header() + '\n' with open(filename, 'w') as f: f.write(head, '# time: {}yr\n'.format(self.t / (2 * np.pi))) # Construct the list of variables that we are going to print Ncell = self.disc.Ncells Ndust = 0 try: Ndust = self.disc.dust_frac.shape[0] except AttributeError: pass head = '# R Sigma T' for i in range(Ndust): head += ' epsilon[{}]'.format(i) for i in range(Ndust): head += ' a[{}]'.format(i) chem = None try: chem = self.disc.chem for k in chem.gas: head += ' {}'.format(k) for k in chem.ice: head += ' s{}'.format(k) except AttributeError: pass f.write(head + '\n') R, Sig, T = self.disc.R, self.disc.Sigma, self.disc.T for i in range(Ncell): f.write('{} {} {}'.format(R[i], Sig[i], T[i])) for j in range(Ndust): f.write(' {}'.format(self.disc.dust_frac[j, i])) for j in range(Ndust): f.write(' {}', format(self.disc.grain_size[j, i])) if chem: for k in chem.gas: f.write(' {}'.format(chem.gas[k][i])) for k in chem.ice: f.write(' {}'.format(chem.ice[k][i])) f.write('\n')
class ChemoDynamicsModel(object): """ """ def __init__(self, disc, chem=None, diffusion=False, radial_drift=False, viscous_evo=False, Sc=1, t0=0): self._disc = disc self._chem = chem self._visc = None if viscous_evo: self._visc = ViscousEvolution() self._diffusion = None if diffusion: diffusion = TracerDiffusion(Sc) # Diffusion can be handled by the radial drift object, without dust we # include it ourself. self._radial_drift = None if radial_drift: self._radial_drift = SingleFluidDrift(diffusion) else: self._diffusion = diffusion self._t = t0 def __call__(self, tmax): """Evolve the disc for a single timestep args: dtmax : Upper limit to time-step returns: dt : Time step taken """ # Compute the maximum time-step dt = tmax - self.t if self._visc: dt = min(dt, self._visc.max_timestep(self._disc)) if self._radial_drift: dt = min(dt, self._radial_drift.max_timestep(self._disc)) disc = self._disc gas_chem, ice_chem = None, None try: gas_chem = disc.chem.gas.data ice_chem = disc.chem.ice.data except AttributeError: pass # Do Advection-diffusion update if self._visc: dust = None try: dust = disc.dust_frac except AttributeError: pass self._visc(dt, disc, [dust, gas_chem, ice_chem]) if self._radial_drift: self._radial_drift(dt, disc, gas_tracers=gas_chem, dust_tracers=ice_chem) if self._diffusion: if gas_chem is not None: gas_chem[:] += dt * self._diffusion(disc, gas_chem) if ice_chem is not None: ice_chem[:] += dt * self._diffusion(disc, ice_chem) # Pin the values to >= 0: disc.Sigma[:] = np.maximum(disc.Sigma, 0) disc.dust_frac[:] = np.maximum(disc.dust_frac, 0) if self._chem: disc.chem.gas.data[:] = np.maximum(disc.chem.gas.data, 0) disc.chem.ice.data[:] = np.maximum(disc.chem.ice.data, 0) # Chemistry if self._chem: rho = disc.midplane_gas_density eps = disc.dust_frac.sum(0) T = disc.T self._chem.update(dt, T, rho, eps, disc.chem) # If we have dust, we should update it now the ice fraction has # changed disc.update_ices(disc.chem.ice) # Now we should update the auxillary properties, do grain growth etc disc.update(dt) self._t += dt return dt @property def disc(self): return self._disc @property def t(self): return self._t def dump(self, filename): """Write the current state to a file, including header information""" # Put together a header containing information about the physics # included head = self.disc.header() + '\n' if self._visc: head += self._visc.header() + '\n' if self._radial_drift: head += self._radial_drift.header() + '\n' if self._diffusion: head += self._diffusion.header() + '\n' if self._chem: head += self._chem.header() + '\n' with open(filename, 'w') as f: f.write(head, '# time: {}yr\n'.format(self.t / (2 * np.pi))) # Construct the list of variables that we are going to print Ncell = self.disc.Ncells Ndust = 0 try: Ndust = self.disc.dust_frac.shape[0] except AttributeError: pass head = '# R Sigma T' for i in range(Ndust): head += ' epsilon[{}]'.format(i) for i in range(Ndust): head += ' a[{}]'.format(i) chem = None try: chem = self.disc.chem for k in chem.gas: head += ' {}'.format(k) for k in chem.ice: head += ' s{}'.format(k) except AttributeError: pass f.write(head + '\n') R, Sig, T = self.disc.R, self.disc.Sigma, self.disc.T for i in range(Ncell): f.write('{} {} {}'.format(R[i], Sig[i], T[i])) for j in range(Ndust): f.write(' {}'.format(self.disc.dust_frac[j, i])) for j in range(Ndust): f.write(' {}', format(self.disc.grain_size[j, i])) if chem: for k in chem.gas: f.write(' {}'.format(chem.gas[k][i])) for k in chem.ice: f.write(' {}'.format(chem.ice[k][i])) f.write('\n')