Ejemplo n.º 1
0
def rho_zr(ICobj):
    """
    Iterates over calc_rho.py to calculate rho(z,r) on a grid of z and r
    values.
    
    Requires ICobj.sigma to be defined already
    
    * Arguments *
    
    ICobj - The initial conditions object for which rho will be calculated
    
    * Output *
    Returns dictionary containing:
        dict['rho'] : 2D array, rho at all pairs of points (z,r)
        dict['z']   : a 1D array of z points
        dict['r']   : a 1D array of r points
        
    If output=filename, dictionary is pickled and saved to filename
    
    To be safe, keep all units in Msol and au
    """
    # Get what's need from the IC object
    settings = ICobj.settings
    
    # PARSE SETTINGS
    # Rho calculation parameters
    nr = settings.rho_calc.nr
    nz = settings.rho_calc.nz
    rmin = settings.rho_calc.rmin
    rmax = settings.rho_calc.rmax
    # Initialize r,z, and rho
    r = np.linspace(rmin,rmax,nr)
    rho = SimArray(np.zeros([nz,nr]), 'Msol au**-3')
    
    start_time = time.time()
    
    for n in range(nr):
        
        print '************************************'
        print 'Calculating rho(z) - {0} of {1}'.format(n+1,nr)
        print '{0} min elapsed'.format((time.time()-start_time)/60)
        print '************************************'
        rho_vector, z = calc_rho.rho_z(ICobj, r[[n]])
        rho[:,n] = rho_vector
        
    # Convert to the units generated by calc_rho
    rho.convert_units(rho_vector.units)
    
    return rho, z, r
Ejemplo n.º 2
0
def convert_particles_to_pynbody_data(particles, length_unit=units.kpc, pynbody_unit="kpc"):
    if not HAS_PYNBODY:
        raise AmuseException("Couldn't find pynbody")

    if hasattr(particles, "u"):
        pynbody_data = new(gas=len(particles))
    else:
        pynbody_data = new(dm=len(particles))
    pynbody_data._filename = "AMUSE"
    if hasattr(particles, "mass"):
        pynbody_data['mass'] = SimArray(particles.mass.value_in(units.MSun), "Msol")
    if hasattr(particles, "position"):
        pynbody_data['x'] = SimArray(particles.x.value_in(length_unit), pynbody_unit)
        pynbody_data['y'] = SimArray(particles.y.value_in(length_unit), pynbody_unit)
        pynbody_data['z'] = SimArray(particles.z.value_in(length_unit), pynbody_unit)
    if hasattr(particles, "velocity"):
        pynbody_data['vx'] = SimArray(particles.vx.value_in(units.km / units.s), "km s^-1")
        pynbody_data['vy'] = SimArray(particles.vy.value_in(units.km / units.s), "km s^-1")
        pynbody_data['vz'] = SimArray(particles.vz.value_in(units.km / units.s), "km s^-1")
    if hasattr(particles, "h_smooth"):
        pynbody_data['smooth'] = SimArray(particles.h_smooth.value_in(length_unit), pynbody_unit)
    if hasattr(particles, "rho"):
        pynbody_data['rho'] = SimArray(particles.rho.value_in(units.g / units.cm**3),
            "g cm^-3")
    if hasattr(particles, "temp"):
        pynbody_data['temp'] = SimArray(particles.temp.value_in(units.K), "K")
    elif hasattr(particles, "u"):
#        pynbody_data['u'] = SimArray(particles.u.value_in(units.km**2 / units.s**2), "km^2 s^-2")
        temp = 2.0/3.0 * particles.u * mu() / constants.kB
        pynbody_data['temp'] = SimArray(temp.value_in(units.K), "K")
    return pynbody_data
Ejemplo n.º 3
0
def rho_z(sigma, T, r, settings):
    """ 
    rho,z = rho_z(...)
    
    Calculates rho(z) to maintain hydrostatic equilibrium in a thin disc.  
    Assumes uniform temperature in the disc, and an infinite disc where 
    rho can be treated (locally) as only a function of z.
    
    Only calculates for z>=0, since the density is assumed to be symmetric
    about z=0
    
    The initial guess for rho (a gaussian) only really seems to work for
    Mstar >> Mdisc.  Otherwise the solution can diverge violently.
    
    * NUMERICAL CALCULATION OF RHO(Z) *
    The calculation proceeds using several steps.
    1) Make an initial guess for I, the integral of rho from z to inf.  This
        is an error function
    2) Modify length scale of the initial guess to minimize the residual
        for the differential equation governing I.  Use this as the new
        initial guess.
    3) Find the root I(z) for the differential equation governing I, with
        the boundary condition that I(0) = sigma/2
    4) Set rho = -dI/dz
    5) Find the root rho(z) for the diff. eq. governing rho.
    6) In order to satisfy the BC on I, scale rho so that:
        Integral(rho) = I(0)
    7) Repeat (5) and (6) until rho is rescaled by a factor closer to unity
        than rho_tol
        
        Steps 5-7 are done because the solution for I does not seem to
        satisfy the diff. eq. for rho very well.  But doing it this way 
        allows rho to satisfy the surface density profile
    
    * Arguments *
    
    sigma - The surface density at r
    __stdout__
    T - the temperature at r
    
    r - The radius at which rho is being calculated.  Should have units
    
    settings - ICobj settings (ie, ICobj.settings)
        
    * Output *
    Returns a 1D SimArray (see pynbody) of rho(z) and a 1D SimArray of z,
    with the same units as ICobj.settings.rho_calc.zmax
    
    """
    # Parse settings
    rho_tol = settings.rho_calc.rho_tol
    nz = settings.rho_calc.nz
    zmax = settings.rho_calc.zmax

    m = settings.physical.m
    M = settings.physical.M

    # Physical constants
    kB = SimArray(1.0, 'k')
    G = SimArray(1.0, 'G')

    # Set up default units
    mass_unit = M.units
    length_unit = zmax.units
    r = (r.in_units(length_unit)).copy()

    # Initial conditions/physical parameters
    rho_int = 0.5 * sigma.in_units(
        mass_unit / length_unit**2)  # Integral of rho from 0 to inf
    a = (G * M * m / (kB * T)).in_units(length_unit)
    b = (2 * np.pi * G * m / (kB * T)).in_units(length_unit / mass_unit)
    z0guess = np.sqrt(2 * r * r * r / a).in_units(
        length_unit)  # Est. scale height of disk
    z0_dummy = (2 / (b * sigma)).in_units(length_unit)

    z = np.linspace(0.0, zmax, nz)
    dz = z[[1]] - z[[0]]

    # Echo parameters used
    print '***********************************************'
    print '* Calculating rho(z)'
    print '***********************************************'
    print 'sigma          = {0} {1}'.format(sigma, sigma.units)
    print 'zmax           = {0} {1}'.format(zmax, zmax.units)
    print 'r              = {0} {1}'.format(r, r.units)
    print 'molecular mass = {0} {1}'.format(m, m.units)
    print 'Star mass      = {0} {1}'.format(M, M.units)
    print 'Temperature    = {0} {1}'.format(T, T.units)
    print ''
    print 'rho_tol        = {0}'.format(rho_tol)
    print 'nz             = {0}'.format(nz)
    print '***********************************************'
    print 'a              = {0} {1}'.format(a, a.units)
    print 'b              = {0} {1}'.format(b, b.units)
    print 'z0guess        = {0} {1}'.format(z0guess, z0guess.units)
    print '***********************************************'

    print 'z0 (from sech^2) = {0} {1}'.format(z0_dummy, z0_dummy.units)
    # --------------------------------------------------------
    # STRIP THE UNITS FROM EVERYTHING!!!
    # This has to be done because many of the scipy/numpy functions used cannot
    # handle pynbody units.  Before returning z, rho, or anything else, the
    # Units must be re-introduced
    # --------------------------------------------------------

    rho_int, a, b, z0guess, z0_dummy, z, dz, r, T, sigma \
    = isaac.strip_units([rho_int, a, b, z0guess, z0_dummy, z, dz, r, T, sigma])

    # --------------------------------------------------------
    # Check sigma and T
    # --------------------------------------------------------
    if sigma < 1e-100:

        warn('Sigma too small.  setting rho = 0')
        rho0 = np.zeros(len(z))
        # Set up units
        rho0 = isaac.set_units(rho0, mass_unit / length_unit**3)
        z = isaac.set_units(z, length_unit)

        return rho0, z

    if T > 1e100:

        warn('Temperature too large.  Setting rho = 0')
        rho0 = np.zeros(len(z))
        # Set up units
        rho0 = isaac.set_units(rho0, mass_unit / length_unit**3)
        z = isaac.set_units(z, length_unit)

        return rho0, z

    # -------------------------------------------------------------------
    # FUNCTION DEFINITIONS
    # -------------------------------------------------------------------
    def dI_dz(I_in):
        """
        Finite difference approximation of dI/dz, assuming I is odd around I(0)
        """
        I = I_in.copy()
        dI = np.zeros(len(I))
        # Fourth order center differencing
        dI[0] = (-I[2] + 8 * I[1] - 7 * I[0]) / (6 * dz)
        dI[1] = (-I[3] + 8 * I[2] - 6 * I[0] - I[1]) / (12 * dz)
        dI[2:-2] = (-I[4:] + 8 * I[3:-1] - 8 * I[1:-3] + I[0:-4]) / (12 * dz)
        # Second order backward differencing for right edge
        dI[-2:] = (3 * I[-2:] - 4 * I[-3:-1] + I[-4:-2]) / (2 * dz)

        return dI

    def d2I_dz2(I_in):

        # Finite difference for d2I/dz2 assuming it is 0 at the origin
        I = I_in.copy()
        d2I = np.zeros(len(I))
        # Boundary condition
        d2I[0] = 0
        # Centered 4th order finite difference
        d2I[1] = (-I[3] + 16 * I[2] - 30 * I[1] + 16 * I[0] -
                  (2 * I[0] - I[1])) / (12 * dz**2)
        d2I[2:-2] = (-I[4:] + 16 * I[3:-1] - 30 * I[2:-2] + 16 * I[1:-3] -
                     I[0:-4]) / (12 * (dz**2))
        # second order backward difference for right edge
        d2I[-2:] = (-2 * I[-2:] + 5 * I[-3:-1] - 4 * I[-4:-2] +
                    I[-5:-3]) / dz**2

        return d2I

    def Ires(I_in):
        """
        Calculate the residual for the differential equation governing I,
        the integral of rho from z to "infinity."
        """
        # DEFINE INITIAL CONDITION:
        I = I_in.copy()
        I[0] = rho_int
        #I[-1] = 0.0
        weight = 1.0

        res = d2I_dz2(I) + dI_dz(I) * (a * z / ((z**2 + r**2)**(1.5)) + 2 * b *
                                       (I[0] - I))

        return weight * res

    def drho_dz(rho_in):
        """
        Fourth order, centered finite difference for d(rho)/dz, assumes that
        rho is an even function.  The right-hand boundary is done using
        backward differencing
        """
        rho = rho_in.copy()
        drho = np.zeros(len(rho))
        drho[0] = 0.0  # defined by boundary condition, rho[0] = max(rho)
        drho[1] = (-rho[3] + 8 * rho[2] - 8 * rho[0] + rho[1]) / (12 * dz)
        drho[2:-2] = (-rho[4:] + 8 * rho[3:-1] - 8 * rho[1:-3] +
                      rho[0:-4]) / (12 * dz)
        drho[-2:] = (3 * rho[-2:] - 4 * rho[-3:-1] + rho[-4:-2]) / (2 * dz)

        return drho

    def residual(rho_in):
        """
        Estimate d(rho)/dz
        """
        rho = rho_in.copy()
        # Estimate integral of rho
        I = np.zeros(len(rho))
        I[1:] = nInt.cumtrapz(rho, z)
        # Estimate residual
        res = drho_dz(rho) + a * rho * z / (
            (z**2 + r**2)**(1.5)) + 2 * b * rho * I

        return res

    def erf_res(scale_size):

        testfct = rho_int * (1 - scipy.special.erf(z / scale_size))

        return abs(Ires(testfct)).sum()

    pass
    # -------------------------------------------------------------------
    # FIND RHO
    # -------------------------------------------------------------------

    maxiter = 40

    # Estimate the scale length of the error function
    z0 = opt.fminbound(erf_res, z0guess / 100.0, 5.0 * z0guess)
    print 'Length scale guess: {0} {1}'.format(z0guess, length_unit)
    print 'Final length scale: {0} {1}'.format(z0, length_unit)

    # Begin by finding I, the integral of rho (from z to inf)
    # Assuming rho is gaussian, I is an error function
    guess = rho_int * (1 - scipy.special.erf(z / z0))

    # Find the root of the differential equation for I
    f_tol = rho_int * 6e-6
    try:

        Isol = opt.newton_krylov(Ires, guess, maxiter=maxiter, f_tol=f_tol)

    except NoConvergence:
        # Assume it didn't converge because f_tol was too strict
        # Read exception
        xepshun = sys.exc_info()
        # Extract rho from the exception
        Isol = xepshun[1][0]

    # rho is the negative derivative
    rho0 = -dI_dz(Isol)

    # Now apply the diff eq on rho
    for n in range(maxiter):

        print 'Iteration {0}'.format(n + 1)
        f_tol = rho0.max() * 6e-6
        try:

            rho0 = opt.newton_krylov(residual,
                                     rho0,
                                     maxiter=maxiter,
                                     f_tol=f_tol)

        except:
            # Assume it didn't converge because f_tol was too strict
            # Read exception
            xepshun = sys.exc_info()
            # Extract rho from the exception
            rho0 = xepshun[1][0]

        rho_scale = rho_int / nInt.cumtrapz(rho0, z)[-1]
        print 'Scaling rho by {0}'.format(rho_scale)
        rho0 = rho0 * rho_scale

        if abs(1 - rho_scale) < rho_tol - 1:

            break

    if n >= maxiter:

        print 'Warning: solution to rho did not converge for r = {0}'.format(r)

    # Re-introduce units
    rho0 = isaac.set_units(rho0, mass_unit / length_unit**3)
    z = isaac.set_units(z, length_unit)

    return SimArray(rho0, 'Msol au**-3'), SimArray(z, 'au')
Ejemplo n.º 4
0
 def finv(m):
     
     return SimArray(finv_spline(m), zunit)