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) kappa = numpy.sqrt(-deriv[:, 0] - 3 * force[:, 0] / R) ToomreQ = sigma[:, 0]**0.5 * kappa / 3.36 / Sigma numpy.savetxt("disk_plane", numpy.vstack((R, Sigma, rho, sigma[:, 0]**0.5, sigma[:, 1]**0.5, ToomreQ)).T, fmt="%.6g") # export model to an N-body snapshot print "Creating an N-body representation of the model" # first create a representation of density profiles without velocities # (just for demonstration), by drawing samples from the density distribution
def plotProfiles(self, chain): ''' plot the radial profiles of various quantities from the set of model in the MCMC chain, together with the true profiles. ''' axes = plt.subplots(2, 2, figsize=(12, 8))[1].T.reshape(-1) rmin = 0.01 rmax = 100. radii = numpy.logspace(numpy.log10(rmin), numpy.log10(rmax), 41) midradii = (radii[1:] * radii[:-1])**0.5 xyz = numpy.column_stack((radii, radii * 0, radii * 0)) # compute and store the profiles for each model in the chain, then take 68% and 95% percentiles dmdens = numpy.zeros((chain.shape[0], len(radii))) dmslope = numpy.zeros((chain.shape[0], len(midradii))) trdens = numpy.zeros((chain.shape[0], len(radii))) trbeta = numpy.zeros((chain.shape[0], len(radii))) print('Plotting profiles...') for i in range(len(chain)): pot, df = self.model.createModel(chain[i]) dmdens[i] = pot.density(xyz) dmslope[i] = numpy.log(dmdens[i, 1:] / dmdens[i, :-1]) / numpy.log( radii[1:] / radii[:-1]) trdens[i], trvel = agama.GalaxyModel(pot, df).moments(xyz, dens=True, vel=False, vel2=True) trbeta[i] = 1 - trvel[:, 1] / trvel[:, 0] # log-slope of the DM density profile d(log rho) / d(log r) cntr = numpy.percentile(dmslope, [2.3, 15.9, 50, 84.1, 97.7], axis=0) axes[0].fill_between(midradii, cntr[0], cntr[4], color='lightgray') # 2 sigma axes[0].fill_between(midradii, cntr[1], cntr[3], color='gray') # 1 sigma axes[0].plot(midradii, cntr[2], color='k') # median axes[0].set_xscale('log') axes[0].set_xlim(rmin, rmax) axes[0].set_ylim(-5, 1) axes[0].set_xlabel('$r$') axes[0].set_ylabel(r'$d(\ln\rho_{DM}) / d(\ln r)$') # DM density profile cntr = numpy.percentile(dmdens, [2.3, 15.9, 50, 84.1, 97.7], axis=0) axes[1].fill_between(radii, cntr[0], cntr[4], color='lightgray') # 2 sigma axes[1].fill_between(radii, cntr[1], cntr[3], color='gray') # 1 sigma axes[1].plot(radii, cntr[2], color='k') # median axes[1].set_xscale('log') axes[1].set_yscale('log') axes[1].set_xlim(rmin, rmax) axes[1].set_xlabel('$r$') axes[1].set_ylabel(r'$\rho_{DM}$') # velocity anisotropy coefficient (beta) of tracers cntr = numpy.percentile(trbeta, [2.3, 15.9, 50, 84.1, 97.7], axis=0) axes[2].fill_between(radii, cntr[0], cntr[4], color='lightgray') # 2 sigma axes[2].fill_between(radii, cntr[1], cntr[3], color='gray') # 1 sigma axes[2].plot(radii, cntr[2], color='k') # median axes[2].set_xscale('log') axes[2].set_xlim(rmin, rmax) axes[2].set_ylim(-1, 1) axes[2].set_xlabel('$r$') axes[2].set_ylabel(r'$\beta_\star$') # 3d density profile of tracers cntr = numpy.percentile(trdens, [2.3, 15.9, 50, 84.1, 97.7], axis=0) axes[3].fill_between(radii, cntr[0], cntr[4], color='lightgray') # 2 sigma axes[3].fill_between(radii, cntr[1], cntr[3], color='gray') # 1 sigma axes[3].plot(radii, cntr[2], color='k') # median axes[3].set_xscale('log') axes[3].set_yscale('log') axes[3].set_xlim(rmin, rmax) axes[3].set_xlabel('$r$') axes[3].set_ylabel(r'$\rho_\star$') # histogram of radial distribution of the original points on each of the four panels ptcount = numpy.histogram( (self.particles[:, 0]**2 + self.particles[:, 1]**2)**0.5, bins=radii)[0] for ax in axes: plt.twinx(ax) plt.plot(numpy.hstack(zip(radii[:-1], radii[1:])), numpy.repeat(ptcount, 2), 'g-', alpha=0.5) plt.ylim(0, 2 * max(ptcount)) try: true_dmdens = self.model.truePotential.density(xyz) true_dmslope = numpy.log( true_dmdens[1:] / true_dmdens[:-1]) / numpy.log( radii[1:] / radii[:-1]) true_trdens = self.model.tracerDensity.density(xyz) true_trbeta = self.model.tracerBeta(radii) axes[0].plot(midradii, true_dmslope, color='r', lw=3, linestyle='--') axes[1].plot(radii, true_dmdens, color='r', lw=3, linestyle='--') axes[2].plot(radii, true_trbeta, color='r', lw=3, linestyle='--') axes[3].plot(radii, true_trdens, color='r', lw=3, linestyle='--') axes[1].set_ylim(true_dmdens[-1] * 0.5, true_dmdens[0] * 5) axes[3].set_ylim(true_trdens[-1] * 0.5, true_trdens[0] * 5) except AttributeError: pass # no true values known plt.tight_layout() plt.savefig(self.filename + "_profiles.png") plt.close()
# original DF using a C++ object df_orig = agama.DistributionFunction( \ type="DoublePowerLaw", J0=J0, slopeIn=0, slopeOut=Beta, norm=2*numpy.pi**3) # to compute the total mass, we create an proxy instance of DistributionFunction class # with a composite DF consisting of a single component (the user-supplied function); # this proxy class provides the totalMass() method # that the user-defined Python function itself does not have mass_orig = df_orig.totalMass() mass_my = agama.DistributionFunction(MyDF).totalMass() print "DF mass=%.8g (orig value=%.8g)" % (mass_my, mass_orig) # GalaxyModel objects constructed from the C++ DF and from the Python function # (without the need of a proxy object; in fact, GalaxyModel.df is itself a proxy object) gm_orig = agama.GalaxyModel(df=df_orig, potential=pot_orig) gm_my = agama.GalaxyModel(df=MyDF, potential=pot_appr) # note that different potentials were used for gm_orig and gm_my, so the results may slightly disagree # DF moments (density and velocity dispersion) computed from the C++ DF object dens_orig, veldisp_orig = gm_orig.moments(point=(1, 0, 0)) print "original DF at r=1: density=%.8g, sigma_r=%.8g, sigma_t=%.8g" % \ ( dens_orig, veldisp_orig[0], veldisp_orig[1] ) # DF moments computed from the Python distribution function dens_my, veldisp_my = gm_my.moments(point=(1, 0, 0)) print "user-def DF at r=1: density=%.8g, sigma_r=%.8g, sigma_t=%.8g" % \ ( dens_my, veldisp_my[0], veldisp_my[1] ) # gm_my.df.totalMass() will give the same result as mass_my if abs(pot0_orig - pot0_appr) < 1e-6 and abs(
def contraction(pot_dm, pot_bar, method='C20', beta_dm=0.0, rmin=1e-2, rmax=1e4): ''' Construct the contracted halo density profile for the given initial halo density and the baryonic density profiles. Arguments: pot_dm: initial halo potential (assumed to be spherical!). pot_bar: potential of baryons (a spherical approximation to it will be used even if it was not spherical). method: choice between two alternative approaches: 'C20' (default) uses the approximate correction procedure from Cautun+2020; 'adiabatic' uses the invariance of actions in conjunction with an internally constructed halo DF. beta_dm: anisotropy coefficient for the halo DF (only used with the adiabatic method, must be between -0.5 and 1, default 0 means isotropy). rmin (1e-2), rmax(1e4): min/max grid radii for representing the contracted density profile (default values are suitable for Milky Way-sized galaxies if expressed in kpc). Return: the spherically symmetric contracted halo potential. ''' gridr = numpy.logspace(numpy.log10(rmin), numpy.log10(rmax), 101) xyz = numpy.column_stack((gridr, gridr * 0, gridr * 0)) if method == 'adiabatic': # create a spherical DF for the DM-only potential/density pair with a constant anisotropy coefficient beta df_dm = agama.DistributionFunction(type='QuasiSpherical', potential=pot_dm, beta0=beta_dm) # create a sphericalized total potential (DM+baryons) pot_total_sph = agama.Potential(type='multipole', potential=agama.Potential( pot_dm, pot_bar), lmax=0, rmin=0.1 * rmin, rmax=10 * rmax) # compute the density generated by the DF in the new total potential at the radial grid dens_contracted = agama.GalaxyModel(pot_total_sph, df_dm).moments(xyz, dens=True, vel=False, vel2=False) elif method == 'C20': # use the differential (d/dr) form of Eq. (11) from Cautun et al (2020) to approximate the effect of contraction cumul_mass_dm = pot_dm.enclosedMass( gridr) # cumulative mass profile of DM cumul_mass_bar = pot_bar.enclosedMass( gridr) # same for baryons (sphericalized) valid_r = numpy.hstack([ True, cumul_mass_bar[:-1] < cumul_mass_bar[1:] * 0.999 ]) # use only those radii where mass keeps increasing sph_dens_bar = agama.Density(cumulmass=numpy.column_stack( (gridr[valid_r], cumul_mass_bar[valid_r]))) # sphericalized baryon density profile f_bar = 0.157 # cosmic baryon fraction; the formula is calibrated against simulations only for this value eta_bar = cumul_mass_bar / cumul_mass_dm * ( 1. - f_bar ) / f_bar # the last two terms account for transforming the DM mass into the corresponding baryonic mass in dark-matter-only simulations first_factor = 0.45 + 0.41 * (eta_bar + 0.98)**0.53 dens_dm_orig = pot_dm.density(xyz) temp = sph_dens_bar.density( xyz) - eta_bar * dens_dm_orig * f_bar / (1. - f_bar) const_term = 0.41 * 0.53 * (eta_bar + 0.98)**(0.53 - 1.) * ( 1. - f_bar) / f_bar * temp dens_contracted = dens_dm_orig * first_factor + const_term # new values of DM density at the radial grid else: raise RuntimeError('Invalid choice of method') # create a cubic spline interpolator in log-log space valid_r = dens_contracted > 0 # make sure the input for log-spline is positive dens_contracted_interp = agama.CubicSpline(numpy.log(gridr[valid_r]), numpy.log( dens_contracted[valid_r]), 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
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')
# original DF using a C++ object df_orig = agama.DistributionFunction( \ type="DoublePowerLaw", J0=J0, slopeIn=0, slopeOut=Beta, norm=2*numpy.pi**3) # to compute the total mass, we create an proxy instance of DistributionFunction class # with a composite DF consisting of a single component (the user-supplied function); # this proxy class provides the totalMass() method # that the user-defined Python function itself does not have mass_orig = df_orig.totalMass() mass_user = agama.DistributionFunction(MyDF).totalMass() print("Integration in the 3d action space: DF mass=%.8g (orig value=%.8g)" % (mass_user, mass_orig)) # GalaxyModel objects constructed from the C++ DF and from the Python function # (without the need of a proxy object; in fact, GalaxyModel.df is itself a proxy object) gm_orig = agama.GalaxyModel(df=df_orig, potential=pot_orig) gm_user = agama.GalaxyModel(df=MyDF, potential=pot_appr) # note that different potentials were used for gm_orig and gm_user, so the results may slightly disagree mass_gm_orig = gm_orig.totalMass() mass_gm_user = gm_user.totalMass() print("Integration in the 6d phase space: DF mass=%.8g (orig value=%.8g)" % (mass_gm_user, mass_gm_orig)) # DF moments (density and velocity dispersion) computed from the C++ DF object dens_orig, veldisp_orig = gm_orig.moments(point=(1, 0, 0)) print("original DF at r=1: density=%.8g, sigma_r=%.8g, sigma_t=%.8g" % \ ( dens_orig, veldisp_orig[0]**0.5, veldisp_orig[1]**0.5 )) # DF moments computed from the Python distribution function dens_user, veldisp_user = gm_user.moments(point=(1, 0, 0)) print("user-def DF at r=1: density=%.8g, sigma_r=%.8g, sigma_t=%.8g" % \
# first create a representation of density profiles without velocities # (just for demonstration), by drawing samples from the density distribution print( "Writing N-body sampled density profile for the stellar bulge, disk and halo" ) # recall that component[0] contains stellar disks and stellar halo, and component[1] - bulge densStars = agama.Density(model.components[0].getDensity(), model.components[1].getDensity()) agama.writeSnapshot("dens_stars_final", densStars.sample(200000), format) print("Writing N-body sampled density profile for the dark matter halo") agama.writeSnapshot("dens_dm_final", model.components[2].getDensity().sample(800000), format) # now create genuinely self-consistent models of all components, # by drawing positions and velocities from the DF in the given (self-consistent) potential print("Writing a complete DF-based N-body model for the dark matter halo") agama.writeSnapshot("model_dm_final", \ agama.GalaxyModel(potential=model.potential, df=dfHalo, af=model.af).sample(800000), format) print( "Writing a complete DF-based N-body model for the stellar bulge, disk and halo" ) agama.writeSnapshot("model_stars_final", \ agama.GalaxyModel(potential=model.potential, df=dfStellarAll, af=model.af).sample(200000), format) # we didn't use an action-based DF for the gas disk, leaving it as a static component; # to create an N-body representation, we sample the density profile and assign velocities # from the axisymmetric Jeans equation with equal velocity dispersions in R,z,phi print("Writing an N-body model for the gas disk") agama.writeSnapshot("model_gas_final", \ model.components[3].getDensity().sample(24000, potential=model.potential, beta=0, kappa=1), format)
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()
float(iniSCMDisc['RminCyl']), float(iniSCMDisc['RmaxCyl'])), \ gridz=agama.nonuniformGrid(int(iniSCMDisc['sizeVerticalCyl']), \ float(iniSCMDisc['zminCyl']), float(iniSCMDisc['zmaxCyl'])) ) # do a few more iterations to obtain the self-consistent density profile for both discs for iteration in range(6, 11): print "Starting iteration #%d" % iteration model.iterate() printoutInfo(model, iteration) # export model to an N-body snapshot print "Creating an N-body representation of the model" # first create a representation of density profiles without velocities # (just for demonstration), by drawing samples from the density distribution print "Sampling halo density" writeNbodySnapshot("dens_halo_iter10", model.components[0].getDensity().sample(100000)) print "Sampling disc density" writeNbodySnapshot("dens_disc_iter10", model.components[1].getDensity().sample(100000)) # now create genuinely self-consistent models of both components, # by drawing positions and velocities from the DF in the given (self-consistent) potential print "Sampling halo DF" writeNbodySnapshot("model_halo_iter10", \ agama.GalaxyModel(pot=model.pot, df=dfHalo, af=model.af).sample(100000)) print "Sampling disc DF" writeNbodySnapshot("model_disc_iter10", \ agama.GalaxyModel(pot=model.pot, df=dfStellar, af=model.af).sample(100000))
format = 'text' # one could also use 'nemo' or 'gadget' here # first create a representation of density profiles without velocities # (just for demonstration), by drawing samples from the density distribution print("Sampling disk density") agama.writeSnapshot("dens_disk_final", model.components[0].getDensity().sample(160000), format) print("Sampling bulge density") agama.writeSnapshot("dens_bulge_final", model.components[1].getDensity().sample(40000), format) print("Sampling halo density") agama.writeSnapshot("dens_halo_final", model.components[2].getDensity().sample(800000), format) # now create genuinely self-consistent models of both components, # by drawing positions and velocities from the DF in the given (self-consistent) potential print("Sampling disk DF") agama.writeSnapshot("model_disk_final", \ agama.GalaxyModel(potential=model.potential, df=dfDisk, af=model.af).sample(1600000), format) print("Sampling bulge DF") agama.writeSnapshot("model_bulge_final", \ agama.GalaxyModel(potential=model.potential, df=dfBulge, af=model.af).sample(400000), format) print("Sampling halo DF") # 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"])
scm = agama.SelfConsistentModel(**params) scm.components=[comp] # prepare visualization r=numpy.logspace(-2.,2.) xyz=numpy.vstack((r,r*0,r*0)).T plt.plot(r, dens.density(xyz), label='Init density', color='k') # perform several iterations of self-consistent modelling procedure for i in range(6): scm.iterate() print('Iteration %i, Phi(0)=%g, Mass=%g' % \ (i, scm.potential.potential(0,0,0), scm.potential.totalMass())) plt.plot(r, scm.potential.density(xyz), label='Iteration #'+str(i)) # save the final density/potential profile and create an N-body snapshot comp.getDensity().export('flattened_sersic_density.ini') scm.potential.export('flattened_sersic_potential.ini') agama.writeSnapshot('flattened_sersic_nbody.nemo', agama.GalaxyModel(scm.potential, df).sample(1000000), 'nemo') # show the results plt.legend(loc='lower left') plt.xlabel("r") plt.ylabel(r'$\rho$') plt.xscale('log') plt.yscale('log') plt.ylim(1e-5, 2e2) plt.xlim(0.01, 10) plt.show()