def markgal(group, max_temp, min_rho) : """Mark all the gas particles in group that have temperatures less than or equal to max_temp and densities greater than or equal to min_rho. This command can be used to mark those gas particles that are likely to be in galaxies.""" # check if simulation loaded if charm.getGroups() == None : raise StandardError('Simulation not loaded') # get opposing boundaries from simulation values max_rho = charm.getAttributeRange('gas', 'density')[1] min_temp = charm.getAttributeRange('gas', 'temperature')[0] # play the createGroup shuffle charm.createGroup_Family('tmp_group_fam', group, 'gas') charm.createGroup_AttributeRange('tmp_group1', 'tmp_group_fam', 'density', min_rho, max_rho) charm.createGroup_AttributeRange('tmp_group2', 'tmp_group1', 'temperature', min_temp, max_temp) charm.markParticlesGroup('tmp_group2') # remove the temporary groups to avoid clutter charm.deleteGroup('tmp_group_fam') charm.deleteGroup('tmp_group1') charm.deleteGroup('tmp_group2')
def writemark(group, filename) : """Write a file which contains the index values for all particles in a group. This file is typically used to export and import particle markings. The file has a header row with ntotal ngas nstar.""" import charm # check if simulation loaded groups = charm.getGroups() if groups == None : raise StandardError('Simulation not loaded') # check if group exists if group not in groups : raise StandardError('Group ' + group + ' does not exist') ngas = charm.getNumParticles(group, 'gas') nstar = charm.getNumParticles(group, 'star') ntotal = ngas + nstar + charm.getNumParticles(group, 'dark') # record header row to file f = open(filename, 'w') f.write('%d %d %d\n' % (ntotal, ngas, nstar)) f.close() # record index values charm.writeIndexes(group, 'gas', filename) charm.writeIndexes(group, 'dark', filename) charm.writeIndexes(group, 'star', filename)
def markbox(group) : """Mark the contents of group. Marked particles are indicated by a "mark" attribute being set to non-zero.""" # check if simulation loaded if charm.getGroups() == None : raise StandardError('Simulation not loaded') charm.markParticlesGroup(group)
def setsphere(group, *args) : """Select particles within radius from coordinate.""" import charm try : # parse args if len(args) < 3 : raise TypeError('Too few arguments') if type(args[0]) == str : if args[0] == 'pot' or args[0] == 'potential ': center = charm.findAttributeMin(args[1], 'potential') radius = args[2] if(len(args) == 4) : parent = args[3] else : parent = 'All' elif args[0] == 'com' : if type(args[1]) != str : raise StandardError('third argument should be a group') if args[2] == 'all' or args[2] == 'All' : center = charm.getCenterOfMass(args[1]) radius = args[3] if(len(args) == 5) : parent = args[4] else : parent = 'All' elif args[2] == 'gas' : charm.createGroup_Family("__tmpgroup", args[1], 'gas') center = charm.getCenterOfMass(args[1]) radius = args[3] if(len(args) == 5) : parent = args[4] else : parent = 'All' elif args[2] == 'dark' : charm.createGroup_Family("__tmpgroup", args[1], 'dark') center = charm.getCenterOfMass(args[1]) radius = args[3] if(len(args) == 5) : parent = args[4] else : parent = 'All' elif args[2] == 'star' : charm.createGroup_Family("__tmpgroup", args[1], 'star') center = charm.getCenterOfMass(args[1]) radius = args[3] if(len(args) == 5) : parent = args[4] else : parent = 'All' else : raise StandardError('unknown particle type: ' + args[2]) else : raise StandardError('unknown center type: ' + args[0]) else : center = (args[0], args[1], args[2]) radius = args[3] if(len(args) == 5) : parent = args[4] else : parent = 'All' # check if simulation loaded if charm.getGroups() == None : raise StandardError('Simulation not loaded') charm.createGroupAttributeSphere(group, parent, 'position', center[0], center[1], center[2], radius) except : print traceback.format_exc()
def getEpsilonDist(group='gal', nBins=50, epsCut=2., center='pot'): """Returns a histogram of dimension 2 x nbins of stellar epsilons (jz/jcirc) and mass-fraction. epsCut defines the histogram bound in both directions. Center can be either 'pot' or 'com'""" numRotBins = 400 #number of points at which we linearly interpolate radiusVCM = 0.25 #fraction of group radius to use when calculating COM velocity radiusAngMom = 0.25 #fraction of group radius to use when calculating stellar angular momentum a = charm.getTime() if ((group in charm.getGroups()==False)) & (group !='gal'): print "Group does not exist, please try again." return if (group == 'gal'): rgal = quesoConfig.rgalFraction*virialgroup.getVirialGroup() center=findcenter.findCenter(group2='virialGroup', method=center) charm.createGroupAttributeSphere('gal', 'All', 'position', center[0], center[1], center[2], rgal) else: center=findcenter.findCenter(group2=group, method=center) # Find maximum radius charm.createGroup_Family('tmp',group,'star') maxRad = getgroupradius.getGroupRadius('tmp', center) massDist = [0]*(numRotBins+1) rad = [0]*(numRotBins+1) radialStep = maxRad/numRotBins #populate mass bins for i in range(numRotBins+1): charm.createGroupAttributeSphere('tmp', 'All', 'position', center[0], center[1], center[2], radialStep*i) massDist[i] = charm.getAttributeSum('tmp','star','mass') massDist[i] += charm.getAttributeSum('tmp','dark','mass') massDist[i] += charm.getAttributeSum('tmp','gas' ,'mass') #Note: I tried shells instead of spheres and it made no real difference. rad[i] = radialStep*i vCM = getVelCMStars(center2=center, radius=radiusVCM*maxRad) #COM velocity angMomVec = getNormalStarAngMom(center2=center, radius=radiusAngMom*maxRad, comVel=vCM) #Ang Mom Velocity #vCM = getVelCMStars(center2=center, radius=10/(a*quesoConfig.kpcunit)) #COM velocity #angMomVec = getNormalStarAngMom(center2=center, radius=6/(a*quesoConfig.kpcunit), comVel=vCM) #Ang Mom Velocity param = (center, vCM, angMomVec, maxRad, numRotBins,massDist, nBins,epsCut) charm.createGroupAttributeSphere('tmp', 'All', 'position', center[0], center[1], center[2], maxRad) charm.createGroup_Family('tmp',group,'star') #calc total stellar mass for mass weighted average reduceResult = charm.reduceParticle('tmp', mapEpsilon, reduceEpsilon, param) sMass = charm.getAttributeSum('tmp','star', 'mass') epsilonDist = [0]*nBins eps = [0]*nBins for i in range(nBins): eps[i] = -epsCut+ 2*epsCut/nBins*(i) for i in range(len(reduceResult)): try: bin = reduceResult[i][0] epsilonDist[bin] = reduceResult[i][1]/sMass #return the mass fraction except:{} return (eps, epsilonDist)
def metalProfile(group, nbins=20, center="pot", family="bar"): if (group in charm.getGroups()) == False: print "Group does not exist, please try again." return if (family in ["star", "bar", "gas"]) == False: print 'Invalid family. Need "bar","gas", or "star"' return center = findcenter.findCenter(group2=group, method=center) radialStep = getgroupradius.getGroupRadius(group, center) / nbins metalRad = [0] * (nbins) metalProf = [0] * (nbins) numStar = charm.getNumParticles(group, "star") numGas = charm.getNumParticles(group, "gas") if (numGas == 0) & (numStar == 0): return metalProf if (numStar != 0) & (family == "star"): charm.createGroup_Family("groupMetStar", group, "star") metalStar = charm.reduceParticle("groupMetStar", massMetal, reduceMassMetal, (radialStep, center)) for i in range(len(metalStar)): metalProf[metalStar[i][0]] = metalStar[i][1] / metalStar[i][2] if (numGas != 0) & (family == "gas"): charm.createGroup_Family("groupMetGas", group, "gas") metalGas = charm.reduceParticle("groupMetGas", massMetal, reduceMassMetal, (radialStep, center)) for i in range(len(metalGas)): metalProf[metalGas[i][0]] = metalGas[i][1] / metalGas[i][2] if family == "bar": charm.createGroup_Family("groupMetGas", group, "gas") charm.createGroup_Family("groupMetStar", group, "star") metalProfGas = [[0, 0]] * nbins metalProfStar = [[0, 0]] * nbins metalGas = charm.reduceParticle("groupMetGas", massMetal, reduceMassMetal, (radialStep, center)) for i in range(len(metalGas)): metalProfGas[metalGas[i][0]][0] = metalGas[i][1] metalProfGas[metalGas[i][0]][1] = metalGas[i][2] metalStar = charm.reduceParticle("groupMetStar", massMetal, reduceMassMetal, (radialStep, center)) for i in range(len(metalStar)): metalProfStar[metalStar[i][0]][0] = metalStar[i][1] metalProfStar[metalStar[i][0]][1] = metalStar[i][2] for i in range(len(metalProf)): metalTot = metalProfStar[i][0] + metalProfGas[i][0] massTot = metalProfStar[i][1] + metalProfGas[i][1] if massTot != 0: metalProf[i] = metalTot / massTot for i in range(nbins): metalRad[i] = (i + 0.5) * radialStep return (metalRad, metalProf)
def setbox(group, xcenter, ycenter, zcenter, xradius, yradius, zradius, sourcegroup='All') : """Select particles within a box centered on coordinate which extends radius in either direction along each axis.""" import charm # check if simulation loaded if charm.getGroups() == None : raise StandardError('Simulation not loaded') edge = [xradius, yradius, zradius] corner = map(lambda center, radius : center - radius, [xcenter, ycenter, zcenter], edge) edge = map(lambda radius : radius * 2, edge) charm.createGroupAttributeBox(group, sourcegroup, 'position', corner[0], corner[1], corner[2], edge[0], 0, 0, 0, edge[1], 0, 0, 0, edge[2])
def calcGalacticCoordinates(angMomGroup=None, center ='pot'): '''Assigns particles a position in the galactic coordinate system. galVelocity = (v+Hr). angMomGroup''' #=========================================================================== # --Overview-- # 1.Form transformation matrix # 2.Translate the coordinates to the center of the galaxy (or COM velocity) # 3.Rotate the coordinate system to be aligned with the angular momentum vector # 4.Write information to vector attributes #=========================================================================== if ((angMomGroup in charm.getGroups())==False) & (angMomGroup!=None): print "Group does not exist, please try again." return ############################## # Actual Routine ############################## if (angMomGroup==None): angMomGroup = 'angMomGroup' cnt = findcenter.findCenter(method=center) virialRadius = virialgroup.getVirialGroup() charm.createGroupAttributeSphere('angMomGroup', 'All', 'position', cnt[0], cnt[1], cnt[2], virialRadius*quesoConfig.rgalFraction) charm.createGroup_Family('angMomGroup', 'angMomGroup', 'gas') charm.createGroup_AttributeRange('angMomGroup', 'angMomGroup', 'temperature', 0, quesoConfig.coldGasTemp) else: cnt = findcenter.findCenter(group2=angMomGroup,method=center) angmom.getAngMomVector(group=angMomGroup,center=center) # Transformation Matrix galZ = vectormath.normalizeVector(angmom.getAngMomVector(group=angMomGroup,center=center)) galX = vectormath.normalizeVector(vectormath.crossVectors((0,1,0),galZ)) galY = vectormath.normalizeVector(vectormath.crossVectors(galZ,galX)) transMatrix = (galX, galY, galZ) fHubble = quesoConfig.fHubble0*math.sqrt(quesoConfig.omega0/charm.getTime()**3+(1-quesoConfig.omega0)) # Galactic Position charm.createVectorAttribute('star','galPosition') charm.createVectorAttribute('gas', 'galPosition') charm.createVectorAttribute('dark','galPosition') param = (cnt, transMatrix) charm.runLocalParticleCodeGroup('All', calcGalPosition, param) # Galactic velocity vCM=angmom.getVelCM(angMomGroup) param = (cnt, transMatrix,vCM, fHubble) charm.createVectorAttribute('star','galVelocity') charm.createVectorAttribute('gas', 'galVelocity') charm.createVectorAttribute('dark','galVelocity') charm.runLocalParticleCodeGroup('All', calcGalVelocity, param) return
def markgal(group, maxTemp, minRho) : """Mark gas particles that are likely to be in galaxies based on selected temperature and density thresholds. Marked particles are stored in a group called "mark" which will be replaced every time a marking command is run.""" # check if simulation loaded if charm.getGroups() == None : raise StandardError('Simulation not loaded') # take only gas particles charm.createGroup_Family('mark', group, 'gas') # get min temp, get max density minTemp = charm.getAttributeRangeGroup('mark', 'gas', 'temperature')[0] maxRho = charm.getAttributeRangeGroup('mark', 'gas', 'density')[1] # reduce members according to criteria charm.createGroup_AttributeRange('mark', 'mark', 'temperature', minTemp, maxTemp) charm.createGroup_AttributeRange('mark', 'mark', 'density', minRho, maxRho)
def getMomentInertiaTensor(group2,center='pot'): '''Return the moment of inertia tensor for given group. center can be 'pot' or 'com'. This is in physical units. [[Ixx, Ixy, Ixz], [Iyx, Iyy, Iyz], [Izx, Izy, Izz]] ''' group = group2 if ((group2 in charm.getGroups())==False): print "Group does not exist, please try again." return center = findcenter.findCenter(group2=group,method=center) triangleTensor = charm.reduceParticle(group , mapMomentInertiaTensor, reduceMomentInertiaTensor, center)[0][1] fullTensor = [[0]*3,[0]*3,[0]*3] units = quesoConfig.msolunit*((charm.getTime()*quesoConfig.kpcunit)**2) fullTensor[0] = vectormath.multVectorScalar(units, triangleTensor[0][0:3]) fullTensor[1] = vectormath.multVectorScalar(units, [triangleTensor[0][1],triangleTensor[1][0],triangleTensor[1][1]]) fullTensor[2] = vectormath.multVectorScalar(units, [triangleTensor[0][2],triangleTensor[1][1],triangleTensor[2][0]]) return fullTensor
def getMeanMetals(group , family='bar'): if ((group in charm.getGroups())==False): print "Group does not exist, please try again." return metalStar = [(0,0,0)] metalGas = [(0,0,0)] numStar = charm.getNumParticles(group, 'star') numGas = charm.getNumParticles(group, 'gas') if (numStar !=0) & (family=='bar' or family=='star'): charm.createGroup_Family('groupMetStar',group, 'star') metalStar = charm.reduceParticle('groupMetStar',massMetal,reduceMassMetal,None) if (numGas !=0) & (family=='bar' or family=='gas'): charm.createGroup_Family('groupMetGas',group , 'gas') metalGas = charm.reduceParticle('groupMetGas' ,massMetal,reduceMassMetal,None) if (numGas==0) & (numStar==0): return 0 metals = (metalStar[0][1] + metalGas[0][1])/(metalStar[0][2]+metalGas[0][2]) return metals
def getAngMomVector(group, center='pot'): """Returns specific angular momentum vectors in physical units. Center can be 'pot' or 'com'""" if ((group in charm.getGroups())==False): print "Group does not exist, please try again." return def multVectorScalar(scalar,vector): return (vector[0]*scalar,vector[1]*scalar,vector[2]*scalar) center = findcenter.findCenter(group2=group,method=center) #find COM Velocity vCM=getVelCM(group) angMomVector = charm.reduceParticle(group, getParticleAngMomentum, sumAngMomentum, [center,vCM]) mass = charm.getAttributeSum(group, 'gas', 'mass') mass += charm.getAttributeSum(group, 'star', 'mass') mass += charm.getAttributeSum(group, 'dark', 'mass') unitConvert = quesoConfig.kpcunit*quesoConfig.velocityunit #masses are divided out anyway. #Convert this into a specific angular momentum 3-vector angMomVector = multVectorScalar(unitConvert/mass, (angMomVector[0][1] ,angMomVector[0][2] ,angMomVector[0][3])) return angMomVector
def boxstat(group, family='all') : """Print statistics for a group as in the tipsy boxstat command Arguments are group and family Return physical parameters of particles: number, mass, center of box, size of box, center of mass, center of mass's velocity, angular momentum vector. Format output to say if gas, dark, star, baryon, or all. Check error cases: Bad input, Not a proper data type, Box not loaded.""" import charm # check if simulation loaded if charm.getGroups() == None : raise StandardError('Simulation not loaded') # There is a potential issue with the capitalization of "all" being mixed # in different implementations. Take either. if family == 'All': family = 'all' if group == 'all': group = 'All' # Prepare values based on selected family if family == 'all': nPartGas = charm.getNumParticles(group, 'gas') nPartDark = charm.getNumParticles(group, 'dark') nPartStar = charm.getNumParticles(group, 'star') mass = charm.getAttributeSum(group, 'gas', 'mass') mass += charm.getAttributeSum(group, 'dark', 'mass') mass += charm.getAttributeSum(group, 'star', 'mass') bBox = list(charm.getAttributeRangeGroup(group, 'gas', 'position')) bBoxTmp = charm.getAttributeRangeGroup(group, 'dark', 'position') bBox[0] = map(lambda x, y : min(x,y), bBox[0], bBoxTmp[0]) bBox[1] = map(lambda x, y : max(x,y), bBox[1], bBoxTmp[1]) bBoxTmp = charm.getAttributeRangeGroup(group, 'star', 'position') bBox[0] = map(lambda x, y : min(x,y), bBox[0], bBoxTmp[0]) bBox[1] = map(lambda x, y : max(x,y), bBox[1], bBoxTmp[1]) groupfamname = group elif family == 'baryon': nPartGas = charm.getNumParticles(group, 'gas') nPartStar = charm.getNumParticles(group, 'star') mass = charm.getAttributeSum(group, 'gas', 'mass') mass += charm.getAttributeSum(group, 'star', 'mass') bBox = list(charm.getAttributeRangeGroup(group, 'gas', 'position')) bBoxTmp = charm.getAttributeRangeGroup(group, 'star', 'position') bBox[0] = map(lambda x, y : min(x,y), bBox[0], bBoxTmp[0]) bBox[1] = map(lambda x, y : max(x,y), bBox[1], bBoxTmp[1]) groupfamname = group else : nPart = charm.getNumParticles(group, family) mass = charm.getAttributeSum(group, family, 'mass') bBox = charm.getAttributeRangeGroup(group, family, 'position') groupfamname = group + 'FAM' + family charm.createGroup_Family(groupfamname, group, family) # Derive box properties size = map(lambda min, max : max - min, bBox[0], bBox[1]) center = map(lambda min, max : 0.5*(max + min), bBox[1], bBox[0]) mmoment = charm.reduceParticle(groupfamname, centmassmap, centmassreduce, None) cm = [mmoment[0][1]/mass, mmoment[0][2]/mass, mmoment[0][3]/mass] vmoment = charm.reduceParticle(groupfamname, centmassVelmap, centmassreduce, None) vcm = [vmoment[0][1]/mass, vmoment[0][2]/mass, vmoment[0][3]/mass] angmom1 = charm.reduceParticle(groupfamname, angmomMap, centmassreduce, None) # Apply parallel axis theorem and make specific angmom = [0,0,0] angmom[0] = (angmom1[0][1] - mass*(cm[1]*vcm[2] - cm[2]*vcm[1]))/mass angmom[1] = (angmom1[0][2] - mass*(cm[2]*vcm[0] - cm[0]*vcm[2]))/mass angmom[2] = (angmom1[0][3] - mass*(cm[0]*vcm[1] - cm[1]*vcm[0]))/mass # Write output header according to type of particles if family == 'all' : print 'number of dark, gas and star particles =', nPartDark, nPartGas, nPartStar elif family == 'baryon' : print 'number of baryon particles =', (nPartGas + nPartStar) else: print 'number of', family, 'particles =', nPart # Write physical parameters print 'mass =', mass print 'center coordinates =', center print 'size =', size print 'center of mass coordinates =', cm print 'center of mass velocity =', vcm print 'angular momentum vector =', angmom
def do_meanmwt() : if 'AllFAMgas' not in charm.getGroups() : charm.createGroup_Family('AllFAMgas', 'All', 'gas') if 'meanmwt' not in charm.getAttributes('gas') : charm.createScalarAttribute('gas', 'meanmwt') charm.runLocalParticleCodeGroup('AllFAMgas', setvals, None)
def profile(group='All', center='pot', family='all', projection='sph', bin_type='log', nbins=30, filename='profile.DAT', min_radius=0., fit_radius=0.) : """Perform the Tipsy profile() function. profile is a command that produces a file of name, file- name, that contains various physical profiles as a func- tion of radius for particles of type particle in group. The center of the radial distribution is taken to be the center of mass of the particles of the type particle in group center. If the string "pot" is given instead of a group name for the argument center, the particle in group with the lowest potential energy is used as the center of the radial bins. The projection argument refers to the shape of the bins and the bin_type argument refers to how the particles are binned, either linearly or log in radius. There are nbins bins distributed equally in either radius or log radius between min_radius and the size of group. The output file has the following columns: radius, number in bin, density, cumulative mass (M(<r)), circular veloc- ity (sqrt(M(<r)/r)), radial velocity, radial velocity dis- persion, tangential velocity, tangential velocity disper- sion, specific angular momentum (j),j_theta, j_phi. The j_theta and j_phi angles specify the direction of the spe- cific angular momentum vector (in degrees). If family is is either gas, baryon, or all, four additional columns containing the mean mass weighted gas density, mass weigh- ted gas temperature, mass weighted gas pressure, and mass weighted gas entropy are added. If particle is either star, baryon, or all an additional column containing the stellar luminosity in the Johnson V band is added. This is calculated using the ages of the star particles as in Katz (Ap.J. 391: 502, 1992). Note: some rarer configuration options are not implemented e.g. non-uniform UV for star luminosity calculation.""" try: import charm # check initialization state of meanmwt attribute if family in ['all', 'gas', 'baryon'] and not 'meanmwt' in charm.getAttributes('gas') : print 'Attribute meanmwt not initialized. Initializing...' import load_meanmwt load_meanmwt.do_meanmwt() print '\n' # these should be moved to tipsyf # fn to take the dot product of two vectors in R3 def dotprod(a, b) : return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] # fn to take the cross product of two vectors in R3 def crossprod(a, b) : return [a[1]*b[2] - a[2]*b[1], a[2]*b[0] - a[0]*b[2], a[0]*b[1] - a[1]*b[0]] # fn to multiply a vector v by a scalar c def scalevec(c, v) : return [c * v[0], c * v[1], c * v[2]] # fn to add two vectors def addvec(a, b) : return [a[0] + b[0], a[1] + b[1], a[2] + b[2]] # fn to subtract vector b from vector a def subvec(a, b) : return [a[0] - b[0], a[1] - b[1], a[2] - b[2]] # fn to find the length of a vector def vlength(a) : import math return math.sqrt(pow(a[0], 2) + pow(a[1], 2) + pow(a[2], 2)) # fn to combine two vectors, weighting by their corresponding mass scalars def weightvecs(m1, x1, m2, x2) : return [(x1[0]*m1 + x2[0]*m2)/(m1+m2), (x1[1]*m1 + x2[1]*m2)/(m1+m2), (x1[2]*m1 + x2[2]*m2)/(m1+m2)] # scrub input # check if simulation loaded, store group list groups = charm.getGroups() if groups == None : raise StandardError('Simulation not loaded') # case is inconsistent between groups and families, work for either if group == 'all' : group = 'All' if family == 'All' : family = 'all' # deal with group or center group does not exist if group not in groups : raise StandardError('Group does not exist!') if (center not in groups) and (center != 'pot') : raise StandardError('Center group does not exist!') # set up families iterator and throw family related exceptions families = charm.getFamilies() if families == None : raise StandardError('Simulation loaded but no families present.') isbaryon = 0 if family == 'baryon' : isbaryon = 1 if 'dark' in families: families.remove('dark') if len(families) == 0 : raise StandardError('No baryon particles present in simulation.') if (family != 'all') and (family != 'baryon') : if family not in families : raise StandardError('Family not present in simulation.') else : families = [family] if len(families) == 0 : raise StandardError('List of families to process has zero length.') # create groups containing one family each, store names in famgroups list famgroups = [] for each in families : famgroup = group + 'FAM' + each charm.createGroup_Family(famgroup, group, each) if charm.getNumParticles(famgroup, each) != 0 : famgroups += [famgroup] if len(famgroups) == 0 : raise StandardError('No particles in group of selected type.') # validate other inputs if bin_type == 'linear' : bin_type = 'lin' elif bin_type == 'logarithmic' : bin_type = 'log' elif bin_type not in ['lin', 'log'] : raise StandardError('Value of bin_type must be lin or log.') if (int(nbins) != nbins) or (nbins < 1) : raise StandardError('Value of nbins must be a positive integer.') if not min_radius >= 0 : raise StandardError('Value of min_radius cannot be negative.') if min_radius == 0 and bin_type == 'log' : min_radius = config.LOG_MIN print 'Parameter min_radius set to ' + str(min_radius) + ' to accomodate logarithmic binning.' if not fit_radius >= 0 : raise StandardError('Value of fit_radius cannot be negative.') # begin calculating parameters # find center point if center == 'pot' : center = charm.findAttributeMin(group, 'potential') else: center = charm.getCenterOfMass(center) # get center_vel and center_angular_mom # families must be parsed individually then combined vmdata = charm.reduceParticle(famgroups[0], vmmap, vmreduce, None) if vmdata == None : raise StandardError('MapReduce returned NoneType object.') center_vel = vmdata[0][2] center_angular_mom = vmdata[0][3] # if there are additional families, parse each then combine results weighting by mass if len(famgroups) > 1 : old_mass = vmdata[0][1] for i in range(len(famgroups)-1) : vmdata = charm.reduceParticle(famgroups[i+1], vmmap, vmreduce, None) new_mass = vmdata[0][1] center_vel = weightvecs(old_mass, center_vel, new_mass, vmdata[0][2]) center_angular_mom = weightvecs(old_mass, center_angular_mom, new_mass, vmdata[0][3]) old_mass += new_mass # for an elliptical projection, use Katz to get shape & then find vel, mom # when restoring this, find out what happened to center_ell? if projection == 'ell' : if fit_radius == 0. : print 'fit_radius = 0 in an elliptical projection.\nusing all particles in batch to find shape of ellipses.' ell_matrix = [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]] center_ell = [0., 0., 0.] ba = ca = phi = theta = psi = 0. center = list(center) shape_data = elliptical.find_shape(famgroups, center, fit_radius, ell_matrix, center_ell) center = tuple(center) if type(shape_data) == type(None) : print 'find_shape() did not succeed' return # raise StandardError('Problem in elliptical.find_shape(): no fit found.') else : ba, ca, phi, theta, psi = shape_data print 'b/a = %g, c/a = %g' % (ba, ca) center = [0.]*config.MAXDIM center_vel = [0.]*config.MAXDIM center_angular_mom = [0.]*config.MAXDIM vel_data = elliptical.find_vel(famgroups,center,center_vel,center_angular_mom, ell_matrix, center_ell, ba, ca, fit_radius) else : ba, ca, phi, theta, psi, center_ell, ell_matrix = (0., 0., 0., 0., 0., [0.]*config.MAXDIM, [[0.]*config.MAXDIM]*config.MAXDIM) # find max_radius # maxradmap iterates over all particles and compares each radius to the previous max found p_param = (center) max_radius = 0. if family == 'all' : max_radius = charm.reduceParticle(group, maxradmap, maxradreduce, p_param)[0][1] else : for i in range(len(famgroups)) : fam_radius = charm.reduceParticle(famgroups[i], maxradmap, maxradreduce, p_param)[0][1] max_radius = max(max_radius, fam_radius) # TIPSY uses an approximation for max_radius which can't be adequately emulated here. # for the run99 data file, the value of max_radius is 8.18829; set it manually. # max_radius = 8.18829 # determine bin_size # if min_radius > 0, then bin 0 should have the specified radius. # other than this exception, all bins have constant size in either lin or log space as specified if min_radius > max_radius and nbins > 1 : raise StandardException('Value for min_radius encompasses all particles; cannot bin data.') # min_radius > 0 case creates a scenario where nbins = 1 would create a /0 error, must handle here. if nbins == 1 : bin_size = max_radius # bin_type and min_radius are superfluous to nbins = 1 case bin_type = 'lin' min_radius = 0. # find size in lin space elif bin_type == 'lin' : if min_radius > 0. : bin_size = (max_radius - min_radius) / float(nbins - 1) else : bin_size = (max_radius - min_radius) / float(nbins) # find size in log space else : min_radius = math.log10(min_radius) bin_size = (math.log10(max_radius) - min_radius) / float(nbins - 1) # calculate bin boundary radii bounds = [0.] * (nbins + 1) # the first boundary radius is affected by min_radius, handle if bin_type == 'lin' and min_radius == 0.: bounds[1] = bin_size else : bounds[1] = min_radius # each following bound should be bound[previous] + bin_size for i in range(2, nbins + 1) : bounds[i] = bounds[i - 1] + bin_size # if logarithmic binning, convert values into linear space if bin_type == 'log' : for i in range(1, nbins + 1) : bounds[i] = pow(10., bounds[i]) starlf = star_lum.star_lum() sim_time = charm.getTime() # do calculations which must see individual particles. # the set of attributes on a particle varies by family, so each family must be processed seperately params = [None, isbaryon, bounds, projection, nbins, bin_type, bin_size, min_radius, max_radius, center, center_vel, ell_matrix, center_ell, ba, ca, config.msolunit, config.gasconst, sim_time, config.time_unit, starlf.age, starlf.lum, starlf.lumv_fit, starlf.vv_dat, starlf.vv_fit, starlf.bv_dat, starlf.bv_fit, center_angular_mom] fam_data = [None] * len(famgroups) for i in range(len(famgroups)) : params[0] = families[i] fam_data[i] = charm.reduceParticle(famgroups[i], basemap, basereduce, tuple(params)) # shows per-family result of MapReduce, sometimes useful for debugging # if debug_flag == 1 : # print '\nThe current family is: ' + families[i] # for line in fam_data[i] : # print line if len(fam_data) < 1 or fam_data == None : raise StandardException('MapReduce for fam_data has length zero or is NoneType.') # sum results into one list. # Note that if a bin has no particles, it will be missing from the reduce result. data[] should have no missing rows. # values are: bin, number, mass, vel_radial, vel_radial_sigma, vel_tang_sigma, angular_mom[x,y,z], lum, density, temp, pressure, entropy, gas_mass data = [None] * nbins for i in range(nbins) : data[i] = [i, 0, 0., 0., 0., 0., [0., 0., 0.], 0., 0., 0., 0., 0., 0.] for fam in fam_data : for row in fam : bin = row[0] # merge fields 0 thru 5 for i in range(1, 6) : data[bin][i] += row[i] # merge field 6 (angular momentum vector) for i in range(3) : data[bin][6][i] += row[6][i] # merge fields 7 thru 12 for i in range(7, 13) : data[bin][i] += row[i] if not len(data) == nbins : raise StandardError('nbins != length of result after basemap') # calculate remaining "base" values that did not need to be handled in the map mass_r = 0. for i in range(nbins) : # get max radius, min radius, and mean radius r_max = bounds[i + 1] r_min = bounds[i] r_mean = (r_max + r_min) / 2. # number of particles in bin number = data[i][1] # if the bin is empty then some calculations e.g. 1/mass will explode; catch these. if number != 0 : # find volume by projection pi = 3.141592653589793 if projection == 'ell' : volume = 4. * ba * ca / 3. * pi * (pow((r_max),3) - pow((r_min),3)) elif projection == 'cyl' : volume = pi * (pow((r_max),2) - pow((r_min),2)) else : volume = (4. / 3.) * pi * (pow(r_max,3) - pow(r_min,3)) # unpack values. deal with non-sum calculations, largely n / mass mass = data[i][2] mass_r += mass vel_radial = data[i][3] / mass vel_radial_sigma = data[i][4] / mass if vel_radial_sigma > vel_radial**2 : vel_radial_sigma = math.sqrt(vel_radial_sigma - vel_radial**2) else : vel_radial_sigma = 0. vel_tang_sigma = data[i][5] / mass ang_mom = scalevec(1./mass, data[i][6]) ang = vlength(ang_mom) if ang > 0.0 : ang_theta = 180.0 * math.acos(ang_mom[2] / ang) / pi else : ang_theta = 0.0 ang_phi = 180.0 * math.atan2(ang_mom[1], ang_mom[0]) / pi rho = mass / volume vel_circ = ang / r_mean c_vel = math.sqrt(mass_r / r_max) if vel_tang_sigma > vel_circ**2 : vel_tang_sigma = math.sqrt(vel_tang_sigma - vel_circ**2) else : vel_tang_sigma = 0. # star stuff lum_den = data[i][7] / volume # gas stuff if 'gas' in families and data[i][12] > 0 : density = data[i][8] / data[i][12] temp = data[i][9] / data[i][12] pressure = data[i][10] / data[i][12] entropy = data[i][11] / data[i][12] else : density = 0. temp = 0. pressure = 0. entropy = -config.HUGE data[i] = [r_max, number, rho, mass_r, c_vel, vel_radial, vel_radial_sigma, vel_circ, vel_tang_sigma, ang, ang_theta, ang_phi, density, temp, pressure, entropy, lum_den] else : c_vel = math.sqrt(mass_r / r_max) data[i] = [r_max, 0, 0., mass_r, c_vel, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.] # build header row # may need to update the exact text used for clarity or for consistency with TIPSY headers = ('radius', 'number', 'rho', 'mass_r', 'c_vel', 'vel_radial', 'vel_radial_sigma', 'vel_circ', 'vel_tang_sigma', 'ang', 'ang_theta', 'ang_phi') if family in ['gas', 'baryon', 'all'] : headers += ('mmwg_density', 'mwg_temp', 'mwg_pres', 'mwg_entr') if family in ['star', 'baryon', 'all'] : headers += ('lum_V',) # write output to file f = open(filename, 'w') # write headers according to families present f.write('#') for i in range(len(headers)) : f.write(' ' + headers[i]) f.write('\n') # write data according to families present for i in range(nbins) : f.write('%g %d %g %g %g %g %g %g %g %g %g %g' % tuple(data[i][0:12])) if 'gas' in families : f.write(' %g %g %g %g' % tuple(data[i][12:16])) if 'star' in families : f.write(' %g' % tuple(data[i][16:17])) f.write('\n') f.close() # there is no current mechanism to delete groups. # if one is added, delete temporary working groups here. except : print traceback.format_exc()