def setup_class(cls): """Setup fixtures for testing.""" agama.setUnits(mass=1, length=1, velocity=1) # FIXME! bad cls.potential = agama.Potential(type="Plummer") # set up the rest super().setup_class()
def setup_class(cls): """Setup fixtures for testing.""" super().setup_class() # now agama stuff # override super agama.setUnits(mass=1, length=1, velocity=1) # FIXME! bad cls.potential = agama.Potential(type="Plummer")
def __init__(self, opt, ai_bar=None, ai_dark=None): opt.set_options(self) agama.setUnits(mass=1, length=1, velocity=1) if ai_bar is not None: self._tevolv_ = True self.ai_bar = ai_bar self.ai_dark = ai_dark else: self._tevolv_ = False
def _gen_axisymmetric_(self, all_snaps=False): agama.setUnits(mass=1, length=1, velocity=1) if all_snaps: pot_file_list = [] for index in self.snapshot_indices: pot_file_list.append(self._pot_cache_file_(index)) else: pot_file_list = (self._pot_cache_file_(self.startnum), ) for i, file in enumerate(pot_file_list): try: self.pdark = agama.Potential(file=file + '_dark') self.pbar = agama.Potential(file=file + '_bar') self.potential = agama.Potential(self.pdark, self.pbar) except: star_position = self.snapshots[i]['star'].prop( 'host.distance.principal') gas_position = self.snapshots[i]['gas'].prop( 'host.distance.principal') dark_position = self.snapshots[i]['dark'].prop( 'host.distance.principal') star_mass = self.snapshots[i]['star']['mass'] gas_mass = self.snapshots[i]['gas']['mass'] dark_mass = self.snapshots[i]['dark']['mass'] position = np.concatenate((star_position, gas_position)) mass = np.concatenate((star_mass, gas_mass)) #TODO make these user-controllable self.pdark = agama.Potential(type="Multipole", particles=(dark_position, dark_mass), symmetry='a', gridsizeR=20, lmax=2) self.pbar = agama.Potential(type="CylSpline", particles=(position, mass), symmetry='a', gridsizer=20, gridsizez=20, mmax=0, Rmin=0.2, Rmax=50, Zmin=0.02, Zmax=10) self.pdark.export(file + '_dark') self.pbar.export(file + '_bar') self.potential = agama.Potential(self.pdark, self.pbar) self.potential.export(file) if self.axisymmetric_tevolve: self._gen_agama_interpolator_() return None
def density( self, potential: PotentialType, points: TH.PositionType, *, frame: TH.OptFrameLikeType = None, representation_type: TH.OptRepresentationLikeType = None, **kwargs ) -> T.Tuple[TH.SkyCoordType, TH.QuantityType]: """Evaluate the density. Parameters ---------- potential : `~agama.Potential` The potential. points : coord-array or |Representation| or None (optional) The points at which to evaluate the density. frame : |CoordinateFrame| or None (optional, keyword-only) The frame of the density. Potentials do not have an intrinsic reference frame, but if one is assigned, then anything needs to be converted to that frame. representation_type : |Representation| or None (optional, keyword-only) The representation type in which to return data. **kwargs Arguments into the density. Returns ------- points: :class:`~astropy.coordinates.CoordinateFrame` The points. values : :class:`~astropy.unit.Quantity` Array of the specific-potential value at the points. """ shape = points.shape[:] # copy the shape p, _ = self._convert_to_frame(points, frame, representation_type) r = p.represent_as(coord.CartesianRepresentation) r = r.reshape(-1) # unfortunately can't flatten in-place agama.setUnits(mass=1, length=1, velocity=1) # TODO! bad values = potential.density(r.xyz.T) * (u.solMass / u.pc ** 3) # reshape r.shape = shape values.shape = shape # TODO! ScalarField to package together p, _ = self._convert_to_frame(p, frame, representation_type) return p, values
def __init__(self, options_reader, grid_snapshot=None): agama.setUnits(mass=1, length=1, velocity=1) self.G = G_astropy.to_value(u.kpc**2 * u.km / (u.s * u.Myr * u.Msun)) self.theta = 0.5 self.convert_kms_Myr_to_kpc = 20000.0 * np.pi / (61478577.0) self.kpc_in_km = 3.08567758E16 # opt = options_reader(options_file) options_reader.set_options(self) self.options_reader = options_reader self.snapshot_indices = range(self.startnum - self.num_prior, self.endnum + 1) self.initial_key = self.num_prior if not os.path.isdir(self.cache_directory): os.makedirs(self.cache_directory) if self.axisymmetric: if self.axisymmetric_tevolve: self._read_snapshots_() self._gen_axisymmetric_(all_snaps=True) self.evolve_model(0 | units.Myr) else: self._read_snapshots_(first_only=True) self._gen_axisymmetric_() return None self._read_snapshots_() # find starting star self._init_starting_star_() # set up trackers self._init_starting_star_interpolators_() # set up grid if grid_snapshot is not None: self._init_grid_(grid_snapshot) else: self._init_grid_() self.evolve_model(0 | units.Myr) self.grid._grid_evolved_kdtree_ = cKDTree(self.grid.evolved_grid)
''' import sys, numpy, agama ############### parse parameters from command-line arguments or assign default values ############# arglist = [] for arg in sys.argv[1:]: nameval = arg.split('=') if len(nameval)!=2: raise ValueError('Command-line arguments should be in the form name=value') arglist.append([nameval[0].upper(), nameval[1]]) args = dict(arglist) distance = float(args.get('DISTANCE', 20626)) # [REQ] assumed distance [kpc] arcsec2kpc= distance * numpy.pi / 648000 # conversion factor (number of kiloparsecs in one arcsecond) agama.setUnits(mass=1, length=arcsec2kpc, velocity=1) # [OPT] units: mass = 1 Msun, length = 1", velocity = 1 km/s Mbh = float(args.get('MBH', 0)) # [REQ] mass of the central black hole [Msun] Omega = float(args.get('OMEGA', 0)) # [REQ] pattern speed (relevant only for non-axisymmetric models) [km/s/length_unit] halotype = args.get('HALOTYPE', 'nfw') # [OPT] halo type: 'LOG' or 'NFW' vhalo = float(args.get('VHALO', 190)) # [OPT] asymptotic (LOG) or peak (NFW) circular velocity of the halo [km/s] rhalo = float(args.get('RHALO', 150)) # [OPT] core (LOG) or scale (NFW) radius of the halo [lenth_unit] Upsilon = float(args.get('UPSILON', 1.0)) # [OPT] initial value of mass-to-light ratio in the search multstep = float(args.get('MULTSTEP', 1.02)) # [OPT] multiplicative step for increasing/decreasing Upsilon during grid search numOrbits = int (args.get('NUMORBITS', 20000)) # [OPT] number of orbit in the model (size of orbit library) intTime = float(args.get('INTTIME', 100.0)) # [OPT] integration time in units of orbital period regul = float(args.get('REGUL', 1. )) # [OPT] regularization parameter (larger => more uniform orbit weight distribution in models) incl = float(args.get('INCL', 60.0)) # [REQ] inclination angle (0 is face-on, 90 is edge-on) [degrees] beta = incl * numpy.pi/180 # same in radians alpha = float(args.get('ALPHA', 0.0)) # [REQ] azimuthal angle of viewing direction in the model coordinates (relevant only for non-axisym) degree = int (args.get('DEGREE', 2)) # [OPT] degree of B-splines (0 means histograms, 2 or 3 is preferred) symmetry = 'a' # [OPT] symmetry of the model ('s'pherical, 'a'xisymmetric, 't'riaxial)
def __init__(self, opt): opt.set_options(self) agama.setUnits(mass=1, length=1, velocity=1)
from pygaia.astrometry.coordinates import CoordinateTransformation, Transformations from pygaia.astrometry.vectorastrometry import cartesianToSpherical, astrometryToPhaseSpace,\ phaseSpaceToAstrometry from pygaia.errors.astrometric import properMotionErrorSkyAvg, properMotionMinError, properMotionMaxError, parallaxErrorSkyAvg #orbit import agama pyplot.rcParams['ps.useafm'] = True pyplot.rcParams['pdf.use14corefonts'] = True pyplot.rcParams['text.usetex'] = True # unit (just for normalization) _R0 = 8.0 # 8 kpc _V0 = 220. # 220 km/s """ #position and velocity of the Sun _xGC_sun_kpc = -8.0 #you can set to (-_R0) if you want _yGC_sun_kpc = 0. _zGC_sun_kpc = 0. _vxGC_sun = 11.1 _vyGC_sun = 232.24 #you can set to (_V0) if you want _vzGC_sun = 7.25 #MWPotential2014----- agama.setUnits( mass=1., length=_R0, velocity=_V0) p_bulge = dict(type='Spheroid', densityNorm=2.70033e9, gamma=1.8, beta=1.8, scaleRadius=8./_R0, outerCutoffRadius=1.9/_R0) p_disk = dict(type='MiyamotoNagai', mass=6.819386e10, scaleradius=3./_R0, scaleheight=0.28/_R0) p_halo = dict(type='Spheroid', densityNorm=4.34527e9, axisRatioZ=1.0, gamma=1.0, beta=3.0, scaleRadius=2.) #-------------------- # Agama potential
# THIRD PARTY import agama import astropy.coordinates as coord import astropy.units as u import numpy as np # PROJECT-SPECIFIC import discO.type_hints as TH from .type_hints import PotentialType from discO.core.wrapper import PotentialWrapper, PotentialWrapperMeta from discO.utils import resolve_representationlike, vectorfield ############################################################################## # Parameters agama.setUnits(mass=1, length=1, velocity=1) # FIXME! bad ############################################################################## # CODE ############################################################################## class AGAMAPotentialMeta(PotentialWrapperMeta): """Metaclass for wrapping :mod:`~agama` potentials.""" def total_mass(self, potential: TH.PositionType) -> TH.QuantityType: """Evaluate the total mass. Parameters ---------- potential : object
from galpy.potential import * from galpy.actionAngle import * from galpy.orbit import * import agama # this allows access to potential, action finder, orbit integration, etc. as standalone classes and routines import galpy_agama # this enables galpy-compatible interface to potentials import numpy, matplotlib, matplotlib.pyplot as plt, time matplotlib.rcParams['legend.frameon']=False ###1. set up galpy potential g_bulge = PowerSphericalPotentialwCutoff(alpha=1.8, rc=1.9/8, amp=0.03) g_disk = MiyamotoNagaiPotential(a=3./8, b=0.28/8, amp=0.755) g_halo = NFWPotential(a=2., amp=4.85) g_pot = [g_bulge,g_disk,g_halo] # same as MWPotential2014 ###2. set up equivalent potential from the Agama library agama.setUnits( mass=1., length=8., velocity=345.67) p_bulge = {"type":"SpheroidDensity", "densityNorm":6.669e9, "gamma":1.8, "beta":1.8, "scaleRadius":1, "outerCutoffRadius":1.9/8}; p_disk = {"type":"MiyamotoNagai", "mass":1.678e11, "scaleradius":3./8, "scaleheight":0.28/8}; p_halo = {"type":"SpheroidDensity", "densityNorm":1.072e10, "gamma":1.0, "beta":3.0, "scaleRadius":2.}; ### one can create the genuine instance of Agama potential as follows: #c_pot = agama.Potential("../data/MWPotential2014.ini") # read parameters from ini file #c_pot = agama.Potential(p_bulge, p_disk, p_halo) # or create potential from a list of parameters ### or one can instead create a galpy-compatible potential as follows: w_pot = galpy_agama.CPotential(p_bulge, p_disk, p_halo) # same as above, two variants ### ...and then use _pot member variable to access the instance of raw Agama potential dt = time.time() c_actfinder = agama.ActionFinder(w_pot._pot) print 'Time to set up action finder: %s s' % (time.time()-dt) ### this needs to be done once for the given potential,
ini.read(iniFileName) iniPotenThinDisc = dict(ini.items("Potential thin disc")) iniPotenThickDisc= dict(ini.items("Potential thick disc")) iniPotenGasDisc = dict(ini.items("Potential gas disc")) iniPotenBulge = dict(ini.items("Potential bulge")) iniPotenDarkHalo = dict(ini.items("Potential dark halo")) iniDFThinDisc = dict(ini.items("DF thin disc")) iniDFThickDisc = dict(ini.items("DF thick disc")) iniDFStellarHalo = dict(ini.items("DF stellar halo")) iniDFDarkHalo = dict(ini.items("DF dark halo")) iniSCMHalo = dict(ini.items("SelfConsistentModel halo")) iniSCMDisc = dict(ini.items("SelfConsistentModel disc")) iniSCM = dict(ini.items("SelfConsistentModel")) # define external unit system describing the data (including the parameters in INI file) agama.setUnits(length=1, velocity=1, mass=1) # in Kpc, km/s, Msun # initialize the SelfConsistentModel object (only the potential expansion parameters) model = agama.SelfConsistentModel(**iniSCM) # create initial ('guessed') density profiles of all components densityBulge = agama.Density(**iniPotenBulge) densityDarkHalo = agama.Density(**iniPotenDarkHalo) densityThinDisc = agama.Density(**iniPotenThinDisc) densityThickDisc = agama.Density(**iniPotenThickDisc) densityGasDisc = agama.Density(**iniPotenGasDisc) densityStellarDisc = agama.Density(densityThinDisc, densityThickDisc) # composite # add components to SCM - at first, all of them are static density profiles model.components.append(agama.Component(dens=densityDarkHalo, disklike=False)) model.components.append(agama.Component(dens=densityStellarDisc, disklike=True))
if i == 0: ax.legend(loc='lower center', frameon=False) ax.text(1.0, 1.05, 'projected velocity distributions in fields', ha='right', va='center', transform=ax.transAxes) ax.set_ylabel('f(V)', labelpad=3) plt.savefig('nsd_model.pdf') plt.show() if __name__ == '__main__': agama.setUnits(length=1, velocity=1, mass=1e10) # 1 kpc, 1 km/s, 1e10 Msun # initialize the SelfConsistentModel object (only the potential expansion parameters) model = agama.SelfConsistentModel(RminCyl=0.005, RmaxCyl=1.0, sizeRadialCyl=25, zminCyl=0.005, zmaxCyl=1.0, sizeVerticalCyl=25) # construct a two component model: NSD + NSC # NSD -> generated self-consistently # NSC -> kept fixed as an external potential # NSC best-fitting model from Chatzopoulos et al. 2015 (see Equation 28 here: https://arxiv.org/pdf/2007.06577.pdf) density_NSC_init = agama.Density(type='Dehnen',
#!/usr/bin/python """ This example illustrates various usage scenarios for Target objects, used in Schwarzschild models. """ import agama, numpy numpy.set_printoptions(linewidth=100, formatter={'all': lambda x: '%11.6f' % x}) # set up some random physical units, to test the correction of unit conversion agama.setUnits(length=3.4567, mass=12345, velocity=6.7) # physical constituents of a model: potential and a corresponding isotropic DF pot = agama.Potential(type='Plummer', scaleRadius=0.1, mass=100) dens = pot df = agama.DistributionFunction(type='QuasiSpherical', potential=pot) gm = agama.GalaxyModel(pot, df) vesc = (-2 * pot.potential(0, 0, 0))**0.5 # escape velocity from r=0 #print("escape velocity: %g" % vesc) # define a grid in radius and a series of concentric rings for recording LOSVD gridr = agama.nonuniformGrid(10, 0.025, 1) # grid in radius (denser at small radii) gridx = agama.symmetricGrid( 19, 0.025, 1) # grids in x,y - symmetrically mirror about origin gridv = numpy.linspace(-vesc, vesc, 25) # grid in velocity space ang = numpy.linspace( 0, 2 * numpy.pi, 73) # approximate a circle with a polygon with this many vertices sa = numpy.sin(ang) sa[36] = sa[72] = 0
zmin=0.25 * scaleHeight, zmax=10.0 * scaleHeight, mmax=3 * numberOfArms, # each arm is represented by three harmonic terms: m, m*2, m*3 symmetry='bisymmetric' if numberOfArms % 2 == 0 else 4, # 4 means the z-reflection symmetry gridSizeZ=20, # rule of thumb for the radial grid spacing is to resolve # the change in the pitch angle of the spiral with one grid cell gridSizeR=max( 25, numpy.log(10.0 / 0.01) / numpy.tan(pitchAngle) * numberOfArms)) agama.setUnits( length=1, velocity=1, mass=1) # some arbitrary units - everything is scale-invariant here numberOfArms = 3 pitchAngle = numpy.pi / 8 scaleRadius = 1.0 scaleHeight = 0.1 surfaceDensityD = 1.0 # density of the underlying axisymmetric disk surfaceDensityS = surfaceDensityD * 0.5 # amplitude of the density variation of the spiral pattern phi0 = numpy.pi / 10 # [arbitrary] phase angle at R=scaleRadius pot_spiral = createSpiralPotential(numberOfArms, surfaceDensityS, scaleRadius, scaleHeight, pitchAngle, phi0) pot_disk = agama.Potential( type='disk', surfaceDensity=surfaceDensityD, scaleRadius=scaleRadius, scaleHeight=-scaleHeight
pos[:, 0] += numpy.random.normal( scale=0.1, size=nboot * nclust) # uncertainty in solar distance from Galactic center vel[:, 0] += numpy.random.normal(scale=1.0, size=nboot * nclust) # uncertainty in solar velocity vel[:, 1] += numpy.random.normal(scale=3.0, size=nboot * nclust) vel[:, 2] += numpy.random.normal(scale=1.0, size=nboot * nclust) pos[:, 0] *= -1 # revert back to normal orientation of coordinate system (solar position at x=+8.2) vel[:, 0] *= -1 # same for velocity posvel = numpy.column_stack((pos, vel)) numpy.savetxt('posvel.txt', posvel, fmt='%.6g') # STEP 2: compute the orbits, min/max galactocentric radii, and actions, for all Monte Carlo samples import agama print(agama.setUnits(length=1, velocity=1, mass=1)) # units: kpc, km/s, Msun; time unit ~ 1 Gyr potential = agama.Potential( 'McMillan17.ini') # MW potential from McMillan(2017) # compute orbits for each realization of initial conditions, # integrated for 100 dynamical times or 20 Gyr (whichever is lower) print("Computing orbits for %d realizations of cluster initial conditions" % len(posvel)) inttime = numpy.minimum(20., potential.Tcirc(posvel) * 100) orbits = agama.orbit(ic=posvel, potential=potential, time=inttime, trajsize=1000)[:, 1] rmin = numpy.zeros(len(orbits)) rmax = numpy.zeros(len(orbits)) for i, o in enumerate(orbits):
(of course, this is only possible if the orbit of the planet is circular); 2) let the two point masses move around on a pre-computed trajectory, and integrate the motion of the test particle in an inertial frame, but with a time-dependent potential; 3) same as above, but use a built-in potential of two moving point masses -- KeplerOrbit. The third approach is more accurate than the second, because the orbit of the two massive bodies is computed analytically, and is more general than the first one, because it can be used even for an eccentric planet. However, the second approach is still more general and can be used with an arbitrary number of moving potentials. """ import agama, numpy, os, matplotlib.pyplot as plt ax = plt.subplots(1, 3, figsize=(15, 5))[1] au = 4.84814e-9 # 1 astronomical unit in kpc # work in solar system units: 1 AU, 1 Msun, 1 km/s, 4.74 yr agama.setUnits(length=au, mass=1, velocity=1) r0 = 1. # AU v0 = 29.784694 # km/s - orbital velocity of Earth om = v0 / r0 # Omega - angular frequency of rotation tu = 4.7405 # years in one time unit ic = [ r0, 0, 0, 0, v0, 0 ] # initial conditions for the orbit - at the other side of the Sun from Earth tmax = 20 def convertToCorotatingFrame(t, o): return numpy.column_stack( (o[:, 0] * numpy.cos(om * t) + o[:, 1] * numpy.sin(om * t), o[:, 1] * numpy.cos(om * t) - o[:, 0] * numpy.sin(om * t), o[:, 2],
print("Vectorized:\n", Gpot.hessian(points.T)) print("Pointwise:\n", numpy.dstack([Gpot.hessian(point) for point in points])) print( "Checking vectorization of hessian for A: the two arrays should be equivalent" ) print("Vectorized:\n", Apot.hessian(points.T)) print("Pointwise:\n", numpy.dstack([Apot.hessian(point) for point in points])) test(gpot1, Gpot1, Apot1) test(gpot2, Gpot2, Apot2) test(gpot3, Gpot3, Apot3) test(gpot4, Gpot4, Apot4) #test(gpot5, Gpot5, Apot5) #### this one also fails because of hessian (and the absense of density method in gala) # now test potentials defined with units agama.setUnits(length=1, mass=1, velocity=977.792594852689) # velocity unit = 1 kpc / 1 Myr gpot6 = gala.potential.MilkyWayPotential() Gpot6 = agama.GalaPotential(gpot6) Apot6 = agama.GalaPotential( dict(type='miyamotonagai', mass=6.8e10, scaleradius=3.0, scaleheight=0.28), # disk dict(type='dehnen', mass=5.00e9, scaleradius=1.0), # bulge dict(type='dehnen', mass=1.71e9, scaleradius=0.07), # nucleus dict(type='nfw', mass=5.4e11, scaleradius=15.62), # halo units=Gpot6.units) test(gpot6, Gpot6, Apot6, test_hessian=False)
4.68875471e-01 ] + # long bar 1 [ 4.95381575e+08, 5.36363324e+00, 9.58522229e-01, 6.10542494e-01, 9.69645220e-01, 3.05125124e+00, 3.19043585e+00, 5.58255674e-01, 1.67310332e+01, 3.19575493e+00 ] + # long bar 2 [ 1.74304936e+13, 4.77961423e-01, 2.66853061e-01, 2.51516920e-01, 1.87882599e+00, 9.80136710e-01, 2.20415408e+00, 7.60708626e+00, -2.72907665e+01, 1.62966434e+00 ]) agama.setUnits(length=1, mass=1, velocity=1) # 1 kpc, 1 Msun, 1 km/s den = makeDensityModel(params) pot = makePotentialModel(params) pot.export('Portail17.ini') print('Created MW potential: total mass in stars=%.3g Msun, halo=%.3g Msun' % (pot[0].totalMass(), pot[1].totalMass())) # create an axisymmetrized version of the potential for plotting the true circular-velocity curve pot_axi = agama.Potential( agama.Potential(type='CylSpline', potential=pot[0], mmax=0, gridsizeR=25, gridsizez=25, Rmin=0.1, Rmax=40, zmin=0.05,
def specific_force( self, potential: PotentialType, points: TH.PositionType, *, frame: TH.OptFrameLikeType = None, representation_type: TH.OptRepresentationLikeType = None, **kwargs ) -> vectorfield.BaseVectorField: """Evaluate the specific force. Parameters ---------- potential : :class:`~agama.Potential` The potential. points : coord-array or |Representation| or None (optional) The points at which to evaluate the potential. frame : |CoordinateFrame| or None (optional, keyword-only) The frame of the potential. Potentials do not have an intrinsic reference frame, but if one is assigned, then anything needs to be converted to that frame. representation_type : |Representation| or None (optional, keyword-only) The representation type in which to return data. **kwargs Arguments into the potential. Returns ------- `~discO.utils.vectorfield.BaseVectorField` subclass instance Type set by `representation_type` """ shape = points.shape[:] # copy the shape p, _ = self._convert_to_frame(points, frame, representation_type) # AGAMA uses a flattened Cartesian representation r = p.represent_as(coord.CartesianRepresentation).reshape(-1) agama.setUnits(mass=1, length=1, velocity=1) # TODO! bad Fx, Fy, Fz = ( potential.force(r.xyz.T).T / u.kpc.to(u.km) # adjustment in AGAMA units * (u.km / u.s ** 2) ) # reshape r.shape = shape Fx.shape = shape Fy.shape = shape Fz.shape = shape # return vectorfield # TODO? convert back to from_frame? vf = vectorfield.CartesianVectorField( points=r, vf_x=Fx, vf_y=Fy, vf_z=Fz, frame=frame, ) if representation_type is not None: vf = vf.represent_as( resolve_representationlike(representation_type), ) return vf
import agama mass_unit = (1.0/4.3)*(10.0**(6.0)) agama.setUnits(mass=mass_unit, length=1, velocity=1) pot = agama.Potential(type='Spheroid', gamma=1.0, beta=3.1, scaleRadius=2.5, outerCutoffRadius=15.0) df = agama.DistributionFunction(type='QuasiSpherical',potential=pot) model = agama.GalaxyModel(pot,df) M = model.sample(10000) print(M[0][9999,0]) agama.writeSnapshot('test_snapshot.snp',M,'n')
def main(args: Optional[list] = None, opts: Optional[argparse.Namespace] = None): """Script Function. Parameters ---------- args : list, optional an optional single argument that holds the sys.argv list, except for the script name (e.g., argv[1:]) opts : Namespace, optional pre-constructed results of parsed args if not None, used ONLY if args is None """ if opts is not None and args is None: pass else: if opts is not None: warnings.warn("Not using `opts` because `args` are given") parser = make_parser() opts = parser.parse_args(args) foutname = DATA + "result_orbits.txt" if not os.path.isfile(foutname): # STEP 1: create Monte Carlo realizations of position and velocity of each cluster, # sampling from their measured uncertainties. # this file should have been produced by run_fit.py tab = np.loadtxt(DATA + "summary.txt", dtype=str) names = tab[:, 0] # 0th column is the cluster name (string) tab = tab[:, 1:].astype(float) # remaining columns are numbers ra0 = tab[:, 0] # coordinates of cluster centers [deg] dec0 = tab[:, 1] dist0 = tab[:, 2] # distance [kpc] vlos0 = tab[:, 3] # line-of-sight velocity [km/s] vlose = tab[:, 4] # its error estimate pmra0 = tab[:, 7] # mean proper motion [mas/yr] pmdec0 = tab[:, 8] pmrae = tab[:, 9] # its uncertainty pmdece = tab[:, 10] pmcorr = tab[:, 11] # correlation coefficient for errors in two PM components vlose = np.maximum( vlose, 2.0) # assumed error of at least 2 km/s on line-of-sight velocity diste = (dist0 * 0.46 * 0.1 ) # assumed error of 0.1 mag in distance modulus # create bootstrap samples np.random.seed(42) # ensure repeatability of random samples nboot = 100 # number of bootstrap samples for each cluster nclust = len(tab) ra = np.repeat(ra0, nboot) dec = np.repeat(dec0, nboot) pmra = np.repeat(pmra0, nboot) pmdec = np.repeat(pmdec0, nboot) for i in range(nclust): # draw PM realizations from a correlated 2d gaussian for each cluster A = np.random.normal(size=nboot) B = (np.random.normal(size=nboot) * (1 - pmcorr[i]**2)**0.5 + A * pmcorr[i]) pmra[i * nboot:(i + 1) * nboot] += pmrae[i] * A pmdec[i * nboot:(i + 1) * nboot] += pmdece[i] * B vlos = np.repeat(vlos0, nboot) + np.hstack( [np.random.normal(scale=e, size=nboot) for e in vlose]) dist = np.repeat(dist0, nboot) + np.hstack( [np.random.normal(scale=e, size=nboot) for e in diste]) # convert coordinates from heliocentric (ra,dec,dist,PM,vlos) to Galactocentric (kpc and km/s) u.kms = u.km / u.s c_sky = coord.ICRS( ra=ra * u.degree, dec=dec * u.degree, pm_ra_cosdec=pmra * u.mas / u.yr, pm_dec=pmdec * u.mas / u.yr, distance=dist * u.kpc, radial_velocity=vlos * u.kms, ) c_gal = c_sky.transform_to( coord.Galactocentric( galcen_distance=8.2 * u.kpc, galcen_v_sun=coord.CartesianDifferential([10.0, 248.0, 7.0] * u.kms), )) pos = np.column_stack( (c_gal.x / u.kpc, c_gal.y / u.kpc, c_gal.z / u.kpc)) vel = np.column_stack( (c_gal.v_x / u.kms, c_gal.v_y / u.kms, c_gal.v_z / u.kms)) # add uncertainties from the solar position and velocity pos[:, 0] += np.random.normal( scale=0.1, size=nboot * nclust) # uncertainty in solar distance from Galactic center vel[:, 0] += np.random.normal(scale=1.0, size=nboot * nclust) # uncertainty in solar velocity vel[:, 1] += np.random.normal(scale=3.0, size=nboot * nclust) vel[:, 2] += np.random.normal(scale=1.0, size=nboot * nclust) pos[:, 0] *= ( -1 ) # revert back to normal orientation of coordinate system (solar position at x=+8.2) vel[:, 0] *= -1 # same for velocity posvel = np.column_stack((pos, vel)).value np.savetxt(DATA + "posvel.txt", posvel, fmt="%.6g") # STEP 2: compute the orbits, min/max galactocentric radii, and actions, for all Monte Carlo samples print(agama.setUnits( length=1, velocity=1, mass=1)) # units: kpc, km/s, Msun; time unit ~ 1 Gyr potential = agama.Potential( DATA + "McMillan17.ini") # MW potential from McMillan(2017) # compute orbits for each realization of initial conditions, # integrated for 100 dynamical times or 20 Gyr (whichever is lower) print( "Computing orbits for %d realizations of cluster initial conditions" % len(posvel)) inttime = np.minimum(20.0, potential.Tcirc(posvel) * 100) orbits = agama.orbit(ic=posvel, potential=potential, time=inttime, trajsize=1000)[:, 1] rmin = np.zeros(len(orbits)) rmax = np.zeros(len(orbits)) for i, o in enumerate(orbits): r = np.sum(o[:, 0:3]**2, axis=1)**0.5 rmin[i] = np.min(r) if len(r) > 0 else np.nan rmax[i] = np.max(r) if len(r) > 0 else np.nan # replace nboot samples rmin/rmax with their median and 68% confidence intervals for each cluster rmin = np.nanpercentile(rmin.reshape(nclust, nboot), [16, 50, 84], axis=1) rmax = np.nanpercentile(rmax.reshape(nclust, nboot), [16, 50, 84], axis=1) # compute actions for the same initial conditions actfinder = agama.ActionFinder(potential) actions = actfinder(posvel) # again compute the median and 68% confidence intervals for each cluster actions = np.nanpercentile(actions.reshape(nclust, nboot, 3), [16, 50, 84], axis=1) # compute the same confidence intervals for the total energy energy = potential.potential( posvel[:, 0:3]) + 0.5 * np.sum(posvel[:, 3:6]**2, axis=1) energy = np.percentile(energy.reshape(nclust, nboot), [16, 50, 84], axis=1) # write the orbit parameters, actions and energy - one line per cluster, with the median and uncertainties fileout = open(foutname, "w") fileout.write( "# Name \t pericenter[kpc] \t apocenter[kpc] \t" + " Jr[kpc*km/s] \t Jz[kpc*km/s] \t Jphi[kpc*km/s] \t Energy[km^2/s^2] \n" ) for i in range(nclust): fileout.write(("%-15s" + "\t%7.2f" * 6 + "\t%7.0f" * 12 + "\n") % ( names[i], rmin[0, i], rmin[1, i], rmin[2, i], rmax[0, i], rmax[1, i], rmax[2, i], actions[0, i, 0], actions[1, i, 0], actions[2, i, 0], actions[0, i, 1], actions[1, i, 1], actions[2, i, 1], actions[0, i, 2], actions[1, i, 2], actions[2, i, 2], energy[0, i], energy[1, i], energy[2, i], )) fileout.close()
using numpy vectorized math operations, which improves efficiency. """ import numpy # if the module has been installed to the globally known directory, just import it try: import agama except ImportError: # otherwise load the shared library from the parent folder import sys sys.path += ['../'] try: import agama except ImportError as ex: sys.exit("\033[1;31mFAILED TO IMPORT AGAMA: %s\033[0m" % ex) # set some non-trivial dimensional units to test the correctness of unit conversion within the library agama.setUnits(length=1, mass=1e5, velocity=1) # note that the value of the gravitational constant in these units is stored as agama.G # user-defined density profile should be a function with a single argument -- # a 2d array Mx3, evaluating the density simultaneously at M points, # i.e., it should operate with columns of the input array x[:,0], x[:,1], etc. # To create a user function with arbitrary internal constants # (such as mass, scale radius, etc.) which are not fixed a priori, # we use a 'factory routine' that takes these internal parameters as arguments, # and creates an anonymous (lambda) function with these parameters built-in. def makeUserDensity(mass, radius): # we use column-wise sum (along axis 1) to obtain r^2 = x[0]^2+x[1]^2+x[2]^2 return lambda x: \ 3 / (4*numpy.pi) * mass * radius**2 * \
iniPotenGasDisk = dict(ini.items("Potential gas disk")) iniPotenBulge = dict(ini.items("Potential bulge")) iniPotenDarkHalo = dict(ini.items("Potential dark halo")) iniDFThinDisk = dict(ini.items("DF thin disk")) iniDFThickDisk = dict(ini.items("DF thick disk")) iniDFStellarHalo = dict(ini.items("DF stellar halo")) iniDFDarkHalo = dict(ini.items("DF dark halo")) iniDFBulge = dict(ini.items("DF bulge")) iniSCMHalo = dict(ini.items("SelfConsistentModel halo")) iniSCMBulge = dict(ini.items("SelfConsistentModel bulge")) iniSCMDisk = dict(ini.items("SelfConsistentModel disk")) iniSCM = dict(ini.items("SelfConsistentModel")) solarRadius = ini.getfloat("Data", "SolarRadius") # define external unit system describing the data (including the parameters in INI file) agama.setUnits(length=1, velocity=1, mass=1) # in Kpc, km/s, Msun # initialize the SelfConsistentModel object (only the potential expansion parameters) model = agama.SelfConsistentModel(**iniSCM) # create initial ('guessed') density profiles of all components densityBulge = agama.Density(**iniPotenBulge) densityDarkHalo = agama.Density(**iniPotenDarkHalo) densityThinDisk = agama.Density(**iniPotenThinDisk) densityThickDisk = agama.Density(**iniPotenThickDisk) densityGasDisk = agama.Density(**iniPotenGasDisk) densityStellarDisk = agama.Density(densityThinDisk, densityThickDisk) # composite # add components to SCM - at first, all of them are static density profiles model.components.append(agama.Component(density=densityStellarDisk, disklike=True)) model.components.append(agama.Component(density=densityBulge, disklike=False))
#!/usr/bin/python """ This script illustrates the use of the class agama.GalpyPotential, which is a subclass of both galpy.potential.Potential and agama.Potential, and provides both interfaces suitable for orbit integration and action finder routines. """ import agama import galpy.potential, galpy.actionAngle, galpy.orbit import numpy, time, matplotlib.pyplot as plt ### set up galpy potential g_pot = galpy.potential.MWPotential2014 ### set up equivalent potential from the Agama library, which also provides potential interface for galpy agama.setUnits(mass=1., length=8., velocity=220) a_pot = agama.GalpyPotential("../data/MWPotential2014galpy.ini") ### initialization of the action finder needs to be done once for the given potential dt = time.time() a_actfinder = agama.ActionFinder(a_pot, interp=False) print('Time to set up action finder: %.4g s' % (time.time() - dt)) ### we have a faster but less accurate "interpolated action finder", which takes a bit longer to initialize dt = time.time() i_actfinder = agama.ActionFinder(a_pot, interp=True) print('Time to set up interpolated action finder: %.4g s' % (time.time() - dt)) ### conversion from prolate spheroidal to cylindrical coords def ProlSphToCyl(la, nu, fd): return (((la - fd * fd) * (1 - abs(nu) / fd**2))**0.5,
axes[1,indx].set_xscale('log') axes[1,indx].set_yscale('log') #axes[1,indx].legend(loc='lower left') axes[1,indx].set_xlim(rmin, rmax) axes[1,indx].set_ylim(densmin, densmax) axes[1,indx].set_xlabel('$r$') axes[1,indx].set_ylabel(r'$\rho$') axes[1,indx].text( (rmin*rmax)**0.5, densmin*2, label, ha='center') ################ MAIN PROGRAM ################## #base = "gs010_bs050_rcrs100_rarcinf_core_0400mpc3_df" if len(sys.argv)<=1: print "Provide the data file name as the command-line argument" exit() agama.setUnits(mass=1, length=1, velocity=1) base = sys.argv[1] model = ModelParams(base) rmin = 0.01 rmax = 100. velmin = 0. velmax = 40. radii = numpy.logspace(numpy.log10(rmin), numpy.log10(rmax), 25) midradii = (radii[1:] * radii[:-1])**0.5 xyz = numpy.vstack((radii, numpy.zeros_like(radii), numpy.zeros_like(radii))).T # plot the inferred density of dark matter and its log-slope as functions of radius fig,axes = pyplot.subplots(2, 3, figsize=(12,8)) plot_profiles("6"+base+"/"+base+"_1000_0.dat", 0, '6d, no errors') plot_profiles("5"+base+"/"+base+"_1000_0_err.dat", 1, r'5d, $\delta v$=2 km/s') plot_profiles("3"+base+"/"+base+"_1000_0_err.dat", 2, r'3d, $\delta v$=2 km/s')
reg=True) # convert the grid-based density profile into a full-fledged potential contracted_pot = agama.Potential( type="Multipole", symmetry="spherical", rmin=rmin, rmax=rmax, density=lambda xyz: numpy.exp( dens_contracted_interp(numpy.log(numpy.sum(xyz**2, axis=1)) * 0.5) )) return contracted_pot if __name__ == '__main__': # example of usage of the above function for the Milky Way potential from Cautun+ 2020 agama.setUnits(mass=1., length=1., velocity=1.) # Msun, kpc, km/s # DM halo fb = 4.825 / 30.7 # Planck 1 baryon fraction m200 = 0.969e12 # the DM halo mass conc = 8.76 NFW_rho0 = 3486926.735447284 NFW_rs = 25.20684733101539 # Note subtletly in Cautun20 NFW definition, scaled overdensity changes from paper value! # bulge params_bulge = dict(type='Spheroid', densityNorm=1.03e11, axisRatioZ=0.5, gamma=0, beta=1.8, scaleRadius=0.075,
They are not provided in the distribution, but could be created by running example_self_consistent_model.py The potential is computed from the snapshot itself, by creating a suitable potential expansion for each component: Multipole for the halo and CylSpline for the disk. This actually takes most of the time. We save the constructed potentials to text files, and on the subsequent launches of this program they are loaded from these files, speeding up the initialization. Then we compute actions for all particles from the disk component, and store them in a text file. An equivalent example in C++ is located in tests folder. """ import agama, numpy, time #1. set units (in Msun, Kpc, km/s) agama.setUnits(mass=1, length=1, velocity=1) #2. get in N-body snapshots: columns 0 to 2 are position, 3 to 5 are velocity, 6 is mass tbegin = time.clock() try: diskParticles = numpy.loadtxt("model_stars_final") haloParticles = numpy.loadtxt("model_dm_final") except: print("Input snapshot files are not available; " \ "you may create them by running example_self_consistent_model.py") exit() print("%g s to load %d disk particles (total mass=%g Msun) " \ "and %d halo particles (total mass=%g Msun)" % \ ( time.clock()-tbegin, \ diskParticles.shape[0], numpy.sum(diskParticles[:,6]), \
from galpy.potential import * from galpy.actionAngle import * from galpy.orbit import * import agama # this allows access to potential, action finder, orbit integration, etc. as standalone classes and routines import galpy_agama # this enables galpy-compatible interface to potentials import numpy, matplotlib, matplotlib.pyplot as plt, time matplotlib.rcParams['legend.frameon']=False ###1. set up galpy potential g_bulge = PowerSphericalPotentialwCutoff(alpha=1.8, rc=1.9/8, amp=0.03) g_disk = MiyamotoNagaiPotential(a=3./8, b=0.28/8, amp=0.755) g_halo = NFWPotential(a=2., amp=4.85) g_pot = [g_bulge,g_disk,g_halo] # same as MWPotential2014 ###2. set up equivalent potential from the Agama library agama.setUnits( mass=1., length=8., velocity=345.67) p_bulge = {"type":"SpheroidDensity", "densityNorm":6.669e9, "gamma":1.8, "beta":1.8, "scaleRadius":1, "outerCutoffRadius":1.9/8}; p_disk = {"type":"MiyamotoNagai", "mass":1.678e11, "scaleradius":3./8, "scaleheight":0.28/8}; p_halo = {"type":"SpheroidDensity", "densityNorm":1.072e10, "gamma":1.0, "beta":3.0, "scaleRadius":2.}; ### one can create the genuine instance of Agama potential as follows: #c_pot = agama.Potential("../data/MWPotential2014.ini") # read parameters from ini file #c_pot = agama.Potential(p_bulge, p_disk, p_halo) # or create potential from a list of parameters ### or one can instead create a galpy-compatible potential as follows: w_pot = galpy_agama.CPotential(p_bulge, p_disk, p_halo) # same as above, two variants ### ...and then use _pot member variable to access the instance of raw Agama potential dt = time.time() ### initialization of the action finder needs to be done once for the given potential c_actfinder = agama.ActionFinder(w_pot._pot, interp=False) print 'Time to set up action finder: %s s' % (time.time()-dt)
def update_index(self, index, ss_id=None, snap=None): agama.setUnits(mass=1, length=1, velocity=1) self.current_index = index self.ss_id = ss_id if snap is not None: self.snap = snap else: self.snap = \ gizmo.io.Read.read_snapshots(['star', 'gas', 'dark'], 'index', index, properties=['id', 'position', 'velocity', 'mass', 'form.scalefactor'], simulation_directory= self.simulation_directory, assign_principal_axes=True) if self.sim_name is None: head = gizmo.io.Read.read_header(snapshot_value=self.startnum, simulation_directory= self.simulation_directory) self.sim_name = head['simulation.name'].replace(" ", "_") potential_cache_file = self.cache_directory + '/potential_id'+str(index) potential_cache_file += '_' + self.sim_name + '_pot' try: self.potential = agama.Potential(file=potential_cache_file) except: star_position = self.snap['star'].prop('host.distance.principal') gas_position = self.snap['gas'].prop('host.distance.principal') dark_position = self.snap['dark'].prop('host.distance.principal') star_mass = self.snap['star']['mass'] gas_mass = self.snap['gas']['mass'] dark_mass = self.snap['dark']['mass'] position = np.concatenate((star_position, gas_position)) mass = np.concatenate((star_mass, gas_mass)) #TODO make these user-controllable self.pdark = agama.Potential(type="Multipole", particles=(dark_position, dark_mass), symmetry='a', gridsizeR=20, lmax=2) self.pbar = agama.Potential(type="CylSpline", particles=(position, mass), gridsizer=20, gridsizez=20, mmax=0, Rmin=0.2, symmetry='a', Rmax=50, Zmin=0.02, Zmax=10) self.potential = agama.Potential(self.pdark, self.pbar) self.potential.export(potential_cache_file) if ss_id is not None: self.ss_init = True ss_key = np.where(self.snap['star']['id'] == self.ss_id)[0] self.chosen_position = self.snap['star'].prop( 'host.distance.principal')[ss_key] self.chosen_velocity = self.snap['star'].prop( 'host.velocity.principal')[ss_key] else: self.ss_init = False self.af = agama.ActionFinder(self.potential, interp=False)
# aux functions for determining the nature of objects def isFloat(obj): return isinstance(obj, float) def isTuple(obj, length): return isinstance(obj, tuple) and len(obj) == length def isArray(obj, shape): return isinstance(obj, numpy.ndarray) and obj.shape == shape # set up some non-trivial dimensional units agama.setUnits(length=2, velocity=3, mass=4e6) dens = agama.Density(type='plummer') pots = agama.Potential(type='dehnen', gamma=0) # spherical potential potf = agama.Potential(type='plummer', q=0.75) # flattened potential pott = agama.Potential(type='plummer', p=0.75, q=0.5) # triaxial potential actf = agama.ActionFinder(potf) actm = agama.ActionMapper(potf, [1, 1, 1]) df0 = agama.DistributionFunction(type='quasispherical', density=pots, potential=pots, r_a=2.0) df1 = agama.DistributionFunction(type='quasispherical', density=dens, potential=pots, beta0=-0.2)