コード例 #1
0
def iterate_temp(gas, tol=1e-4, do_temp=True):
    """
    Solve for the equilibrium temperature iteratively, given an initial pressure
    and the corresponding temperature assuming a mean molecular weight of unity.
    At each step the temperature is varied, the mean molecular mass recalculated
    according to the new temperature and the given pressure.

    The resulting temperatures are directly stored into the input snapshot.

    Parameters:
    -----------

    gas : pynbody snapshot of gas particles, must have arrays 'temp' and 'pressure'

    Keyword argumens:
    -----------------

    tol (1e-4) : relative tolerance level at which temperature is considered to
                 be converged

    """
    temp = gas['temp'].in_units('K').view(np.ndarray)
    pressure = gas['pressure'].in_units('g cm**-1 s**-2').view(np.ndarray)

    t_diff = np.ones(temp.shape)
    t_orig = temp
    t_old = temp
    mu = 1.
    if do_temp:
        mu = get_mu(mu * temp, pressure)
    else:
        mu = get_mu(temp, pressure / mu)
    mu_high = np.ones(mu.shape)
    mu_low = np.ones(mu.shape)
    high = np.where(mu > mu_high)
    low = np.where(mu < mu_low)
    mu_high[high] = mu[high]
    mu_low[low] = mu[low]
    mu_half = (mu_high + mu_low) / 2.
    while ((np.abs(mu_high - mu_low) / mu_low) > tol).any():
        if do_temp:
            mu = get_mu(mu * temp, pressure)
        else:
            mu = get_mu(temp, pressure / mu)
        high = np.where(mu > mu_half)
        mu_low[high] = mu_half[high]
        low = np.where(mu <= mu_half)
        mu_high[low] = mu_half[low]
        mu_half = (mu_high + mu_low) / 2.
    gas['mu'] = mu_half
    if do_temp:
        gas['temp'] = array.SimArray(mu_half * temp, 'K')
    else:
        gas['pressure'] = array.SimArray(pressure / mu, 'g cm**-1 s**-2')
コード例 #2
0
 def __calc_enclosed_mass_of_R(self, R):
     """Calculate enclosed mass as function of polar distance"""
     z = np.append(0, np.logspace(-4, 0, 1000))
     if not 0 in R:
         R = array.SimArray(np.append(0, R), R.units)
     Rs, Zs = np.meshgrid(R, z)
     Rs = array.SimArray(Rs, R.units)
     c = self.__gas_pars['c']
     rs = self.__r_vir / c
     integrand = self.__gas_profile(
         np.sqrt(Rs * Rs + (c * c * rs * rs - Rs * Rs) * Zs * Zs).in_units(
             R.units), self.__gas_pars) * Rs
     integrand[np.where((Rs == 0) * (Zs == 0))] = 0.
     integrand = 0.5 * (integrand[1:, ] + integrand[:-1, ])
     dz = z[1:] - z[:-1]
     rintegrand = (integrand.T * dz).T.sum(axis=0)
     zz = c * c * rs * rs - R * R
     zz[np.where(zz < 0)] *= 0
     rintegrand *= np.sqrt(zz)
     rintegrand = 0.5 * (rintegrand[1:] + rintegrand[:-1])
     dR = R[1:] - R[:-1]
     return 4. * np.pi * ((rintegrand * dR).cumsum()) * self.__f_bary
コード例 #3
0
 def __interpolate_jz_of_R(self):
     """Calculate angular momentum as a function of axisymmetric distance"""
     R = array.SimArray(
         np.logspace(
             np.log10(self._gas.sim['rxy'].min()),
             #np.log10(self._gas.sim['rxy'].max()), 1000), self.__r_s)
             np.log10(self._gas.sim['rxy'].max()),
             1000),
         self._gas.sim['rxy'].units).in_units(self.__r_s)
     m_encl = self.__calc_enclosed_mass_of_R(R)
     m_encl /= m_encl.max()
     self.__invert_ang_mom_profile()
     jz = interp.splev(m_encl, self.__inverse_ang_mom_prof_tck)
     self.__jz_of_R_tck = interp.splrep(R.in_units('kpc'), jz, k=1)
コード例 #4
0
 def sample_equilibrium_halo(self):
     """This method actually creates the halo"""
     start = time.clock()
     print('SampleDarkHalo: setting positions ...'),
     sys.stdout.flush()
     self.__set_positions()
     pos_time = time.clock()
     print('done in {0:.2g} s'.format(pos_time - start))
     if self.__do_velocities:
         print('SampleDarkHalo: calculating distribution function ...'),
         sys.stdout.flush()
         self.__calc_f()
         f_time = time.clock()
         print('done in {0:.2g} s'.format(f_time - pos_time))
         print('SampleDarkHalo: setting velocities ...'),
         sys.stdout.flush()
         self.__set_velocities()
         v_time = time.clock()
         print(' done in {0:.2g} s'.format(v_time - f_time) + ' ' * 10)
     self.__set_softening()
     #self.__calc_r_vir()
     self.sim['mass'] = array.SimArray(
         np.ones(self.__n_particles) / self.__n_inside_r_vir, self.__m_vir)
     #self.sim['mass'].units = self.__m_vir
     self.sim['pos'] = array.SimArray(self.__pos, self.__r_s)
     #self.sim['pos'].units = self.__r_s
     self.sim['eps'] = array.SimArray(
         np.ones(self.__n_particles) * self.__eps, self.__r_s)
     #self.sim['eps'].units = self.__r_s
     if self.__do_velocities:
         self.__calc_vel_units()
         self.sim['vel'] = array.SimArray(self.__vel, self.__vel_fac)
     else:
         self.sim['vel'] = np.zeros(self.sim['vel'].shape)
     if self.__no_bulk_vel: self.sim['vel'] -= self.sim.mean_by_mass('vel')
     end = time.clock()
     print('SampleDarkHalo: halo created in {0:.2g} s'.format(end - start))
コード例 #5
0
 def __calc_temp(self):
     """Calculate the gas temperature based on its density and pressure"""
     # units
     rho_0 = 2. / 3. * self.__gas_pars[
         'c']**3 * self.__overden / self.__m_c_gas
     rho_0 *= tools.calc_rho_crit(self.__h)
     # density 1st guess
     dens = array.SimArray(
         np.interp(self._gas.sim['r'].in_units(self.__r_s_gas),
                   self.__x_rho,
                   self.__gas_profile(self.__x_rho, self.__gas_pars)),
         rho_0 / 2.)
     temp = (self.sim.g['pressure'] / dens / units.k *
             units.m_p).in_units('K')
     self.sim.g['temp'] = temp
     tools.iterate_temp(self._gas.sim)
コード例 #6
0
 def __calc_gravity_pressure(self):
     """
     Solve the equation of hydrostatic equilibrium neglecting centrifugal effects of rotation
     """
     integrand = lambda x: self.__gravity(x) * self.__gas_profile(
         x * self.__gas_pars['c'] / self.__pars['c'], self.__gas_pars)
     self.__p = array.SimArray(
         tools.simpsons_integral(self.__x_rho, integrand, norm_ind=-1))
     self.__calc_m_c()
     unit = 4. * np.pi * self.__pars['c'] * self.__gas_pars[
         'c']**3 * self.__overden**2
     unit /= 9. * self.__m_c * self.__m_c_gas
     unit *= units.G * self.__r_vir**2 * tools.calc_rho_crit(self.__h)**2
     self.__p.units = tools.sim_array_to_unit(unit)
     self.sim.g['pressure'] += np.interp(
         self.sim.g['r'].in_units(self.__r_s), self.__x_rho,
         self.__p.in_units(self.sim.g['pressure'].units))
コード例 #7
0
 def __set_gas_velocities(self):
     """Set the gas velocities according to the specified angular momentum profile"""
     self.__interpolate_jz_of_R()
     vc = array.SimArray(
         interp.splev(self._gas.sim['rxy'].in_units('kpc'),
                      self.__jz_of_R_tck), self.__j_max)
     vc /= self._gas.sim['rxy']
     # Truncate velocity profile smoothly outside R_vir
     r_v = self.__r_vir
     vc *= tools.outer_smooth_cutoff(
         self._gas.sim['r'].in_units(r_v).view(np.ndarray),
         self.__vel_pars['factor'])
     # Quick and dirty hack due to some weird behaviour of the 'az' derived array in pynbody
     az = np.arctan2(self._gas.sim['y'], self._gas.sim['x'])
     #self._gas.sim['vel'].units = units.Unit('km s**-1')
     self._gas.sim['vel'][:, :-1] = (
         np.array([-np.sin(az), np.cos(az)]) *
         vc.in_units(self.sim['vel'].units)).transpose()
     self._gas.sim['vel'][:, -1] = np.zeros(self.__n_gas_particles)
コード例 #8
0
 def __make_gas_sphere(self):
     """Sample the gas component of the halo but set velocities to 0"""
     prng = np.random.RandomState(self.__random_seed + 1)
     self._gas = SampleDarkHalo(profile=self.__gas_profile,
                                pars=self.__gas_pars,
                                m_vir=self.__m_vir,
                                h=self.__h,
                                overden=self.__overden,
                                logxmin_rho=self.__logxmin_rho,
                                logxmax_rho=self.__logxmax_rho,
                                n_samlple_rho=self.__n_sample_rho,
                                n_particles=self.__n_gas_particles,
                                prng=prng,
                                do_velocities=False,
                                snap=self.sim.g)
     self._gas.sample_equilibrium_halo()
     #self._gas.finalize()
     self.sim.g['mass'] *= self.__f_bary
     self.sim.g['pressure'] = array.SimArray(
         np.zeros(self.__n_gas_particles), 'g cm**-1 s**-2')
コード例 #9
0
ファイル: zoom.py プロジェクト: pynbody/tangos
    def calculate(self, halo, exist):
        loaded_data_min_dm_mass = halo.dm['mass'].min()

        # If mass resolution is not present as a simulation property,
        # ensure backward compatibility by setting the min mass to infinity
        import numpy as np
        import pynbody.array as array
        simulation_min_dm_mass = array.SimArray(self.get_simulation_property("approx_resolution_Msol", default=np.inf),
                                                units="Msol")

        if np.isclose(loaded_data_min_dm_mass, simulation_min_dm_mass, rtol=1e-1, atol=1):
            # Loaded data contains some light particles. Tolerance of this test (agreement to 10% or to 1 Msol)
            # is sufficiently tight to separate heavy and light resolution,
            # while being broad enough to capture numerical inaccuracies coming
            # from the simulation mass resolution.
            n_heavy = (halo.dm['mass'] > loaded_data_min_dm_mass).sum()
            return float(n_heavy) / len(halo.dm)
        else:
            # Loaded data contains only "heavy" particles, e.g. an unzoomed halo in server mode
            return 1.0
コード例 #10
0
 def __init__(self, **kwargs):
     self.__kwargs = kwargs
     self.__profile = kwargs.get('profile', density_profiles.alphabetagamma)
     self.__drhodr = kwargs.get('drhodr',
                                density_profiles.dalphabetagammadr)
     self.__d2rhodr2 = kwargs.get('d2rhodr2',
                                  density_profiles.d2alphabetagammadr2)
     self.__pars = kwargs.get('pars', {
         'alpha': 1.,
         'beta': 3.,
         'gamma': 1.,
         'c': 10.,
         'factor': 0.1
     })
     if self.__profile == density_profiles.alphabetagamma and self.__pars[
             'beta'] <= 3.:
         if 'factor' not in self.__pars.keys(): self.__pars['factor'] = 0.1
     self.__m_vir = kwargs.get('m_vir', '1e12 Msol')
     self.__m_vir = units.Unit(self.__m_vir)
     self.__h = kwargs.get('h', 0.7)
     self.__overden = kwargs.get('overden', 200.)
     self.__r_vir = tools.calc_r_vir(self.__m_vir, self.__h, self.__overden)
     self.__r_s = self.__r_vir / self.__pars['c']
     self.__n_particles = int(kwargs.get('n_particles', 1e5))
     self.__logxmax_rho = np.log10(self.__pars['c']) + 2.
     # Make sure to sample well inside the gravitational softening
     self.__logxmin_rho = self.__logxmax_rho - .5 * np.log10(
         self.__n_particles) - 3.
     self.__logxmin_dist_func = kwargs.get('logxmin_dist_func', -3.)
     self.__logxmax_dist_func = kwargs.get('logxmax_dist_func', 14.)
     self.__n_sample_rho = int(kwargs.get('n_sample_rho', 1e4))
     self.__n_sample_dist_func = int(kwargs.get('n_sample_dist_func', 1e2))
     self.__n_sample_dist_func_rho = int(
         kwargs.get('n_sample_dist_func_rho', 1e4))
     self.__random_seed = kwargs.get('random_seed', 4)
     if 'prng' in kwargs.keys():
         self.__prng = kwargs['prng']
     else:
         self.__prng = np.random.RandomState(self.__random_seed)
     self.__spline_order = kwargs.get('spline_order', 3)
     self.__progress_bar = kwargs.get('progress_bar', False)
     self.__no_bulk_vel = kwargs.get('no_bulk_vel', True)
     self.__x_rho = np.logspace(self.__logxmin_rho, self.__logxmax_rho,
                                self.__n_sample_rho)
     self.__f_bary = kwargs.get('f_bary', 0.1)
     self.__mu = kwargs.get('mu', 1.3)
     self.__spin_parameter = kwargs.get('spin_parameter', 0.04)
     self.__rot_balanced = kwargs.get('rot_balanced', False)
     # Different gas profiles are not yet implemented or successfully tested
     self.__gas_profile = self.__profile
     self.__gas_pars = self.__pars
     #self.__gas_profile = kwargs.get('gas_profile', density_profiles.alphabetagamma)
     #self.__gas_pars = kwargs.get('gas_pars', {'alpha': 1., 'beta': 3., 'gamma': 1.,
     #   'c': 10., 'factor': 0.1})
     self.__r_s_gas = self.__r_vir / self.__gas_pars['c']
     #self.__vel_prof = kwargs.get('vel_prof', None)
     self.__vel_pars = kwargs.get(
         'vel_pars', {
             'rs_v': array.SimArray(1., 'kpc'),
             'c': self.__pars['c'],
             'prefac': 1.,
             'factor': 1.
         })
     self.__n_gas_particles = int(
         kwargs.get('n_gas_particles', self.__n_particles))
     self.__ang_mom_prof = kwargs.get('ang_mom_prof',
                                      am_profiles.bullock_prof)
     self.__ang_mom_pars = kwargs.get('ang_mom_pars', {'mu': self.__mu})
     self.__fname = kwargs.get('fname', 'halo.out')
     # Careful here: as of now, only the output as tipsy files has been successfully tested
     self.__type = {
         'gadget': gadget.GadgetSnap,
         'grafic': grafic.GrafICSnap,
         'nchilada': nchilada.NchiladaSnap,
         'ramses': ramses.RamsesSnap,
         'tipsy': tipsy.TipsySnap
     }[kwargs.get('type', 'tipsy')]
     self.sim = new(dm=self.__n_particles, gas=self.__n_gas_particles)
     self.sim.physical_units()
コード例 #11
0
def trackIsoBH(simname, SF=False, filename=False, BeLazy=False):
    if not os.path.exists('files.list'):
        getFileLists(simname)
    f = open('files.list')
    files = f.readlines()
    distp = array.SimArray(np.zeros(len(files)), 'pc')
    xp = array.SimArray(np.zeros(len(files)), 'pc')
    yp = array.SimArray(np.zeros(len(files)), 'pc')
    zp = array.SimArray(np.zeros(len(files)), 'pc')
    vx = array.SimArray(np.zeros(len(files)), 'km s**-1')
    vy = array.SimArray(np.zeros(len(files)), 'km s**-1')
    vz = array.SimArray(np.zeros(len(files)), 'km s**-1')
    vcx = array.SimArray(np.zeros(len(files)), 'km s**-1')
    vcy = array.SimArray(np.zeros(len(files)), 'km s**-1')
    vcz = array.SimArray(np.zeros(len(files)), 'km s**-1')
    vrp = array.SimArray(np.zeros(len(files)), 'km s**-1')
    distc = array.SimArray(np.zeros(len(files)), 'pc')
    xc = array.SimArray(np.zeros(len(files)), 'pc')
    yc = array.SimArray(np.zeros(len(files)), 'pc')
    zc = array.SimArray(np.zeros(len(files)), 'pc')
    vrc = array.SimArray(np.zeros(len(files)), 'km s**-1')
    cnt = 0
    time = array.SimArray(np.zeros(len(files)), 'Gyr')
    #	time = (np.arange(len(files))+1)*outInterval*dt
    for sim in files:
        print "getting data from ", sim.strip('\n')
        s = pynbody.load(sim.strip('\n'))
        if BeLazy == False:
            cen = pynbody.analysis.halo.shrink_sphere_center(s)
            vcen = pynbody.analysis.halo.center_of_mass_velocity(s)
        cenpot = s['pos'][(s['phi'] == float(s['phi'].min()))]
        cenpot = cenpot[0]
        time[cnt] = s.properties['time'].in_units('Gyr')
        if SF == False:
            if BeLazy == False:
                vx[cnt] = s.stars['vx'].in_units('km s**-1')[0]
                vy[cnt] = s.stars['vx'].in_units('km s**-1')[0]
                vz[cnt] = s.stars['vx'].in_units('km s**-1')[0]
                s.stars['vel'] -= vcen
                vcx[cnt] = s.stars['vx'].in_units('km s**-1')[0]
                vcy[cnt] = s.stars['vy'].in_units('km s**-1')[0]
                vcz[cnt] = s.stars['vz'].in_units('km s**-1')[0]
            s.stars['pos'] -= cenpot
            xp[cnt] = s.stars['x'].in_units('pc')[0]
            yp[cnt] = s.stars['y'].in_units('pc')[0]
            zp[cnt] = s.stars['z'].in_units('pc')[0]
            if BeLazy == False:
                vrp[cnt] = s.stars['vr'].in_units('km s**-1')[0]
            distp[cnt] = s.stars['r'].in_units('pc')[0]
            s.stars['pos'] += cenpot
            if BeLazy == False: s.stars['pos'] -= cen
            xc[cnt] = s.stars['x'].in_units('pc')[0]
            yc[cnt] = s.stars['y'].in_units('pc')[0]
            zc[cnt] = s.stars['z'].in_units('pc')[0]
            if BeLazy == False:
                vrc[cnt] = s.stars['vr'].in_units('km s**-1')[0]
                distc[cnt] = s.stars['r'].in_units('pc')[0]
        cnt += 1
        del (s)
        gc.collect()

    BHorbitInfo = {
        'xp': xp,
        'yp': yp,
        'zp': zp,
        'xc': xc,
        'yc': yc,
        'zc': zc,
        'vrp': vrp,
        'vrc': vrc,
        'vx': vx,
        'vy': vy,
        'vz': vz,
        'vcx': vcx,
        'vcy': vcy,
        'vcz': vcz,
        'distp': distp,
        'distc': distc,
        'time': time
    }
    if filename:
        f = open(str(filename), 'wb')
        pickle.dump(BHorbitInfo, f)
        f.close()
    return BHorbitInfo
コード例 #12
0
def getBHhalo(simname,
              findcenter='hyb',
              minHM=1e10,
              minNum=30,
              filename=None,
              initFile=None):
    if not os.path.exists("grpfiles.list"):
        simname_split = simname.split('.')
        num = len(simname_split)
        os.system('ls ' + simname + '.00*.grp | cut -d "." -f1-' +
                  str(num + 1) + '> grpfiles.list')
    if filename:
        if os.path.exists(filename):
            print "file", filename, "already exists! reading it in and appending it with new data"
            f = open(filename, 'rb')
            BHhaloOLD = pickle.load(f)
            f.close()
            startStep = len(BHhaloOLD['haloID'][0])
            os.system('rm ' + filename)
            print "old file has", startStep, "halos already completed"
        else:
            startStep = 0
    if initFile:
        if os.path.exists(initFile):
            print "found file ", initFile, "reading it in now"
            f = open(initFile, 'rb')
            BHhaloOLD = pickle.load(f)
            f.close()
            startStep = len(BHhaloOLD['haloID'][0])
            print "given file has", startStep, "halos already completed"
            if initFile == filename:
                print "given file has same name as target file... deleting old file to replace with new file"
                os.system('rm ' + filename)

    if initFile == None: startStep = 0

    f = open("grpfiles.list")
    munits = 'Msol'
    vunits = 'km s**-1'
    posunits = 'kpc'
    cposunits = 'a kpc'

    print "finding BH iords..."
    bhiords = getBHiords(simname)
    files = f.readlines()
    f.close()
    nsteps = len(files) - startStep
    nbh = len(bhiords)

    bhmass = array.SimArray(np.zeros((nbh, nsteps)), munits)
    haloid = np.zeros((nbh, nsteps))
    mhalo = array.SimArray(np.zeros((nbh, nsteps)), munits)
    mdark = array.SimArray(np.zeros((nbh, nsteps)), munits)
    mstar = array.SimArray(np.zeros((nbh, nsteps)), munits)
    mgas = array.SimArray(np.zeros((nbh, nsteps)), munits)
    #vhalo = array.SimArray(np.zeros((nbh,nsteps,3)),vunits)
    dist = array.SimArray(np.zeros((nbh, nsteps)), posunits)
    distcen = array.SimArray(np.zeros((nbh, nsteps)), posunits)
    bhpos = array.SimArray(np.zeros((nbh, nsteps, 3)), posunits)
    bhposcen = array.SimArray(np.zeros((nbh, nsteps, 3)), posunits)
    bhvel = array.SimArray(np.zeros((nbh, nsteps, 3)), vunits)
    bhvelcen = array.SimArray(np.zeros((nbh, nsteps, 3)), vunits)
    halorad = array.SimArray(np.zeros((nbh, nsteps)), posunits)
    scaleFac = np.zeros((nbh, nsteps))
    interact = np.zeros((nbh, nsteps))
    intpos = array.SimArray(np.zeros((nbh, nsteps, 3)), posunits)
    intvel = array.SimArray(np.zeros((nbh, nsteps, 3)), vunits)
    intdist = array.SimArray(np.zeros((nbh, nsteps)), posunits)
    #rho = array.SimArray(np.zeros((nbh,nsteps)),'g cm**-3')
    #cs = array.SimArray(np.zeros((nbh,nsteps)),'cm s**-1')

    for stepcnt in range(nsteps):
        line = files[stepcnt + startStep].strip()
        print "getting halo information for ", line
        s = pynbody.load(line)
        s.physical_units()
        cboxsize = 2 * s['x'].in_units('a kpc').max()
        simBH, = np.where(np.in1d(s.star['iord'], bhiords))
        if not len(simBH):
            print "no BHs in this step! moving on..."
            continue
        boxsize = cboxsize.in_units('kpc')
        amigastat = readcol.readcol(line + '.amiga.stat', asdict=True)
        amigastat['cen'] = pynbody.array.SimArray(
            (np.array([amigastat['Xc'], amigastat['Yc'], amigastat['Zc']]).T *
             1e3 - cboxsize / 2.) * s.properties['a'], posunits)
        h = s.halos()
        #simBH, = np.where(np.in1d(s.star['iord'],bhiords))
        okgrp, = np.where(np.in1d(s.star['amiga.grp'][simBH],
                                  amigastat['Grp']))
        simBH = simBH[okgrp]
        asort = np.argsort(s.star['iord'][simBH])
        simBH = simBH[asort]
        simoutBH, = np.where(np.in1d(bhiords, s.star['iord'][simBH]))
        #outBH = np.where(np.in1d(bhiords,s.star['iord'][simBH]))
        print "there are ", len(simBH), "BHs in the step"
        allHaloID, invInd = np.unique(s.star['amiga.grp'][simBH],
                                      return_inverse=True)
        statind, = np.where(np.in1d(amigastat['Grp'], allHaloID))
        #bad, = np.where(np.in1d(allHaloID,amigastat['Grp'])==False)
        #np.delete(allHaloID,bad)
        #badind, = np.where(np.in1d(invInd,bad))
        #np.delete(invInd,badind)
        if not np.array_equal(allHaloID[invInd],
                              amigastat['Grp'][statind[invInd]]):
            print "f**k!"
            return
        haloid[simoutBH, stepcnt] = allHaloID[invInd]
        mhalo[simoutBH, stepcnt] = pynbody.array.SimArray(
            amigastat['Mvir(M_sol)'][statind[invInd]], munits)
        mstar[simoutBH, stepcnt] = pynbody.array.SimArray(
            amigastat['StarMass(M_sol)'][statind[invInd]], munits)
        mgas[simoutBH, stepcnt] = pynbody.array.SimArray(
            amigastat['GasMass(M_sol)'][statind[invInd]], munits)
        mgas[simoutBH, stepcnt] = pynbody.array.SimArray(
            amigastat['GasMass(M_sol)'][statind[invInd]], munits)
        halorad[simoutBH, stepcnt] = pynbody.array.SimArray(
            amigastat['Rvir(kpc)'][statind[invInd]] * s.properties['a'],
            posunits)
        scaleFac[simoutBH, stepcnt] = s.properties['a']
        vel = np.array([
            amigastat['VXc'][statind[invInd]],
            amigastat['VYc'][statind[invInd]],
            amigastat['VZc'][statind[invInd]]
        ]).T
        bhvel[simoutBH,
              stepcnt, :] = s.stars['vel'][simBH].in_units(vunits) - vel
        postemp = s.stars['pos'][simBH].in_units(posunits) - amigastat['cen'][
            statind[invInd]]
        postemp[(np.abs(postemp) > boxsize / 2.)] = -1.0 * (
            postemp[(np.abs(postemp) > boxsize / 2.)] /
            np.abs(postemp[(np.abs(postemp) > boxsize / 2.)])) * (
                boxsize - np.abs(postemp[(np.abs(postemp) > boxsize / 2.)]))
        bhpos[simoutBH, stepcnt, :] = postemp

        bhmass[simoutBH, stepcnt] = s.stars['mass'][simBH].in_units(munits)
        dist[simoutBH, stepcnt] = np.sqrt((bhpos[simoutBH,
                                                 stepcnt, :]**2).sum(axis=1))
        for cnt in range(len(allHaloID)):
            if allHaloID[cnt] == 0: continue
            print allHaloID[cnt]
            oo, = np.where(amigastat['Grp'] == allHaloID[cnt])
            #cen = pynbody.array.SimArray([amigastat['Xc'][oo[0]],amigastat['Yc'][oo[0]],amigastat['Zc'][oo[0]]],posunits)
            if amigastat['Mvir(M_sol)'][
                (amigastat['Grp']
                 == allHaloID[cnt])] < minHM and allHaloID[cnt] > minNum:
                continue
            okcenter = 1
            try:
                pynbody.analysis.halo.center(h[allHaloID[cnt]],
                                             mode=findcenter,
                                             wrap=True,
                                             cen_size='2 kpc')
            except ValueError:
                okcenter = 0
                pynbody.analysis.halo.center(h[allHaloID[cnt]],
                                             mode=findcenter,
                                             Wrap=True,
                                             cen_size='2 kpc',
                                             vel=False)

            haloBHs, = np.where(
                np.in1d(h[allHaloID[cnt]].star['iord'], bhiords))
            outBH, = np.where(
                np.in1d(bhiords, h[allHaloID[cnt]].star['iord'][haloBHs]))
            #pynbody.transformation.inverse_translate(s,cen)
            closeBHs, = np.where((s.star['r'][simBH].in_units(
                'kpc') < amigastat['Rvir(kpc)'][oo] * s.properties['a']) &
                                 (s.star['amiga.grp'][simBH] > allHaloID[cnt]))
            closeBHs = simBH[closeBHs]
            otheroutBHs, = np.where(np.in1d(bhiords, s.star['iord'][closeBHs]))

            bhposcen[outBH, stepcnt, :] = h[
                allHaloID[cnt]].stars['pos'][haloBHs].in_units(posunits)
            distcen[outBH, stepcnt] = h[
                allHaloID[cnt]].stars['r'][haloBHs].in_units(posunits)
            interact[otheroutBHs, stepcnt] = allHaloID[cnt]
            intpos[otheroutBHs,
                   stepcnt, :] = s.stars['pos'][closeBHs].in_units(posunits)
            #intvel[otheroutBHs,stepcnt,:] = s.stars['vel'][closeBHs].in_units(vunits)
            intdist[otheroutBHs,
                    stepcnt] = s.stars['r'][closeBHs].in_units(posunits)
            if okcenter == 1:
                intvel[otheroutBHs,
                       stepcnt, :] = s.stars['vel'][closeBHs].in_units(vunits)
                bhvelcen[outBH, stepcnt, :] = h[
                    allHaloID[cnt]].stars['vel'][haloBHs].in_units(vunits)

        print "deleting stuff"
        del (s)
        del (h)
        gc.collect()

    bhhalo = {
        'iord': bhiords,
        'mass': bhmass,
        'pos': bhpos,
        'poscen': bhposcen,
        'vel': bhvel,
        'velcen': bhvelcen,
        'haloID': haloid,
        'halomass': mhalo,
        'halostarmass': mstar,
        'halodarkmass': mdark,
        'halogasmass': mgas,
        'rhalo': halorad,
        'dist': dist,
        'distcen': distcen,
        'interact': interact,
        'intdist': intdist,
        'intvel': intvel,
        'intpos': intpos,
        'scaleFac': scaleFac
    }
    if startStep != 0:
        bhhalo['mass'] = np.append(BHhaloOLD['mass'], bhhalo['mass'], axis=1)
        bhhalo['pos'] = np.append(BHhaloOLD['pos'], bhhalo['pos'], axis=1)
        bhhalo['poscen'] = np.append(BHhaloOLD['poscen'],
                                     bhhalo['poscen'],
                                     axis=1)
        bhhalo['vel'] = np.append(BHhaloOLD['vel'], bhhalo['vel'], axis=1)
        bhhalo['velcen'] = np.append(BHhaloOLD['velcen'],
                                     bhhalo['velcen'],
                                     axis=1)
        bhhalo['haloID'] = np.append(BHhaloOLD['haloID'],
                                     bhhalo['haloID'],
                                     axis=1)
        bhhalo['halomass'] = np.append(BHhaloOLD['halomass'],
                                       bhhalo['halomass'],
                                       axis=1)
        bhhalo['halostarmass'] = np.append(BHhaloOLD['halostarmass'],
                                           bhhalo['halostarmass'],
                                           axis=1)
        bhhalo['halodarkmass'] = np.append(BHhaloOLD['halodarkmass'],
                                           bhhalo['halodarkmass'],
                                           axis=1)
        bhhalo['halogasmass'] = np.append(BHhaloOLD['halogasmass'],
                                          bhhalo['halogasmass'],
                                          axis=1)
        bhhalo['rhalo'] = np.append(BHhaloOLD['rhalo'],
                                    bhhalo['rhalo'],
                                    axis=1)
        bhhalo['dist'] = np.append(BHhaloOLD['dist'], bhhalo['dist'], axis=1)
        bhhalo['distcen'] = np.append(BHhaloOLD['distcen'],
                                      bhhalo['distcen'],
                                      axis=1)
        bhhalo['interact'] = np.append(BHhaloOLD['interact'],
                                       bhhalo['interact'],
                                       axis=1)
        bhhalo['intdist'] = np.append(BHhaloOLD['intdist'],
                                      bhhalo['intdist'],
                                      axis=1)
        bhhalo['intpos'] = np.append(BHhaloOLD['intpos'],
                                     bhhalo['intpos'],
                                     axis=1)
        bhhalo['intvel'] = np.append(BHhaloOLD['intvel'],
                                     bhhalo['intvel'],
                                     axis=1)
        bhhalo['scaleFac'] = np.append(BHhaloOLD['scaleFac'],
                                       bhhalo['scaleFac'],
                                       axis=1)
    if filename:
        f = open(str(filename), 'wb')
        pickle.dump(bhhalo, f)
        f.close()
    return bhhalo
コード例 #13
0
def getBHorbit(simname, BHlist=[], filename=None):
    if not os.path.exists('files.list'):
        print "files.list not found.  generating list of output files..."
        getFileLists(simname)
    print "getting all BH id numbers..."
    bhids = getBHiords(simname)
    files = open("files.list", 'r')
    f1 = files.readlines()
    s = pynbody.load(f1[0].strip('\n'))
    munits = s['mass'].units
    posunits = s['x'].units
    velunits = s['vx'].units
    potunits = s['phi'].units
    tunits = posunits / velunits
    Eunits = munits * potunits
    scaleUnit = pynbody.units.Unit('a')
    files.close()
    print posunits / scaleUnit
    print velunits / scaleUnit

    orbitfile = simname + ".orbit"
    #print "reading "+orbitfile+"...."
    #bhorbitData = readcol.readcol(orbitfile)
    #bhids = np.unique(bhorbitData[:,0])
    if len(BHlist) > 0:
        matches = np.in1d(bhids, BHlist)
        bhorbit = {'iord': bhids[matches], 'data': np.array([])}
    else:
        bhorbit = {'iord': bhids, 'data': np.array([])}
    print "there are ", len(bhids), " BHs that have existed in this simulation"
    if len(BHlist) > 0: nBHs = len(BHlist)
    else: nBHs = len(bhids)

    print "getting data...."
    cnt = 0
    os.system("awk -F ' ' '{print >$1}' " + orbitfile)
    print bhorbit['iord']
    for id in bhids:
        print "getting data for BH ", id
        if len(BHlist) > 0:
            match, = np.where(BHlist == id)
            if len(match) == 0:
                os.system("rm " + str(np.int(id)))
                continue
        bhorbitData = readcol.readcol(str(np.int(id)))
        os.system("rm " + str(np.int(id)))
        bad, = np.where(bhorbitData[:, 0] != id)
        if len(bad) > 0:
            print "WARNING: bad ID found in miniorbit.txt file after awk... deleting"
            bhorbitData = np.delete(bhorbitData, bad, axis=0)
        cnt += 1
        GoodScale = True
        print "BH #" + str(cnt) + "/" + str(len(bhids))
        #		curbh, = np.where(bhorbitData[:,0]==id)
        time = array.SimArray(bhorbitData[:, 1], tunits)
        step = bhorbitData[:, 2]
        mass = bhorbitData[:, 3]
        x = bhorbitData[:, 4]
        y = bhorbitData[:, 5]
        z = bhorbitData[:, 6]
        vx = bhorbitData[:, 7]
        vy = bhorbitData[:, 8]
        vz = bhorbitData[:, 9]
        pot = bhorbitData[:, 10]
        mdot = bhorbitData[:, 11]
        deltaM = bhorbitData[:, 12]
        E = bhorbitData[:, 13]
        dtEff = bhorbitData[:, 14]
        if len(bhorbitData[0, :]) < 16:
            print "uh oh, trying to find scale factor data, but cannot!"
            scaleFac = np.ones(len(bhorbitData[:, 1]))
            redshift = np.ones(len(bhorbitData[:, 1]))
            GoodScale = False
        else:
            scaleFac = bhorbitData[:, 15]
            redshift = 1 / scaleFac - 1
        o = np.argsort(time)
        timeOrd = time[o]
        t1 = timeOrd[0:len(timeOrd) - 1]
        t2 = timeOrd[1:len(timeOrd)]
        bad = np.where(np.equal(t1, t2))
        np.delete(o, bad)
        time = array.SimArray(time[o], tunits)
        step = step[o]
        mass = array.SimArray(mass[o], munits)
        x = array.SimArray(x[o] * scaleFac[o], posunits / scaleUnit)
        y = array.SimArray(y[o] * scaleFac[o], posunits / scaleUnit)
        z = array.SimArray(z[o] * scaleFac[o], posunits / scaleUnit)
        vx = array.SimArray(vx[o] * scaleFac[o], velunits / scaleUnit)
        vy = array.SimArray(vy[o] * scaleFac[o], velunits / scaleUnit)
        vz = array.SimArray(vz[o] * scaleFac[o], velunits / scaleUnit)
        pot = array.SimArray(pot[o], potunits)
        mdot = array.SimArray(mdot[o], munits / tunits)
        deltaM = array.SimArray(deltaM[o], munits)
        E = array.SimArray(E[o], Eunits)
        dtEff = array.SimArray(dtEff[o], tunits)
        scaleFac = scaleFac[o]
        redshift = redshift[o]
        if GoodScale:
            data = {
                'Time': time,
                'step': step,
                'mass': mass.in_units('Msol'),
                'x': x.in_units('kpc'),
                'y': y.in_units('kpc'),
                'z': z.in_units('kpc'),
                'vx': vx.in_units('km s**-1'),
                'vy': vy.in_units('km s**-1'),
                'vz': vz.in_units('km s**-1'),
                'pot': pot,
                'mdot': mdot.in_units('Msol yr**-1'),
                'dM': deltaM.in_units('Msol'),
                'E': E,
                'dt': dtEff,
                'redshift': redshift,
                'scaleFac': scaleFac
            }
        else:
            data = {
                'Time': time,
                'step': step,
                'mass': mass.in_units('Msol'),
                'x': x,
                'y': y,
                'z': z,
                'vx': vx,
                'vy': vy,
                'vz': vz,
                'pot': pot,
                'mdot': mdot.in_units('Msol yr**-1'),
                'dM': deltaM.in_units('Msol'),
                'E': E,
                'dt': dtEff,
                'redshift': redshift,
                'scaleFac': scaleFac
            }
        bhorbit['data'] = np.append(bhorbit['data'], data)

    del (s)
    if filename:
        f = open(str(filename), 'wb')
        pickle.dump(bhorbit, f)
        f.close()
    return bhorbit
コード例 #14
0
def getBHMergers(simname,
                 orbitfile=None,
                 halofile=None,
                 outputname=None,
                 filename=None):
    if orbitfile:
        f = open(orbitfile, 'rb')
        BHorbit = pickle.load(f)
        f.close()
    if halofile:
        f2 = open(halofile, 'rb')
        BHhalo = pickle.load(f2)
        f2.close()
    if orbitfile or halofile:
        if not os.path.exists(orbitfile) or not os.path.exists(halofile):
            print "ERROR: cannot fine orbit and/or halo file"
            return
    if not os.path.exists('files.list'):
        print "files.list not found.  generating list of output files..."
        getFileLists(simname)
    files = open("files.list", 'r')
    f1 = files.readlines()
    s = pynbody.load(f1[0].strip('\n'))
    munits = s['mass'].units
    posunits = s['x'].units
    velunits = s['vx'].units
    potunits = s['phi'].units
    tunits = posunits / velunits
    Eunits = munits * potunits
    files.close()

    if not os.path.exists('BHmerge.txt'):
        if outputname == None:
            os.system(
                "awk '/BHSink/ && /Merge/ && /eating/' *out* > BHmerge.txt")
        else:
            os.system("awk '/BHSink/ && /Merge/ && /eating/' *" + outputname +
                      "* > BHmerge.txt")
    else:
        print "WARNING: BHmerge.txt already exists for this run... using that one. Please delete if you would like it to be updated."
    a, b, ID1, c, ID2, d, Time, e, f, kvel, g, h, Mratio = readcol.readcol(
        'BHmerge.txt', twod=False)
    del (a, b, c, d, e, f, g, h)
    ID1 = np.int(ID1)
    ID2 = np.int(ID2)
    Time = np.float(Time)
    kvel = np.float(kvel)
    Mratio = np.float(Mratio)
    id2tmp, count = np.unique(ID2, return_counts=True)
    bad, = np.where(count > 1)
    if len(bad) > 0:
        print "Warning! Found a double counted merger. Fixing..."
        idbad = id2tmp[bad]
        for i in idbad:
            baddat, = np.where(ID2 == idbad)
            np.delete(ID2, baddat[0:len(ID2) - 1])
            np.delete(ID1, baddat[0:len(ID2) - 1])
            np.delete(Time, baddat[0:len(ID2) - 1])
            np.delete(kvel, baddat[0:len(ID2) - 1])
            np.delete(Mratio, baddat[0:len(ID2) - 1])
    o = np.argsort(Time)
    Time = array.SimArray(Time[o], tunits)
    Time = Time.in_units('Gyr')
    ID1 = ID1[o]
    ID2 = ID2[o]
    kvel = kvel[o]
    Mratio = Mratio[o]
    nMergers = len(Time)
    print "found", nMergers, "BH-BH mergers occuring in simulation"
    M1 = array.SimArray(np.zeros(nMergers), 'Msol')
    M2 = array.SimArray(np.zeros(nMergers), 'Msol')
    HaloID1 = np.zeros(nMergers)
    HaloID2 = np.zeros(nMergers)
    HaloMass1 = array.SimArray(np.zeros(nMergers), 'Msol')
    HaloGas1 = array.SimArray(np.zeros(nMergers), 'Msol')
    HaloStars1 = array.SimArray(np.zeros(nMergers), 'Msol')
    HaloMass2 = array.SimArray(np.zeros(nMergers), 'Msol')
    HaloGas2 = array.SimArray(np.zeros(nMergers), 'Msol')
    HaloStars2 = array.SimArray(np.zeros(nMergers), 'Msol')

    if BHorbit or BHhalo:
        for i in range(nMergers):
            if BHorbit:
                no1, = np.where(BHorbit['iord'] == ID1[i])
                no2, = np.where(BHorbit['iord'] == ID2[i])
                to, = np.where(
                    BHorbit['data'][no1]['Time'].in_units('Gyr') < Time[i])
                if BHorbit['data'][no2]['Time'][-1].in_units('Gyr') > Time[1]:
                    print "WARNING larger time in orbit file for BH", BHhalo[
                        'iord'][no2], " Tmerge", Time[i], "Torbit", BHorbit[
                            'data'][n1]['Time'].max()
                M1[i] = BHorbit['data'][no1]['mass'][to[-1]].in_units('Msol')
                M2[i] = BHorbit['data'][no2]['mass'][-1].in_units('Msol')
            if BHhalo:
                nh1, = np.where(BHhalo['iord'] == ID1[i])
                nh2, = np.where(BHhalo['iord'] == ID2[i])
                nonz, = np.where(BHhalo['mass'][nh2] > 0)
                HaloID1[i] = BHhalo['haloID'][nh1][nonz[-1]]
                HaloID2[i] = BHhalo['haloID'][nh2][nonz[-1]]
                HaloMass1[i] = BHhalo['halomass'][nh1][nonz[-1]]
                HaloMass2[i] = BHhalo['halomass'][nh2][nonz[-1]]
                HaloGas1[i] = BHhalo['halogasmass'][nh1][nonz[-1]]
                HaloGas2i[i] = BHhalo['halogasmass'][nh2][nonz[-1]]
                HaloStars1[i] = BHhalo['halostarmass'][nh1][nonz[-1]]
                HaloStars2[i] = BHhalo['halostarmass'][nh2][nonz[-1]]
    BHmerge = {
        'Time': Time,
        'ID1': ID1,
        'ID2': ID2,
        'M1': M1,
        'M2': M2,
        'halo1': HaloID1,
        'halo2': HaloID2,
        'Hmass1': HaloMass1,
        'HGasMass1': HaloGas1,
        'HStarMass1': HaloStars1,
        'Hmass1': HaloMass2,
        'HGasMass1': HaloGas2,
        'HStarMass1': HaloStars2,
        'kickV': kvel,
        'ratio': Mratio
    }

    if filename:
        f = open(filename, 'wb')
        pickle.dump(BHmerge, f)
        f.close()
    return BHmerge
コード例 #15
0
def getAccretion(simname, BHlist=[], filename=False, allData=False):
    if not os.path.exists('files.list'):
        print "files.list not found.  generating list of output files..."
        getFileLists(simname)
    bhids = getBHiords(simname)
    files = open("files.list", 'r')
    f1 = files.readlines()
    s = pynbody.load(f1[0].strip('\n'))
    munits = s['mass'].units
    posunits = s['x'].units
    velunits = s['vx'].units
    potunits = s['phi'].units
    tunits = posunits / velunits
    Eunits = munits * potunits
    files.close()

    print "separating BH data..."
    acclogFile = simname + '.BHAccLog'
    os.system("awk -F ' ' '{print >$1}' " + acclogFile)
    #bhAccData = readcol.readcol(acclogFile)
    #bhids = np.unique(bhAccData[:,0])
    if len(BHlist) > 0:
        matches = np.in1d(bhids, BHlist)
        bhAccHist = {'iord': bhids[matches], 'data': np.array([])}
    else:
        bhAccHist = {'iord': bhids, 'data': np.array([])}
    print "there are ", len(bhids), " BHs that have existed in this simulation"
    if len(BHlist) > 0: nBHs = len(BHlist)
    else: nBHs = len(bhids)
    print "getting data...."
    cnt = 0
    for id in bhids:
        if len(BHlist) > 0:
            match, = np.where(BHlist == id)
            if len(match) == 0:
                os.system("rm " + str(np.int(id)))
                continue
        print "getting data for BH ", id
        bhAccData = readcol.readcol(str(np.int(id)))
        os.system("rm " + str(np.int(id)))
        bad, = np.where(bhAccData[:, 0] != id)
        if len(bad) > 0:
            print "WARNING: bad ID found in miniorbit.txt file after awk... deleting"
            bhAccData = np.delete(bhAccData, bad, axis=0)
        cnt += 1
        GoodScale = True
        print "BH #" + str(cnt) + "/" + str(nBHs)

        time = bhAccData[:, 2]
        o = np.argsort(time)
        timeOrd = time[o]
        t1 = timeOrd[0:len(timeOrd) - 1]
        t2 = timeOrd[1:len(timeOrd)]
        bad = np.where(np.equal(t1, t2))
        np.delete(o, bad)
        time = array.SimArray(time[o], tunits)

        iGasOrd = bhAccData[o, 1]
        MgasInit = array.SimArray(bhAccData[o, 3], munits)
        MbhInit = array.SimArray(bhAccData[o, 4], munits)
        MgasFinal = array.SimArray(bhAccData[o, 5], munits)
        MbhFinal = array.SimArray(bhAccData[o, 6], munits)
        dMgas = array.SimArray(bhAccData[o, 7], munits)
        dMBH = array.SimArray(bhAccData[o, 8], munits)
        dMneed = array.SimArray(bhAccData[o, 9], munits)
        scaleFac = bhAccData[o, 19]
        dx = array.SimArray(bhAccData[o, 10], posunits) * scaleFac
        dy = array.SimArray(bhAccData[o, 11], posunits) * scaleFac
        dz = array.SimArray(bhAccData[o, 12], posunits) * scaleFac
        dvx = array.SimArray(bhAccData[o, 13], velunits)
        dvy = array.SimArray(bhAccData[o, 14], velunits)
        dvz = array.SimArray(bhAccData[o, 15], velunits)
        Ugas = array.SimArray(bhAccData[o, 16], Eunits)
        fBall = array.SimArray(bhAccData[o, 17], posunits) * scaleFac
        tCoolOff = array.SimArray(bhAccData[o, 18], tunits)
        density = array.SimArray(bhAccData[o, 20],
                                 munits / posunits**3) * scaleFac**(-3)
        temp = array.SimArray(bhAccData[o, 21], 'K')
        metals = array.SimArray(bhAccData[o, 22])
        if allData:
            datastruct = {
                'time': time.in_units('Gyr'),
                'Mgas': MgasInit.in_units('Msol'),
                'Mbh': MbhInit.in_units('Msol'),
                'MgasFinal': MgasFinal.in_units('Msol'),
                'MbhFinal': MbhFinal.in_units('Msol'),
                'deltaMgas': dMgas.in_units('Msol'),
                'deltaM': dMBH.in_units('Msol'),
                'Mneed': dMneed.in_units('Msol'),
                'dx': dx.in_units('kpc'),
                'dy': dy.in_units('kpc'),
                'dz': dz.in_units('kpc'),
                'dvx': dvx.in_units('kpc'),
                'dvy': dvy.in_units('kpc'),
                'dvz': dvz.in_units('kpc'),
                'Ugas': Ugas,
                'fBall': fBall.in_units('kpc', a=1),
                'tCoolOff': tCoolOff,
                'scaleFac': scaleFac,
                'density': density.in_units('m_p cm**-3', a=1),
                'temp': temp,
                'metals': metals
            }
        else:
            datastruct = {
                'time': time.in_units('Gyr'),
                'Mgas': MgasInit.in_units('Msol'),
                'Mbh': MbhInit.in_units('Msol'),
                'deltaM': dMBH.in_units('Msol'),
                'dx': dx.in_units('kpc', a=1),
                'dy': dy.in_units('kpc', a=1),
                'dz': dz.in_units('kpc', a=1),
                'dvx': dvx.in_units('km s**-1', a=1),
                'dvy': dvy.in_units('km s**-1', a=1),
                'dvz': dvz.in_units('km s**-1', a=1),
                'Ugas': Ugas,
                'fBall': fBall.in_units('kpc', a=1),
                'tCoolOff': tCoolOff,
                'scaleFac': scaleFac,
                'density': density.in_units('m_p cm**-3', a=1),
                'temp': temp,
                'metals': metals
            }
        bhAccHist['data'] = np.append(bhAccHist['data'], datastruct)
    del (s)
    if filename:
        f = open(str(filename), 'wb')
        pickle.dump(bhAccHist, f)
        f.close()
    return bhAccHist
コード例 #16
0
def ang_mom_vec_units(snap):
    angmom = ang_mom_vec(snap)
    return array.SimArray(
        angmom, snap['mass'].units * snap['pos'].units * snap['vel'].units)
コード例 #17
0
def render(sim,
           filename=None,
           r_band='i',
           g_band='v',
           b_band='u',
           r_scale=0.5,
           g_scale=1.0,
           b_scale=1.0,
           dynamic_range=2.0,
           mag_range=None,
           width=50,
           starsize=None,
           plot=True,
           axes=None,
           ret_im=False,
           clear=True,
           ret_range=False):
    '''
    Make a 3-color image of stars.

    The colors are based on magnitudes found using stellar Marigo
    stellar population code.  However there is no radiative transfer
    to account for dust.

    Returns: If ret_im=True, an NxNx3 array representing an RGB image

    **Optional keyword arguments:**

       *filename*: string (default: None)
         Filename to be written to (if a filename is specified)

       *r_band*: string (default: 'i')
         Determines which Johnston filter will go into the image red channel

       *g_band*: string (default: 'v')
         Determines which Johnston filter will go into the image green channel

       *b_band*: string (default: 'b')
         Determines which Johnston filter will go into the image blue channel

       *r_scale*: float (default: 0.5)
         The scaling of the red channel before channels are combined

       *g_scale*: float (default: 1.0)
         The scaling of the green channel before channels are combined

       *b_scale*: float (default: 1.0)
         The scaling of the blue channel before channels are combined

       *width*: float in kpc (default:50)
         Sets the size of the image field in kpc

       *starsize*: float in kpc (default: None)
         If not None, sets the maximum size of stars in the image

       *ret_im*: bool (default: False)
         if True, the NxNx3 image array is returned

       *ret_range*: bool (default: False)
         if True, the range of the image in mag arcsec^-2 is returned.

       *plot*: bool (default: True)
         if True, the image is plotted

       *axes*: matplotlib axes object (deault: None)
         if not None, the axes object to plot to

       *dynamic_range*: float (default: 2.0)
         The number of dex in luminosity over which the image brightness ranges

       *mag_range*: float, float (default: None)
         If provided, the brightest and faintest surface brightnesses in the range,
         in mag arcsec^-2. Takes precedence over dynamic_range.
    '''

    if isinstance(width, str) or issubclass(width.__class__, _units.UnitBase):
        if isinstance(width, str):
            width = _units.Unit(width)
        width = width.in_units(sim['pos'].units, **sim.conversion_context())

    if starsize is not None:
        smf = filt.HighPass('smooth', str(starsize) + ' kpc')
        sim.s[smf]['smooth'] = array.SimArray(starsize, 'kpc', sim=sim)

    r = image(sim.s,
              qty=r_band + '_lum_den',
              width=width,
              log=False,
              units="pc^-2",
              clear=False,
              noplot=True) * r_scale
    g = image(sim.s,
              qty=g_band + '_lum_den',
              width=width,
              log=False,
              units="pc^-2",
              clear=False,
              noplot=True) * g_scale
    b = image(sim.s,
              qty=b_band + '_lum_den',
              width=width,
              log=False,
              units="pc^-2",
              clear=False,
              noplot=True) * b_scale

    # convert all channels to mag arcsec^-2

    r = pynbody.plot.stars.convert_to_mag_arcsec2(r)
    g = pynbody.plot.stars.convert_to_mag_arcsec2(g)
    b = pynbody.plot.stars.convert_to_mag_arcsec2(b)

    #r,g,b = nw_scale_rgb(r,g,b)
    #r,g,b = nw_arcsinh_fit(r,g,b)

    if mag_range is None:
        rgbim, mag_max = pynbody.plot.stars.combine(r, g, b,
                                                    dynamic_range * 2.5)
        mag_min = mag_max + 2.5 * dynamic_range
    else:
        mag_max, mag_min = mag_range
        rgbim, mag_max = pynbody.plot.stars.combine(r, g, b, mag_min - mag_max,
                                                    mag_max)

    if plot:
        if clear:
            plt.clf()
        if axes is None:
            axes = plt.gca()

        if axes:
            axes.imshow(rgbim[::-1, :],
                        extent=(-width / 2, width / 2, -width / 2, width / 2))
            axes.set_xlabel('x [' + str(sim.s['x'].units) + ']')
            axes.set_ylabel('y [' + str(sim.s['y'].units) + ']')
            plt.draw()

    if filename:
        plt.axis('off')
        plt.savefig(filename, dpi=1600, figsize=(25, 25), bbox_inches='tight')

    if ret_im:
        return rgbim

    if ret_range:
        return mag_max, mag_min