Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F67753375
main.py
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Mon, Jun 24, 04:06
Size
147 KB
Mime Type
text/x-objective-c
Expires
Wed, Jun 26, 04:06 (2 d)
Engine
blob
Format
Raw Data
Handle
18463674
Attached To
rPNBODY pNbody
main.py
View Options
# -*- 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
:
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
):
"""
This is the constructor for the \texttt{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 function 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
"""
#################################
# init vars
#################################
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
)
if
self
.
vel
==
None
:
self
.
vel
=
zeros
((
self
.
nbody
,
3
),
float32
)
self
.
vel
=
self
.
vel
.
astype
(
float32
)
#pass
if
self
.
mass
==
None
:
self
.
mass
=
ones
((
self
.
nbody
,
),
float32
)
/
self
.
nbody
self
.
mass
=
self
.
mass
.
astype
(
float32
)
#pass
if
self
.
tpe
==
None
:
self
.
tpe
=
zeros
(
self
.
nbody
,
int
)
self
.
tpe
=
self
.
tpe
.
astype
(
int
)
#pass
if
self
.
num
==
None
:
self
.
num
=
self
.
get_num
()
self
.
num
=
self
.
num
.
astype
(
int
)
#pass
# 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_vect
()
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
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_vect
(
self
):
#################################
'''
return specific vector 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
()
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
)
print
"UnitLength_in_cm =
%g
"
%
(
params
[
'UnitLength_in_cm'
])
print
"UnitVelocity_in_cm_per_s =
%g
"
%
(
params
[
'UnitVelocity_in_cm_per_s'
])
print
"UnitMass_in_g =
%g
"
%
(
params
[
'UnitMass_in_g'
])
#################################
#
# 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 =
%d
K"
%
(
array_size
)
else
:
array_size
=
array_size
/
1024.0
if
array_size
<
1024
:
print
"total arrays size =
%d
M"
%
(
array_size
)
else
:
array_size
=
array_size
/
1024.0
if
array_size
<
1024
:
print
"total arrays size =
%d
G"
%
(
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
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
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
()
<
r2
)))
prof
,
z
=
histogram
(
ann
.
pos
[:,
2
],
z
)
z1
=
z
[:
-
1
]
z2
=
z
[
1
:]
ds
=
z2
-
z1
ds1
=
pi
*
(
r2
**
2
-
r1
**
2
)
prof
=
prof
/
(
ds
*
ds1
)
z
=
z
[:
-
1
]
return
z
,
mpi
.
mpi_allreduce
(
prof
)
def
sigma
(
self
,
r
=
None
,
nb
=
25.
,
rm
=
50.
):
"""
Return the 3 velocity dispersion (in cylindrical coordinates) and the mean azimuthal velocity curve.
If r is not specified, it is computed with nb and rm.
The output is a list (r,sr,st,sz,mt) of 5 $n\times 1$ float arrays,
where r is the radius, sr the radial velocity dispersion, st, the azimuthal velocity dispersion,
sz, the vertical velocity dispersion and mt, the mean azimuthal velocity curve.
!!! This routine works only if particles have equal masses !!!
r : radius where to compute the values
nb : number of bins (size of the output)
rm : maximal radius
return : r,sr,st,sz,mt
"""
if
r
!=
None
:
r1
=
r
[:
-
1
]
r2
=
r
[
1
:]
r
=
(
r1
+
r2
)
/
2.0
else
:
rmax
=
rm
dr
=
rm
/
float
(
nb
)
r1
=
arange
(
0
,
rmax
,
dr
)
r2
=
arange
(
dr
,
rmax
+
dr
,
dr
)
r
=
(
r1
+
r2
)
/
2.
sr
=
[]
sz
=
[]
st
=
[]
mt
=
[]
for
i
in
range
(
len
(
r1
)):
#print i,len(r1)
ann
=
self
.
selectc
((
self
.
rxy
()
>
r1
[
i
])
*
((
self
.
rxy
()
<
r2
[
i
])))
x
=
ann
.
pos
[:,
0
]
y
=
ann
.
pos
[:,
1
]
vx
=
ann
.
vel
[:,
0
]
vy
=
ann
.
vel
[:,
1
]
vz
=
ann
.
vel
[:,
2
]
xr
=
sqrt
(
x
**
2
+
y
**
2
)
vr
=
(
x
*
vx
+
y
*
vy
)
/
xr
vt
=
(
x
*
vy
-
y
*
vx
)
/
xr
# stand dev.
if
len
(
vr
)
>
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
<
ps
[
j
+
1
])
if
sum
(
c
.
astype
(
int
))
<=
1
:
break
zm
.
append
(
compress
(
c
,
z
)
.
mean
())
else
:
ok
=
1
if
not
ok
:
#print "less than 1 particle in the sub coupe",R[i]
amp
=
zeros
(
len
(
ps
)
/
2
)
.
astype
(
float
)
m1
=
concatenate
((
m1
,
amp
))
m2
=
concatenate
((
m2
,
amp
))
continue
ps
=
ps
.
astype
(
float
)
zm
=
array
(
zm
,
float
)
t
=
t
.
astype
(
float
)
z
=
z
.
astype
(
float
)
# fourier decomposition
f
,
amp
,
phi
=
fourier
.
fourier
(
ps
,
zm
)
m1
=
concatenate
((
m1
,
amp
))
m2
=
concatenate
((
m2
,
phi
))
Rs
=
concatenate
((
Rs
,
array
([(
R
[
i
]
+
R
[
i
+
1
])
/
2.
])))
m
=
f
*
2
*
pi
m1
=
reshape
(
m1
,(
nr
,
nm
))
m2
=
reshape
(
m2
,(
nr
,
nm
))
m1
=
fliplr
(
m1
,
0
)
m2
=
fliplr
(
m2
,
0
)
return
Rs
,
m
,
m1
,
m2
def
dmodes
(
self
,
nr
=
32
,
nm
=
16
,
rm
=
32
):
"""
Compute the density 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
dm
=
[]
ok
=
0
for
j
in
range
(
len
(
ps
)
-
1
):
c
=
(
t
>=
ps
[
j
])
*
(
t
<
ps
[
j
+
1
])
if
sum
(
c
.
astype
(
int
))
<=
1
:
break
dm
.
append
(
sum
(
c
.
astype
(
int
)))
else
:
ok
=
1
if
not
ok
:
#print "less than 1 particle in the sub coupe",R[i]
amp
=
zeros
(
len
(
ps
)
/
2
)
.
astype
(
float
)
m1
=
concatenate
((
m1
,
amp
))
m2
=
concatenate
((
m2
,
amp
))
Rs
=
concatenate
((
Rs
,
array
([(
R
[
i
]
+
R
[
i
+
1
])
/
2.
])))
continue
ps
=
ps
.
astype
(
float
)
dm
=
array
(
dm
,
float
)
t
=
t
.
astype
(
float
)
z
=
z
.
astype
(
float
)
# fourier decomposition
f
,
amp
,
phi
=
fourier
.
fourier
(
ps
,
dm
)
phi
=
-
phi
m1
=
concatenate
((
m1
,
amp
))
m2
=
concatenate
((
m2
,
phi
))
Rs
=
concatenate
((
Rs
,
array
([(
R
[
i
]
+
R
[
i
+
1
])
/
2.
])))
'''
#an.rotate(pi,axis='y')
#an.show(view='xy',size=(20,20))
print (R[i]+R[i+1])/2.,phi[2]+2*pi
g = SM.plot()
g.erase()
g.limits(0,360,dm)
g.box()
g.connect(ps*180/pi,dm)
fdm = amp[2]*cos(ps*2-phi[2])
#fdm = amp[0]*cos(ps*0+phi[0])
#for j in range(1,16):
# fdm = fdm + amp[j]*cos(ps*j+phi[j])
g.ctype('red')
g.connect(ps*180/pi,fdm)
g.ctype('black')
g.show()
'''
m
=
f
*
2
*
pi
m1
=
reshape
(
m1
,(
nr
,
nm
))
m2
=
reshape
(
m2
,(
nr
,
nm
))
m1
=
fliplr
(
m1
,
0
)
m2
=
fliplr
(
m2
,
0
)
return
Rs
,
m
,
m1
,
m2
def
getRadiusInCylindricalGrid
(
self
,
z
,
Rmax
,
nr
=
32
,
nt
=
32
):
'''
Compute the radius in cells of a cylindrical grid
'''
irs
=
arange
(
nr
)
its
=
arange
(
nt
)
Radi
=
zeros
((
nt
,
nr
),
float
)
Rs
=
irs
*
Rmax
/
float
(
nr
)
ts
=
its
*
2
*
pi
/
float
(
nt
)
# use to compute values at the center of the cell
dr
=
Rs
[
1
]
-
Rs
[
0
]
dt
=
ts
[
1
]
-
ts
[
0
]
for
ir
in
irs
:
for
it
in
its
:
R
=
ir
*
Rmax
/
float
(
nr
)
+
dr
/
2.
#t = it*2*pi/float(nt) + dt/2.
Radi
[
it
,
ir
]
=
R
return
Radi
def
getAccelerationInCylindricalGrid
(
self
,
eps
,
z
,
Rmax
,
nr
=
32
,
nt
=
32
,
UseTree
=
False
):
'''
Compute the Acceleration in cells of a cylindrical grid
'''
irs
=
arange
(
nr
)
its
=
arange
(
nt
)
Accx
=
zeros
((
nt
,
nr
),
float
)
Accy
=
zeros
((
nt
,
nr
),
float
)
Accz
=
zeros
((
nt
,
nr
),
float
)
Rs
=
irs
*
Rmax
/
float
(
nr
)
ts
=
its
*
2
*
pi
/
float
(
nt
)
# use to compute values at the center of the cell
dr
=
Rs
[
1
]
-
Rs
[
0
]
dt
=
ts
[
1
]
-
ts
[
0
]
# build the tree
if
UseTree
:
if
self
.
Tree
==
None
:
self
.
Tree
=
self
.
getTree
()
for
ir
in
irs
:
for
it
in
its
:
R
=
ir
*
Rmax
/
float
(
nr
)
+
dr
/
2.
t
=
it
*
2
*
pi
/
float
(
nt
)
+
dt
/
2.
x
=
R
*
cos
(
t
)
y
=
R
*
sin
(
t
)
z
=
0.0
if
UseTree
:
a
=
self
.
TreeAccel
(
array
([[
x
,
y
,
z
]],
float32
),
eps
,
Tree
=
None
)
Accx
[
it
,
ir
]
=
a
[
0
][
0
]
Accy
[
it
,
ir
]
=
a
[
0
][
1
]
Accz
[
it
,
ir
]
=
a
[
0
][
2
]
else
:
a
=
self
.
Accel
([
x
,
y
,
z
],
eps
)
Accx
[
it
,
ir
]
=
a
[
0
]
Accy
[
it
,
ir
]
=
a
[
1
]
Accz
[
it
,
ir
]
=
a
[
2
]
return
Accx
,
Accy
,
Accz
def
getPotentialInCylindricalGrid
(
self
,
eps
,
z
,
Rmax
,
nr
=
32
,
nt
=
32
,
UseTree
=
False
):
'''
Compute the potential in cells of a cylindrical grid
'''
irs
=
arange
(
nr
)
its
=
arange
(
nt
)
Phis
=
zeros
((
nt
,
nr
),
float
)
Rs
=
irs
*
Rmax
/
float
(
nr
)
ts
=
its
*
2
*
pi
/
float
(
nt
)
# build the tree
if
UseTree
:
if
self
.
Tree
==
None
:
self
.
Tree
=
self
.
getTree
()
# use to compute values at the center of the cell
dr
=
Rs
[
1
]
-
Rs
[
0
]
dt
=
ts
[
1
]
-
ts
[
0
]
for
ir
in
irs
:
for
it
in
its
:
R
=
ir
*
Rmax
/
float
(
nr
)
+
dr
/
2.
t
=
it
*
2
*
pi
/
float
(
nt
)
+
dt
/
2.
x
=
R
*
cos
(
t
)
y
=
R
*
sin
(
t
)
z
=
0.0
if
UseTree
:
P
=
self
.
TreePot
(
array
([[
x
,
y
,
z
]],
float32
),
eps
,
Tree
=
None
)
Phis
[
it
,
ir
]
=
P
[
0
]
else
:
Phis
[
it
,
ir
]
=
self
.
Pot
([
x
,
y
,
z
],
eps
)
return
Phis
def
getSurfaceDensityInCylindricalGrid
(
self
,
Rmax
,
nr
=
32
,
nt
=
32
):
'''
Compute the surface density in cells of a cylindrical grid
'''
# r and t between 1 and 2
r
=
self
.
rxy
()
/
Rmax
t
=
(
self
.
phi_xy
()
+
pi
)
/
(
2
*
pi
)
pos
=
transpose
(
array
([
t
,
r
,
r
],
float32
))
shape
=
(
nt
,
nr
)
Sdens
=
GetMassMap
(
pos
,
self
.
mass
,
shape
)
# divide by the suface of a cell
Rs
=
arange
(
nr
+
1
)
*
Rmax
/
float
(
nr
)
R1
=
Rs
[:
-
1
]
R2
=
Rs
[
1
:]
S
=
pi
*
(
R2
**
2
-
R1
**
2
)
/
float
(
nt
)
return
Sdens
/
S
def
getNumberParticlesInCylindricalGrid
(
self
,
Rmax
,
nr
=
32
,
nt
=
32
):
'''
Compute the number of particles in cells of a cylindrical grid
'''
# r and t between 1 and 2
r
=
self
.
rxy
()
/
Rmax
t
=
(
self
.
phi_xy
()
+
pi
)
/
(
2
*
pi
)
pos
=
transpose
(
array
([
t
,
r
,
r
],
float32
))
shape
=
(
nt
,
nr
)
Num
=
GetMassMap
(
pos
,
ones
(
len
(
pos
))
.
astype
(
float32
),
shape
)
return
Num
def
getRadialVelocityDispersionInCylindricalGrid
(
self
,
Rmax
,
nr
=
32
,
nt
=
32
):
'''
Compute the radial velocity dispersion in cells of a cylindrical grid
'''
# r and t between 1 and 2
r
=
self
.
rxy
()
/
Rmax
t
=
(
self
.
phi_xy
()
+
pi
)
/
(
2
*
pi
)
pos
=
transpose
(
array
([
t
,
r
,
r
],
float32
))
shape
=
(
nt
,
nr
)
Sigmar
=
GetSigmaValMap
(
pos
,
self
.
mass
,
self
.
Vr
(),
shape
)
return
Sigmar
#################################
#
# geometrical operations
#
#################################
def
cmcenter
(
self
):
"""
Move the N-body object in order
to center the mass center at the origin.
"""
self
.
pos
=
(
self
.
pos
-
self
.
cm
())
.
astype
(
float32
)
def
cvcenter
(
self
):
"""
Center the center of velocities at the origin.
"""
self
.
vel
=
(
self
.
vel
-
self
.
cv
())
.
astype
(
float32
)
def
histocenter
(
self
,
rbox
=
50
,
nb
=
500
):
"""
Move the N-body object in order to center the higher
density region found near the mass center.
The higher density region is determined whith density histograms.
rbox : box dimension, where to compute the histograms
nb : number of bins for the histograms
"""
self
.
pos
=
(
self
.
pos
-
self
.
get_histocenter
(
rbox
=
rbox
,
nb
=
nb
))
.
astype
(
float32
)
def
histocenter2
(
self
,
rbox
=
50
,
nb
=
64
):
"""
Move the N-body object in order to center the higher
density region found near the mass center.
The higher density region is determined whith density histograms.
rbox : box dimension, where to compute the histograms
nb : number of bins for the histograms
"""
self
.
pos
=
(
self
.
pos
-
self
.
get_histocenter2
(
rbox
=
rbox
,
nb
=
nb
))
.
astype
(
float32
)
def
hdcenter
(
self
):
"""
Move the N-body object in order to center the higher
density region found.
"""
if
self
.
has_array
(
'rho'
):
idx
=
mpi
.
mpi_argmax
(
self
.
rho
)
self
.
pos
=
(
self
.
pos
-
mpi
.
mpi_getval
(
self
.
pos
,
idx
))
.
astype
(
float32
)
def
translate
(
self
,
dx
,
mode
=
'p'
):
"""
Translate the positions or the velocities of the object.
dx : shift (array vector)
mode : 'p' : translate positions
'v' : translate velocities
"""
if
mode
==
'p'
:
self
.
pos
=
(
self
.
pos
+
dx
)
.
astype
(
float32
)
elif
mode
==
'v'
:
self
.
vel
=
(
self
.
vel
+
dx
)
.
astype
(
float32
)
def
rebox
(
self
,
boxsize
=
None
,
mode
=
None
):
"""
Translate the positions of the object in order that all particles
being contained in a box of size boxsize.
boxsize : size of the box
if boxsize is not defined, we try first to see if self.boxsize
is defined.
"""
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
])
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
])
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
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
((
r
<
h
),
wk
,
0
)
return
wk
i
=
self
.
getindex
(
num
)
h
=
self
.
rsp
[
i
]
r
=
sqrt
(
(
self
.
pos
[:,
0
]
-
self
.
pos
[
i
,
0
])
**
2
+
(
self
.
pos
[:,
1
]
-
self
.
pos
[
i
,
1
])
**
2
+
(
self
.
pos
[:,
2
]
-
self
.
pos
[
i
,
2
])
**
2
)
# compute wk for these particle
wk
=
getwk
(
r
,
h
)
NORM_COEFF
=
4.188790204786
# 4/3.*pi
return
NORM_COEFF
*
sum
(
wk
)
*
h
**
3
def
real_numngb
(
self
,
num
):
'''
number of particles wor wich r<h
'''
i
=
self
.
getindex
(
num
)
h
=
self
.
rsp
[
i
]
r
=
sqrt
(
(
self
.
pos
[:,
0
]
-
self
.
pos
[
i
,
0
])
**
2
+
(
self
.
pos
[:,
1
]
-
self
.
pos
[
i
,
1
])
**
2
+
(
self
.
pos
[:,
2
]
-
self
.
pos
[
i
,
2
])
**
2
)
n
=
sum
(
where
((
r
<
h
),
1
,
0
))
return
n
def
usual_numngb
(
self
,
num
):
'''
usual way to compute the number of neighbors
'''
i
=
self
.
getindex
(
num
)
h
=
self
.
rsp
[
i
]
r
=
sqrt
(
(
self
.
pos
[:,
0
]
-
self
.
pos
[
i
,
0
])
**
2
+
(
self
.
pos
[:,
1
]
-
self
.
pos
[
i
,
1
])
**
2
+
(
self
.
pos
[:,
2
]
-
self
.
pos
[
i
,
2
])
**
2
)
c
=
(
r
<
h
)
M1
=
sum
(
c
*
self
.
mass
)
/
sum
(
c
.
astype
(
int
))
# mean mass
M2
=
4
/
3.
*
pi
*
h
**
3
*
self
.
rho
[
i
]
# total mass
n
=
M2
/
M1
return
n
#################################
#
# redistribution of particles
#
#################################
def
redistribute
(
self
):
"""
This function redistribute particles amoung all nodes in order to
have a similar number of particles per nodes
"""
if
mpi
.
NTask
>
1
:
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
):
'''
does not work well
'''
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
):
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
):
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
\n
You 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
):
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
)
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
####################################################################################################################################
#
# other classes
#
####################################################################################################################################
#if FORMATSFILE != None:
# execfile(FORMATSFILE)
#if USERFORMATSFILE != None:
# execfile(USERFORMATSFILE)
#if USERFORMATSDIR != None:
# formatsfiles = glob.glob(os.path.join(USERFORMATSDIR,'*.py'))
if
FORMATSDIR
!=
None
:
formatsfiles
=
glob
.
glob
(
os
.
path
.
join
(
FORMATSDIR
,
'*.py'
))
for
format
in
formatsfiles
:
execfile
(
format
)
####################################################################################################################################
#
# NBODY REDIRECTOR
#
####################################################################################################################################
ELTS
=
dir
()
def
get_known_formats
():
'''
return the name of known Nbody formats
'''
formats
=
[]
for
elt
in
ELTS
:
if
elt
[:
6
]
==
'Nbody_'
:
formats
.
append
(
elt
)
return
formats
def
Nbody
(
*
arg
,
**
kw
):
"""
The aim of this function is simply to return to the right class
"""
# default value
nb
=
None
# gather all known classes
formats
=
get_known_formats
()
# check ftype
if
kw
.
has_key
(
'ftype'
):
if
kw
[
'ftype'
]
!=
None
:
format
=
"Nbody_
%s
"
%
kw
[
'ftype'
]
class_name
=
eval
(
format
)
nb
=
class_name
(
*
arg
,
**
kw
)
else
:
format
=
'Nbody_default'
class_name
=
eval
(
format
)
nb
=
class_name
(
*
arg
,
**
kw
)
if
nb
==
None
:
self
.
log
.
write
(
"you should give the right format ftype"
)
sys
.
exit
()
'''
# check ftype
if kw.has_key('ftype'):
if kw['ftype'] != None:
format = "Nbody_%s"%kw['ftype']
# check if format is known
try:
formats.index(format)
except ValueError:
print "%s format is unknown"%format
format = None
class_name = eval(format)
nb = class_name(*arg,**kw)
# if format exists, try to create the object
if format != None:
class_name = eval(format)
try:
nb = class_name(*arg,**kw)
return nb
except "ReadBlockError":
pass
except "ReadAsciiError":
pass
except "ReadError":
pass
except "IOError":
print "IOError"
# except:
# pass
print "not a %s format"%(format)
# test all known Nbody class
for format in formats:
#print "try to open as %s"%format
class_name = eval(format)
try:
nb = class_name(*arg,**kw)
break
except "ReadBlockError":
pass
except "ReadAsciiError":
pass
except "ReadError":
pass
except "IOError":
print "IOError"
# except:
# pass
if nb == None:
print "unreconized format"
sys.exit()
'''
return
nb
Event Timeline
Log In to Comment