# note: use a 10x larger particle mass for halo than for bulge/disk agama.writeSnapshot("model_halo_final", \ agama.GalaxyModel(potential=model.potential, df=dfHalo, af=model.af).sample(3000000), format) # the remaining part computes and plots various diagnostics print("\033[1;33mComputing disk density and velocity profiles\033[0m") ax=plt.subplots(2, 3, figsize=(16,10))[1].reshape(-1) # take only the disk component modelDisk = agama.GalaxyModel(potential=model.potential, df=dfDisk, af=model.af) # radial grid for computing various quantities in the disk plane Sigma0 = float(iniPotenDisk["surfacedensity"]) Rdisk = float(iniPotenDisk["scaleradius"]) Hdisk =-float(iniPotenDisk["scaleheight"]) sigmar0= float(iniDFDisk["sigmar0"]) rsigmar= float(iniDFDisk["rsigmar"]) R = agama.nonuniformGrid(60, 0.01*Rdisk, 10.0*Rdisk) xyz = numpy.column_stack((R, R*0, R*0)) print("Computing surface density") Sigma,rmsh,rmsv = modelDisk.projectedMoments(R) print("Computing 3d density and velocity dispersion") rho,vel,sigma = modelDisk.moments(xyz, dens=True, vel=True, vel2=True) force, deriv = model.potential.forceDeriv(xyz) kappa = numpy.sqrt(-deriv[:,0] - 3*force[:,0]/R) ToomreQ = sigma[:,0]**0.5 * kappa / 3.36 / Sigma numpy.savetxt("disk_plane", numpy.column_stack((R, Sigma, rho, rmsh, sigma[:,0]**0.5, (sigma[:,2]-vel**2)**0.5, \ sigma[:,1]**0.5, vel, ToomreQ)), header="R Sigma rho(R,z=0) height sigma_R sigma_phi sigma_z v_phi ToomreQ", fmt="%.6g") ax[0].plot(R, Sigma / (Sigma0 * numpy.exp(-R/Rdisk)), 'r-', label=r'$\Sigma$') ax[0].plot(R, rho / (Sigma0 * numpy.exp(-R/Rdisk) * 0.25/Hdisk), 'g-', label=r'$\rho_{z=0}$') ax[0].plot(R, sigma[:,0]**0.5 / (sigmar0 * numpy.exp(-R/rsigmar)), 'b-', label=r'$\sigma_r$')
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) command = args.get('DO', 'run').upper() # [REQ] operation mode: 'RUN' - run a model, 'PLOT' - show the model grid and maps usehist = args.get('HIST', 'n')[0] in 'yYtT1' # [OPT] whether to use LOSVD histograms as input (default 'no' is to use GH moments) variant = 'Hist' if usehist else 'GH' # suffix for disinguishing runs using histogramed LOSVDs or GH moments fileResult= 'results%s.txt' % variant # [OPT] filename for collecting summary results for the entire model grid numpy.random.seed(42) # make things repeatable numpy.set_printoptions(precision=4, linewidth=200, suppress=True) ### parameters for the density dataset densityParams = dict( type = 'DensityCylindricalLinear', # [REQ]: variant of density discretization grid; remaining parameters depend on this choice gridr = agama.nonuniformGrid(nnodes=20, xmin=0.2, xmax=100.), # [REQ] grid in cylindrical radius (TODO: determine automatically?) gridz = agama.nonuniformGrid(nnodes=15, xmin=0.2, xmax=15.0), # [REQ] grid in vertical coordinate mmax = 0 # [OPT] number of azimuthal-harmonic coefficients (0 for axisymmetric systems) ) filenameMGE = 'mge.txt' # [REQ] file with parameters of the MGE model for the surface density profile (if MGE is used) ### common parameters for kinematic datasets (though in principle they may also differ between them) gridv = numpy.linspace(-500, 500, 26) # [REQ] the grid in model velocity space (will be multiplied by sqrt(Upsilon) when comparing to data) velpsf = 0.0 # [OPT] velocity-space PSF (usually not needed, as the spectroscopic fits produce deconvolved LOSVDs) # [OPT] define the degree and velocity grid for the observed LOSVD provided as histograms or (less likely) higher-degree B-splines; # the following two lines are needed [REQ] only if the input is provided in the form of binned LOSVDs (usehist=True), # but we also use these parameters to generate mock LOSVD histograms if command=='MOCK' hist_degree = 0 # [OPT] B-spline degree for the observed LOSVDs (0 means histogram) hist_gridv = numpy.linspace(-400, 400, 17) # [OPT] velocity grid for the observed LOSVDs (boundaries of velocity bins, not centers!) ### parameters of the 1st kinematic dataset
model.iterate() printoutInfo(model, 1) # now that we have a reasonable guess for the total potential, # we may initialize the DF of the stellar disk dfDisk = agama.DistributionFunction(pot=model.pot, **iniDFDisk) # we can compute the masses even though we don't know the density profile yet print "**** STARTING TWO-COMPONENT MODELLING ****\nMasses are: ", \ "Mhalo=%g," % dfHalo.totalMass(), \ "Mbulge=%g," % dfBulge.totalMass(), \ "Mdisk=%g" % dfDisk.totalMass() # and replace the static disk component them with a DF-based disk one model.components[2] = agama.Component(df=dfDisk, disklike=True, \ gridR=agama.nonuniformGrid(int(iniSCMDisk['sizeRadialCyl']), \ float(iniSCMDisk['RminCyl']), float(iniSCMDisk['RmaxCyl'])), \ gridz=agama.nonuniformGrid(int(iniSCMDisk['sizeVerticalCyl']), \ float(iniSCMDisk['zminCyl']), float(iniSCMDisk['zmaxCyl'])) ) # do a few more iterations to obtain the self-consistent density profile for both disks for iteration in range(6,9): print "Starting iteration #%d" % iteration model.iterate() printoutInfo(model, iteration) print "Computing disk density and velocity profiles" R=numpy.linspace(0.2,10,50) xyz=numpy.vstack((R,R*0,R*0)).T Sigma,_ = agama.GalaxyModel(pot=model.pot, df=dfDisk, af=model.af).projectedMoments(R) rho,sigma = agama.GalaxyModel(pot=model.pot, df=dfDisk, af=model.af).moments(xyz) force, deriv = model.pot.forceDeriv(xyz)
""" import agama, numpy # density profile den = agama.Density(type='Dehnen', axisRatioY=0.8, axisRatioZ=0.6, scaleRadius=1, mass=1, gamma=1) # potential corresponding to this density pot = agama.Potential(type='Multipole', lmax=8, mmax=6, density=den) # target1 is discretized density profile target1 = agama.Target(type='DensityClassicLinear', gridr=agama.nonuniformGrid(25, 0.1, 20.0), \ axisratioy=0.8, axisratioz=0.6, stripsPerPane=2) # target2 is discretized kinematic constraint (we will enforce velocity isotropy) target2 = agama.Target(type='KinemShell', gridR=agama.nonuniformGrid(15, 0.2, 10.0), degree=1) # construct initial conditions for the orbit library initcond, weightprior = den.sample(5000, potential=pot) # integration time is 50 orbital periods inttimes = 50 * pot.Tcirc(initcond) # integrate all orbits, storing the recorded data corresponding to each target # in the data1 and data2 arrays, and the trajectories - in trajs data1, data2, trajs = agama.orbit(potential=pot, ic=initcond, time=inttimes, \
model.iterate() printoutInfo(model, 1) # now that we have a reasonable guess for the total potential, # we may initialize the DF of the stellar disk dfDisk = agama.DistributionFunction(pot=model.pot, **iniDFDisk) # we can compute the masses even though we don't know the density profile yet print "**** STARTING TWO-COMPONENT MODELLING ****\nMasses are: ", \ "Mhalo=%g," % dfHalo.totalMass(), \ "Mbulge=%g," % dfBulge.totalMass(), \ "Mdisk=%g" % dfDisk.totalMass() # and replace the static disk component them with a DF-based disk one model.components[2] = agama.Component(df=dfDisk, disklike=True, \ gridR=agama.nonuniformGrid(int(iniSCMDisk['sizeRadialCyl']), \ float(iniSCMDisk['RminCyl']), float(iniSCMDisk['RmaxCyl'])), \ gridz=agama.nonuniformGrid(int(iniSCMDisk['sizeVerticalCyl']), \ float(iniSCMDisk['zminCyl']), float(iniSCMDisk['zmaxCyl'])) ) # do a few more iterations to obtain the self-consistent density profile for both disks for iteration in range(6, 9): print "Starting iteration #%d" % iteration model.iterate() printoutInfo(model, iteration) print "Computing disk density and velocity profiles" R = numpy.linspace(0.2, 10, 50) xyz = numpy.vstack((R, R * 0, R * 0)).T Sigma, _ = agama.GalaxyModel(pot=model.pot, df=dfDisk, af=model.af).projectedMoments(R) rho, sigma = agama.GalaxyModel(pot=model.pot, df=dfDisk,
# stellar potential potstars = agama.Potential(type='Sersic', sersicIndex=4, axisRatioZ=0.6, scaleRadius=1, mass=1) # central black hole (slightly softened) potbh = agama.Potential(type='Plummer', scaleRadius=1e-4, mass=0.1) # total potential pot = agama.Potential(potstars, potbh) # discretized density profile is recorded on a grid of radial points and spherical harmonics up to lmax gridr = agama.nonuniformGrid( 50, 0.02, 20.0) # !! make sure that the grid covers the range of interest !! target = agama.Target(type='DensitySphHarm', gridr=gridr, lmax=8, mmax=0) # discretized density profile to be used as the density constraint rhs = target(potstars) print("Density constraint values") for i in range(len(gridr)): print("%s: mass= %g" % (target[i], rhs[i])) # construct initial conditions for the orbit library: # use the anisotropic Jeans eqs to set the (approximate) shape of the velocity ellipsoid and the mean v_phi; beta = 0.0 # velocity anisotropy in R/z plane: >0 - sigma_R>sigma_z, <0 - reverse. kappa = 0.8 # sets the amount of rotation v_phi vs. dispersion sigma_phi; 1 is rather fast rotation, 0 - no rot. initcond, _ = potstars.sample(10000, potential=pot, beta=beta, kappa=kappa)
def plotModel(model, df): print('Creating density and kinematic plots...') gridx = numpy.linspace(0, 0.70, 141) gridz = numpy.linspace(0, 0.25, 51) gridxz = numpy.column_stack( (numpy.tile(gridx, len(gridz)), numpy.repeat(gridz, len(gridx)))) gridxyz = numpy.column_stack( (numpy.tile(gridx, len(gridz)), numpy.zeros(len(gridx) * len(gridz)), numpy.repeat(gridz, len(gridx)))) nsc = model.components[0].getDensity() nsd = model.components[1].getDensity() rho_nsd = nsd.density(gridxyz).reshape(len(gridz), len(gridx)) Sig_nsd = nsd.projectedDensity(gridxz, beta=numpy.pi / 2).reshape( len(gridz), len(gridx)) plt.figure(figsize=(20, 15)) ax = numpy.array( [plt.axes([0.035, 0.81 - 0.195 * i, 0.38, 0.18]) for i in range(5)] + [plt.axes([0.425, 0.81 - 0.195 * i, 0.38, 0.18]) for i in range(5)]).reshape(2, 5).T ax[0, 0].contour(gridx, gridz, numpy.log10(rho_nsd), levels=numpy.log10(numpy.max(rho_nsd)) + numpy.linspace(-6, -0, 16), cmap='Blues') ax[0, 1].contour(gridx, gridz, numpy.log10(Sig_nsd), levels=numpy.log10(numpy.max(Sig_nsd)) + numpy.linspace(-6, -0, 16), cmap='Blues') # compute moments on a coarser grid gridx = agama.nonuniformGrid(20, 0.02, 0.70) gridz = agama.nonuniformGrid(10, 0.02, 0.25) gridxz = numpy.column_stack( (numpy.tile(gridx, len(gridz)), numpy.repeat(gridz, len(gridx)))) gridxyz = numpy.column_stack( (numpy.tile(gridx, len(gridz)), numpy.zeros(len(gridx) * len(gridz)), numpy.repeat(gridz, len(gridx)))) gm = agama.GalaxyModel(model.potential, df) gridv = numpy.linspace(0, 100, 11) # intrinsic moments vel, vel2 = gm.moments(gridxyz, dens=False, vel=True, vel2=True, beta=numpy.pi / 2) plt.clabel(ax[1, 0].contour(gridx, gridz, -vel[:, 2].reshape(len(gridz), len(gridx)), levels=gridv, cmap='Blues'), fmt='%.0f') plt.clabel(ax[2, 0].contour(gridx, gridz, numpy.sqrt(vel2[:, 2] - vel[:, 2]**2).reshape( len(gridz), len(gridx)), levels=gridv, cmap='Blues'), fmt='%.0f') plt.clabel(ax[3, 0].contour(gridx, gridz, numpy.sqrt(vel2[:, 0] - vel[:, 0]**2).reshape( len(gridz), len(gridx)), levels=gridv, cmap='Blues'), fmt='%.0f') plt.clabel(ax[4, 0].contour(gridx, gridz, numpy.sqrt(vel2[:, 1] - vel[:, 1]**2).reshape( len(gridz), len(gridx)), levels=gridv, cmap='Blues'), fmt='%.0f') # projected moments vel, vel2 = gm.moments(gridxz, dens=False, vel=True, vel2=True, beta=numpy.pi / 2) plt.clabel(ax[1, 1].contour(gridx, gridz, -vel[:, 2].reshape(len(gridz), len(gridx)), levels=gridv, cmap='Blues'), fmt='%.0f') plt.clabel(ax[2, 1].contour(gridx, gridz, numpy.sqrt(vel2[:, 2] - vel[:, 2]**2).reshape( len(gridz), len(gridx)), levels=gridv, cmap='Blues'), fmt='%.0f') plt.clabel(ax[3, 1].contour(gridx, gridz, numpy.sqrt(vel2[:, 0] - vel[:, 0]**2).reshape( len(gridz), len(gridx)), levels=gridv, cmap='Blues'), fmt='%.0f') plt.clabel(ax[4, 1].contour(gridx, gridz, numpy.sqrt(vel2[:, 1] - vel[:, 1]**2).reshape( len(gridz), len(gridx)), levels=gridv, cmap='Blues'), fmt='%.0f') labels = ['density', 'v_los', 'sigma_los', 'sigma_R', 'sigma_z'] colors = ['c', 'm', 'b', 'r', 'g'] # fiducial fields for computing projected velocity distributions points = [[0.05, 0.01], [0.15, 0.01], [0.15, 0.10], [0.30, 0.01]] for i in range(5): ax[i, 0].set_ylabel('z [kpc]', labelpad=4) ax[i, 1].set_yticklabels([]) ax[i, 0].text(0.5, 0.98, 'intrinsic ' + labels[i], ha='center', va='top', transform=ax[i, 0].transAxes, color=colors[i]) ax[i, 1].text(0.5, 0.98, 'projected ' + labels[i], ha='center', va='top', transform=ax[i, 1].transAxes, color=colors[i]) if i < 4: ax[i, 0].set_xticklabels([]) ax[i, 1].set_xticklabels([]) else: ax[i, 0].set_xlabel('R [kpc]', labelpad=2) ax[i, 1].set_xlabel('R [kpc]', labelpad=2) ax[i, 0].set_xlim(min(gridx), max(gridx)) ax[i, 1].set_xlim(min(gridx), max(gridx)) ax[i, 0].set_ylim(min(gridz), max(gridz)) ax[i, 1].set_ylim(min(gridz), max(gridz)) for k, point in enumerate(points): ax[i, 1].text(point[0], point[1], chr(k + 65), ha='center', va='center', color='olive') gridv = numpy.linspace( -250, 250, 26) # coarse grid for computing the velocity distribution gridV = numpy.linspace( -250, 250, 101) # fine grid for plotting a smoother spline-interpolated VDF vdfx, vdfz, vdfd = gm.vdf(points, gridv, beta=numpy.pi / 2) for i in range(4): ax = plt.axes([0.845, 0.75 - 0.24 * i, 0.15, 0.225]) ax.plot(gridV, vdfd[i](-gridV), 'b', label='f(v_los)') ax.plot(gridV, vdfx[i](-gridV), 'r', label='f(v_R)') ax.plot(gridV, vdfz[i](-gridV), 'g', label='f(v_z)') ax.set_yscale('log') ax.text(0.02, 0.98, 'field %s' % chr(i + 65), ha='left', va='top', transform=ax.transAxes, color='olive') ax.set_xlim(min(gridv), max(gridv)) ax.set_ylim(1e-5, 3e-2) if i < 3: ax.set_xticklabels([]) else: ax.set_xlabel('V [km/s]') 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()
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 ca = numpy.cos(ang) ca[18] = ca[54] = 0 circ = [numpy.column_stack((rr * ca, rr * sa)) for rr in gridr[1:]] poly = [circ[0]] + [ numpy.vstack((p1, p2[::-1])) for p1, p2 in zip(circ[1:], circ[:-1]) ] numaper = len(