Beispiel #1
0
def rewind_zvz(z_init, vz_init, mass, omega, step):
    grewind = wendypy.nbody(z_init,
                            vz_init,
                            mass,
                            -step,
                            omega=omega,
                            approx=True,
                            nleap=1)
    z_rewind, vz_rewind = next(grewind)

    return (z_rewind, vz_rewind)
Beispiel #2
0
def forward_nstep_zvz(z_init, vz_init, mass, omega, step, nstep):
    gforward = wendypy.nbody(z_init,
                             vz_init,
                             mass,
                             -step,
                             omega=omega,
                             approx=True,
                             nleap=1)
    for ii in range(nstep):
        z_forward, vz_forward = next(gforward)

    return (z_forward, vz_forward)
Beispiel #3
0
def rewind_nstep_zvz(z_init, vz_init, mass, omega, step, nstep):
    grewind = wendypy.nbody(z_init,
                            vz_init,
                            mass,
                            -step,
                            omega=omega,
                            approx=True,
                            nleap=1)
    for ii in range(nstep):
        z_rewind, vz_rewind = next(grewind)

    return (z_rewind, vz_rewind)
Beispiel #4
0
def zvzdiff(z_init, vz_init, mass, omega1, omega2, step):
    # (temporary?) way to deal with small masses
    relevant_particles_index = mass > (numpy.median(mass[mass > 10.**-9.]) *
                                       10.**-6.)
    if numpy.any(mass[relevant_particles_index] < (
            10.**-8. * numpy.median(mass[relevant_particles_index]))):
        print(
            numpy.sum(mass[relevant_particles_index] < (
                10.**-8. * numpy.median(mass[relevant_particles_index]))))

    # integrate with wendy
    g1 = wendypy.nbody(z_init[relevant_particles_index],
                       vz_init[relevant_particles_index],
                       mass[relevant_particles_index],
                       step,
                       omega=omega1,
                       approx=True,
                       nleap=1)
    z_next1, vz_next1 = next(g1)
    dz1 = numpy.zeros_like(z_init)
    dvz1 = numpy.zeros_like(z_init)
    dz1[relevant_particles_index] = z_next1 - z_init[relevant_particles_index]
    dvz1[relevant_particles_index] = vz_next1 - vz_init[
        relevant_particles_index]
    g2 = wendypy.nbody(z_init[relevant_particles_index],
                       vz_init[relevant_particles_index],
                       mass[relevant_particles_index],
                       step,
                       omega=omega2,
                       approx=True,
                       nleap=1)
    z_next2, vz_next2 = next(g2)
    dz2 = numpy.zeros_like(z_init)
    dvz2 = numpy.zeros_like(z_init)
    dz2[relevant_particles_index] = z_next2 - z_init[relevant_particles_index]
    dvz2[relevant_particles_index] = vz_next2 - vz_init[
        relevant_particles_index]

    return (dz2 - dz1, dvz2 - dvz1)
Beispiel #5
0
def fit_m2m(w_init,
            z_init,
            vz_init,
            omega_m2m,
            zsun_m2m,
            data_dicts,
            step=0.001,
            nstep=1000,
            eps=0.1,
            mu=1.,
            prior='entropy',
            w_prior=None,
            kernel=hom2m.epanechnikov_kernel,
            kernel_deriv=hom2m.epanechnikov_kernel_deriv,
            h_m2m=0.02,
            npop=1,
            smooth=None,
            st96smooth=False,
            output_wevolution=False,
            output_zvzevolution=False,
            fit_zsun=False,
            fit_omega=False,
            skipomega=10,
            delta_omega_frac=0.1,
            number_density=False,
            xnm_m2m=1.0,
            skipxnm=10,
            fit_xnm=False):
    """
    NAME:
       fit_m2m
    PURPOSE:
       Run M2M optimization for wendy M2M
    INPUT:
       w_init - initial weights [N] or [N,npop]
       z_init - initial z [N]
       vz_init - initial vz (rad) [N]
       omega_m2m - background potential parameter omega, if None no background
       zsun_m2m - Sun's height above the plane [N]
       data_dicts - list of dictionaries that hold the data, these are described in more detail below
       step= stepsize of orbit integration
       nstep= number of steps to integrate the orbits for
       eps= M2M epsilon parameter (can be array when fitting zsun, omega; in that case eps[0] = eps_weights, eps[1] = eps_zsun, eps[1 or 2 based on fit_zsun] = eps_omega)
       mu= M2M entropy parameter mu
       prior= ('entropy' or 'gamma')
       w_prior= (None) prior weights (if None, equal to w_init)
       fit_zsun= (False) if True, also optimize zsun
       fit_omega= (False) if True, also optimize omega
       skipomega= only update omega every skipomega steps
       delta_omega_frac= (0.1) fractional difference in omega to use to compute derivative of objective function wrt omega
       kernel= a smoothing kernel
       kernel_deriv= the derivative of the smoothing kernel
       h_m2m= kernel size parameter for computing the observables
       npop= (1) number of theoretical populations
       smooth= smoothing parameter alpha (None for no smoothing)
       st96smooth= (False) if True, smooth the constraints (Syer & Tremaine 1996), if False, smooth the objective function and its derivative (Dehnen 2000)
       output_wevolution= if set to an integer, return the time evolution of this many randomly selected weights
       output_zvzevolution= if set to an integer, return the time evolution
                of this many randomly selected weights only when
                output_wevolution is True
       number_density = (False) if True, observed density is number density and density calculation requires xnm
       xnm_m2m = (1.0) initial value of xnm: number_density/mass_density [1], assuming a single population
       skipxnm = only update Xnm every skipxnm steps
       fit_xnm = (False) if True, also optimise xnm
    DATA DICTIONARIES:
       The data dictionaries have the following form:
           'type': type of measurement: 'dens', 'v2'
           'pops': the theoretical populations included in this measurement; 
                   single number or list
           'zobs': vertical height of the observation
           'zrange': width of vertical bin relative to some fiducial value (used to scale h_m2m, which should therefore be appropriate for the fiducial value)
           'obs': the actual observation
           'unc': the uncertainty in the observation
       of these, zobs, obs, and unc can be arrays for mulitple measurements
    OUTPUT:
       (w_out,[zsun_out, [omega_out, [xnm_out]],z_m2m,vz_m2m,Q_out,[wevol,rndindx]) - 
              (output weights [N],
              [Solar offset [nstep] optional],
              [omega [nstep] optional when fit_omega],
              z_m2m [N] final z,
              vz_m2m [N] final vz,
              objective function as a function of time [nstep],
              [weight evolution for randomly selected weights,index of random weights])
    HISTORY:
       2017-07-20 - Started from hom2m.fit_m2m - Bovy (UofT)
       2018-10-16 - add external potential using omega_m2m - Kawata (MSSL/UCL)
       2018-10-29 - add xnm=number_ensity/mass_density - Kawata (MSSL/UCL)
    """
    if len(w_init.shape) == 1:
        w_out = numpy.empty((len(w_init), npop))
        w_out[:, :] = numpy.tile(copy.deepcopy(w_init), (npop, 1)).T
    else:
        w_out = copy.deepcopy(w_init)
    zsun_out = numpy.empty(nstep)
    omega_out = numpy.empty(nstep)
    if number_density:
        xnm_out = numpy.empty(nstep)
    else:
        xnm_out = numpy.ones(nstep)
        xnm_m2m = 1.0
    if w_prior is None:
        if len(w_init.shape) == 1:
            w_prior = numpy.empty((len(w_init), npop))
            w_prior[:, :] = numpy.tile(copy.deepcopy(w_init), (npop, 1)).T
        else:
            w_piror = copy.deepcopy(w_init)
    else:
        if len(w_prior.shape) == 1:
            w_prior = numpy.tile(w_prior, (npop, 1)).T
    # Parse data_dict
    data_dict = parse_data_dict(data_dicts)
    # Parse eps
    if isinstance(eps, float):
        eps = [eps]
        if fit_zsun: eps.append(eps[0])
        if fit_omega: eps.append(eps[0])
        if fit_xnm: eps.append(eps[0])
    Q_out = []
    if output_wevolution:
        rndindx = numpy.random.permutation(len(w_out))[:output_wevolution]
        wevol = numpy.zeros((output_wevolution, npop, nstep))
        if output_zvzevolution:
            zevol = numpy.zeros((output_wevolution, nstep))
            vzevol = numpy.zeros((output_wevolution, nstep))
    # Compute force of change for first iteration
    fcw, delta_m2m_new = \
        force_of_change_weights(w_out,zsun_m2m,z_init,vz_init,
                                data_dicts,prior,mu,w_prior,
                                h_m2m=h_m2m,kernel=kernel,
                                xnm_m2m=xnm_m2m)
    fcw *= w_out
    fcz = 0.
    if fit_zsun:
        fcz = force_of_change_zsun(w_init,
                                   zsun_m2m,
                                   z_init,
                                   vz_init,
                                   z_obs,
                                   dens_obs_noise,
                                   delta_m2m_new,
                                   densv2_obs_noise,
                                   deltav2_m2m_new,
                                   kernel=kernel,
                                   kernel_deriv=kernel_deriv,
                                   h_m2m=h_m2m)
    fcxnm = 0.0
    if fit_xnm:
        fcxnm = force_of_change_xnm(w_out,
                                    zsun_m2m,
                                    z_init,
                                    vz_init,
                                    data_dicts,
                                    delta_m2m_new,
                                    h_m2m=h_m2m,
                                    kernel=kernel,
                                    xnm_m2m=xnm_m2m)
    fco = 0.0
    # Rewind for first step
    mass = numpy.sum(w_out, axis=1)
    z_prev, vz_prev = rewind_zvz(z_init, vz_init, mass, omega_m2m, step)
    if fit_omega:
        fco = force_of_change_omega(w_out,
                                    zsun_m2m,
                                    omega_m2m,
                                    z_init,
                                    vz_init,
                                    z_prev,
                                    vz_prev,
                                    step,
                                    data_dicts,
                                    delta_m2m_new,
                                    h_m2m=h_m2m,
                                    kernel=kernel,
                                    delta_omega_frac=delta_omega_frac,
                                    xnm_m2m=xnm_m2m)
    if not smooth is None:
        delta_m2m = delta_m2m_new
    else:
        delta_m2m = [None for d in data_dicts]
    if not smooth is None and not st96smooth:
        Q = [d**2 for d in delta_m2m**2.]
    # setup skipomega omega counter and prev. (z,vz) for F(omega)
    # xcounter= skipxnm-1 # Causes F(Xnm) to be computed in the 1st step
    # ocounter= skipomega-1 # Causes F(omega) to be computed in the 1st step
    # no update for omega or xnm at the first step.
    xcounter = 0
    ocounter = 0
    z_m2m, vz_m2m = z_init, vz_init
    for ii in range(nstep):
        # Update weights first
        if True:
            w_out += eps[0] * step * fcw
            w_out[w_out < 10.**-16.] = 10.**-16.
        # then zsun
        if fit_zsun:
            zsun_m2m += eps[1] * step * fcz
            zsun_out[ii] = zsun_m2m
        # then xnm
        if fit_xnm and xcounter == skipxnm:
            # assume single population and use fcxnm[0] only
            dxnm = eps[1 + fit_zsun + fit_omega] * step * fcxnm[0]
            # print(' xnm_m2m, dxnm=',xnm_m2m,dxnm)
            max_dxnm = xnm_m2m / 10.0
            if numpy.fabs(dxnm) > max_dxnm:
                dxnm = max_dxnm * numpy.sign(dxnm)
            xnm_m2m += dxnm
            # print(ii,' step Xnm=',xnm_m2m, eps[1+fit_zsun+fit_omega])
            xcounter = 0
        # then omega (skipped in the first step, so undeclared vars okay)
        if fit_omega and ocounter == skipomega:
            domega = eps[1 + fit_zsun] * step * fco
            # print(' omega_m2m, dom=',omega_m2m,domega, numpy.sum(delta_m2m_new, axis=1))
            # max_domega= delta_omega/30.
            max_domega = omega_m2m / 10.0
            if numpy.fabs(domega) > max_domega:
                domega = max_domega * numpy.sign(domega)
            omega_m2m += domega
            # Keep (z,vz) the same in new potential
            # A_now, phi_now= zvz_to_Aphi(z_m2m,vz_m2m,omega_m2m)
            ocounter = 0
        # (Store objective function)
        if not smooth is None and st96smooth:
            Q_out.append([d**2. for d in delta_m2m])
        elif not smooth is None:
            Q_out.append(copy.deepcopy(Q))
        else:
            Q_out.append(
                [d**2. for d in list(chain.from_iterable(delta_m2m_new))])
        # Then update the dynamics
        mass = numpy.sum(w_out, axis=1)
        # (temporary?) way to deal with small masses
        relevant_particles_index = mass > (
            numpy.median(mass[mass > 10.**-9.]) * 10.**-6.)
        if numpy.any(mass[relevant_particles_index] < (
                10.**-8. * numpy.median(mass[relevant_particles_index]))):
            print(
                numpy.sum(mass[relevant_particles_index] < (
                    10.**-8. * numpy.median(mass[relevant_particles_index]))))
        # g= wendypy.nbody(z_m2m[relevant_particles_index],
        #               vz_m2m[relevant_particles_index],
        #               mass[relevant_particles_index],
        #               step, omega=omega_m2m, maxcoll=10000000)
        g = wendypy.nbody(z_m2m[relevant_particles_index],
                          vz_m2m[relevant_particles_index],
                          mass[relevant_particles_index],
                          step,
                          omega=omega_m2m,
                          approx=True,
                          nleap=1)
        tz_m2m, tvz_m2m = next(g)
        z_m2m[relevant_particles_index] = tz_m2m
        vz_m2m[relevant_particles_index] = tvz_m2m
        z_m2m -= numpy.sum(mass * z_m2m) / numpy.sum(mass)
        vz_m2m -= numpy.sum(mass * vz_m2m) / numpy.sum(mass)
        # Compute force of change
        if smooth is None or not st96smooth:
            # Turn these off
            tdelta_m2m = None
        else:
            tdelta_m2m = delta_m2m
        fcw_new, delta_m2m_new = \
            force_of_change_weights(w_out,zsun_m2m,z_m2m,vz_m2m,
                                    data_dicts,prior,mu,w_prior,
                                    h_m2m=h_m2m,kernel=kernel,
                                    delta_m2m=tdelta_m2m, xnm_m2m=xnm_m2m)
        fcw_new *= w_out
        if fit_zsun:
            if smooth is None or not st96smooth:
                tdelta_m2m = delta_m2m_new
            fcz_new = force_of_change_zsun(w_out,
                                           zsun_m2m,
                                           z_m2m,
                                           vz_m2m,
                                           data_dicts,
                                           tdelta_m2m,
                                           kernel=kernel,
                                           kernel_deriv=kernel_deriv,
                                           h_m2m=h_m2m)
        if fit_xnm:
            # Update Xnm in this step?
            xnm_out[ii] = xnm_m2m
            xcounter += 1
            if xcounter == skipxnm:
                if smooth is None or not st96smooth:
                    tdelta_m2m = delta_m2m_new
                fcxnm_new = force_of_change_xnm(w_out,
                                                zsun_m2m,
                                                z_m2m,
                                                vz_m2m,
                                                data_dicts,
                                                tdelta_m2m,
                                                h_m2m=h_m2m,
                                                kernel=kernel,
                                                xnm_m2m=xnm_m2m)
        if fit_omega:
            omega_out[ii] = omega_m2m
            # Update omega in this step?
            ocounter += 1
            if ocounter == skipomega:
                if not fit_zsun and (smooth is None or not st96smooth):
                    tdelta_m2m = delta_m2m_new
                    # tdeltav2_m2m= deltav2_m2m_new
                # use step to evaluate gradient of omega
                fco_new = force_of_change_omega(
                    w_out,
                    zsun_m2m,
                    omega_m2m,
                    z_m2m,
                    vz_m2m,
                    z_prev,
                    vz_prev,
                    step,
                    data_dicts,
                    tdelta_m2m,
                    h_m2m=h_m2m,
                    kernel=kernel,
                    delta_omega_frac=delta_omega_frac,
                    xnm_m2m=xnm_m2m)
            # store previous position and velocity every step.
            z_prev = copy.copy(z_m2m)
            vz_prev = copy.copy(vz_m2m)
        # Increment smoothing
        if not smooth is None and st96smooth:
            delta_m2m = [
                d + step * smooth * (dn - d)
                for d, dn in zip(list(chain.from_iterable(delta_m2m)),
                                 list(chain.from_iterable(delta_m2m_new)))
            ]
            fcw = fcw_new
            if fit_zsun: fcz = fcz_new
            if fit_omega and ocounter == skipomega: fco = fco_new
            if fit_xnm and xcounter == skipxnm: fcxnm = fcxnm_new
        elif not smooth is None:
            Q_new = [d**2. for d in delta_m2m_new]
            Q = [q + step * smooth * (qn - q) for q, qn in zip(Q, Q_new)]
            fcw += step * smooth * (fcw_new - fcw)
            if fit_zsun: fcz += step * smooth * (fcz_new - fcz)
            if fit_xnm and xcounter == skipxnm:
                fcxnm += step * smooth * (fcxnm - fcxnm_new)
            if fit_omega and ocounter == skipomega:
                fco += step * smooth * (fco_new - fco)
        else:
            fcw = fcw_new
            if fit_zsun: fcz = fcz_new
            if fit_xnm and xcounter == skipxnm: fcxnm = fcxnm_new
            if fit_omega and ocounter == skipomega: fco = fco_new
        # Record random weights if requested
        if output_wevolution:
            wevol[:, :, ii] = w_out[rndindx]
            if output_zvzevolution:
                zevol[:, ii] = z_m2m[rndindx]
                vzevol[:, ii] = vz_m2m[rndindx]
    out = (w_out, )
    if fit_zsun: out = out + (zsun_out, )
    if fit_omega:
        out = out + (omega_out, )
    if fit_xnm:
        out = out + (xnm_out, )
    out = out + (
        z_m2m,
        vz_m2m,
    )
    out = out + (numpy.array(Q_out), )
    if output_wevolution:
        out = out + (
            wevol,
            rndindx,
        )
        if output_zvzevolution:
            out = out + (zevol, )
            out = out + (vzevol, )
    return out
Beispiel #6
0
def force_of_change_omega(w_m2m,
                          zsun_m2m,
                          omega_m2m,
                          z_m2m,
                          vz_m2m,
                          z_prev,
                          vz_prev,
                          step,
                          data_dicts,
                          delta_m2m,
                          h_m2m=0.02,
                          kernel=hom2m.epanechnikov_kernel,
                          delta_omega_frac=0.1,
                          xnm_m2m=1.0):
    """Compute the force of change by direct finite difference 
    of the objective function"""
    mass = numpy.sum(w_m2m, axis=1)
    # delta omega to evaluate the gradient
    delta_omega = omega_m2m * delta_omega_frac
    # difference in dz and dvz due to the different omega
    # dz, dvz = zvzdiff(
    #  z_prev, vz_prev, mass, omega_m2m, omega_m2m+delta_omega, step)

    # fcw, delta_m2m_do = force_of_change_weights(\
    #    w_m2m,zsun_m2m,z_m2m+dz,vz_m2m+dvz,
    #    data_dicts,
    #    'entropy',0.,1., # weights prior doesn't matter, so set to zero
    #    h_m2m=h_m2m,kernel=kernel,
    #    delta_m2m=delta_m2m, xnm_m2m=xnm_m2m)

    # return -numpy.nansum(\
    #    delta_m2m[0]*(delta_m2m_do[0]-delta_m2m[0])
    #    +delta_m2m[1]*(delta_m2m_do[1]-delta_m2m[1]))\
    #    /delta_omega

    # more directly computing derivative, with no mass limit
    omega1 = omega_m2m
    omega2 = omega_m2m + delta_omega
    # integrate with wendy with omega1
    g1 = wendypy.nbody(z_prev, vz_prev, mass, step, omega=omega1, \
                       approx=True, nleap=1)
    z_next1, vz_next1 = next(g1)
    # evaluate delta
    fcw, delta_m2m_do1 = force_of_change_weights(\
        w_m2m,zsun_m2m,z_next1,vz_next1,
        data_dicts,
        'entropy',0.,1., # weights prior doesn't matter, so set to zero
        h_m2m=h_m2m,kernel=kernel,
        delta_m2m=delta_m2m, xnm_m2m=xnm_m2m)
    # integrate with omega2
    g2 = wendypy.nbody(z_prev, vz_prev, mass, step, omega=omega2, \
                       approx=True, nleap=1)
    z_next2, vz_next2 = next(g2)
    fcw, delta_m2m_do2 = force_of_change_weights(\
        w_m2m,zsun_m2m,z_next2,vz_next2,
        data_dicts,
        'entropy',0.,1., # weights prior doesn't matter, so set to zero
        h_m2m=h_m2m,kernel=kernel,
        delta_m2m=delta_m2m, xnm_m2m=xnm_m2m)

    return -numpy.nansum(\
        delta_m2m_do1[0]*(delta_m2m_do2[0]-delta_m2m_do1[0])
        +delta_m2m_do1[1]*(delta_m2m_do2[1]-delta_m2m_do2[1]))\
        /delta_omega
h_def = 0.1  # default h
print(' disk parameters, np, sigma, Mtot, omega, zsun, Xnm=', \
      n_init, sigma_true, totmass_true, omegadm_true, zsun_true, xnm_true)
print(' smoothing length (default) =', h_def)

n_init0 = n_init
zh_true = sigma_true**2. / totmass_true  # Where 2\pi G= 1 so units of zh are ~311 pc
tdyn = zh_true / sigma_true
z_init, vz_init, m_init = wendym2m.sample_sech2(sigma_true,
                                                totmass_true,
                                                n=n_init)
print('zh, tdyn, omega_dm =', zh_true, tdyn, omegadm_true)

# run Wendy to introduce the background potential adiabatically
print(' running Wendy to add the background potential adiabatically')
g = wendypy.nbody(z_init, vz_init, m_init, dttdyn * tdyn, approx=True, nleap=1)
nt = 4000
zt = numpy.empty((n_init, nt + 1))
vzt = numpy.empty((n_init, nt + 1))
# Et = numpy.empty((nt+1))
zt[:, 0] = z_init
vzt[:, 0] = vz_init
# Et[0] = wendy.energy(z_init, vz_init, m_init)
# increasing omega
nstep_omega = 1000
if nstep_omega > nt:
    print(' Error nstep_omega=', nstep_omega, ' should be larger than nt=', nt)
    sys.exit()
domega = omegadm_true / nstep_omega
omega_ii = 0.0
tz = z_init