diff --git a/pNbody/main.py b/pNbody/main.py index 4c8d690..ddf5157 100644 --- a/pNbody/main.py +++ b/pNbody/main.py @@ -1,5775 +1,5793 @@ # -*- coding: utf-8 -*- # some standard modules import os,sys,string,types,glob from copy import deepcopy import warnings # array module from numpy import * from numpy import clip as numclip from numpy import random as RandomArray # module that init parameters from parameters import * # nbody python modules import io from libutil import * from palette import * import geometry as geo import fourier import param import liblog import libgrid import libdisk import libutil import nbdrklib # nbody C modules from myNumeric import * from mapping import * from nbodymodule import * # Gtools module (now integrated in nbody) #import Gtools as Gt import units import ctes import thermodyn import coolinglib import cosmo import treelib import asciilib try: import ptreelib except: pass try: import libqt except: pass try: import SM except: pass try: # all this is usefull to read files from mpi4py import MPI except: MPI = None import mpi # maybe we should send mpi instead of MPI FLOAT = float #################################################################################################################################### # # DEFAULT CLASS NBODY # #################################################################################################################################### class NbodyDefault: ''' This is the reference Nbody class. This is the constructor for the **Nbody** object. Optional arguments are: p_name : name of the file in case of multiple files, files must be included in a list ["file1","file2"] pos : positions (3xN array) vel : positions (3xN array) mass : positions (1x array) num : id of particles (1xN array) tpe : type of particles (1xN array) ftype : type of input file (binary,ascii) status : 'old' : open an old file 'new' : create a new object byteorder : 'little' or 'big' pio : parallel io : 'yes' or 'no' local : True=local object, False=global object (paralellized) Not implemeted Yet log : log file unitsfile : define the type of units by default this class initialize the following variables : self.p_name : name of the file(s) to read or write self.pos : array of positions self.vel : array of velocities self.mass : array of masses self.num : array of id self.tpe : array of types self.ftype : type of the file self.status : object status ('old' or 'new') self.byteorder : byter order ('little' or 'big') self.pio : parallel io ('yes' or 'no') self.log : log object # new variables self.nbody : local number of particles self.nbody_tot : total number of particles self.mass_tot : total mass self.npart : number of particles of each type self.npart_tot : total number of particles of each type self.spec_vars : dictionary of variables specific for the format used self.spec_vect : dictionary of vector specific for the format used ''' def __init__(self,p_name=None,pos=None,vel=None,mass=None,num=None,tpe=None,ftype=None,status='old',byteorder=sys.byteorder,pio='no',local=False,log=None,unitsfile=None): ################################# # init vars ################################# /home/leo/.pNbody/formats/gadget.py if p_name == None: status = 'new' self.set_filenames(p_name,pio=pio) self.pos = pos self.vel = vel self.mass = mass self.num = num self.tpe = tpe self.ftype = self.__class__.__name__ self.status = status self.byteorder = byteorder self.pio = pio self.log = log self.nbody = None self.nbody_tot = None self.mass_tot = None self.npart = None self.npart_tot = None self.unitsfile = unitsfile ################################# # init units ################################# self.init_units() ################################# # init other parameters ################################# self.parameters = param.Params(PARAMETERFILE,None) self.defaultparameters = self.parameters.get_dic() # log if self.log == None: self.log = liblog.Log(os.path.join(HOME,'.nbodylog'),show='yes') ################################################### # in case of an old file, open and read the file(s) ################################################### if status=='old': self.read() ################################################### # in case of a new file ################################################### elif status=='new': for i in range(len(self.p_name)): if self.p_name[i] == None: self.p_name[i] = 'file.dat' ################################################### # final initialisation ################################################### self.init() ################################################### # check consistency ################################################### # to be done ################################# # # init functions # ################################# ################################# def init(self): ################################# ''' Initialize normal and specific class variables ''' # 1) find the number of particles self.nbody = self.get_nbody() # 2) define undefined vectors if self.pos == None: self.pos = zeros((self.nbody,3),float32) self.pos = self.pos.astype(float32) else: self.pos = self.pos.astype(float32) if self.vel == None: self.vel = zeros((self.nbody,3),float32) self.vel = self.vel.astype(float32) else: self.vel = self.vel.astype(float32) if self.mass == None: self.mass = ones((self.nbody, ),float32)/self.nbody self.mass = self.mass.astype(float32) else: self.mass = self.mass.astype(float32) if self.tpe == None: self.tpe = zeros(self.nbody,int) self.tpe = self.tpe.astype(int) else: self.tpe = self.tpe.astype(int) if self.num == None: self.num = self.get_num() self.num = self.num.astype(int) else: self.num = self.num.astype(int) # 3) other variables self.nbody_tot = self.get_nbody_tot() self.mass_tot = self.get_mass_tot() self.npart = self.get_npart() self.npart_tot = self.get_npart_tot() # Init specific class variables # (may be redundant with make_specific_variables_global) self.spec_vars = self.get_default_spec_vars() list_of_vars = self.get_list_of_vars() for name in self.spec_vars.keys(): try: list_of_vars.index(name) except ValueError: setattr(self, name, self.spec_vars[name]) # Init specific class vectors self.spec_vect = self.get_default_spec_array() list_of_vect = self.get_list_of_array() for name in self.spec_vect.keys(): try: list_of_vect.index(name) except ValueError: setattr(self, name, ones(self.nbody,self.spec_vect[name][1])*self.spec_vect[name][0]) # init specific parameters self.InitSpec() # sph parameters/variables self.InitSphParameters() ################################# def InitSpec(self): ################################# """ This function allows to initialize specific parameters. It must be defined in format files. """ pass ################################# def get_format_file(self): ################################# "return the format file" return self._formatfile ################################# def get_ftype(self,ftype='binary'): ################################# """ get the current used format """ return self.ftype ################################# def set_ftype(self,ftype='binary'): ################################# """ Change the type of the file ftype : type of the file """ if mpi.NTask > 1: raise "Warning","set_ftype function is currently not suported with multi proc." new = Nbody(status='new',ftype=ftype) # now, copy all var linked to the model for name in self.get_list_of_vars(): if name != 'ftype': setattr(new, name, getattr(self,name)) # now, copy all array linked to the model for name in self.get_list_of_array(): vec = getattr(self,name) setattr(new, name, vec) # other vars new.init() return new ################################# def get_num(self): ################################# """ Compute the num variable in order to be consistent with particles types """ # compute npart_all if self.npart == None: npart = self.get_npart() else: npart = self.npart npart_all = array(mpi.mpi_allgather(npart)) return mpi.mpi_sarange(npart_all) # + 1 ################################# def get_default_spec_vars(self): ################################# ''' return specific variables default values for the class ''' return {} ################################# def get_default_spec_array(self): ################################# ''' return specific array default values for the class ''' return {} ################################# def set_pio(self,pio): ################################# """ Set parallel input/output or not io pio : 'yes' or 'no' """ self.pio = pio self.set_filenames(self.p_name_global,pio=pio) if pio=='yes': self.num_files = mpi.NTask else: self.num_files = 1 ################################# def rename(self,p_name=None): ################################# """ Rename the files p_name : new name(s) """ if p_name != None: self.set_filenames(p_name,pio=self.pio) ################################# def set_filenames(self,p_name,pio=None): ################################# """ Set the local and global names p_name : new name(s) pio : 'yes' or 'no' """ if type(p_name) == types.ListType: self.p_name_global = [] self.p_name = [] for name in p_name: if pio == 'yes': self.p_name_global.append(name) self.p_name.append("%s.%d"%(name,mpi.mpi_ThisTask())) else: self.p_name_global.append(name) self.p_name.append(name) else: if pio == 'yes': self.p_name_global = [p_name] self.p_name = ["%s.%d"%(p_name,mpi.mpi_ThisTask())] else: self.p_name_global = [p_name] self.p_name = [p_name] ################################# def get_ntype(self): ################################# """ return the number of paticles types """ return len(self.npart) ################################# def get_nbody(self): ################################# """ Return the local number of particles. """ if self.pos != None: nbody = len(self.pos) elif self.vel != None: nbody = len(self.vel) elif self.mass != None: nbody = len(self.mass) elif self.num != None: nbody = len(self.num) elif self.tpe != None: nbody = len(self.tpe) else: nbody = 0 return nbody ################################# def get_nbody_tot(self): ################################# """ Return the total number of particles. """ nbody_tot = mpi.mpi_allreduce(self.nbody) return nbody_tot ################################# def get_npart(self): ################################# """ Return the local number of particles of each types, based on the variable tpe """ npart = array([],int) n = 0 if self.tpe==None: return npart.tolist() for tpe in range(self.get_mxntpe()): np = sum( (self.tpe==tpe).astype(int) ) npart = concatenate((npart,array([np]))) n = n + np if n != self.nbody: print "get_npart : n (=%d) is different from self.nbody (=%d)"%(n,self.nbody) raise "get_npart : n is different from self.nbody" return npart.tolist() ################################# def get_npart_tot(self): ################################# """ Return the total number of particles of each types. """ npart = array(self.npart) npart_tot = mpi.mpi_allreduce(npart) npart_tot = npart_tot.tolist() return npart_tot ################################# def get_npart_all(self,npart_tot,NTask): ################################# ''' From npart_tot, the total number of particles per type, return npart_per_proc, an array where each element corresponds to the value of npart of each process. ''' if (type(npart_tot) != types.ListType) and (type(npart_tot) !=ndarray): npart_tot = array([npart_tot]) ntype = len(npart_tot) npart_all = zeros((NTask,ntype)) for i in range(len(npart_tot)): for Task in range(NTask-1,-1,-1): npart_all[Task,i] = npart_tot[i]/NTask + npart_tot[i]%NTask*(Task==0) return npart_all ################################# def get_npart_and_npart_all(self,npart): ################################# ''' From npart (usually read for the header of a file), compute : npart : number of particles in each type npart_tot : total number of particles in each type npart_all : npart for each process. ''' ################################# def get_mxntpe(self): ################################# ''' Return the max number of type for this format ''' return 6 ################################# def make_default_vars_global(self): ################################# ''' Make specific variables global ''' self.spec_vars = self.get_default_spec_vars() for name in self.spec_vars.keys(): if not self.has_var(name): setattr(self, name, self.spec_vars[name]) ################################# def set_npart(self,npart): ################################# """ Set the local number of particles of each types. This function modifies the variable self.tpe """ if sum(array(npart)) > self.nbody: raise "Error (set_npart)","sum(npart) is greater than nbody" i = 0 n0 = 0 for n in npart: self.tpe[n0:n0+n] = ones(n)*i i = i + 1 n0 = n0+n self.tpe[n0:self.nbody] = ones(self.nbody-n0)*i self.npart = self.get_npart() self.npart_tot = self.get_npart_tot() ################################# def set_tpe(self,tpe): ################################# """ Set all particles to the type tpe """ self.tpe = ones(self.nbody)*tpe self.npart = self.get_npart() self.npart_tot = self.get_npart_tot() ################################# # # parameters functions # ################################# ''' Warning, these routines are a bit bad... ''' def set_parameters(self,params): ''' Set parameters for the class ''' self.parameters = param.Params(PARAMETERFILE,None) self.parameters.params = params.params self.defaultparameters = self.parameters.get_dic() ################################# # # units functions # ################################# ''' There is several ways to set the units in pNbody In an object, the units are stored in self.localsystem_of_units which is a UnitSystem object defined in units.py We define a unit system by giving Unit_lenght, Unit_mass, Unit_time, Unit_K, Unit_mol, and Unit_C Actually only Unit_lenght, Unit_mass, Unit_time are used, all are Units object (units.py) Following Gadget2, easy ways to definde units is to give three floats, UnitVelocity_in_cm_per_s UnitMass_in_g UnitLength_in_cm This is done using the method self.set_local_system_of_units() which uses UnitVelocity_in_cm_per_s,UnitMass_in_g,UnitLength_in_cm if they are given, or read a gadget parameter file or read a pNbody unitsparameter file or use the default unitsparameter file. ''' def init_units(self): ''' This function is responsible for the units initialization. It will create : self.unitsparameters that contains parameters like - the hydrogen mass fraction, - the metalicity ionisation flag - the adiabatic index - ... and self.localsystem_of_units a UnitSystem object that really defines the system of units in the Nbody object. It uses the values : UnitLength_in_cm UnitMass_in_g UnitVelocity_in_cm_per_s All physical values computed in pNbody should use self.localsystem_of_units to be converted in other units. self.unitsparameters is usefull if other parameters needs to be known, like the adiabatic index, etc. ''' self.unitsparameters = param.Params(UNITSPARAMETERFILE,None) if self.unitsfile!=None: ############################################################## # 1) this part should be only in the gadget.py format file, no ? BOF, non # 2) we could simplify using self.set_local_system_of_units() # 3) and some options -> but this needs to update self.unitsparameters ############################################################## # if it is a gadget parameter file try: gparams = io.read_params(self.unitsfile) self.unitsparameters.set('HubbleParam', gparams['HubbleParam']) self.unitsparameters.set('UnitLength_in_cm', gparams['UnitLength_in_cm']) self.unitsparameters.set('UnitMass_in_g', gparams['UnitMass_in_g']) self.unitsparameters.set('UnitVelocity_in_cm_per_s',gparams['UnitVelocity_in_cm_per_s']) # those parameters may be in the header of the file self.unitsparameters.set('Omega0', gparams['Omega0']) self.unitsparameters.set('OmegaLambda', gparams['OmegaLambda']) self.unitsparameters.set('OmegaBaryon', gparams['OmegaBaryon']) self.unitsparameters.set('BoxSize', gparams['BoxSize']) self.unitsparameters.set('ComovingIntegrationOn', gparams['ComovingIntegrationOn']) #self.set_local_system_of_units(gadgetparameterfile=self.unitsfile) except: # try to read a pNbody units file try: self.unitsparameters = param.Params(self.unitsfile,None) #self.set_local_system_of_units(unitparameterfile=self.unitsfile) except: raise IOError(015,'format of unitsfile %s unknown ! Pease check.'%(self.unitsfile)) # define local system of units it it does not exists #if not self.has_var("localsystem_of_units"): self.set_local_system_of_units() # print info #self.localsystem_of_units.info() def set_unitsparameters(self,unitsparams): ''' Set units parameters for the class. ''' print "!!!!!! in set_unitsparameters !!!!" print "!!!!!! this is bad !!!! we should never use UNITSPARAMETERFILE" print "!!!!!! this is bad !!!! we should never use UNITSPARAMETERFILE" self.unitsparameters = param.Params(UNITSPARAMETERFILE,None) self.unitsparameters.params = unitsparams.params self.set_local_system_of_units() def set_local_system_of_units(self,params=None,UnitLength_in_cm=None,UnitVelocity_in_cm_per_s=None,UnitMass_in_g=None,unitparameterfile=None,gadgetparameterfile=None): ''' Set local system of units using UnitLength_in_cm,UnitVelocity_in_cm_per_s,UnitMass_in_g 1) if nothing is given, we use self.unitsparameters to obtain these values 2) if UnitLength_in_cm UnitVelocity_in_cm_per_s UnitMass_in_g are given, we use them 2b) if UnitLength_in_cm,UnitVelocity_in_cm_per_s,UnitMass_in_g are given in a dictionary 3) if unitparameterfile is given we read the parameters from the file (units parameter format) 4) if gadgetparameterfile is given we read the parameters from the file (gadget param format) ''' if gadgetparameterfile!=None: params = io.read_params(gadgetparameterfile) #print "Units Set From %s"%gadgetparameterfile elif unitparameterfile!=None: unitsparameters = param.Params(unitparameterfile,None) params = {} params['UnitLength_in_cm'] = unitsparameters.get('UnitLength_in_cm') params['UnitVelocity_in_cm_per_s'] = unitsparameters.get('UnitVelocity_in_cm_per_s') params['UnitMass_in_g'] = unitsparameters.get('UnitMass_in_g') print "Units Set From %s"%unitparameterfile elif params!=None: print "Units Set From %s"%params elif UnitLength_in_cm!=None and UnitVelocity_in_cm_per_s!=None and UnitMass_in_g!=None: params = {} params['UnitLength_in_cm'] = UnitLength_in_cm params['UnitVelocity_in_cm_per_s'] = UnitVelocity_in_cm_per_s params['UnitMass_in_g'] = UnitMass_in_g print "Units Set From UnitLength_in_cm,UnitVelocity_in_cm_per_s,UnitMass_in_g" else: params = {} params['UnitLength_in_cm'] = self.unitsparameters.get('UnitLength_in_cm') params['UnitVelocity_in_cm_per_s'] = self.unitsparameters.get('UnitVelocity_in_cm_per_s') params['UnitMass_in_g'] = self.unitsparameters.get('UnitMass_in_g') print "Units Set From %s (%s)"%("self.unitsparameters",self.unitsparameters.filename) # now, create the self.localsystem_of_units = units.Set_SystemUnits_From_Params(params) ################################# # # info functions # ################################# ################################# def info(self): ################################# """ Write info """ infolist = [] infolist.append("-----------------------------------") if mpi.NTask>1: infolist.append("") infolist.append("ThisTask : %s"%mpi.ThisTask.__repr__()) infolist.append("NTask : %s"%mpi.NTask.__repr__()) infolist.append("") infolist.append("particle file : %s"%self.p_name.__repr__()) infolist.append("ftype : %s"%self.ftype.__repr__()) infolist.append("mxntpe : %s"%self.get_mxntpe().__repr__()) infolist.append("nbody : %s"%self.nbody.__repr__()) infolist.append("nbody_tot : %s"%self.nbody_tot.__repr__()) infolist.append("npart : %s"%self.npart.__repr__()) infolist.append("npart_tot : %s"%self.npart_tot.__repr__()) infolist.append("mass_tot : %s"%self.mass_tot.__repr__()) infolist.append("byteorder : %s"%self.byteorder.__repr__()) infolist.append("pio : %s"%self.pio.__repr__()) if self.nbody != 0: infolist.append("") infolist.append("len pos : %s"%len(self.pos).__repr__()) infolist.append("pos[0] : %s"%self.pos[0].__repr__()) infolist.append("pos[-1] : %s"%self.pos[-1].__repr__()) infolist.append("len vel : %s"%len(self.vel).__repr__()) infolist.append("vel[0] : %s"%self.vel[0].__repr__()) infolist.append("vel[-1] : %s"%self.vel[-1].__repr__()) infolist.append("len mass : %s"%len(self.mass).__repr__()) infolist.append("mass[0] : %s"%self.mass[0].__repr__()) infolist.append("mass[-1] : %s"%self.mass[-1].__repr__()) infolist.append("len num : %s"%len(self.num).__repr__()) infolist.append("num[0] : %s"%self.num[0].__repr__()) infolist.append("num[-1] : %s"%self.num[-1].__repr__()) infolist.append("len tpe : %s"%len(self.tpe).__repr__()) infolist.append("tpe[0] : %s"%self.tpe[0].__repr__()) infolist.append("tpe[-1] : %s"%self.tpe[-1].__repr__()) if self.spec_info()!=None: infolist = infolist + self.spec_info() all_infolist = mpi.mpi_allgather(infolist) if mpi.mpi_IsMaster(): for infolist in all_infolist: for line in infolist: #print line self.log.write(line) ################################# def spec_info(self): ################################# """ Write specific info """ return None ################################# def object_info(self): ################################# """ Write class(object) info """ list_of_vars = self.get_list_of_vars() list_of_array = self.get_list_of_array() self.log.write("#############################") self.log.write("list of vars") self.log.write("#############################") for name in list_of_vars: self.log.write("%s %s"%( name,str(type(getattr(self,name))))) self.log.write("#############################") self.log.write("list of arrays") self.log.write("#############################") for name in list_of_array: self.log.write("%s %s"%(name,str(type(getattr(self,name))))) ################################# def nodes_info(self): ################################# """ Write info on nodes """ all_npart = mpi.mpi_allgather(self.npart) all_nbody = mpi.mpi_allgather(self.nbody) if mpi.mpi_IsMaster(): for Task in range(mpi.NTask): line = "Task=%4d nbody=%10d"%(Task,all_nbody[Task]) line = line + " npart= " for npart in all_npart[Task]: line = line + "%10d "%npart self.log.write(line) ################################# def memory_info(self): ################################# """ Write info on memory size of the current object (only counting arrays size) """ total_size = 0 array_size = 0 elts = self.get_list_of_array() for elt in elts: #num_of_elts = getattr(self,elt).size #byte_per_elts = getattr(self,elt).itemsize #bytes = num_of_elts*byte_per_elts bytes = getattr(self,elt).nbytes total_size = total_size + bytes array_size = array_size + bytes print "(%d) %10s %14d"%(mpi.ThisTask,elt,bytes) #elts = self.get_list_of_vars() #for elt in elts: array_size = mpi.mpi_reduce(array_size) # only the master return the info total_size = mpi.mpi_reduce(total_size) if mpi.mpi_IsMaster(): print "total size = %d octets"%(total_size) if array_size < 1024: print "total arrays size = %d octets"%(array_size) else: array_size = array_size/1024.0 if array_size < 1024: print "total arrays size = %dK"%(array_size) else: array_size = array_size/1024.0 if array_size < 1024: print "total arrays size = %dM"%(array_size) else: array_size = array_size/1024.0 if array_size < 1024: print "total arrays size = %dG"%(array_size) ################################# def print_filenames(self): ################################# """ Print files names """ self.log.write("p_name_global = %s"%str(self.p_name_global)) self.log.write("p_name = %s"%str(self.p_name)) ################################# # # list of variables functions # ################################# def get_list_of_array(self): """ Return the list of numpy vectors of size nbody. """ list_of_arrays = [] for name in dir(self): if type(getattr(self,name)) == ndarray: if len(getattr(self,name)) == self.nbody: #if (name!="nall") and (name!="nallhw") and (name!="massarr") and (name!="npart") and (name!="npart_tot"): list_of_arrays.append(name) return list_of_arrays def get_list_of_method(self): """ Return the list of instance methods (functions). """ list_of_instancemethod = [] for name in dir(self): if type(getattr(self,name)) == types.MethodType: list_of_instancemethod.append(name) return list_of_instancemethod def get_list_of_vars(self): """ Get the list of vars that are linked to the model """ list_of_allvars = dir(self) list_of_arrays = self.get_list_of_array() list_of_method = self.get_list_of_method() for name in list_of_arrays: list_of_allvars.remove(name) for name in list_of_method: list_of_allvars.remove(name) #list_of_allvars.remove('log') #list_of_allvars.remove('read_fcts') # becose these vars are linked to fcts #list_of_allvars.remove('write_fcts') # should be definitely removed return list_of_allvars def has_var(self,name): ''' Return true if the object pNbody has a variable called self.name ''' get_list_of_vars = self.get_list_of_vars() try: getattr(self,name) return True except AttributeError: return False def has_array(self,name): ''' Return true if the object pNbody has an array called self.name ''' list_of_array = self.get_list_of_array() try: list_of_array.index(name) return True except ValueError: return False def find_vars(self): ''' This function return a list of variables defined in the current object ''' elts = dir(self) lst = [] for elt in elts: exec("obj = self.%s"%(elt)) if type(obj) != types.MethodType: lst.append(elt) return lst ################################# # # check special values # ################################# def check_arrays(self): ''' check if the array contains special values like NaN or Inf ''' status = 0 for name in self.get_list_of_array(): vec = getattr(self,name) # check nan if isnan(vec).any(): msg = "array %s contains Nan !!!"%name warnings.warn(msg) status = 1 # check nan if isinf(vec).any(): msg = "array %s contains Inf !!!"%name warnings.warn(msg) status = 1 return status ################################# # # read/write functions # ################################# def read(self): """ Read the particle file(s) """ for i in range(len(self.p_name)): self.open_and_read(self.p_name[i],self.get_read_fcts()[i]) self.make_default_vars_global() def open_and_read(self,name,readfct): ''' open and read file name name : name of the input readfct : function used to read the file ''' # check p_name if self.pio=='yes' or mpi.mpi_IsMaster(): io.checkfile(name) # get size if self.pio=='yes' or mpi.mpi_IsMaster(): isize = os.path.getsize(name) # open file if self.pio=='yes' or mpi.mpi_IsMaster(): f = open(name,'r') else: f = None # read the file readfct(f) if self.pio=='yes' or mpi.mpi_IsMaster(): fsize = f.tell() else: fsize = None if self.pio=='yes' or mpi.mpi_IsMaster(): if fsize != isize: raise "ReadError","file %s not red completely"%(name) # close file if self.pio=='yes' or mpi.mpi_IsMaster(): f.close() def get_read_fcts(self): """ returns the functions needed to read a snapshot file. """ return [] def write(self): """ Write the particle file(s) """ for i in range(len(self.p_name)): self.open_and_write(self.p_name[i],self.get_write_fcts()[i]) def open_and_write(self,name,writefct): """ Open and write file name : name of the output writefct : function used to write the file """ if self.pio=='yes' or mpi.mpi_IsMaster(): f = open(name,'w') else: f = None writefct(f) if self.pio=='yes' or mpi.mpi_IsMaster(): f.close() def get_write_fcts(self): """ returns the functions needed to write a snapshot file. """ return [] def write_num(self,name): """ Write a num file name : name of the output """ if self.pio =='yes': f = open("%s.%d"%(name,mpi.ThisTask),'w') for n in self.num: f.write('%8i\n'%(n)) f.close() else: if mpi.mpi_IsMaster(): f = open(name,'w') for Task in range(mpi.NTask-1,-1,-1): if Task != 0: num = mpi.mpi_recv(source = Task) for n in num: f.write('%8i\n'%(n)) else: for n in self.num: f.write('%8i\n'%(n)) else: mpi.mpi_send(self.num, dest = 0) def read_num(self,name): """ Read a num file name : name of the input """ ################################# # # coordinate transformation # ################################# ################################# # positions ################################# def x(self): """ Return a 1xn float array containing x coordinate """ return self.pos[:,0] def y(self): """ Return a 1xn float array containing y coordinate """ return self.pos[:,1] def z(self): """ Return a 1xn float array containing z coordinate """ return self.pos[:,2] def rxyz(self,center=None): """ Return a 1xn float array that corresponds to the distance from the center of each particle. """ if center!=None: r = sqrt( (self.pos[:,0]-center[0])**2 + (self.pos[:,1]-center[1])**2 + (self.pos[:,2]-center[2])**2 ) else: r = sqrt( self.pos[:,0]**2 + self.pos[:,1]**2 + self.pos[:,2]**2 ) return r def phi_xyz(self): """ Return a 1xn float array that corresponds to the azimuth in spherical coordinate of each particle. """ r = self.rxyz() rxy = self.rxy() xp = self.pos[:,0]*r/rxy # x projection in the plane yp = self.pos[:,1]*r/rxy # y projection in the plane p = arctan2(yp,xp) return p def theta_xyz(self): """ Return a 1xn float array that corresponds to the elevation angle in spherical coordinate of each particle. """ r = self.rxyz() t = arcsin(self.pos[:,2]/r) return t def rxy(self): """ Return a 1xn float array that corresponds to the projected distance from the center of each particle. """ r = sqrt( self.pos[:,0]**2 + self.pos[:,1]**2) return r def phi_xy(self): """ Return a 1xn float array that corresponds to the azimuth in cylindrical coordinate of each particle. """ p = arctan2(self.pos[:,1],self.pos[:,0]) return p r = rxyz R = rxy ###################### # spherical coord ###################### def cart2sph(self,pos=None): """ Transform carthesian coodinates x,y,z into spherical coordinates r,p,t Return a 3xn float array. """ if pos!=None: x = pos[:,0] y = pos[:,1] z = pos[:,2] else: x = self.pos[:,0] y = self.pos[:,1] z = self.pos[:,2] r = self.rxyz() rxy = self.rxy() #xp = x*r/rxy # x projection in the plane #yp = y*r/rxy # y projection in the plane #p = arctan2(yp,xp) #t = arcsin(z/r) p = arctan2(y,x) t = arctan2(rxy,z) return transpose(array([r,p,t])).astype(float32) def sph2cart(self,pos=None): """ Transform spherical coordinates r,p,t into carthesian coodinates x,y,z Return a 3xn float array. """ if pos!=None: r = pos[:,0] p = pos[:,1] t = pos[:,2] else: r = self.pos[:,0] p = self.pos[:,1] t = self.pos[:,2] x = r*sin(t)*cos(p) y = r*sin(t)*sin(p) z = r*cos(t) return transpose(array([x,y,z])).astype(float32) ################################# # velocities ################################# def vx(self): """ Return a 1xn float array containing x velocity """ return self.vel[:,0] def vy(self): """ Return a 1xn float array containing y velocity """ return self.vel[:,1] def vz(self): """ Return a 1xn float array containing z velocity """ return self.vel[:,2] def vn(self): """ Return a 1xn float array that corresponds to the norm of velocities """ return sqrt(self.vel[:,0]*self.vel[:,0] + self.vel[:,1]*self.vel[:,1] + self.vel[:,2]*self.vel[:,2]) def vrxyz(self): """ Return a 1xn float array that corresponds to the radial velocity in spherical system """ r = self.rxyz() return (self.pos[:,0]*self.vel[:,0] + self.pos[:,1]*self.vel[:,1] + self.pos[:,2]*self.vel[:,2])/r def Vr(self): """ Return the radial velocies of particles The output is an 3xn float array. """ xr = sqrt(self.pos[:,0]**2+self.pos[:,1]**2) vr = (self.pos[:,0]*self.vel[:,0] + self.pos[:,1]*self.vel[:,1]) / xr return vr def Vt(self): """ Return the tangential velocies of particles The output is an 3xn float array. """ xr = sqrt(self.pos[:,0]**2+self.pos[:,1]**2) vt = (self.pos[:,0]*self.vel[:,1] - self.pos[:,1]*self.vel[:,0]) / xr return vt def Vz(self): """ Return a 1xn float array containing z velocity """ return self.vel[:,2] ###################### # cylindrical coord ###################### def vel_cyl2cart(self,pos=None,vel=None): """ Transform velocities in cylindrical coordinates vr,vt,vz into carthesian coodinates vx,vy,vz. Pos is the position of particles in cart. coord. Vel is the velocity in cylindrical coord. Return a 3xn float array. """ return vel_cyl2cart(self.pos,self.vel) def vel_cart2cyl(self): """ Transform velocities in carthesian coordinates vx,vy,vz into cylindrical coodinates vr,vz,vz. Pos is the position of particles in cart. coord. Vel is the velocity in cart. coord. Return a 3xn float array. """ return vel_cart2cyl(self.pos,self.vel) ################################# # # physical values # ################################# def get_ns(self): """ Return in an array the number of particles of each node. """ ns = mpi.mpi_allgather(self.nbody) return ns def get_mass_tot(self): """ Return the total mass of system. """ mass_tot = mpi.mpi_sum(self.mass) return mass_tot def size(self): """ Estimate the model size, using the inertial momentum """ return max(self.minert()) def cm(self): """ Return the mass center of the model. The output is an 3x1 float array. """ mtot = mpi.mpi_sum(self.mass.astype(FLOAT)) cmx = mpi.mpi_sum(self.pos[:,0].astype(float64)*self.mass.astype(FLOAT)) / mtot cmy = mpi.mpi_sum(self.pos[:,1].astype(float64)*self.mass.astype(FLOAT)) / mtot cmz = mpi.mpi_sum(self.pos[:,2].astype(float64)*self.mass.astype(FLOAT)) / mtot return array([cmx,cmy,cmz]) def get_histocenter(self,rbox=50,nb=500): """ Return the position of the higher density region in x,y,z (not good) found by the function "histocenter". rbox : size of the box nb : number of bins in each dimension """ rm = rbox/2. bins = arange(-rm,rm,float(2*rm)/float(nb)) # histograms in x,y,z (cut the tail) hx = mpi.mpi_histogram(self.pos[:,0],bins)[:-1] hy = mpi.mpi_histogram(self.pos[:,1],bins)[:-1] hz = mpi.mpi_histogram(self.pos[:,2],bins)[:-1] # max in each dim dx = bins[argmax(hx)] dy = bins[argmax(hy)] dz = bins[argmax(hz)] return array([dx,dy,dz]) def get_histocenter2(self,rbox=50,nb=64): """ Return the position of the higher density region in x,y,z (not good) found by the function "histocenter". rbox : size of the box nb : number of bins in each dimension """ # transformation -rbox->0, 0->nb/2, rbox->nb # y = (x+rbox)/(2*rbox)*nb pos = ( self.pos + [rbox,rbox,rbox] )/(2*rbox) # 0 to 1 pos = pos*[nb,nb,nb] # 0 to nb pos = pos.astype(float32) mass = self.mass.astype(float32) mat = mkmap3d(pos/nb,mass,mass,(nb,nb,nb)) # find max m = ravel(mat) arg = argmax(m) i = indices((nb,nb,nb)) # not that good ix = ravel(i[0]) # not that good iy = ravel(i[1]) # not that good iz = ravel(i[2]) # not that good ix = ix[arg] iy = iy[arg] iz = iz[arg] # transformation inverse # x = 2*rbox*(y/nb)-rbox dx = 2*rbox*(float(ix)/nb)-rbox dy = 2*rbox*(float(iy)/nb)-rbox dz = 2*rbox*(float(iz)/nb)-rbox return array([dx,dy,dz]) def cv(self): """ Return the center of the velocities of the model. The output is an 3x1 float array. """ cmx = mpi.mpi_sum(self.vel[:,0]*self.mass) / self.mass_tot cmy = mpi.mpi_sum(self.vel[:,1]*self.mass) / self.mass_tot cmz = mpi.mpi_sum(self.vel[:,2]*self.mass) / self.mass_tot return array([cmx,cmy,cmz]) def minert(self): """ Return the diagonal of the intertial momentum. """ mx = mpi.mpi_sum(self.pos[:,0]**2 *self.mass) / self.mass_tot my = mpi.mpi_sum(self.pos[:,1]**2 *self.mass) / self.mass_tot mz = mpi.mpi_sum(self.pos[:,2]**2 *self.mass) / self.mass_tot mx = sqrt(mx) my = sqrt(my) mz = sqrt(mz) return array([mx,my,mz]) def inertial_tensor(self): """ Return the inertial tensor. """ Ixx = mpi.mpi_sum(self.mass * (self.y()**2 + self.z()**2)) Iyy = mpi.mpi_sum(self.mass * (self.x()**2 + self.z()**2)) Izz = mpi.mpi_sum(self.mass * (self.x()**2 + self.y()**2)) Ixy = -mpi.mpi_sum(self.mass * self.x() * self.y()) Ixz = -mpi.mpi_sum(self.mass * self.x() * self.z()) Iyz = -mpi.mpi_sum(self.mass * self.y() * self.z()) I = array( [[Ixx,Ixy,Ixz],[Ixy,Iyy,Iyz],[Ixz,Iyz,Izz]] ) return I def x_sigma(self): """ Return the norm of the position dispersions. """ x = (self.pos - self.cm()) x2 = x[:,0]**2 + x[:,1]**2 + x[:,2]**2 x_s2 = mpi.mpi_sum( x2 *self.mass )/self.mass_tot x_s = sqrt(x_s2) return x_s def v_sigma(self): """ Return the norm of the velocity dispersions. """ v = (self.vel - self.cv()) v2 = v[:,0]**2 + v[:,1]**2 + v[:,2]**2 v_s2 = mpi.mpi_sum( v2 *self.mass )/self.mass_tot v_s = sqrt(v_s2) return v_s def dx_mean(self): """ Return the average distance between particles. """ # 1) estimate the size of the system D = self.x_sigma() # 2) estimate the # of particules per unit volume n = self.nbody_tot/D # 3) estimate the average distance between particules l = 1./n**(1./3.) return l def dv_mean(self): """ Return the average relative speed between particles. """ # 1) estimate the size of the system D = self.v_sigma() # 2) estimate the # of particules per unit volume n = self.nbody_tot/D # 3) estimate the average distance between particules l = 1./n**(1./3.) return l def Ekin(self): """ Return the total kinetic energy """ E = 0.5 * mpi.mpi_sum (self.mass * (self.vel[:,0]**2 + self.vel[:,1]**2 + self.vel[:,2]**2) ) return E def ekin(self): """ Return the total specific kinetic energy """ E = 0.5 * mpi.mpi_sum ( (self.vel[:,0]**2 + self.vel[:,1]**2 + self.vel[:,2]**2) ) return E def Epot(self,eps): """ Return the total potential energy using the softening lenght eps. eps : softening WARNING : THIS FUNCTION DO NOT WORK IN MPI MODE """ E = epot(self.pos,self.mass,eps) return E def epot(self,eps): """ Return the total specific potential energy using the softening lenght eps. eps : softening WARNING : THIS FUNCTION DO NOT WORK IN MPI MODE """ e = epot(self.pos,self.mass,eps)/self.mass_tot return e def L(self): """ Return the angular momentum in x,y,z of all particles. The output is an 3xn float array. """ l = amxyz(self.pos,self.vel,self.mass) return l def l(self): """ Return the specific angular momentum in x,y,z of all particles. The output is an 3xn float array. """ l = samxyz(self.pos,self.vel,self.mass) return l def Ltot(self): """ Return the total angular momentum. The output is an 3x1 float array. """ l = mpi.mpi_allreduce (am(self.pos,self.vel,self.mass)) #l = mpi.mpi_sum(self.L()) return l def ltot(self): """ Return the specific total angular momentum. The output is an 3x1 float array. """ l = mpi.mpi_allreduce (am(self.pos,self.vel,self.mass))/self.mass_tot #l = self.Ltot()/self.mass_tot return l def Pot(self,x,eps): """ Return the potential at a given position, using the softening lenght eps. x : position (array vector) eps : softening """ if type(x) == ndarray: p = zeros(len(x),float32) for i in range(len(x)): p[i] = mpi.mpi_allreduce ( potential(self.pos,self.mass,array(x[i],float32),eps) ) else: p = mpi.mpi_allreduce ( potential(self.pos,self.mass,array(x,float32),eps) ) return p def TreePot(self,pos,eps,Tree=None): """ Return the potential at a given position, using the softening lenght eps and using a tree. pos : position (array vector) eps : softening Tree: gravitational tree if already computed WARNING : this function do not work in parallel """ if Tree==None: self.Tree = Tree = self.getTree() pot = Tree.Potential(pos,eps) return pot def Accel(self,x,eps): """ Return the acceleration at a given position, using the softening lenght eps. x : position (array vector) eps : softening """ if type(x) == ndarray: ax = zeros(len(x),float32) ay = zeros(len(x),float32) az = zeros(len(x),float32) for i in range(len(x)): ax[i],ay[i],az[i] = acceleration(self.pos,self.mass,array(x[i],float32),eps) a = transpose(array([ax,ay,az],float32)) else: ax,ay,az = acceleration(self.pos,self.mass,array(x,float32),eps) ax = mpi.mpi_allreduce ( ax ) ay = mpi.mpi_allreduce ( ay ) az = mpi.mpi_allreduce ( az ) a = array([ax,ay,az],float32) return a def TreeAccel(self,pos,eps,Tree=None): """ Return the acceleration at a given position, using the softening lenght eps and using a tree. pos : position (array vector) eps : softening Tree: gravitational tree if already computed WARNING : this function do not work in parallel """ if Tree==None: self.Tree = Tree = self.getTree() acc = Tree.Acceleration(pos,eps) return acc def tork(self,acc): """ Return the total tork on the system due to the force acting on each particle (acc). The output is an 3xn float array. acc : 3xn float array """ trk = mpi.mpi_allreduce ( am(self.pos,array(acc,float32),self.mass) ) return trk def dens(self,r=None,nb=25,rm=50): """ Return the number density at radius r (supposing a spherical density distribution). If r is not specified, it is computed with nb and rm. The output is an n x 1 float array. !!! This routine do not use masses !!! r : radius nb : number of bins (size of the output) rm : maximal radius """ if r!= None: r = array(r,float) else: rmax = rm dr = rm/float(nb) r = arange(0.,rm,dr) xr = sqrt(self.pos[:,0]**2 + self.pos[:,1]**2 + self.pos[:,2]**2) dens,r = histogram(xr,r) r1 = r[:-1] r2 = r[1:] dv = (4./3.)*pi*(r2**3-r1**3) dens = dens/dv # surface density # take the mean r = (r1+r2)/2 return r,mpi.mpi_allreduce(dens) def mdens(self,r=None,nb=25,rm=50): """ Return the density at radius r (supposing a spherical density distribution). If r is not specified, it is computed with nb and rm. The output is an n x 1 float array. r : radius nb : number of bins (size of the output) rm : maximal radius """ if r!= None: r = array(r,float) else: rmax = rm dr = rm/float(nb) r = arange(0.,rm,dr) xr = sqrt(self.pos[:,0]**2 + self.pos[:,1]**2 + self.pos[:,2]**2) dens = whistogram(xr.astype(float),self.mass.astype(float),r.astype(float)) r1 = r[:-1] r2 = r[1:] dv = (4./3.)*pi*(r2**3-r1**3) dens = dens[:-1]/dv # surface density # take the mean r = (r1+r2)/2 return r,mpi.mpi_allreduce(dens) def mr(self,r=None,nb=25,rm=50): """ Return the mass inside radius r (supposing a spherical density distribution). If r is not specified, it is computed with nb and rm. The output is an n x 1 float array. r : radius nb : number of bins (size of the output) rm : maximal radius """ if r!= None: r = array(r,float) else: rmax = rm dr = rm/float(nb) r = arange(0.,rm,dr) xr = self.rxyz() mr = whistogram(xr.astype(float),self.mass.astype(float),r.astype(float)) mr = add.accumulate(mr) return r,mpi.mpi_allreduce(mr) def Mr_Spherical(self,nr=25,rmin=0,rmax=50): """ Return the mass inside radius r (supposing a spherical density distribution). The output is 2 n x 1 float arrays. nr : number of bins (size of the output) rmin : minimal radius (this must be zero, instead it is wrong...) rmax : maximal radius """ rmin = float(rmin) rmax = float(rmax) shape = (nr,) val = ones(self.pos.shape).astype(float32) mass = self.mass.astype(float32) r = self.rxyz() r = (r-rmin)/(rmax-rmin) r = r.astype(float32) # compute the map mr = mkmap1d(r,mass,val,shape).astype(float) # compute the radii rs = arange(0.,rmax,(rmax-rmin)/nr) # sum mr = add.accumulate(mr) return rs,mpi.mpi_allreduce(mr) def sdens(self,r=None,nb=25,rm=50): """ Return the surface density at radius r. If r is not specified, it is computed with nb and rm. The output is an nx1 float array. !!! This routine do not uses masses !!! r : radius nb : number of bins (size of the output) rm : maximal radius """ if r!= None: r = array(r,float) else: rmax = rm dr = rm/float(nb) r = arange(0.,rm,dr) xr = sqrt(self.pos[:,0]**2 + self.pos[:,1]**2) sdens,r = histogram(xr,r) r1 = r[:-1] r2 = r[1:] ds = pi*(r2**2-r1**2) sdens = sdens/ds # surface density # take the mean r = (r1+r2)/2. return r,mpi.mpi_allreduce(sdens) def msdens(self,r=None,nb=25,rm=50): """ Return the mass surface density at radius r. If r is not specified, it is computed with nb and rm. The output is an nx1 float array. r : radius nb : number of bins (size of the output) rm : maximal radius """ if r!= None: r = array(r,float) else: rmax = rm dr = rm/float(nb) r = arange(0.,rm,dr) xr = sqrt(self.pos[:,0]**2 + self.pos[:,1]**2) sdens = whistogram(xr.astype(float),self.mass.astype(float),r.astype(float)) r1 = r[:-1] r2 = r[1:] ds = pi*(r2**2-r1**2) sdens = sdens[:-1]/ds # surface density # take the mean r = (r1+r2)/2. return r,mpi.mpi_allreduce(sdens) def sigma_z(self,r=None,nb=25,rm=50): """ Return the vertical dispertion in z at radius r. If r is not specified, it is computed with nb and rm. The output is an nx1 float array. r : radius nb : number of bins (size of the output) rm : maximal radius """ if r!= None: r = array(r,float) else: rmax = rm dr = rm/float(nb) r = arange(0.,rm,dr) r,h = self.Histo(r,mode='sz') return r,h def sigma_vz(self,r=None,nb=25,rm=50): """ Return the vertical dispertion in z at radius r. If r is not specified, it is computed with nb and rm. The output is an nx1 float array. r : radius nb : number of bins (size of the output) rm : maximal radius """ if r!= None: r = array(r,float) else: rmax = rm dr = rm/float(nb) r = arange(0.,rm,dr) r,h = self.Histo(r,mode='svz') return r,h def zprof(self,z=None,r=2.5,dr=0.5,nb=25,zm=5.): """ Return the z-profile in a vector for a given radius !!! This routine works only if particles have equal masses !!! z : bins in z (optional) r : radius of the cut dr : width in r of the cut nb : number of bins (size of the output) zm : maximal height """ if z!= None: pass else: zmax = zm dz = 2.*zm/float(nb) z = arange(-zm,zm,dz) # select r1 = r-dr/2. r2 = r+dr/2. ann = self.selectc((self.rxy()>r1)*((self.rxy()r1[i])*((self.rxy() 1: sr.append(vr.std()) st.append(vt.std()) sz.append(vz.std()) mt.append(vt.mean()) else: sr.append(0.) st.append(0.) sz.append(0.) mt.append(0.) sr = array(sr,float) st = array(st,float) sz = array(sz,float) mt = array(mt,float) return r,sr,st,sz,mt def histovel(self,nb=100,vmin=None,vmax=None,mode='n'): """ Return or plot the histrogram of the norm of velocities or of the radial velocities. The output is a list (r,h) of 2 nx1 float arrays, where r is the radius and h the values of the histogram. nb : number of bins (size of the output) vmax : maximum velocity vmin : minimum velocity mode : 'n' (norme of the velocities) 'r' (radial velocities) """ if mode == 'r': v = (self.pos[:,0]*self.vel[:,0] + self.pos[:,1]*self.vel[:,1]) / sqrt(self.pos[:,0]**2+self.pos[:,1]**2) elif mode == 'n': v = sqrt(self.vel[:,0]**2 + self.vel[:,1]**2 + self.vel[:,2]**2) if vmax == None : vmax = mpi.mpi_max(v) if vmin == None : vmin = mpi.mpi_min(v) bins = arange(vmin,vmax,(vmax-vmin)/float(nb)) h = mpi.mpi_histogram(v,bins) return h,bins def zmodes(self,nr=32,nm=16,rm=32): """ Compute the vertical modes of a model nm = 16 : number of modes nr = 32 : number of radius rm = 50 : max radius return r : the radius used m : the modes computed m1 : the matrix of the amplitude m2 : the matrix of the phases """ ps = arange(-pi,pi+pi/(2.*nm),2*pi/(2.*nm))+pi # phases R = arange(0,nr+1,1)*float(rm)/nr # radius Rs = array([],float) r = self.rxy() m1 = array([],float) m2 = array([],float) # loop over all radius for i in range(len(R)-1): c = (r>=R[i])*(r<=R[i+1]) an = self.selectc(c) if sum(c.astype(int))<=1: #print "less than 1 particle in the coupe",R[i] amp = zeros(len(ps)/2).astype(float) m1 = concatenate((m1,amp)) m2 = concatenate((m2,amp)) continue x = an.pos[:,0] y = an.pos[:,1] z = an.pos[:,2] t = arctan2(y,x)+pi zm = [] ok = 0 for j in range(len(ps)-1): c = (t>=ps[j])*(t=R[i])*(r<=R[i+1]) an = self.selectc(c) if sum(c.astype(int))<=1: #print "less than 1 particle in the coupe",R[i] amp = zeros(len(ps)/2).astype(float) m1 = concatenate((m1,amp)) m2 = concatenate((m2,amp)) continue x = an.pos[:,0] y = an.pos[:,1] z = an.pos[:,2] t = arctan2(y,x)+pi dm = [] ok = 0 for j in range(len(ps)-1): c = (t>=ps[j])*(t [0,boxsize] centred : -> [-boxsize/2,boxsize/2] [x,y,z] : """ if boxsize==None: if self.has_var('boxsize'): boxsize = self.boxsize if boxsize != None: if mode == None: - - self.pos[:,0] = where((self.pos[:,0]<0.0 ),self.pos[:,0]+boxsize,self.pos[:,0]) - self.pos[:,1] = where((self.pos[:,1]<0.0 ),self.pos[:,1]+boxsize,self.pos[:,1]) - self.pos[:,2] = where((self.pos[:,2]<0.0 ),self.pos[:,2]+boxsize,self.pos[:,2]) - - self.pos[:,0] = where((self.pos[:,0]>boxsize),self.pos[:,0]-boxsize,self.pos[:,0]) - self.pos[:,1] = where((self.pos[:,1]>boxsize),self.pos[:,1]-boxsize,self.pos[:,1]) - self.pos[:,2] = where((self.pos[:,2]>boxsize),self.pos[:,2]-boxsize,self.pos[:,2]) + + if string.find(axis,'x')!=-1: + self.pos[:,0] = where((self.pos[:,0]<0.0 ),self.pos[:,0]+boxsize,self.pos[:,0]) + if string.find(axis,'y')!=-1: + self.pos[:,1] = where((self.pos[:,1]<0.0 ),self.pos[:,1]+boxsize,self.pos[:,1]) + if string.find(axis,'z')!=-1: + self.pos[:,2] = where((self.pos[:,2]<0.0 ),self.pos[:,2]+boxsize,self.pos[:,2]) + + if string.find(axis,'x')!=-1: + self.pos[:,0] = where((self.pos[:,0]>boxsize),self.pos[:,0]-boxsize,self.pos[:,0]) + if string.find(axis,'y')!=-1: + self.pos[:,1] = where((self.pos[:,1]>boxsize),self.pos[:,1]-boxsize,self.pos[:,1]) + if string.find(axis,'z')!=-1: + self.pos[:,2] = where((self.pos[:,2]>boxsize),self.pos[:,2]-boxsize,self.pos[:,2]) elif mode=='centred': - - self.pos[:,0] = where((self.pos[:,0]<=-boxsize/2.),self.pos[:,0]+boxsize,self.pos[:,0]) - self.pos[:,1] = where((self.pos[:,1]<=-boxsize/2.),self.pos[:,1]+boxsize,self.pos[:,1]) - self.pos[:,2] = where((self.pos[:,2]<=-boxsize/2.),self.pos[:,2]+boxsize,self.pos[:,2]) - - self.pos[:,0] = where((self.pos[:,0]> boxsize/2.),self.pos[:,0]-boxsize,self.pos[:,0]) - self.pos[:,1] = where((self.pos[:,1]> boxsize/2.),self.pos[:,1]-boxsize,self.pos[:,1]) - self.pos[:,2] = where((self.pos[:,2]> boxsize/2.),self.pos[:,2]-boxsize,self.pos[:,2]) + + if string.find(axis,'x')!=-1: + self.pos[:,0] = where((self.pos[:,0]<=-boxsize/2.),self.pos[:,0]+boxsize,self.pos[:,0]) + if string.find(axis,'y')!=-1: + self.pos[:,1] = where((self.pos[:,1]<=-boxsize/2.),self.pos[:,1]+boxsize,self.pos[:,1]) + if string.find(axis,'z')!=-1: + self.pos[:,2] = where((self.pos[:,2]<=-boxsize/2.),self.pos[:,2]+boxsize,self.pos[:,2]) + + if string.find(axis,'x')!=-1: + self.pos[:,0] = where((self.pos[:,0]> boxsize/2.),self.pos[:,0]-boxsize,self.pos[:,0]) + if string.find(axis,'y')!=-1: + self.pos[:,1] = where((self.pos[:,1]> boxsize/2.),self.pos[:,1]-boxsize,self.pos[:,1]) + if string.find(axis,'z')!=-1: + self.pos[:,2] = where((self.pos[:,2]> boxsize/2.),self.pos[:,2]-boxsize,self.pos[:,2]) elif (type(mode)==ndarray) or (type(mode)==types.ListType): - self.pos[:,0] = where((self.pos[:,0]<=mode[0]-boxsize/2.),self.pos[:,0]+boxsize,self.pos[:,0]) - self.pos[:,1] = where((self.pos[:,1]<=mode[1]-boxsize/2.),self.pos[:,1]+boxsize,self.pos[:,1]) - self.pos[:,2] = where((self.pos[:,2]<=mode[2]-boxsize/2.),self.pos[:,2]+boxsize,self.pos[:,2]) - - self.pos[:,0] = where((self.pos[:,0]> mode[0]+boxsize/2.),self.pos[:,0]-boxsize,self.pos[:,0]) - self.pos[:,1] = where((self.pos[:,1]> mode[1]+boxsize/2.),self.pos[:,1]-boxsize,self.pos[:,1]) - self.pos[:,2] = where((self.pos[:,2]> mode[2]+boxsize/2.),self.pos[:,2]-boxsize,self.pos[:,2]) + if string.find(axis,'x')!=-1: + self.pos[:,0] = where((self.pos[:,0]<=mode[0]-boxsize/2.),self.pos[:,0]+boxsize,self.pos[:,0]) + if string.find(axis,'y')!=-1: + self.pos[:,1] = where((self.pos[:,1]<=mode[1]-boxsize/2.),self.pos[:,1]+boxsize,self.pos[:,1]) + if string.find(axis,'z')!=-1: + self.pos[:,2] = where((self.pos[:,2]<=mode[2]-boxsize/2.),self.pos[:,2]+boxsize,self.pos[:,2]) + + if string.find(axis,'x')!=-1: + self.pos[:,0] = where((self.pos[:,0]> mode[0]+boxsize/2.),self.pos[:,0]-boxsize,self.pos[:,0]) + if string.find(axis,'y')!=-1: + self.pos[:,1] = where((self.pos[:,1]> mode[1]+boxsize/2.),self.pos[:,1]-boxsize,self.pos[:,1]) + if string.find(axis,'z')!=-1: + self.pos[:,2] = where((self.pos[:,2]> mode[2]+boxsize/2.),self.pos[:,2]-boxsize,self.pos[:,2]) def rotate_old(self,angle=0,mode='a',axis='x'): """ Rotate the positions and/or the velocities of the object around a specific axis. angle : rotation angle in radian axis : 'x' : around x 'y' : around y 'z' : around z : [x,y,z] : around this axis mode : 'p' : rotate only position 'v' : rotate only velocities 'a' : rotate both (default) """ if type(axis) == type('a'): # rotate around x,y or z if axis =='x': if mode=='p' or mode=='a': self.pos=rotx(angle,self.pos) if mode=='v' or mode=='a': self.vel=rotx(angle,self.vel) elif axis =='y': if mode=='p' or mode=='a': self.pos=roty(angle,self.pos) if mode=='v' or mode=='a': self.vel=roty(angle,self.vel) elif axis =='z': if mode=='p' or mode=='a': self.pos=rotz(angle,self.pos) if mode=='v' or mode=='a': self.vel=rotz(angle,self.vel) else: # rotate around a given axis # construction of the rotation matrix nxy = sqrt(axis[0]**2+axis[1]**2) theta_x = angle theta_z = 2.*pi - arctan2(axis[1],axis[0]) theta_y = arctan2(axis[2],nxy) if mode=='p' or mode=='a': # rot in z self.pos=rotz(theta_z,self.pos) # rot in y self.pos=roty(theta_y,self.pos) # rot in x self.pos=rotx(theta_x,self.pos) # rot in -y self.pos=roty(-theta_y,self.pos) # rot in -z self.pos=rotz(-theta_z,self.pos) if mode=='v' or mode=='a': # rot in z self.vel=rotz(theta_z,self.vel) # rot in y self.vel=roty(theta_y,self.vel) # rot in x self.vel=rotx(theta_x,self.vel) # rot in -y self.vel=roty(-theta_y,self.vel) # rot in -z self.vel=rotz(-theta_z,self.vel) def rotate(self,angle=0,axis=[1,0,0],point=[0,0,0],mode='a'): """ Rotate the positions and/or the velocities of the object around a specific axis defined by a vector and an point. angle : rotation angle in radian axis : direction of the axis point : center of the rotation mode : 'p' : rotate only position 'v' : rotate only velocities 'a' : rotate both (default) """ if axis=='x': axis = array([1,0,0],float) elif axis=='y': axis = array([0,1,0],float) elif axis=='z': axis = array([0,0,1],float) x = self.pos v = self.vel # center point x = x-point # construction of the rotation matrix norm = sqrt(axis[0]**2 + axis[1]**2 + axis[2]**2) if norm == 0: return x sn = sin(-angle/2.) e0 = cos(-angle/2.) e1 = axis[0]*sn/norm e2 = axis[1]*sn/norm e3 = axis[2]*sn/norm a = zeros((3,3),float) a[0,0] = e0**2 + e1**2 - e2**2 - e3**2 a[1,0] = 2.*(e1*e2 + e0*e3) a[2,0] = 2.*(e1*e3 - e0*e2) a[0,1] = 2.*(e1*e2 - e0*e3) a[1,1] = e0**2 - e1**2 + e2**2 - e3**2 a[2,1] = 2.*(e2*e3 + e0*e1) a[0,2] = 2.*(e1*e3 + e0*e2) a[1,2] = 2.*(e2*e3 - e0*e1) a[2,2] = e0**2 - e1**2 - e2**2 + e3**2 a = a.astype(float) # multiply x and v if mode=='p': x = dot(x,a) elif mode=='v': v = dot(v,a) else: x = dot(x,a) v = dot(v,a) # decenter point x = x+point self.pos = x.astype(float32) self.vel = v.astype(float32) def rotateR(self,R,mode='a'): """ Rotate the model using the matrix R mode : 'p' : only position 'v' : only velocities 'a' : both (default) """ # multiply x and v if mode=='p': self.pos = dot(self.pos,R) elif mode=='v': self.vel = dot(self.vel,R) else: self.pos = dot(self.pos,R) self.vel = dot(self.vel,R) def get_rotation_matrix_to_align_with_main_axis(self): """ Get the rotation matrix used to rotate the object in order to align it's main axis with the axis of its inertial tensor. """ # compute inertial tensor I = self.inertial_tensor() # find eigen values and vectors val,vec =linalg.eig(I) l1 = val[0] l2 = val[1] l3 = val[2] a1 = vec[:,0] a2 = vec[:,1] a3 = vec[:,2] # find Rm such that Rm*1,0,0 = a1 # that Rm*0,1,0 = a2 # that Rm*0,0,1 = a3 Rm = transpose(array([a1,a2,a3])) return Rm def align_with_main_axis(self,mode='a'): """ Rotate the object in order to align it's major axis with the axis of its inertial tensor. mode : 'p' : only position 'v' : only velocities 'a' : both (default) """ # find rotation matrix R = self.get_rotation_matrix_to_align_with_main_axis() # apply it self.rotateR(R,mode) def align(self,axis,mode='a',sgn='+',fact=None): """ Rotate the object in order to align the axis 'axis' with the z axis. axis : [x,y,z] mode : 'p' : only position 'v' : only velocities 'a' : both (default) sgn : '+' : normal rotation '-' : reverse sense of rotation fact : int : factor to increase the angle """ n = [axis[1], -axis[0],0.] theta = arccos(axis[2]/sqrt(axis[0]**2+axis[1]**2+axis[2]**2)) if sgn =='-': theta = -theta if fact != None: theta = theta*fact self.rotate(angle=theta,mode=mode,axis=n) def align2(self,axis1=[1,0,0],axis2=[0,0,1],point=[0,0,0]): ''' Rotate the object in order to align the axis 'axis' with the z axis. axis1 : [x,y,z] axis2 : [x,y,z] point : [x,y,z] ''' a1 = array(axis1,float) a2 = array(axis2,float) a3 = array([0,0,0],float) a3[0] = a1[1]*a2[2] - a1[2]*a2[1] a3[1] = a1[2]*a2[0] - a1[0]*a2[2] a3[2] = a1[0]*a2[1] - a1[1]*a2[0] n1 = sqrt(a1[0]**2 + a1[1]**2 + a1[2]**2) n2 = sqrt(a2[0]**2 + a2[1]**2 + a2[2]**2) angle = arccos(inner(a1,a2)/(n1*n2)) self.rotate(angle=angle,axis=a3,point=point) def spin(self,omega=None,L=None,j=None,E=None): """ Spin the object with angular velocity "omega" (rigid rotation). Omega is a 1 x 3 array object If L (total angular momentum) is explicitely given, compute Omega from L (1 x 3 array object). omega : angular speed (array vector) L : desired angular momentum j : desired energy fraction in rotation E : Total energy (without rotation) """ # do nothing if L==None and omega==None and j==None: pass # use j and E (spin around z axis) if j!=None: if E==None: "spin : print you must give E" else: if (j > 1): self.log.write("spin : j must be less than 1") sys.exit() Erot = j*E/(1-j) omega = sqrt(2*Erot/mpi_sum(self.mass*self.rxy()**2) ) omega = array([0,0,omega],float32) self.vel=spin(self.pos,self.vel,omega) # use omega elif L==None and omega!=None: omega = array(omega,float32) self.vel=spin(self.pos,self.vel,omega) # use L # Pfenniger 93 elif L!=None: L = array(L,float32) aamx = L[0] aamy = L[1] aamz = L[2] x = self.pos[:,0] y = self.pos[:,1] z = self.pos[:,2] vx = self.vel[:,0] vy = self.vel[:,1] vz = self.vel[:,2] m = self.mass Ixy = sum(m*x*y) Iyz = sum(m*y*z) Izx = sum(m*z*x) Ixx = sum(m*x*x) Iyy = sum(m*y*y) Izz = sum(m*z*z) Axx = Iyy+Izz Ayy = Izz+Ixx Azz = Ixx+Iyy Axy = -Ixy Ayz = -Iyz Azx = -Izx D = Axx*Ayy*Azz + 2*Axy*Ayz*Azx - Axx*Ayz**2 - Ayy*Azx**2 - Azz*Axy**2 DLX = sum(m*(y*vz-z*vy))-aamx DLY = sum(m*(z*vx-x*vz))-aamy DLZ = sum(m*(x*vy-y*vx))-aamz Bxx = Ayy*Azz - Ayz**2 Byy = Azz*Axx - Azx**2 Bzz = Axx*Ayy - Axy**2 Bxy = Azx*Ayz - Axy*Azz Byz = Axy*Azx - Ayz*Axx Bzx = Ayz*Axy - Azx*Ayy omega = array([0,0,0],float32) omega[0] = -(Bxx*DLX + Bxy*DLY + Bzx*DLZ)/D omega[1] = -(Bxy*DLX + Byy*DLY + Byz*DLZ)/D omega[2] = -(Bzx*DLX + Byz*DLY + Bzz*DLZ)/D self.vel=spin(self.pos,self.vel,omega) ################################# # # selection of particles # ################################# def selectc(self,c,local=False): """ Return an N-body object that contain only particles where the corresponding value in c is not zero. c is a nx1 Nbody array. c : the condition vector local : local selection (True) or global selection (False) """ new = Nbody(status='new',ftype=self.ftype[6:],local=local) # now, copy all var linked to the model for name in self.get_list_of_vars(): setattr(new, name, getattr(self,name)) # here, we create ptype on the fly (used to create new.npart) #self.ptype = array([],int) #for i in range(len(self.npart)): # self.ptype = concatenate( (self.ptype,ones(self.npart[i])*i) ) # now, copy and compress all array linked to the model for name in self.get_list_of_array(): vec = getattr(self,name) setattr(new, name, compress(c,vec,axis=0)) # now, compute new.npart #new.npart = array([],int) #for i in range(len(self.npart)): # c = (new.tpe==i) # npart_i = sum(c.astype(int)) # new.npart = concatenate( (new.npart, npart_i ) ) # check #if len(new.pos)!= sum(new.npart): # pass # other vars new.init() return new def selecti(self,i,local=False): """ Return an N-body object that contain only particles having their index (not id) in i. i : vector containing indexes local : local selection (True) or global selection (False) """ new = Nbody(status='new',ftype=self.ftype[6:],local=local) # now, copy all var linked to the model for name in self.get_list_of_vars(): setattr(new, name, getattr(self,name)) # here, we create ptype on the fly (used to create new.npart) #self.ptype = array([],int) #for i in range(len(self.npart)): # self.ptype = concatenate( (self.ptype,ones(self.npart[i])*i) ) # now, copy and compress all array linked to the model for name in self.get_list_of_array(): vec = getattr(self,name) setattr(new, name, vec[i]) # now, compute new.npart #new.npart = array([],int) #for i in range(len(self.npart)): # c = (new.tpe==i) # npart_i = sum(c.astype(int)) # new.npart = concatenate( (new.npart, npart_i ) ) # check #if len(new.pos)!= sum(new.npart): # pass # other vars new.init() return new def select(self,i=0): """ Return an N-body object that contain only particles of type i """ import types if type(i)== int: return self.selectc(self.tpe==i) elif type(i)==types.ListType: types = i for j in types: c = c * (j==self.tpe) return self.selectc(c) else: return self def sub(self,n1=0,n2=None): """ Return an N-body object that have particles whith indicies in the range [n1:n2]. n1 : number of the first particule n2 : number of the last particule Note : the first particle is 0 """ if n1 == None: n1 = 0 if n2 == None: n2 = self.nbody if n2 <= n1: n2 = n1+1 num = arange(self.nbody) return self.selectc((num>=n1)*(num<=n2)) def reduc(self,n,mass=False): """ Return an N-body object that contain a fraction 1/n of particles. n : inverse of the fraction of particule to be returned """ c = where(fmod(arange(self.nbody),n).astype(int)==0,1,0) nb = self.selectc(c) if mass: nb.mass = nb.mass * n return nb def selectp(self,lst=None,file=None,reject=False,local=False,from_num=True): """ Return an N-body object that contain only particles with specific number id. The list of id's is given either by lst (nx1 int array) or by the name ("file") of a file containing the list of id's. lst : vector list (integer) reject : True/False : if True, reject particles in lst (default = False) local : local selection (True) or global selection (False) frum_num : if True, use self.num to select particules if False, use arange(self.nbody) """ if lst != None: lst = array(lst,int) if file != None: lst = [] f = open(file) while 1: try: lst.append(int(string.split(f.readline())[0])) except: break f.close() lst = array(lst,int) # 1) sort the list ys = sort(lst) # 2) sort index in current file if from_num: xs = sort(self.num) zs = take(arange(self.nbody),argsort(self.num)) # sort 0,1,2,n following xs (or self.num) else: xs = arange(self.nbody) # 3) apply mask on sorted lists (here, getmask need xs and ys to be sorted) m = getmask(xs.astype(int),ys.astype(int)) if reject: m = logical_not(m) # 4) revert mask, following zs inverse transformation if from_num: c = take(m,argsort(zs)) else: c = m new = self.selectc(c,local=local) return new def getindex(self,num): """ Return an array of index of a particle from its specific number id. The array is empty if no particle corresponds to the specific number id. num : Id of the particle """ idx = compress((self.num == num),arange(self.nbody)) if len(idx)==1: return idx[0] else: return idx ################################# # # add particles # ################################# def append(self,solf,do_not_sort=False): """ Add to the current N-body object, particles form the N-body object "new". solf : Nbody object """ if solf.ftype != self.ftype: raise "append Error","files have different type" return if solf.get_list_of_array() != self.get_list_of_array(): raise "append Error","files have different arrays" return # loop over all types self_npart = self.npart solf_npart = solf.npart if len(self_npart) != len(self_npart): print "append : files have different mxnpart !" sys.exit() # add array linked to the model names = self.get_list_of_array() for name in names: vec1 = getattr(self,name) vec2 = getattr(solf,name) ''' vec = array([],float32) if vec1.ndim == 1: vec.shape = (0,) else: vec.shape = (0,3) # here, we guarantee the order of particles according to npart for i in arange(len(self_npart)): e11 = sum((arange(len(self_npart)) < i) * self_npart,0) e21 = sum((arange(len(solf_npart)) < i) * solf_npart,0) vec = concatenate((vec,vec1[e11:e11+self_npart[i]],vec2[e21:e21+solf_npart[i]])) ''' vec = concatenate((vec1,vec2)) setattr(self, name, vec) # here, we sort the particles, according to tpe if do_not_sort: pass else: sequence = self.tpe.argsort() for name in names: vec = getattr(self,name) vec = take(vec,sequence,axis=0) setattr(self, name, vec) self.nbody = self.nbody + solf.nbody self.npart = self.get_npart() # needed by self.get_num() self.npart_tot = self.get_npart_tot() # needed by self.get_num() self.num = self.get_num() self.init() def __add__(self,solf,do_not_sort=False): # first copy self new = deepcopy(self) # now, add solf new.append(solf,do_not_sort) return new ################################# # # sort particles # ################################# def sort(self): ''' sort particles according to their num variable ''' new = Nbody(status='new',ftype=self.ftype[6:]) sequence = argsort(self.num) # now, copy all var linked to the model for name in self.get_list_of_vars(): setattr(new, name, getattr(self,name)) # add array linked to the model for name in self.get_list_of_array(): setattr(new, name, take(getattr(self,name),sequence,axis=0)) new.num = new.get_num() new.init() return new def sort_type(self): ''' Contrary to sort, this fonction sort particles respecting their type. ''' new = Nbody(status='new',ftype=self.ftype[6:]) # now, copy all var linked to the model for name in self.get_list_of_vars(): setattr(new, name, getattr(self,name)) # add array linked to the model for name in self.get_list_of_array(): #vec = take(getattr(self,name),sequence,axis=0) vec = array([],float32) vec1 = getattr(self,name) if vec1.ndim == 1: vec.shape = (0,) else: vec.shape = (0,3) # loop over all types npart = self.npart for i in arange(len(npart)): e11 = sum((arange(len(npart)) < i) * npart) sequence = argsort(self.num[e11:e11+npart[i]]) vec = concatenate((vec,take(vec1[e11:e11+npart[i]],sequence,axis=0))) setattr(new, name, vec) new.num = new.get_num() new.init() return new ################################# # # Tree and SPH functions # ################################# def InitSphParameters(self,DesNumNgb=33,MaxNumNgbDeviation=3): self.DesNumNgb = DesNumNgb self.MaxNumNgbDeviation = MaxNumNgbDeviation self.Density = None self.Hsml = None if not self.has_var('Tree'): self.Tree = None def setTreeParameters(self,Tree,DesNumNgb,MaxNumNgbDeviation): if Tree==None: self.Tree = Tree = self.getTree() if DesNumNgb==None: DesNumNgb = self.DesNumNgb else: self.DesNumNgb = DesNumNgb if MaxNumNgbDeviation==None: MaxNumNgbDeviation = self.MaxNumNgbDeviation else: self.MaxNumNgbDeviation = MaxNumNgbDeviation return Tree,DesNumNgb,MaxNumNgbDeviation def getTree(self,force_computation=False,ErrTolTheta=0.8): ''' Return a Tree object ''' if self.Tree!=None and force_computation==False: return self.Tree else: print "create the tree : ErrTolTheta=",ErrTolTheta # decide if we use tree or ptree npart = array(self.npart) if mpi.mpi_NTask()>1: print "%d : use ptree"%(mpi.mpi_ThisTask()) self.Tree = ptreelib.Tree(npart=npart,pos=self.pos,vel=self.vel,mass=self.mass,num=self.num,tpe=self.tpe) else: self.Tree = treelib.Tree(npart=npart,pos=self.pos,vel=self.vel,mass=self.mass,ErrTolTheta=ErrTolTheta) return self.Tree def get_rsp_approximation(self,DesNumNgb=None,MaxNumNgbDeviation=None,Tree=None): ''' Return an aproximation of rsp, based on the tree. ''' Tree,DesNumNgb,MaxNumNgbDeviation = self.setTreeParameters(Tree,DesNumNgb,MaxNumNgbDeviation) return Tree.InitHsml(DesNumNgb,MaxNumNgbDeviation) def ComputeSph(self,DesNumNgb=None,MaxNumNgbDeviation=None,Tree=None): ''' Compute self.Density and self.Hsml using sph approximation ''' Tree,DesNumNgb,MaxNumNgbDeviation = self.setTreeParameters(Tree,DesNumNgb,MaxNumNgbDeviation) if self.Hsml==None: if not self.has_array('rsp'): self.Hsml = self.get_rsp_approximation(DesNumNgb,MaxNumNgbDeviation,Tree) else: self.Hsml=self.rsp self.Density,self.Hsml = Tree.Density(self.pos,self.Hsml,DesNumNgb,MaxNumNgbDeviation) def ComputeDensityAndHsml(self,pos=None,Hsml=None,DesNumNgb=None,MaxNumNgbDeviation=None,Tree=None): ''' Compute Density and Hsml (for a specific place) ''' Tree,DesNumNgb,MaxNumNgbDeviation = self.setTreeParameters(Tree,DesNumNgb,MaxNumNgbDeviation) if pos==None: pos = self.pos if Hsml==None: Hsml = ones(len(pos)).astype(float32) Density,Hsml = Tree.Density(pos,Hsml,DesNumNgb,MaxNumNgbDeviation) return Density,Hsml def SphEvaluate(self,val,pos=None,vel=None,hsml=None,DesNumNgb=None,MaxNumNgbDeviation=None,Tree=None): ''' Return an sph evaluation of the variable var ''' Tree,DesNumNgb,MaxNumNgbDeviation = self.setTreeParameters(Tree,DesNumNgb,MaxNumNgbDeviation) if pos == None: pos = self.pos if vel == None: vel = self.vel if hsml == None: if self.Hsml==None: if not self.has_array('rsp'): self.Hsml = self.get_rsp_approximation(DesNumNgb,MaxNumNgbDeviation,Tree) else: self.Hsml=self.rsp hsml = self.Hsml if self.Density==None: if not self.has_array('rho'): self.Density = self.SphDensity(DesNumNgb,MaxNumNgbDeviation,Tree) else: self.Density=self.rho if type(val) == ndarray: val = Tree.SphEvaluate(pos,hsml,self.Density,val,DesNumNgb,MaxNumNgbDeviation) else: if val =='div': val = Tree.SphEvaluateDiv(pos,vel,hsml,self.Density,DesNumNgb,MaxNumNgbDeviation) elif val =='rot': val = Tree.SphEvaluateRot(pos,vel,hsml,self.Density,DesNumNgb,MaxNumNgbDeviation) elif val =='ngb': val = Tree.SphEvaluateNgb(pos,hsml,DesNumNgb,MaxNumNgbDeviation) return val ################################# # # sph functions # ################################# def weighted_numngb(self,num): ''' num = particle where to compute weighted_numngb see Springel 05 ''' def wk1(hinv3,u): KERNEL_COEFF_1=2.546479089470 KERNEL_COEFF_2=15.278874536822 wk = hinv3 * (KERNEL_COEFF_1 + KERNEL_COEFF_2 * (u - 1) * u * u) return wk def wk2(hinv3,u): KERNEL_COEFF_5=5.092958178941 wk = hinv3 * KERNEL_COEFF_5 * (1.0 - u) * (1.0 - u) * (1.0 - u) return wk def getwk(r,h): # we do not exclude the particle itself u = r/h hinv3 = 1./h**3 wk = where((u<0.5),wk1(hinv3,u),wk2(hinv3,u)) wk = where((r1: list_of_array = self.get_list_of_array() # loop over all particles type npart = self.npart new_npart = npart for i in range(len(npart)): #if i==0: nparts = mpi.mpi_allgather(npart[i]) nparts = array(nparts) if mpi.mpi_IsMaster(): ex_table = mpi.mpi_GetExchangeTable(nparts) ex_table = mpi.mpi_bcast(ex_table,0) else: ex_table = None ex_table = mpi.mpi_bcast(ex_table,0) # send particles for toTask in range(mpi.NTask): if ex_table[mpi.ThisTask,toTask] > 0: n_elt = ex_table[mpi.ThisTask,toTask] #print "%d send %d to %d"%(mpi.ThisTask,n_elt,toTask) # first_elt = first elt of the current block first_elt = sum((arange(len(new_npart)) < i) * new_npart) # update npart new_npart[i] = new_npart[i] - n_elt # loop over all vect erd,mass,num,pos,rho,rsp,u,vel for name in list_of_array: vec = getattr(self,name) sub_vec = vec[first_elt:first_elt+n_elt] if len(sub_vec) != n_elt: print "redistribute error :" print "node %d should send len=%d got len=%d"%(mpi.ThisTask,n_elt,len(sub_vec)) sys.exit() mpi.mpi_send(name,toTask) mpi.mpi_send(sub_vec,toTask) #self.pos = concatenate( (self.pos[:first_elt],self.pos[first_elt+n_elt:]) ) setattr(self, name, concatenate( (vec[:first_elt],vec[first_elt+n_elt:]) ) ) # recieve particles for fromTask in range(mpi.NTask): if ex_table[fromTask,mpi.ThisTask] > 0: n_elt = ex_table[fromTask,mpi.ThisTask] #print "%d get %d from %d"%(mpi.ThisTask,n_elt,fromTask) # first_elt = first elt of the current block first_elt = sum((arange(len(new_npart)) < i) * new_npart) # update npart new_npart[i] = new_npart[i] + n_elt # loop over all vect for name in list_of_array: # first, check name send_name = mpi.mpi_recv(fromTask) if send_name != name: raise "Task %d FromTask %d, %s != %s"%(mpi.mpi_ThisTask(),fromTask,send_name,name) vec = getattr(self,name) sub_vec = mpi.mpi_recv(fromTask) if len(sub_vec) != n_elt: print "redistribute error :" print "node %d should recive len=%d got len=%d"%(mpi.ThisTask,n_elt,len(sub_vec)) sys.exit() #self.pos = concatenate( (vec[:first_elt],sub_vec,vec[first_elt:]) ) setattr(self, name, concatenate( (vec[:first_elt],sub_vec,vec[first_elt:]) ) ) self.init() def ExchangeParticles(self): ''' Exchange particles betwee procs, using peano-hilbert decomposition computed in ptree ''' if self.Tree==None: self.Tree = self.getTree() # get num and procs from the Tree num,procs = self.Tree.GetExchanges() # compute the transition table T H,bins = histogram(procs,arange(mpi.mpi_NTask())) T = mpi.mpi_AllgatherAndConcatArray(H) T.shape = (mpi.mpi_NTask(),mpi.mpi_NTask()) # loop over all numpy vectors list_of_array = self.get_list_of_array() # loop over all vect for name in list_of_array: if name != "num": setattr(self, name, mpi.mpi_ExchangeFromTable(T,procs,num,getattr(self,name),copy(self.num)) ) # do num at the end self.num = mpi.mpi_ExchangeFromTable(T,procs,num,self.num,copy(self.num)) self.init() def SendAllToAll(self): ''' Send all particles to all nodes at the end of the day, all nodes have the same nbody object ''' nbs = [] for i in xrange(mpi.NTask-1): prev = (mpi.ThisTask-i-1)%mpi.NTask next = (mpi.ThisTask+i+1)%mpi.NTask nbs.append(mpi.mpi_sendrecv(self,dest=next,source=prev)) for nbi in nbs: self = self + nbi return self ################################# # # specific parallel functions # ################################# def gather_pos(self): ''' Gather in a unique array all positions of all nodes. ''' return self.gather_vec(self.pos) def gather_vel(self): ''' Gather in a unique array all velocites of all nodes. ''' return self.gather_vec(self.vel) def gather_mass(self): ''' Gather in a unique array all mass of all nodes. ''' return self.gather_vec(self.mass) def gather_num(self): ''' Gather in a unique array all num of all nodes. ''' return self.gather_vec(self.num) def gather_vec(self,vec): ''' Gather in a unique array all vectors vec of all nodes. ''' # here, we assume that we have a vector npart # giving the number of particles per type vec_all = array([],vec.dtype) if vec.ndim==1: vec_all.shape = (0,) else: vec_all.shape = (0,vec.shape[1]) i1 = 0 npart = self.npart for i in range(len(npart)): i2 = i1 + npart[i] if (i1!=i2): vec_all = concatenate((vec_all,mpi.mpi_AllgatherAndConcatArray(vec[i1:i2]))) i1 = i1 + npart[i] return vec_all ################################# # # graphical operations # ################################# def display(self,*arg,**kw): ''' Display the model ''' if kw.has_key('palette'): palette = kw['palette'] else: palette = None if kw.has_key('save'): save = kw['save'] else: save = None if kw.has_key('marker'): marker = kw['marker'] else: marker = None params = extract_parameters(arg,kw,self.defaultparameters) mat,matint,mn_opts,mx_opts,cd_opts = self.Map(params) if mpi.mpi_IsMaster(): if save != None: if os.path.splitext(save)[1] == ".fits": io.WriteFits(transpose(mat).astype(float32),save, None) return if palette!=None: mplot(matint,palette=palette,save=save,marker=marker) else: mplot(matint,save=save,marker=marker) def show(self,*arg,**kw): ''' Display the model this is an alias to display ''' self.display(*arg,**kw) def Map(self,*arg,**kw): ''' Return 2 final images (float and int) ''' params = extract_parameters(arg,kw,self.defaultparameters) mn_opts = [] mx_opts = [] cd_opts = [] if self.nbody==0 and mpi.mpi_NTask()==1: mat = zeros(params['shape'],float32) matint = mat.astype(int) mn_opts.append(params['mn']) mx_opts.append(params['mx']) cd_opts.append(params['cd']) return mat,matint,mn_opts,mx_opts,cd_opts # compute map mat = self.CombiMap(params) # set ranges matint,mn_opt,mx_opt,cd_opt = set_ranges(mat,scale=params['scale'],cd=params['cd'],mn=params['mn'],mx=params['mx']) mn_opts.append(mn_opt) mx_opts.append(mx_opt) cd_opts.append(cd_opt) # add contour if params['l_color'] != 0: matint = contours(mat,matint,params['l_n'],params['l_min'],params['l_max'],params['l_kx'],params['l_ky'],params['l_color'],params['l_crush']) # add box and ticks if params['b_weight'] != 0: matint = add_box(matint,shape=params['shape'],size=params['size'],center=None,box_opts=(params['b_weight'],params['b_xopts'],params['b_yopts'],params['b_color'])) return mat,matint,mn_opts,mx_opts,cd_opts def CombiMap(self,*arg,**kw): ''' Return an image in form of a matrix (nx x ny float array). Contrary to ComputeMap, CombiMap compose different output of ComputeMap. pos : position of particles (moment 0) sr : dispertion in r (with respect to xp) svr : dispertion in vr vxyr : mean velocity in the plane svxyr: dispertion in vxy vtr : mean tangential velocity in the plane svtr : dispertion in vt szr : ratio sigma z/sigma r ''' params = extract_parameters(arg,kw,self.defaultparameters) mode = params['mode'] #if mode == 'pos': # mat = self.ComputeMap(params) if mode == 'm': mat = self.ComputeMap(params) elif mode == 'sr': mat = self.ComputeSigmaMap(params,mode1='r',mode2='r2') elif mode == 'svr': mat = self.ComputeSigmaMap(params,mode1='vr',mode2='vr2') elif mode == 'svxyr': mat = self.ComputeSigmaMap(params,mode1='vxyr',mode2='vxyr2') elif mode == 'svtr': mat = self.ComputeSigmaMap(params,mode1='vtr',mode2='vtr2') elif mode == 'szr': # could be simplified m0 = self.ComputeMap(params,mode='m') m1 = self.ComputeMap(params,mode='vr') m2 = self.ComputeMap(params,mode='vr2') m1 = where(m0==0,0,m1) m2 = where(m0==0,0,m2) m0 = where(m0==0,1,m0) mat = m2/m0 - (m1/m0)**2 mat_sz = sqrt(numclip(mat,0,1e10)) m0 = self.ComputeMap(params,mode='m') m1 = self.ComputeMap(params,mode='vxyr') m2 = self.ComputeMap(params,mode='vxyr2') m1 = where(m0==0,0,m1) m2 = where(m0==0,0,m2) m0 = where(m0==0,1,m0) mat = m2/m0 - (m1/m0)**2 mat_sr = sqrt(numclip(mat,0,1e10)) mat_sz = where(mat_sr==0,0,mat_sz) mat_sr = where(mat_sr==0,1,mat_sr) mat = mat_sz/mat_sr elif mode == 'lum': mat = self.ComputeMap(params,mode='lum') else: mat = self.ComputeMeanMap(params,mode1=mode) return mat def ComputeMeanMap(self,*arg,**kw): """ Compute the mean map of an observable. """ params = extract_parameters(arg,kw,self.defaultparameters) if kw.has_key('mode1'): mode1 = kw['mode1'] else: raise "ComputeMeanMap :","you must give parameter mode1" m0 = self.ComputeMap(params,mode='0') m1 = self.ComputeMap(params,mode=mode1) m1 = where(m0==0,0,m1) m0 = where(m0==0,1,m0) mat = m1/m0 return mat def ComputeSigmaMap(self,*arg,**kw): """ Compute the sigma map of an observable. """ params = extract_parameters(arg,kw,self.defaultparameters) if kw.has_key('mode1'): mode1 = kw['mode1'] else: raise "ComputeMeanMap","you must give parameter mode1" if kw.has_key('mode2'): mode2 = kw['mode2'] else: raise "ComputeMeanMap","you must give parameter mode2" m0 = self.ComputeMap(params,mode='0') m1 = self.ComputeMap(params,mode=mode1) m2 = self.ComputeMap(params,mode=mode2) m1 = where(m0==0,0,m1) m2 = where(m0==0,0,m2) m0 = where(m0==0,1,m0) mat = m2/m0 - (m1/m0)**2 mat = sqrt(numclip(mat,0,1e10)) return mat def ComputeMap(self,*arg,**kw): ''' Return an image in form of a matrix (nx x ny float array) obs : position of observer x0 : eye position xp : focal position alpha : angle of the head view : 'xy' 'xz' 'yz' eye : 'right' 'left' dist_eye : distance between eyes mode : mode of map space : pos or vel persp : 'on' 'off' clip : (near,far) size : (maxx,maxy) cut : 'yes' 'no' frsp : factor for rsp shape : shape of the map ''' params = extract_parameters(arg,kw,self.defaultparameters) obs = params['obs'] x0 = params['x0'] xp = params['xp'] alpha = params['alpha'] mode = params['mode'] view = params['view'] r_obs = params['r_obs'] eye = params['eye'] dist_eye = params['dist_eye'] foc = params['foc'] space = params['space'] persp = params['persp'] clip = params['clip'] size = params['size'] shape = params['shape'] cut = params['cut'] frsp = params['frsp'] filter_name = params['filter_name'] filter_opts = params['filter_opts'] # 0) if getvaltype(mode)=='normal': val = getval(self,mode=mode,obs=obs) # 1) get observer position if obs==None: obs = geo.get_obs(x0=x0,xp=xp,alpha=alpha,view=view,r_obs=r_obs) # 2) expose the model # !!! as in self.expose we use Nbody() this must be called by each Task nb,obs = self.expose(obs,eye,dist_eye,foc=foc,space=space) if self.nbody > 0: # 3) compute val if getvaltype(mode)=='in projection': val = getval(nb,mode=mode,obs=obs) # 4) projection transformation if persp == 'on': zp = - nb.pos[:,2] # save dist obs-point pos = geo.frustum(nb.pos,clip,size) else: pos = geo.ortho(nb.pos,clip,size) # 5) keep only particles in 1:1:1 if not self.has_array('rsp'): # bad !!! self.rsp = None if cut=='yes': if self.rsp!= None: if params['rendering']=='map': pos,(mass,rsp,val,zp) = geo.boxcut(pos,[self.mass,self.rsp,val,zp]) else: pos,(mass,rsp,val,zp) = geo.boxcut_segments(pos,[self.mass,self.rsp,val,zp]) else: if params['rendering']=='map': pos,(mass,val,zp) = geo.boxcut(pos,[self.mass,val,zp]) else: pos,(mass,val,zp) = geo.boxcut_segments(pos,[self.mass,val,zp]) rsp = None else: mass = self.mass rsp = self.rsp if len(pos)!=0: # 6) scale rsp and scale mass if frsp!= 0: if (rsp==None) or (sum(rsp)==0): rsp = ones(len(pos),float32) if persp == 'on': fact = 1/(zp+clip[0]) # rsp is distance dependant... rsp = rsp * fact rsp = rsp.astype(float32) # mass is distance dependant... mass = mass*fact**2 mass = mass.astype(float32) rsp = rsp*frsp # multiply with the factor self.log.write( "rsp : min = %10.5f max = %10.5f mean = %10.5f"%(min(rsp),max(rsp),rsp.mean()) ) rsp = numclip(rsp,0,100) self.log.write( "rsp : min = %10.5f max = %10.5f mean = %10.5f"%(min(rsp),max(rsp),rsp.mean()) ) rsp = rsp.astype(float32) else: rsp = None # 7) viewport transformation : (x,y) -> ((0,1),(0,1)) pos = geo.viewport(pos,shape=None) pos = pos.astype(float32) # 8) render : map or lines if params['rendering']=='map': if rsp != None: mat = mkmap2dsph(pos,mass,val,rsp,shape) else: mat = mkmap2d(pos,mass,val,shape) elif params['rendering']=='polygon': pos = pos * array([params['shape'][0],params['shape'][1] ,0]) mat = zeros(params['shape'],float32) mat = draw_polygon(mat,pos[:,0],pos[:,1],1) elif params['rendering']=='lines': pos = pos * array([params['shape'][0],params['shape'][1] ,0]) mat = zeros(params['shape'],float32) mat = draw_lines(mat,pos[:,0],pos[:,1],1) elif params['rendering']=='segments': pos = pos * array([params['shape'][0],params['shape'][1] ,0]) mat = zeros(params['shape'],float32) mat = draw_segments(mat,pos[:,0],pos[:,1],1,zp) elif params['rendering']=='points': pos = pos * array([params['shape'][0],params['shape'][1] ,0]) mat = zeros(params['shape'],float32) mat = draw_points(mat,pos[:,0],pos[:,1],1) elif params['rendering']=='polygon2': pos = pos * array([params['shape'][0],params['shape'][1] ,0]) mat = zeros(params['shape'],float32) mat = draw_polygonN(mat,pos[:,0],pos[:,1],1,2) elif params['rendering']=='polygon4': pos = pos * array([params['shape'][0],params['shape'][1] ,0]) mat = zeros(params['shape'],float32) mat = draw_polygonN(mat,pos[:,0],pos[:,1],1,4) elif params['rendering']=='polygon10': pos = pos * array([params['shape'][0],params['shape'][1] ,0]) mat = zeros(params['shape'],float32) mat = draw_polygonN(mat,pos[:,0],pos[:,1],1,10) elif params['rendering'][:8]=='polygon#': n = int(params['rendering'][8:]) pos = pos * array([params['shape'][0],params['shape'][1] ,0]) mat = zeros(params['shape'],float32) mat = draw_polygonN(mat,pos[:,0],pos[:,1],1,n) else: # compute a map if rsp != None: mat = mkmap2dsph(pos,mass,val,rsp,shape) else: mat = mkmap2d(pos,mass,val,shape) # there is no particles (after 5) else: mat = zeros(params['shape'],float32) # there is no particles else: mat = zeros(params['shape'],float32) # 9) sum mat over all proc mat = mpi.mpi_allreduce(mat) # could be more efficient if only the master get the final mat # 10) filter matrix if mpi.mpi_IsMaster(): if params['filter_name'] != None: mat = apply_filter(mat,name=filter_name,opt=filter_opts) return mat def ComputeObjectMap(self,*arg,**kw): ''' * * * IN DEVELOPPEMENT : allow to draw an object like a box, a grid... * * * Return an image in form of a matrix (nx x ny float array) obs : position of observer x0 : eye position xp : focal position alpha : angle of the head view : 'xy' 'xz' 'yz' eye : 'right' 'left' dist_eye : distance between eyes mode : mode of map space : pos or vel persp : 'on' 'off' clip : (near,far) size : (maxx,maxy) cut : 'yes' 'no' frsp : factor for rsp shape : shape of the map ''' # here, nb must represent a geometric object # ob # expose : -> must use ob instead of self # --> we can give explicitely pos and vel # ensuite, le nb est le bon params = extract_parameters(arg,kw,self.defaultparameters) obs = params['obs'] x0 = params['x0'] xp = params['xp'] alpha = params['alpha'] mode = params['mode'] view = params['view'] r_obs = params['r_obs'] eye = params['eye'] dist_eye = params['dist_eye'] foc = params['foc'] space = params['space'] persp = params['persp'] clip = params['clip'] size = params['size'] shape = params['shape'] cut = params['cut'] frsp = params['frsp'] filter_name = params['filter_name'] filter_opts = params['filter_opts'] # 0) if getvaltype(mode)=='normal': val = getval(self,mode=mode,obs=obs) # 1) get observer position if obs==None: obs = geo.get_obs(x0=x0,xp=xp,alpha=alpha,view=view,r_obs=r_obs) # 2) expose the model # !!! as in self.expose we use Nbody() this must be called by each Task nb,obs = self.expose(obs,eye,dist_eye,foc=foc,space=space) if self.nbody > 0: # 3) compute val if getvaltype(mode)=='in projection': val = getval(nb,mode=mode,obs=obs) # 4) projection transformation if persp == 'on': zp = - nb.pos[:,2] # save dist obs-point pos = geo.frustum(nb.pos,clip,size) else: pos = geo.ortho(nb.pos,clip,size) # 5) keep only particles in 1:1:1 if not self.has_array('rsp'): # bad !!! self.rsp = None if cut=='yes': if self.rsp!= None: pos,(mass,rsp,val,zp) = geo.boxcut(pos,[self.mass,self.rsp,val,zp]) else: pos,(mass,val,zp) = geo.boxcut(pos,[self.mass,val,zp]) rsp = None else: mass = self.mass rsp = self.rsp if len(pos)!=0: # 6) scale rsp and scale mass if frsp!= 0: if (rsp==None) or (sum(rsp)==0): rsp = ones(len(pos),float32) if persp == 'on': fact = 1/((zp-clip[0])+ 2*clip[0]) # rsp is distance dependant... rsp = rsp * fact rsp = rsp.astype(float32) # mass is distance dependant... mass = mass*fact**2 mass = mass.astype(float32) rsp = rsp*frsp # multiply with the factor self.log.write( "rsp : min = %10.5f max = %10.5f mean = %10.5f"%(min(rsp),max(rsp),rsp.mean()) ) rsp = numclip(rsp,0,100) self.log.write( "rsp : min = %10.5f max = %10.5f mean = %10.5f"%(min(rsp),max(rsp),rsp.mean()) ) rsp = rsp.astype(float32) else: rsp = None # 7) viewport transformation : (x,y) -> ((0,1),(0,1)) pos = geo.viewport(pos,shape=None) pos = pos.astype(float32) # 8) get the map #if rsp != None: # mat = mkmap2dsph(pos,mass,val,rsp,shape) #else: # mat = mkmap2d(pos,mass,val,shape) # # empty matrix mat = zeros(params['shape'],float32) #for po in pos: # i = int(po[0]*params['shape'][0]) # j = int(po[1]*params['shape'][1]) # mat[i,j]=255 pos = pos * [ params['shape'][0],params['shape'][1],1 ] x0 = pos[0][0] y0 = pos[0][1] x1 = pos[1][0] y1 = pos[1][1] mat = libutil.draw_line(mat,x0,x1,y0,y1,255) #mat = libutil.draw_cube(mat,pos,255) # there is no particles (after 5) else: mat = zeros(params['shape'],float32) # there is no particles else: mat = zeros(params['shape'],float32) # 9) sum mat over all proc mat = mpi.mpi_allreduce(mat) # may be inefficient, better use reduce ? # 10) filter matrix if mpi.mpi_IsMaster(): if params['filter_name'] != None: mat = apply_filter(mat,name=filter_name,opt=filter_opts) return mat def expose(self,obs,eye=None,dist_eye=None,foc=None,space='pos',pos=None,vel=None): """ Rotate and translate the object in order to be seen as if the observer was in x0, looking at a point in xp. obs : observer matrix eye : 'right' or 'left' dist_eye : distance between eyes (separation = angle) space : pos or vel foc : focal """ # create a minimal copy of self if pos!=None and vel!=None: obj = Nbody(status='new',p_name='none',pos=pos,vel=vel,mass=None,ftype='default') else: obj = Nbody(status='new',p_name='none',pos=self.pos,vel=self.vel,mass=None,ftype='default') if space=='vel': obj.pos = self.vel # first : put x0 at the origin obj.translate(- obs[0]) obs = obs - obs[0] # second : anti-align e1 with z obj.align2( axis1=obs[1],axis2=[0,0,-1]) obs = geo.align(obs,axis1=obs[1],axis2=[0,0,-1]) # third : align e3 with y obj.align2( axis1=obs[3],axis2=[0,1,0]) obs = geo.align(obs,axis1=obs[3],axis2=[0,1,0]) # fourth if eye is defined if eye=='right': if foc==None or foc==0: # simple translation (wee look at infini) obj.translate([-dist_eye,0,0]) # not /2 for compatibility with glups else: Robs = foc phi = -arctan(dist_eye) obj.rotate( angle=-phi,axis=[0,1,0],point=[0,0,-Robs]) elif eye=='left': if foc==None or foc==0: # simple translation (wee look at infini) obj.translate([+dist_eye,0,0]) # not /2 for compatibility with glups else: Robs = foc phi = -arctan(dist_eye) obj.rotate( angle=+phi,axis=[0,1,0],point=[0,0,-Robs]) return obj,obs ''' def getvxy(self,shape=(256,256),size=(30.,30.),center=(0.,0.,0.),view='xz',vn=8.,vmax=0.1,color=1): self.log.write( "the result may be incertain (in development)" ) # choice of the view if view=='xz': view=1 elif view=='xy': view=2 elif view=='yz': view=3 elif view!='xz'and view!='xy'and view!='yz': view=1 dx = mapone(self.pos,self.mass,self.vel[:,0],shape,size,center,view) * vn/vmax dy = - mapone(self.pos,self.mass,self.vel[:,2],shape,size,center,view) * vn/vmax # mask mask = fromfunction(lambda x,y: (fmod(x,vn) + fmod(y,vn))==0 ,shape) # points de depart x0 = indices(shape)[0] + int(vn/2.) y0 = indices(shape)[1] + int(vn/2.) # points d'arrivee x1 = x0 + dx.astype(int) y1 = y0 + dy.astype(int) # truncation x1 = numclip(x1,0,shape[0]) y1 = numclip(y1,0,shape[1]) # compress mask = mask*(x1!=x0)*(y1!=y0) mask = ravel(mask) x0 = compress(mask,ravel(x0)) x1 = compress(mask,ravel(x1)) y0 = compress(mask,ravel(y0)) y1 = compress(mask,ravel(y1)) # trace lines mat = zeros(shape,float32) color = array(color,int8)[0] for i in range(len(x0)): create_line(mat,x0[i],y0[i],x1[i],y1[i],color) create_line(mat,x0[i],y0[i],x0[i]+1,y0[i]+1,color) create_line(mat,x0[i],y0[i],x0[i]+1,y0[i] ,color) create_line(mat,x0[i],y0[i],x0[i] ,y0[i]+1,color) return mat.astype(int8) ''' ################################# # # 1d histograms routines # ################################# ########################### def Histo(self,bins,mode='m',space='R'): ########################### histo = self.CombiHisto(bins,mode=mode,space=space) # take the mean bins1 = bins[:-1] bins2 = bins[1:] bins = (bins1+bins2)/2. return bins,mpi.mpi_allreduce(histo) ########################### def CombiHisto(self,bins,mode='m',space='R'): ########################### if mode == 'm': histo = self.ComputeHisto(bins,mode='0',space=space) elif mode == 'sz': histo = self.ComputeSigmaHisto(bins,mode1='z',mode2='z2',space=space) elif mode == 'svz': histo = self.ComputeSigmaHisto(bins,mode1='vz',mode2='vz2',space=space) elif mode == 'svt': histo = self.ComputeSigmaHisto(bins,mode1='vt',mode2='vt2',space=space) elif mode == 'svr': histo = self.ComputeSigmaHisto(bins,mode1='vr',mode2='vr2',space=space) elif mode == 'vt': histo = self.ComputeMeanHisto(bins,mode1='vt',space=space) elif mode == 'vr': histo = self.ComputeMeanHisto(bins,mode1='vr',space=space) elif mode == 'vz': histo = self.ComputeMeanHisto(bins,mode1='vz',space=space) else: print "unknown mode %s"%(mode) return histo ################################# def ComputeMeanHisto(self,bins,mode1,space): ################################# """ Compute the mean map of an observable. """ h0 = self.ComputeHisto(bins,mode='0',space=space) h1 = self.ComputeHisto(bins,mode=mode1,space=space) h1 = where(h0==0,0,h1) h0 = where(h0==0,1,h0) h = h1/h0 return h ################################# def ComputeSigmaHisto(self,bins,mode1,mode2,space): ################################# """ Compute the histogram of an observable. """ h0 = self.ComputeHisto(bins,mode='0',space=space) h1 = self.ComputeHisto(bins,mode=mode1,space=space) h2 = self.ComputeHisto(bins,mode=mode2,space=space) h1 = where(h0==0,0,h1) h2 = where(h0==0,0,h2) h0 = where(h0==0,1,h0) h = h2/h0 - (h1/h0)**2 h = sqrt(numclip(h,0,1e10)) return h ################################# def ComputeHisto(self,bins,mode,space): ################################# ''' Compute and histogram ''' # set space if space == 'R': x = self.rxy() elif space == 'r': x = self.rxyz() # set mode if mode == 'm' or mode=='0': v = self.mass elif mode == 'z': v = self.mass*self.z() elif mode == 'z2': v = self.mass*self.z()**2 elif mode == 'vz': v = self.mass*self.vz() elif mode == 'vz2': v = self.mass*self.vz()**2 elif mode == 'vt': v = self.mass*self.Vt() elif mode == 'vt2': v = self.mass*self.Vt()**2 elif mode == 'vr': v = self.mass*self.Vr() elif mode == 'vr2': v = self.mass*self.Vr()**2 else: print "unknown mode %s"%(mode) histo = whistogram(x.astype(float),v.astype(float),bins.astype(float)) return histo ############################################ # # Routines to get velocities from positions # ############################################ def Get_Velocities_From_Virial_Approximation(self,select=None,vf=1.,eps=0.1,UseTree=True,Tree=None,ErrTolTheta=0.5): ''' This routine does not work ! Do not use it, or check ! ''' if select!=None: nb_sph = self.select(select) else: nb_sph = self # build the Tree for nb self.getTree(force_computation=True,ErrTolTheta=ErrTolTheta) # compute potential pot = 0.5*self.TreePot(nb_sph.pos,eps) # virial approximation to get the velocities sigmasp = sqrt(-pot/3*vf) # compute accel acc = self.TreeAccel(nb_sph.pos,eps) pot = (acc[:,0]*nb_sph.pos[:,0] + acc[:,1]*nb_sph.pos[:,1] + acc[:,2]*nb_sph.pos[:,2]) # virial approximation to get the velocities sigmas = sqrt(-pot/3*vf) # avoid negative values sigmas = where( (pot>0),sigmasp, sigmas ) # generate velocities vx = sigmas*RandomArray.standard_normal([nb_sph.nbody]) vy = sigmas*RandomArray.standard_normal([nb_sph.nbody]) vz = sigmas*RandomArray.standard_normal([nb_sph.nbody]) nb_sph.vel = transpose(array([vx,vy,vz])).astype(float32) return nb_sph def Get_Velocities_From_AdaptativeSpherical_Grid(self,select=None,eps=0.1,n=1000,UseTree=True,Tree=None,phi=None,ErrTolTheta=0.5): ''' Computes velocities using the jeans equation in spherical coordinates. An adaptative grid is set automatically. ''' if select!=None: nb_sph = self.select(select) else: nb_sph = self # create the adaptiative grid and compute rho r = nb_sph.rxyz() a = r.argsort() x = take(nb_sph.pos[:,0],a) y = take(nb_sph.pos[:,1],a) z = take(nb_sph.pos[:,2],a) mass = take(nb_sph.mass,a) r = sqrt( x**2 + y**2 + z**2 ) n_bins = int((nb_sph.nbody+1)/n + 1) rs = [] rsmin = [] rsmax = [] rhos = [] ns = [] for i in xrange(n_bins): jmin = i*n jmax = i*n + n jmin = min(jmin,nb_sph.nbody-1) jmax = min(jmax,nb_sph.nbody-1) if jmin!=jmax: rr = r[jmin:jmax] mm = mass[jmin:jmax] rmean = rr.mean() rmin = rr.min() rmax = rr.max() rs.append(rmean) rsmin.append(rmin) rsmax.append(rmax) # density rho = sum(mm)/(4/3.*pi*(rmax**3-rmin**3)) rhos.append(rho) # number ns.append(len(rr)) r = array(rs) rsmin = array(rsmin) rsmax = array(rsmax) rho = array(rhos) dr = rsmax-rsmin nn = array(ns) # build the Tree for nb self.getTree(force_computation=True,ErrTolTheta=ErrTolTheta) # compute potential x = r y = zeros(len(r)) z = zeros(len(r)) pos = transpose(array([x,y,z])).astype(float32) phi = self.TreePot(pos,eps) # compute sigma sigma = libdisk.get_1d_Sigma_From_Rho_Phi(rho=rho,phi=phi,r=r,dr=dr) # generate velocities for all particles sigmas = lininterp1d(nb_sph.rxyz().astype(float32),r.astype(float32),sigma.astype(float32)) vx = sigmas*RandomArray.standard_normal([nb_sph.nbody]) vy = sigmas*RandomArray.standard_normal([nb_sph.nbody]) vz = sigmas*RandomArray.standard_normal([nb_sph.nbody]) nb_sph.vel = transpose(array([vx,vy,vz])).astype(float32) # here we should limit the speed according to max speed phis = lininterp1d(nb_sph.rxyz().astype(float32),r.astype(float32),phi.astype(float32)) vm = 0.95*sqrt(-2*phis) vn = nb_sph.vn() vf = where(vn>vm,vm/vn,1) vf.shape = (len(vf),1) nb_sph.vel = nb_sph.vel * vf # other info phi dphi = libgrid.get_First_Derivative(phi,r) vcirc = libdisk.Vcirc(r,dphi) stats = {} stats['r'] = r stats['nn'] = nn stats['phi'] = phi stats['rho'] = rho stats['sigma'] = sigma stats['vc'] = vcirc return nb_sph,phi,stats def Get_Velocities_From_Spherical_Grid(self,select=None,eps=0.1,nr=128,rmax=100.0,UseTree=True,Tree=None,phi=None,ErrTolTheta=0.5,g=None,gm=None,NoDispertion=False,omega=None): ''' Computes velocities using the jeans equation in spherical coordinates. ''' if select!=None: nb_sph = self.select(select) else: nb_sph = self # build the Tree for nb self.getTree(force_computation=True,ErrTolTheta=ErrTolTheta) # create the grid G = libgrid.Spherical_1d_Grid(rmin=0,rmax=rmax,nr=nr,g=g,gm=gm) if phi==None: phi = G.get_PotentialMap(self,eps=eps,UseTree=UseTree) r = G.get_r() rho = G.get_DensityMap(nb_sph) nn = G.get_NumberMap(nb_sph) # dr dr = G.get_r(offr=1)-G.get_r(offr=0) # compute sigma sigma = libdisk.get_1d_Sigma_From_Rho_Phi(rho=rho,phi=phi,r=r,dr=dr) # correct sigma in case of rotation (we assume the rotation around z) if omega!=None: print "add rotation" e_jeans = 0.5*sigma*sigma e_rot = 0.5*r**2 * omega**2 e = e_jeans - e_rot if (e<0).any(): print "at some radius the kinetic specifig energy is less than zero\nYou should decrease omega." e = where(e<0,0,e) sigma = sqrt(2*e) # generate velocities for all particles sigmas = G.get_Interpolation(nb_sph.pos,sigma) if NoDispertion: vx = sigmas*ones(nb_sph.nbody) vy = sigmas*ones(nb_sph.nbody) vz = sigmas*ones(nb_sph.nbody) else: vx = sigmas*RandomArray.standard_normal([nb_sph.nbody]) vy = sigmas*RandomArray.standard_normal([nb_sph.nbody]) vz = sigmas*RandomArray.standard_normal([nb_sph.nbody]) nb_sph.vel = transpose(array([vx,vy,vz])).astype(float32) # do not spin # add rotation #if omega!=None: # nb_sph.spin(omega=array([0,0,omega])) # here we should limit the speed according to max speed phis = G.get_Interpolation(nb_sph.pos,phi) vm = 0.95*sqrt(-2*phis) vn = nb_sph.vn() vf = where(vn>vm,vm/vn,1) vf.shape = (len(vf),1) nb_sph.vel = nb_sph.vel * vf # other info phi dphi = libgrid.get_First_Derivative(phi,r) vcirc = libdisk.Vcirc(r,dphi) stats = {} stats['r'] = r stats['nn'] = nn stats['phi'] = phi stats['rho'] = rho stats['sigma'] = sigma stats['vc'] = vcirc return nb_sph,phi,stats def Get_Velocities_From_Cylindrical_Grid(self,select='disk',disk=('gas','disk'),eps=0.1,nR=32,nz=32,nt=2,Rmax=100,zmin=-10,zmax=10,params=[None,None,None],UseTree=True,Tree=None,Phi=None,ErrTolTheta=0.5,AdaptativeSoftenning=False,g=None,gm=None,NoDispertion=False): ''' Computes velocities using the jeans equation in cylindrical coordinates. ''' mode_sigma_z = params[0] mode_sigma_r = params[1] mode_sigma_p = params[2] if params[0]==None: mode_sigma_z = {"name":"jeans","param":None} if params[1]==None: mode_sigma_r = {"name":"toomre","param":1.0} if params[2]==None: mode_sigma_p = {"name":"epicyclic_approximation","param":None} nb_cyl = self.select(select) # current component nb_dis = self.select(disk) # disk component, for Q computation # build the Tree for nb self.getTree(force_computation=True,ErrTolTheta=ErrTolTheta) # create the grid G = libgrid.Cylindrical_2drz_Grid(rmin=0,rmax=Rmax,nr=nR,zmin=zmin,zmax=zmax,nz=nz,g=g,gm=gm) R,z = G.get_rz() #################################### # compute Phi in a 2d rz grid #################################### # here, we could use Acc instead Phi = G.get_PotentialMap(self,eps=eps,UseTree=UseTree,AdaptativeSoftenning=AdaptativeSoftenning) Phi = libgrid.get_Symetrisation_Along_Axis(Phi,axis=1) #Accx,Accy,Accz = libgrid.get_AccelerationMap_On_Cylindrical_2dv_Grid(self,nR,nz,Rmax,zmin,zmax,eps=eps) #Ar = sqrt(Accx**2+Accy**2) #################################### # compute Phi (z=0) in a 2d rt grid #################################### Grt = libgrid.Cylindrical_2drt_Grid(rmin=0,rmax=Rmax,nr=nR,nt=nt,z=0,g=g,gm=gm) Accx,Accy,Accz = Grt.get_AccelerationMap(self,eps=eps,UseTree=UseTree,AdaptativeSoftenning=AdaptativeSoftenning) Ar = sqrt(Accx**2+Accy**2) Ar = sum(Ar,axis=1)/nt Rp,tp = Grt.get_rt() Phi0 = Phi[:,nz/2] # not used dPhi0 = Ar d2Phi0 = libgrid.get_First_Derivative(dPhi0,R) # density rho = G.get_DensityMap(nb_cyl,offz=-0.5) rho = libgrid.get_Symetrisation_Along_Axis(rho,axis=1) # number per bin nn = G.get_NumberMap(nb_cyl,offz=-0.5) Sden = G.get_SurfaceDensityMap(nb_cyl,offz=-0.5) Sdend = G.get_SurfaceDensityMap(nb_dis,offz=-0.5) # compute frequencies (in the plane) kappa = libdisk.Kappa(R,dPhi0,d2Phi0) omega = libdisk.Omega(R,dPhi0) vcirc = libdisk.Vcirc(R,dPhi0) nu = libdisk.Nu(z,Phi) # compute sigma_z if mode_sigma_z['name']=='jeans': R1,z1 = G.get_rz(offz=0) R2,z2 = G.get_rz(offz=1) dz = z2-z1 sigma_z = libdisk.get_2d_Sigma_From_Rho_Phi(rho=rho,Phi=Phi,z=z,dz=dz) sigma_z2 = sigma_z**2 elif mode_sigma_z['name']=='surface density': """sigma_z2 = pi*G*Sden*Hz""" print "mode sigma z : 'surface density', not implemented yet" sys.exit() # compute sigma_r if mode_sigma_r['name']=='epicyclic_approximation': beta2 = mode_sigma_r['param'] f = where( kappa**2>0 , (1./beta2) * (nu**2/kappa**2) , 1.0 ) f.shape = (nR,1) sigma_r2 = sigma_z2 * f elif mode_sigma_r['name']=='isothropic': sigma_r2 = sigma_z2 elif mode_sigma_r['name']=='toomre': Q = mode_sigma_r['param'] Gg = 1.0 sr = where(kappa>0,Q*3.36*Gg*Sdend/kappa,sigma_z[:,nz/2]) sr.shape = (nR,1) sigma_r2 = ones((nR,nz)) * sr sigma_r2 = sigma_r2**2 elif mode_sigma_r['name']=='constant': sr = mode_sigma_r['param'] sigma_r2 = ones((nR,nz)) * sr sigma_r2 = sigma_r2**2 # compute sigma_p if mode_sigma_p['name']=='epicyclic_approximation': f = where( omega**2>0 , (1/4.0) * (kappa**2/omega**2) , 1.0 ) f.shape = (nR,1) sigma_p2 = sigma_r2 * f elif mode_sigma_p['name']=='isothropic': sigma_p2 = sigma_z2 notok = True count = 0 while notok: count = count + 1 print "compute vm" # compute vm sr2 = sigma_r2[:,nz/2] # should not be only in the plane sp2 = sigma_p2[:,nz/2] # should not be only in the plane vc = vcirc # should not be only in the plane T1 = vc**2 T2 = + sr2 - sp2 T3 = where(Sden>0,R/Sden,0)* libgrid.get_First_Derivative(Sden*sr2,R) vm2 = T1 + T2 + T3 # if vm2 < 0 c = (vm2<0) if sum(c)>0: print "Get_Velocities_From_Cylindrical_Grid : vm2 < 0 for %d elements"%(sum(c)) ''' vm2 = where(c,0,vm2) dsr2 = where(c,(T1+T2+T3)/2.,0) # energie qu'il faut retirer a sr dsp2 = where(c,(T1+T2+T3)/2.,0) # energie qu'il faut retirer a sp # take energy from sigma_r and sigma_p sigma_r2 = transpose(transpose(sigma_r2) + dsr2) sigma_p2 = transpose(transpose(sigma_p2) + dsp2) ''' E = sr2 + sp2 + vm2 if sum(E<0) != 0: print "-----------------------------------------------------" for i in range(len(R)): print R[i],vc[i]**2,sr2[i],sp2[i],vm2[i],sigma_z[i,nz/2]**2 print "-----------------------------------------------------" print "Get_Velocities_From_Cylindrical_Grid : we are in trouble here..." raise "E<0" vm2 = where(c,E/3.,vm2) sr2 = where(c,E/3.,sr2) sp2 = where(c,E/3.,sp2) sigma_r2 = transpose(ones((nz,nR)) * sr2) sigma_p2 = transpose(ones((nz,nR)) * sp2) if count > 0: notok = False else: notok = False # old implementation #vm2 = where(c,T1,vm2) #dsr2 = where(c,-(T2+T3)/2.,0) #dsp2 = where(c,-(T2+T3)/2.,0) # check again c = (vm2<0).astype(int) nvm = sum(c) if sum(nvm)>0: print "WARNING : %d cells still have vm<0 !!!"%(nvm) print "Vc^2 < 0 !!!" vm2 = where(c,0,vm2) vm = where(vm2>0,sqrt(vm2),0) # generate velocities for all particles sigma_r2s = G.get_Interpolation(nb_cyl.pos,sigma_r2) sigma_p2s = G.get_Interpolation(nb_cyl.pos,sigma_p2) sigma_z2s = G.get_Interpolation(nb_cyl.pos,sigma_z2) sigma_rs = where(sigma_r2s>0,sqrt(sigma_r2s),0) sigma_ps = where(sigma_p2s>0,sqrt(sigma_p2s),0) sigma_zs = where(sigma_z2s>0,sqrt(sigma_z2s),0) vcircs = G.get_r_Interpolation(nb_cyl.pos,vcirc) vms = G.get_r_Interpolation(nb_cyl.pos,vm) if NoDispertion: vr = sigma_rs vp = sigma_ps*0 + vms vz = sigma_zs else: vr = sigma_rs*RandomArray.standard_normal([nb_cyl.nbody]) vp = sigma_ps*RandomArray.standard_normal([nb_cyl.nbody]) + vms vz = sigma_zs*RandomArray.standard_normal([nb_cyl.nbody]) vel = transpose(array([vr,vp,vz])).astype(float32) nb_cyl.vel = libutil.vel_cyl2cart(nb_cyl.pos,vel) # here we should limit the speed according to max speed phis = G.get_Interpolation(nb_cyl.pos,Phi) vmax = 0.95*sqrt(-2*phis) vn = nb_cyl.vn() vf = where(vn>vmax,vmax/vn,1) vf.shape = (len(vf),1) nb_cyl.vel = nb_cyl.vel * vf # some output sr = sigma_r = sqrt(sigma_r2[:,nz/2]) sp = sigma_p = sqrt(sigma_p2[:,nz/2]) sz = sigma_z = sqrt(sigma_z2[:,nz/2]) Q = where((Sden>0),sr*kappa/(3.36*Sdend),0) stats = {} stats['R'] = R stats['z'] = z stats['vc'] = vc stats['vm'] = vm stats['sr'] = sr stats['sp'] = sp stats['sz'] = sz stats['kappa'] = kappa stats['omega'] = omega stats['nu'] = nu stats['Sden'] = Sden stats['Sdend'] = Sdend stats['Q'] = Q #stats['Ar'] = Ar stats['rho'] = rho stats['phi'] = Phi stats['nn'] = nn stats['sigma_z']= sqrt(sigma_z2) stats['Phi0'] = Phi0 stats['dPhi0'] = dPhi0 stats['d2Phi0'] = d2Phi0 return nb_cyl,Phi,stats ############################################ # # evolution routines # ############################################ def IntegrateUsingRK(self,tstart=0,dt=1,dt0=1e-5,epsx=1.e-13,epsv=1.e-13): """ Integrate the equation of motion using RK78 integrator. tstart : initial time dt : interval time dt0 : inital dt epsx : position precision epsv : velocity precision tmin,tmax,dt,dtout,epsx,epsv,filename """ tend = tstart + dt self.pos,self.vel,self.atime,self.dt = nbdrklib.IntegrateOverDt(self.pos.astype(float),self.vel.astype(float),self.mass.astype(float),tstart,tend,dt,epsx,epsv) self.pos = self.pos.astype(float32) self.vel = self.vel.astype(float32) ################################# # # Thermodynamic functions # ################################# def U(self): ''' Return the gas specific energy of the model. The output is an nx1 float array. ''' return self.u def Rho(self): ''' Return the gas density of the model. The output is an nx1 float array. ''' try: a3inv = 1./self.atime**3 except: a3inv = 1. if self.unitsparameters.get('HubbleParam') == 1: self.log.write("assuming non cosmological simulation") a3inv = 1. try: rho = self.rho*a3inv return rho except: return self.rho def T(self): ''' Return the gas temperature of the model. The output is an nx1 float array. ''' gamma = self.unitsparameters.get('gamma') xi = self.unitsparameters.get('xi') ionisation = self.unitsparameters.get('ionisation') mu = thermodyn.MeanWeight(xi,ionisation) mh = ctes.PROTONMASS.into(self.localsystem_of_units) k = ctes.BOLTZMANN.into(self.localsystem_of_units) thermopars = {"k":k,"mh":mh,"mu":mu,"gamma":gamma} T = where((self.u>0),thermodyn.Tru(self.Rho(),self.u,thermopars),0) return T def MeanWeight(self): ''' Return the mean weight of a model, taking into account heating by UV source. The output is an nx1 float array. ''' xi = self.unitsparameters.get('xi') Redshift = 1./self.atime - 1. UnitDensity_in_cgs = self.localsystem_of_units.get_UnitDensity_in_cgs() UnitEnergy_in_cgs = self.localsystem_of_units.get_UnitEnergy_in_cgs() UnitMass_in_g = self.localsystem_of_units.get_UnitMass_in_g() HubbleParam = self.hubbleparam # 0) convert into cgs Density = self.rho.astype(float) *UnitDensity_in_cgs * (HubbleParam*HubbleParam) / self.atime**3 Egyspec = self.u.astype(float) *UnitEnergy_in_cgs/UnitMass_in_g # 1) compute mu MeanWeight,Lambda = coolinglib.cooling(Egyspec,Density,xi,Redshift) return MeanWeight.astype(float32) def Tmu(self): ''' Return the gas temperature of the model. The output is an nx1 float array. ''' gamma = self.unitsparameters.get('gamma') mh = ctes.PROTONMASS.into(self.localsystem_of_units) k = ctes.BOLTZMANN.into(self.localsystem_of_units) T = (gamma-1) *self.MeanWeight().astype(float) *mh/k * self.u return T.astype(float32) def A(self): ''' Return the gas entropy of the model. The output is an nx1 float array. ''' gamma = self.unitsparameters.get('gamma') xi = self.unitsparameters.get('xi') ionisation = self.unitsparameters.get('ionisation') mu = thermodyn.MeanWeight(xi,ionisation) mh = ctes.PROTONMASS.into(self.localsystem_of_units) k = ctes.BOLTZMANN.into(self.localsystem_of_units) thermopars = {"k":k,"mh":mh,"mu":mu,"gamma":gamma} A = where((self.u>0),thermodyn.Aru(self.Rho(),self.u,thermopars),0) return A def P(self): ''' Return the gas pressure of the model. The output is an nx1 float array. ''' gamma = self.unitsparameters.get('gamma') xi = self.unitsparameters.get('xi') ionisation = self.unitsparameters.get('ionisation') mu = thermodyn.MeanWeight(xi,ionisation) mh = ctes.PROTONMASS.into(self.localsystem_of_units) k = ctes.BOLTZMANN.into(self.localsystem_of_units) thermopars = {"k":k,"mh":mh,"mu":mu,"gamma":gamma} P = where((self.u>0),thermodyn.Pru(self.Rho(),self.u,thermopars),0) return P def Tcool(self,coolingfile=None): ''' Return the cooling time of the model. The output is an nx1 float array. ''' gamma = self.unitsparameters.get('gamma') xi = self.unitsparameters.get('xi') ionisation = self.unitsparameters.get('ionisation') metalicity = self.unitsparameters.get('metalicity') hubbleparam= self.unitsparameters.get('HubbleParam') if coolingfile==None: coolingfile = self.unitsparameters.get('coolingfile') mu = thermodyn.MeanWeight(xi,ionisation) mh = ctes.PROTONMASS.into(self.localsystem_of_units) k = ctes.BOLTZMANN.into(self.localsystem_of_units) try: if self.hubbleparam != hubbleparam: self.log.write("Warning (Tcool): using hubbleparam=%f, but self.hubbleparam=%f"%(hubbleparam,self.hubbleparam)) except: pass thermopars = {"k":k,"mh":mh,"mu":mu,"gamma":gamma,"Xi":xi,"metalicity":metalicity,"hubbleparam":hubbleparam} tc,c = thermodyn.CoolingTime(self.Rho(),self.u,self.localsystem_of_units,thermopars,coolingfile) tc = where(c,tc,0) return tc def Ne(self): ''' Return the electron density of the model. The output is an nx1 float array. ''' xi = self.unitsparameters.get('xi') ionisation = self.unitsparameters.get('ionisation') mh = ctes.PROTONMASS.into(self.localsystem_of_units) thermopars = {"mh":mh,"Xi":xi,"ionisation":ionisation} ne = thermodyn.ElectronDensity(self.Rho(),thermopars) return ne def S(self): ''' Return the `entropy` of the model, defined as S = T * Ne^(1-gamma) The output is an nx1 float array. ''' gamma = self.unitsparameters.get('gamma') s = self.T()*self.Ne()**(1.-gamma) return s def Lum(self): ''' Return the luminosty of the model, defined as Lum = m*u/Tcool = m*Lambda/rho The output is an nx1 float array. ''' Lum = self.mass*self.u/self.Tcool() return Lum #################################################################################################################################### # # NBODY REDIRECTOR # #################################################################################################################################### if FORMATSDIR != None: formatsfiles = glob.glob(os.path.join(FORMATSDIR,'*.py')) def Nbody(*arg,**kw): """ The aim of this function is simply to return to the right class """ # default value nb = None if not kw.has_key('ftype'): kw['ftype']='default' # find the right file formatfile = os.path.join(FORMATSDIR,"%s.py"%kw['ftype']) try: formatsfiles.index(formatfile) except ValueError: print "format %s is unknown !"%kw['ftype'] print "%s does not exists"%formatfile sys.exit() # this does not work, because NbodyDefault is unknown #sys.path.append(FORMATSDIR) #__import__(kw['ftype'],) # instead, we use execfile(formatfile) # check class name class_name = "Nbody_%s"%kw['ftype'] try: dir().index(class_name) except ValueError: print "format %s is unknown !"%kw['ftype'] sys.exit() ## not very good class_name = eval(class_name) nb = class_name(*arg,**kw) nb._formatfile = formatfile return nb diff --git a/src/PyGadget/build/lib.linux-x86_64-2.6/PyGadget/gadget.so b/src/PyGadget/build/lib.linux-x86_64-2.6/PyGadget/gadget.so index 75894c7..d1d448d 100755 Binary files a/src/PyGadget/build/lib.linux-x86_64-2.6/PyGadget/gadget.so and b/src/PyGadget/build/lib.linux-x86_64-2.6/PyGadget/gadget.so differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/accel.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/accel.o index ae43fa5..def8bd7 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/accel.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/accel.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/allocate.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/allocate.o index 36be195..3db7270 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/allocate.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/allocate.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/allocateQ.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/allocateQ.o index 751b6fb..3a76ee4 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/allocateQ.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/allocateQ.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/allvars.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/allvars.o index 3e4087b..17a590b 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/allvars.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/allvars.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/begrun.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/begrun.o index 754bb7b..86bcf96 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/begrun.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/begrun.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/density.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/density.o index 89464b5..a37c0b9 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/density.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/density.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/domain.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/domain.o index fdb8f85..4456107 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/domain.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/domain.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/domainQ.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/domainQ.o index 16440ed..73a97e0 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/domainQ.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/domainQ.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/driftfac.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/driftfac.o index a7772c3..bb70aa4 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/driftfac.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/driftfac.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/endrun.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/endrun.o index 461a760..3db8494 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/endrun.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/endrun.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/forcetree.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/forcetree.o index e12ffb9..39873d9 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/forcetree.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/forcetree.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/global.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/global.o index 9bd2416..c57381d 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/global.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/global.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/gravtree.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/gravtree.o index 1639521..963fcb0 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/gravtree.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/gravtree.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/gravtree_forcetest.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/gravtree_forcetest.o index b1be23a..ba8b240 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/gravtree_forcetest.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/gravtree_forcetest.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/hydra.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/hydra.o index 2e06067..c435b72 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/hydra.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/hydra.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/init.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/init.o index 9be0199..8d40af2 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/init.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/init.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/io.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/io.o index 4d207f8..040598a 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/io.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/io.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/longrange.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/longrange.o index e1b9adc..115625f 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/longrange.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/longrange.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/main.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/main.o index f8a2c26..871a8af 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/main.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/main.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/ngb.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/ngb.o index 7bb6820..f6c98fa 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/ngb.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/ngb.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/peano.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/peano.o index 5e724aa..ab5cd7c 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/peano.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/peano.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/pm_nonperiodic.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/pm_nonperiodic.o index aec930b..ef084ee 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/pm_nonperiodic.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/pm_nonperiodic.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/pm_periodic.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/pm_periodic.o index e411e66..c0b110f 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/pm_periodic.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/pm_periodic.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/potential.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/potential.o index b782ca6..1fb436e 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/potential.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/potential.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/predict.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/predict.o index b8d346a..8c865d3 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/predict.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/predict.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/python_interface.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/python_interface.o index 7af0fb0..a151ecd 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/python_interface.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/python_interface.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/read_ic.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/read_ic.o index 70bf98d..6b08f2e 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/read_ic.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/read_ic.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/restart.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/restart.o index c9ce011..5714358 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/restart.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/restart.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/run.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/run.o index b26b9a3..f9482d5 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/run.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/run.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/sph.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/sph.o index 97f7b78..8db3c58 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/sph.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/sph.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/system.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/system.o index b100123..370db31 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/system.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/system.o differ diff --git a/src/PyGadget/build/temp.linux-x86_64-2.6/src/timestep.o b/src/PyGadget/build/temp.linux-x86_64-2.6/src/timestep.o index c778f5b..5ff8796 100644 Binary files a/src/PyGadget/build/temp.linux-x86_64-2.6/src/timestep.o and b/src/PyGadget/build/temp.linux-x86_64-2.6/src/timestep.o differ diff --git a/src/PyGadget/setup.py b/src/PyGadget/setup.py index 204b37a..d61482b 100644 --- a/src/PyGadget/setup.py +++ b/src/PyGadget/setup.py @@ -1,77 +1,77 @@ #!/usr/bin/env python import os,sys from distutils.core import setup, Extension from distutils.sysconfig import get_python_inc from distutils.sysconfig import get_python_lib ''' In order to compile with mpi, export CC=mpicc python setup.py build ''' incdir = os.path.join(get_python_inc(plat_specific=1), 'numpy') libdir = os.path.join(get_python_lib(plat_specific=1), 'numpy') gadget_files = ["src/main.c", "src/run.c", "src/predict.c", "src/begrun.c", "src/endrun.c", "src/global.c", "src/timestep.c", "src/init.c", "src/restart.c", "src/io.c", "src/accel.c", "src/read_ic.c", "src/ngb.c", "src/system.c", "src/allocate.c", "src/density.c", "src/gravtree.c", "src/hydra.c", "src/driftfac.c", "src/domain.c", "src/allvars.c", "src/potential.c", "src/forcetree.c", "src/peano.c", "src/gravtree_forcetest.c", "src/pm_periodic.c", "src/pm_nonperiodic.c", "src/longrange.c", "src/sph.c", "src/python_interface.c", "src/domainQ.c", "src/allocateQ.c"] PYTHON_INC = "/usr/include/python2.6/" MPI_DIR = "/usr/lib64/openmpi/lib/" MPI_LIB = "mpi" MPI_INC = "/usr/include/openmpi-x86_64/" setup(name='PyGadget', version='0.0', description='Python Gadget Wrapping', author='Greg Ward', author_email='yves.revaz@epfl.ch', url='http://obswww.unige.ch/~revaz/pNbody', packages=['PyGadget'], ext_modules=[ - Extension('PyGadget.gadget', gadget_files,include_dirs=[MPI_INC,"src/",PYTHON_INC],define_macros=[('PY_INTERFACE', '1'),('UNEQUALSOFTENINGS', '1') ,('PERIODIC', '1'),('PEANOHILBERT', '1'),('NOGRAVITY', '1'),('LONG_X', '32'),('LONG_Y', '32'),('LONG_Z', '1')] , library_dirs=[MPI_DIR],libraries=['gsl','gslcblas','m',MPI_LIB] ) + Extension('PyGadget.gadget', gadget_files,include_dirs=[MPI_INC,"src/",PYTHON_INC],define_macros=[('PY_INTERFACE', '1'),('UNEQUALSOFTENINGS', '1') ,('PERIODIC', '1'),('PEANOHILBERT', '1'),('NOGRAVITY', '1'),('LONG_X', '1'),('LONG_Y', '1'),('LONG_Z', '0.125')] , library_dirs=[MPI_DIR],libraries=['gsl','gslcblas','m',MPI_LIB] ) ] ) diff --git a/src/PyGadget/src/allocate.c b/src/PyGadget/src/allocate.c index 7e2abdf..ea4be4f 100644 --- a/src/PyGadget/src/allocate.c +++ b/src/PyGadget/src/allocate.c @@ -1,165 +1,173 @@ #include #include #include #include #include "allvars.h" #include "proto.h" /*! \file allocate.c * \brief routines for allocating particle and tree storage */ /*! Allocates a number of small buffers and arrays, the largest one being * the communication buffer. The communication buffer itself is mapped * onto various tables used in the different parts of the force * algorithms. We further allocate space for the top-level tree nodes, and * auxiliary arrays for the domain decomposition algorithm. */ void allocate_commbuffers(void) { size_t bytes; Exportflag = malloc(NTask * sizeof(char)); DomainStartList = malloc(NTask * sizeof(int)); DomainEndList = malloc(NTask * sizeof(int)); TopNodes = malloc(MAXTOPNODES * sizeof(struct topnode_data)); DomainWork = malloc(MAXTOPNODES * sizeof(double)); DomainCount = malloc(MAXTOPNODES * sizeof(int)); DomainCountSph = malloc(MAXTOPNODES * sizeof(int)); DomainTask = malloc(MAXTOPNODES * sizeof(int)); DomainNodeIndex = malloc(MAXTOPNODES * sizeof(int)); DomainTreeNodeLen = malloc(MAXTOPNODES * sizeof(FLOAT)); DomainHmax = malloc(MAXTOPNODES * sizeof(FLOAT)); DomainMoment = malloc(MAXTOPNODES * sizeof(struct DomainNODE)); if(!(CommBuffer = malloc(bytes = All.BufferSize * 1024 * 1024))) { printf("failed to allocate memory for `CommBuffer' (%g MB).\n", bytes / (1024.0 * 1024.0)); endrun(2); } All.BunchSizeForce = (All.BufferSize * 1024 * 1024) / (sizeof(struct gravdata_index) + 2 * sizeof(struct gravdata_in)); if(All.BunchSizeForce & 1) All.BunchSizeForce -= 1; /* make sure that All.BunchSizeForce is an even number --> 8-byte alignment for 64bit processors */ GravDataIndexTable = (struct gravdata_index *) CommBuffer; GravDataIn = (struct gravdata_in *) (GravDataIndexTable + All.BunchSizeForce); GravDataGet = GravDataIn + All.BunchSizeForce; GravDataOut = GravDataIn; /* this will overwrite the GravDataIn-Table */ GravDataResult = GravDataGet; /* this will overwrite the GravDataGet-Table */ All.BunchSizeDensity = (All.BufferSize * 1024 * 1024) / (2 * sizeof(struct densdata_in) + 2 * sizeof(struct densdata_out)); DensDataIn = (struct densdata_in *) CommBuffer; DensDataGet = DensDataIn + All.BunchSizeDensity; DensDataResult = (struct densdata_out *) (DensDataGet + All.BunchSizeDensity); DensDataPartialResult = DensDataResult + All.BunchSizeDensity; All.BunchSizeHydro = (All.BufferSize * 1024 * 1024) / (2 * sizeof(struct hydrodata_in) + 2 * sizeof(struct hydrodata_out)); HydroDataIn = (struct hydrodata_in *) CommBuffer; HydroDataGet = HydroDataIn + All.BunchSizeHydro; HydroDataResult = (struct hydrodata_out *) (HydroDataGet + All.BunchSizeHydro); HydroDataPartialResult = HydroDataResult + All.BunchSizeHydro; #ifdef PY_INTERFACE All.BunchSizeSph = (All.BufferSize * 1024 * 1024) / (2 * sizeof(struct sphdata_in) + 2 * sizeof(struct sphdata_out)); SphDataIn = (struct sphdata_in *) CommBuffer; SphDataGet = SphDataIn + All.BunchSizeSph; SphDataResult = (struct sphdata_out *) (SphDataGet + All.BunchSizeSph); SphDataPartialResult = SphDataResult + All.BunchSizeSph; #endif +#ifdef PY_INTERFACE + All.BunchSizeDensitySph = + (All.BufferSize * 1024 * 1024) / (2 * sizeof(struct denssphdata_in) + 2 * sizeof(struct denssphdata_out)); + DensSphDataIn = (struct denssphdata_in *) CommBuffer; + DensSphDataGet = DensSphDataIn + All.BunchSizeDensitySph; + DensSphDataResult = (struct denssphdata_out *) (DensSphDataGet + All.BunchSizeDensitySph); + DensSphDataPartialResult = DensSphDataResult + All.BunchSizeDensitySph; +#endif All.BunchSizeDomain = (All.BufferSize * 1024 * 1024) / (sizeof(struct particle_data) + sizeof(struct sph_particle_data) + sizeof(peanokey)); if(All.BunchSizeDomain & 1) All.BunchSizeDomain -= 1; /* make sure that All.BunchSizeDomain is even --> 8-byte alignment of DomainKeyBuf for 64bit processors */ DomainPartBuf = (struct particle_data *) CommBuffer; DomainSphBuf = (struct sph_particle_data *) (DomainPartBuf + All.BunchSizeDomain); DomainKeyBuf = (peanokey *) (DomainSphBuf + All.BunchSizeDomain); if(ThisTask == 0) { printf("\nAllocated %d MByte communication buffer per processor.\n\n", All.BufferSize); printf("Communication buffer has room for %d particles in gravity computation\n", All.BunchSizeForce); printf("Communication buffer has room for %d particles in density computation\n", All.BunchSizeDensity); printf("Communication buffer has room for %d particles in hydro computation\n", All.BunchSizeHydro); printf("Communication buffer has room for %d particles in domain decomposition\n", All.BunchSizeDomain); printf("\n"); } } /*! This routine allocates memory for particle storage, both the * collisionless and the SPH particles. */ void allocate_memory(void) { size_t bytes; double bytes_tot = 0; if(All.MaxPart > 0) { if(!(P = malloc(bytes = All.MaxPart * sizeof(struct particle_data)))) { printf("failed to allocate memory for `P' (%g MB).\n", bytes / (1024.0 * 1024.0)); endrun(1); } bytes_tot += bytes; if(ThisTask == 0) printf("\nAllocated %g MByte for particle storage. %d\n\n", bytes_tot / (1024.0 * 1024.0), sizeof(struct particle_data)); } if(All.MaxPartSph > 0) { bytes_tot = 0; if(!(SphP = malloc(bytes = All.MaxPartSph * sizeof(struct sph_particle_data)))) { printf("failed to allocate memory for `SphP' (%g MB) %d.\n", bytes / (1024.0 * 1024.0), sizeof(struct sph_particle_data)); endrun(1); } bytes_tot += bytes; if(ThisTask == 0) printf("Allocated %g MByte for storage of SPH data. %d\n\n", bytes_tot / (1024.0 * 1024.0), sizeof(struct sph_particle_data)); } } /*! This routine frees the memory for the particle storage. Note: We don't * actually bother to call it in the code... When the program terminats, * the memory will be automatically freed by the operating system. */ void free_memory(void) { if(All.MaxPartSph > 0) free(SphP); if(All.MaxPart > 0) free(P); } diff --git a/src/PyGadget/src/allvars.c b/src/PyGadget/src/allvars.c index 14fffe2..6873e41 100644 --- a/src/PyGadget/src/allvars.c +++ b/src/PyGadget/src/allvars.c @@ -1,268 +1,280 @@ /*! \file allvars.c * \brief provides instances of all global variables. */ #include #include #include "tags.h" #include "allvars.h" int ThisTask; /*!< the rank of the local processor */ int NTask; /*!< number of processors */ int PTask; /*!< smallest integer such that NTask <= 2^PTask */ int NumPart; /*!< number of particles on the LOCAL processor */ #ifdef PY_INTERFACE int NumPartQ; #endif int N_gas; /*!< number of gas particles on the LOCAL processor */ #ifdef PY_INTERFACE int N_gasQ; #endif long long Ntype[6]; /*!< total number of particles of each type */ int NtypeLocal[6]; /*!< local number of particles of each type */ #ifdef PY_INTERFACE long long NtypeQ[6]; /*!< total number of particles of each type */ int NtypeLocalQ[6]; /*!< local number of particles of each type */ #endif int NumForceUpdate; /*!< number of active particles on local processor in current timestep */ int NumSphUpdate; /*!< number of active SPH particles on local processor in current timestep */ double CPUThisRun; /*!< Sums the CPU time for the process (current submission only) */ int RestartFlag; /*!< taken from command line used to start code. 0 is normal start-up from initial conditions, 1 is resuming a run from a set of restart files, while 2 marks a restart from a snapshot file. */ char *Exportflag; /*!< Buffer used for flagging whether a particle needs to be exported to another process */ int *Ngblist; /*!< Buffer to hold indices of neighbours retrieved by the neighbour search routines */ int TreeReconstructFlag; /*!< Signals that a new tree needs to be constructed */ int Flag_FullStep; /*!< This flag signals that the current step involves all particles */ gsl_rng *random_generator; /*!< the employed random number generator of the GSL library */ double RndTable[RNDTABLE]; /*!< Hold a table with random numbers, refreshed every timestep */ double DomainCorner[3]; /*!< gives the lower left corner of simulation volume */ double DomainCenter[3]; /*!< gives the center of simulation volume */ double DomainLen; /*!< gives the (maximum) side-length of simulation volume */ double DomainFac; /*!< factor used for converting particle coordinates to a Peano-Hilbert mesh covering the simulation volume */ int DomainMyStart; /*!< first domain mesh cell that resides on the local processor */ int DomainMyLast; /*!< last domain mesh cell that resides on the local processor */ int *DomainStartList; /*!< a table that lists the first domain mesh cell for all processors */ int *DomainEndList; /*!< a table that lists the last domain mesh cell for all processors */ double *DomainWork; /*!< a table that gives the total "work" due to the particles stored by each processor */ int *DomainCount; /*!< a table that gives the total number of particles held by each processor */ int *DomainCountSph; /*!< a table that gives the total number of SPH particles held by each processor */ int *DomainTask; /*!< this table gives for each leaf of the top-level tree the processor it was assigned to */ int *DomainNodeIndex; /*!< this table gives for each leaf of the top-level tree the corresponding node of the gravitational tree */ FLOAT *DomainTreeNodeLen; /*!< this table gives for each leaf of the top-level tree the side-length of the corresponding node of the gravitational tree */ FLOAT *DomainHmax; /*!< this table gives for each leaf of the top-level tree the maximum SPH smoothing length among the particles of the corresponding node of the gravitational tree */ #ifdef PY_INTERFACE double DomainCornerQ[3]; /*!< gives the lower left corner of simulation volume */ double DomainCenterQ[3]; /*!< gives the center of simulation volume */ double DomainLenQ; /*!< gives the (maximum) side-length of simulation volume */ double DomainFacQ; /*!< factor used for converting particle coordinates to a Peano-Hilbert mesh covering the simulation volume */ int DomainMyStartQ; /*!< first domain mesh cell that resides on the local processor */ int DomainMyLastQ; /*!< last domain mesh cell that resides on the local processor */ int *DomainStartListQ; /*!< a table that lists the first domain mesh cell for all processors */ int *DomainEndListQ; /*!< a table that lists the last domain mesh cell for all processors */ double *DomainWorkQ; /*!< a table that gives the total "work" due to the particles stored by each processor */ int *DomainCountQ; /*!< a table that gives the total number of particles held by each processor */ int *DomainCountSphQ; /*!< a table that gives the total number of SPH particles held by each processor */ int *DomainTaskQ; /*!< this table gives for each leaf of the top-level tree the processor it was assigned to */ #endif struct DomainNODE *DomainMoment; /*!< this table stores for each node of the top-level tree corresponding node data from the gravitational tree */ peanokey *DomainKeyBuf; /*!< this points to a buffer used during the exchange of particle data */ #ifdef PY_INTERFACE peanokey *DomainKeyBufQ; /*!< this points to a buffer used during the exchange of particle data */ #endif peanokey *Key; /*!< a table used for storing Peano-Hilbert keys for particles */ peanokey *KeySorted; /*!< holds a sorted table of Peano-Hilbert keys for all particles, used to construct top-level tree */ int NTopnodes; /*!< total number of nodes in top-level tree */ int NTopleaves; /*!< number of leaves in top-level tree. Each leaf can be assigned to a different processor */ #ifdef PY_INTERFACE int NTopnodesQ; /*!< total number of nodes in top-level tree */ int NTopleavesQ; /*!< number of leaves in top-level tree. Each leaf can be assigned to a different processor */ #endif #ifdef PY_INTERFACE int NTopnodes; /*!< total number of nodes in top-level tree */ int NTopleaves; /*!< number of leaves in top-level tree. Each leaf can be assigned to a different processor */ #endif struct topnode_data #ifdef PY_INTERFACE *TopNodesQ, #endif *TopNodes; /*!< points to the root node of the top-level tree */ double TimeOfLastTreeConstruction; /*!< holds what it says, only used in connection with FORCETEST */ /* variables for input/output, usually only used on process 0 */ char ParameterFile[MAXLEN_FILENAME]; /*!< file name of parameterfile used for starting the simulation */ FILE *FdInfo; /*!< file handle for info.txt log-file. */ FILE *FdEnergy; /*!< file handle for energy.txt log-file. */ FILE *FdTimings; /*!< file handle for timings.txt log-file. */ FILE *FdCPU; /*!< file handle for cpu.txt log-file. */ #ifdef FORCETEST FILE *FdForceTest; /*!< file handle for forcetest.txt log-file. */ #endif double DriftTable[DRIFT_TABLE_LENGTH]; /*!< table for the cosmological drift factors */ double GravKickTable[DRIFT_TABLE_LENGTH]; /*!< table for the cosmological kick factor for gravitational forces */ double HydroKickTable[DRIFT_TABLE_LENGTH]; /*!< table for the cosmological kick factor for hydrodynmical forces */ void *CommBuffer; /*!< points to communication buffer, which is used in the domain decomposition, the parallel tree-force computation, the SPH routines, etc. */ #ifdef PY_INTERFACE void *CommBufferQ; /*!< points to communication buffer, which is used in the domain decomposition, the parallel tree-force computation, the SPH routines, etc. */ #endif /*! This structure contains data which is the SAME for all tasks (mostly code parameters read from the * parameter file). Holding this data in a structure is convenient for writing/reading the restart file, and * it allows the introduction of new global variables in a simple way. The only thing to do is to introduce * them into this structure. */ struct global_data_all_processes All; /*! This structure holds all the information that is * stored for each particle of the simulation. */ struct particle_data *P, /*!< holds particle data on local processor */ #ifdef PY_INTERFACE *Q, *DomainPartBufQ, /*!< buffer for particle data used in domain decomposition */ #endif *DomainPartBuf; /*!< buffer for particle data used in domain decomposition */ /* the following struture holds data that is stored for each SPH particle in addition to the collisionless * variables. */ struct sph_particle_data *SphP, /*!< holds SPH particle data on local processor */ #ifdef PY_INTERFACE *SphQ, *DomainSphBufQ, /*!< buffer for SPH particle data in domain decomposition */ #endif *DomainSphBuf; /*!< buffer for SPH particle data in domain decomposition */ /* Variables for Tree */ int MaxNodes; /*!< maximum allowed number of internal nodes */ int Numnodestree; /*!< number of (internal) nodes in each tree */ struct NODE *Nodes_base, /*!< points to the actual memory allocted for the nodes */ *Nodes; /*!< this is a pointer used to access the nodes which is shifted such that Nodes[All.MaxPart] gives the first allocated node */ int *Nextnode; /*!< gives next node in tree walk */ int *Father; /*!< gives parent node in tree */ struct extNODE /*!< this structure holds additional tree-node information which is not needed in the actual gravity computation */ *Extnodes_base, /*!< points to the actual memory allocted for the extended node information */ *Extnodes; /*!< provides shifted access to extended node information, parallel to Nodes/Nodes_base */ /*! Header for the standard file format. */ struct io_header header; /*!< holds header for snapshot files */ char Tab_IO_Labels[IO_NBLOCKS][4]; /* #include #include "tags.h" #define GADGETVERSION "2.0" /*!< code version string */ #define TIMEBASE (1<<28) /*!< The simulated timespan is mapped onto the integer interval [0,TIMESPAN], * where TIMESPAN needs to be a power of 2. Note that (1<<28) corresponds to 2^29 */ #define MAXTOPNODES 200000 /*!< Maximum number of nodes in the top-level tree used for domain decomposition */ typedef long long peanokey; /*!< defines the variable type used for Peano-Hilbert keys */ #define BITS_PER_DIMENSION 18 /*!< Bits per dimension available for Peano-Hilbert order. Note: If peanokey is defined as type int, the allowed maximum is 10. If 64-bit integers are used, the maximum is 21 */ #define PEANOCELLS (((peanokey)1)<<(3*BITS_PER_DIMENSION)) /*!< The number of different Peano-Hilbert cells */ #define RNDTABLE 3000 /*!< gives the length of a table with random numbers, refreshed at every timestep. This is used to allow application of random numbers to a specific particle in a way that is independent of the number of processors used. */ #define MAX_REAL_NUMBER 1e37 #define MIN_REAL_NUMBER 1e-37 #define MAXLEN_FILENAME 100 /*!< Maximum number of characters for filenames (including the full path) */ #ifdef ISOTHERM_EQS #define GAMMA (1.0) /*!< index for isothermal gas */ #else #define GAMMA (5.0/3) /*!< adiabatic index of simulated gas */ #endif #define GAMMA_MINUS1 (GAMMA-1) #define HYDROGEN_MASSFRAC 0.76 /*!< mass fraction of hydrogen, relevant only for radiative cooling */ /* Some physical constants in cgs units */ #define GRAVITY 6.672e-8 /*!< Gravitational constant (in cgs units) */ #define SOLAR_MASS 1.989e33 #define SOLAR_LUM 3.826e33 #define RAD_CONST 7.565e-15 #define AVOGADRO 6.0222e23 #define BOLTZMANN 1.3806e-16 #define GAS_CONST 8.31425e7 #define C 2.9979e10 #define PLANCK 6.6262e-27 #define CM_PER_MPC 3.085678e24 #define PROTONMASS 1.6726e-24 #define ELECTRONMASS 9.10953e-28 #define THOMPSON 6.65245e-25 #define ELECTRONCHARGE 4.8032e-10 #define HUBBLE 3.2407789e-18 /* in h/sec */ /* Some conversion factors */ #define SEC_PER_MEGAYEAR 3.155e13 #define SEC_PER_YEAR 3.155e7 #ifndef ASMTH #define ASMTH 1.25 /*!< ASMTH gives the scale of the short-range/long-range force split in units of FFT-mesh cells */ #endif #ifndef RCUT #define RCUT 4.5 /*!< RCUT gives the maximum distance (in units of the scale used for the force split) out to which short-range forces are evaluated in the short-range tree walk. */ #endif #define MAX_NGB 20000 /*!< defines maximum length of neighbour list */ #define MAXLEN_OUTPUTLIST 500 /*!< maxmimum number of entries in list of snapshot output times */ #define DRIFT_TABLE_LENGTH 1000 /*!< length of the lookup table used to hold the drift and kick factors */ #define MAXITER 150 /*!< maxmimum number of steps for SPH neighbour iteration */ #ifdef DOUBLEPRECISION /*!< If defined, the variable type FLOAT is set to "double", otherwise to FLOAT */ #define FLOAT double #else #define FLOAT float #endif #ifndef TWODIMS #define NUMDIMS 3 /*!< For 3D-normalized kernel */ #define KERNEL_COEFF_1 2.546479089470 /*!< Coefficients for SPH spline kernel and its derivative */ #define KERNEL_COEFF_2 15.278874536822 #define KERNEL_COEFF_3 45.836623610466 #define KERNEL_COEFF_4 30.557749073644 #define KERNEL_COEFF_5 5.092958178941 #define KERNEL_COEFF_6 (-15.278874536822) #define NORM_COEFF 4.188790204786 /*!< Coefficient for kernel normalization. Note: 4.0/3 * PI = 4.188790204786 */ #else #define NUMDIMS 2 /*!< For 2D-normalized kernel */ #define KERNEL_COEFF_1 (5.0/7*2.546479089470) /*!< Coefficients for SPH spline kernel and its derivative */ #define KERNEL_COEFF_2 (5.0/7*15.278874536822) #define KERNEL_COEFF_3 (5.0/7*45.836623610466) #define KERNEL_COEFF_4 (5.0/7*30.557749073644) #define KERNEL_COEFF_5 (5.0/7*5.092958178941) #define KERNEL_COEFF_6 (5.0/7*(-15.278874536822)) #define NORM_COEFF M_PI /*!< Coefficient for kernel normalization. */ #endif extern int ThisTask; /*!< the rank of the local processor */ extern int NTask; /*!< number of processors */ extern int PTask; /*!< smallest integer such that NTask <= 2^PTask */ extern int NumPart; /*!< number of particles on the LOCAL processor */ #ifdef PY_INTERFACE extern int NumPartQ; #endif extern int N_gas; /*!< number of gas particles on the LOCAL processor */ #ifdef PY_INTERFACE extern int N_gasQ; #endif extern long long Ntype[6]; /*!< total number of particles of each type */ extern int NtypeLocal[6]; /*!< local number of particles of each type */ #ifdef PY_INTERFACE extern long long NtypeQ[6]; /*!< total number of particles of each type */ extern int NtypeLocalQ[6]; /*!< local number of particles of each type */ #endif extern int NumForceUpdate; /*!< number of active particles on local processor in current timestep */ extern int NumSphUpdate; /*!< number of active SPH particles on local processor in current timestep */ extern double CPUThisRun; /*!< Sums the CPU time for the process (current submission only) */ extern int RestartFlag; /*!< taken from command line used to start code. 0 is normal start-up from initial conditions, 1 is resuming a run from a set of restart files, while 2 marks a restart from a snapshot file. */ extern char *Exportflag; /*!< Buffer used for flagging whether a particle needs to be exported to another process */ extern int *Ngblist; /*!< Buffer to hold indices of neighbours retrieved by the neighbour search routines */ extern int TreeReconstructFlag; /*!< Signals that a new tree needs to be constructed */ extern int Flag_FullStep; /*!< This flag signals that the current step involves all particles */ extern gsl_rng *random_generator; /*!< the employed random number generator of the GSL library */ extern double RndTable[RNDTABLE]; /*!< Hold a table with random numbers, refreshed every timestep */ extern double DomainCorner[3]; /*!< gives the lower left corner of simulation volume */ extern double DomainCenter[3]; /*!< gives the center of simulation volume */ extern double DomainLen; /*!< gives the (maximum) side-length of simulation volume */ extern double DomainFac; /*!< factor used for converting particle coordinates to a Peano-Hilbert mesh covering the simulation volume */ extern int DomainMyStart; /*!< first domain mesh cell that resides on the local processor */ extern int DomainMyLast; /*!< last domain mesh cell that resides on the local processor */ extern int *DomainStartList; /*!< a table that lists the first domain mesh cell for all processors */ extern int *DomainEndList; /*!< a table that lists the last domain mesh cell for all processors */ extern double *DomainWork; /*!< a table that gives the total "work" due to the particles stored by each processor */ extern int *DomainCount; /*!< a table that gives the total number of particles held by each processor */ extern int *DomainCountSph; /*!< a table that gives the total number of SPH particles held by each processor */ extern int *DomainTask; /*!< this table gives for each leaf of the top-level tree the processor it was assigned to */ extern int *DomainNodeIndex; /*!< this table gives for each leaf of the top-level tree the corresponding node of the gravitational tree */ extern FLOAT *DomainTreeNodeLen; /*!< this table gives for each leaf of the top-level tree the side-length of the corresponding node of the gravitational tree */ extern FLOAT *DomainHmax; /*!< this table gives for each leaf of the top-level tree the maximum SPH smoothing length among the particles of the corresponding node of the gravitational tree */ #ifdef PY_INTERFACE extern double DomainCornerQ[3]; /*!< gives the lower left corner of simulation volume */ extern double DomainCenterQ[3]; /*!< gives the center of simulation volume */ extern double DomainLenQ; /*!< gives the (maximum) side-length of simulation volume */ extern double DomainFacQ; /*!< factor used for converting particle coordinates to a Peano-Hilbert mesh covering the simulation volume */ extern int DomainMyStartQ; /*!< first domain mesh cell that resides on the local processor */ extern int DomainMyLastQ; /*!< last domain mesh cell that resides on the local processor */ extern int *DomainStartListQ; /*!< a table that lists the first domain mesh cell for all processors */ extern int *DomainEndListQ; /*!< a table that lists the last domain mesh cell for all processors */ extern double *DomainWorkQ; /*!< a table that gives the total "work" due to the particles stored by each processor */ extern int *DomainCountQ; /*!< a table that gives the total number of particles held by each processor */ extern int *DomainCountSphQ; /*!< a table that gives the total number of SPH particles held by each processor */ extern int *DomainTaskQ; /*!< this table gives for each leaf of the top-level tree the processor it was assigned to */ #endif extern struct DomainNODE { FLOAT s[3]; /*!< center-of-mass coordinates */ FLOAT vs[3]; /*!< center-of-mass velocities */ FLOAT mass; /*!< mass of node */ #ifdef UNEQUALSOFTENINGS #ifndef ADAPTIVE_GRAVSOFT_FORGAS int bitflags; /*!< this bit-field encodes the particle type with the largest softening among the particles of the nodes, and whether there are particles with different softening in the node */ #else FLOAT maxsoft; /*!< hold the maximum gravitational softening of particles in the node if the ADAPTIVE_GRAVSOFT_FORGAS option is selected */ #endif #endif } *DomainMoment; /*!< this table stores for each node of the top-level tree corresponding node data from the gravitational tree */ extern peanokey *DomainKeyBuf; /*!< this points to a buffer used during the exchange of particle data */ #ifdef PY_INTERFACE extern peanokey *DomainKeyBufQ; /*!< this points to a buffer used during the exchange of particle data */ #endif extern peanokey *Key; /*!< a table used for storing Peano-Hilbert keys for particles */ extern peanokey *KeySorted; /*!< holds a sorted table of Peano-Hilbert keys for all particles, used to construct top-level tree */ extern int NTopnodes; /*!< total number of nodes in top-level tree */ extern int NTopleaves; /*!< number of leaves in top-level tree. Each leaf can be assigned to a different processor */ #ifdef PY_INTERFACE extern int NTopnodesQ; /*!< total number of nodes in top-level tree */ extern int NTopleavesQ; /*!< number of leaves in top-level tree. Each leaf can be assigned to a different processor */ #endif extern struct topnode_data { int Daughter; /*!< index of first daughter cell (out of 8) of top-level node */ int Pstart; /*!< for the present top-level node, this gives the index of the first node in the concatenated list of topnodes collected from all processors */ int Blocks; /*!< for the present top-level node, this gives the number of corresponding nodes in the concatenated list of topnodes collected from all processors */ int Leaf; /*!< if the node is a leaf, this gives its number when all leaves are traversed in Peano-Hilbert order */ peanokey Size; /*!< number of Peano-Hilbert mesh-cells represented by top-level node */ peanokey StartKey; /*!< first Peano-Hilbert key in top-level node */ long long Count; /*!< counts the number of particles in this top-level node */ } #ifdef PY_INTERFACE *TopNodesQ, #endif *TopNodes; /*!< points to the root node of the top-level tree */ extern double TimeOfLastTreeConstruction; /*!< holds what it says, only used in connection with FORCETEST */ /* variables for input/output, usually only used on process 0 */ extern char ParameterFile[MAXLEN_FILENAME]; /*!< file name of parameterfile used for starting the simulation */ extern FILE *FdInfo; /*!< file handle for info.txt log-file. */ extern FILE *FdEnergy; /*!< file handle for energy.txt log-file. */ extern FILE *FdTimings; /*!< file handle for timings.txt log-file. */ extern FILE *FdCPU; /*!< file handle for cpu.txt log-file. */ #ifdef FORCETEST extern FILE *FdForceTest; /*!< file handle for forcetest.txt log-file. */ #endif extern double DriftTable[DRIFT_TABLE_LENGTH]; /*!< table for the cosmological drift factors */ extern double GravKickTable[DRIFT_TABLE_LENGTH]; /*!< table for the cosmological kick factor for gravitational forces */ extern double HydroKickTable[DRIFT_TABLE_LENGTH]; /*!< table for the cosmological kick factor for hydrodynmical forces */ extern void *CommBuffer; /*!< points to communication buffer, which is used in the domain decomposition, the parallel tree-force computation, the SPH routines, etc. */ #ifdef PY_INTERFACE extern void *CommBufferQ; /*!< points to communication buffer, which is used in the domain decomposition, the parallel tree-force computation, the SPH routines, etc. */ #endif /*! This structure contains data which is the SAME for all tasks (mostly code parameters read from the * parameter file). Holding this data in a structure is convenient for writing/reading the restart file, and * it allows the introduction of new global variables in a simple way. The only thing to do is to introduce * them into this structure. */ extern struct global_data_all_processes { long long TotNumPart; /*!< total particle numbers (global value) */ long long TotN_gas; /*!< total gas particle number (global value) */ #ifdef PY_INTERFACE long long TotNumPartQ; /*!< total particle numbers (global value) */ long long TotN_gasQ; /*!< total gas particle number (global value) */ #endif int MaxPart; /*!< This gives the maxmimum number of particles that can be stored on one processor. */ int MaxPartSph; /*!< This gives the maxmimum number of SPH particles that can be stored on one processor. */ #ifdef PY_INTERFACE int MaxPartQ; /*!< This gives the maxmimum number of particles that can be stored on one processor. */ int MaxPartSphQ; /*!< This gives the maxmimum number of SPH particles that can be stored on one processor. */ #endif double BoxSize; /*!< Boxsize in case periodic boundary conditions are used */ int ICFormat; /*!< selects different versions of IC file-format */ int SnapFormat; /*!< selects different versions of snapshot file-formats */ int NumFilesPerSnapshot; /*!< number of files in multi-file snapshot dumps */ int NumFilesWrittenInParallel;/*!< maximum number of files that may be written simultaneously when writing/reading restart-files, or when writing snapshot files */ int BufferSize; /*!< size of communication buffer in MB */ int BunchSizeForce; /*!< number of particles fitting into the buffer in the parallel tree-force algorithm */ int BunchSizeDensity; /*!< number of particles fitting into the communication buffer in the density computation */ int BunchSizeHydro; /*!< number of particles fitting into the communication buffer in the SPH hydrodynamical force computation */ int BunchSizeDomain; /*!< number of particles fitting into the communication buffer in the domain decomposition */ #ifdef PY_INTERFACE int BunchSizeSph; + int BunchSizeDensitySph; #endif double PartAllocFactor; /*!< in order to maintain work-load balance, the particle load will usually NOT be balanced. Each processor allocates memory for PartAllocFactor times the average number of particles to allow for that */ double TreeAllocFactor; /*!< Each processor allocates a number of nodes which is TreeAllocFactor times the maximum(!) number of particles. Note: A typical local tree for N particles needs usually about ~0.65*N nodes. */ /* some SPH parameters */ double DesNumNgb; /*!< Desired number of SPH neighbours */ double MaxNumNgbDeviation; /*!< Maximum allowed deviation neighbour number */ double ArtBulkViscConst; /*!< Sets the parameter \f$\alpha\f$ of the artificial viscosity */ double InitGasTemp; /*!< may be used to set the temperature in the IC's */ double MinGasTemp; /*!< may be used to set a floor for the gas temperature */ double MinEgySpec; /*!< the minimum allowed temperature expressed as energy per unit mass */ /* some force counters */ long long TotNumOfForces; /*!< counts total number of force computations */ long long NumForcesSinceLastDomainDecomp; /*!< count particle updates since last domain decomposition */ /* system of units */ double G; /*!< Gravity-constant in internal units */ double UnitTime_in_s; /*!< factor to convert internal time unit to seconds/h */ double UnitMass_in_g; /*!< factor to convert internal mass unit to grams/h */ double UnitVelocity_in_cm_per_s; /*!< factor to convert intqernal velocity unit to cm/sec */ double UnitLength_in_cm; /*!< factor to convert internal length unit to cm/h */ double UnitPressure_in_cgs; /*!< factor to convert internal pressure unit to cgs units (little 'h' still around!) */ double UnitDensity_in_cgs; /*!< factor to convert internal length unit to g/cm^3*h^2 */ double UnitCoolingRate_in_cgs; /*!< factor to convert internal cooling rate to cgs units */ double UnitEnergy_in_cgs; /*!< factor to convert internal energy to cgs units */ double UnitTime_in_Megayears; /*!< factor to convert internal time to megayears/h */ double GravityConstantInternal; /*!< If set to zero in the parameterfile, the internal value of the gravitational constant is set to the Newtonian value based on the system of units specified. Otherwise the value provided is taken as internal gravity constant G. */ /* Cosmological parameters */ double Hubble; /*!< Hubble-constant in internal units */ double Omega0; /*!< matter density in units of the critical density (at z=0)*/ double OmegaLambda; /*!< vaccum energy density relative to crictical density (at z=0) */ double OmegaBaryon; /*!< baryon density in units of the critical density (at z=0)*/ double HubbleParam; /*!< little `h', i.e. Hubble constant in units of 100 km/s/Mpc. Only needed to get absolute physical values for cooling physics */ /* Code options */ int ComovingIntegrationOn; /*!< flags that comoving integration is enabled */ int PeriodicBoundariesOn; /*!< flags that periodic boundaries are enabled */ int ResubmitOn; /*!< flags that automatic resubmission of job to queue system is enabled */ int TypeOfOpeningCriterion; /*!< determines tree cell-opening criterion: 0 for Barnes-Hut, 1 for relative criterion */ int TypeOfTimestepCriterion; /*!< gives type of timestep criterion (only 0 supported right now - unlike gadget-1.1) */ int OutputListOn; /*!< flags that output times are listed in a specified file */ /* Parameters determining output frequency */ int SnapshotFileCount; /*!< number of snapshot that is written next */ double TimeBetSnapshot; /*!< simulation time interval between snapshot files */ double TimeOfFirstSnapshot; /*!< simulation time of first snapshot files */ double CpuTimeBetRestartFile; /*!< cpu-time between regularly generated restart files */ double TimeLastRestartFile; /*!< cpu-time when last restart-file was written */ double TimeBetStatistics; /*!< simulation time interval between computations of energy statistics */ double TimeLastStatistics; /*!< simulation time when the energy statistics was computed the last time */ int NumCurrentTiStep; /*!< counts the number of system steps taken up to this point */ /* Current time of the simulation, global step, and end of simulation */ double Time; /*!< current time of the simulation */ double TimeBegin; /*!< time of initial conditions of the simulation */ double TimeStep; /*!< difference between current times of previous and current timestep */ double TimeMax; /*!< marks the point of time until the simulation is to be evolved */ /* variables for organizing discrete timeline */ double Timebase_interval; /*!< factor to convert from floating point time interval to integer timeline */ int Ti_Current; /*!< current time on integer timeline */ int Ti_nextoutput; /*!< next output time on integer timeline */ #ifdef FLEXSTEPS int PresentMinStep; /*!< If FLEXSTEPS is used, particle timesteps are chosen as multiples of the present minimum timestep. */ int PresentMaxStep; /*!< If FLEXSTEPS is used, this is the maximum timestep in timeline units, rounded down to the next power 2 division */ #endif #ifdef PMGRID int PM_Ti_endstep; /*!< begin of present long-range timestep */ int PM_Ti_begstep; /*!< end of present long-range timestep */ #endif /* Placement of PM grids */ #ifdef PMGRID double Asmth[2]; /*!< Gives the scale of the long-range/short-range split (in mesh-cells), both for the coarse and the high-res mesh */ double Rcut[2]; /*!< Gives the maximum radius for which the short-range force is evaluated with the tree (in mesh-cells), both for the coarse and the high-res mesh */ double Corner[2][3]; /*!< lower left corner of coarse and high-res PM-mesh */ double UpperCorner[2][3]; /*!< upper right corner of coarse and high-res PM-mesh */ double Xmintot[2][3]; /*!< minimum particle coordinates both for coarse and high-res PM-mesh */ double Xmaxtot[2][3]; /*!< maximum particle coordinates both for coarse and high-res PM-mesh */ double TotalMeshSize[2]; /*!< total extension of coarse and high-res PM-mesh */ #endif /* Variables that keep track of cumulative CPU consumption */ double TimeLimitCPU; /*!< CPU time limit as defined in parameterfile */ double CPU_TreeConstruction; /*!< time spent for constructing the gravitational tree */ double CPU_TreeWalk; /*!< actual time spent for pure tree-walks */ double CPU_Gravity; /*!< cumulative time used for gravity computation (tree-algorithm only) */ double CPU_Potential; /*!< time used for computing gravitational potentials */ double CPU_Domain; /*!< cumulative time spent for domain decomposition */ double CPU_Snapshot; /*!< time used for writing snapshot files */ double CPU_Total; /*!< cumulative time spent for domain decomposition */ double CPU_CommSum; /*!< accumulated time used for communication, and for collecting partial results, in tree-gravity */ double CPU_Imbalance; /*!< cumulative time lost accross all processors as work-load imbalance in gravitational tree */ double CPU_HydCompWalk; /*!< time used for actual SPH computations, including neighbour search */ double CPU_HydCommSumm; /*!< cumulative time used for communication in SPH, and for collecting partial results */ double CPU_HydImbalance; /*!< cumulative time lost due to work-load imbalance in SPH */ double CPU_Hydro; /*!< cumulative time spent for SPH related computations */ double CPU_EnsureNgb; /*!< time needed to iterate on correct neighbour numbers */ double CPU_Predict; /*!< cumulative time to drift the system forward in time, including dynamic tree updates */ double CPU_TimeLine; /*!< time used for determining new timesteps, and for organizing the timestepping, including kicks of active particles */ double CPU_PM; /*!< time used for long-range gravitational force */ double CPU_Peano; /*!< time required to establish Peano-Hilbert order */ /* tree code opening criterion */ double ErrTolTheta; /*!< BH tree opening angle */ double ErrTolForceAcc; /*!< parameter for relative opening criterion in tree walk */ /* adjusts accuracy of time-integration */ double ErrTolIntAccuracy; /*!< accuracy tolerance parameter \f$ \eta \f$ for timestep criterion. The timestep is \f$ \Delta t = \sqrt{\frac{2 \eta eps}{a}} \f$ */ double MinSizeTimestep; /*!< minimum allowed timestep. Normally, the simulation terminates if the timestep determined by the timestep criteria falls below this limit. */ double MaxSizeTimestep; /*!< maximum allowed timestep */ double MaxRMSDisplacementFac; /*!< this determines a global timestep criterion for cosmological simulations in comoving coordinates. To this end, the code computes the rms velocity of all particles, and limits the timestep such that the rms displacement is a fraction of the mean particle separation (determined from the particle mass and the cosmological parameters). This parameter specifies this fraction. */ double CourantFac; /*!< SPH-Courant factor */ /* frequency of tree reconstruction/domain decomposition */ double TreeDomainUpdateFrequency; /*!< controls frequency of domain decompositions */ /* Gravitational and hydrodynamical softening lengths (given in terms of an `equivalent' Plummer softening length). * Five groups of particles are supported 0="gas", 1="halo", 2="disk", 3="bulge", 4="stars", 5="bndry" */ double MinGasHsmlFractional; /*!< minimum allowed SPH smoothing length in units of SPH gravitational softening length */ double MinGasHsml; /*!< minimum allowed SPH smoothing length */ double SofteningGas; /*!< comoving gravitational softening lengths for type 0 */ double SofteningHalo; /*!< comoving gravitational softening lengths for type 1 */ double SofteningDisk; /*!< comoving gravitational softening lengths for type 2 */ double SofteningBulge; /*!< comoving gravitational softening lengths for type 3 */ double SofteningStars; /*!< comoving gravitational softening lengths for type 4 */ double SofteningBndry; /*!< comoving gravitational softening lengths for type 5 */ double SofteningGasMaxPhys; /*!< maximum physical softening length for type 0 */ double SofteningHaloMaxPhys; /*!< maximum physical softening length for type 1 */ double SofteningDiskMaxPhys; /*!< maximum physical softening length for type 2 */ double SofteningBulgeMaxPhys; /*!< maximum physical softening length for type 3 */ double SofteningStarsMaxPhys; /*!< maximum physical softening length for type 4 */ double SofteningBndryMaxPhys; /*!< maximum physical softening length for type 5 */ double SofteningTable[6]; /*!< current (comoving) gravitational softening lengths for each particle type */ double ForceSoftening[6]; /*!< the same, but multiplied by a factor 2.8 - at that scale the force is Newtonian */ #ifdef PY_INTERFACE double ForceSofteningQ; #endif double MassTable[6]; /*!< Table with particle masses for particle types with equal mass. If particle masses are all equal for one type, the corresponding entry in MassTable is set to this value, allowing the size of the snapshot files to be reduced. */ /* some filenames */ char InitCondFile[MAXLEN_FILENAME]; /*!< filename of initial conditions */ char OutputDir[MAXLEN_FILENAME]; /*!< output directory of the code */ char SnapshotFileBase[MAXLEN_FILENAME]; /*!< basename to construct the names of snapshotf files */ char EnergyFile[MAXLEN_FILENAME]; /*!< name of file with energy statistics */ char CpuFile[MAXLEN_FILENAME]; /*!< name of file with cpu-time statistics */ char InfoFile[MAXLEN_FILENAME]; /*!< name of log-file with a list of the timesteps taken */ char TimingsFile[MAXLEN_FILENAME]; /*!< name of file with performance metrics of gravitational tree algorithm */ char RestartFile[MAXLEN_FILENAME]; /*!< basename of restart-files */ char ResubmitCommand[MAXLEN_FILENAME]; /*!< name of script-file that will be executed for automatic restart */ char OutputListFilename[MAXLEN_FILENAME]; /*!< name of file with list of desired output times */ double OutputListTimes[MAXLEN_OUTPUTLIST]; /*!< table with desired output times */ int OutputListLength; /*!< number of output times stored in the table of desired output times */ } All; /*!< a container variable for global variables that are equal on all processors */ /*! This structure holds all the information that is * stored for each particle of the simulation. */ extern struct particle_data { FLOAT Pos[3]; /*!< particle position at its current time */ FLOAT Mass; /*!< particle mass */ FLOAT Vel[3]; /*!< particle velocity at its current time */ FLOAT GravAccel[3]; /*!< particle acceleration due to gravity */ #ifdef PMGRID FLOAT GravPM[3]; /*!< particle acceleration due to long-range PM gravity force*/ #endif #ifdef FORCETEST FLOAT GravAccelDirect[3]; /*!< particle acceleration when computed with direct summation */ #endif FLOAT Potential; /*!< gravitational potential */ FLOAT OldAcc; /*!< magnitude of old gravitational force. Used in relative opening criterion */ #ifndef LONGIDS unsigned int ID; /*!< particle identifier */ #else unsigned long long ID; /*!< particle identifier */ #endif int Type; /*!< flags particle type. 0=gas, 1=halo, 2=disk, 3=bulge, 4=stars, 5=bndry */ int Ti_endstep; /*!< marks start of current timestep of particle on integer timeline */ int Ti_begstep; /*!< marks end of current timestep of particle on integer timeline */ #ifdef FLEXSTEPS int FlexStepGrp; /*!< a random 'offset' on the timeline to create a smooth groouping of particles */ #endif float GravCost; /*!< weight factor used for balancing the work-load */ #ifdef PSEUDOSYMMETRIC float AphysOld; /*!< magnitude of acceleration in last timestep. Used to make a first order prediction of the change of acceleration expected in the future, thereby allowing to guess whether a decrease/increase of the timestep should occur in the timestep that is started. */ #endif } *P, /*!< holds particle data on local processor */ #ifdef PY_INTERFACE *Q, *DomainPartBufQ, /*!< buffer for particle data used in domain decomposition */ #endif *DomainPartBuf; /*!< buffer for particle data used in domain decomposition */ /* the following struture holds data that is stored for each SPH particle in addition to the collisionless * variables. */ extern struct sph_particle_data { FLOAT Entropy; /*!< current value of entropy (actually entropic function) of particle */ FLOAT Density; /*!< current baryonic mass density of particle */ FLOAT Hsml; /*!< current smoothing length */ FLOAT Left; /*!< lower bound in iterative smoothing length search */ FLOAT Right; /*!< upper bound in iterative smoothing length search */ FLOAT NumNgb; /*!< weighted number of neighbours found */ FLOAT Pressure; /*!< current pressure */ FLOAT DtEntropy; /*!< rate of change of entropy */ FLOAT HydroAccel[3]; /*!< acceleration due to hydrodynamical force */ FLOAT VelPred[3]; /*!< predicted SPH particle velocity at the current time */ FLOAT DivVel; /*!< local velocity divergence */ FLOAT CurlVel; /*!< local velocity curl */ FLOAT Rot[3]; /*!< local velocity curl */ FLOAT DhsmlDensityFactor; /*!< correction factor needed in the equation of motion of the conservative entropy formulation of SPH */ FLOAT MaxSignalVel; /*!< maximum "signal velocity" occuring for this particle */ #if PY_INTERFACE FLOAT Observable; FLOAT ObsMoment0; FLOAT ObsMoment1; FLOAT GradObservable[3]; #endif } *SphP, /*!< holds SPH particle data on local processor */ #ifdef PY_INTERFACE *SphQ, *DomainSphBufQ, /*!< buffer for SPH particle data in domain decomposition */ #endif *DomainSphBuf; /*!< buffer for SPH particle data in domain decomposition */ /* Variables for Tree */ extern int MaxNodes; /*!< maximum allowed number of internal nodes */ extern int Numnodestree; /*!< number of (internal) nodes in each tree */ extern struct NODE { FLOAT len; /*!< sidelength of treenode */ FLOAT center[3]; /*!< geometrical center of node */ #ifdef ADAPTIVE_GRAVSOFT_FORGAS FLOAT maxsoft; /*!< hold the maximum gravitational softening of particles in the node if the ADAPTIVE_GRAVSOFT_FORGAS option is selected */ #endif union { int suns[8]; /*!< temporary pointers to daughter nodes */ struct { FLOAT s[3]; /*!< center of mass of node */ FLOAT mass; /*!< mass of node */ int bitflags; /*!< a bit-field with various information on the node */ int sibling; /*!< this gives the next node in the walk in case the current node can be used */ int nextnode; /*!< this gives the next node in case the current node needs to be opened */ int father; /*!< this gives the parent node of each node (or -1 if we have the root node) */ } d; } u; } *Nodes_base, /*!< points to the actual memory allocted for the nodes */ *Nodes; /*!< this is a pointer used to access the nodes which is shifted such that Nodes[All.MaxPart] gives the first allocated node */ extern int *Nextnode; /*!< gives next node in tree walk */ extern int *Father; /*!< gives parent node in tree */ extern struct extNODE /*!< this structure holds additional tree-node information which is not needed in the actual gravity computation */ { FLOAT hmax; /*!< maximum SPH smoothing length in node. Only used for gas particles */ FLOAT vs[3]; /*!< center-of-mass velocity */ } *Extnodes_base, /*!< points to the actual memory allocted for the extended node information */ *Extnodes; /*!< provides shifted access to extended node information, parallel to Nodes/Nodes_base */ /*! Header for the standard file format. */ extern struct io_header { int npart[6]; /*!< number of particles of each type in this file */ double mass[6]; /*!< mass of particles of each type. If 0, then the masses are explicitly stored in the mass-block of the snapshot file, otherwise they are omitted */ double time; /*!< time of snapshot file */ double redshift; /*!< redshift of snapshot file */ int flag_sfr; /*!< flags whether the simulation was including star formation */ int flag_feedback; /*!< flags whether feedback was included (obsolete) */ unsigned int npartTotal[6]; /*!< total number of particles of each type in this snapshot. This can be different from npart if one is dealing with a multi-file snapshot. */ int flag_cooling; /*!< flags whether cooling was included */ int num_files; /*!< number of files in multi-file snapshot */ double BoxSize; /*!< box-size of simulation in case periodic boundaries were used */ double Omega0; /*!< matter density in units of critical density */ double OmegaLambda; /*!< cosmological constant parameter */ double HubbleParam; /*!< Hubble parameter in units of 100 km/sec/Mpc */ int flag_stellarage; /*!< flags whether the file contains formation times of star particles */ int flag_metals; /*!< flags whether the file contains metallicity values for gas and star particles */ unsigned int npartTotalHighWord[6]; /*!< High word of the total number of particles of each type */ int flag_entropy_instead_u; /*!< flags that IC-file contains entropy instead of u */ char fill[60]; /*!< fills to 256 Bytes */ } header; /*!< holds header for snapshot files */ #define IO_NBLOCKS 11 /*!< total number of defined information blocks for snapshot files. Must be equal to the number of entries in "enum iofields" */ enum iofields /*!< this enumeration lists the defined output blocks in snapshot files. Not all of them need to be present. */ { IO_POS, IO_VEL, IO_ID, IO_MASS, IO_U, IO_RHO, IO_HSML, IO_POT, IO_ACCEL, IO_DTENTR, IO_TSTP, }; extern char Tab_IO_Labels[IO_NBLOCKS][4]; /* #include #include #include #include #include "allvars.h" #include "proto.h" /*! \file density.c * \brief SPH density computation and smoothing length determination * * This file contains the "first SPH loop", where the SPH densities and * some auxiliary quantities are computed. If the number of neighbours * obtained falls outside the target range, the correct smoothing * length is determined iteratively, if needed. */ #ifdef PERIODIC static double boxSize, boxHalf; #ifdef LONG_X static double boxSize_X, boxHalf_X; #else #define boxSize_X boxSize #define boxHalf_X boxHalf #endif #ifdef LONG_Y static double boxSize_Y, boxHalf_Y; #else #define boxSize_Y boxSize #define boxHalf_Y boxHalf #endif #ifdef LONG_Z static double boxSize_Z, boxHalf_Z; #else #define boxSize_Z boxSize #define boxHalf_Z boxHalf #endif #endif /*! This function computes the local density for each active SPH particle, * the number of neighbours in the current smoothing radius, and the * divergence and curl of the velocity field. The pressure is updated as * well. If a particle with its smoothing region is fully inside the * local domain, it is not exported to the other processors. The function * also detects particles that have a number of neighbours outside the * allowed tolerance range. For these particles, the smoothing length is * adjusted accordingly, and the density computation is executed again. * Note that the smoothing length is not allowed to fall below the lower * bound set by MinGasHsml. */ void density(void) { long long ntot, ntotleft; int *noffset, *nbuffer, *nsend, *nsend_local, *numlist, *ndonelist; int i, j, n, ndone, npleft, maxfill, source, iter = 0; int level, ngrp, sendTask, recvTask, place, nexport; double dt_entr, tstart, tend, tstart_ngb = 0, tend_ngb = 0; double sumt, sumcomm, timengb, sumtimengb; double timecomp = 0, timeimbalance = 0, timecommsumm = 0, sumimbalance; MPI_Status status; #ifdef PERIODIC boxSize = All.BoxSize; boxHalf = 0.5 * All.BoxSize; #ifdef LONG_X boxHalf_X = boxHalf * LONG_X; boxSize_X = boxSize * LONG_X; #endif #ifdef LONG_Y boxHalf_Y = boxHalf * LONG_Y; boxSize_Y = boxSize * LONG_Y; #endif #ifdef LONG_Z boxHalf_Z = boxHalf * LONG_Z; boxSize_Z = boxSize * LONG_Z; #endif #endif noffset = malloc(sizeof(int) * NTask); /* offsets of bunches in common list */ nbuffer = malloc(sizeof(int) * NTask); nsend_local = malloc(sizeof(int) * NTask); nsend = malloc(sizeof(int) * NTask * NTask); ndonelist = malloc(sizeof(int) * NTask); for(n = 0, NumSphUpdate = 0; n < N_gas; n++) { SphP[n].Left = SphP[n].Right = 0; if(P[n].Ti_endstep == All.Ti_Current) NumSphUpdate++; } numlist = malloc(NTask * sizeof(int) * NTask); MPI_Allgather(&NumSphUpdate, 1, MPI_INT, numlist, 1, MPI_INT, MPI_COMM_WORLD); for(i = 0, ntot = 0; i < NTask; i++) ntot += numlist[i]; free(numlist); /* we will repeat the whole thing for those particles where we didn't * find enough neighbours */ do { i = 0; /* beginn with this index */ ntotleft = ntot; /* particles left for all tasks together */ while(ntotleft > 0) { for(j = 0; j < NTask; j++) nsend_local[j] = 0; /* do local particles and prepare export list */ tstart = second(); for(nexport = 0, ndone = 0; i < N_gas && nexport < All.BunchSizeDensity - NTask; i++) if(P[i].Ti_endstep == All.Ti_Current) { ndone++; for(j = 0; j < NTask; j++) Exportflag[j] = 0; density_evaluate(i, 0); for(j = 0; j < NTask; j++) { if(Exportflag[j]) { DensDataIn[nexport].Pos[0] = P[i].Pos[0]; DensDataIn[nexport].Pos[1] = P[i].Pos[1]; DensDataIn[nexport].Pos[2] = P[i].Pos[2]; DensDataIn[nexport].Vel[0] = SphP[i].VelPred[0]; DensDataIn[nexport].Vel[1] = SphP[i].VelPred[1]; DensDataIn[nexport].Vel[2] = SphP[i].VelPred[2]; DensDataIn[nexport].Hsml = SphP[i].Hsml; DensDataIn[nexport].Index = i; DensDataIn[nexport].Task = j; nexport++; nsend_local[j]++; } } } tend = second(); timecomp += timediff(tstart, tend); qsort(DensDataIn, nexport, sizeof(struct densdata_in), dens_compare_key); for(j = 1, noffset[0] = 0; j < NTask; j++) noffset[j] = noffset[j - 1] + nsend_local[j - 1]; tstart = second(); MPI_Allgather(nsend_local, NTask, MPI_INT, nsend, NTask, MPI_INT, MPI_COMM_WORLD); tend = second(); timeimbalance += timediff(tstart, tend); /* now do the particles that need to be exported */ for(level = 1; level < (1 << PTask); level++) { tstart = second(); for(j = 0; j < NTask; j++) nbuffer[j] = 0; for(ngrp = level; ngrp < (1 << PTask); ngrp++) { maxfill = 0; for(j = 0; j < NTask; j++) { if((j ^ ngrp) < NTask) if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]) maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]; } if(maxfill >= All.BunchSizeDensity) break; sendTask = ThisTask; recvTask = ThisTask ^ ngrp; if(recvTask < NTask) { if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0) { /* get the particles */ MPI_Sendrecv(&DensDataIn[noffset[recvTask]], nsend_local[recvTask] * sizeof(struct densdata_in), MPI_BYTE, recvTask, TAG_DENS_A, &DensDataGet[nbuffer[ThisTask]], nsend[recvTask * NTask + ThisTask] * sizeof(struct densdata_in), MPI_BYTE, recvTask, TAG_DENS_A, MPI_COMM_WORLD, &status); } } for(j = 0; j < NTask; j++) if((j ^ ngrp) < NTask) nbuffer[j] += nsend[(j ^ ngrp) * NTask + j]; } tend = second(); timecommsumm += timediff(tstart, tend); tstart = second(); for(j = 0; j < nbuffer[ThisTask]; j++) density_evaluate(j, 1); tend = second(); timecomp += timediff(tstart, tend); /* do a block to explicitly measure imbalance */ tstart = second(); MPI_Barrier(MPI_COMM_WORLD); tend = second(); timeimbalance += timediff(tstart, tend); /* get the result */ tstart = second(); for(j = 0; j < NTask; j++) nbuffer[j] = 0; for(ngrp = level; ngrp < (1 << PTask); ngrp++) { maxfill = 0; for(j = 0; j < NTask; j++) { if((j ^ ngrp) < NTask) if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]) maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]; } if(maxfill >= All.BunchSizeDensity) break; sendTask = ThisTask; recvTask = ThisTask ^ ngrp; if(recvTask < NTask) { if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0) { /* send the results */ MPI_Sendrecv(&DensDataResult[nbuffer[ThisTask]], nsend[recvTask * NTask + ThisTask] * sizeof(struct densdata_out), MPI_BYTE, recvTask, TAG_DENS_B, &DensDataPartialResult[noffset[recvTask]], nsend_local[recvTask] * sizeof(struct densdata_out), MPI_BYTE, recvTask, TAG_DENS_B, MPI_COMM_WORLD, &status); /* add the result to the particles */ for(j = 0; j < nsend_local[recvTask]; j++) { source = j + noffset[recvTask]; place = DensDataIn[source].Index; SphP[place].NumNgb += DensDataPartialResult[source].Ngb; SphP[place].Density += DensDataPartialResult[source].Rho; SphP[place].DivVel += DensDataPartialResult[source].Div; SphP[place].DhsmlDensityFactor += DensDataPartialResult[source].DhsmlDensity; SphP[place].Rot[0] += DensDataPartialResult[source].Rot[0]; SphP[place].Rot[1] += DensDataPartialResult[source].Rot[1]; SphP[place].Rot[2] += DensDataPartialResult[source].Rot[2]; } } } for(j = 0; j < NTask; j++) if((j ^ ngrp) < NTask) nbuffer[j] += nsend[(j ^ ngrp) * NTask + j]; } tend = second(); timecommsumm += timediff(tstart, tend); level = ngrp - 1; } MPI_Allgather(&ndone, 1, MPI_INT, ndonelist, 1, MPI_INT, MPI_COMM_WORLD); for(j = 0; j < NTask; j++) ntotleft -= ndonelist[j]; } /* do final operations on results */ tstart = second(); for(i = 0, npleft = 0; i < N_gas; i++) { if(P[i].Ti_endstep == All.Ti_Current) { { SphP[i].DhsmlDensityFactor = 1 / (1 + SphP[i].Hsml * SphP[i].DhsmlDensityFactor / (NUMDIMS * SphP[i].Density)); SphP[i].CurlVel = sqrt(SphP[i].Rot[0] * SphP[i].Rot[0] + SphP[i].Rot[1] * SphP[i].Rot[1] + SphP[i].Rot[2] * SphP[i].Rot[2]) / SphP[i].Density; SphP[i].DivVel /= SphP[i].Density; dt_entr = (All.Ti_Current - (P[i].Ti_begstep + P[i].Ti_endstep) / 2) * All.Timebase_interval; SphP[i].Pressure = (SphP[i].Entropy + SphP[i].DtEntropy * dt_entr) * pow(SphP[i].Density, GAMMA); } /* now check whether we had enough neighbours */ if(SphP[i].NumNgb < (All.DesNumNgb - All.MaxNumNgbDeviation) || (SphP[i].NumNgb > (All.DesNumNgb + All.MaxNumNgbDeviation) && SphP[i].Hsml > (1.01 * All.MinGasHsml))) { /* need to redo this particle */ npleft++; if(SphP[i].Left > 0 && SphP[i].Right > 0) if((SphP[i].Right - SphP[i].Left) < 1.0e-3 * SphP[i].Left) { /* this one should be ok */ npleft--; P[i].Ti_endstep = -P[i].Ti_endstep - 1; /* Mark as inactive */ continue; } if(SphP[i].NumNgb < (All.DesNumNgb - All.MaxNumNgbDeviation)) SphP[i].Left = dmax(SphP[i].Hsml, SphP[i].Left); else { if(SphP[i].Right != 0) { if(SphP[i].Hsml < SphP[i].Right) SphP[i].Right = SphP[i].Hsml; } else SphP[i].Right = SphP[i].Hsml; } if(iter >= MAXITER - 10) { printf ("i=%d task=%d ID=%d Hsml=%g Left=%g Right=%g Ngbs=%g Right-Left=%g\n pos=(%g|%g|%g)\n", i, ThisTask, (int) P[i].ID, SphP[i].Hsml, SphP[i].Left, SphP[i].Right, (float) SphP[i].NumNgb, SphP[i].Right - SphP[i].Left, P[i].Pos[0], P[i].Pos[1], P[i].Pos[2]); fflush(stdout); } if(SphP[i].Right > 0 && SphP[i].Left > 0) SphP[i].Hsml = pow(0.5 * (pow(SphP[i].Left, 3) + pow(SphP[i].Right, 3)), 1.0 / 3); else { if(SphP[i].Right == 0 && SphP[i].Left == 0) endrun(8188); /* can't occur */ if(SphP[i].Right == 0 && SphP[i].Left > 0) { if(P[i].Type == 0 && fabs(SphP[i].NumNgb - All.DesNumNgb) < 0.5 * All.DesNumNgb) { SphP[i].Hsml *= 1 - (SphP[i].NumNgb - All.DesNumNgb) / (NUMDIMS * SphP[i].NumNgb) * SphP[i].DhsmlDensityFactor; } else SphP[i].Hsml *= 1.26; } if(SphP[i].Right > 0 && SphP[i].Left == 0) { if(P[i].Type == 0 && fabs(SphP[i].NumNgb - All.DesNumNgb) < 0.5 * All.DesNumNgb) { SphP[i].Hsml *= 1 - (SphP[i].NumNgb - All.DesNumNgb) / (NUMDIMS * SphP[i].NumNgb) * SphP[i].DhsmlDensityFactor; } else SphP[i].Hsml /= 1.26; } } if(SphP[i].Hsml < All.MinGasHsml) SphP[i].Hsml = All.MinGasHsml; } else P[i].Ti_endstep = -P[i].Ti_endstep - 1; /* Mark as inactive */ } } tend = second(); timecomp += timediff(tstart, tend); numlist = malloc(NTask * sizeof(int) * NTask); MPI_Allgather(&npleft, 1, MPI_INT, numlist, 1, MPI_INT, MPI_COMM_WORLD); for(i = 0, ntot = 0; i < NTask; i++) ntot += numlist[i]; free(numlist); if(ntot > 0) { if(iter == 0) tstart_ngb = second(); iter++; if(iter > 0 && ThisTask == 0) { printf("ngb iteration %d: need to repeat for %d%09d particles.\n", iter, (int) (ntot / 1000000000), (int) (ntot % 1000000000)); fflush(stdout); } if(iter > MAXITER) { printf("failed to converge in neighbour iteration in density()\n"); fflush(stdout); endrun(1155); } } else tend_ngb = second(); } while(ntot > 0); /* mark as active again */ for(i = 0; i < NumPart; i++) if(P[i].Ti_endstep < 0) P[i].Ti_endstep = -P[i].Ti_endstep - 1; free(ndonelist); free(nsend); free(nsend_local); free(nbuffer); free(noffset); /* collect some timing information */ if(iter > 0) timengb = timediff(tstart_ngb, tend_ngb); else timengb = 0; MPI_Reduce(&timengb, &sumtimengb, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&timecomp, &sumt, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&timecommsumm, &sumcomm, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&timeimbalance, &sumimbalance, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if(ThisTask == 0) { All.CPU_HydCompWalk += sumt / NTask; All.CPU_HydCommSumm += sumcomm / NTask; All.CPU_HydImbalance += sumimbalance / NTask; All.CPU_EnsureNgb += sumtimengb / NTask; } } /*! This function represents the core of the SPH density computation. The * target particle may either be local, or reside in the communication * buffer. */ void density_evaluate(int target, int mode) { int j, n, startnode, numngb, numngb_inbox; double h, h2, fac, hinv, hinv3, hinv4; double rho, divv, wk, dwk; double dx, dy, dz, r, r2, u, mass_j; double dvx, dvy, dvz, rotv[3]; double weighted_numngb, dhsmlrho; FLOAT *pos, *vel; if(mode == 0) { pos = P[target].Pos; vel = SphP[target].VelPred; h = SphP[target].Hsml; } else { pos = DensDataGet[target].Pos; vel = DensDataGet[target].Vel; h = DensDataGet[target].Hsml; } h2 = h * h; hinv = 1.0 / h; #ifndef TWODIMS hinv3 = hinv * hinv * hinv; #else hinv3 = hinv * hinv / boxSize_Z; #endif hinv4 = hinv3 * hinv; rho = divv = rotv[0] = rotv[1] = rotv[2] = 0; weighted_numngb = 0; dhsmlrho = 0; startnode = All.MaxPart; numngb = 0; do { numngb_inbox = ngb_treefind_variable(&pos[0], h, &startnode); for(n = 0; n < numngb_inbox; n++) { j = Ngblist[n]; dx = pos[0] - P[j].Pos[0]; dy = pos[1] - P[j].Pos[1]; dz = pos[2] - P[j].Pos[2]; #ifdef PERIODIC /* now find the closest image in the given box size */ if(dx > boxHalf_X) dx -= boxSize_X; if(dx < -boxHalf_X) dx += boxSize_X; if(dy > boxHalf_Y) dy -= boxSize_Y; if(dy < -boxHalf_Y) dy += boxSize_Y; if(dz > boxHalf_Z) dz -= boxSize_Z; if(dz < -boxHalf_Z) dz += boxSize_Z; #endif r2 = dx * dx + dy * dy + dz * dz; if(r2 < h2) { numngb++; r = sqrt(r2); u = r * hinv; if(u < 0.5) { wk = hinv3 * (KERNEL_COEFF_1 + KERNEL_COEFF_2 * (u - 1) * u * u); dwk = hinv4 * u * (KERNEL_COEFF_3 * u - KERNEL_COEFF_4); } else { wk = hinv3 * KERNEL_COEFF_5 * (1.0 - u) * (1.0 - u) * (1.0 - u); dwk = hinv4 * KERNEL_COEFF_6 * (1.0 - u) * (1.0 - u); } mass_j = P[j].Mass; rho += mass_j * wk; weighted_numngb += NORM_COEFF * wk / hinv3; dhsmlrho += -mass_j * (NUMDIMS * hinv * wk + u * dwk); if(r > 0) { fac = mass_j * dwk / r; dvx = vel[0] - SphP[j].VelPred[0]; dvy = vel[1] - SphP[j].VelPred[1]; dvz = vel[2] - SphP[j].VelPred[2]; divv -= fac * (dx * dvx + dy * dvy + dz * dvz); rotv[0] += fac * (dz * dvy - dy * dvz); rotv[1] += fac * (dx * dvz - dz * dvx); rotv[2] += fac * (dy * dvx - dx * dvy); } } } } while(startnode >= 0); if(mode == 0) { SphP[target].NumNgb = weighted_numngb; SphP[target].Density = rho; SphP[target].DivVel = divv; SphP[target].DhsmlDensityFactor = dhsmlrho; SphP[target].Rot[0] = rotv[0]; SphP[target].Rot[1] = rotv[1]; SphP[target].Rot[2] = rotv[2]; } else { DensDataResult[target].Rho = rho; DensDataResult[target].Div = divv; DensDataResult[target].Ngb = weighted_numngb; DensDataResult[target].DhsmlDensity = dhsmlrho; DensDataResult[target].Rot[0] = rotv[0]; DensDataResult[target].Rot[1] = rotv[1]; DensDataResult[target].Rot[2] = rotv[2]; } } #ifdef PY_INTERFACE /*! This function computes the local density for each active SPH particle, * the number of neighbours in the current smoothing radius, and the * divergence and curl of the velocity field. The pressure is updated as * well. If a particle with its smoothing region is fully inside the * local domain, it is not exported to the other processors. The function * also detects particles that have a number of neighbours outside the * allowed tolerance range. For these particles, the smoothing length is * adjusted accordingly, and the density computation is executed again. * Note that the smoothing length is not allowed to fall below the lower * bound set by MinGasHsml. */ void density_sub(void) { long long ntot, ntotleft; int *noffset, *nbuffer, *nsend, *nsend_local, *numlist, *ndonelist; int i, j, n, ndone, npleft, maxfill, source, iter = 0; int level, ngrp, sendTask, recvTask, place, nexport; double dt_entr, tstart, tend, tstart_ngb = 0, tend_ngb = 0; double sumt, sumcomm, timengb, sumtimengb; double timecomp = 0, timeimbalance = 0, timecommsumm = 0, sumimbalance; MPI_Status status; #ifdef PERIODIC boxSize = All.BoxSize; boxHalf = 0.5 * All.BoxSize; #ifdef LONG_X boxHalf_X = boxHalf * LONG_X; boxSize_X = boxSize * LONG_X; #endif #ifdef LONG_Y boxHalf_Y = boxHalf * LONG_Y; boxSize_Y = boxSize * LONG_Y; #endif #ifdef LONG_Z boxHalf_Z = boxHalf * LONG_Z; boxSize_Z = boxSize * LONG_Z; #endif #endif noffset = malloc(sizeof(int) * NTask); /* offsets of bunches in common list */ nbuffer = malloc(sizeof(int) * NTask); nsend_local = malloc(sizeof(int) * NTask); nsend = malloc(sizeof(int) * NTask * NTask); ndonelist = malloc(sizeof(int) * NTask); for(n = 0, NumSphUpdate = 0; n < N_gasQ; n++) { SphQ[n].Left = SphQ[n].Right = 0; //if(Q[n].Ti_endstep == All.Ti_Current) Q[n].Ti_endstep = All.Ti_Current; NumSphUpdate++; } numlist = malloc(NTask * sizeof(int) * NTask); MPI_Allgather(&NumSphUpdate, 1, MPI_INT, numlist, 1, MPI_INT, MPI_COMM_WORLD); for(i = 0, ntot = 0; i < NTask; i++) ntot += numlist[i]; free(numlist); /* we will repeat the whole thing for those particles where we didn't * find enough neighbours */ do { i = 0; /* beginn with this index */ ntotleft = ntot; /* particles left for all tasks together */ while(ntotleft > 0) { for(j = 0; j < NTask; j++) nsend_local[j] = 0; /* do local particles and prepare export list */ tstart = second(); for(nexport = 0, ndone = 0; i < N_gasQ && nexport < All.BunchSizeDensity - NTask; i++) if(Q[i].Ti_endstep == All.Ti_Current) { ndone++; for(j = 0; j < NTask; j++) Exportflag[j] = 0; density_evaluate_sub(i, 0); for(j = 0; j < NTask; j++) { if(Exportflag[j]) { DensDataIn[nexport].Pos[0] = Q[i].Pos[0]; DensDataIn[nexport].Pos[1] = Q[i].Pos[1]; DensDataIn[nexport].Pos[2] = Q[i].Pos[2]; DensDataIn[nexport].Vel[0] = SphQ[i].VelPred[0]; DensDataIn[nexport].Vel[1] = SphQ[i].VelPred[1]; DensDataIn[nexport].Vel[2] = SphQ[i].VelPred[2]; DensDataIn[nexport].Hsml = SphQ[i].Hsml; DensDataIn[nexport].Index = i; DensDataIn[nexport].Task = j; nexport++; nsend_local[j]++; } } } tend = second(); timecomp += timediff(tstart, tend); qsort(DensDataIn, nexport, sizeof(struct densdata_in), dens_compare_key); for(j = 1, noffset[0] = 0; j < NTask; j++) noffset[j] = noffset[j - 1] + nsend_local[j - 1]; tstart = second(); MPI_Allgather(nsend_local, NTask, MPI_INT, nsend, NTask, MPI_INT, MPI_COMM_WORLD); tend = second(); timeimbalance += timediff(tstart, tend); /* now do the particles that need to be exported */ for(level = 1; level < (1 << PTask); level++) { tstart = second(); for(j = 0; j < NTask; j++) nbuffer[j] = 0; for(ngrp = level; ngrp < (1 << PTask); ngrp++) { maxfill = 0; for(j = 0; j < NTask; j++) { if((j ^ ngrp) < NTask) if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]) maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]; } if(maxfill >= All.BunchSizeDensity) break; sendTask = ThisTask; recvTask = ThisTask ^ ngrp; if(recvTask < NTask) { if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0) { /* get the particles */ MPI_Sendrecv(&DensDataIn[noffset[recvTask]], nsend_local[recvTask] * sizeof(struct densdata_in), MPI_BYTE, recvTask, TAG_DENS_A, &DensDataGet[nbuffer[ThisTask]], nsend[recvTask * NTask + ThisTask] * sizeof(struct densdata_in), MPI_BYTE, recvTask, TAG_DENS_A, MPI_COMM_WORLD, &status); } } for(j = 0; j < NTask; j++) if((j ^ ngrp) < NTask) nbuffer[j] += nsend[(j ^ ngrp) * NTask + j]; } tend = second(); timecommsumm += timediff(tstart, tend); tstart = second(); for(j = 0; j < nbuffer[ThisTask]; j++) density_evaluate_sub(j, 1); tend = second(); timecomp += timediff(tstart, tend); /* do a block to explicitly measure imbalance */ tstart = second(); MPI_Barrier(MPI_COMM_WORLD); tend = second(); timeimbalance += timediff(tstart, tend); /* get the result */ tstart = second(); for(j = 0; j < NTask; j++) nbuffer[j] = 0; for(ngrp = level; ngrp < (1 << PTask); ngrp++) { maxfill = 0; for(j = 0; j < NTask; j++) { if((j ^ ngrp) < NTask) if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]) maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]; } if(maxfill >= All.BunchSizeDensity) break; sendTask = ThisTask; recvTask = ThisTask ^ ngrp; if(recvTask < NTask) { if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0) { /* send the results */ MPI_Sendrecv(&DensDataResult[nbuffer[ThisTask]], nsend[recvTask * NTask + ThisTask] * sizeof(struct densdata_out), MPI_BYTE, recvTask, TAG_DENS_B, &DensDataPartialResult[noffset[recvTask]], nsend_local[recvTask] * sizeof(struct densdata_out), MPI_BYTE, recvTask, TAG_DENS_B, MPI_COMM_WORLD, &status); /* add the result to the particles */ for(j = 0; j < nsend_local[recvTask]; j++) { source = j + noffset[recvTask]; place = DensDataIn[source].Index; SphQ[place].NumNgb += DensDataPartialResult[source].Ngb; SphQ[place].Density += DensDataPartialResult[source].Rho; SphQ[place].DivVel += DensDataPartialResult[source].Div; SphQ[place].DhsmlDensityFactor += DensDataPartialResult[source].DhsmlDensity; SphQ[place].Rot[0] += DensDataPartialResult[source].Rot[0]; SphQ[place].Rot[1] += DensDataPartialResult[source].Rot[1]; SphQ[place].Rot[2] += DensDataPartialResult[source].Rot[2]; } } } for(j = 0; j < NTask; j++) if((j ^ ngrp) < NTask) nbuffer[j] += nsend[(j ^ ngrp) * NTask + j]; } tend = second(); timecommsumm += timediff(tstart, tend); level = ngrp - 1; } MPI_Allgather(&ndone, 1, MPI_INT, ndonelist, 1, MPI_INT, MPI_COMM_WORLD); for(j = 0; j < NTask; j++) ntotleft -= ndonelist[j]; } /* do final operations on results */ tstart = second(); for(i = 0, npleft = 0; i < N_gasQ; i++) { if(Q[i].Ti_endstep == All.Ti_Current) { { SphQ[i].DhsmlDensityFactor = 1 / (1 + SphQ[i].Hsml * SphQ[i].DhsmlDensityFactor / (NUMDIMS * SphQ[i].Density)); SphQ[i].CurlVel = sqrt(SphQ[i].Rot[0] * SphQ[i].Rot[0] + SphQ[i].Rot[1] * SphQ[i].Rot[1] + SphQ[i].Rot[2] * SphQ[i].Rot[2]) / SphQ[i].Density; SphQ[i].DivVel /= SphQ[i].Density; dt_entr = (All.Ti_Current - (Q[i].Ti_begstep + Q[i].Ti_endstep) / 2) * All.Timebase_interval; SphQ[i].Pressure = (SphQ[i].Entropy + SphQ[i].DtEntropy * dt_entr) * pow(SphQ[i].Density, GAMMA); } /* now check whether we had enough neighbours */ if(SphQ[i].NumNgb < (All.DesNumNgb - All.MaxNumNgbDeviation) || (SphQ[i].NumNgb > (All.DesNumNgb + All.MaxNumNgbDeviation) && SphQ[i].Hsml > (1.01 * All.MinGasHsml))) { /* need to redo this particle */ npleft++; if(SphQ[i].Left > 0 && SphQ[i].Right > 0) if((SphQ[i].Right - SphQ[i].Left) < 1.0e-3 * SphQ[i].Left) { /* this one should be ok */ npleft--; Q[i].Ti_endstep = -Q[i].Ti_endstep - 1; /* Mark as inactive */ continue; } if(SphQ[i].NumNgb < (All.DesNumNgb - All.MaxNumNgbDeviation)) SphQ[i].Left = dmax(SphQ[i].Hsml, SphQ[i].Left); else { if(SphQ[i].Right != 0) { if(SphQ[i].Hsml < SphQ[i].Right) SphQ[i].Right = SphQ[i].Hsml; } else SphQ[i].Right = SphQ[i].Hsml; } if(iter >= MAXITER - 10) { printf ("i=%d task=%d ID=%d Hsml=%g Left=%g Right=%g Ngbs=%g Right-Left=%g\n pos=(%g|%g|%g)\n", i, ThisTask, (int) Q[i].ID, SphQ[i].Hsml, SphQ[i].Left, SphQ[i].Right, (float) SphQ[i].NumNgb, SphQ[i].Right - SphQ[i].Left, Q[i].Pos[0], Q[i].Pos[1], Q[i].Pos[2]); fflush(stdout); } if(SphQ[i].Right > 0 && SphQ[i].Left > 0) SphQ[i].Hsml = pow(0.5 * (pow(SphQ[i].Left, 3) + pow(SphQ[i].Right, 3)), 1.0 / 3); else { if(SphQ[i].Right == 0 && SphQ[i].Left == 0) endrun(8188); /* can't occur */ if(SphQ[i].Right == 0 && SphQ[i].Left > 0) { if(Q[i].Type == 0 && fabs(SphQ[i].NumNgb - All.DesNumNgb) < 0.5 * All.DesNumNgb) { SphQ[i].Hsml *= 1 - (SphQ[i].NumNgb - All.DesNumNgb) / (NUMDIMS * SphQ[i].NumNgb) * SphQ[i].DhsmlDensityFactor; } else SphQ[i].Hsml *= 1.26; } if(SphQ[i].Right > 0 && SphQ[i].Left == 0) { if(Q[i].Type == 0 && fabs(SphQ[i].NumNgb - All.DesNumNgb) < 0.5 * All.DesNumNgb) { SphQ[i].Hsml *= 1 - (SphQ[i].NumNgb - All.DesNumNgb) / (NUMDIMS * SphQ[i].NumNgb) * SphQ[i].DhsmlDensityFactor; } else SphQ[i].Hsml /= 1.26; } } if(SphQ[i].Hsml < All.MinGasHsml) SphQ[i].Hsml = All.MinGasHsml; } else Q[i].Ti_endstep = -Q[i].Ti_endstep - 1; /* Mark as inactive */ } } tend = second(); timecomp += timediff(tstart, tend); numlist = malloc(NTask * sizeof(int) * NTask); MPI_Allgather(&npleft, 1, MPI_INT, numlist, 1, MPI_INT, MPI_COMM_WORLD); for(i = 0, ntot = 0; i < NTask; i++) ntot += numlist[i]; free(numlist); if(ntot > 0) { if(iter == 0) tstart_ngb = second(); iter++; if(iter > 0 && ThisTask == 0) { printf("ngb iteration %d: need to repeat for %d%09d particles.\n", iter, (int) (ntot / 1000000000), (int) (ntot % 1000000000)); fflush(stdout); } if(iter > MAXITER) { printf("failed to converge in neighbour iteration in density()\n"); fflush(stdout); endrun(1155); } } else tend_ngb = second(); } while(ntot > 0); /* mark as active again */ for(i = 0; i < NumPartQ; i++) if(Q[i].Ti_endstep < 0) Q[i].Ti_endstep = -Q[i].Ti_endstep - 1; free(ndonelist); free(nsend); free(nsend_local); free(nbuffer); free(noffset); /* collect some timing information */ if(iter > 0) timengb = timediff(tstart_ngb, tend_ngb); else timengb = 0; MPI_Reduce(&timengb, &sumtimengb, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&timecomp, &sumt, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&timecommsumm, &sumcomm, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&timeimbalance, &sumimbalance, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if(ThisTask == 0) { All.CPU_HydCompWalk += sumt / NTask; All.CPU_HydCommSumm += sumcomm / NTask; All.CPU_HydImbalance += sumimbalance / NTask; All.CPU_EnsureNgb += sumtimengb / NTask; } } #endif #ifdef PY_INTERFACE /*! This function represents the core of the SPH density computation. The * target particle may either be local, or reside in the communication * buffer. */ void density_evaluate_sub(int target, int mode) { int j, n, startnode, numngb, numngb_inbox; double h, h2, fac, hinv, hinv3, hinv4; double rho, divv, wk, dwk; double dx, dy, dz, r, r2, u, mass_j; double dvx, dvy, dvz, rotv[3]; double weighted_numngb, dhsmlrho; FLOAT *pos, *vel; if(mode == 0) { pos = Q[target].Pos; vel = SphQ[target].VelPred; h = SphQ[target].Hsml; } else { pos = DensDataGet[target].Pos; vel = DensDataGet[target].Vel; h = DensDataGet[target].Hsml; } h2 = h * h; hinv = 1.0 / h; #ifndef TWODIMS hinv3 = hinv * hinv * hinv; #else hinv3 = hinv * hinv / boxSize_Z; #endif hinv4 = hinv3 * hinv; rho = divv = rotv[0] = rotv[1] = rotv[2] = 0; weighted_numngb = 0; dhsmlrho = 0; startnode = All.MaxPart; numngb = 0; do { numngb_inbox = ngb_treefind_variable(&pos[0], h, &startnode); for(n = 0; n < numngb_inbox; n++) { j = Ngblist[n]; dx = pos[0] - P[j].Pos[0]; dy = pos[1] - P[j].Pos[1]; dz = pos[2] - P[j].Pos[2]; #ifdef PERIODIC /* now find the closest image in the given box size */ if(dx > boxHalf_X) dx -= boxSize_X; if(dx < -boxHalf_X) dx += boxSize_X; if(dy > boxHalf_Y) dy -= boxSize_Y; if(dy < -boxHalf_Y) dy += boxSize_Y; if(dz > boxHalf_Z) dz -= boxSize_Z; if(dz < -boxHalf_Z) dz += boxSize_Z; #endif r2 = dx * dx + dy * dy + dz * dz; if(r2 < h2) { numngb++; r = sqrt(r2); u = r * hinv; if(u < 0.5) { wk = hinv3 * (KERNEL_COEFF_1 + KERNEL_COEFF_2 * (u - 1) * u * u); dwk = hinv4 * u * (KERNEL_COEFF_3 * u - KERNEL_COEFF_4); } else { wk = hinv3 * KERNEL_COEFF_5 * (1.0 - u) * (1.0 - u) * (1.0 - u); dwk = hinv4 * KERNEL_COEFF_6 * (1.0 - u) * (1.0 - u); } mass_j = P[j].Mass; rho += mass_j * wk; weighted_numngb += NORM_COEFF * wk / hinv3; dhsmlrho += -mass_j * (NUMDIMS * hinv * wk + u * dwk); if(r > 0) { fac = mass_j * dwk / r; dvx = vel[0] - SphP[j].VelPred[0]; dvy = vel[1] - SphP[j].VelPred[1]; dvz = vel[2] - SphP[j].VelPred[2]; divv -= fac * (dx * dvx + dy * dvy + dz * dvz); rotv[0] += fac * (dz * dvy - dy * dvz); rotv[1] += fac * (dx * dvz - dz * dvx); rotv[2] += fac * (dy * dvx - dx * dvy); } } } } while(startnode >= 0); if(mode == 0) { SphQ[target].NumNgb = weighted_numngb; SphQ[target].Density = rho; SphQ[target].DivVel = divv; SphQ[target].DhsmlDensityFactor = dhsmlrho; SphQ[target].Rot[0] = rotv[0]; SphQ[target].Rot[1] = rotv[1]; SphQ[target].Rot[2] = rotv[2]; } else { DensDataResult[target].Rho = rho; DensDataResult[target].Div = divv; DensDataResult[target].Ngb = weighted_numngb; DensDataResult[target].DhsmlDensity = dhsmlrho; DensDataResult[target].Rot[0] = rotv[0]; DensDataResult[target].Rot[1] = rotv[1]; DensDataResult[target].Rot[2] = rotv[2]; } } #endif /*! This routine is a comparison kernel used in a sort routine to group * particles that are exported to the same processor. */ int dens_compare_key(const void *a, const void *b) { if(((struct densdata_in *) a)->Task < (((struct densdata_in *) b)->Task)) return -1; if(((struct densdata_in *) a)->Task > (((struct densdata_in *) b)->Task)) return +1; return 0; } + + + + + + + + + + + +#ifdef PY_INTERFACE + + +/*! This is a comparison kernel for a sort routine, which is used to group + * particles that are going to be exported to the same CPU. + */ +int denssph_compare_key(const void *a, const void *b) +{ + if(((struct denssphdata_in *) a)->Task < (((struct denssphdata_in *) b)->Task)) + return -1; + if(((struct denssphdata_in *) a)->Task > (((struct denssphdata_in *) b)->Task)) + return +1; + return 0; +} + + + + + + + +/*! This function computes the local density for each active SPH particle, + * the number of neighbours in the current smoothing radius, and the + * divergence and curl of the velocity field. The pressure is updated as + * well. If a particle with its smoothing region is fully inside the + * local domain, it is not exported to the other processors. The function + * also detects particles that have a number of neighbours outside the + * allowed tolerance range. For these particles, the smoothing length is + * adjusted accordingly, and the density computation is executed again. + * Note that the smoothing length is not allowed to fall below the lower + * bound set by MinGasHsml. + */ +void density_sph_gradient(void) +{ + long long ntot, ntotleft; + int *noffset, *nbuffer, *nsend, *nsend_local, *numlist, *ndonelist; + int i, j, n, ndone, npleft, maxfill, source, iter = 0; + int level, ngrp, sendTask, recvTask, place, nexport; + double dt_entr, tstart, tend, tstart_ngb = 0, tend_ngb = 0; + double sumt, sumcomm, timengb, sumtimengb; + double timecomp = 0, timeimbalance = 0, timecommsumm = 0, sumimbalance; + MPI_Status status; + +#ifdef PERIODIC + boxSize = All.BoxSize; + boxHalf = 0.5 * All.BoxSize; +#ifdef LONG_X + boxHalf_X = boxHalf * LONG_X; + boxSize_X = boxSize * LONG_X; +#endif +#ifdef LONG_Y + boxHalf_Y = boxHalf * LONG_Y; + boxSize_Y = boxSize * LONG_Y; +#endif +#ifdef LONG_Z + boxHalf_Z = boxHalf * LONG_Z; + boxSize_Z = boxSize * LONG_Z; +#endif +#endif + + + noffset = malloc(sizeof(int) * NTask); /* offsets of bunches in common list */ + nbuffer = malloc(sizeof(int) * NTask); + nsend_local = malloc(sizeof(int) * NTask); + nsend = malloc(sizeof(int) * NTask * NTask); + ndonelist = malloc(sizeof(int) * NTask); + + for(n = 0, NumSphUpdate = 0; n < N_gas; n++) + { + SphP[n].Left = SphP[n].Right = 0; + + SphP[n].GradObservable[0]=0; + SphP[n].GradObservable[1]=0; + SphP[n].GradObservable[2]=0; + + if(P[n].Ti_endstep == All.Ti_Current) + NumSphUpdate++; + } + + numlist = malloc(NTask * sizeof(int) * NTask); + MPI_Allgather(&NumSphUpdate, 1, MPI_INT, numlist, 1, MPI_INT, MPI_COMM_WORLD); + for(i = 0, ntot = 0; i < NTask; i++) + ntot += numlist[i]; + free(numlist); + + + /* we will repeat the whole thing for those particles where we didn't + * find enough neighbours + */ + do + { + i = 0; /* beginn with this index */ + ntotleft = ntot; /* particles left for all tasks together */ + + while(ntotleft > 0) + { + for(j = 0; j < NTask; j++) + nsend_local[j] = 0; + + /* do local particles and prepare export list */ + tstart = second(); + for(nexport = 0, ndone = 0; i < N_gas && nexport < All.BunchSizeDensitySph - NTask; i++) + if(P[i].Ti_endstep == All.Ti_Current) + { + ndone++; + + for(j = 0; j < NTask; j++) + Exportflag[j] = 0; + + density_sph_gradient_evaluate(i, 0); + + for(j = 0; j < NTask; j++) + { + if(Exportflag[j]) + { + DensSphDataIn[nexport].Pos[0] = P[i].Pos[0]; + DensSphDataIn[nexport].Pos[1] = P[i].Pos[1]; + DensSphDataIn[nexport].Pos[2] = P[i].Pos[2]; + DensSphDataIn[nexport].Vel[0] = SphP[i].VelPred[0]; + DensSphDataIn[nexport].Vel[1] = SphP[i].VelPred[1]; + DensSphDataIn[nexport].Vel[2] = SphP[i].VelPred[2]; + DensSphDataIn[nexport].Hsml = SphP[i].Hsml; + + DensSphDataIn[nexport].Observable = SphP[i].Observable; + + DensSphDataIn[nexport].Index = i; + DensSphDataIn[nexport].Task = j; + nexport++; + nsend_local[j]++; + } + } + } + tend = second(); + timecomp += timediff(tstart, tend); + + qsort(DensSphDataIn, nexport, sizeof(struct denssphdata_in), denssph_compare_key); + + for(j = 1, noffset[0] = 0; j < NTask; j++) + noffset[j] = noffset[j - 1] + nsend_local[j - 1]; + + tstart = second(); + + MPI_Allgather(nsend_local, NTask, MPI_INT, nsend, NTask, MPI_INT, MPI_COMM_WORLD); + + tend = second(); + timeimbalance += timediff(tstart, tend); + + + for(level = 1; level < (1 << PTask); level++) + { + tstart = second(); + for(j = 0; j < NTask; j++) + nbuffer[j] = 0; + for(ngrp = level; ngrp < (1 << PTask); ngrp++) + { + maxfill = 0; + for(j = 0; j < NTask; j++) + { + if((j ^ ngrp) < NTask) + if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]) + maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]; + } + if(maxfill >= All.BunchSizeDensitySph) + break; + + sendTask = ThisTask; + recvTask = ThisTask ^ ngrp; + + if(recvTask < NTask) + { + if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0) + { + /* get the particles */ + MPI_Sendrecv(&DensSphDataIn[noffset[recvTask]], + nsend_local[recvTask] * sizeof(struct denssphdata_in), MPI_BYTE, + recvTask, TAG_DENS_A, + &DensSphDataGet[nbuffer[ThisTask]], + nsend[recvTask * NTask + ThisTask] * sizeof(struct denssphdata_in), + MPI_BYTE, recvTask, TAG_DENS_A, MPI_COMM_WORLD, &status); + } + } + + for(j = 0; j < NTask; j++) + if((j ^ ngrp) < NTask) + nbuffer[j] += nsend[(j ^ ngrp) * NTask + j]; + } + tend = second(); + timecommsumm += timediff(tstart, tend); + + + tstart = second(); + for(j = 0; j < nbuffer[ThisTask]; j++) + density_sph_gradient_evaluate(j, 1); + tend = second(); + timecomp += timediff(tstart, tend); + + /* do a block to explicitly measure imbalance */ + tstart = second(); + MPI_Barrier(MPI_COMM_WORLD); + tend = second(); + timeimbalance += timediff(tstart, tend); + + /* get the result */ + tstart = second(); + for(j = 0; j < NTask; j++) + nbuffer[j] = 0; + for(ngrp = level; ngrp < (1 << PTask); ngrp++) + { + maxfill = 0; + for(j = 0; j < NTask; j++) + { + if((j ^ ngrp) < NTask) + if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]) + maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]; + } + if(maxfill >= All.BunchSizeDensitySph) + break; + + sendTask = ThisTask; + recvTask = ThisTask ^ ngrp; + + if(recvTask < NTask) + { + if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0) + { + /* send the results */ + MPI_Sendrecv(&DensSphDataResult[nbuffer[ThisTask]], + nsend[recvTask * NTask + ThisTask] * sizeof(struct denssphdata_out), + MPI_BYTE, recvTask, TAG_DENS_B, + &DensSphDataPartialResult[noffset[recvTask]], + nsend_local[recvTask] * sizeof(struct denssphdata_out), + MPI_BYTE, recvTask, TAG_DENS_B, MPI_COMM_WORLD, &status); + + /* add the result to the particles */ + for(j = 0; j < nsend_local[recvTask]; j++) + { + source = j + noffset[recvTask]; + place = DensSphDataIn[source].Index; + + SphP[place].NumNgb += DensSphDataPartialResult[source].Ngb; + SphP[place].Density += DensSphDataPartialResult[source].Rho; + SphP[place].DivVel += DensSphDataPartialResult[source].Div; + + SphP[place].DhsmlDensityFactor += DensSphDataPartialResult[source].DhsmlDensity; + + SphP[place].Rot[0] += DensSphDataPartialResult[source].Rot[0]; + SphP[place].Rot[1] += DensSphDataPartialResult[source].Rot[1]; + SphP[place].Rot[2] += DensSphDataPartialResult[source].Rot[2]; + + SphP[place].GradObservable[0] += DensSphDataPartialResult[source].GradObservable[0]; + SphP[place].GradObservable[1] += DensSphDataPartialResult[source].GradObservable[1]; + SphP[place].GradObservable[2] += DensSphDataPartialResult[source].GradObservable[2]; + + } + } + } + + for(j = 0; j < NTask; j++) + if((j ^ ngrp) < NTask) + nbuffer[j] += nsend[(j ^ ngrp) * NTask + j]; + } + tend = second(); + timecommsumm += timediff(tstart, tend); + + level = ngrp - 1; + } + + MPI_Allgather(&ndone, 1, MPI_INT, ndonelist, 1, MPI_INT, MPI_COMM_WORLD); + for(j = 0; j < NTask; j++) + ntotleft -= ndonelist[j]; + } + + + + /* do final operations on results */ + tstart = second(); + for(i = 0, npleft = 0; i < N_gas; i++) + { + if(P[i].Ti_endstep == All.Ti_Current) + { + { + SphP[i].DhsmlDensityFactor = + 1 / (1 + SphP[i].Hsml * SphP[i].DhsmlDensityFactor / (NUMDIMS * SphP[i].Density)); + + SphP[i].CurlVel = sqrt(SphP[i].Rot[0] * SphP[i].Rot[0] + + SphP[i].Rot[1] * SphP[i].Rot[1] + + SphP[i].Rot[2] * SphP[i].Rot[2]) / SphP[i].Density; + + SphP[i].DivVel /= SphP[i].Density; + + SphP[i].GradObservable[0] /= SphP[i].Density; + SphP[i].GradObservable[1] /= SphP[i].Density; + SphP[i].GradObservable[2] /= SphP[i].Density; + + dt_entr = (All.Ti_Current - (P[i].Ti_begstep + P[i].Ti_endstep) / 2) * All.Timebase_interval; + + SphP[i].Pressure = + (SphP[i].Entropy + SphP[i].DtEntropy * dt_entr) * pow(SphP[i].Density, GAMMA); + } + + + /* now check whether we had enough neighbours */ + + if(SphP[i].NumNgb < (All.DesNumNgb - All.MaxNumNgbDeviation) || + (SphP[i].NumNgb > (All.DesNumNgb + All.MaxNumNgbDeviation) + && SphP[i].Hsml > (1.01 * All.MinGasHsml))) + { + /* need to redo this particle */ + npleft++; + + if(SphP[i].Left > 0 && SphP[i].Right > 0) + if((SphP[i].Right - SphP[i].Left) < 1.0e-3 * SphP[i].Left) + { + /* this one should be ok */ + npleft--; + P[i].Ti_endstep = -P[i].Ti_endstep - 1; /* Mark as inactive */ + continue; + } + + if(SphP[i].NumNgb < (All.DesNumNgb - All.MaxNumNgbDeviation)) + SphP[i].Left = dmax(SphP[i].Hsml, SphP[i].Left); + else + { + if(SphP[i].Right != 0) + { + if(SphP[i].Hsml < SphP[i].Right) + SphP[i].Right = SphP[i].Hsml; + } + else + SphP[i].Right = SphP[i].Hsml; + } + + if(iter >= MAXITER - 10) + { + printf + ("i=%d task=%d ID=%d Hsml=%g Left=%g Right=%g Ngbs=%g Right-Left=%g\n pos=(%g|%g|%g)\n", + i, ThisTask, (int) P[i].ID, SphP[i].Hsml, SphP[i].Left, SphP[i].Right, + (float) SphP[i].NumNgb, SphP[i].Right - SphP[i].Left, P[i].Pos[0], P[i].Pos[1], + P[i].Pos[2]); + fflush(stdout); + } + + if(SphP[i].Right > 0 && SphP[i].Left > 0) + SphP[i].Hsml = pow(0.5 * (pow(SphP[i].Left, 3) + pow(SphP[i].Right, 3)), 1.0 / 3); + else + { + if(SphP[i].Right == 0 && SphP[i].Left == 0) + endrun(8188); /* can't occur */ + + if(SphP[i].Right == 0 && SphP[i].Left > 0) + { + if(P[i].Type == 0 && fabs(SphP[i].NumNgb - All.DesNumNgb) < 0.5 * All.DesNumNgb) + { + SphP[i].Hsml *= + 1 - (SphP[i].NumNgb - + All.DesNumNgb) / (NUMDIMS * SphP[i].NumNgb) * SphP[i].DhsmlDensityFactor; + } + else + SphP[i].Hsml *= 1.26; + } + + if(SphP[i].Right > 0 && SphP[i].Left == 0) + { + if(P[i].Type == 0 && fabs(SphP[i].NumNgb - All.DesNumNgb) < 0.5 * All.DesNumNgb) + { + SphP[i].Hsml *= + 1 - (SphP[i].NumNgb - + All.DesNumNgb) / (NUMDIMS * SphP[i].NumNgb) * SphP[i].DhsmlDensityFactor; + } + else + SphP[i].Hsml /= 1.26; + } + } + + if(SphP[i].Hsml < All.MinGasHsml) + SphP[i].Hsml = All.MinGasHsml; + } + else + P[i].Ti_endstep = -P[i].Ti_endstep - 1; /* Mark as inactive */ + } + } + tend = second(); + timecomp += timediff(tstart, tend); + + + numlist = malloc(NTask * sizeof(int) * NTask); + MPI_Allgather(&npleft, 1, MPI_INT, numlist, 1, MPI_INT, MPI_COMM_WORLD); + for(i = 0, ntot = 0; i < NTask; i++) + ntot += numlist[i]; + free(numlist); + + if(ntot > 0) + { + if(iter == 0) + tstart_ngb = second(); + + iter++; + + if(iter > 0 && ThisTask == 0) + { + printf("ngb iteration %d: need to repeat for %d%09d particles.\n", iter, + (int) (ntot / 1000000000), (int) (ntot % 1000000000)); + fflush(stdout); + } + + if(iter > MAXITER) + { + printf("failed to converge in neighbour iteration in density()\n"); + fflush(stdout); + endrun(1155); + } + } + else + tend_ngb = second(); + } + while(ntot > 0); + + + /* mark as active again */ + for(i = 0; i < NumPart; i++) + if(P[i].Ti_endstep < 0) + P[i].Ti_endstep = -P[i].Ti_endstep - 1; + + free(ndonelist); + free(nsend); + free(nsend_local); + free(nbuffer); + free(noffset); + + + /* collect some timing information */ + if(iter > 0) + timengb = timediff(tstart_ngb, tend_ngb); + else + timengb = 0; + + MPI_Reduce(&timengb, &sumtimengb, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); + MPI_Reduce(&timecomp, &sumt, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); + MPI_Reduce(&timecommsumm, &sumcomm, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); + MPI_Reduce(&timeimbalance, &sumimbalance, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); + + if(ThisTask == 0) + { + All.CPU_HydCompWalk += sumt / NTask; + All.CPU_HydCommSumm += sumcomm / NTask; + All.CPU_HydImbalance += sumimbalance / NTask; + All.CPU_EnsureNgb += sumtimengb / NTask; + } +} + + + +/*! This function represents the core of the SPH density computation. The + * target particle may either be local, or reside in the communication + * buffer. + */ +void density_sph_gradient_evaluate(int target, int mode) +{ + int j, n, startnode, numngb, numngb_inbox; + double h, h2, fac, hinv, hinv3, hinv4; + double rho, divv, wk, dwk; + double dx, dy, dz, r, r2, u, mass_j; + double dvx, dvy, dvz, rotv[3]; + double weighted_numngb, dhsmlrho; + FLOAT *pos, *vel; + FLOAT gradx,grady,gradz,observable; + + if(mode == 0) + { + pos = P[target].Pos; + vel = SphP[target].VelPred; + h = SphP[target].Hsml; + observable = SphP[target].Observable; + } + else + { + pos = DensSphDataGet[target].Pos; + vel = DensSphDataGet[target].Vel; + h = DensSphDataGet[target].Hsml; + observable = DensSphDataGet[target].Observable; + } + + h2 = h * h; + hinv = 1.0 / h; +#ifndef TWODIMS + hinv3 = hinv * hinv * hinv; +#else + hinv3 = hinv * hinv / boxSize_Z; +#endif + hinv4 = hinv3 * hinv; + + rho = divv = rotv[0] = rotv[1] = rotv[2] = 0; + gradx = grady = gradz = 0; + weighted_numngb = 0; + dhsmlrho = 0; + + startnode = All.MaxPart; + numngb = 0; + do + { + numngb_inbox = ngb_treefind_variable(&pos[0], h, &startnode); + + for(n = 0; n < numngb_inbox; n++) + { + j = Ngblist[n]; + + dx = pos[0] - P[j].Pos[0]; + dy = pos[1] - P[j].Pos[1]; + dz = pos[2] - P[j].Pos[2]; + +#ifdef PERIODIC /* now find the closest image in the given box size */ + if(dx > boxHalf_X) + dx -= boxSize_X; + if(dx < -boxHalf_X) + dx += boxSize_X; + if(dy > boxHalf_Y) + dy -= boxSize_Y; + if(dy < -boxHalf_Y) + dy += boxSize_Y; + if(dz > boxHalf_Z) + dz -= boxSize_Z; + if(dz < -boxHalf_Z) + dz += boxSize_Z; +#endif + r2 = dx * dx + dy * dy + dz * dz; + + if(r2 < h2) + { + numngb++; + + r = sqrt(r2); + + u = r * hinv; + + if(u < 0.5) + { + wk = hinv3 * (KERNEL_COEFF_1 + KERNEL_COEFF_2 * (u - 1) * u * u); + dwk = hinv4 * u * (KERNEL_COEFF_3 * u - KERNEL_COEFF_4); + } + else + { + wk = hinv3 * KERNEL_COEFF_5 * (1.0 - u) * (1.0 - u) * (1.0 - u); + dwk = hinv4 * KERNEL_COEFF_6 * (1.0 - u) * (1.0 - u); + } + + mass_j = P[j].Mass; + + rho += mass_j * wk; + + weighted_numngb += NORM_COEFF * wk / hinv3; + + dhsmlrho += -mass_j * (NUMDIMS * hinv * wk + u * dwk); + + if(r > 0) + { + fac = mass_j * dwk / r; + + dvx = vel[0] - SphP[j].VelPred[0]; + dvy = vel[1] - SphP[j].VelPred[1]; + dvz = vel[2] - SphP[j].VelPred[2]; + + divv -= fac * (dx * dvx + dy * dvy + dz * dvz); + + rotv[0] += fac * (dz * dvy - dy * dvz); + rotv[1] += fac * (dx * dvz - dz * dvx); + rotv[2] += fac * (dy * dvx - dx * dvy); + +/* if (r==0) + fac = 0; + else + fac = mass_j * (observable-SphP[j].Observable) /(SphP[j].Density) * dwk / r; + + gradx += fac * dx; + grady += fac * dy; + gradz += fac * dz; */ + + gradx += fac * (observable-SphP[j].Observable)*dx; + grady += fac * (observable-SphP[j].Observable)*dy; + gradz += fac * (observable-SphP[j].Observable)*dz; + + + + + } + } + } + } + while(startnode >= 0); + + if(mode == 0) + { + SphP[target].NumNgb = weighted_numngb; + SphP[target].Density = rho; + SphP[target].DivVel = divv; + SphP[target].DhsmlDensityFactor = dhsmlrho; + SphP[target].Rot[0] = rotv[0]; + SphP[target].Rot[1] = rotv[1]; + SphP[target].Rot[2] = rotv[2]; + + SphP[target].GradObservable[0] = gradx; + SphP[target].GradObservable[1] = grady; + SphP[target].GradObservable[2] = gradz; + + } + else + { + DensSphDataResult[target].Rho = rho; + DensSphDataResult[target].Div = divv; + DensSphDataResult[target].Ngb = weighted_numngb; + DensSphDataResult[target].DhsmlDensity = dhsmlrho; + DensSphDataResult[target].Rot[0] = rotv[0]; + DensSphDataResult[target].Rot[1] = rotv[1]; + DensSphDataResult[target].Rot[2] = rotv[2]; + + DensSphDataResult[target].GradObservable[0] = gradx; + DensSphDataResult[target].GradObservable[1] = grady; + DensSphDataResult[target].GradObservable[2] = gradz; + } +} + +#endif + + + + + + + + + diff --git a/src/PyGadget/src/python_interface.c b/src/PyGadget/src/python_interface.c index 8bd97df..b4d619c 100644 --- a/src/PyGadget/src/python_interface.c +++ b/src/PyGadget/src/python_interface.c @@ -1,2949 +1,3026 @@ #include #include #include #include #include #include #include "allvars.h" #include "proto.h" #define TO_INT(a) ( (PyArrayObject*) PyArray_CastToType(a, PyArray_DescrFromType(NPY_INT) ,0) ) #define TO_DOUBLE(a) ( (PyArrayObject*) PyArray_CastToType(a, PyArray_DescrFromType(NPY_DOUBLE) ,0) ) #define TO_FLOAT(a) ( (PyArrayObject*) PyArray_CastToType(a, PyArray_DescrFromType(NPY_FLOAT) ,0) ) static int Init() { /* main.c */ RestartFlag = 0; All.CPU_TreeConstruction = All.CPU_TreeWalk = All.CPU_Gravity = All.CPU_Potential = All.CPU_Domain = All.CPU_Snapshot = All.CPU_Total = All.CPU_CommSum = All.CPU_Imbalance = All.CPU_Hydro = All.CPU_HydCompWalk = All.CPU_HydCommSumm = All.CPU_HydImbalance = All.CPU_EnsureNgb = All.CPU_Predict = All.CPU_TimeLine = All.CPU_PM = All.CPU_Peano = 0; CPUThisRun = 0; /* from init.c, after read ic */ int i, j; double a3; All.Time = All.TimeBegin; All.Ti_Current = 0; if(All.ComovingIntegrationOn) { All.Timebase_interval = (log(All.TimeMax) - log(All.TimeBegin)) / TIMEBASE; a3 = All.Time * All.Time * All.Time; } else { All.Timebase_interval = (All.TimeMax - All.TimeBegin) / TIMEBASE; a3 = 1; } set_softenings(); All.NumCurrentTiStep = 0; /* setup some counters */ All.SnapshotFileCount = 0; if(RestartFlag == 2) All.SnapshotFileCount = atoi(All.InitCondFile + strlen(All.InitCondFile) - 3) + 1; All.TotNumOfForces = 0; All.NumForcesSinceLastDomainDecomp = 0; if(All.ComovingIntegrationOn) if(All.PeriodicBoundariesOn == 1) check_omega(); All.TimeLastStatistics = All.TimeBegin - All.TimeBetStatistics; if(All.ComovingIntegrationOn) /* change to new velocity variable */ { for(i = 0; i < NumPart; i++) for(j = 0; j < 3; j++) P[i].Vel[j] *= sqrt(All.Time) * All.Time; } for(i = 0; i < NumPart; i++) /* start-up initialization */ { for(j = 0; j < 3; j++) P[i].GravAccel[j] = 0; #ifdef PMGRID for(j = 0; j < 3; j++) P[i].GravPM[j] = 0; #endif P[i].Ti_endstep = 0; P[i].Ti_begstep = 0; P[i].OldAcc = 0; P[i].GravCost = 1; P[i].Potential = 0; } #ifdef PMGRID All.PM_Ti_endstep = All.PM_Ti_begstep = 0; #endif #ifdef FLEXSTEPS All.PresentMinStep = TIMEBASE; for(i = 0; i < NumPart; i++) /* start-up initialization */ { P[i].FlexStepGrp = (int) (TIMEBASE * get_random_number(P[i].ID)); } #endif for(i = 0; i < N_gas; i++) /* initialize sph_properties */ { for(j = 0; j < 3; j++) { SphP[i].VelPred[j] = P[i].Vel[j]; SphP[i].HydroAccel[j] = 0; } SphP[i].DtEntropy = 0; if(RestartFlag == 0) { SphP[i].Hsml = 0; SphP[i].Density = -1; } } ngb_treeallocate(MAX_NGB); force_treeallocate(All.TreeAllocFactor * All.MaxPart, All.MaxPart); All.NumForcesSinceLastDomainDecomp = 1 + All.TotNumPart * All.TreeDomainUpdateFrequency; Flag_FullStep = 1; /* to ensure that Peano-Hilber order is done */ domain_Decomposition(); /* do initial domain decomposition (gives equal numbers of particles) */ ngb_treebuild(); /* will build tree */ setup_smoothinglengths(); TreeReconstructFlag = 1; /* at this point, the entropy variable normally contains the * internal energy, read in from the initial conditions file, unless the file * explicitly signals that the initial conditions contain the entropy directly. * Once the density has been computed, we can convert thermal energy to entropy. */ #ifndef ISOTHERM_EQS if(header.flag_entropy_instead_u == 0) for(i = 0; i < N_gas; i++) SphP[i].Entropy = GAMMA_MINUS1 * SphP[i].Entropy / pow(SphP[i].Density / a3, GAMMA_MINUS1); #endif return 1; } static void Begrun1() { struct global_data_all_processes all; if(ThisTask == 0) { printf("\nThis is pyGadget, version `%s'.\n", GADGETVERSION); printf("\nRunning on %d processors.\n", NTask); } //read_parameter_file(ParameterFile); /* ... read in parameters for this run */ allocate_commbuffers(); /* ... allocate buffer-memory for particle exchange during force computation */ set_units(); #if defined(PERIODIC) && (!defined(PMGRID) || defined(FORCETEST)) ewald_init(); #endif //open_outputfiles(); random_generator = gsl_rng_alloc(gsl_rng_ranlxd1); gsl_rng_set(random_generator, 42); /* start-up seed */ #ifdef PMGRID long_range_init(); #endif All.TimeLastRestartFile = CPUThisRun; if(RestartFlag == 0 || RestartFlag == 2) { set_random_numbers(); } else { all = All; /* save global variables. (will be read from restart file) */ restart(RestartFlag); /* ... read restart file. Note: This also resets all variables in the struct `All'. However, during the run, some variables in the parameter file are allowed to be changed, if desired. These need to copied in the way below. Note: All.PartAllocFactor is treated in restart() separately. */ All.MinSizeTimestep = all.MinSizeTimestep; All.MaxSizeTimestep = all.MaxSizeTimestep; All.BufferSize = all.BufferSize; All.BunchSizeForce = all.BunchSizeForce; All.BunchSizeDensity = all.BunchSizeDensity; All.BunchSizeHydro = all.BunchSizeHydro; All.BunchSizeDomain = all.BunchSizeDomain; All.TimeLimitCPU = all.TimeLimitCPU; All.ResubmitOn = all.ResubmitOn; All.TimeBetSnapshot = all.TimeBetSnapshot; All.TimeBetStatistics = all.TimeBetStatistics; All.CpuTimeBetRestartFile = all.CpuTimeBetRestartFile; All.ErrTolIntAccuracy = all.ErrTolIntAccuracy; All.MaxRMSDisplacementFac = all.MaxRMSDisplacementFac; All.ErrTolForceAcc = all.ErrTolForceAcc; All.TypeOfTimestepCriterion = all.TypeOfTimestepCriterion; All.TypeOfOpeningCriterion = all.TypeOfOpeningCriterion; All.NumFilesWrittenInParallel = all.NumFilesWrittenInParallel; All.TreeDomainUpdateFrequency = all.TreeDomainUpdateFrequency; All.SnapFormat = all.SnapFormat; All.NumFilesPerSnapshot = all.NumFilesPerSnapshot; All.MaxNumNgbDeviation = all.MaxNumNgbDeviation; All.ArtBulkViscConst = all.ArtBulkViscConst; All.OutputListOn = all.OutputListOn; All.CourantFac = all.CourantFac; All.OutputListLength = all.OutputListLength; memcpy(All.OutputListTimes, all.OutputListTimes, sizeof(double) * All.OutputListLength); strcpy(All.ResubmitCommand, all.ResubmitCommand); strcpy(All.OutputListFilename, all.OutputListFilename); strcpy(All.OutputDir, all.OutputDir); strcpy(All.RestartFile, all.RestartFile); strcpy(All.EnergyFile, all.EnergyFile); strcpy(All.InfoFile, all.InfoFile); strcpy(All.CpuFile, all.CpuFile); strcpy(All.TimingsFile, all.TimingsFile); strcpy(All.SnapshotFileBase, all.SnapshotFileBase); if(All.TimeMax != all.TimeMax) readjust_timebase(All.TimeMax, all.TimeMax); } } static void Begrun2() { if(RestartFlag == 0 || RestartFlag == 2) Init(); /* ... read in initial model */ #ifdef PMGRID long_range_init_regionsize(); #endif if(All.ComovingIntegrationOn) init_drift_table(); //if(RestartFlag == 2) // All.Ti_nextoutput = find_next_outputtime(All.Ti_Current + 1); //else // All.Ti_nextoutput = find_next_outputtime(All.Ti_Current); All.TimeLastRestartFile = CPUThisRun; } /************************************************************/ /* PYTHON INTERFACE */ /************************************************************/ static PyObject *gadget_Info(PyObject *self, PyObject *args, PyObject *kwds) { printf("I am proc %d among %d procs.\n",ThisTask,NTask); return Py_BuildValue("i",1); } static PyObject *gadget_InitMPI(PyObject *self, PyObject *args, PyObject *kwds) { //MPI_Init(0, 0); /* this is done in mpi4py */ MPI_Comm_rank(MPI_COMM_WORLD, &ThisTask); MPI_Comm_size(MPI_COMM_WORLD, &NTask); for(PTask = 0; NTask > (1 << PTask); PTask++); return Py_BuildValue("i",1); } static PyObject * gadget_InitDefaultParameters(PyObject* self) { /* list of Gadget parameters */ /* All.InitCondFile ="ICs/cluster_littleendian.dat"; All.OutputDir ="cluster/"; All.EnergyFile ="energy.txt"; All.InfoFile ="info.txt"; All.TimingsFile ="timings.txt"; All.CpuFile ="cpu.txt"; All.RestartFile ="restart"; All.SnapshotFileBase ="snapshot"; All.OutputListFilename ="parameterfiles/outputs_lcdm_gas.txt"; */ /* CPU time -limit */ All.TimeLimitCPU = 36000; /* = 10 hours */ All.ResubmitOn = 0; //All.ResubmitCommand = "my-scriptfile"; All.ICFormat = 1; All.SnapFormat = 1; All.ComovingIntegrationOn = 0; All.TypeOfTimestepCriterion = 0; All.OutputListOn = 0; All.PeriodicBoundariesOn = 0; /* Caracteristics of run */ All.TimeBegin = 0.0; /*% Begin of the simulation (z=23)*/ All.TimeMax = 1.0; All.Omega0 = 0; All.OmegaLambda = 0; All.OmegaBaryon = 0; All.HubbleParam = 0; All.BoxSize = 0; /* Output frequency */ All.TimeBetSnapshot = 0.1; All.TimeOfFirstSnapshot = 0.0; /*% 5 constant steps in log(a) */ All.CpuTimeBetRestartFile = 36000.0; /* here in seconds */ All.TimeBetStatistics = 0.05; All.NumFilesPerSnapshot = 1; All.NumFilesWrittenInParallel = 1; /* Accuracy of time integration */ All.ErrTolIntAccuracy = 0.025; All.MaxRMSDisplacementFac = 0.2; All.CourantFac = 0.15; All.MaxSizeTimestep = 0.03; All.MinSizeTimestep = 0.0; /* Tree algorithm, force accuracy, domain update frequency */ All.ErrTolTheta = 0.7; All.TypeOfOpeningCriterion = 0; All.ErrTolForceAcc = 0.005; All.TreeDomainUpdateFrequency = 0.1; /* Further parameters of SPH */ All.DesNumNgb = 50; All.MaxNumNgbDeviation = 2; All.ArtBulkViscConst = 0.8; All.InitGasTemp = 0; All.MinGasTemp = 0; /* Memory allocation */ All.PartAllocFactor = 2.0; All.TreeAllocFactor = 2.0; All.BufferSize = 30; /* System of units */ All.UnitLength_in_cm = 3.085678e21; /* 1.0 kpc */ All.UnitMass_in_g = 1.989e43; /* 1.0e10 solar masses */ All.UnitVelocity_in_cm_per_s = 1e5; /* 1 km/sec */ All.GravityConstantInternal = 0; /* Softening lengths */ All.MinGasHsmlFractional = 0.25; All.SofteningGas = 0.5; All.SofteningHalo = 0.5; All.SofteningDisk = 0.5; All.SofteningBulge = 0.5; All.SofteningStars = 0.5; All.SofteningBndry = 0.5; All.SofteningGasMaxPhys = 0.5; All.SofteningHaloMaxPhys = 0.5; All.SofteningDiskMaxPhys = 0.5; All.SofteningBulgeMaxPhys = 0.5; All.SofteningStarsMaxPhys = 0.5; All.SofteningBndryMaxPhys = 0.5; return Py_BuildValue("i",1); } static PyObject * gadget_GetParameters() { PyObject *dict; PyObject *key; PyObject *value; dict = PyDict_New(); /* CPU time -limit */ key = PyString_FromString("TimeLimitCPU"); value = PyFloat_FromDouble(All.TimeLimitCPU); PyDict_SetItem(dict,key,value); key = PyString_FromString("ResubmitOn"); value = PyFloat_FromDouble(All.ResubmitOn); PyDict_SetItem(dict,key,value); //All.ResubmitCommand key = PyString_FromString("ICFormat"); value = PyInt_FromLong(All.ICFormat); PyDict_SetItem(dict,key,value); key = PyString_FromString("SnapFormat"); value = PyInt_FromLong(All.SnapFormat); PyDict_SetItem(dict,key,value); key = PyString_FromString("ComovingIntegrationOn"); value = PyInt_FromLong(All.ComovingIntegrationOn); PyDict_SetItem(dict,key,value); key = PyString_FromString("TypeOfTimestepCriterion"); value = PyInt_FromLong(All.TypeOfTimestepCriterion); PyDict_SetItem(dict,key,value); key = PyString_FromString("OutputListOn"); value = PyInt_FromLong(All.OutputListOn); PyDict_SetItem(dict,key,value); key = PyString_FromString("PeriodicBoundariesOn"); value = PyInt_FromLong(All.PeriodicBoundariesOn); PyDict_SetItem(dict,key,value); /* Caracteristics of run */ key = PyString_FromString("TimeBegin"); value = PyFloat_FromDouble(All.TimeBegin); PyDict_SetItem(dict,key,value); key = PyString_FromString("TimeMax"); value = PyFloat_FromDouble(All.TimeMax); PyDict_SetItem(dict,key,value); key = PyString_FromString("Omega0"); value = PyFloat_FromDouble(All.Omega0); PyDict_SetItem(dict,key,value); key = PyString_FromString("OmegaLambda"); value = PyFloat_FromDouble(All.OmegaLambda); PyDict_SetItem(dict,key,value); key = PyString_FromString("OmegaBaryon"); value = PyFloat_FromDouble(All.OmegaBaryon); PyDict_SetItem(dict,key,value); key = PyString_FromString("HubbleParam"); value = PyFloat_FromDouble(All.HubbleParam); PyDict_SetItem(dict,key,value); key = PyString_FromString("BoxSize"); value = PyFloat_FromDouble(All.BoxSize); PyDict_SetItem(dict,key,value); /* Output frequency */ key = PyString_FromString("TimeBetSnapshot"); value = PyFloat_FromDouble(All.TimeBetSnapshot); PyDict_SetItem(dict,key,value); key = PyString_FromString("TimeOfFirstSnapshot"); value = PyFloat_FromDouble(All.TimeOfFirstSnapshot); PyDict_SetItem(dict,key,value); key = PyString_FromString("CpuTimeBetRestartFile"); value = PyFloat_FromDouble(All.CpuTimeBetRestartFile); PyDict_SetItem(dict,key,value); key = PyString_FromString("TimeBetStatistics"); value = PyFloat_FromDouble(All.TimeBetStatistics); PyDict_SetItem(dict,key,value); key = PyString_FromString("NumFilesPerSnapshot"); value = PyInt_FromLong(All.NumFilesPerSnapshot); PyDict_SetItem(dict,key,value); key = PyString_FromString("NumFilesWrittenInParallel"); value = PyInt_FromLong(All.NumFilesWrittenInParallel); PyDict_SetItem(dict,key,value); /* Accuracy of time integration */ key = PyString_FromString("ErrTolIntAccuracy"); value = PyFloat_FromDouble(All.ErrTolIntAccuracy); PyDict_SetItem(dict,key,value); key = PyString_FromString("MaxRMSDisplacementFac"); value = PyFloat_FromDouble(All.MaxRMSDisplacementFac); PyDict_SetItem(dict,key,value); key = PyString_FromString("CourantFac"); value = PyFloat_FromDouble(All.CourantFac); PyDict_SetItem(dict,key,value); key = PyString_FromString("MaxSizeTimestep"); value = PyFloat_FromDouble(All.MaxSizeTimestep); PyDict_SetItem(dict,key,value); key = PyString_FromString("MinSizeTimestep"); value = PyFloat_FromDouble(All.MinSizeTimestep); PyDict_SetItem(dict,key,value); /* Tree algorithm, force accuracy, domain update frequency */ key = PyString_FromString("ErrTolTheta"); value = PyFloat_FromDouble(All.ErrTolTheta); PyDict_SetItem(dict,key,value); key = PyString_FromString("TypeOfOpeningCriterion"); value = PyInt_FromLong(All.TypeOfOpeningCriterion); PyDict_SetItem(dict,key,value); key = PyString_FromString("ErrTolForceAcc"); value = PyFloat_FromDouble(All.ErrTolForceAcc); PyDict_SetItem(dict,key,value); key = PyString_FromString("TreeDomainUpdateFrequency"); value = PyFloat_FromDouble(All.TreeDomainUpdateFrequency); PyDict_SetItem(dict,key,value); /* Further parameters of SPH */ key = PyString_FromString("DesNumNgb"); value = PyInt_FromLong(All.DesNumNgb); PyDict_SetItem(dict,key,value); key = PyString_FromString("MaxNumNgbDeviation"); value = PyInt_FromLong(All.MaxNumNgbDeviation); PyDict_SetItem(dict,key,value); key = PyString_FromString("ArtBulkViscConst"); value = PyInt_FromLong(All.ArtBulkViscConst); PyDict_SetItem(dict,key,value); key = PyString_FromString("InitGasTemp"); value = PyInt_FromLong(All.InitGasTemp); PyDict_SetItem(dict,key,value); key = PyString_FromString("MinGasTemp"); value = PyInt_FromLong(All.MinGasTemp); PyDict_SetItem(dict,key,value); /* Memory allocation */ key = PyString_FromString("PartAllocFactor"); value = PyFloat_FromDouble(All.PartAllocFactor); PyDict_SetItem(dict,key,value); key = PyString_FromString("TreeAllocFactor"); value = PyFloat_FromDouble(All.TreeAllocFactor); PyDict_SetItem(dict,key,value); key = PyString_FromString("BufferSize"); value = PyInt_FromLong(All.BufferSize); PyDict_SetItem(dict,key,value); /* System of units */ key = PyString_FromString("UnitLength_in_cm"); value = PyFloat_FromDouble(All.UnitLength_in_cm); PyDict_SetItem(dict,key,value); key = PyString_FromString("UnitMass_in_g"); value = PyFloat_FromDouble(All.UnitMass_in_g); PyDict_SetItem(dict,key,value); key = PyString_FromString("UnitVelocity_in_cm_per_s"); value = PyFloat_FromDouble(All.UnitVelocity_in_cm_per_s); PyDict_SetItem(dict,key,value); key = PyString_FromString("GravityConstantInternal"); value = PyFloat_FromDouble(All.GravityConstantInternal); PyDict_SetItem(dict,key,value); /* Softening lengths */ key = PyString_FromString("MinGasHsmlFractional"); value = PyFloat_FromDouble(All.MinGasHsmlFractional); PyDict_SetItem(dict,key,value); key = PyString_FromString("SofteningGas"); value = PyFloat_FromDouble(All.SofteningGas); PyDict_SetItem(dict,key,value); key = PyString_FromString("SofteningHalo"); value = PyFloat_FromDouble(All.SofteningHalo); PyDict_SetItem(dict,key,value); key = PyString_FromString("SofteningDisk"); value = PyFloat_FromDouble(All.SofteningDisk); PyDict_SetItem(dict,key,value); key = PyString_FromString("SofteningBulge"); value = PyFloat_FromDouble(All.SofteningBulge); PyDict_SetItem(dict,key,value); key = PyString_FromString("SofteningStars"); value = PyFloat_FromDouble(All.SofteningStars); PyDict_SetItem(dict,key,value); key = PyString_FromString("SofteningBndry"); value = PyFloat_FromDouble(All.SofteningBndry); PyDict_SetItem(dict,key,value); key = PyString_FromString("SofteningGasMaxPhys"); value = PyFloat_FromDouble(All.SofteningGasMaxPhys); PyDict_SetItem(dict,key,value); key = PyString_FromString("SofteningHaloMaxPhys"); value = PyFloat_FromDouble(All.SofteningHaloMaxPhys); PyDict_SetItem(dict,key,value); key = PyString_FromString("SofteningDiskMaxPhys"); value = PyFloat_FromDouble(All.SofteningDiskMaxPhys); PyDict_SetItem(dict,key,value); key = PyString_FromString("SofteningBulgeMaxPhys"); value = PyFloat_FromDouble(All.SofteningBulgeMaxPhys); PyDict_SetItem(dict,key,value); key = PyString_FromString("SofteningStarsMaxPhys"); value = PyFloat_FromDouble(All.SofteningStarsMaxPhys); PyDict_SetItem(dict,key,value); key = PyString_FromString("SofteningBndryMaxPhys"); value = PyFloat_FromDouble(All.SofteningBndryMaxPhys); PyDict_SetItem(dict,key,value); /* key = PyString_FromString("OutputInfo"); value = PyFloat_FromDouble(All.OutputInfo); PyDict_SetItem(dict,key,value); key = PyString_FromString("PeanoHilbertOrder"); value = PyFloat_FromDouble(All.PeanoHilbertOrder); PyDict_SetItem(dict,key,value); */ return Py_BuildValue("O",dict); } static PyObject * gadget_SetParameters(PyObject *self, PyObject *args) { PyObject *dict; PyObject *key; PyObject *value; int ivalue; float fvalue; double dvalue; /* here, we can have either arguments or dict directly */ if(PyDict_Check(args)) { dict = args; } else { if (! PyArg_ParseTuple(args, "O",&dict)) return NULL; } /* check that it is a PyDictObject */ if(!PyDict_Check(dict)) { PyErr_SetString(PyExc_AttributeError, "argument is not a dictionary."); return NULL; } Py_ssize_t pos=0; while(PyDict_Next(dict,&pos,&key,&value)) { if(PyString_Check(key)) { /* CPU time -limit */ if(strcmp(PyString_AsString(key), "TimeLimitCPU")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.TimeLimitCPU = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "ResubmitOn")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.ResubmitOn = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "ICFormat")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.ICFormat = PyInt_AsLong(value); } if(strcmp(PyString_AsString(key), "SnapFormat")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.SnapFormat = PyInt_AsLong(value); } if(strcmp(PyString_AsString(key), "ComovingIntegrationOn")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.ComovingIntegrationOn = PyInt_AsLong(value); } if(strcmp(PyString_AsString(key), "TypeOfTimestepCriterion")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.TypeOfTimestepCriterion = PyInt_AsLong(value); } if(strcmp(PyString_AsString(key), "OutputListOn")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.OutputListOn = PyInt_AsLong(value); } if(strcmp(PyString_AsString(key), "PeriodicBoundariesOn")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.PeriodicBoundariesOn = PyInt_AsLong(value); } /* Caracteristics of run */ if(strcmp(PyString_AsString(key), "TimeBegin")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.TimeBegin = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "TimeMax")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.TimeMax = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "Omega0")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.Omega0 = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "OmegaLambda")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.OmegaLambda = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "OmegaBaryon")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.OmegaBaryon = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "HubbleParam")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.HubbleParam = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "BoxSize")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.BoxSize = PyFloat_AsDouble(value); } /* Output frequency */ if(strcmp(PyString_AsString(key), "TimeBetSnapshot")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.TimeBetSnapshot = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "TimeOfFirstSnapshot")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.TimeOfFirstSnapshot = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "CpuTimeBetRestartFile")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.CpuTimeBetRestartFile = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "TimeBetStatistics")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.TimeBetStatistics = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "NumFilesPerSnapshot")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.NumFilesPerSnapshot = PyInt_AsLong(value); } if(strcmp(PyString_AsString(key), "NumFilesWrittenInParallel")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.NumFilesWrittenInParallel = PyInt_AsLong(value); } /* Accuracy of time integration */ if(strcmp(PyString_AsString(key), "ErrTolIntAccuracy")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.ErrTolIntAccuracy = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "MaxRMSDisplacementFac")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.MaxRMSDisplacementFac = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "CourantFac")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.CourantFac = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "MaxSizeTimestep")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.MaxSizeTimestep = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "MinSizeTimestep")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.MinSizeTimestep = PyFloat_AsDouble(value); } /* Tree algorithm, force accuracy, domain update frequency */ if(strcmp(PyString_AsString(key), "ErrTolTheta")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.ErrTolTheta = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "TypeOfOpeningCriterion")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.TypeOfOpeningCriterion = PyInt_AsLong(value); } if(strcmp(PyString_AsString(key), "ErrTolForceAcc")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.ErrTolForceAcc = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "TreeDomainUpdateFrequency")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.TreeDomainUpdateFrequency = PyFloat_AsDouble(value); } /* Further parameters of SPH */ if(strcmp(PyString_AsString(key), "DesNumNgb")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.DesNumNgb = PyInt_AsLong(value); } if(strcmp(PyString_AsString(key), "MaxNumNgbDeviation")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.MaxNumNgbDeviation = PyInt_AsLong(value); } if(strcmp(PyString_AsString(key), "ArtBulkViscConst")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.ArtBulkViscConst = PyInt_AsLong(value); } if(strcmp(PyString_AsString(key), "InitGasTemp")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.InitGasTemp = PyInt_AsLong(value); } if(strcmp(PyString_AsString(key), "MinGasTemp")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.MinGasTemp = PyInt_AsLong(value); } /* Memory allocation */ if(strcmp(PyString_AsString(key), "PartAllocFactor")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.PartAllocFactor = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "TreeAllocFactor")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.TreeAllocFactor = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "BufferSize")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.BufferSize = PyInt_AsLong(value); } /* System of units */ if(strcmp(PyString_AsString(key), "UnitLength_in_cm")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.UnitLength_in_cm = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "UnitMass_in_g")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.UnitMass_in_g = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "UnitVelocity_in_cm_per_s")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.UnitVelocity_in_cm_per_s = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "GravityConstantInternal")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.GravityConstantInternal = PyFloat_AsDouble(value); } /* Softening lengths */ if(strcmp(PyString_AsString(key), "MinGasHsmlFractional")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.MinGasHsmlFractional = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "SofteningGas")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.SofteningGas = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "SofteningHalo")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.SofteningHalo = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "SofteningDisk")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.SofteningDisk = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "SofteningBulge")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.SofteningBulge = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "SofteningStars")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.SofteningStars = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "SofteningBndry")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.SofteningBndry = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "SofteningGasMaxPhys")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.SofteningGasMaxPhys = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "SofteningHaloMaxPhys")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.SofteningHaloMaxPhys = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "SofteningDiskMaxPhys")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.SofteningDiskMaxPhys = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "SofteningBulgeMaxPhys")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.SofteningBulgeMaxPhys = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "SofteningStarsMaxPhys")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.SofteningStarsMaxPhys = PyFloat_AsDouble(value); } if(strcmp(PyString_AsString(key), "SofteningBndryMaxPhys")==0) { if(PyInt_Check(value)||PyLong_Check(value)||PyFloat_Check(value)) All.SofteningBndryMaxPhys = PyFloat_AsDouble(value); } } } return Py_BuildValue("i",1); } static PyObject * gadget_check_parser(PyObject *self, PyObject *args, PyObject *keywds) { int voltage; char *state = "a stiff"; char *action = "voom"; char *type = "Norwegian Blue"; static char *kwlist[] = {"voltage", "state", "action", "type", NULL}; if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|sss", kwlist, &voltage, &state, &action, &type)) return NULL; printf("-- This parrot wouldn't %s if you put %i Volts through it.\n", action, voltage); printf("-- Lovely plumage, the %s -- It's %s!\n", type, state); Py_INCREF(Py_None); return Py_None; } static PyObject *gadget_LoadParticles(PyObject *self, PyObject *args, PyObject *kwds) { int i,j; size_t bytes; PyArrayObject *ntype=Py_None; PyArrayObject *pos=Py_None; PyArrayObject *vel=Py_None; PyArrayObject *mass=Py_None; PyArrayObject *num=Py_None; PyArrayObject *tpe=Py_None; PyArrayObject *u=Py_None; PyArrayObject *rho=Py_None; static char *kwlist[] = {"npart", "pos","vel","mass","num","tpe","u","rho", NULL}; if (! PyArg_ParseTupleAndKeywords(args,kwds, "OOOOOO|OO",kwlist,&ntype,&pos,&vel,&mass,&num,&tpe,&u,&rho )) return NULL; /* check type */ if (!(PyArray_Check(pos))) { PyErr_SetString(PyExc_ValueError,"aruments 2 must be array."); return NULL; } /* check type */ if (!(PyArray_Check(mass))) { PyErr_SetString(PyExc_ValueError,"aruments 3 must be array."); return NULL; } /* check dimension */ if ( (pos->nd!=2)) { PyErr_SetString(PyExc_ValueError,"Dimension of argument 2 must be 2."); return NULL; } /* check dimension */ if ( (mass->nd!=1)) { PyErr_SetString(PyExc_ValueError,"Dimension of argument 3 must be 1."); return NULL; } /* check size */ if ( (pos->dimensions[1]!=3)) { PyErr_SetString(PyExc_ValueError,"First size of argument must be 3."); return NULL; } /* check size */ if ( (pos->dimensions[0]!=mass->dimensions[0])) { PyErr_SetString(PyExc_ValueError,"Size of argument 2 must be similar to argument 3."); return NULL; } /* ensure double */ ntype = TO_INT(ntype); pos = TO_FLOAT(pos); vel = TO_FLOAT(vel); mass = TO_FLOAT(mass); num = TO_FLOAT(num); tpe = TO_FLOAT(tpe); /* optional variables */ if (u!=Py_None) u = TO_FLOAT(u); if (rho!=Py_None) rho = TO_FLOAT(rho); /*************************************** * some inits * /***************************************/ RestartFlag = 0; Begrun1(); /*************************************** * LOAD PARTILES * /***************************************/ NumPart = 0; N_gas = *(int*) (ntype->data + 0*(ntype->strides[0])); for (i = 0; i < 6; i++) NumPart += *(int*) (ntype->data + i*(ntype->strides[0])); if (NumPart!=pos->dimensions[0]) { PyErr_SetString(PyExc_ValueError,"Numpart != pos->dimensions[0]."); return NULL; } MPI_Allreduce(&NumPart, &All.TotNumPart, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); MPI_Allreduce(&N_gas, &All.TotN_gas, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); All.MaxPart = All.PartAllocFactor * (All.TotNumPart / NTask); All.MaxPartSph = All.PartAllocFactor * (All.TotN_gas / NTask); /*********************/ /* allocate P */ /*********************/ if(!(P = malloc(bytes = All.MaxPart * sizeof(struct particle_data)))) { printf("failed to allocate memory for `P' (%g MB).\n", bytes / (1024.0 * 1024.0)); endrun(1); } if(!(SphP = malloc(bytes = All.MaxPartSph * sizeof(struct sph_particle_data)))) { printf("failed to allocate memory for `SphP' (%g MB) %d.\n", bytes / (1024.0 * 1024.0), sizeof(struct sph_particle_data)); endrun(1); } /*********************/ /* init P */ /*********************/ for (i = 0; i < NumPart; i++) { P[i].Pos[0] = *(float *) (pos->data + i*(pos->strides[0]) + 0*pos->strides[1]); P[i].Pos[1] = *(float *) (pos->data + i*(pos->strides[0]) + 1*pos->strides[1]); P[i].Pos[2] = *(float *) (pos->data + i*(pos->strides[0]) + 2*pos->strides[1]); P[i].Vel[0] = *(float *) (vel->data + i*(vel->strides[0]) + 0*vel->strides[1]); P[i].Vel[1] = *(float *) (vel->data + i*(vel->strides[0]) + 1*vel->strides[1]); P[i].Vel[2] = *(float *) (vel->data + i*(vel->strides[0]) + 2*vel->strides[1]); P[i].Mass = *(float *) (mass->data + i*(mass->strides[0])); P[i].ID = *(unsigned int *) (num->data + i*(num->strides[0])); P[i].Type = *(int *) (tpe->data + i*(tpe->strides[0])); //P[i].Active = 1; } /*********************/ /* init SphP */ /*********************/ if (u!=Py_None) for (i = 0; i < NumPart; i++) { SphP[i].Entropy = *(float *) (u->data + i*(u->strides[0])); //#ifndef ISOTHERM_EQS // SphP[i].Entropy = GAMMA_MINUS1 * SphP[i].Entropy / pow(SphP[i].Density / a3, GAMMA_MINUS1); //#endif } if (rho!=Py_None) for (i = 0; i < NumPart; i++) { SphP[i].Density = *(float *) (rho->data + i*(rho->strides[0])); } /*************************************** * END LOAD PARTILES * /***************************************/ /*************************************** * finish inits * /***************************************/ Begrun2(); return Py_BuildValue("i",1); } static PyObject *gadget_LoadParticlesQ(PyObject *self, PyObject *args, PyObject *kwds) { int i,j; size_t bytes; PyArrayObject *ntype,*pos,*vel,*mass,*num,*tpe; static char *kwlist[] = {"npart", "pos","vel","mass","num","tpe", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOOOOO",kwlist,&ntype,&pos,&vel,&mass,&num,&tpe)) return Py_BuildValue("i",1); /* check type */ if (!(PyArray_Check(pos))) { PyErr_SetString(PyExc_ValueError,"aruments 1 must be array."); return NULL; } /* check type */ if (!(PyArray_Check(mass))) { PyErr_SetString(PyExc_ValueError,"aruments 2 must be array."); return NULL; } /* check dimension */ if ( (pos->nd!=2)) { PyErr_SetString(PyExc_ValueError,"Dimension of argument 1 must be 2."); return NULL; } /* check dimension */ if ( (mass->nd!=1)) { PyErr_SetString(PyExc_ValueError,"Dimension of argument 2 must be 1."); return NULL; } /* check size */ if ( (pos->dimensions[1]!=3)) { PyErr_SetString(PyExc_ValueError,"First size of argument must be 3."); return NULL; } /* check size */ if ( (pos->dimensions[0]!=mass->dimensions[0])) { PyErr_SetString(PyExc_ValueError,"Size of argument 1 must be similar to argument 2."); return NULL; } /* ensure double */ ntype = TO_INT(ntype); pos = TO_FLOAT(pos); vel = TO_FLOAT(vel); mass = TO_FLOAT(mass); num = TO_FLOAT(num); tpe = TO_FLOAT(tpe); /*************************************** * some inits * /***************************************/ allocate_commbuffersQ(); /*************************************** * LOAD PARTILES * /***************************************/ NumPartQ = 0; N_gasQ = *(int*) (ntype->data + 0*(ntype->strides[0])); for (i = 0; i < 6; i++) NumPartQ += *(int*) (ntype->data + i*(ntype->strides[0])); if (NumPartQ!=pos->dimensions[0]) { PyErr_SetString(PyExc_ValueError,"NumpartQ != pos->dimensions[0]."); return NULL; } MPI_Allreduce(&NumPartQ, &All.TotNumPartQ, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); MPI_Allreduce(&N_gasQ, &All.TotN_gasQ, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); All.MaxPartQ = All.PartAllocFactor * (All.TotNumPartQ / NTask); All.MaxPartSphQ = All.PartAllocFactor * (All.TotN_gasQ / NTask); /*********************/ /* allocate Q */ /*********************/ if(!(Q = malloc(bytes = All.MaxPartQ * sizeof(struct particle_data)))) { printf("failed to allocate memory for `Q' (%g MB).\n", bytes / (1024.0 * 1024.0)); endrun(1); } if(!(SphQ = malloc(bytes = All.MaxPartSphQ * sizeof(struct sph_particle_data)))) { printf("failed to allocate memory for `SphQ' (%g MB) %d.\n", bytes / (1024.0 * 1024.0), sizeof(struct sph_particle_data)); endrun(1); } /*********************/ /* init P */ /*********************/ for (i = 0; i < NumPartQ; i++) { Q[i].Pos[0] = *(float *) (pos->data + i*(pos->strides[0]) + 0*pos->strides[1]); Q[i].Pos[1] = *(float *) (pos->data + i*(pos->strides[0]) + 1*pos->strides[1]); Q[i].Pos[2] = *(float *) (pos->data + i*(pos->strides[0]) + 2*pos->strides[1]); Q[i].Vel[0] = *(float *) (vel->data + i*(vel->strides[0]) + 0*vel->strides[1]); Q[i].Vel[1] = *(float *) (vel->data + i*(vel->strides[0]) + 1*vel->strides[1]); Q[i].Vel[2] = *(float *) (vel->data + i*(vel->strides[0]) + 2*vel->strides[1]); Q[i].Mass = *(float *) (mass->data + i*(mass->strides[0])); Q[i].ID = *(unsigned int *) (num->data + i*(num->strides[0])); Q[i].Type = *(int *) (tpe->data + i*(tpe->strides[0])); //Q[i].Active = 1; } /*************************************** * END LOAD PARTILES * /***************************************/ domain_DecompositionQ(); /*************************************** * finish inits * /***************************************/ return Py_BuildValue("i",1); } static PyObject *gadget_AllPotential(PyObject *self) { compute_potential(); return Py_BuildValue("i",1); } static PyObject * gadget_GetAllPotential(PyObject* self) { PyArrayObject *pot; npy_intp ld[1]; int i; ld[0] = NumPart; pot = (PyArrayObject *) PyArray_SimpleNew(1,ld,PyArray_FLOAT); for (i = 0; i < pot->dimensions[0]; i++) { *(float *) (pot->data + i*(pot->strides[0])) = P[i].Potential; } return PyArray_Return(pot); } static PyObject *gadget_AllAcceleration(PyObject *self) { NumForceUpdate = NumPart; gravity_tree(); return Py_BuildValue("i",1); } static PyObject * gadget_GetAllAcceleration(PyObject* self) { PyArrayObject *acc; npy_intp ld[2]; int i; ld[0] = NumPart; ld[1] = 3; acc = (PyArrayObject *) PyArray_SimpleNew(2,ld,PyArray_FLOAT); for (i = 0; i < acc->dimensions[0]; i++) { *(float *) (acc->data + i*(acc->strides[0]) + 0*acc->strides[1]) = P[i].GravAccel[0]; *(float *) (acc->data + i*(acc->strides[0]) + 1*acc->strides[1]) = P[i].GravAccel[1]; *(float *) (acc->data + i*(acc->strides[0]) + 2*acc->strides[1]) = P[i].GravAccel[2]; } return PyArray_Return(acc); } static PyObject *gadget_GetAllDensities(PyObject* self) { PyArrayObject *rho; npy_intp ld[1]; int i; ld[0] = N_gas; rho = (PyArrayObject *) PyArray_SimpleNew(1,ld,PyArray_FLOAT); for (i = 0; i < rho->dimensions[0]; i++) { *(float *) (rho->data + i*(rho->strides[0])) = SphP[i].Density; } return PyArray_Return(rho); } static PyObject *gadget_GetAllHsml(PyObject* self) { PyArrayObject *hsml; npy_intp ld[1]; int i; ld[0] = N_gas; hsml = (PyArrayObject *) PyArray_SimpleNew(1,ld,PyArray_FLOAT); for (i = 0; i < hsml->dimensions[0]; i++) { *(float *) (hsml->data + i*(hsml->strides[0])) = SphP[i].Hsml; } return PyArray_Return(hsml); } static PyObject *gadget_GetAllPositions(PyObject* self) { PyArrayObject *pos; npy_intp ld[2]; int i; ld[0] = NumPart; ld[1] = 3; pos = (PyArrayObject *) PyArray_SimpleNew(2,ld,PyArray_FLOAT); for (i = 0; i < pos->dimensions[0]; i++) { *(float *) (pos->data + i*(pos->strides[0]) + 0*pos->strides[1]) = P[i].Pos[0]; *(float *) (pos->data + i*(pos->strides[0]) + 1*pos->strides[1]) = P[i].Pos[1]; *(float *) (pos->data + i*(pos->strides[0]) + 2*pos->strides[1]) = P[i].Pos[2]; } return PyArray_Return(pos); } static PyObject *gadget_GetAllVelocities(PyObject* self) { PyArrayObject *vel; npy_intp ld[2]; int i; ld[0] = NumPart; ld[1] = 3; vel = (PyArrayObject *) PyArray_SimpleNew(2,ld,PyArray_FLOAT); for (i = 0; i < vel->dimensions[0]; i++) { *(float *) (vel->data + i*(vel->strides[0]) + 0*vel->strides[1]) = P[i].Vel[0]; *(float *) (vel->data + i*(vel->strides[0]) + 1*vel->strides[1]) = P[i].Vel[1]; *(float *) (vel->data + i*(vel->strides[0]) + 2*vel->strides[1]) = P[i].Vel[2]; } return PyArray_Return(vel); } static PyObject *gadget_GetAllMasses(PyObject* self) { PyArrayObject *mass; npy_intp ld[1]; int i; ld[0] = NumPart; mass = (PyArrayObject *) PyArray_SimpleNew(1,ld,PyArray_FLOAT); for (i = 0; i < mass->dimensions[0]; i++) { *(float *) (mass->data + i*(mass->strides[0])) = P[i].Mass; } return PyArray_Return(mass); } static PyObject *gadget_GetAllEntropy(PyObject* self) { PyArrayObject *entropy; npy_intp ld[1]; int i; ld[0] = NumPart; entropy = (PyArrayObject *) PyArray_SimpleNew(1,ld,PyArray_FLOAT); for (i = 0; i < entropy->dimensions[0]; i++) { *(float *) (entropy->data + i*(entropy->strides[0])) = SphP[i].Entropy; } return PyArray_Return(entropy); } static PyObject *gadget_GetAllEnergySpec(PyObject* self) { PyArrayObject *energy; npy_intp ld[1]; int i; double a3inv; ld[0] = NumPart; energy = (PyArrayObject *) PyArray_SimpleNew(1,ld,PyArray_FLOAT); if(All.ComovingIntegrationOn) { a3inv = 1 / (All.Time * All.Time * All.Time); } else a3inv = 1; for (i = 0; i < energy->dimensions[0]; i++) { #ifdef ISOTHERM_EQS *(float *) (energy->data + i*(energy->strides[0])) = SphP[i].Entropy; #else a3inv = 1.; *(float *) (energy->data + i*(energy->strides[0])) = dmax(All.MinEgySpec,SphP[i].Entropy / GAMMA_MINUS1 * pow(SphP[i].Density * a3inv, GAMMA_MINUS1)); #endif } return PyArray_Return(energy); } static PyObject *gadget_GetAllID(PyObject* self) { PyArrayObject *id; npy_intp ld[1]; int i; ld[0] = NumPart; id = (PyArrayObject *) PyArray_SimpleNew(1,ld,PyArray_INT); for (i = 0; i < id->dimensions[0]; i++) { *(float *) (id->data + i*(id->strides[0])) = P[i].ID; } return PyArray_Return(id); } static PyObject *gadget_GetAllTypes(PyObject* self) { PyArrayObject *type; npy_intp ld[1]; int i; ld[0] = NumPart; type = (PyArrayObject *) PyArray_SimpleNew(1,ld,PyArray_INT); for (i = 0; i < type->dimensions[0]; i++) { *(int *) (type->data + i*(type->strides[0])) = P[i].Type; } return PyArray_Return(type); } static PyObject *gadget_GetAllPositionsQ(PyObject* self) { PyArrayObject *pos; npy_intp ld[2]; int i; ld[0] = NumPartQ; ld[1] = 3; pos = (PyArrayObject *) PyArray_SimpleNew(2,ld,PyArray_FLOAT); for (i = 0; i < pos->dimensions[0]; i++) { *(float *) (pos->data + i*(pos->strides[0]) + 0*pos->strides[1]) = Q[i].Pos[0]; *(float *) (pos->data + i*(pos->strides[0]) + 1*pos->strides[1]) = Q[i].Pos[1]; *(float *) (pos->data + i*(pos->strides[0]) + 2*pos->strides[1]) = Q[i].Pos[2]; } return PyArray_Return(pos); } static PyObject *gadget_GetAllVelocitiesQ(PyObject* self) { PyArrayObject *vel; npy_intp ld[2]; int i; ld[0] = NumPartQ; ld[1] = 3; vel = (PyArrayObject *) PyArray_SimpleNew(2,ld,PyArray_FLOAT); for (i = 0; i < vel->dimensions[0]; i++) { *(float *) (vel->data + i*(vel->strides[0]) + 0*vel->strides[1]) = Q[i].Vel[0]; *(float *) (vel->data + i*(vel->strides[0]) + 1*vel->strides[1]) = Q[i].Vel[1]; *(float *) (vel->data + i*(vel->strides[0]) + 2*vel->strides[1]) = Q[i].Vel[2]; } return PyArray_Return(vel); } static PyObject *gadget_GetAllMassesQ(PyObject* self) { PyArrayObject *mass; npy_intp ld[1]; int i; ld[0] = NumPartQ; mass = (PyArrayObject *) PyArray_SimpleNew(1,ld,PyArray_FLOAT); for (i = 0; i < mass->dimensions[0]; i++) { *(float *) (mass->data + i*(mass->strides[0])) = Q[i].Mass; } return PyArray_Return(mass); } static PyObject *gadget_GetAllIDQ(PyObject* self) { PyArrayObject *id; npy_intp ld[1]; int i; ld[0] = NumPartQ; id = (PyArrayObject *) PyArray_SimpleNew(1,ld,PyArray_INT); for (i = 0; i < id->dimensions[0]; i++) { *(float *) (id->data + i*(id->strides[0])) = Q[i].ID; } return PyArray_Return(id); } static PyObject *gadget_GetAllTypesQ(PyObject* self) { PyArrayObject *type; npy_intp ld[1]; int i; ld[0] = NumPartQ; type = (PyArrayObject *) PyArray_SimpleNew(1,ld,PyArray_INT); for (i = 0; i < type->dimensions[0]; i++) { *(int *) (type->data + i*(type->strides[0])) = Q[i].Type; } return PyArray_Return(type); } static PyObject *gadget_GetPos(PyObject *self, PyObject *args, PyObject *kwds) { int i,j; size_t bytes; PyArrayObject *pos; if (! PyArg_ParseTuple(args, "O",&pos)) return PyString_FromString("error : GetPos"); for (i = 0; i < pos->dimensions[0]; i++) { *(float *) (pos->data + i*(pos->strides[0]) + 0*pos->strides[1]) = P[i].Pos[0]; *(float *) (pos->data + i*(pos->strides[0]) + 1*pos->strides[1]) = P[i].Pos[1]; *(float *) (pos->data + i*(pos->strides[0]) + 2*pos->strides[1]) = P[i].Pos[2]; } //return PyArray_Return(Py_None); return Py_BuildValue("i",1); } static PyObject * gadget_Potential(PyObject* self, PyObject *args) { PyArrayObject *pos; float eps; if (! PyArg_ParseTuple(args, "Of",&pos,&eps)) return PyString_FromString("error"); PyArrayObject *pot; int i; npy_intp ld[1]; int input_dimension; size_t bytes; input_dimension =pos->nd; if (input_dimension != 2) PyErr_SetString(PyExc_ValueError,"dimension of first argument must be 2"); pos = TO_FLOAT(pos); /* create a NumPy object */ ld[0]=pos->dimensions[0]; pot = (PyArrayObject *) PyArray_SimpleNew(1,ld,PyArray_FLOAT); NumPartQ = pos->dimensions[0]; All.ForceSofteningQ = eps; if(!(Q = malloc(bytes = NumPartQ * sizeof(struct particle_data)))) { printf("failed to allocate memory for `Q' (%g MB).\n", bytes / (1024.0 * 1024.0)); endrun(1); } if(!(SphQ = malloc(bytes = NumPartQ * sizeof(struct sph_particle_data)))) { printf("failed to allocate memory for `SphQ' (%g MB).\n", bytes / (1024.0 * 1024.0)); endrun(1); } for (i = 0; i < pos->dimensions[0]; i++) { Q[i].Pos[0] = *(float *) (pos->data + i*(pos->strides[0]) + 0*pos->strides[1]); Q[i].Pos[1] = *(float *) (pos->data + i*(pos->strides[0]) + 1*pos->strides[1]); Q[i].Pos[2] = *(float *) (pos->data + i*(pos->strides[0]) + 2*pos->strides[1]); Q[i].Type = 0; Q[i].Mass = 0; Q[i].Potential = 0; } compute_potential_sub(); for (i = 0; i < pos->dimensions[0]; i++) { *(float *)(pot->data + i*(pot->strides[0])) = Q[i].Potential; } free(Q); free(SphQ); return PyArray_Return(pot); } static PyObject * gadget_Acceleration(PyObject* self, PyObject *args) { PyArrayObject *pos; float eps; if (! PyArg_ParseTuple(args, "Of",&pos,&eps)) return PyString_FromString("error"); PyArrayObject *acc; int i; int input_dimension; size_t bytes; input_dimension =pos->nd; if (input_dimension != 2) PyErr_SetString(PyExc_ValueError,"dimension of first argument must be 2"); pos = TO_FLOAT(pos); /* create a NumPy object */ acc = (PyArrayObject *) PyArray_SimpleNew(pos->nd,pos->dimensions,PyArray_FLOAT); NumPartQ = pos->dimensions[0]; All.ForceSofteningQ = eps; if(!(Q = malloc(bytes = NumPartQ * sizeof(struct particle_data)))) { printf("failed to allocate memory for `Q' (%g MB).\n", bytes / (1024.0 * 1024.0)); endrun(1); } if(!(SphQ = malloc(bytes = NumPartQ * sizeof(struct sph_particle_data)))) { printf("failed to allocate memory for `SphQ' (%g MB).\n", bytes / (1024.0 * 1024.0)); endrun(1); } for (i = 0; i < pos->dimensions[0]; i++) { Q[i].Pos[0] = *(float *) (pos->data + i*(pos->strides[0]) + 0*pos->strides[1]); Q[i].Pos[1] = *(float *) (pos->data + i*(pos->strides[0]) + 1*pos->strides[1]); Q[i].Pos[2] = *(float *) (pos->data + i*(pos->strides[0]) + 2*pos->strides[1]); Q[i].Type = 0; Q[i].Mass = 0; Q[i].GravAccel[0] = 0; Q[i].GravAccel[1] = 0; Q[i].GravAccel[2] = 0; } gravity_tree_sub(); for (i = 0; i < pos->dimensions[0]; i++) { *(float *)(acc->data + i*(acc->strides[0]) + 0*acc->strides[1]) = Q[i].GravAccel[0]; *(float *)(acc->data + i*(acc->strides[0]) + 1*acc->strides[1]) = Q[i].GravAccel[1]; *(float *)(acc->data + i*(acc->strides[0]) + 2*acc->strides[1]) = Q[i].GravAccel[2]; } free(Q); free(SphQ); return PyArray_Return(acc); } static PyObject * gadget_InitHsml(PyObject* self, PyObject *args) { PyArrayObject *pos,*hsml; if (! PyArg_ParseTuple(args, "OO",&pos,&hsml)) return PyString_FromString("error"); int i; int input_dimension; size_t bytes; int ld[1]; PyArrayObject *vden,*vhsml; input_dimension =pos->nd; if (input_dimension != 2) PyErr_SetString(PyExc_ValueError,"dimension of first argument must be 2"); if (pos->dimensions[0] != hsml->dimensions[0]) PyErr_SetString(PyExc_ValueError,"pos and hsml must have the same dimension."); pos = TO_FLOAT(pos); hsml = TO_FLOAT(hsml); /* create a NumPy object */ ld[0]=pos->dimensions[0]; vden = (PyArrayObject *) PyArray_SimpleNew(1,pos->dimensions,pos->descr->type_num); vhsml = (PyArrayObject *) PyArray_SimpleNew(1,pos->dimensions,pos->descr->type_num); NumPartQ = pos->dimensions[0]; N_gasQ = NumPartQ; All.Ti_Current=1; /* need to flag active particles */ if(!(Q = malloc(bytes = NumPartQ * sizeof(struct particle_data)))) { printf("failed to allocate memory for `Q' (%g MB).\n", bytes / (1024.0 * 1024.0)); endrun(1); } if(!(SphQ = malloc(bytes = NumPartQ * sizeof(struct sph_particle_data)))) { printf("failed to allocate memory for `SphQ' (%g MB).\n", bytes / (1024.0 * 1024.0)); endrun(1); } for (i = 0; i < pos->dimensions[0]; i++) { Q[i].Pos[0] = *(float *) (pos->data + i*(pos->strides[0]) + 0*pos->strides[1]); Q[i].Pos[1] = *(float *) (pos->data + i*(pos->strides[0]) + 1*pos->strides[1]); Q[i].Pos[2] = *(float *) (pos->data + i*(pos->strides[0]) + 2*pos->strides[1]); SphQ[i].Hsml = *(float *) (hsml->data + i*(hsml->strides[0])); } setup_smoothinglengths_sub(); for (i = 0; i < pos->dimensions[0]; i++) { *(float *)(vhsml->data + i*(vhsml->strides[0])) = SphQ[i].Hsml; *(float *)(vden->data + i*(vden->strides[0])) = SphQ[i].Density; } free(Q); free(SphQ); return Py_BuildValue("OO",vden,vhsml); } static PyObject * gadget_Density(PyObject* self, PyObject *args) { PyArrayObject *pos,*hsml; if (! PyArg_ParseTuple(args, "OO",&pos,&hsml)) return PyString_FromString("error"); int i; int input_dimension; size_t bytes; int ld[1]; PyArrayObject *vden,*vhsml; input_dimension =pos->nd; if (input_dimension != 2) PyErr_SetString(PyExc_ValueError,"dimension of first argument must be 2"); if (pos->dimensions[0] != hsml->dimensions[0]) PyErr_SetString(PyExc_ValueError,"pos and hsml must have the same dimension."); pos = TO_FLOAT(pos); hsml = TO_FLOAT(hsml); /* create a NumPy object */ ld[0]=pos->dimensions[0]; vden = (PyArrayObject *) PyArray_SimpleNew(1,pos->dimensions,pos->descr->type_num); vhsml = (PyArrayObject *) PyArray_SimpleNew(1,pos->dimensions,pos->descr->type_num); NumPartQ = pos->dimensions[0]; N_gasQ = NumPartQ; All.Ti_Current=1; /* need to flag active particles */ if(!(Q = malloc(bytes = NumPartQ * sizeof(struct particle_data)))) { printf("failed to allocate memory for `Q' (%g MB).\n", bytes / (1024.0 * 1024.0)); endrun(1); } if(!(SphQ = malloc(bytes = NumPartQ * sizeof(struct sph_particle_data)))) { printf("failed to allocate memory for `SphQ' (%g MB).\n", bytes / (1024.0 * 1024.0)); endrun(1); } for (i = 0; i < pos->dimensions[0]; i++) { Q[i].Pos[0] = *(float *) (pos->data + i*(pos->strides[0]) + 0*pos->strides[1]); Q[i].Pos[1] = *(float *) (pos->data + i*(pos->strides[0]) + 1*pos->strides[1]); Q[i].Pos[2] = *(float *) (pos->data + i*(pos->strides[0]) + 2*pos->strides[1]); SphQ[i].Hsml = *(float *) (hsml->data + i*(hsml->strides[0])); } density_sub(); for (i = 0; i < pos->dimensions[0]; i++) { *(float *)(vhsml->data + i*(vhsml->strides[0])) = SphQ[i].Hsml; *(float *)(vden->data + i*(vden->strides[0])) = SphQ[i].Density; } free(Q); free(SphQ); return Py_BuildValue("OO",vden,vhsml); } static PyObject * gadget_SphEvaluate(PyObject* self, PyObject *args) { PyArrayObject *pos,*hsml,*obs; if (! PyArg_ParseTuple(args, "OOO",&pos,&hsml,&obs)) return PyString_FromString("error"); int i; int input_dimension; size_t bytes; int ld[1]; PyArrayObject *vobs; input_dimension =pos->nd; if (input_dimension != 2) PyErr_SetString(PyExc_ValueError,"dimension of first argument must be 2"); if (pos->dimensions[0] != hsml->dimensions[0]) PyErr_SetString(PyExc_ValueError,"pos and hsml must have the same dimension."); if (obs->nd != 1) PyErr_SetString(PyExc_ValueError,"dimension of obs must be 1."); if (obs->dimensions[0] != NumPart) PyErr_SetString(PyExc_ValueError,"The size of obs must be NumPart."); pos = TO_FLOAT(pos); hsml = TO_FLOAT(hsml); obs = TO_FLOAT(obs); /* create a NumPy object */ ld[0]=pos->dimensions[0]; vobs = (PyArrayObject *) PyArray_SimpleNew(1,pos->dimensions,pos->descr->type_num); NumPartQ = pos->dimensions[0]; N_gasQ = NumPartQ; All.Ti_Current=1; /* need to flag active particles */ if(!(Q = malloc(bytes = NumPartQ * sizeof(struct particle_data)))) { printf("failed to allocate memory for `Q' (%g MB).\n", bytes / (1024.0 * 1024.0)); endrun(1); } if(!(SphQ = malloc(bytes = NumPartQ * sizeof(struct sph_particle_data)))) { printf("failed to allocate memory for `SphQ' (%g MB).\n", bytes / (1024.0 * 1024.0)); endrun(1); } for (i = 0; i < pos->dimensions[0]; i++) { Q[i].Pos[0] = *(float *) (pos->data + i*(pos->strides[0]) + 0*pos->strides[1]); Q[i].Pos[1] = *(float *) (pos->data + i*(pos->strides[0]) + 1*pos->strides[1]); Q[i].Pos[2] = *(float *) (pos->data + i*(pos->strides[0]) + 2*pos->strides[1]); SphQ[i].Hsml = *(float *) (hsml->data + i*(hsml->strides[0])); } /* now, give observable value for P */ for (i = 0; i < NumPart; i++) { SphP[i].Observable = *(float *) (obs->data + i*(obs->strides[0])); } sph_sub(); for (i = 0; i < pos->dimensions[0]; i++) { *(float *)(vobs->data + i*(vobs->strides[0])) = SphQ[i].Observable; } free(Q); free(SphQ); return Py_BuildValue("O",vobs); } static PyObject * gadget_Ngbs(PyObject* self, PyObject *args) { PyArrayObject *pos; float eps; if (! PyArg_ParseTuple(args, "Of",&pos,&eps)) return PyString_FromString("error"); PyArrayObject *poss; int i,j,n,nn; int input_dimension; size_t bytes; int startnode,numngb; FLOAT searchcenter[3]; double dx,dy,dz,r2,eps2; input_dimension =pos->nd; if (input_dimension != 1) PyErr_SetString(PyExc_ValueError,"dimension of first argument must be 1"); pos = TO_FLOAT(pos); eps2 = eps*eps; searchcenter[0] = (FLOAT)*(float *) (pos->data + 0*(pos->strides[0])); searchcenter[1] = (FLOAT)*(float *) (pos->data + 1*(pos->strides[0])); searchcenter[2] = (FLOAT)*(float *) (pos->data + 2*(pos->strides[0])); startnode = All.MaxPart; /* ici, il faut faire une fct qui fonctionne en //, cf hydra --> Exportflag */ numngb = ngb_treefind_pairs(&searchcenter[0], (FLOAT)eps, &startnode); nn=0; for(n = 0;n < numngb; n++) { j = Ngblist[n]; dx = searchcenter[0] - P[j].Pos[0]; dy = searchcenter[1] - P[j].Pos[1]; dz = searchcenter[2] - P[j].Pos[2]; r2 = dx * dx + dy * dy + dz * dz; if (r2<=eps2) { printf("%d r=%g\n",nn,sqrt(r2)); nn++; } } return PyArray_Return(pos); } static PyObject * gadget_SphEvaluateOrigAll(PyObject* self, PyObject *args) { PyArrayObject *obs; if (! PyArg_ParseTuple(args, "O",&obs)) return PyString_FromString("error"); int i; size_t bytes; int ld[1]; PyArrayObject *vobs; if (obs->nd != 1) PyErr_SetString(PyExc_ValueError,"dimension of obs must be 1."); if (obs->dimensions[0] != NumPart) PyErr_SetString(PyExc_ValueError,"The size of obs must be NumPart."); obs = TO_FLOAT(obs); /* create a NumPy object */ ld[0]=NumPart; vobs = (PyArrayObject *) PyArray_SimpleNew(1,ld,PyArray_FLOAT); /* flag all particles as active */ for (i = 0; i < NumPart; i++) P[i].Ti_endstep == All.Ti_Current; /* now, give observable value for P */ for (i = 0; i < NumPart; i++) { SphP[i].Observable = *(float *) (obs->data + i*(obs->strides[0])); } sph_orig(); for (i = 0; i < NumPart; i++) { *(float *)(vobs->data + i*(vobs->strides[0])) = SphP[i].Observable; } return Py_BuildValue("O",vobs); } static PyObject * gadget_SphEvaluateAll(PyObject* self, PyObject *args) { PyArrayObject *obs; if (! PyArg_ParseTuple(args, "O",&obs)) return PyString_FromString("error"); int i; size_t bytes; int ld[1]; PyArrayObject *vobs; if (obs->nd != 1) PyErr_SetString(PyExc_ValueError,"dimension of obs must be 1."); if (obs->dimensions[0] != NumPart) PyErr_SetString(PyExc_ValueError,"The size of obs must be NumPart."); obs = TO_FLOAT(obs); /* create a NumPy object */ ld[0]=NumPart; vobs = (PyArrayObject *) PyArray_SimpleNew(1,ld,PyArray_FLOAT); /* flag all particles as active */ for (i = 0; i < NumPart; i++) P[i].Ti_endstep == All.Ti_Current; /* now, give observable value for P */ for (i = 0; i < NumPart; i++) { SphP[i].Observable = *(float *) (obs->data + i*(obs->strides[0])); } sph(); for (i = 0; i < NumPart; i++) { *(float *)(vobs->data + i*(vobs->strides[0])) = SphP[i].Observable; } return Py_BuildValue("O",vobs); } static PyObject * gadget_SphEvaluateGradientAll(PyObject* self, PyObject *args) { PyArrayObject *obs; if (! PyArg_ParseTuple(args, "O",&obs)) return PyString_FromString("error"); int i; size_t bytes; npy_intp ld[2]; PyArrayObject *grad; if (obs->nd != 1) PyErr_SetString(PyExc_ValueError,"dimension of obs must be 1."); if (obs->dimensions[0] != NumPart) PyErr_SetString(PyExc_ValueError,"The size of obs must be NumPart."); obs = TO_FLOAT(obs); All.Ti_Current=1; /* need to flag active particles */ /* now, give observable value for P */ for (i = 0; i < NumPart; i++) { SphP[i].Observable = *(float *) (obs->data + i*(obs->strides[0])); } sph(); /* create a NumPy object */ ld[0] = NumPart; ld[1] = 3; grad = (PyArrayObject *) PyArray_SimpleNew(2,ld,PyArray_FLOAT); for (i = 0; i < NumPart; i++) { *(float *) (grad->data + i*(grad->strides[0]) + 0*grad->strides[1]) = SphP[i].GradObservable[0]; *(float *) (grad->data + i*(grad->strides[0]) + 1*grad->strides[1]) = SphP[i].GradObservable[1]; *(float *) (grad->data + i*(grad->strides[0]) + 2*grad->strides[1]) = SphP[i].GradObservable[2]; } return PyArray_Return(grad); } +static PyObject * gadget_DensityEvaluateAll(PyObject* self, PyObject *args) +{ + int i; + + /* flag all particles as active */ + //All.Ti_Current=1; /* need to flag active particles */ + for (i = 0; i < NumPart; i++) + P[i].Ti_endstep == All.Ti_Current; + + density(); + + return Py_BuildValue("i",1); +} +static PyObject * gadget_DensityEvaluateGradientAll(PyObject* self, PyObject *args) +{ + + + PyArrayObject *obs; + + if (! PyArg_ParseTuple(args, "O",&obs)) + return PyString_FromString("error"); + + int i; + size_t bytes; + npy_intp ld[2]; + PyArrayObject *grad; + + + if (obs->nd != 1) + PyErr_SetString(PyExc_ValueError,"dimension of obs must be 1."); + + if (obs->dimensions[0] != NumPart) + PyErr_SetString(PyExc_ValueError,"The size of obs must be NumPart."); + + obs = TO_FLOAT(obs); + + + + //All.Ti_Current=1; /* need to flag active particles */ + for (i = 0; i < NumPart; i++) + P[i].Ti_endstep == All.Ti_Current; + + + /* now, give observable value for P */ + + for (i = 0; i < NumPart; i++) + { + SphP[i].Observable = *(float *) (obs->data + i*(obs->strides[0])); + } + + density_sph_gradient(); + + /* create a NumPy object */ + ld[0] = NumPart; + ld[1] = 3; + + grad = (PyArrayObject *) PyArray_SimpleNew(2,ld,PyArray_FLOAT); + + for (i = 0; i < NumPart; i++) + { + *(float *) (grad->data + i*(grad->strides[0]) + 0*grad->strides[1]) = SphP[i].GradObservable[0]; + *(float *) (grad->data + i*(grad->strides[0]) + 1*grad->strides[1]) = SphP[i].GradObservable[1]; + *(float *) (grad->data + i*(grad->strides[0]) + 2*grad->strides[1]) = SphP[i].GradObservable[2]; + } + + return PyArray_Return(grad); +} + + static PyObject *gadget_LoadParticles2(PyObject *self, PyObject *args, PyObject *kwds) { int i,j; size_t bytes; PyArrayObject *ntype,*pos,*vel,*mass,*num,*tpe; static char *kwlist[] = {"npart", "pos","vel","mass","num","tpe", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOOOOO",kwlist,&ntype,&pos,&vel,&mass,&num,&tpe)) return Py_BuildValue("i",1); /* check type */ if (!(PyArray_Check(pos))) { PyErr_SetString(PyExc_ValueError,"aruments 1 must be array."); return NULL; } /* check type */ if (!(PyArray_Check(mass))) { PyErr_SetString(PyExc_ValueError,"aruments 2 must be array."); return NULL; } /* check dimension */ if ( (pos->nd!=2)) { PyErr_SetString(PyExc_ValueError,"Dimension of argument 1 must be 2."); return NULL; } /* check dimension */ if ( (mass->nd!=1)) { PyErr_SetString(PyExc_ValueError,"Dimension of argument 2 must be 1."); return NULL; } /* check size */ if ( (pos->dimensions[1]!=3)) { PyErr_SetString(PyExc_ValueError,"First size of argument must be 3."); return NULL; } /* check size */ if ( (pos->dimensions[0]!=mass->dimensions[0])) { PyErr_SetString(PyExc_ValueError,"Size of argument 1 must be similar to argument 2."); return NULL; } /* ensure double */ // ntype = TO_INT(ntype); // pos = TO_FLOAT(pos); // vel = TO_FLOAT(vel); // mass = TO_FLOAT(mass); // num = TO_FLOAT(num); // tpe = TO_FLOAT(tpe); /*************************************** * some inits * /***************************************/ RestartFlag = 0; Begrun1(); /*************************************** * LOAD PARTILES * /***************************************/ NumPart = 0; N_gas = *(int*) (ntype->data + 0*(ntype->strides[0])); for (i = 0; i < 6; i++) NumPart += *(int*) (ntype->data + i*(ntype->strides[0])); if (NumPart!=pos->dimensions[0]) { PyErr_SetString(PyExc_ValueError,"Numpart != pos->dimensions[0]."); return NULL; } MPI_Allreduce(&NumPart, &All.TotNumPart, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); MPI_Allreduce(&N_gas, &All.TotN_gas, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); All.MaxPart = All.PartAllocFactor * (All.TotNumPart / NTask); All.MaxPartSph = All.PartAllocFactor * (All.TotN_gas / NTask); /*********************/ /* allocate P */ /*********************/ if(!(P = malloc(bytes = All.MaxPart * sizeof(struct particle_data)))) { printf("failed to allocate memory for `P' (%g MB).\n", bytes / (1024.0 * 1024.0)); endrun(1); } if(!(SphP = malloc(bytes = All.MaxPartSph * sizeof(struct sph_particle_data)))) { printf("failed to allocate memory for `SphP' (%g MB) %d.\n", bytes / (1024.0 * 1024.0), sizeof(struct sph_particle_data)); endrun(1); } /*********************/ /* init P */ /*********************/ float * fpt; for (i = 0; i < NumPart; i++) { //P[i].Pos[0] = *(float *) (pos->data + i*(pos->strides[0]) + 0*pos->strides[1]); //P[i].Pos[1] = *(float *) (pos->data + i*(pos->strides[0]) + 1*pos->strides[1]); //P[i].Pos[2] = *(float *) (pos->data + i*(pos->strides[0]) + 2*pos->strides[1]); //&P[i].Pos[0] = (float *) (pos->data + i*(pos->strides[0]) + 0*pos->strides[1]); //&P[i].Pos[1] = (float *) (pos->data + i*(pos->strides[0]) + 1*pos->strides[1]); //&P[i].Pos[2] = (float *) (pos->data + i*(pos->strides[0]) + 2*pos->strides[1]); fpt = (float *) (pos->data + i*(pos->strides[0]) + 0*pos->strides[1]); P[i].Vel[0] = *(float *) (vel->data + i*(vel->strides[0]) + 0*vel->strides[1]); P[i].Vel[1] = *(float *) (vel->data + i*(vel->strides[0]) + 1*vel->strides[1]); P[i].Vel[2] = *(float *) (vel->data + i*(vel->strides[0]) + 2*vel->strides[1]); P[i].Mass = *(float *) (mass->data + i*(mass->strides[0])); P[i].ID = *(unsigned int *) (num->data + i*(num->strides[0])); P[i].Type = *(int *) (tpe->data + i*(tpe->strides[0])); //P[i].Active = 1; } /*************************************** * END LOAD PARTILES * /***************************************/ /*************************************** * finish inits * /***************************************/ Begrun2(); return Py_BuildValue("i",1); } /* definition of the method table */ static PyMethodDef gadgetMethods[] = { {"Info", (PyCFunction)gadget_Info, METH_VARARGS, "give some info"}, {"InitMPI", (PyCFunction)gadget_InitMPI, METH_VARARGS, "Init MPI"}, {"InitDefaultParameters", (PyCFunction)gadget_InitDefaultParameters, METH_VARARGS, "Init default parameters"}, {"GetParameters", (PyCFunction)gadget_GetParameters, METH_VARARGS, "get gadget parameters"}, {"SetParameters", (PyCFunction)gadget_SetParameters, METH_VARARGS, "Set gadget parameters"}, {"check_parser", (PyCFunction)gadget_check_parser, METH_VARARGS|METH_KEYWORDS, "check the parser"}, {"LoadParticles", (PyCFunction)gadget_LoadParticles, METH_VARARGS|METH_KEYWORDS, "LoadParticles partilces"}, {"LoadParticlesQ", (PyCFunction)gadget_LoadParticlesQ, METH_VARARGS, "LoadParticles partilces Q"}, {"LoadParticles2", (PyCFunction)gadget_LoadParticles2, METH_VARARGS, "LoadParticles partilces"}, {"AllPotential", (PyCFunction)gadget_AllPotential, METH_VARARGS, "Computes the potential for each particle"}, {"AllAcceleration", (PyCFunction)gadget_AllAcceleration, METH_VARARGS, "Computes the gravitational acceleration for each particle"}, {"GetAllAcceleration", (PyCFunction)gadget_GetAllAcceleration, METH_VARARGS, "get the gravitational acceleration for each particle"}, {"GetAllPotential", (PyCFunction)gadget_GetAllPotential, METH_VARARGS, "get the potential for each particle"}, {"GetAllDensities", (PyCFunction)gadget_GetAllDensities, METH_VARARGS, "get the densities for each particle"}, {"GetAllHsml", (PyCFunction)gadget_GetAllHsml, METH_VARARGS, "get the sph smoothing length for each particle"}, {"GetAllPositions", (PyCFunction)gadget_GetAllPositions, METH_VARARGS, "get the position for each particle"}, {"GetAllVelocities", (PyCFunction)gadget_GetAllVelocities, METH_VARARGS, "get the velocities for each particle"}, {"GetAllMasses", (PyCFunction)gadget_GetAllMasses, METH_VARARGS, "get the mass for each particle"}, {"GetAllEnergySpec", (PyCFunction)gadget_GetAllEnergySpec, METH_VARARGS, "get the specific energy for each particle"}, {"GetAllEntropy", (PyCFunction)gadget_GetAllEntropy, METH_VARARGS, "get the entropy for each particle"}, {"GetAllID", (PyCFunction)gadget_GetAllID, METH_VARARGS, "get the ID for each particle"}, {"GetAllTypes", (PyCFunction)gadget_GetAllTypes, METH_VARARGS, "get the type for each particle"}, {"GetPos", (PyCFunction)gadget_GetPos, METH_VARARGS, "get the position for each particle (no memory overhead)"}, {"Potential", (PyCFunction)gadget_Potential, METH_VARARGS, "get the potential for a givent sample of points"}, {"Acceleration", (PyCFunction)gadget_Acceleration, METH_VARARGS, "get the acceleration for a givent sample of points"}, {"SphEvaluateOrigAll", (PyCFunction)gadget_SphEvaluateOrigAll, METH_VARARGS, "run the original sph routine."}, {"SphEvaluateAll", (PyCFunction)gadget_SphEvaluateAll, METH_VARARGS, "compute mean value of a given field based on the sph convolution for all points."}, {"SphEvaluateGradientAll", (PyCFunction)gadget_SphEvaluateGradientAll, METH_VARARGS, "compute the gradient of a given field based on the sph convolution for all points."}, - + + {"DensityEvaluateAll", (PyCFunction)gadget_DensityEvaluateAll, METH_VARARGS, + "simply run the density function"}, + + {"DensityEvaluateGradientAll", (PyCFunction)gadget_DensityEvaluateGradientAll, METH_VARARGS, + "run the modified density function and compute a gradient from the given value"}, + + + {"SphEvaluate", (PyCFunction)gadget_SphEvaluate, METH_VARARGS, "compute mean value based on the sph convolution for a given number of points"}, {"InitHsml", (PyCFunction)gadget_InitHsml, METH_VARARGS, - "Init hsml based on the three for a given number of points"}, - + "Init hsml based on the three for a given number of points"}, + {"Density", (PyCFunction)gadget_Density, METH_VARARGS, "compute Density based on the three for a given number of points"}, {"Ngbs", (PyCFunction)gadget_Ngbs, METH_VARARGS, "return the position of the neighbors for a given point"}, {"GetAllPositionsQ", (PyCFunction)gadget_GetAllPositionsQ, METH_VARARGS, "get the position for each particle Q"}, {"GetAllVelocitiesQ", (PyCFunction)gadget_GetAllVelocitiesQ, METH_VARARGS, "get the velocities for each particle Q"}, {"GetAllMassesQ", (PyCFunction)gadget_GetAllMassesQ, METH_VARARGS, "get the mass for each particle Q"}, {"GetAllIDQ", (PyCFunction)gadget_GetAllIDQ, METH_VARARGS, "get the ID for each particle Q"}, {"GetAllTypesQ", (PyCFunction)gadget_GetAllTypesQ, METH_VARARGS, "get the type for each particle Q"}, {NULL, NULL, 0, NULL} /* Sentinel */ }; void initgadget(void) { (void) Py_InitModule("gadget", gadgetMethods); import_array(); } diff --git a/src/PyGadget/src/sph.c b/src/PyGadget/src/sph.c index e5041a8..cd3bfd3 100644 --- a/src/PyGadget/src/sph.c +++ b/src/PyGadget/src/sph.c @@ -1,1444 +1,1435 @@ #include #include #include #include #include #include #include "allvars.h" #include "proto.h" /*! \file hydra.c * \brief Computation of SPH forces and rate of entropy generation * * This file contains the "second SPH loop", where the SPH forces are * computed, and where the rate of change of entropy due to the shock heating * (via artificial viscosity) is computed. */ static double hubble_a, atime, hubble_a2, fac_mu, fac_vsic_fix, a3inv, fac_egy; #ifdef PERIODIC static double boxSize, boxHalf; #ifdef LONG_X static double boxSize_X, boxHalf_X; #else #define boxSize_X boxSize #define boxHalf_X boxHalf #endif #ifdef LONG_Y static double boxSize_Y, boxHalf_Y; #else #define boxSize_Y boxSize #define boxHalf_Y boxHalf #endif #ifdef LONG_Z static double boxSize_Z, boxHalf_Z; #else #define boxSize_Z boxSize #define boxHalf_Z boxHalf #endif #endif + + /*! This is a comparison kernel for a sort routine, which is used to group * particles that are going to be exported to the same CPU. */ int sph_compare_key(const void *a, const void *b) { if(((struct sphdata_in *) a)->Task < (((struct sphdata_in *) b)->Task)) return -1; if(((struct sphdata_in *) a)->Task > (((struct sphdata_in *) b)->Task)) return +1; return 0; } + /*! This function is the driver routine for the calculation of hydrodynamical * force and rate of change of entropy due to shock heating for all active * particles . */ void sph_orig(void) { long long ntot, ntotleft; int i, j, k, n, ngrp, maxfill, source, ndone; int *nbuffer, *noffset, *nsend_local, *nsend, *numlist, *ndonelist; int level, sendTask, recvTask, nexport, place; double soundspeed_i; double tstart, tend, sumt, sumcomm; double timecomp = 0, timecommsumm = 0, timeimbalance = 0, sumimbalance; MPI_Status status; #ifdef PERIODIC boxSize = All.BoxSize; boxHalf = 0.5 * All.BoxSize; #ifdef LONG_X boxHalf_X = boxHalf * LONG_X; boxSize_X = boxSize * LONG_X; #endif #ifdef LONG_Y boxHalf_Y = boxHalf * LONG_Y; boxSize_Y = boxSize * LONG_Y; #endif #ifdef LONG_Z boxHalf_Z = boxHalf * LONG_Z; boxSize_Z = boxSize * LONG_Z; #endif #endif if(All.ComovingIntegrationOn) { /* Factors for comoving integration of hydro */ hubble_a = All.Omega0 / (All.Time * All.Time * All.Time) + (1 - All.Omega0 - All.OmegaLambda) / (All.Time * All.Time) + All.OmegaLambda; hubble_a = All.Hubble * sqrt(hubble_a); hubble_a2 = All.Time * All.Time * hubble_a; fac_mu = pow(All.Time, 3 * (GAMMA - 1) / 2) / All.Time; fac_egy = pow(All.Time, 3 * (GAMMA - 1)); fac_vsic_fix = hubble_a * pow(All.Time, 3 * GAMMA_MINUS1); a3inv = 1 / (All.Time * All.Time * All.Time); atime = All.Time; } else hubble_a = hubble_a2 = atime = fac_mu = fac_vsic_fix = a3inv = fac_egy = 1.0; /* `NumSphUpdate' gives the number of particles on this processor that want a force update */ for(n = 0, NumSphUpdate = 0; n < N_gas; n++) { if(P[n].Ti_endstep == All.Ti_Current) NumSphUpdate++; } numlist = malloc(NTask * sizeof(int) * NTask); MPI_Allgather(&NumSphUpdate, 1, MPI_INT, numlist, 1, MPI_INT, MPI_COMM_WORLD); for(i = 0, ntot = 0; i < NTask; i++) ntot += numlist[i]; free(numlist); noffset = malloc(sizeof(int) * NTask); /* offsets of bunches in common list */ nbuffer = malloc(sizeof(int) * NTask); nsend_local = malloc(sizeof(int) * NTask); nsend = malloc(sizeof(int) * NTask * NTask); ndonelist = malloc(sizeof(int) * NTask); i = 0; /* first particle for this task */ ntotleft = ntot; /* particles left for all tasks together */ while(ntotleft > 0) { for(j = 0; j < NTask; j++) nsend_local[j] = 0; /* do local particles and prepare export list */ tstart = second(); for(nexport = 0, ndone = 0; i < N_gas && nexport < All.BunchSizeHydro - NTask; i++) if(P[i].Ti_endstep == All.Ti_Current) { ndone++; for(j = 0; j < NTask; j++) Exportflag[j] = 0; sph_orig_evaluate(i, 0); for(j = 0; j < NTask; j++) { if(Exportflag[j]) { for(k = 0; k < 3; k++) { HydroDataIn[nexport].Pos[k] = P[i].Pos[k]; HydroDataIn[nexport].Vel[k] = SphP[i].VelPred[k]; } HydroDataIn[nexport].Hsml = SphP[i].Hsml; HydroDataIn[nexport].Mass = P[i].Mass; HydroDataIn[nexport].DhsmlDensityFactor = SphP[i].DhsmlDensityFactor; HydroDataIn[nexport].Density = SphP[i].Density; HydroDataIn[nexport].Pressure = SphP[i].Pressure; HydroDataIn[nexport].Timestep = P[i].Ti_endstep - P[i].Ti_begstep; /* calculation of F1 */ soundspeed_i = sqrt(GAMMA * SphP[i].Pressure / SphP[i].Density); HydroDataIn[nexport].F1 = fabs(SphP[i].DivVel) / (fabs(SphP[i].DivVel) + SphP[i].CurlVel + 0.0001 * soundspeed_i / SphP[i].Hsml / fac_mu); HydroDataIn[nexport].Index = i; HydroDataIn[nexport].Task = j; nexport++; nsend_local[j]++; } } } tend = second(); timecomp += timediff(tstart, tend); qsort(HydroDataIn, nexport, sizeof(struct hydrodata_in), sph_compare_key); for(j = 1, noffset[0] = 0; j < NTask; j++) noffset[j] = noffset[j - 1] + nsend_local[j - 1]; tstart = second(); MPI_Allgather(nsend_local, NTask, MPI_INT, nsend, NTask, MPI_INT, MPI_COMM_WORLD); tend = second(); timeimbalance += timediff(tstart, tend); /* now do the particles that need to be exported */ for(level = 1; level < (1 << PTask); level++) { tstart = second(); for(j = 0; j < NTask; j++) nbuffer[j] = 0; for(ngrp = level; ngrp < (1 << PTask); ngrp++) { maxfill = 0; for(j = 0; j < NTask; j++) { if((j ^ ngrp) < NTask) if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]) maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]; } if(maxfill >= All.BunchSizeHydro) break; sendTask = ThisTask; recvTask = ThisTask ^ ngrp; if(recvTask < NTask) { if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0) { /* get the particles */ MPI_Sendrecv(&HydroDataIn[noffset[recvTask]], nsend_local[recvTask] * sizeof(struct hydrodata_in), MPI_BYTE, recvTask, TAG_HYDRO_A, &HydroDataGet[nbuffer[ThisTask]], nsend[recvTask * NTask + ThisTask] * sizeof(struct hydrodata_in), MPI_BYTE, recvTask, TAG_HYDRO_A, MPI_COMM_WORLD, &status); } } for(j = 0; j < NTask; j++) if((j ^ ngrp) < NTask) nbuffer[j] += nsend[(j ^ ngrp) * NTask + j]; } tend = second(); timecommsumm += timediff(tstart, tend); /* now do the imported particles */ tstart = second(); for(j = 0; j < nbuffer[ThisTask]; j++) sph_orig_evaluate(j, 1); tend = second(); timecomp += timediff(tstart, tend); /* do a block to measure imbalance */ tstart = second(); MPI_Barrier(MPI_COMM_WORLD); tend = second(); timeimbalance += timediff(tstart, tend); /* get the result */ tstart = second(); for(j = 0; j < NTask; j++) nbuffer[j] = 0; for(ngrp = level; ngrp < (1 << PTask); ngrp++) { maxfill = 0; for(j = 0; j < NTask; j++) { if((j ^ ngrp) < NTask) if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]) maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]; } if(maxfill >= All.BunchSizeHydro) break; sendTask = ThisTask; recvTask = ThisTask ^ ngrp; if(recvTask < NTask) { if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0) { /* send the results */ MPI_Sendrecv(&HydroDataResult[nbuffer[ThisTask]], nsend[recvTask * NTask + ThisTask] * sizeof(struct hydrodata_out), MPI_BYTE, recvTask, TAG_HYDRO_B, &HydroDataPartialResult[noffset[recvTask]], nsend_local[recvTask] * sizeof(struct hydrodata_out), MPI_BYTE, recvTask, TAG_HYDRO_B, MPI_COMM_WORLD, &status); /* add the result to the particles */ for(j = 0; j < nsend_local[recvTask]; j++) { source = j + noffset[recvTask]; place = HydroDataIn[source].Index; for(k = 0; k < 3; k++) SphP[place].HydroAccel[k] += HydroDataPartialResult[source].Acc[k]; SphP[place].DtEntropy += HydroDataPartialResult[source].DtEntropy; if(SphP[place].MaxSignalVel < HydroDataPartialResult[source].MaxSignalVel) SphP[place].MaxSignalVel = HydroDataPartialResult[source].MaxSignalVel; } } } for(j = 0; j < NTask; j++) if((j ^ ngrp) < NTask) nbuffer[j] += nsend[(j ^ ngrp) * NTask + j]; } tend = second(); timecommsumm += timediff(tstart, tend); level = ngrp - 1; } MPI_Allgather(&ndone, 1, MPI_INT, ndonelist, 1, MPI_INT, MPI_COMM_WORLD); for(j = 0; j < NTask; j++) ntotleft -= ndonelist[j]; } free(ndonelist); free(nsend); free(nsend_local); free(nbuffer); free(noffset); /* do final operations on results */ tstart = second(); for(i = 0; i < N_gas; i++) if(P[i].Ti_endstep == All.Ti_Current) { SphP[i].DtEntropy *= GAMMA_MINUS1 / (hubble_a2 * pow(SphP[i].Density, GAMMA_MINUS1)); #ifdef SPH_BND_PARTICLES if(P[i].ID == 0) { SphP[i].DtEntropy = 0; for(k = 0; k < 3; k++) SphP[i].HydroAccel[k] = 0; } #endif } tend = second(); timecomp += timediff(tstart, tend); /* collect some timing information */ MPI_Reduce(&timecomp, &sumt, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&timecommsumm, &sumcomm, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&timeimbalance, &sumimbalance, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if(ThisTask == 0) { All.CPU_HydCompWalk += sumt / NTask; All.CPU_HydCommSumm += sumcomm / NTask; All.CPU_HydImbalance += sumimbalance / NTask; } } /*! This function is the 'core' of the SPH force computation. A target * particle is specified which may either be local, or reside in the * communication buffer. */ void sph_orig_evaluate(int target, int mode) { int j, k, n, timestep, startnode, numngb; FLOAT *pos, *vel; FLOAT mass, h_i, dhsmlDensityFactor, rho, pressure, f1, f2; double acc[3], dtEntropy, maxSignalVel; double dx, dy, dz, dvx, dvy, dvz; double h_i2, hinv, hinv4; double p_over_rho2_i, p_over_rho2_j, soundspeed_i, soundspeed_j; double hfc, dwk_i, vdotr, vdotr2, visc, mu_ij, rho_ij, vsig; double h_j, dwk_j, r, r2, u, hfc_visc; #ifndef NOVISCOSITYLIMITER double dt; #endif if(mode == 0) { pos = P[target].Pos; vel = SphP[target].VelPred; h_i = SphP[target].Hsml; mass = P[target].Mass; dhsmlDensityFactor = SphP[target].DhsmlDensityFactor; rho = SphP[target].Density; pressure = SphP[target].Pressure; timestep = P[target].Ti_endstep - P[target].Ti_begstep; soundspeed_i = sqrt(GAMMA * pressure / rho); f1 = fabs(SphP[target].DivVel) / (fabs(SphP[target].DivVel) + SphP[target].CurlVel + 0.0001 * soundspeed_i / SphP[target].Hsml / fac_mu); } else { pos = HydroDataGet[target].Pos; vel = HydroDataGet[target].Vel; h_i = HydroDataGet[target].Hsml; mass = HydroDataGet[target].Mass; dhsmlDensityFactor = HydroDataGet[target].DhsmlDensityFactor; rho = HydroDataGet[target].Density; pressure = HydroDataGet[target].Pressure; timestep = HydroDataGet[target].Timestep; soundspeed_i = sqrt(GAMMA * pressure / rho); f1 = HydroDataGet[target].F1; } /* initialize variables before SPH loop is started */ acc[0] = acc[1] = acc[2] = dtEntropy = 0; maxSignalVel = 0; p_over_rho2_i = pressure / (rho * rho) * dhsmlDensityFactor; h_i2 = h_i * h_i; /* Now start the actual SPH computation for this particle */ startnode = All.MaxPart; do { numngb = ngb_treefind_pairs(&pos[0], h_i, &startnode); for(n = 0; n < numngb; n++) { j = Ngblist[n]; dx = pos[0] - P[j].Pos[0]; dy = pos[1] - P[j].Pos[1]; dz = pos[2] - P[j].Pos[2]; #ifdef PERIODIC /* find the closest image in the given box size */ if(dx > boxHalf_X) dx -= boxSize_X; if(dx < -boxHalf_X) dx += boxSize_X; if(dy > boxHalf_Y) dy -= boxSize_Y; if(dy < -boxHalf_Y) dy += boxSize_Y; if(dz > boxHalf_Z) dz -= boxSize_Z; if(dz < -boxHalf_Z) dz += boxSize_Z; #endif r2 = dx * dx + dy * dy + dz * dz; h_j = SphP[j].Hsml; if(r2 < h_i2 || r2 < h_j * h_j) { r = sqrt(r2); if(r > 0) { p_over_rho2_j = SphP[j].Pressure / (SphP[j].Density * SphP[j].Density); soundspeed_j = sqrt(GAMMA * p_over_rho2_j * SphP[j].Density); dvx = vel[0] - SphP[j].VelPred[0]; dvy = vel[1] - SphP[j].VelPred[1]; dvz = vel[2] - SphP[j].VelPred[2]; vdotr = dx * dvx + dy * dvy + dz * dvz; if(All.ComovingIntegrationOn) vdotr2 = vdotr + hubble_a2 * r2; else vdotr2 = vdotr; if(r2 < h_i2) { hinv = 1.0 / h_i; #ifndef TWODIMS hinv4 = hinv * hinv * hinv * hinv; #else hinv4 = hinv * hinv * hinv / boxSize_Z; #endif u = r * hinv; if(u < 0.5) dwk_i = hinv4 * u * (KERNEL_COEFF_3 * u - KERNEL_COEFF_4); else dwk_i = hinv4 * KERNEL_COEFF_6 * (1.0 - u) * (1.0 - u); } else { dwk_i = 0; } if(r2 < h_j * h_j) { hinv = 1.0 / h_j; #ifndef TWODIMS hinv4 = hinv * hinv * hinv * hinv; #else hinv4 = hinv * hinv * hinv / boxSize_Z; #endif u = r * hinv; if(u < 0.5) dwk_j = hinv4 * u * (KERNEL_COEFF_3 * u - KERNEL_COEFF_4); else dwk_j = hinv4 * KERNEL_COEFF_6 * (1.0 - u) * (1.0 - u); } else { dwk_j = 0; } if(soundspeed_i + soundspeed_j > maxSignalVel) maxSignalVel = soundspeed_i + soundspeed_j; if(vdotr2 < 0) /* ... artificial viscosity */ { mu_ij = fac_mu * vdotr2 / r; /* note: this is negative! */ vsig = soundspeed_i + soundspeed_j - 3 * mu_ij; if(vsig > maxSignalVel) maxSignalVel = vsig; rho_ij = 0.5 * (rho + SphP[j].Density); f2 = fabs(SphP[j].DivVel) / (fabs(SphP[j].DivVel) + SphP[j].CurlVel + 0.0001 * soundspeed_j / fac_mu / SphP[j].Hsml); visc = 0.25 * All.ArtBulkViscConst * vsig * (-mu_ij) / rho_ij * (f1 + f2); /* .... end artificial viscosity evaluation */ #ifndef NOVISCOSITYLIMITER /* make sure that viscous acceleration is not too large */ dt = imax(timestep, (P[j].Ti_endstep - P[j].Ti_begstep)) * All.Timebase_interval; if(dt > 0 && (dwk_i + dwk_j) < 0) { visc = dmin(visc, 0.5 * fac_vsic_fix * vdotr2 / (0.5 * (mass + P[j].Mass) * (dwk_i + dwk_j) * r * dt)); } #endif } else visc = 0; p_over_rho2_j *= SphP[j].DhsmlDensityFactor; hfc_visc = 0.5 * P[j].Mass * visc * (dwk_i + dwk_j) / r; hfc = hfc_visc + P[j].Mass * (p_over_rho2_i * dwk_i + p_over_rho2_j * dwk_j) / r; acc[0] -= hfc * dx; acc[1] -= hfc * dy; acc[2] -= hfc * dz; dtEntropy += 0.5 * hfc_visc * vdotr2; } } } } while(startnode >= 0); /* Now collect the result at the right place */ if(mode == 0) { for(k = 0; k < 3; k++) SphP[target].HydroAccel[k] = acc[k]; SphP[target].DtEntropy = dtEntropy; SphP[target].MaxSignalVel = maxSignalVel; } else { for(k = 0; k < 3; k++) HydroDataResult[target].Acc[k] = acc[k]; HydroDataResult[target].DtEntropy = dtEntropy; HydroDataResult[target].MaxSignalVel = maxSignalVel; } } /*! This function is the driver routine for the calculation of hydrodynamical * force and rate of change of entropy due to shock heating for all active * particles . */ void sph(void) { long long ntot, ntotleft; int i, j, k, n, ngrp, maxfill, source, ndone; int *nbuffer, *noffset, *nsend_local, *nsend, *numlist, *ndonelist; int level, sendTask, recvTask, nexport, place; double soundspeed_i; double tstart, tend, sumt, sumcomm; double timecomp = 0, timecommsumm = 0, timeimbalance = 0, sumimbalance; MPI_Status status; #ifdef PERIODIC boxSize = All.BoxSize; boxHalf = 0.5 * All.BoxSize; #ifdef LONG_X boxHalf_X = boxHalf * LONG_X; boxSize_X = boxSize * LONG_X; #endif #ifdef LONG_Y boxHalf_Y = boxHalf * LONG_Y; boxSize_Y = boxSize * LONG_Y; #endif #ifdef LONG_Z boxHalf_Z = boxHalf * LONG_Z; boxSize_Z = boxSize * LONG_Z; #endif #endif if(All.ComovingIntegrationOn) { /* Factors for comoving integration of hydro */ hubble_a = All.Omega0 / (All.Time * All.Time * All.Time) + (1 - All.Omega0 - All.OmegaLambda) / (All.Time * All.Time) + All.OmegaLambda; hubble_a = All.Hubble * sqrt(hubble_a); hubble_a2 = All.Time * All.Time * hubble_a; fac_mu = pow(All.Time, 3 * (GAMMA - 1) / 2) / All.Time; fac_egy = pow(All.Time, 3 * (GAMMA - 1)); fac_vsic_fix = hubble_a * pow(All.Time, 3 * GAMMA_MINUS1); a3inv = 1 / (All.Time * All.Time * All.Time); atime = All.Time; } else hubble_a = hubble_a2 = atime = fac_mu = fac_vsic_fix = a3inv = fac_egy = 1.0; /* `NumSphUpdate' gives the number of particles on this processor that want a force update */ for(n = 0, NumSphUpdate = 0; n < N_gas; n++) { SphP[n].ObsMoment0=0; SphP[n].ObsMoment1=0; SphP[n].GradObservable[0]=0; SphP[n].GradObservable[1]=0; SphP[n].GradObservable[2]=0; P[n].Ti_endstep = All.Ti_Current; if(P[n].Ti_endstep == All.Ti_Current) NumSphUpdate++; } numlist = malloc(NTask * sizeof(int) * NTask); MPI_Allgather(&NumSphUpdate, 1, MPI_INT, numlist, 1, MPI_INT, MPI_COMM_WORLD); for(i = 0, ntot = 0; i < NTask; i++) ntot += numlist[i]; free(numlist); noffset = malloc(sizeof(int) * NTask); /* offsets of bunches in common list */ nbuffer = malloc(sizeof(int) * NTask); nsend_local = malloc(sizeof(int) * NTask); nsend = malloc(sizeof(int) * NTask * NTask); ndonelist = malloc(sizeof(int) * NTask); i = 0; /* first particle for this task */ ntotleft = ntot; /* particles left for all tasks together */ while(ntotleft > 0) { for(j = 0; j < NTask; j++) nsend_local[j] = 0; /* do local particles and prepare export list */ //tstart = second(); for(nexport = 0, ndone = 0; i < N_gas && nexport < All.BunchSizeSph - NTask; i++) if(P[i].Ti_endstep == All.Ti_Current) { ndone++; for(j = 0; j < NTask; j++) Exportflag[j] = 0; sph_evaluate(i, 0); for(j = 0; j < NTask; j++) { if(Exportflag[j]) { for(k = 0; k < 3; k++) { SphDataIn[nexport].Pos[k] = P[i].Pos[k]; SphDataIn[nexport].Vel[k] = SphP[i].VelPred[k]; } SphDataIn[nexport].Hsml = SphP[i].Hsml; //SphDataIn[nexport].Mass = P[i].Mass; //SphDataIn[nexport].DhsmlDensityFactor = SphP[i].DhsmlDensityFactor; //SphDataIn[nexport].Density = SphP[i].Density; //SphDataIn[nexport].Pressure = SphP[i].Pressure; //SphDataIn[nexport].Timestep = P[i].Ti_endstep - P[i].Ti_begstep; //SphDataIn[nexport].ObsMoment0 = 0; //SphDataIn[nexport].ObsMoment1 = 0; SphDataIn[nexport].Observable = SphP[i].Observable; SphDataIn[nexport].Index = i; SphDataIn[nexport].Task = j; nexport++; nsend_local[j]++; } } } //tend = second(); //timecomp += timediff(tstart, tend); qsort(SphDataIn, nexport, sizeof(struct sphdata_in), sph_compare_key); for(j = 1, noffset[0] = 0; j < NTask; j++) noffset[j] = noffset[j - 1] + nsend_local[j - 1]; //tstart = second(); MPI_Allgather(nsend_local, NTask, MPI_INT, nsend, NTask, MPI_INT, MPI_COMM_WORLD); //tend = second(); //timeimbalance += timediff(tstart, tend); /* now do the particles that need to be exported */ for(level = 1; level < (1 << PTask); level++) { //tstart = second(); for(j = 0; j < NTask; j++) nbuffer[j] = 0; for(ngrp = level; ngrp < (1 << PTask); ngrp++) { maxfill = 0; for(j = 0; j < NTask; j++) { if((j ^ ngrp) < NTask) if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]) maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]; } if(maxfill >= All.BunchSizeSph) break; sendTask = ThisTask; recvTask = ThisTask ^ ngrp; if(recvTask < NTask) { if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0) { /* get the particles */ MPI_Sendrecv(&SphDataIn[noffset[recvTask]], nsend_local[recvTask] * sizeof(struct sphdata_in), MPI_BYTE, recvTask, TAG_HYDRO_A, &SphDataGet[nbuffer[ThisTask]], nsend[recvTask * NTask + ThisTask] * sizeof(struct sphdata_in), MPI_BYTE, recvTask, TAG_HYDRO_A, MPI_COMM_WORLD, &status); } } for(j = 0; j < NTask; j++) if((j ^ ngrp) < NTask) nbuffer[j] += nsend[(j ^ ngrp) * NTask + j]; } tend = second(); timecommsumm += timediff(tstart, tend); /* now do the imported particles */ tstart = second(); for(j = 0; j < nbuffer[ThisTask]; j++) sph_evaluate(j, 1); tend = second(); timecomp += timediff(tstart, tend); /* do a block to measure imbalance */ tstart = second(); MPI_Barrier(MPI_COMM_WORLD); tend = second(); timeimbalance += timediff(tstart, tend); /* get the result */ tstart = second(); for(j = 0; j < NTask; j++) nbuffer[j] = 0; for(ngrp = level; ngrp < (1 << PTask); ngrp++) { maxfill = 0; for(j = 0; j < NTask; j++) { if((j ^ ngrp) < NTask) if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]) maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]; } if(maxfill >= All.BunchSizeSph) break; sendTask = ThisTask; recvTask = ThisTask ^ ngrp; if(recvTask < NTask) { if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0) { /* send the results */ MPI_Sendrecv(&SphDataResult[nbuffer[ThisTask]], nsend[recvTask * NTask + ThisTask] * sizeof(struct sphdata_out), MPI_BYTE, recvTask, TAG_HYDRO_B, &SphDataPartialResult[noffset[recvTask]], nsend_local[recvTask] * sizeof(struct sphdata_out), MPI_BYTE, recvTask, TAG_HYDRO_B, MPI_COMM_WORLD, &status); /* add the result to the particles */ for(j = 0; j < nsend_local[recvTask]; j++) { source = j + noffset[recvTask]; place = SphDataIn[source].Index; SphP[place].ObsMoment0 += SphDataPartialResult[source].ObsMoment0; SphP[place].ObsMoment1 += SphDataPartialResult[source].ObsMoment1; SphP[place].GradObservable[0] += SphDataPartialResult[source].GradObservable[0]; SphP[place].GradObservable[1] += SphDataPartialResult[source].GradObservable[1]; SphP[place].GradObservable[2] += SphDataPartialResult[source].GradObservable[2]; } } } for(j = 0; j < NTask; j++) if((j ^ ngrp) < NTask) nbuffer[j] += nsend[(j ^ ngrp) * NTask + j]; } tend = second(); timecommsumm += timediff(tstart, tend); level = ngrp - 1; } MPI_Allgather(&ndone, 1, MPI_INT, ndonelist, 1, MPI_INT, MPI_COMM_WORLD); for(j = 0; j < NTask; j++) ntotleft -= ndonelist[j]; } free(ndonelist); free(nsend); free(nsend_local); free(nbuffer); free(noffset); /* do final operations on results */ //tstart = second(); for(i = 0; i < N_gas; i++) if(P[i].Ti_endstep == All.Ti_Current) { //SphP[i].Observable = SphP[i].ObsMoment1/SphP[i].ObsMoment0; SphP[i].Observable = SphP[i].ObsMoment1; } } /*! This function is the 'core' of the SPH force computation. A target * particle is specified which may either be local, or reside in the * communication buffer. */ void sph_evaluate(int target, int mode) { int j, n, startnode, numngb, numngb_inbox; double h, h2, fac, hinv, hinv3, hinv4; double rho, divv, wk, dwk; double dx, dy, dz, r, r2, u, mass_j; double dvx, dvy, dvz, rotv[3]; double weighted_numngb, dhsmlrho; FLOAT *pos, *vel; FLOAT mom1,mom0,gradx,grady,gradz,observable; #ifndef NOVISCOSITYLIMITER double dt; #endif if(mode == 0) { pos = P[target].Pos; vel = SphP[target].VelPred; h = SphP[target].Hsml; //mom0 = SphP[target].ObsMoment0; //mom1 = SphP[target].ObsMoment1; observable = SphP[target].Observable; } else { pos = SphDataGet[target].Pos; vel = SphDataGet[target].Vel; h = SphDataGet[target].Hsml; //mom0 = SphDataGet[target].ObsMoment0; //mom1 = SphDataGet[target].ObsMoment1; observable = SphDataGet[target].Observable; } mom0 = mom1 = gradx = grady = gradz = 0; h2 = h * h; hinv = 1.0 / h; #ifndef TWODIMS hinv3 = hinv * hinv * hinv; #else hinv3 = hinv * hinv / boxSize_Z; #endif hinv4 = hinv3 * hinv; rho = divv = rotv[0] = rotv[1] = rotv[2] = 0; weighted_numngb = 0; dhsmlrho = 0; startnode = All.MaxPart; numngb = 0; do { numngb_inbox = ngb_treefind_variable(&pos[0], h, &startnode); for(n = 0; n < numngb_inbox; n++) { j = Ngblist[n]; dx = pos[0] - P[j].Pos[0]; dy = pos[1] - P[j].Pos[1]; dz = pos[2] - P[j].Pos[2]; #ifdef PERIODIC /* now find the closest image in the given box size */ if(dx > boxHalf_X) dx -= boxSize_X; if(dx < -boxHalf_X) dx += boxSize_X; if(dy > boxHalf_Y) dy -= boxSize_Y; if(dy < -boxHalf_Y) dy += boxSize_Y; if(dz > boxHalf_Z) dz -= boxSize_Z; if(dz < -boxHalf_Z) dz += boxSize_Z; #endif r2 = dx * dx + dy * dy + dz * dz; if(r2 < h2) { numngb++; r = sqrt(r2); u = r * hinv; if(u < 0.5) { wk = hinv3 * (KERNEL_COEFF_1 + KERNEL_COEFF_2 * (u - 1) * u * u); dwk = hinv4 * u * (KERNEL_COEFF_3 * u - KERNEL_COEFF_4); } else { wk = hinv3 * KERNEL_COEFF_5 * (1.0 - u) * (1.0 - u) * (1.0 - u); dwk = hinv4 * KERNEL_COEFF_6 * (1.0 - u) * (1.0 - u); } mass_j = P[j].Mass; mom1 += mass_j*(SphP[j].Observable)/(SphP[j].Density) * wk; mom0 += mass_j /(SphP[j].Density) * wk; if (r==0) fac = 0; else fac = mass_j * (observable-SphP[j].Observable) /(SphP[j].Density) * dwk / r; gradx += fac * dx; grady += fac * dy; gradz += fac * dz; } } } while(startnode >= 0); /* Now collect the result at the right place */ if(mode == 0) { SphP[target].ObsMoment0 = mom0; SphP[target].ObsMoment1 = mom1; SphP[target].GradObservable[0] = gradx; SphP[target].GradObservable[1] = grady; SphP[target].GradObservable[2] = gradz; } else { SphDataResult[target].ObsMoment0 = mom0; SphDataResult[target].ObsMoment1 = mom1; SphDataResult[target].GradObservable[0] = gradx; SphDataResult[target].GradObservable[1] = grady; SphDataResult[target].GradObservable[2] = gradz; } } /*! This function is the driver routine for the calculation of hydrodynamical * force and rate of change of entropy due to shock heating for all active * particles . */ void sph_sub(void) { long long ntot, ntotleft; int i, j, k, n, ngrp, maxfill, source, ndone; int *nbuffer, *noffset, *nsend_local, *nsend, *numlist, *ndonelist; int level, sendTask, recvTask, nexport, place; double soundspeed_i; double tstart, tend, sumt, sumcomm; double timecomp = 0, timecommsumm = 0, timeimbalance = 0, sumimbalance; MPI_Status status; #ifdef PERIODIC boxSize = All.BoxSize; boxHalf = 0.5 * All.BoxSize; #ifdef LONG_X boxHalf_X = boxHalf * LONG_X; boxSize_X = boxSize * LONG_X; #endif #ifdef LONG_Y boxHalf_Y = boxHalf * LONG_Y; boxSize_Y = boxSize * LONG_Y; #endif #ifdef LONG_Z boxHalf_Z = boxHalf * LONG_Z; boxSize_Z = boxSize * LONG_Z; #endif #endif if(All.ComovingIntegrationOn) { /* Factors for comoving integration of hydro */ hubble_a = All.Omega0 / (All.Time * All.Time * All.Time) + (1 - All.Omega0 - All.OmegaLambda) / (All.Time * All.Time) + All.OmegaLambda; hubble_a = All.Hubble * sqrt(hubble_a); hubble_a2 = All.Time * All.Time * hubble_a; fac_mu = pow(All.Time, 3 * (GAMMA - 1) / 2) / All.Time; fac_egy = pow(All.Time, 3 * (GAMMA - 1)); fac_vsic_fix = hubble_a * pow(All.Time, 3 * GAMMA_MINUS1); a3inv = 1 / (All.Time * All.Time * All.Time); atime = All.Time; } else hubble_a = hubble_a2 = atime = fac_mu = fac_vsic_fix = a3inv = fac_egy = 1.0; /* `NumSphUpdate' gives the number of particles on this processor that want a force update */ for(n = 0, NumSphUpdate = 0; n < N_gasQ; n++) { SphQ[n].ObsMoment0=0; SphQ[n].ObsMoment1=0; Q[n].Ti_endstep = All.Ti_Current; if(Q[n].Ti_endstep == All.Ti_Current) NumSphUpdate++; } numlist = malloc(NTask * sizeof(int) * NTask); MPI_Allgather(&NumSphUpdate, 1, MPI_INT, numlist, 1, MPI_INT, MPI_COMM_WORLD); for(i = 0, ntot = 0; i < NTask; i++) ntot += numlist[i]; free(numlist); noffset = malloc(sizeof(int) * NTask); /* offsets of bunches in common list */ nbuffer = malloc(sizeof(int) * NTask); nsend_local = malloc(sizeof(int) * NTask); nsend = malloc(sizeof(int) * NTask * NTask); ndonelist = malloc(sizeof(int) * NTask); i = 0; /* first particle for this task */ ntotleft = ntot; /* particles left for all tasks together */ while(ntotleft > 0) { for(j = 0; j < NTask; j++) nsend_local[j] = 0; /* do local particles and prepare export list */ //tstart = second(); for(nexport = 0, ndone = 0; i < N_gasQ && nexport < All.BunchSizeSph - NTask; i++) if(Q[i].Ti_endstep == All.Ti_Current) { ndone++; for(j = 0; j < NTask; j++) Exportflag[j] = 0; sph_evaluate_sub(i, 0); for(j = 0; j < NTask; j++) { if(Exportflag[j]) { for(k = 0; k < 3; k++) { SphDataIn[nexport].Pos[k] = Q[i].Pos[k]; SphDataIn[nexport].Vel[k] = SphQ[i].VelPred[k]; } SphDataIn[nexport].Hsml = SphQ[i].Hsml; //SphDataIn[nexport].Mass = Q[i].Mass; //SphDataIn[nexport].DhsmlDensityFactor = SphQ[i].DhsmlDensityFactor; //SphDataIn[nexport].Density = SphQ[i].Density; //SphDataIn[nexport].Pressure = SphQ[i].Pressure; //SphDataIn[nexport].Timestep = Q[i].Ti_endstep - Q[i].Ti_begstep; SphDataIn[nexport].ObsMoment0 = Q[i].Mass; SphDataIn[nexport].ObsMoment1 = Q[i].Mass; SphDataIn[nexport].Index = i; SphDataIn[nexport].Task = j; nexport++; nsend_local[j]++; } } } //tend = second(); //timecomp += timediff(tstart, tend); qsort(SphDataIn, nexport, sizeof(struct sphdata_in), sph_compare_key); for(j = 1, noffset[0] = 0; j < NTask; j++) noffset[j] = noffset[j - 1] + nsend_local[j - 1]; //tstart = second(); MPI_Allgather(nsend_local, NTask, MPI_INT, nsend, NTask, MPI_INT, MPI_COMM_WORLD); //tend = second(); //timeimbalance += timediff(tstart, tend); /* now do the particles that need to be exported */ for(level = 1; level < (1 << PTask); level++) { //tstart = second(); for(j = 0; j < NTask; j++) nbuffer[j] = 0; for(ngrp = level; ngrp < (1 << PTask); ngrp++) { maxfill = 0; for(j = 0; j < NTask; j++) { if((j ^ ngrp) < NTask) if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]) maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]; } if(maxfill >= All.BunchSizeSph) break; sendTask = ThisTask; recvTask = ThisTask ^ ngrp; if(recvTask < NTask) { if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0) { /* get the particles */ MPI_Sendrecv(&SphDataIn[noffset[recvTask]], nsend_local[recvTask] * sizeof(struct sphdata_in), MPI_BYTE, recvTask, TAG_HYDRO_A, &SphDataGet[nbuffer[ThisTask]], nsend[recvTask * NTask + ThisTask] * sizeof(struct sphdata_in), MPI_BYTE, recvTask, TAG_HYDRO_A, MPI_COMM_WORLD, &status); } } for(j = 0; j < NTask; j++) if((j ^ ngrp) < NTask) nbuffer[j] += nsend[(j ^ ngrp) * NTask + j]; } tend = second(); timecommsumm += timediff(tstart, tend); /* now do the imported particles */ tstart = second(); for(j = 0; j < nbuffer[ThisTask]; j++) sph_evaluate_sub(j, 1); tend = second(); timecomp += timediff(tstart, tend); /* do a block to measure imbalance */ tstart = second(); MPI_Barrier(MPI_COMM_WORLD); tend = second(); timeimbalance += timediff(tstart, tend); /* get the result */ tstart = second(); for(j = 0; j < NTask; j++) nbuffer[j] = 0; for(ngrp = level; ngrp < (1 << PTask); ngrp++) { maxfill = 0; for(j = 0; j < NTask; j++) { if((j ^ ngrp) < NTask) if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]) maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j]; } if(maxfill >= All.BunchSizeSph) break; sendTask = ThisTask; recvTask = ThisTask ^ ngrp; if(recvTask < NTask) { if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0) { /* send the results */ MPI_Sendrecv(&SphDataResult[nbuffer[ThisTask]], nsend[recvTask * NTask + ThisTask] * sizeof(struct sphdata_out), MPI_BYTE, recvTask, TAG_HYDRO_B, &SphDataPartialResult[noffset[recvTask]], nsend_local[recvTask] * sizeof(struct sphdata_out), MPI_BYTE, recvTask, TAG_HYDRO_B, MPI_COMM_WORLD, &status); /* add the result to the particles */ for(j = 0; j < nsend_local[recvTask]; j++) { source = j + noffset[recvTask]; place = SphDataIn[source].Index; SphQ[place].ObsMoment0 += SphDataPartialResult[source].ObsMoment0; SphQ[place].ObsMoment1 += SphDataPartialResult[source].ObsMoment1; } } } for(j = 0; j < NTask; j++) if((j ^ ngrp) < NTask) nbuffer[j] += nsend[(j ^ ngrp) * NTask + j]; } tend = second(); timecommsumm += timediff(tstart, tend); level = ngrp - 1; } MPI_Allgather(&ndone, 1, MPI_INT, ndonelist, 1, MPI_INT, MPI_COMM_WORLD); for(j = 0; j < NTask; j++) ntotleft -= ndonelist[j]; } free(ndonelist); free(nsend); free(nsend_local); free(nbuffer); free(noffset); /* do final operations on results */ //tstart = second(); for(i = 0; i < N_gasQ; i++) if(Q[i].Ti_endstep == All.Ti_Current) { SphQ[i].Observable = SphQ[i].ObsMoment1/SphQ[i].ObsMoment0; } } /*! This function is the 'core' of the SPH force computation. A target * particle is specified which may either be local, or reside in the * communication buffer. */ void sph_evaluate_sub(int target, int mode) { int j, n, startnode, numngb, numngb_inbox; double h, h2, fac, hinv, hinv3, hinv4; double rho, divv, wk, dwk; double dx, dy, dz, r, r2, u, mass_j; double dvx, dvy, dvz, rotv[3]; double weighted_numngb, dhsmlrho; FLOAT *pos, *vel; FLOAT mom1,mom0; #ifndef NOVISCOSITYLIMITER double dt; #endif if(mode == 0) { pos = Q[target].Pos; vel = SphQ[target].VelPred; h = SphQ[target].Hsml; mom0 = SphQ[target].ObsMoment0; mom1 = SphQ[target].ObsMoment1; } else { pos = SphDataGet[target].Pos; vel = SphDataGet[target].Vel; h = SphDataGet[target].Hsml; mom0 = SphDataGet[target].ObsMoment0; mom1 = SphDataGet[target].ObsMoment1; } h2 = h * h; hinv = 1.0 / h; #ifndef TWODIMS hinv3 = hinv * hinv * hinv; #else hinv3 = hinv * hinv / boxSize_Z; #endif hinv4 = hinv3 * hinv; rho = divv = rotv[0] = rotv[1] = rotv[2] = 0; weighted_numngb = 0; dhsmlrho = 0; startnode = All.MaxPart; numngb = 0; do { numngb_inbox = ngb_treefind_variable(&pos[0], h, &startnode); for(n = 0; n < numngb_inbox; n++) { j = Ngblist[n]; dx = pos[0] - P[j].Pos[0]; dy = pos[1] - P[j].Pos[1]; dz = pos[2] - P[j].Pos[2]; #ifdef PERIODIC /* now find the closest image in the given box size */ if(dx > boxHalf_X) dx -= boxSize_X; if(dx < -boxHalf_X) dx += boxSize_X; if(dy > boxHalf_Y) dy -= boxSize_Y; if(dy < -boxHalf_Y) dy += boxSize_Y; if(dz > boxHalf_Z) dz -= boxSize_Z; if(dz < -boxHalf_Z) dz += boxSize_Z; #endif r2 = dx * dx + dy * dy + dz * dz; if(r2 < h2) { numngb++; r = sqrt(r2); u = r * hinv; if(u < 0.5) { wk = hinv3 * (KERNEL_COEFF_1 + KERNEL_COEFF_2 * (u - 1) * u * u); } else { wk = hinv3 * KERNEL_COEFF_5 * (1.0 - u) * (1.0 - u) * (1.0 - u); } mom1 += P[j].Mass*(SphP[j].Observable)/(SphP[j].Density) * wk; mom0 += P[j].Mass /(SphP[j].Density) * wk; } } } while(startnode >= 0); /* Now collect the result at the right place */ if(mode == 0) { SphQ[target].ObsMoment0 = mom0; SphQ[target].ObsMoment1 = mom1; } else { SphDataResult[target].ObsMoment0 = mom0; SphDataResult[target].ObsMoment1 = mom1; } } - - - - - - - - - - - -