def _make_sigma(self, settings, sigma_input=None): """ Generates the surface density as a function of r, a callable object sigma(r) and assigns it to self.sigma The method used is determined by kind = settings.sigma.kind kind = 'file': Generates a spline interpolation of sigma vs r from the file defined by settings.filenames.sigmaFileName. Returns sigma vs r as an cubic spline interpolation object (see scipy.interpolation.interp1d) sigma_input should be a pickled dictionary with the entries: 'sigma': <sigma evaluated at r> 'r': <r for the bins> If the input sigma has units, sigma vs r will be returned in units of Msol/au^2 """ kind = settings.sigma.kind self.kind = kind if kind == "file": inDict = sigma_input.copy() # Save the input dictionary in self self.input_dict = inDict sigmaBinned = inDict["sigma"] r_bins = inDict["r"] # Convert to default units of Msol/au^2. If no units, assign default sigmaBinned = isaac.match_units(sigmaBinned, "Msol au**-2")[0] # Convert r_bins to default units of 'au' r_bins = isaac.match_units(r_bins, "au")[0] # Calculate spline interpolation print "Calculating spline interpolation (slow for many data points)" sigspline = interp1d(r_bins, sigmaBinned, kind="cubic", fill_value=0.0, bounds_error=False) def sigout(r): """ Linear spline interpolation of sigma(r). ARGUMENTS: r - can be scalar, numpy array, or sim array RETURNS: sigma (surface density) evaluated at r """ # Try to convert r to the units used to make sigspline ('au') r = isaac.match_units(r, "au")[0] return SimArray(sigspline(r), "Msol au**-2") self.sigma = sigout self.r_bins = r_bins
def Qest(ICobj, r=None): """ Estimate Toomre Q at r (optional) for ICs, assuming omega=epicyclic frequency. Ignores disk self-gravity """ if not hasattr(ICobj, 'sigma'): raise ValueError, 'Could not find surface density profile (sigma)' G = SimArray(1.0, 'G') kB = SimArray(1.0, 'k') if r is None: r = ICobj.sigma.r_bins sigma = ICobj.sigma(r) T = ICobj.T(r) M = ICobj.settings.physical.M m = ICobj.settings.physical.m M = isaac.match_units(M, 'Msol')[0] m = isaac.match_units(m, 'm_p')[0] Q = np.sqrt(M * kB * T / (G * m * r**3)) / (np.pi * sigma) Q.convert_units('1') return Q
def Qest(ICobj, r=None): """ Estimate Toomre Q at r (optional) for ICs, assuming omega=epicyclic frequency. Ignores disk self-gravity """ if not hasattr(ICobj, 'sigma'): raise ValueError, 'Could not find surface density profile (sigma)' G = SimArray(1.0, 'G') kB = SimArray(1.0, 'k') if r is None: r = ICobj.sigma.r_bins sigma = ICobj.sigma(r) T = ICobj.T(r) M = ICobj.settings.physical.M m = ICobj.settings.physical.m M = isaac.match_units(M, 'Msol')[0] m = isaac.match_units(m, 'm_p')[0] Q = np.sqrt(M*kB*T/(G*m*r**3))/(np.pi*sigma) Q.convert_units('1') return Q
def rho(self,z,r): """ A Callable method that works like a spline but handles units. returns rho(z,r), an N-D array evaluated over the N-D arrays z, r """ # Fix up units zunit = self.z_bins.units runit = self.r_bins.units rho_unit = self.rho_binned.units z = isaac.match_units(z, zunit)[0] r = isaac.match_units(r, runit)[0] if not hasattr(z, '__iter__'): rho_out = SimArray(self._rho_spline(z,r), rho_unit) else: rho_out = np.zeros(z.shape) iterator = np.nditer([z,r], flags=['multi_index']) while not iterator.finished: z_val, r_val = iterator.value ind = iterator.multi_index rho_out[ind] = self._rho_spline(z_val, r_val) iterator.iternext() rho_out = SimArray(rho_out, rho_unit) return rho_out
def __call__(self, r): # Load settings params = self._parent.settings.physical T0 = params.T0 Tpower = params.Tpower r0 = params.r0 Tmin = params.Tmin # Calculate T(r) r = isaac.match_units(r, r0)[0] a = (r/r0) a = isaac.match_units(a, '1')[0] Tout = T0 * np.power(a, Tpower) Tout[Tout < Tmin] = Tmin return Tout
def _make_sigma(self, r_bins, sigmaBinned): """ Generates the surface density as a function of r, a callable object sigma(r) and assigns it to self.sigma Generates a spline interpolation of sigma vs r from the file defined by settings.filenames.sigmaFileName. Returns sigma vs r as an cubic spline interpolation object (see scipy.interpolation.interp1d) sigma_input should be a pickled dictionary with the entries: 'sigma': <sigma evaluated at r> 'r': <r for the bins> If the input sigma has units, sigma vs r will be returned in units of Msol/au^2 """ # Convert to default units of Msol/au^2. If no units, assign default sigmaBinned = isaac.match_units(sigmaBinned, 'Msol au**-2')[0] # Convert r_bins to default units of 'au' r_bins = isaac.match_units(r_bins, 'au')[0] # Calculate spline interpolation print 'Calculating spline interpolation (slow for many data points)' sigspline = interp1d(r_bins,sigmaBinned,kind='cubic',fill_value=0.0,\ bounds_error=False) def sigout(r): """ Linear spline interpolation of sigma(r). ARGUMENTS: r - can be scalar, numpy array, or sim array RETURNS: sigma (surface density) evaluated at r """ # Try to convert r to the units used to make sigspline ('au') r = isaac.match_units(r, 'au')[0] return SimArray(sigspline(r), 'Msol au**-2') self.sigma = sigout self.r_bins = r_bins
def __call__(self, r): # Load settings params = self._parent.settings.physical if not hasattr(params, 'kind'): # Add this check for backwards compatibility. Previous versions # only had one kind of temperature profile params.kind = 'powerlaw' T0 = params.T0 Tmin = params.Tmin r0 = params.r0 kind = params.kind # Calculate T(r) r = isaac.match_units(r, r0)[0] a = (r / r0) a = isaac.match_units(a, '1')[0] # Powerlaw temperature (default) if kind == 'powerlaw': Tpower = params.Tpower Tout = T0 * np.power(a, Tpower) Tout[Tout < Tmin] = Tmin # MQWS temperature profile elif (kind == 'mqws') | (kind == 'MQWS'): # NOTE: I'm not sure how exactly they generate the temperature # profile. The quoted equation doesn't match their figures Tout = T0 * np.exp(-3 * a / 2) + Tmin else: raise TypeError, 'Could not find temperature kind {0}'.format(kind) if hasattr(params, 'Tmax'): Tmax = params.Tmax Tout[Tout > Tmax] = Tmax return Tout
def __call__(self, r): # Load settings params = self._parent.settings.physical if not hasattr(params, "kind"): # Add this check for backwards compatibility. Previous versions # only had one kind of temperature profile params.kind = "powerlaw" T0 = params.T0 Tmin = params.Tmin r0 = params.r0 kind = params.kind # Calculate T(r) r = isaac.match_units(r, r0)[0] a = r / r0 a = isaac.match_units(a, "1")[0] # Powerlaw temperature (default) if kind == "powerlaw": Tpower = params.Tpower Tout = T0 * np.power(a, Tpower) Tout[Tout < Tmin] = Tmin # MQWS temperature profile elif (kind == "mqws") | (kind == "MQWS"): # NOTE: I'm not sure how exactly they generate the temperature # profile. The quoted equation doesn't match their figures Tout = T0 * np.exp(-3 * a / 2) + Tmin else: raise TypeError, "Could not find temperature kind {0}".format(kind) if hasattr(params, "Tmax"): Tmax = params.Tmax Tout[Tout > Tmax] = Tmax return Tout
def cdf_inv(self, m, r): """ A callable interface for the inverse CDF. cdf_inv(m,r) returns z at a given r for 0 < m <1 IF m and r are the same length, the CDF_inv is calculated over the pairs m(i), r(i). IF one argument is a single point and the other is an array, the value of the single point is used for every evaluation. eg: r = SimArray(np.linspace(0, 20, 100), 'au') m = 0.5 cdf_vals = cdf_inv(m, r) # Returns z at cdf = 0.5 for all r """ # Make them iterable if they are floats/0D arrays if not hasattr(m, '__iter__'): m = np.array(m).reshape(1) if not hasattr(r, '__iter__'): r = np.array(r).reshape(1) # Check to see if one of the arrays is longer than the other. IF so, # assume that one is length one if np.prod(m.shape) > np.prod(r.shape): r = r * np.ones(m.shape) elif np.prod(m.shape) < np.prod(r.shape): m = m * np.ones(r.shape) # Check units runit = self.r_bins.units zunit = self.z_bins.units r = isaac.match_units(r, runit)[0] # Initialize n_pts = len(r) z_out = SimArray(np.zeros([len(r)]), zunit) dr = self.r_bins[[1]] - self.r_bins[[0]] r_indices = np.digitize(r, self.r_bins) # Ignore values outside of the r range mask = (r >= self.r_bins.min()) & (r < self.r_bins.max()) z_ind = np.arange(n_pts) # Now calculate the values of z_out for i, j in zip(z_ind[mask], r_indices[mask]): z_lo = self._cdf_inv[j - 1](m[i]) z_hi = self._cdf_inv[j](m[i]) z_out[i] = z_lo + ( (z_hi - z_lo) / dr) * (r[[i]] - self.r_bins[[j - 1]]) return z_out
def drho_dr(self, z, r): """ Radial derivative of rho. A callable method that works like a spline but handles units. USAGE: drho_dr(z,r) returns the radial derivative of rho at z, r """ # Set-up units zunit = self.z_bins.units runit = self.r_bins.units rho_unit = self.rho_binned.units drho_unit = rho_unit/runit # Put z, r in the correct units z = isaac.match_units(z, zunit)[0] r = isaac.match_units(r, runit)[0] # Iterate over drho if not hasattr(z, '__iter__'): drho = self._drho_dr(z,r) else: drho = np.zeros(z.shape) iterator = np.nditer([z,r], flags=['multi_index']) while not iterator.finished: z_val, r_val = iterator.value ind = iterator.multi_index drho[ind] = self._drho_dr(z_val, r_val) iterator.iternext() # Fix up units drho = isaac.match_units(drho, drho_unit)[0] return drho
def __call__(self, r): # Load settings params = self._parent.settings.physical T0 = params.T0 Tpower = params.Tpower r0 = params.r0 Tmin = params.Tmin if hasattr(params, 'Tmax'): Tmax = params.Tmax # Calculate T(r) r = isaac.match_units(r, r0)[0] a = (r/r0) a = isaac.match_units(a, '1')[0] Tout = T0 * np.power(a, Tpower) Tout[Tout < Tmin] = Tmin if hasattr(params, 'Tmax'): Tout[Tout > Tmax] = Tmax return Tout
def pdf_fcn(r_in): """ Normalized cubic spline interpolation of the PDF(r) from sigma(r). The PDF is just calculated as 2*pi*r*sigma(r). ARGUMENTS: r_in - radii at which to calculate the PDF RETURNS: probability density function from sigma(r) evaluated at r_in """ # Put r_in into the units used in generating the pdf r_in = isaac.match_units(r_in, self.r_bins)[0] # Evaluate the pdf at r_in pdf_vals = pdfSpline(r_in) # Put the pdf into units of r_in.units**-1 pdf_vals = isaac.match_units(pdf_vals, 1 / r_in)[0] return pdf_vals
def _cartesian_pos(self): """ Generate x,y """ r = self.r z = self.z theta = self.theta x = r*np.cos(theta) y = r*np.sin(theta) xyz = np.zeros([self.nParticles, 3]) xyz = isaac.match_units(xyz, r)[0] xyz[:,0] = x xyz[:,1] = y xyz[:,2] = isaac.match_units(z, r)[0] self.x = x self.y = y self.xyz = xyz
def cdf_inv(self,m,r): """ A callable interface for the inverse CDF. cdf_inv(m,r) returns z at a given r for 0 < m <1 IF m and r are the same length, the CDF_inv is calculated over the pairs m(i), r(i). IF one argument is a single point and the other is an array, the value of the single point is used for every evaluation. eg: r = SimArray(np.linspace(0, 20, 100), 'au') m = 0.5 cdf_vals = cdf_inv(m, r) # Returns z at cdf = 0.5 for all r """ # Make them iterable if they are floats/0D arrays if not hasattr(m, '__iter__'): m = np.array(m).reshape(1) if not hasattr(r, '__iter__'): r = np.array(r).reshape(1) # Check to see if one of the arrays is longer than the other. IF so, # assume that one is length one if np.prod(m.shape) > np.prod(r.shape): r = r*np.ones(m.shape) elif np.prod(m.shape) < np.prod(r.shape): m = m*np.ones(r.shape) # Check units runit = self.r_bins.units zunit = self.z_bins.units r = isaac.match_units(r, runit)[0] # Initialize n_pts = len(r) z_out = SimArray(np.zeros([len(r)]), zunit) dr = self.r_bins[[1]] - self.r_bins[[0]] r_indices = np.digitize(r, self.r_bins) # Ignore values outside of the r range mask = (r >= self.r_bins.min()) & (r < self.r_bins.max()) z_ind = np.arange(n_pts) # Now calculate the values of z_out for i,j in zip(z_ind[mask], r_indices[mask]): z_lo = self._cdf_inv[j-1](m[i]) z_hi = self._cdf_inv[j](m[i]) z_out[i] = z_lo + ((z_hi-z_lo)/dr)*(r[[i]] - self.r_bins[[j-1]]) return z_out
def _disk_mass(self): """ Calculate the total disk mass by integrating sigma """ # Assign variables r = self.r_bins sig = self.sigma(r) # Now integrate m_disk = simps(2 * np.pi * r * sig, r) m_units = sig.units * (r.units)**2 m_disk = isaac.match_units(m_disk, m_units)[0] self.m_disk = m_disk
def _disk_mass(self): """ Calculate the total disk mass by integrating sigma """ # Assign variables r = self.r_bins sig = self.sigma(r) # Now integrate m_disk = simps(2*np.pi*r*sig, r) m_units = sig.units * (r.units)**2 m_disk = isaac.match_units(m_disk, m_units)[0] self.m_disk = m_disk
def sigout(r): """ Linear spline interpolation of sigma(r). ARGUMENTS: r - can be scalar, numpy array, or sim array RETURNS: sigma (surface density) evaluated at r """ # Try to convert r to the units used to make sigspline ('au') r = isaac.match_units(r, 'au')[0] return SimArray(sigspline(r), 'Msol au**-2')
def sigout(r): """ Linear spline interpolation of sigma(r). ARGUMENTS: r - can be scalar, numpy array, or sim array RETURNS: sigma (surface density) evaluated at r """ # Try to convert r to the units used to make sigspline ('au') r = isaac.match_units(r, "au")[0] return SimArray(sigspline(r), "Msol au**-2")
def finv_fcn(m_in): """ The inverse CDF for sigma(r). input: 0 <= m_in < 1 returns: r (radius), the inverse CDF evaluated at m_in Uses a linear spline interpolation. """ r_out = finv(m_in) r_out = isaac.match_units(r_out, r)[0] return r_out
def snapshot_gen(ICobj): """ Generates a tipsy snapshot from the initial conditions object ICobj. Returns snapshot, param snapshot: tipsy snapshot param: dictionary containing info for a .param file """ print 'Generating snapshot...' # Constants G = SimArray(1.0,'G') # ------------------------------------ # Load in things from ICobj # ------------------------------------ print 'Accessing data from ICs' settings = ICobj.settings # filenames snapshotName = settings.filenames.snapshotName paramName = settings.filenames.paramName # particle positions r = ICobj.pos.r xyz = ICobj.pos.xyz # Number of particles nParticles = ICobj.pos.nParticles # molecular mass m = settings.physical.m # star mass m_star = settings.physical.M.copy() # disk mass m_disk = ICobj.sigma.m_disk.copy() m_disk = isaac.match_units(m_disk, m_star)[0] # mass of the gas particles m_particles = m_disk / float(nParticles) # re-scale the particles (allows making of lo-mass disk) m_particles *= settings.snapshot.mScale # ------------------------------------------------- # Assign output # ------------------------------------------------- print 'Assigning data to snapshot' # Get units all set up m_unit = m_star.units pos_unit = r.units if xyz.units != r.units: xyz.convert_units(pos_unit) # time units are sqrt(L^3/GM) t_unit = np.sqrt((pos_unit**3)*np.power((G*m_unit), -1)).units # velocity units are L/t v_unit = (pos_unit/t_unit).ratio('km s**-1') # Make it a unit v_unit = pynbody.units.Unit('{0} km s**-1'.format(v_unit)) # Other settings metals = settings.snapshot.metals star_metals = metals # ------------------------------------------------- # Initialize snapshot # ------------------------------------------------- # Note that empty pos, vel, and mass arrays are created in the snapshot snapshot = pynbody.new(star=1,gas=nParticles) snapshot['vel'].units = v_unit snapshot['eps'] = 0.01*SimArray(np.ones(nParticles+1, dtype=np.float32), pos_unit) snapshot['metals'] = SimArray(np.zeros(nParticles+1, dtype=np.float32)) snapshot['rho'] = SimArray(np.zeros(nParticles+1, dtype=np.float32)) snapshot.gas['pos'] = xyz snapshot.gas['temp'] = ICobj.T(r) snapshot.gas['mass'] = m_particles snapshot.gas['metals'] = metals snapshot.star['pos'] = SimArray([[ 0., 0., 0.]],pos_unit) snapshot.star['vel'] = SimArray([[ 0., 0., 0.]], v_unit) snapshot.star['mass'] = m_star snapshot.star['metals'] = SimArray(star_metals) # Estimate the star's softening length as the closest particle distance snapshot.star['eps'] = r.min() # Make param file param = isaac.make_param(snapshot, snapshotName) param['dMeanMolWeight'] = m gc.collect() # ------------------------------------------------- # CALCULATE VELOCITY USING calc_velocity.py. This also estimates the # gravitational softening length eps # ------------------------------------------------- print 'Calculating circular velocity' preset = settings.changa_run.preset max_particles = global_settings['misc']['max_particles'] calc_velocity.v_xy(snapshot, param, changa_preset=preset, max_particles=max_particles) gc.collect() # ------------------------------------------------- # Estimate time step for changa to use # ------------------------------------------------- # Save param file isaac.configsave(param, paramName, 'param') # Save snapshot snapshot.write(filename=snapshotName, fmt=pynbody.tipsy.TipsySnap) # est dDelta dDelta = ICgen_utils.est_time_step(paramName, preset) param['dDelta'] = dDelta # ------------------------------------------------- # Create director file # ------------------------------------------------- # largest radius to plot r_director = float(0.9 * r.max()) # Maximum surface density sigma_min = float(ICobj.sigma(r_director)) # surface density at largest radius sigma_max = float(ICobj.sigma.input_dict['sigma'].max()) # Create director dict director = isaac.make_director(sigma_min, sigma_max, r_director, filename=param['achOutName']) ## Save .director file #isaac.configsave(director, directorName, 'director') # ------------------------------------------------- # Wrap up # ------------------------------------------------- print 'Wrapping up' # Now set the star particle's tform to a negative number. This allows # UW ChaNGa treat it as a sink particle. snapshot.star['tform'] = -1.0 # Update params r_sink = isaac.strip_units(r.min()) param['dSinkBoundOrbitRadius'] = r_sink param['dSinkRadius'] = r_sink param['dSinkMassMin'] = 0.9 * isaac.strip_units(m_star) param['bDoSinks'] = 1 return snapshot, param, director
def v_xy(snapshot, param, changbin=None): """ Attempts to calculate the circular velocities for particles in a thin (not flat) keplerian disk. Requires ChaNGa ARGUMENTS: snapshot: a tipsy snapshot for a gaseous disk param: a dictionary containing params for changa. (see isaac.configparser) changbin: (OPTIONAL) If set, should be the full path to the ChaNGa executable. If None, an attempt to find ChaNGa is made """ if changbin is None: # Try to find the ChaNGa binary full path changbin = os.popen('which ChaNGa').read().strip() # -------------------------------------------- # Initialize # -------------------------------------------- # Load things from snapshot x = snapshot.g['x'] y = snapshot.g['y'] z = snapshot.g['z'] r = snapshot.g['rxy'] v_initial = snapshot.g['vel'].copy() # Temporary filenames for running ChaNGa f_prefix = str(np.random.randint(0, 2**32)) f_name = f_prefix + '.std' p_name = f_prefix + '.param' # Update parameters p_temp = param.copy() p_temp['achInFile'] = f_name p_temp['achOutName'] = f_prefix if 'dDumpFrameTime' in p_temp: p_temp.pop('dDumpFrameTime') if 'dDumpFrameStep' in p_temp: p_temp.pop('dDumpFrameStep') # -------------------------------------------- # Estimate velocity from gravity only # -------------------------------------------- # Save files snapshot.write(filename=f_name, fmt = pynbody.tipsy.TipsySnap) isaac.configsave(p_temp, p_name, ftype='param') # Run ChaNGa, only calculating gravity command = 'charmrun ++local ' + changbin + ' -gas -n 0 ' + p_name p = subprocess.Popen(command.split(), stdout=subprocess.PIPE) while p.poll() is None: time.sleep(0.1) # Load accelerations acc_name = f_prefix + '.000000.acc2' a = isaac.load_acc(acc_name) # Calculate radial acceleration a_r = a[:,0]*x/r + a[:,1]*y/r # circular velocity v = np.sqrt(abs(r*a_r)) # Assign snapshot.g['vel'][:,0] = -v*y/r snapshot.g['vel'][:,1] = v*x/r # -------------------------------------------- # Estimate velocity from gravity & sph (assuming bDoGas=1 in param) # -------------------------------------------- # Write file snapshot.write(filename=f_name, fmt = pynbody.tipsy.TipsySnap) # Run ChaNGa, only calculating gravity changbin = os.popen('which ChaNGa').read().strip() #command = 'charmrun ++local ' + changbin + ' +gas -n 0 ' + p_name command = 'charmrun ++local ' + changbin + ' -n 0 ' + p_name p = subprocess.Popen(command.split(), stdout=subprocess.PIPE) while p.poll() is None: time.sleep(0.1) # Load accelerations acc_name = f_prefix + '.000000.acc2' a = isaac.load_acc(acc_name) # Calculate radial acceleration a_r = a[:,0]*x/r + a[:,1]*y/r # circular velocity v = np.sqrt(abs(r*a_r)) logv = np.log(v) logr = np.log(r) logr_bins, logv_mean, err = isaac.binned_mean(logr, logv, 50, \ weighted_bins=True) logv_spline = isaac.extrap1d(logr_bins, logv_mean) v_calc = np.exp(logv_spline(logr)) v_calc = isaac.match_units(v_calc, v_initial.units)[0] v_out = v_initial * 0.0 v_out[:,0] = -v_calc * y/r v_out[:,1] = v_calc * x/r # Clean-up for fname in glob.glob(f_prefix + '*'): os.remove(fname) # Re-assign velocity to snapshot snapshot.g['vel'] = v_initial return v_out
def snapshot_gen(ICobj): """ Generates a tipsy snapshot from the initial conditions object ICobj. Returns snapshot, param snapshot: tipsy snapshot param: dictionary containing info for a .param file Note: Code has been edited (dflemin3) such that now it returns a snapshot for a circumbinary disk where initial conditions generated assuming star at origin of mass M. After gas initialized, replaced star at origin with binary system who's center of mass lies at the origin and who's mass m1 +m2 = M """ print 'Generating snapshot...' # Constants G = SimArray(1.0, 'G') # ------------------------------------ # Load in things from ICobj # ------------------------------------ print 'Accessing data from ICs' settings = ICobj.settings # snapshot file name snapshotName = settings.filenames.snapshotName paramName = settings.filenames.paramName # particle positions r = ICobj.pos.r xyz = ICobj.pos.xyz # Number of particles nParticles = ICobj.pos.nParticles # molecular mass m = settings.physical.m # star mass m_star = settings.physical.M.copy() # disk mass m_disk = ICobj.sigma.m_disk.copy() m_disk = isaac.match_units(m_disk, m_star)[0] # mass of the gas particles m_particles = m_disk / float(nParticles) # re-scale the particles (allows making of low-mass disk) m_particles *= settings.snapshot.mScale # ------------------------------------------------- # Assign output # ------------------------------------------------- print 'Assigning data to snapshot' # Get units all set up m_unit = m_star.units pos_unit = r.units if xyz.units != r.units: xyz.convert_units(pos_unit) # time units are sqrt(L^3/GM) t_unit = np.sqrt((pos_unit**3) * np.power((G * m_unit), -1)).units # velocity units are L/t v_unit = (pos_unit / t_unit).ratio('km s**-1') # Make it a unit, save value for future conversion v_unit_vel = v_unit #Ensure v_unit_vel is the same as what I assume it is. assert (np.fabs(AddBinary.VEL_UNIT - v_unit_vel) < AddBinary.SMALL), "VEL_UNIT not equal to ChaNGa unit! Why??" v_unit = pynbody.units.Unit('{0} km s**-1'.format(v_unit)) # Other settings metals = settings.snapshot.metals star_metals = metals # Generate snapshot # Note that empty pos, vel, and mass arrays are created in the snapshot snapshot = pynbody.new(star=1, gas=nParticles) snapshot['vel'].units = v_unit snapshot['eps'] = 0.01 * SimArray( np.ones(nParticles + 1, dtype=np.float32), pos_unit) snapshot['metals'] = SimArray(np.zeros(nParticles + 1, dtype=np.float32)) snapshot['rho'] = SimArray(np.zeros(nParticles + 1, dtype=np.float32)) snapshot.gas['pos'] = xyz snapshot.gas['temp'] = ICobj.T(r) snapshot.gas['mass'] = m_particles snapshot.gas['metals'] = metals snapshot.star['pos'] = SimArray([[0., 0., 0.]], pos_unit) snapshot.star['vel'] = SimArray([[0., 0., 0.]], v_unit) snapshot.star['mass'] = m_star snapshot.star['metals'] = SimArray(star_metals) # Estimate the star's softening length as the closest particle distance #snapshot.star['eps'] = r.min() # Make param file param = isaac.make_param(snapshot, snapshotName) param['dMeanMolWeight'] = m gc.collect() # CALCULATE VELOCITY USING calc_velocity.py. This also estimates the # gravitational softening length eps print 'Calculating circular velocity' preset = settings.changa_run.preset max_particles = global_settings['misc']['max_particles'] calc_velocity.v_xy(snapshot, param, changa_preset=preset, max_particles=max_particles) gc.collect() # ------------------------------------------------- # Estimate time step for changa to use # ------------------------------------------------- # Save param file isaac.configsave(param, paramName, 'param') # Save snapshot snapshot.write(filename=snapshotName, fmt=pynbody.tipsy.TipsySnap) # est dDelta dDelta = ICgen_utils.est_time_step(paramName, preset) param['dDelta'] = dDelta # ------------------------------------------------- # Create director file # ------------------------------------------------- # largest radius to plot r_director = float(0.9 * r.max()) # Maximum surface density sigma_min = float(ICobj.sigma(r_director)) # surface density at largest radius sigma_max = float(ICobj.sigma.input_dict['sigma'].max()) # Create director dict director = isaac.make_director(sigma_min, sigma_max, r_director, filename=param['achOutName']) ## Save .director file #isaac.configsave(director, directorName, 'director') #Now that velocities and everything are all initialized for gas particles, create new snapshot to return in which #single star particle is replaced by 2, same units as above snapshotBinary = pynbody.new(star=2, gas=nParticles) snapshotBinary['eps'] = 0.01 * SimArray( np.ones(nParticles + 2, dtype=np.float32), pos_unit) snapshotBinary['metals'] = SimArray( np.zeros(nParticles + 2, dtype=np.float32)) snapshotBinary['vel'].units = v_unit snapshotBinary['pos'].units = pos_unit snapshotBinary['mass'].units = snapshot['mass'].units snapshotBinary['rho'] = SimArray(np.zeros(nParticles + 2, dtype=np.float32)) #Assign gas particles with calculated/given values from above snapshotBinary.gas['pos'] = snapshot.gas['pos'] snapshotBinary.gas['vel'] = snapshot.gas['vel'] snapshotBinary.gas['temp'] = snapshot.gas['temp'] snapshotBinary.gas['rho'] = snapshot.gas['rho'] snapshotBinary.gas['eps'] = snapshot.gas['eps'] snapshotBinary.gas['mass'] = snapshot.gas['mass'] snapshotBinary.gas['metals'] = snapshot.gas['metals'] #Load Binary system obj to initialize system binsys = ICobj.settings.physical.binsys x1, x2, v1, v2 = binsys.generateICs() #Put velocity in sim units #!!! Note: v_unit_vel will always be 29.785598165 km/s when m_unit = Msol and r_unit = 1 AU in kpc!!! #conv = v_unit_vel #km/s in sim units #v1 /= conv #v2 /= conv #Assign position, velocity assuming CCW orbit snapshotBinary.star[0]['pos'] = SimArray(x1, pos_unit) snapshotBinary.star[0]['vel'] = SimArray(v1, v_unit) snapshotBinary.star[1]['pos'] = SimArray(x2, pos_unit) snapshotBinary.star[1]['vel'] = SimArray(v2, v_unit) #Set stellar masses #Set Mass units #Create simArray for mass, convert units to simulation mass units priMass = SimArray(binsys.m1, m_unit) secMass = SimArray(binsys.m2, m_unit) snapshotBinary.star[0]['mass'] = priMass snapshotBinary.star[1]['mass'] = secMass snapshotBinary.star['metals'] = SimArray(star_metals) #Estimate stars' softening length as fraction of distance to COM d = np.sqrt(AddBinary.dotProduct(x1 - x2, x1 - x2)) snapshotBinary.star[0]['eps'] = SimArray(math.fabs(d) / 4.0, pos_unit) snapshotBinary.star[1]['eps'] = SimArray(math.fabs(d) / 4.0, pos_unit) print 'Wrapping up' # Now set the star particle's tform to a negative number. This allows # UW ChaNGa treat it as a sink particle. snapshotBinary.star['tform'] = -1.0 #Set Sink Radius to be mass-weighted average of Roche lobes of two stars r1 = AddBinary.calcRocheLobe(binsys.m1 / binsys.m2, binsys.a) r2 = AddBinary.calcRocheLobe(binsys.m2 / binsys.m1, binsys.a) p = isaac.strip_units(binsys.m1 / (binsys.m1 + binsys.m2)) r_sink = (r1 * p) + (r2 * (1.0 - p)) param['dSinkBoundOrbitRadius'] = r_sink param['dSinkRadius'] = r_sink param['dSinkMassMin'] = 0.9 * isaac.strip_units(secMass) param['bDoSinks'] = 1 return snapshotBinary, param, director
def cdf_inv(self,m,r): """ A callable interface for the inverse CDF. cdf_inv(m,r) returns z at a given r for 0 < m <1 IF m and r are the same length, the CDF_inv is calculated over the pairs m(i), r(i). IF one argument is a single point and the other is an array, the value of the single point is used for every evaluation. eg: r = SimArray(np.linspace(0, 20, 100), 'au') m = 0.5 cdf_vals = cdf_inv(m, r) # Returns z at cdf = 0.5 for all r """ # Make them iterable if they are floats/0D arrays if not hasattr(m, '__iter__'): m = np.array(m).reshape(1) if not hasattr(r, '__iter__'): r = np.array(r).reshape(1) # Check to see if one of the arrays is longer than the other. IF so, # assume that one is length one if np.prod(m.shape) > np.prod(r.shape): r = r*np.ones(m.shape) elif np.prod(m.shape) < np.prod(r.shape): m = m*np.ones(r.shape) # Check units runit = self.r_bins.units zunit = self.z_bins.units r = isaac.match_units(r, runit)[0] # Initialize n_pts = len(r) z_out = SimArray(np.zeros([len(r)]), zunit) dr = self.r_bins[[1]] - self.r_bins[[0]] r_indices = np.digitize(r, self.r_bins) # Ignore values outside of the r range mask = (r >= self.r_bins.min()) & (r < self.r_bins.max()) z = z_out[mask] r = r[mask] r_indices = r_indices[mask] m = m[mask] # Loop through all used radial bins used_indices = set(r_indices) for i in used_indices: # Look at all particles in radial bin i mask2 = (r_indices == i) # Calculate z at the bin edges z_lo = self._cdf_inv[i-1](m[mask2]) z_hi = self._cdf_inv[i](m[mask2]) # Linearly interpolate z from bin edges z[mask2] = z_lo + ((z_hi-z_lo)/dr) * (r[mask2] - self.r_bins[[i-1]]) # Assign z for all particles within the bin range z_out[mask] = z return z_out
def snapshot_gen(ICobj): """ Generates a tipsy snapshot from the initial conditions object ICobj. Returns snapshot, param snapshot: tipsy snapshot param: dictionary containing info for a .param file """ # Constants G = SimArray(1.0, 'G') kB = SimArray(1.0, 'k') # ------------------------------------ # Load in things from ICobj # ------------------------------------ # snapshot file name snapshotName = ICobj.settings.filenames.snapshotName # particle positions theta = ICobj.pos.theta r = ICobj.pos.r x = ICobj.pos.x y = ICobj.pos.y z = ICobj.pos.z # Number of particles nParticles = ICobj.pos.nParticles # Temperature power law (used for pressure gradient) Tpower = ICobj.settings.physical.Tpower # molecular mass m = ICobj.settings.physical.m # star mass m_star = ICobj.settings.physical.M.copy() # disk mass m_disk = ICobj.sigma.m_disk.copy() m_disk = isaac.match_units(m_disk, m_star)[0] # mass of the gas particles m_particles = np.ones(nParticles) * m_disk / float(nParticles) # re-scale the particles (allows making of lo-mass disk) m_particles *= ICobj.settings.snapshot.mScale # ------------------------------------ # Initial calculations # ------------------------------------ # Find total mass interior to every particle N_interior = np.array(r.argsort().argsort()) m_int = m_particles[[0]] * N_interior + m_star # Retrieve rho (density) at each position rho = ICobj.rho(z, r) # Retrieve radial derivative at each position drho_dr = ICobj.rho.drho_dr(z, r) # Get temperature at each position T = ICobj.T(r) # ------------------------------------ # Calculate particle velocities # ------------------------------------ # Find keperlerian velocity squared due to gravity v2grav = G * m_int / r # Find contribution from density gradient v2dens = (kB * T / m) * (r * drho_dr / rho) # ignore nans and infs v2dens[(np.isnan(v2dens)) | (np.isinf(v2dens))] = 0.0 # Find contribution from temperature gradient v2temp = (kB * T / m) * Tpower # Now find velocity from all contributions v = np.sqrt(v2grav + v2dens + v2temp) # Sometimes, at large r, the velocities due to the pressure and temp # Gradients become negative. If this is the case, set them to 0 nanind = np.isnan(v) v[nanind] = 0.0 # ------------------------------------------------- # Assign output # ------------------------------------------------- # Get units all set up m_unit = m_star.units pos_unit = r.units # time units are sqrt(L^3/GM) t_unit = np.sqrt((pos_unit**3) * np.power((G * m_unit), -1)).units # velocity units are L/t v_unit = (pos_unit / t_unit).ratio('km s**-1') # Make it a unit v_unit = pynbody.units.Unit('{} km s**-1'.format(v_unit)) x.convert_units(pos_unit) y.convert_units(pos_unit) z.convert_units(pos_unit) # 3-D velocity vel = SimArray(np.zeros([nParticles, 3]), v_unit) vel[:, 0] = -np.sin(theta) * v vel[:, 1] = np.cos(theta) * v # Generate positions xyz = SimArray(np.zeros([nParticles, 3]), pos_unit) xyz[:, 0] = x xyz[:, 1] = y xyz[:, 2] = z # Other settings eps = ICobj.settings.snapshot.eps star_eps = eps eps *= SimArray(np.ones(nParticles), pos_unit) metals = ICobj.settings.snapshot.metals star_metals = metals metals *= SimArray(np.ones(nParticles)) # Generate snapshot snapshot = pynbody.new(star=1, gas=nParticles) snapshot.gas['vel'] = vel snapshot.gas['pos'] = xyz snapshot.gas['temp'] = T snapshot.gas['mass'] = m_particles snapshot.gas['metals'] = metals snapshot.gas['eps'] = eps snapshot.gas['mu'].derived = False snapshot.gas['mu'] = float(m.in_units('m_p')) snapshot.star['pos'] = SimArray([[0., 0., 0.]], pos_unit) snapshot.star['vel'] = SimArray([[0., 0., 0.]], v_unit) snapshot.star['mass'] = m_star snapshot.star['metals'] = SimArray(star_metals) snapshot.star['eps'] = SimArray(star_eps, pos_unit) param = isaac.make_param(snapshot, snapshotName) # CALCULATE VELOCITY USING calc_velocity.py vel = calc_velocity.v_xy(snapshot, param) snapshot.gas['vel'] = vel return snapshot, param
def powerlaw(settings, T = None): """ Generates a surface density profile according to a powerlaw sigma ~ 1/r with a smooth interior cutoff and smooth exterior exponential cutoff. **ARGUMENTS** settings : IC settings settings like those contained in an IC object (see ICgen_settings.py) T : callable function Function that returns temperature of the disk as a function of radius IF none, a powerlaw temperature is assumed **RETURNS** R : SimArray Radii at which sigma is calculated sigma : SimArray Surface density profile as a function of R """ # Parse settings Rd = settings.sigma.Rd rin = settings.sigma.rin rmax = settings.sigma.rmax cutlength = settings.sigma.cutlength Mstar = settings.physical.M Qmin = settings.sigma.Qmin n_points = settings.sigma.n_points m = settings.physical.m if T is None: # If no callable object to calculate Temperature(R) is provided, # default to a powerlaw T ~ R^-q T0 = SimArray([129.0],'K') # Temperature at 1 AU R0 = SimArray([1.0],'au') q = 0.59 def T(x): return T0 * np.power((x/R0).in_units('1'),-q) Rd = isaac.match_units(pynbody.units.au, Rd)[1] Mstar = isaac.match_units(pynbody.units.Msol, Mstar)[1] # Molecular weight m = isaac.match_units(m, pynbody.units.m_p)[0] # Maximum R to calculate sigma at (needed for the exponential cutoff region) Rmax = rmax*Rd # Q calculation parameters: G = SimArray([1.0],'G') kB = SimArray([1.0],'k') # Initialize stuff A = SimArray(1.0,'Msol')/(2*np.pi*np.power(Rd,2)) R = np.linspace(0,Rmax,n_points) r = np.array((R/Rd).in_units('1')) # Calculate sigma # Powerlaw sigma = A/r sigma[0] = 0.0 # Interior cutoff sigma[r>1] *= np.exp(-(r[r>1] - 1)**2 / (2*cutlength**2)) # Exterior cutoff sigma[r<rin] *= isaac.smoothstep(r[r<rin],degree=21,rescale=True) # Calculate Q Q = np.sqrt(Mstar*kB*T(R)/(G*m*R**3))/(np.pi*sigma) Q.convert_units('1') # Rescale sigma to meet the minimum Q requirement sigma *= Q.min()/Qmin # Calculate Q Q = np.sqrt(Mstar*kB*T(R)/(G*m*R**3))/(np.pi*sigma) Q.convert_units('1') return R, sigma
def MQWS(settings, T): """ Generates a surface density profile as the per method used in Mayer, Quinn, Wadsley, and Stadel 2004 ** ARGUMENTS ** NOTE: if units are not supplied, assumed units are AU, Msol settings : IC settings settings like those contained in an IC object (see ICgen_settings.py) T : callable A function to calculate temperature as a function of radius ** RETURNS ** r : SimArray Radii at which sigma is calculated sigma : SimArray Surface density profile as a function of R """ # Q calculation parameters: G = SimArray([1.0],'G') kB = SimArray([1.0],'k') # Load in settings n_points = settings.sigma.n_points rin = settings.sigma.rin rout = settings.sigma.rout rmax = settings.sigma.rmax Qmin = settings.sigma.Qmin m = settings.physical.m Mstar = settings.physical.M #m_disk = settings.sigma.m_disk rin = isaac.match_units(pynbody.units.au, rin)[1] rout = isaac.match_units(pynbody.units.au, rout)[1] #m_disk = isaac.match_units(pynbody.units.Msol, m_disk)[1] if rmax is None: rmax = 2.5 * rout else: rmax = isaac.match_units(pynbody.units.au, rmax)[1] r = np.linspace(0, rmax, n_points) #A = m_disk * np.exp(2 * (rin/rout).in_units('1'))/(rout * np.pi**1.5) a = (rin/r).in_units('1') b = (r/rout).in_units('1') #sigma = A * np.exp(-a**2 - b**2)/r sigma = (np.exp(-a**2 - b**2)/r) * Mstar.units/r.units # Calculate Q Q = np.sqrt(Mstar*kB*T(r)/(G*m*r**3))/(np.pi*sigma) Q.convert_units('1') sigma *= np.nanmin(Q)/Qmin # Remove all nans sigma[np.isnan(sigma)] = 0.0 return r, sigma
def v_xy(snapshot, param, changbin=None): """ Attempts to calculate the circular velocities for particles in a thin (not flat) keplerian disk. Requires ChaNGa ARGUMENTS: snapshot: a tipsy snapshot for a gaseous disk param: a dictionary containing params for changa. (see isaac.configparser) changbin: (OPTIONAL) If set, should be the full path to the ChaNGa executable. If None, an attempt to find ChaNGa is made """ if changbin is None: # Try to find the ChaNGa binary full path changbin = os.popen('which ChaNGa').read().strip() # -------------------------------------------- # Initialize # -------------------------------------------- # Load things from snapshot x = snapshot.g['x'] y = snapshot.g['y'] z = snapshot.g['z'] r = snapshot.g['rxy'] v_initial = snapshot.g['vel'].copy() # Temporary filenames for running ChaNGa f_prefix = str(np.random.randint(0, 2**32)) f_name = f_prefix + '.std' p_name = f_prefix + '.param' # Update parameters p_temp = param.copy() p_temp['achInFile'] = f_name p_temp['achOutName'] = f_prefix if 'dDumpFrameTime' in p_temp: p_temp.pop('dDumpFrameTime') if 'dDumpFrameStep' in p_temp: p_temp.pop('dDumpFrameStep') # -------------------------------------------- # Estimate velocity from gravity only # -------------------------------------------- # Save files snapshot.write(filename=f_name, fmt=pynbody.tipsy.TipsySnap) isaac.configsave(p_temp, p_name, ftype='param') # Run ChaNGa, only calculating gravity command = 'charmrun ++local ' + changbin + ' -gas -n 0 ' + p_name p = subprocess.Popen(command.split(), stdout=subprocess.PIPE) while p.poll() is None: time.sleep(0.1) # Load accelerations acc_name = f_prefix + '.000000.acc2' a = isaac.load_acc(acc_name) # Calculate radial acceleration a_r = a[:, 0] * x / r + a[:, 1] * y / r # circular velocity v = np.sqrt(abs(r * a_r)) # Assign snapshot.g['vel'][:, 0] = -v * y / r snapshot.g['vel'][:, 1] = v * x / r # -------------------------------------------- # Estimate velocity from gravity & sph (assuming bDoGas=1 in param) # -------------------------------------------- # Write file snapshot.write(filename=f_name, fmt=pynbody.tipsy.TipsySnap) # Run ChaNGa, only calculating gravity changbin = os.popen('which ChaNGa').read().strip() #command = 'charmrun ++local ' + changbin + ' +gas -n 0 ' + p_name command = 'charmrun ++local ' + changbin + ' -n 0 ' + p_name p = subprocess.Popen(command.split(), stdout=subprocess.PIPE) while p.poll() is None: time.sleep(0.1) # Load accelerations acc_name = f_prefix + '.000000.acc2' a = isaac.load_acc(acc_name) # Calculate radial acceleration a_r = a[:, 0] * x / r + a[:, 1] * y / r # circular velocity v = np.sqrt(abs(r * a_r)) logv = np.log(v) logr = np.log(r) logr_bins, logv_mean, err = isaac.binned_mean(logr, logv, 50, \ weighted_bins=True) logv_spline = isaac.extrap1d(logr_bins, logv_mean) v_calc = np.exp(logv_spline(logr)) v_calc = isaac.match_units(v_calc, v_initial.units)[0] v_out = v_initial * 0.0 v_out[:, 0] = -v_calc * y / r v_out[:, 1] = v_calc * x / r # Clean-up for fname in glob.glob(f_prefix + '*'): os.remove(fname) # Re-assign velocity to snapshot snapshot.g['vel'] = v_initial return v_out
def MQWS(settings, T): """ Generates a surface density profile as the per method used in Mayer, Quinn, Wadsley, and Stadel 2004 ** ARGUMENTS ** NOTE: if units are not supplied, assumed units are AU, Msol settings : IC settings settings like those contained in an IC object (see ICgen_settings.py) T : callable A function to calculate temperature as a function of radius ** RETURNS ** r : SimArray Radii at which sigma is calculated sigma : SimArray Surface density profile as a function of R """ # Q calculation parameters: G = SimArray([1.0], 'G') kB = SimArray([1.0], 'k') # Load in settings n_points = settings.sigma.n_points rin = settings.sigma.rin rout = settings.sigma.rout rmax = settings.sigma.rmax Qmin = settings.sigma.Qmin m = settings.physical.m Mstar = settings.physical.M #m_disk = settings.sigma.m_disk rin = isaac.match_units(pynbody.units.au, rin)[1] rout = isaac.match_units(pynbody.units.au, rout)[1] #m_disk = isaac.match_units(pynbody.units.Msol, m_disk)[1] if rmax is None: rmax = 2.5 * rout else: rmax = isaac.match_units(pynbody.units.au, rmax)[1] r = np.linspace(0, rmax, n_points) #A = m_disk * np.exp(2 * (rin/rout).in_units('1'))/(rout * np.pi**1.5) a = (rin / r).in_units('1') b = (r / rout).in_units('1') #sigma = A * np.exp(-a**2 - b**2)/r sigma = (np.exp(-a**2 - b**2) / r) * Mstar.units / r.units # Calculate Q Q = np.sqrt(Mstar * kB * T(r) / (G * m * r**3)) / (np.pi * sigma) Q.convert_units('1') sigma *= np.nanmin(Q) / Qmin # Remove all nans sigma[np.isnan(sigma)] = 0.0 return r, sigma
def snapshot_gen(ICobj): """ Generates a tipsy snapshot from the initial conditions object ICobj. Returns snapshot, param snapshot: tipsy snapshot param: dictionary containing info for a .param file Note: Code has been edited (dflemin3) such that now it returns a snapshot for a circumbinary disk where initial conditions generated assuming star at origin of mass M. After gas initialized, replaced star at origin with binary system who's center of mass lies at the origin and who's mass m1 +m2 = M """ print 'Generating snapshot...' # Constants G = SimArray(1.0, 'G') # ------------------------------------ # Load in things from ICobj # ------------------------------------ print 'Accessing data from ICs' settings = ICobj.settings # snapshot file name snapshotName = settings.filenames.snapshotName paramName = settings.filenames.paramName #Load user supplied snapshot (assumed to be in cwd) path = "/astro/store/scratch/tmp/dflemin3/nbodyshare/9au-Q1.05-129K/" snapshot = pynbody.load(path + snapshotName) # particle positions r = snapshot.gas['r'] xyz = snapshot.gas['pos'] # Number of particles nParticles = len(snapshot.gas) # molecular mass m = settings.physical.m #Pull star mass from user-supplied snapshot ICobj.settings.physical.M = snapshot.star[ 'mass'] #Total stellar mass in solar masses m_star = ICobj.settings.physical.M # disk mass m_disk = np.sum(snapshot.gas['mass']) m_disk = isaac.match_units(m_disk, m_star)[0] # mass of the gas particles m_particles = m_disk / float(nParticles) # re-scale the particles (allows making of low-mass disk) m_particles *= settings.snapshot.mScale # ------------------------------------------------- # Assign output # ------------------------------------------------- print 'Assigning data to snapshot' # Get units all set up m_unit = m_star.units pos_unit = r.units if xyz.units != r.units: xyz.convert_units(pos_unit) # time units are sqrt(L^3/GM) t_unit = np.sqrt((pos_unit**3) * np.power((G * m_unit), -1)).units # velocity units are L/t v_unit = (pos_unit / t_unit).ratio('km s**-1') # Make it a unit, save value for future conversion v_unit_vel = v_unit #Ensure v_unit_vel is the same as what I assume it is. assert (np.fabs(AddBinary.VEL_UNIT - v_unit_vel) < AddBinary.SMALL), "VEL_UNIT not equal to ChaNGa unit! Why??" v_unit = pynbody.units.Unit('{0} km s**-1'.format(v_unit)) # Other settings metals = settings.snapshot.metals star_metals = metals # Estimate the star's softening length as the closest particle distance eps = r.min() # Make param file param = isaac.make_param(snapshot, snapshotName) param['dMeanMolWeight'] = m gc.collect() # CALCULATE VELOCITY USING calc_velocity.py. This also estimates the # gravitational softening length eps preset = settings.changa_run.preset # ------------------------------------------------- # Estimate time step for changa to use # ------------------------------------------------- # Save param file isaac.configsave(param, paramName, 'param') # Save snapshot snapshot.write(filename=snapshotName, fmt=pynbody.tipsy.TipsySnap) # est dDelta dDelta = ICgen_utils.est_time_step(paramName, preset) param['dDelta'] = dDelta # ------------------------------------------------- # Create director file # ------------------------------------------------- # largest radius to plot r_director = float(0.9 * r.max()) # Maximum surface density sigma_min = float(ICobj.sigma(r_director)) # surface density at largest radius sigma_max = float(ICobj.sigma.input_dict['sigma'].max()) # Create director dict director = isaac.make_director(sigma_min, sigma_max, r_director, filename=param['achOutName']) ## Save .director file #isaac.configsave(director, directorName, 'director') """ Now that the gas disk is initializes around the primary (M=m1), add in the second star as specified by the user. """ #Now that velocities and everything are all initialized for gas particles, create new snapshot to return in which #single star particle is replaced by 2, same units as above snapshotBinary = pynbody.new(star=2, gas=nParticles) snapshotBinary['eps'] = 0.01 * SimArray( np.ones(nParticles + 2, dtype=np.float32), pos_unit) snapshotBinary['metals'] = SimArray( np.zeros(nParticles + 2, dtype=np.float32)) snapshotBinary['vel'].units = v_unit snapshotBinary['pos'].units = pos_unit snapshotBinary['mass'].units = snapshot['mass'].units snapshotBinary['rho'] = SimArray(np.zeros(nParticles + 2, dtype=np.float32)) #Assign gas particles with calculated/given values from above snapshotBinary.gas['pos'] = snapshot.gas['pos'] snapshotBinary.gas['vel'] = snapshot.gas['vel'] snapshotBinary.gas['temp'] = snapshot.gas['temp'] snapshotBinary.gas['rho'] = snapshot.gas['rho'] snapshotBinary.gas['eps'] = snapshot.gas['eps'] snapshotBinary.gas['mass'] = snapshot.gas['mass'] snapshotBinary.gas['metals'] = snapshot.gas['metals'] #Load Binary system obj to initialize system binsys = ICobj.settings.physical.binsys m_disk = isaac.strip_units(np.sum(snapshotBinary.gas['mass'])) binsys.m1 = isaac.strip_units(m_star) binsys.m1 = binsys.m1 + m_disk #Recompute cartesian coords considering primary as m1+m_disk binsys.computeCartesian() x1, x2, v1, v2 = binsys.generateICs() #Assign position, velocity assuming CCW orbit snapshotBinary.star[0]['pos'] = SimArray(x1, pos_unit) snapshotBinary.star[0]['vel'] = SimArray(v1, v_unit) snapshotBinary.star[1]['pos'] = SimArray(x2, pos_unit) snapshotBinary.star[1]['vel'] = SimArray(v2, v_unit) """ We have the binary positions about their center of mass, (0,0,0), so shift the position, velocity of the gas disk to be around the primary. """ snapshotBinary.gas['pos'] += snapshotBinary.star[0]['pos'] snapshotBinary.gas['vel'] += snapshotBinary.star[0]['vel'] #Set stellar masses: Create simArray for mass, convert units to simulation mass units snapshotBinary.star[0]['mass'] = SimArray(binsys.m1 - m_disk, m_unit) snapshotBinary.star[1]['mass'] = SimArray(binsys.m2, m_unit) snapshotBinary.star['metals'] = SimArray(star_metals) print 'Wrapping up' # Now set the star particle's tform to a negative number. This allows # UW ChaNGa treat it as a sink particle. snapshotBinary.star['tform'] = -1.0 #Set sink radius, stellar smoothing length as fraction of distance #from primary to inner edge of the disk r_sink = eps snapshotBinary.star[0]['eps'] = SimArray(r_sink / 2.0, pos_unit) snapshotBinary.star[1]['eps'] = SimArray(r_sink / 2.0, pos_unit) param['dSinkBoundOrbitRadius'] = r_sink param['dSinkRadius'] = r_sink param['dSinkMassMin'] = 0.9 * binsys.m2 param['bDoSinks'] = 1 return snapshotBinary, param, director
def snapshot_gen(ICobj): """ Generates a tipsy snapshot from the initial conditions object ICobj. Returns snapshot, param snapshot: tipsy snapshot param: dictionary containing info for a .param file """ print 'Generating snapshot...' # Constants G = SimArray(1.0, 'G') # ------------------------------------ # Load in things from ICobj # ------------------------------------ print 'Accessing data from ICs' settings = ICobj.settings # filenames snapshotName = settings.filenames.snapshotName paramName = settings.filenames.paramName # particle positions r = ICobj.pos.r xyz = ICobj.pos.xyz # Number of particles nParticles = ICobj.pos.nParticles # molecular mass m = settings.physical.m # star mass m_star = settings.physical.M.copy() # disk mass m_disk = ICobj.sigma.m_disk.copy() m_disk = isaac.match_units(m_disk, m_star)[0] # mass of the gas particles m_particles = m_disk / float(nParticles) # re-scale the particles (allows making of lo-mass disk) m_particles *= settings.snapshot.mScale # ------------------------------------------------- # Assign output # ------------------------------------------------- print 'Assigning data to snapshot' # Get units all set up m_unit = m_star.units pos_unit = r.units if xyz.units != r.units: xyz.convert_units(pos_unit) # time units are sqrt(L^3/GM) t_unit = np.sqrt((pos_unit**3) * np.power((G * m_unit), -1)).units # velocity units are L/t v_unit = (pos_unit / t_unit).ratio('km s**-1') # Make it a unit v_unit = pynbody.units.Unit('{0} km s**-1'.format(v_unit)) # Other settings metals = settings.snapshot.metals star_metals = metals # ------------------------------------------------- # Initialize snapshot # ------------------------------------------------- # Note that empty pos, vel, and mass arrays are created in the snapshot snapshot = pynbody.new(star=1, gas=nParticles) snapshot['vel'].units = v_unit snapshot['eps'] = 0.01 * SimArray( np.ones(nParticles + 1, dtype=np.float32), pos_unit) snapshot['metals'] = SimArray(np.zeros(nParticles + 1, dtype=np.float32)) snapshot['rho'] = SimArray(np.zeros(nParticles + 1, dtype=np.float32)) snapshot.gas['pos'] = xyz snapshot.gas['temp'] = ICobj.T(r) snapshot.gas['mass'] = m_particles snapshot.gas['metals'] = metals snapshot.star['pos'] = SimArray([[0., 0., 0.]], pos_unit) snapshot.star['vel'] = SimArray([[0., 0., 0.]], v_unit) snapshot.star['mass'] = m_star snapshot.star['metals'] = SimArray(star_metals) # Estimate the star's softening length as the closest particle distance snapshot.star['eps'] = r.min() # Make param file param = isaac.make_param(snapshot, snapshotName) param['dMeanMolWeight'] = m eos = (settings.physical.eos).lower() if eos == 'adiabatic': param['bGasAdiabatic'] = 1 param['bGasIsothermal'] = 0 param['dConstGamma'] gc.collect() # ------------------------------------------------- # CALCULATE VELOCITY USING calc_velocity.py. This also estimates the # gravitational softening length eps # ------------------------------------------------- print 'Calculating circular velocity' preset = settings.changa_run.preset max_particles = global_settings['misc']['max_particles'] calc_velocity.v_xy(snapshot, param, changa_preset=preset, max_particles=max_particles) gc.collect() # ------------------------------------------------- # Estimate time step for changa to use # ------------------------------------------------- # Save param file isaac.configsave(param, paramName, 'param') # Save snapshot snapshot.write(filename=snapshotName, fmt=pynbody.tipsy.TipsySnap) # est dDelta dDelta = ICgen_utils.est_time_step(paramName, preset) param['dDelta'] = dDelta # ------------------------------------------------- # Create director file # ------------------------------------------------- # largest radius to plot r_director = float(0.9 * r.max()) # Maximum surface density sigma_min = float(ICobj.sigma(r_director)) # surface density at largest radius sigma_max = float(ICobj.sigma.input_dict['sigma'].max()) # Create director dict director = isaac.make_director(sigma_min, sigma_max, r_director, filename=param['achOutName']) ## Save .director file #isaac.configsave(director, directorName, 'director') # ------------------------------------------------- # Wrap up # ------------------------------------------------- print 'Wrapping up' # Now set the star particle's tform to a negative number. This allows # UW ChaNGa treat it as a sink particle. snapshot.star['tform'] = -1.0 # Update params r_sink = isaac.strip_units(r.min()) param['dSinkBoundOrbitRadius'] = r_sink param['dSinkRadius'] = r_sink param['dSinkMassMin'] = 0.9 * isaac.strip_units(m_star) param['bDoSinks'] = 1 return snapshot, param, director
def v_xy(f, param, changbin=None, nr=50, min_per_bin=100): """ Attempts to calculate the circular velocities for particles in a thin (not flat) keplerian disk. Requires ChaNGa **ARGUMENTS** f : tipsy snapshot For a gaseous disk param : dict a dictionary containing params for changa. (see isaac.configparser) changbin : str (OPTIONAL) If set, should be the full path to the ChaNGa executable. If None, an attempt to find ChaNGa is made nr : int (optional) number of radial bins to use when averaging over accelerations min_per_bin : int (optional) The minimum number of particles to be in each bin. If there are too few particles in a bin, it is merged with an adjacent bin. Thus, actual number of radial bins may be less than nr. **RETURNS** vel : SimArray An N by 3 SimArray of gas particle velocities. """ if changbin is None: # Try to find the ChaNGa binary full path changbin = os.popen('which ChaNGa').read().strip() # Load stuff from the snapshot x = f.g['x'] y = f.g['y'] z = f.g['z'] r = f.g['rxy'] vel0 = f.g['vel'].copy() # Remove units from all quantities r = isaac.strip_units(r) x = isaac.strip_units(x) y = isaac.strip_units(y) z = isaac.strip_units(z) # Temporary filenames for running ChaNGa f_prefix = str(np.random.randint(0, 2**32)) f_name = f_prefix + '.std' p_name = f_prefix + '.param' # Update parameters p_temp = param.copy() p_temp['achInFile'] = f_name p_temp['achOutName'] = f_prefix if 'dDumpFrameTime' in p_temp: p_temp.pop('dDumpFrameTime') if 'dDumpFrameStep' in p_temp: p_temp.pop('dDumpFrameStep') # -------------------------------------------- # Estimate velocity from gravity only # -------------------------------------------- # Note, accelerations due to gravity are calculated twice to be extra careful # This is so that any velocity dependent effects are properly accounted for # (although, ideally, there should be none) # The second calculation uses the updated velocities from the first for iGrav in range(2): # Save files f.write(filename=f_name, fmt=pynbody.tipsy.TipsySnap) isaac.configsave(p_temp, p_name, ftype='param') # Run ChaNGa, only calculating gravity command = 'charmrun ++local ' + changbin + ' -gas -n 0 ' + p_name p = subprocess.Popen(command.split(), stdout=subprocess.PIPE) while p.poll() is None: time.sleep(0.1) # Load accelerations acc_name = f_prefix + '.000000.acc2' a = isaac.load_acc(acc_name) # Clean-up for fname in glob.glob(f_prefix + '*'): os.remove(fname) # If a is not a vector, calculate radial acceleration. Otherwise, assume # a is the radial acceleration a_r = a[:, 0] * x / r + a[:, 1] * y / r # Make sure the units are correct then remove them a_r = isaac.match_units(a_r, a)[0] a_r = isaac.strip_units(a_r) # Calculate cos(theta) where theta is angle above x-y plane cos = r / np.sqrt(r**2 + z**2) ar2 = a_r * r**2 # Bin the data r_edges = np.linspace(r.min(), (1 + np.spacing(2)) * r.max(), nr + 1) ind, r_edges = isaac.digitize_threshold(r, min_per_bin, r_edges) ind -= 1 nr = len(r_edges) - 1 r_bins, ar2_mean, err = isaac.binned_mean(r, ar2, binedges=r_edges, \ weighted_bins=True) # Fit lines to ar2 vs cos for each radial bin m = np.zeros(nr) b = np.zeros(nr) for i in range(nr): mask = (ind == i) p = np.polyfit(cos[mask], ar2[mask], 1) m[i] = p[0] b[i] = p[1] # Interpolate the line fits m_spline = isaac.extrap1d(r_bins, m) b_spline = isaac.extrap1d(r_bins, b) # Calculate circular velocity ar2_calc = m_spline(r) * cos + b_spline(r) v_calc = np.sqrt(abs(ar2_calc) / r) vel = f.g['vel'].copy() v_calc = isaac.match_units(v_calc, vel)[0] vel[:, 0] = -v_calc * y / r vel[:, 1] = v_calc * x / r # Assign to f f.g['vel'] = vel # -------------------------------------------- # Estimate pressure/gas dynamics accelerations # -------------------------------------------- a_grav = a ar2_calc_grav = ar2_calc # Save files f.write(filename=f_name, fmt=pynbody.tipsy.TipsySnap) isaac.configsave(p_temp, p_name, ftype='param') # Run ChaNGa, including SPH command = 'charmrun ++local ' + changbin + ' +gas -n 0 ' + p_name p = subprocess.Popen(command.split(), stdout=subprocess.PIPE) while p.poll() is None: time.sleep(0.1) # Load accelerations acc_name = f_prefix + '.000000.acc2' a_total = isaac.load_acc(acc_name) # Clean-up for fname in glob.glob(f_prefix + '*'): os.remove(fname) # Estimate the accelerations due to pressure gradients/gas dynamics a_gas = a_total - a_grav ar_gas = a_gas[:, 0] * x / r + a_gas[:, 1] * y / r ar_gas = isaac.strip_units(ar_gas) ar2_gas = ar_gas * r**2 logr_bins, ratio, err = isaac.binned_mean(np.log(r), ar2_gas/ar2_calc_grav, nbins=nr,\ weighted_bins=True) r_bins = np.exp(logr_bins) ratio_spline = isaac.extrap1d(r_bins, ratio) ar2_calc = ar2_calc_grav * (1 + ratio_spline(r)) a_calc = ar2_calc / r**2 v = np.sqrt(r * abs(a_calc)) v = isaac.match_units(v, vel0.units)[0] vel = vel0.copy() vel[:, 0] = -v * y / r vel[:, 1] = v * x / r # more cleanup f.g['vel'] = vel0 return vel
def snapshot_gen(ICobj): """ Generates a tipsy snapshot from the initial conditions object ICobj. Returns snapshot, param snapshot: tipsy snapshot param: dictionary containing info for a .param file """ # Constants G = SimArray(1.0, "G") kB = SimArray(1.0, "k") # ------------------------------------ # Load in things from ICobj # ------------------------------------ # snapshot file name snapshotName = ICobj.settings.filenames.snapshotName # particle positions theta = ICobj.pos.theta r = ICobj.pos.r x = ICobj.pos.x y = ICobj.pos.y z = ICobj.pos.z # Number of particles nParticles = ICobj.pos.nParticles # Temperature power law (used for pressure gradient) Tpower = ICobj.settings.physical.Tpower # molecular mass m = ICobj.settings.physical.m # star mass m_star = ICobj.settings.physical.M.copy() # disk mass m_disk = ICobj.sigma.m_disk.copy() m_disk = isaac.match_units(m_disk, m_star)[0] # mass of the gas particles m_particles = np.ones(nParticles) * m_disk / float(nParticles) # re-scale the particles (allows making of lo-mass disk) m_particles *= ICobj.settings.snapshot.mScale # ------------------------------------ # Initial calculations # ------------------------------------ # Find total mass interior to every particle N_interior = np.array(r.argsort().argsort()) m_int = m_particles[[0]] * N_interior + m_star # Retrieve rho (density) at each position rho = ICobj.rho(z, r) # Retrieve radial derivative at each position drho_dr = ICobj.rho.drho_dr(z, r) # Get temperature at each position T = ICobj.T(r) # ------------------------------------ # Calculate particle velocities # ------------------------------------ # Find keperlerian velocity squared due to gravity v2grav = G * m_int / r # Find contribution from density gradient v2dens = (kB * T / m) * (r * drho_dr / rho) # ignore nans and infs v2dens[(np.isnan(v2dens)) | (np.isinf(v2dens))] = 0.0 # Find contribution from temperature gradient v2temp = (kB * T / m) * Tpower # Now find velocity from all contributions v = np.sqrt(v2grav + v2dens + v2temp) # Sometimes, at large r, the velocities due to the pressure and temp # Gradients become negative. If this is the case, set them to 0 nanind = np.isnan(v) v[nanind] = 0.0 # ------------------------------------------------- # Assign output # ------------------------------------------------- # Get units all set up m_unit = m_star.units pos_unit = r.units # time units are sqrt(L^3/GM) t_unit = np.sqrt((pos_unit ** 3) * np.power((G * m_unit), -1)).units # velocity units are L/t v_unit = (pos_unit / t_unit).ratio("km s**-1") # Make it a unit v_unit = pynbody.units.Unit("{} km s**-1".format(v_unit)) x.convert_units(pos_unit) y.convert_units(pos_unit) z.convert_units(pos_unit) # 3-D velocity vel = SimArray(np.zeros([nParticles, 3]), v_unit) vel[:, 0] = -np.sin(theta) * v vel[:, 1] = np.cos(theta) * v # Generate positions xyz = SimArray(np.zeros([nParticles, 3]), pos_unit) xyz[:, 0] = x xyz[:, 1] = y xyz[:, 2] = z # Other settings eps = ICobj.settings.snapshot.eps star_eps = eps eps *= SimArray(np.ones(nParticles), pos_unit) metals = ICobj.settings.snapshot.metals star_metals = metals metals *= SimArray(np.ones(nParticles)) # Generate snapshot snapshot = pynbody.new(star=1, gas=nParticles) snapshot.gas["vel"] = vel snapshot.gas["pos"] = xyz snapshot.gas["temp"] = T snapshot.gas["mass"] = m_particles snapshot.gas["metals"] = metals snapshot.gas["eps"] = eps snapshot.gas["mu"].derived = False snapshot.gas["mu"] = float(m.in_units("m_p")) snapshot.star["pos"] = SimArray([[0.0, 0.0, 0.0]], pos_unit) snapshot.star["vel"] = SimArray([[0.0, 0.0, 0.0]], v_unit) snapshot.star["mass"] = m_star snapshot.star["metals"] = SimArray(star_metals) snapshot.star["eps"] = SimArray(star_eps, pos_unit) param = isaac.make_param(snapshot, snapshotName) return snapshot, param
def powerlaw(settings, T=None): """ Generates a surface density profile according to a powerlaw sigma ~ 1/r with a smooth interior cutoff and smooth exterior exponential cutoff. **ARGUMENTS** settings : IC settings settings like those contained in an IC object (see ICgen_settings.py) T : callable function Function that returns temperature of the disk as a function of radius IF none, a powerlaw temperature is assumed **RETURNS** R : SimArray Radii at which sigma is calculated sigma : SimArray Surface density profile as a function of R """ # Parse settings Rd = settings.sigma.Rd rin = settings.sigma.rin rmax = settings.sigma.rmax cutlength = settings.sigma.cutlength Mstar = settings.physical.M Qmin = settings.sigma.Qmin n_points = settings.sigma.n_points m = settings.physical.m power = settings.sigma.power if T is None: # If no callable object to calculate Temperature(R) is provided, # default to a powerlaw T ~ R^-q T0 = SimArray([129.0], 'K') # Temperature at 1 AU R0 = SimArray([1.0], 'au') q = 0.59 def T(x): return T0 * np.power((x / R0).in_units('1'), -q) Rd = isaac.match_units(pynbody.units.au, Rd)[1] Mstar = isaac.match_units(pynbody.units.Msol, Mstar)[1] # Molecular weight m = isaac.match_units(m, pynbody.units.m_p)[0] # Maximum R to calculate sigma at (needed for the exponential cutoff region) Rmax = rmax * Rd # Q calculation parameters: G = SimArray([1.0], 'G') kB = SimArray([1.0], 'k') # Initialize stuff A = SimArray(1.0, 'Msol') / (2 * np.pi * np.power(Rd, 2)) R = np.linspace(0, Rmax, n_points) r = np.array((R / Rd).in_units('1')) # Calculate sigma # Powerlaw #sigma = A/r #dflemin3 edit 06/10/2015: Try powerlaw of the form sigma ~ r^power sigma = A * np.power(r, power) sigma[0] = 0.0 # Exterior cutoff sigma[r > 1] *= np.exp(-(r[r > 1] - 1)**2 / (2 * cutlength**2)) # Interior cutoff sigma[r < rin] *= isaac.smoothstep(r[r < rin], degree=21, rescale=True) # Calculate Q Q = np.sqrt(Mstar * kB * T(R) / (G * m * R**3)) / (np.pi * sigma) Q.convert_units('1') # Rescale sigma to meet the minimum Q requirement sigma *= Q.min() / Qmin # Calculate Q Q = np.sqrt(Mstar * kB * T(R) / (G * m * R**3)) / (np.pi * sigma) Q.convert_units('1') return R, sigma
def v_xy(f, param, changbin=None, nr=50, min_per_bin=100): """ Attempts to calculate the circular velocities for particles in a thin (not flat) keplerian disk. Requires ChaNGa **ARGUMENTS** f : tipsy snapshot For a gaseous disk param : dict a dictionary containing params for changa. (see isaac.configparser) changbin : str (OPTIONAL) If set, should be the full path to the ChaNGa executable. If None, an attempt to find ChaNGa is made nr : int (optional) number of radial bins to use when averaging over accelerations min_per_bin : int (optional) The minimum number of particles to be in each bin. If there are too few particles in a bin, it is merged with an adjacent bin. Thus, actual number of radial bins may be less than nr. **RETURNS** vel : SimArray An N by 3 SimArray of gas particle velocities. """ if changbin is None: # Try to find the ChaNGa binary full path changbin = os.popen('which ChaNGa_uw_mpi').read().strip() # Load up mpi # Load stuff from the snapshot x = f.g['x'] y = f.g['y'] z = f.g['z'] r = f.g['rxy'] vel0 = f.g['vel'].copy() # Remove units from all quantities r = isaac.strip_units(r) x = isaac.strip_units(x) y = isaac.strip_units(y) z = isaac.strip_units(z) # Temporary filenames for running ChaNGa f_prefix = str(np.random.randint(0, 2**32)) f_name = f_prefix + '.std' p_name = f_prefix + '.param' # Update parameters p_temp = param.copy() p_temp['achInFile'] = f_name p_temp['achOutName'] = f_prefix if 'dDumpFrameTime' in p_temp: p_temp.pop('dDumpFrameTime') if 'dDumpFrameStep' in p_temp: p_temp.pop('dDumpFrameStep') # -------------------------------------------- # Estimate velocity from gravity only # -------------------------------------------- # Note, accelerations due to gravity are calculated twice to be extra careful # This is so that any velocity dependent effects are properly accounted for # (although, ideally, there should be none) # The second calculation uses the updated velocities from the first for iGrav in range(2): # Save files f.write(filename=f_name, fmt = pynbody.tipsy.TipsySnap) isaac.configsave(p_temp, p_name, ftype='param') # Run ChaNGa, only calculating gravity command = 'mpirun --mca mtl mx --mca pml cm ' + changbin + ' -gas -n 0 ' + p_name #command = 'charmrun ++local ' + changbin + ' -gas -n 0 ' + p_name p = subprocess.Popen(command.split(), stdout=subprocess.PIPE) while p.poll() is None: time.sleep(0.1) # Load accelerations acc_name = f_prefix + '.000000.acc2' a = isaac.load_acc(acc_name) # Clean-up for fname in glob.glob(f_prefix + '*'): os.remove(fname) # If a is not a vector, calculate radial acceleration. Otherwise, assume # a is the radial acceleration a_r = a[:,0]*x/r + a[:,1]*y/r # Make sure the units are correct then remove them a_r = isaac.match_units(a_r, a)[0] a_r = isaac.strip_units(a_r) # Calculate cos(theta) where theta is angle above x-y plane cos = r/np.sqrt(r**2 + z**2) ar2 = a_r*r**2 # Bin the data r_edges = np.linspace(r.min(), (1+np.spacing(2))*r.max(), nr + 1) ind, r_edges = isaac.digitize_threshold(r, min_per_bin, r_edges) ind -= 1 nr = len(r_edges) - 1 r_bins, ar2_mean, err = isaac.binned_mean(r, ar2, binedges=r_edges, \ weighted_bins=True) # Fit lines to ar2 vs cos for each radial bin m = np.zeros(nr) b = np.zeros(nr) for i in range(nr): mask = (ind == i) p = np.polyfit(cos[mask], ar2[mask], 1) m[i] = p[0] b[i] = p[1] # Interpolate the line fits m_spline = isaac.extrap1d(r_bins, m) b_spline = isaac.extrap1d(r_bins, b) # Calculate circular velocity ar2_calc = m_spline(r)*cos + b_spline(r) v_calc = np.sqrt(abs(ar2_calc)/r) vel = f.g['vel'].copy() v_calc = isaac.match_units(v_calc,vel)[0] vel[:,0] = -v_calc*y/r vel[:,1] = v_calc*x/r # Assign to f f.g['vel'] = vel # -------------------------------------------- # Estimate pressure/gas dynamics accelerations # -------------------------------------------- a_grav = a ar2_calc_grav = ar2_calc # Save files f.write(filename=f_name, fmt = pynbody.tipsy.TipsySnap) isaac.configsave(p_temp, p_name, ftype='param') # Run ChaNGa, including SPH command = 'mpirun --mca mtl mx --mca pml cm ' + changbin + ' +gas -n 0 ' + p_name p = subprocess.Popen(command.split(), stdout=subprocess.PIPE) while p.poll() is None: time.sleep(0.1) # Load accelerations acc_name = f_prefix + '.000000.acc2' a_total = isaac.load_acc(acc_name) # Clean-up for fname in glob.glob(f_prefix + '*'): os.remove(fname) # Estimate the accelerations due to pressure gradients/gas dynamics a_gas = a_total - a_grav ar_gas = a_gas[:,0]*x/r + a_gas[:,1]*y/r ar_gas = isaac.strip_units(ar_gas) ar2_gas = ar_gas*r**2 logr_bins, ratio, err = isaac.binned_mean(np.log(r), ar2_gas/ar2_calc_grav, nbins=nr,\ weighted_bins=True) r_bins = np.exp(logr_bins) ratio_spline = isaac.extrap1d(r_bins, ratio) ar2_calc = ar2_calc_grav*(1 + ratio_spline(r)) a_calc = ar2_calc/r**2 v = np.sqrt(r*abs(a_calc)) v = isaac.match_units(v, vel0.units)[0] vel = vel0.copy() vel[:,0] = -v*y/r vel[:,1] = v*x/r # more cleanup f.g['vel'] = vel0 return vel
def snapshot_gen(ICobj): """ Generates a tipsy snapshot from the initial conditions object ICobj. Returns snapshot, param snapshot: tipsy snapshot param: dictionary containing info for a .param file Note: Code has been edited (dflemin3) such that now it returns a snapshot for a circumbinary disk where initial conditions generated assuming star at origin of mass M. After gas initialized, replaced star at origin with binary system who's center of mass lies at the origin and who's mass m1 +m2 = M """ print 'Generating snapshot...' # Constants G = SimArray(1.0,'G') # ------------------------------------ # Load in things from ICobj # ------------------------------------ print 'Accessing data from ICs' settings = ICobj.settings # snapshot file name snapshotName = settings.filenames.snapshotName paramName = settings.filenames.paramName # particle positions r = ICobj.pos.r xyz = ICobj.pos.xyz # Number of particles nParticles = ICobj.pos.nParticles # molecular mass m = settings.physical.m # star mass m_star = settings.physical.M.copy() # disk mass m_disk = ICobj.sigma.m_disk.copy() m_disk = isaac.match_units(m_disk, m_star)[0] # mass of the gas particles m_particles = m_disk / float(nParticles) # re-scale the particles (allows making of low-mass disk) m_particles *= settings.snapshot.mScale # ------------------------------------------------- # Assign output # ------------------------------------------------- print 'Assigning data to snapshot' # Get units all set up m_unit = m_star.units pos_unit = r.units if xyz.units != r.units: xyz.convert_units(pos_unit) # time units are sqrt(L^3/GM) t_unit = np.sqrt((pos_unit**3)*np.power((G*m_unit), -1)).units # velocity units are L/t v_unit = (pos_unit/t_unit).ratio('km s**-1') # Make it a unit, save value for future conversion v_unit_vel = v_unit #Ensure v_unit_vel is the same as what I assume it is. assert(np.fabs(AddBinary.VEL_UNIT-v_unit_vel)<AddBinary.SMALL),"VEL_UNIT not equal to ChaNGa unit! Why??" v_unit = pynbody.units.Unit('{0} km s**-1'.format(v_unit)) # Other settings metals = settings.snapshot.metals star_metals = metals # Generate snapshot # Note that empty pos, vel, and mass arrays are created in the snapshot snapshot = pynbody.new(star=1,gas=nParticles) snapshot['vel'].units = v_unit snapshot['eps'] = 0.01*SimArray(np.ones(nParticles+1, dtype=np.float32), pos_unit) snapshot['metals'] = SimArray(np.zeros(nParticles+1, dtype=np.float32)) snapshot['rho'] = SimArray(np.zeros(nParticles+1, dtype=np.float32)) snapshot.gas['pos'] = xyz snapshot.gas['temp'] = ICobj.T(r) snapshot.gas['mass'] = m_particles snapshot.gas['metals'] = metals snapshot.star['pos'] = SimArray([[ 0., 0., 0.]],pos_unit) snapshot.star['vel'] = SimArray([[ 0., 0., 0.]], v_unit) snapshot.star['mass'] = m_star snapshot.star['metals'] = SimArray(star_metals) # Estimate the star's softening length as the closest particle distance eps = r.min() # Make param file param = isaac.make_param(snapshot, snapshotName) param['dMeanMolWeight'] = m gc.collect() # CALCULATE VELOCITY USING calc_velocity.py. This also estimates the # gravitational softening length eps print 'Calculating circular velocity' preset = settings.changa_run.preset max_particles = global_settings['misc']['max_particles'] calc_velocity.v_xy(snapshot, param, changa_preset=preset, max_particles=max_particles) gc.collect() # ------------------------------------------------- # Estimate time step for changa to use # ------------------------------------------------- # Save param file isaac.configsave(param, paramName, 'param') # Save snapshot snapshot.write(filename=snapshotName, fmt=pynbody.tipsy.TipsySnap) # est dDelta dDelta = ICgen_utils.est_time_step(paramName, preset) param['dDelta'] = dDelta # ------------------------------------------------- # Create director file # ------------------------------------------------- # largest radius to plot r_director = float(0.9 * r.max()) # Maximum surface density sigma_min = float(ICobj.sigma(r_director)) # surface density at largest radius sigma_max = float(ICobj.sigma.input_dict['sigma'].max()) # Create director dict director = isaac.make_director(sigma_min, sigma_max, r_director, filename=param['achOutName']) ## Save .director file #isaac.configsave(director, directorName, 'director') """ Now that the gas disk is initializes around the primary (M=m1), add in the second star as specified by the user. """ #Now that velocities and everything are all initialized for gas particles, create new snapshot to return in which #single star particle is replaced by 2, same units as above snapshotBinary = pynbody.new(star=2,gas=nParticles) snapshotBinary['eps'] = 0.01*SimArray(np.ones(nParticles+2, dtype=np.float32), pos_unit) snapshotBinary['metals'] = SimArray(np.zeros(nParticles+2, dtype=np.float32)) snapshotBinary['vel'].units = v_unit snapshotBinary['pos'].units = pos_unit snapshotBinary['mass'].units = snapshot['mass'].units snapshotBinary['rho'] = SimArray(np.zeros(nParticles+2, dtype=np.float32)) #Assign gas particles with calculated/given values from above snapshotBinary.gas['pos'] = snapshot.gas['pos'] snapshotBinary.gas['vel'] = snapshot.gas['vel'] snapshotBinary.gas['temp'] = snapshot.gas['temp'] snapshotBinary.gas['rho'] = snapshot.gas['rho'] snapshotBinary.gas['eps'] = snapshot.gas['eps'] snapshotBinary.gas['mass'] = snapshot.gas['mass'] snapshotBinary.gas['metals'] = snapshot.gas['metals'] #Load Binary system obj to initialize system binsys = ICobj.settings.physical.binsys m_disk = isaac.strip_units(np.sum(snapshotBinary.gas['mass'])) binsys.m1 = binsys.m1 + m_disk #Recompute cartesian coords considering primary as m1+m_disk binsys.computeCartesian() x1,x2,v1,v2 = binsys.generateICs() #Assign position, velocity assuming CCW orbit snapshotBinary.star[0]['pos'] = SimArray(x1,pos_unit) snapshotBinary.star[0]['vel'] = SimArray(v1,v_unit) snapshotBinary.star[1]['pos'] = SimArray(x2,pos_unit) snapshotBinary.star[1]['vel'] = SimArray(v2,v_unit) """ We have the binary positions about their center of mass, (0,0,0), so shift the position, velocity of the gas disk to be around the primary. """ snapshotBinary.gas['pos'] += snapshotBinary.star[0]['pos'] snapshotBinary.gas['vel'] += snapshotBinary.star[0]['vel'] #Set stellar masses: Create simArray for mass, convert units to simulation mass units snapshotBinary.star[0]['mass'] = SimArray(binsys.m1-m_disk,m_unit) snapshotBinary.star[1]['mass'] = SimArray(binsys.m2,m_unit) snapshotBinary.star['metals'] = SimArray(star_metals) #Now that everything has masses and positions, adjust positions so the #system center of mass corresponds to the origin """ com = binaryUtils.computeCOM(snapshotBinary.stars,snapshotBinary.gas) print com snapshotBinary.stars['pos'] -= com snapshotBinary.gas['pos'] -= com """ print 'Wrapping up' # Now set the star particle's tform to a negative number. This allows # UW ChaNGa treat it as a sink particle. snapshotBinary.star['tform'] = -1.0 #Set sink radius, stellar smoothing length as fraction of distance #from primary to inner edge of the disk r_sink = eps snapshotBinary.star[0]['eps'] = SimArray(r_sink/2.0,pos_unit) snapshotBinary.star[1]['eps'] = SimArray(r_sink/2.0,pos_unit) param['dSinkBoundOrbitRadius'] = r_sink param['dSinkRadius'] = r_sink param['dSinkMassMin'] = 0.9 * binsys.m2 param['bDoSinks'] = 1 return snapshotBinary, param, director