def createModel(self, params): ''' create a model (potential and DF) specified by the given [scaled] parameters ''' potential = agama.Potential(type='Spheroid', densityNorm=10**params[0], scaleRadius=10**params[1], gamma=params[2], beta=params[3], alpha=params[4]) # first create an un-normalized DF dfparams = dict(type='DoublePowerLaw', slopeOut=params[self.numPotParams + 0], slopeIn=params[self.numPotParams + 1], steepness=params[self.numPotParams + 2], coefJrOut=params[self.numPotParams + 3], coefJzOut=(3 - params[self.numPotParams + 3]) / 2, coefJrIn=params[self.numPotParams + 4], coefJzIn=(3 - params[self.numPotParams + 4]) / 2, j0=10**params[self.numPotParams + 5], norm=1.) # compute its total mass totalMass = agama.DistributionFunction(**dfparams).totalMass() # and now normalize the DF to have a unit total mass dfparams["norm"] = 1. / totalMass df = agama.DistributionFunction(**dfparams) return potential, df
def test(Component, SelfConsistentModel): # test a two-component spherical model (to speed up things, do not use disky components); # the first component is an isotropic DF of a NFW halo with a cutoff, # and the second one represents a more concentrated baryonic component, # which will cause the adiabatic contraction of the halo # when both components are iterated to achieve equilibrium params_comp1 = dict(disklike=False, rminSph=0.01, rmaxSph=100.0, sizeRadialSph=21, lmaxAngularSph=0) params_comp2 = dict(disklike=False, rminSph=0.01, rmaxSph=10.0, sizeRadialSph=16, lmaxAngularSph=0) params_scm = dict(rminSph=0.005, rmaxSph=200, sizeRadialSph=25, lmaxAngularSph=0) potential_init = agama.Potential(type='spheroid', gamma=1, beta=3, mass=20.0, scaleRadius=5.0, outerCutoffRadius=40.0) df_comp1 = agama.DistributionFunction(type='quasispherical', density=potential_init, potential=potential_init) df_comp2 = agama.DistributionFunction(type='doublepowerlaw', norm=12.0, J0=1.0, coefJrIn=1., coefJzIn=1., coefJrOut=1., coefJzOut=1., slopeIn=1, slopeOut=6) model = SelfConsistentModel(**params_scm) model.components.append(Component(df=df_comp1, **params_comp1)) model.components.append(Component(df=df_comp2, **params_comp2)) model.potential = potential_init for it in range(5): model.iterate() #model.components[0].getDensity().export('comp1') #model.components[1].getDensity().export('comp2') return agama.GalaxyModel(model.potential, df_comp1).moments([1, 0.5, 0.3])
def createModel(self, params): ''' create a model (potential and DF) specified by the given [scaled] parameters ''' potential = agama.Potential(type='Spheroid', densityNorm=10**params[0], scaleRadius=10**params[1], gamma=params[2], beta=params[3], alpha=params[4]) density = agama.Density(type='Spheroid', scaleRadius=10**params[self.numPotParams + 0], gamma=params[self.numPotParams + 1], beta=params[self.numPotParams + 2], alpha=params[self.numPotParams + 3]) df = agama.DistributionFunction(type='QuasiSpherical', potential=potential, density=density, beta0=params[self.numPotParams + 4], r_a=10**params[self.numPotParams + 5]) # check if the DF is everywhere nonnegative j = numpy.logspace(-5, 10, 200) if any(df(numpy.column_stack((j, j * 0 + 1e-10, j * 0))) <= 0): raise ValueError("Bad DF") return potential, df
def createDF(self, params): # first create an un-normalized DF dfparams = dict(type='DoublePowerLaw', slopeOut=params[self.numPotParams + 0], slopeIn=params[self.numPotParams + 1], steepness=params[self.numPotParams + 2], coefJrOut=params[self.numPotParams + 3], coefJzOut=(3 - params[self.numPotParams + 3]) / 2, coefJrIn=params[self.numPotParams + 4], coefJzIn=(3 - params[self.numPotParams + 4]) / 2, j0=10**params[self.numPotParams + 5], norm=1.) # compute its total mass totalMass = agama.DistributionFunction(**dfparams).totalMass() # and now normalize the DF to have a unit total mass dfparams["norm"] = 1. / totalMass return agama.DistributionFunction(**dfparams)
def model_likelihood(params, points): print "J0=%6.5g, slopeIn=%6.5g, slopeOut=%6.5g, steepness=%6.5g, coefJrIn=%6.5g: " \ % (params['J0'], params['slopeIn'], params['slopeOut'], params['steepness'], params['coefJrIn']), try: dpl = agama.DistributionFunction(**params) norm = dpl.totalMass() sumlog = numpy.sum(numpy.log(dpl(points) / norm)) print "LogL=%.8g" % sumlog return sumlog except ValueError as err: print "Exception ", err return -1000. * len(points)
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
#!/usr/bin/python """ Create a simple single-component spherical self-consistent model determined by its distribution function in terms of actions. We use the true DF of the Plummer model, expressed in terms of actions, and start iterations from a deliberately wrong initial guess; nevertheless, after 10 iterations we converge to the true solution within 1%; each iteration approximately halves the error. """ import agama, numpy, matplotlib.pyplot as plt # the distribution function defining the model truepot = agama.Potential(type='Plummer') df = agama.DistributionFunction(type='QuasiSpherical', potential=truepot, density=truepot) # initial guess for the density profile - deliberately a wrong one dens = agama.Density(type='Dehnen', mass=0.1, scaleRadius=0.5) # define the self-consistent model consisting of a single component params = dict(rminSph=0.001, rmaxSph=1000., sizeRadialSph=40, lmaxAngularSph=0) comp = agama.Component(df=df, density=dens, disklike=False, **params) 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') plt.plot(r, truepot.density(xyz), label='True density', c='k')[0].set_dashes([4,4])
densityHalo = agama.Density(**iniPotenHalo) densityDisk = agama.Density(**iniPotenDisk) # add components to SCM - at first, all of them are static density profiles model.components.append(agama.Component(density=densityHalo, disklike=False)) model.components.append(agama.Component(density=densityBulge, disklike=False)) model.components.append(agama.Component(density=densityDisk, disklike=True)) # compute the initial potential model.iterate() writeRotationCurve("rotcurve_init", model.potential) # initialize the DFs of spheroidal components using the Eddington inversion formula # for their respective density profiles in the spherically-symmetric initial guess for the potential pot_sph = agama.Potential(type='Multipole', density=model.potential, lmax=0, gridsizer=100, rmin=1e-3, rmax=1e3) dfHalo = agama.DistributionFunction(type='PseudoIsotropic', potential=pot_sph, density=densityHalo) dfBulge = agama.DistributionFunction(type='PseudoIsotropic', potential=pot_sph, density=densityBulge) printoutInfo(model, 0) print "\033[1;33m**** STARTING ONE-COMPONENT MODELLING ****\033[0m\nMasses are: " \ "Mhalo=%g," % densityHalo.totalMass(), \ "Mbulge=%g," % densityBulge.totalMass(), \ "Mdisk=%g" % densityDisk.totalMass() # replace the halo and bulge SCM components with the DF-based ones model.components[0] = agama.Component(df=dfHalo, disklike=False, **iniSCMHalo) model.components[1] = agama.Component(df=dfBulge, disklike=False, **iniSCMBulge) # do one iteration to determine the self-consistent density profile of the halo and the bulge print "\033[1;37mStarting iteration #1\033[0m" model.iterate()
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) df2 = agama.DistributionFunction(df1, df0) # composite DF with two components gms1 = agama.GalaxyModel(pots, df1) # simple DF, spherical potential gms2 = agama.GalaxyModel(pots, df2) # composite DF (2 components), spherical gmf1 = agama.GalaxyModel(potf, df1) # simple, flattened Phi0 = pots.potential(0, 0, 0) # value of the potential at origin ### test the shapes of output arrays for various inputs ### # Density class methods testCond('isFloat(dens.density( 1,0,0 ))' ) # single point (3 coordinates) => output is a scalar value
model.components.append(agama.Component(dens=densityGasDisc, disklike=True)) # compute the initial potential model.iterate() writeRotationCurve("rotcurve_init", model.pot) print "**** STARTING ONE-COMPONENT MODELLING ****\nMasses are: " \ "Mbulge=%g Msun,"% densityBulge.totalMass(), \ "Mgas=%g Msun," % densityGasDisc.totalMass(), \ "Mdisc=%g Msun," % densityStellarDisc.totalMass(), \ "Mhalo=%g Msun" % densityDarkHalo.totalMass() # create the dark halo DF from the parameters in INI file; # here the initial potential is only used to create epicyclic frequency interpolation table dfHalo = agama.DistributionFunction(pot=model.pot, **iniDFDarkHalo) # replace the halo SCM component with the DF-based one model.components[0] = agama.Component(df=dfHalo, disklike=False, **iniSCMHalo) # do a few iterations to determine the self-consistent density profile of the halo for iteration in range(1, 6): print "Starting iteration #%d" % iteration model.iterate() printoutInfo(model, iteration) # now that we have a reasonable guess for the total potential, # we may initialize the DF of the stellar components dfThinDisc = agama.DistributionFunction(pot=model.pot, **iniDFThinDisc)
J0 = 1e3, slopeIn = 1.5, slopeOut = 1.5, steepness= 1.0, coefJrIn = 0.8, coefJzIn = 1.7, coefJrOut= 0.8, coefJzOut= 1.7, Jcutoff = 0.56, cutoffstrength=1.5, rotFrac = 1.0, # make a rotating model (just for fun) Jphi0 = 0., # size of non-(or weakly-)rotating core norm = 1.0) # compute the mass and rescale norm to get the total mass = 1 params['norm'] /= agama.DistributionFunction(**params).totalMass() df = agama.DistributionFunction(**params) # initial guess for the density profile dens = agama.Potential(type='sersic', mass=1, scaleRadius=0.65, sersicindex=2) # define the self-consistent model consisting of a single component params = dict(rminSph=0.01, rmaxSph=10., sizeRadialSph=25, lmaxAngularSph=8) comp = agama.Component(df=df, density=dens, disklike=False, **params) 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')
#!/usr/bin/python """ Create a simple single-component spherical self-consistent model determined by its distribution function in terms of actions """ import agama, numpy, matplotlib.pyplot as plt # the distribution function defining the model df = agama.DistributionFunction(type="DoublePowerLaw", J0=1, slopeIn=0, slopeOut=6, norm=30) mass = df.totalMass() scaleradius = 2. # educated guess # initial guess for the density profile dens = agama.Density(type='Plummer', mass=mass, scaleRadius=scaleradius) print "DF mass=", mass, ' Density mass=', dens.totalMass() # should be identical # define the self-consistent model consisting of a single component params = dict(rminSph=0.01, rmaxSph=100., sizeRadialSph=25, lmaxAngularSph=0) comp = agama.Component(df=df, density=dens, disklike=False, **params) 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') # perform several iterations of self-consistent modelling procedure for i in range(5): scm.iterate()
model.components.append(agama.Component(density=densityStellarDisk, disklike=True)) model.components.append(agama.Component(density=densityBulge, disklike=False)) model.components.append(agama.Component(density=densityDarkHalo, disklike=False)) model.components.append(agama.Component(density=densityGasDisk, disklike=True)) # compute the initial potential model.iterate() printoutInfo(model, "init") print("\033[1;33m**** STARTING MODELLING ****\033[0m\nInitial masses of density components: " \ "Mdisk=%g Msun, Mbulge=%g Msun, Mhalo=%g Msun, Mgas=%g Msun" % \ (densityStellarDisk.totalMass(), densityBulge.totalMass(), \ densityDarkHalo.totalMass(), densityGasDisk.totalMass())) # create the dark halo DF dfHalo = agama.DistributionFunction(potential=model.potential, **iniDFDarkHalo) # same for the bulge dfBulge = agama.DistributionFunction(potential=model.potential, **iniDFBulge) # same for the stellar components (thin/thick disks and stellar halo) dfThinDisk = agama.DistributionFunction(potential=model.potential, **iniDFThinDisk) dfThickDisk = agama.DistributionFunction(potential=model.potential, **iniDFThickDisk) dfStellarHalo = agama.DistributionFunction(potential=model.potential, **iniDFStellarHalo) # composite DF of all stellar components except the bulge dfStellar = agama.DistributionFunction(dfThinDisk, dfThickDisk, dfStellarHalo) # composite DF of all stellar components including the bulge dfStellarAll = agama.DistributionFunction(dfThinDisk, dfThickDisk, dfStellarHalo, dfBulge) # replace the disk, halo and bulge SCM components with the DF-based ones model.components[0] = agama.Component(df=dfStellar, disklike=True, **iniSCMDisk) model.components[1] = agama.Component(df=dfBulge, disklike=False, **iniSCMBulge) model.components[2] = agama.Component(df=dfHalo, disklike=False, **iniSCMHalo)
( pot_appr.density(1,0,0), pot_orig.density(1,0,0), MyPlummer(numpy.array([[1,0,0]])) ) # user-defined distribution function, again it must be a function of a single argument -- # a 2d array Mx3, where the columns are Jr,Jz,Jphi, and rows are M independent points # in action space where the function should be evaluated simultaneously. # Here, instead of creating a lambda function, we fix the internal constants at the outset. J0 = 2.0 # constants -- parameters of the DF Beta = 5.0 def MyDF(J): return (J[:, 0] + J[:, 1] + abs(J[:, 2]) + J0)**-Beta # 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
#!/usr/bin/python """ Create a simple single-component spherical self-consistent model determined by its distribution function in terms of actions. We use the true DF of the Plummer model, expressed in terms of actions, and start iterations from a deliberately wrong initial guess; nevertheless, after 10 iterations we converge to the true solution within 1%; each iteration approximately halves the error. """ import agama, numpy, matplotlib.pyplot as plt # the distribution function defining the model truepot = agama.Potential(type='Plummer') df = agama.DistributionFunction(type='QuasiIsotropic', potential=truepot, density=truepot) # initial guess for the density profile - deliberately a wrong one dens = agama.Density(type='Dehnen', mass=0.1, scaleRadius=0.5) # define the self-consistent model consisting of a single component params = dict(rminSph=0.001, rmaxSph=1000., sizeRadialSph=40, lmaxAngularSph=0) comp = agama.Component(df=df, density=dens, disklike=False, **params) 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') plt.plot(r, truepot.density(xyz), label='True density',
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')
model.iterate() plotVcirc(model, 0) # introduce DF for the NSD component mass = 0.097 Rdisk = 0.075 Hdisk = 0.025 sigmar0 = 75.0 Rsigmar = 1.0 sigmamin = 2.0 Jmin = 10.0 dfNSD = agama.DistributionFunction(potential=model.potential, type='QuasiIsothermal', mass=mass, Rdisk=Rdisk, Hdisk=Hdisk, sigmar0=sigmar0, Rsigmar=Rsigmar, sigmamin=sigmamin, Jmin=Jmin) # replace the static density of the NSD by a DF-based component model.components[1] = agama.Component(df=dfNSD, disklike=True, RminCyl=0.005, RmaxCyl=0.75, sizeRadialCyl=20, zminCyl=0.005, zmaxCyl=0.25, sizeVerticalCyl=15)
densityBulge = agama.Density(**iniPotenBulge) densityHalo = agama.Density(**iniPotenHalo) potentialBH = agama.Potential(**iniPotenBH) # add components to SCM - at first, all of them are static density profiles model.components.append(agama.Component(density=densityDisk, disklike=True)) model.components.append(agama.Component(density=densityBulge, disklike=False)) model.components.append(agama.Component(density=densityHalo, disklike=False)) model.components.append(agama.Component(potential=potentialBH)) # compute the initial potential model.iterate() printoutInfo(model,'init') # construct the DF of the disk component, using the initial (non-spherical) potential dfDisk = agama.DistributionFunction(potential=model.potential, **iniDFDisk) # initialize the DFs of spheroidal components using the Eddington inversion formula # for their respective density profiles in the initial potential dfBulge = agama.DistributionFunction(type='QuasiSpherical', potential=model.potential, density=densityBulge) dfHalo = agama.DistributionFunction(type='QuasiSpherical', potential=model.potential, density=densityHalo) print("\033[1;33m**** STARTING ITERATIVE MODELLING ****\033[0m\nMasses (computed from DF): " \ "Mdisk=%g, Mbulge=%g, Mhalo=%g" % (dfDisk.totalMass(), dfBulge.totalMass(), dfHalo.totalMass())) # replace the initially static SCM components with the DF-based ones model.components[0] = agama.Component(df=dfDisk, disklike=True, **iniSCMDisk) model.components[1] = agama.Component(df=dfBulge, disklike=False, **iniSCMBulge) model.components[2] = agama.Component(df=dfHalo, disklike=False, **iniSCMHalo) # do a few more iterations to obtain the self-consistent density profile for both disks for iteration in range(1,5):
hdisk_thick = 1.0 Rsigmar = Rd/q sigmar0_thin_binney = 27. sigmar0_thick_binney = 48. sigmar0_thin = sigmar0_thin_binney * np.exp(R0/Rsigmar) sigmar0_thick = sigmar0_thick_binney * np.exp(R0/Rsigmar) sigmamin_thin = 0.05*sigmar0_thin sigmamin_thick = 0.05*sigmar0_thick Jmin = 0.05*J0 df_thin = agama.DistributionFunction(type='QuasiIsothermal', potential=mwpot, coefJr=coefJr, coefJz=coefJz, Sigma0=Sigma0_thin, Rdisk=Rd, Hdisk=hdisk_thin, sigmar0=sigmar0_thin, Rsigmar=Rsigmar, sigmamin=sigmamin_thin, Jmin=Jmin) df_thick = agama.DistributionFunction(type='QuasiIsothermal', potential=mwpot, coefJr=coefJr, coefJz=coefJz, Sigma0=Sigma0_thick, Rdisk=Rd, Hdisk=hdisk_thick, sigmar0=sigmar0_thick, Rsigmar=Rsigmar, sigmamin=sigmamin_thick, Jmin=Jmin) df = agama.DistributionFunction(df_thin, df_thick) # df = df_thin gm = agama.GalaxyModel(mwpot, df) try: f = h5.File('posvel_mass.h5', 'r') posvel = np.array(f['posvel']) mass = np.array(f['mass']) f.close()