Example #1
0
 def __setitem__(self, key, values):
     try:
         super(Lattice, self).__setitem__(key, values)
     except TypeError:
         key = uint32_refpts(key, len(self))
         for i, v in zip(*numpy.broadcast_arrays(key, values)):
             super(Lattice, self).__setitem__(i, v)
Example #2
0
 def __delitem__(self, key):
     try:
         super(Lattice, self).__delitem__(key)
     except TypeError:
         key = uint32_refpts(key, len(self))
         for i in reversed(key):
             super(Lattice, self).__delitem__(i)
Example #3
0
def find_m44(ring, dp=0.0, refpts=None, orbit4=None, XYStep=XYDEFSTEP):
    """
    Determine the transfer matrix for ring, by first finding the closed orbit.
    """
    refpts = lattice.uint32_refpts(refpts, len(ring))
    nrefs = refpts.size
    if refpts[-1] != len(ring):
        refpts = numpy.append(refpts, [len(ring)])
    keeplattice = False
    if orbit4 is None:
        orbit4, _ = find_orbit4(ring, dp)
        keeplattice = True
    # Append zeros to closed 4-orbit.
    orbit6 = numpy.append(orbit4, (dp, 0.0)).reshape(6, 1)
    # Construct matrix of plus and minus deltas
    dmat = numpy.zeros((6, 8), order='F')
    for i in range(4):
        dmat[i, i] = 0.5 * XYStep
        dmat[i, i + 4] = -0.5 * XYStep
    # Add the deltas to multiple copies of the closed orbit
    in_mat = orbit6 + dmat

    out_mat = at.lattice_pass(ring,
                              in_mat,
                              refpts=refpts,
                              keep_lattice=keeplattice)
    # out_mat: 8 particles at n refpts for one turn
    tmat3 = out_mat[:4, :, :, 0]
    # (x + d) - (x - d) / d
    mstack = (tmat3[:, :4, :] - tmat3[:, 4:8, :]) / XYStep
    m44 = mstack[:, :, -1]
    return m44, mstack[:, :, :nrefs]
Example #4
0
def _dmatr(ring, orbit=None, keep_lattice=False):
    """
    compute the cumulative diffusion and orbit
    matrices over the ring
    """
    nelems = len(ring)
    energy = ring.energy
    allrefs = uint32_refpts(range(nelems + 1), nelems)

    if orbit is None:
        orbit, _ = find_orbit6(ring, keep_lattice=keep_lattice)
        keep_lattice = True

    orbs = numpy.squeeze(lattice_pass(ring,
                                      orbit.copy(order='K'),
                                      refpts=allrefs,
                                      keep_lattice=keep_lattice),
                         axis=(1, 3)).T
    b0 = numpy.zeros((6, 6))
    bb = [
        find_mpole_raddiff_matrix(elem, elemorb, energy)
        if elem.PassMethod.endswith('RadPass') else b0
        for elem, elemorb in zip(ring, orbs)
    ]
    bbcum = numpy.stack(list(_cumulb(zip(ring, orbs, bb))), axis=0)
    return bbcum, orbs
Example #5
0
def patpass(ring, r_in, nturns, refpts=None, reuse=True, pool_size=None):
    """
    Simple parallel implementation of atpass().  If more than one particle
    is supplied, use multiprocessing to run each particle in a separate
    process.

    INPUT:
        ring            lattice description
        r_in:           6xN array: input coordinates of N particles
        nturns:         number of passes through the lattice line
        refpts          elements at which data is returned. It can be:
                        1) an integer in the range [-len(ring), len(ring)-1]
                           selecting the element according to python indexing
                           rules. As a special case, len(ring) is allowed and
                           refers to the end of the last element,
                        2) an ordered list of such integers without duplicates,
                        3) a numpy array of booleans of maximum length
                           len(ring)+1, where selected elements are True.
                        Defaults to None, meaning no refpts, equivelent to
                        passing an empty array for calculation purposes.
    """
    if not reuse:
        raise ValueError('patpass does not support altering lattices')
    if refpts is None:
        refpts = len(ring)
    refs = uint32_refpts(refpts, len(ring))
    if pool_size is None:
        pool_size = multiprocessing.cpu_count()
    return _patpass(ring, r_in, nturns, refs, pool_size)
Example #6
0
def patpass(ring,
            r_in,
            nturns=1,
            refpts=None,
            losses=False,
            pool_size=None,
            globvar=True,
            **kwargs):
    """
    Simple parallel implementation of atpass().  If more than one particle
    is supplied, use multiprocessing to run each particle in a separate
    process. In case a single particle is provided or the ring contains
    ImpedanceTablePass element, atpass is returned

    INPUT:
        ring            lattice description
        r_in:           6xN array: input coordinates of N particles
        nturns:         number of passes through the lattice line
        refpts          elements at which data is returned. It can be:
                        1) an integer in the range [-len(ring), len(ring)-1]
                           selecting the element according to python indexing
                           rules. As a special case, len(ring) is allowed and
                           refers to the end of the last element,
                        2) an ordered list of such integers without duplicates,
                        3) a numpy array of booleans of maximum length
                           len(ring)+1, where selected elements are True.
                        Defaults to None, meaning no refpts, equivelent to
                        passing an empty array for calculation purposes.
        losses          Activate loss maps
        pool_size       number of processes, if None the min(npart,nproc) is used
        globvar         For linux machines speed-up is achieved by defining a global
                        ring variable, this can be disabled using globvar=False

     OUTPUT:
        (6, N, R, T) array containing output coordinates of N particles
        at R reference points for T turns.
        If losses ==True: {islost,turn,elem,coord} dictionnary containing
        flag for particles lost (True -> particle lost), turn, element and
        coordinates at which the particle is lost. Set to zero for particles
        that survived
    """
    if refpts is None:
        refpts = len(ring)
    refpts = uint32_refpts(refpts, len(ring))
    pm_ok = [e.PassMethod in elements._collective for e in ring]
    if len(numpy.atleast_1d(r_in[0])) > 1 and not any(pm_ok):
        if pool_size is None:
            pool_size = min(len(r_in[0]), multiprocessing.cpu_count())
        return _atpass(ring,
                       r_in,
                       pool_size,
                       globvar,
                       nturns=nturns,
                       refpts=refpts,
                       losses=losses)
    else:
        if any(pm_ok):
            warn(AtWarning('Collective PassMethod found: use single process'))
        return atpass(ring, r_in, nturns=nturns, refpts=refpts, losses=losses)
Example #7
0
 def iterator(self, key):
     """Iterates over the indices selected by a slice or an array"""
     if isinstance(key, slice):
         indices = key.indices(len(self))
         rg = range(*indices)
     else:
         rg = uint32_refpts(key, len(self))
     return (super(Lattice, self).__getitem__(i) for i in rg)
Example #8
0
 def i_range(self):
     """Range of elements inside the range of interest"""
     try:
         i_range = self._i_range
     except AttributeError:
         self.s_range = None
         i_range = self._i_range
     return uint32_refpts(i_range, len(self))
Example #9
0
def get_twiss(ring, dp=0.0, refpts=None, get_chrom=False, ddp=DDP):
    """
    Determine Twiss parameters by first finding the transfer matrix.
    """
    def twiss22(mat, ms):
        """
        Calculate Twiss parameters from the standard 2x2 transfer matrix
        (i.e. x or y).
        """
        sin_mu_end = (numpy.sign(mat[0, 1]) *
                      math.sqrt(-mat[0, 1] * mat[1, 0] -
                                (mat[0, 0] - mat[1, 1])**2 / 4))
        alpha0 = (mat[0, 0] - mat[1, 1]) / 2.0 / sin_mu_end
        beta0 = mat[0, 1] / sin_mu_end
        beta = ((ms[0, 0, :] * beta0 - ms[0, 1, :] * alpha0)**2 +
                ms[0, 1, :]**2) / beta0
        alpha = -((ms[0, 0, :] * beta0 - ms[0, 1, :] * alpha0) *
                  (ms[1, 0, :] * beta0 - ms[1, 1, :] * alpha0) +
                  ms[0, 1, :] * ms[1, 1, :]) / beta0
        mu = numpy.arctan(ms[0, 1, :] /
                          (ms[0, 0, :] * beta0 - ms[0, 1, :] * alpha0))
        mu = betatron_phase_unwrap(mu)
        return alpha, beta, mu

    chrom = None

    refpts = lattice.uint32_refpts(refpts, len(ring))
    nrefs = refpts.size
    if refpts[-1] != len(ring):
        refpts = numpy.append(refpts, [len(ring)])

    orbit4, orbit = find_orbit4(ring, dp, refpts)
    m44, mstack = find_m44(ring, dp, refpts, orbit4=orbit4)

    ax, bx, mx = twiss22(m44[:2, :2], mstack[:2, :2, :])
    ay, by, my = twiss22(m44[2:, 2:], mstack[2:, 2:, :])

    tune = numpy.array((mx[-1], my[-1])) / (2 * numpy.pi)
    twiss = numpy.zeros(nrefs, dtype=TWISS_DTYPE)
    twiss['idx'] = refpts[:nrefs]
    twiss['s_pos'] = lattice.get_s_pos(ring, refpts[:nrefs])
    twiss['closed_orbit'] = numpy.rollaxis(orbit, -1)[:nrefs]
    twiss['m44'] = numpy.rollaxis(mstack, -1)[:nrefs]
    twiss['alpha'] = numpy.rollaxis(numpy.vstack((ax, ay)), -1)[:nrefs]
    twiss['beta'] = numpy.rollaxis(numpy.vstack((bx, by)), -1)[:nrefs]
    twiss['mu'] = numpy.rollaxis(numpy.vstack((mx, my)), -1)[:nrefs]
    twiss['dispersion'] = numpy.NaN
    # Calculate chromaticity by calling this function again at a slightly
    # different momentum.
    if get_chrom:
        twissb, tuneb, _ = get_twiss(ring, dp + ddp, refpts[:nrefs])
        chrom = (tuneb - tune) / ddp
        twiss['dispersion'] = (twissb['closed_orbit'] -
                               twiss['closed_orbit']) / ddp

    return twiss, tune, chrom
Example #10
0
def lattice_pass(lattice, r_in, nturns=1, refpts=None, keep_lattice=False):
    """lattice_pass tracks particles through each element of a lattice
    calling the element-specific tracking function specified in the
    lattice[i].PassMethod field.

    Note:

     * lattice_pass(lattice, r_in, refpts=len(line)) is the same as
       lattice_pass(lattice, r_in) since the reference point len(line) is the
       exit of the last element
     * lattice_pass(lattice, r_in, refpts=0) is a copy of r_in since the
       reference point 0 is the entrance of the first element

    PARAMETERS
        lattice:    iterable of AT elements
        r_in:       (6, N) array: input coordinates of N particles.
                    r_in is modified in-place and reports the coordinates at
                    the end of the tracking. For the the best efficiency, r_in
                    should be given as F_CONTIGUOUS numpy array.
        nturns:     number of passes through the lattice line
        refpts      elements at which data is returned. It can be:
                    1) an integer in the range [-len(ring), len(ring)-1]
                       selecting the element according to python indexing
                       rules. As a special case, len(ring) is allowed and
                       refers to the end of the last element,
                    2) an ordered list of such integers without duplicates,
                    3) a numpy array of booleans of maximum length
                       len(ring)+1, where selected elements are True.
                    Defaults to None, meaning no refpts, equivelent to
                    passing an empty array for calculation purposes.
        keep_lattice: use elements persisted from a previous call to at.atpass.
                    If True, assume that the lattice has not changed since
                    that previous call.

    OUTPUT
        (6, N, R, T) array containing output coordinates of N particles
        at R reference points for T turns.
    """
    assert r_in.shape[0] == 6 and r_in.ndim in (1, 2), DIMENSION_ERROR
    if not isinstance(lattice, list):
        lattice = list(lattice)
    nelems = len(lattice)
    if refpts is None:
        refpts = nelems
    refs = uint32_refpts(refpts, nelems)
    # atpass returns 6xAxBxC array where n = x*y*z;
    # * A is number of particles;
    # * B is number of refpts
    # * C is the number of turns
    if r_in.flags.f_contiguous:
        return atpass(lattice, r_in, nturns, refs, int(keep_lattice))
    else:
        r_fin = numpy.asfortranarray(r_in)
        r_out = atpass(lattice, r_fin, nturns, refs, int(keep_lattice))
        r_in[:] = r_fin[:]
        return r_out
Example #11
0
def test_uint32_refpts_throws_ValueError_correctly(ref_in):
    with pytest.raises(ValueError):
        uint32_refpts(ref_in, 2)
Example #12
0
def test_uint32_refpts_converts_numerical_inputs_correctly(ref_in, expected):
    numpy.testing.assert_equal(uint32_refpts(ref_in, 5), expected)
    ref_in2 = numpy.asarray(ref_in).astype(numpy.float64)
    numpy.testing.assert_equal(uint32_refpts(ref_in2, 5), expected)
    ref_in2 = numpy.asarray(ref_in).astype(numpy.int64)
    numpy.testing.assert_equal(uint32_refpts(ref_in2, 5), expected)
Example #13
0
def test_uint32_refpts_converts_other_input_types_correctly(ref_in, expected):
    numpy.testing.assert_equal(uint32_refpts(ref_in, 5), expected)
Example #14
0
def find_orbit6(ring,
                refpts=None,
                orbit=None,
                dp=None,
                dct=None,
                keep_lattice=False,
                **kwargs):
    """find_orbit6 finds the closed orbit in the full 6-D phase space
    by numerically solving  for a fixed point of the one turn
    map M calculated with lattice_pass

    (X, PX, Y, PY, DP, CT2 ) = M (X, PX, Y, PY, DP, CT1)

    with constraint  CT2 - CT1 = C*HarmNumber(1/Frf - 1/Frf0)

    IMPORTANT!!! find_orbit6 is a realistic simulation
    1.  The Frf frequency in the RF cavities (may be different from Frf0)
        imposes the synchronous condition
        CT2 - CT1 = C*HarmNumber(1/Frf - 1/Frf0)
    2.  The algorithm numerically calculates
        6-by-6 Jacobian matrix J6. In order for (J-E) matrix
        to be non-singular it is NECESSARY to use a realistic
        PassMethod for cavities with non-zero momentum kick
        (such as ThinCavityPass).
    3.  find_orbit6 can find orbits with radiation.
        In order for the solution to exist the cavity must supply
        adequate energy compensation.
        In the simplest case of a single cavity, it must have
        'Voltage' field set so that Voltage > Erad - energy loss per turn
    4.  There is a family of solutions that correspond to different RF buckets
        They differ in the 6-th coordinate by C*Nb/Frf. Nb = 1 .. HarmNum-1
    5.  The value of the 6-th coordinate found at the cavity gives
        the equilibrium RF phase. If there is no radiation the phase is 0;

    PARAMETERS
        ring            lattice description (radiation must be ON)
        refpts          elements at which data is returned. It can be:
                        1) an integer in the range [-len(ring), len(ring)-1]
                           selecting the element according to python indexing
                           rules. As a special case, len(ring) is allowed and
                           refers to the end of the last element,
                        2) an ordered list of such integers without duplicates,
                        3) a numpy array of booleans of maximum length
                           len(ring)+1, where selected elements are True.
                        Defaults to None, if refpts is None an empty array is
                        returned for orbit.

    OUTPUT
        orbit0          ((6,) closed orbit vector at the entrance of the
                        1-st element (x,px,y,py)
        orbit           (6, Nrefs) closed orbit vector at each location
                        specified in refpts

    KEYWORDS
        orbit=None      avoids looking for initial the closed orbit if is
                        already known ((6,) array). find_orbit6 propagates it
                        to the specified refpts.
        guess           Initial value for the closed orbit. It may help
                        convergence. The default is computed from the energy
                        loss of the ring
        keep_lattice    Assume no lattice change since the previous tracking.
                        Default: False
        method          Method for energy loss computation (see get_energy_loss)
                        default: ELossMethod.TRACKING
        cavpts=None     Cavity location. If None, use all cavities. This is used
                        to compute the initial synchronous phase.
        convergence     Convergence criterion. Default: 1.e-12
        max_iterations  Maximum number of iterations. Default: 20
        XYStep          Step size. Default: DConstant.XYStep
        DPStep          Step size. Default: DConstant.DPStep

    See also find_orbit4, find_sync_orbit.
    """
    if not (dp is None and dct is None):
        warnings.warn(AtWarning('In 6D, "dp" and "dct" are ignored'))
    if orbit is None:
        orbit = _orbit6(ring, keep_lattice=keep_lattice, **kwargs)
        keep_lattice = True

    uint32refs = uint32_refpts(refpts, len(ring))
    # bug in numpy < 1.13
    all_points = numpy.empty(
        (0, 6), dtype=float) if len(uint32refs) == 0 else numpy.squeeze(
            lattice_pass(ring,
                         orbit.copy(order='K'),
                         refpts=uint32refs,
                         keep_lattice=keep_lattice),
            axis=(1, 3)).T
    return orbit, all_points
Example #15
0
def find_sync_orbit(ring, dct=0.0, refpts=None, guess=None, **kwargs):
    """find_sync_orbit finds the closed orbit, synchronous with the RF cavity
    and momentum deviation dP (first 5 components of the phase space vector)
    % by numerically solving  for a fixed point
    % of the one turn map M calculated with lattice_pass

        (X, PX, Y, PY, dP, CT2 ) = M (X, PX, Y, PY, dP, CT1)

    under the constraint dCT = CT2 - CT1 = C/Frev - C/Frev0, where
    Frev0 = Frf0/HarmNumber is the design revolution frequency
    Frev  = (Frf0 + dFrf)/HarmNumber is the imposed revolution frequency

    IMPORTANT!!!  find_sync_orbit imposes a constraint (CT2 - CT1) and
    dP2 = dP1 but no constraint on the value of dP1, dP2
    The algorithm assumes time-independent fixed-momentum ring
    to reduce the dimensionality of the problem.

    To impose this artificial constraint in find_sync_orbit
    PassMethod used for any element SHOULD NOT
    1.  change the longitudinal momentum dP (cavities , magnets with radiation)
    2.  have any time dependence (localized impedance, fast kickers etc).

    PARAMETERS
        ring            Sequence of AT elements
        dct             Path lehgth deviation. Default: 0
        refpts          elements at which data is returned. It can be:
                        1) an integer in the range [-len(ring), len(ring)-1]
                           selecting the element according to python indexing
                           rules. As a special case, len(ring) is allowed and
                           refers to the end of the last element,
                        2) an ordered list of such integers without duplicates,
                        3) a numpy array of booleans of maximum length
                           len(ring)+1, where selected elements are True.
                        Defaults to None, if refpts is None an empty array is
                        returned for orbit.

    OUTPUT
        orbit0          ((6,) closed orbit vector at the entrance of the
                        1-st element (x,px,y,py)
        orbit           (6, Nrefs) closed orbit vector at each location
                        specified in refpts

    KEYWORDS
        keep_lattice    Assume no lattice change since the previous tracking.
                        Default: False
        guess           Initial value for the closed orbit. It may help
                        convergence. Default: (0, 0, 0, 0)
        convergence     Convergence criterion. Default: 1.e-12
        max_iterations  Maximum number of iterations. Default: 20
        step_size       Step size. Default: 1.e-6

    See also find_orbit4, find_orbit6.
    """
    keep_lattice = kwargs.pop('keep_lattice', False)
    convergence = kwargs.pop('convergence', CONVERGENCE)
    max_iterations = kwargs.pop('max_iterations', MAX_ITERATIONS)
    step_size = kwargs.pop('step_size', STEP_SIZE)
    ref_in = numpy.zeros((6, ), order='F') if guess is None else guess

    delta_matrix = numpy.zeros((6, 6), order='F')
    for i in range(5):
        delta_matrix[i, i] = step_size
    theta5 = numpy.zeros((5, ), order='F')
    theta5[4] = dct
    id5 = numpy.zeros((5, 5), order='F')
    for i in range(4):
        id5[i, i] = 1.0
    idx = numpy.array([0, 1, 2, 3, 5])
    change = 1
    itercount = 0
    while (change > convergence) and itercount < max_iterations:
        in_mat = ref_in.reshape((6, 1)) + delta_matrix
        _ = lattice_pass(ring, in_mat, refpts=[], keep_lattice=keep_lattice)
        # the reference particle after one turn
        ref_out = in_mat[:, -1]
        # 5x5 jacobian matrix from numerical differentiation:
        # f(x+d) - f(x) / d
        j5 = (in_mat[idx, :5] - in_mat[idx, 5:]) / step_size
        a = j5 - id5  # f'(r_n) - 1
        b = ref_out[idx] - numpy.append(ref_in[:4], 0.0) - theta5
        b_over_a, _, _, _ = numpy.linalg.lstsq(a, b, rcond=-1)
        r_next = ref_in - numpy.append(b_over_a, 0.0)
        # determine if we are close enough
        change = numpy.linalg.norm(r_next - ref_in)
        itercount += 1
        ref_in = r_next
        keep_lattice = True

    if itercount == max_iterations:
        warnings.warn(
            AtWarning('Maximum number of iterations reached. '
                      'Possible non-convergence'))

    uint32refs = uint32_refpts(refpts, len(ring))
    # bug in numpy < 1.13
    all_points = numpy.empty(
        (0, 6), dtype=float) if len(uint32refs) == 0 else numpy.squeeze(
            lattice_pass(ring,
                         ref_in.copy(order='K'),
                         refpts=uint32refs,
                         keep_lattice=keep_lattice),
            axis=(1, 3)).T

    return ref_in, all_points
Example #16
0
def find_orbit4(ring, dp=0.0, refpts=None, guess=None, **kwargs):
    """findorbit4 finds the closed orbit in the 4-d transverse phase
    space by numerically solving for a fixed point of the one turn
    map M calculated with lattice_pass.

        (X, PX, Y, PY, dP, CT2 ) = M (X, PX, Y, PY, dP, CT1)

    under the CONSTANT MOMENTUM constraint dP and with NO constraint
    on the 6-th coordinate CT

    IMPORTANT!!! findorbit4 imposes a constraint on dP and relaxes
    the constraint on the revolution frequency. A physical storage
    ring does exactly the opposite: the momentum deviation of a
    particle on the closed orbit settles at the value
    such that the revolution is synchronous with the RF cavity

                HarmNumber*Frev = Frf

    To impose this artificial constraint in find_orbit4, PassMethod
    used for any element SHOULD NOT
    1.  change the longitudinal momentum dP (cavities , magnets with radiation)
    2.  have any time dependence (localized impedance, fast kickers etc)

    PARAMETERS
        ring            Sequence of AT elements
        dp              momentum deviation. Defaults to 0
        refpts          elements at which data is returned. It can be:
                        1) an integer in the range [-len(ring), len(ring)-1]
                           selecting the element according to python indexing
                           rules. As a special case, len(ring) is allowed and
                           refers to the end of the last element,
                        2) an ordered list of such integers without duplicates,
                        3) a numpy array of booleans of maximum length
                           len(ring)+1, where selected elements are True.
                        Defaults to None, if refpts is None an empty array is
                        returned for orbit.

    OUTPUT
        orbit0          ((6,) closed orbit vector at the entrance of the
                        1-st element (x,px,y,py)
        orbit           (6, Nrefs) closed orbit vector at each location
                        specified in refpts

    KEYWORDS
        keep_lattice    Assume no lattice change since the previous tracking.
                        Default: False
        guess           (6,) initial value for the closed orbit. It may help
                        convergence. Default: (0, 0, 0, 0, 0, 0)
        convergence     Convergence criterion. Default: 1.e-12
        max_iterations  Maximum number of iterations. Default: 20
        step_size       Step size. Default: 1.e-6

    See also find_sync_orbit, find_orbit6.
    """
    # We seek
    #  - f(x) = x
    #  - g(x) = f(x) - x = 0
    #  - g'(x) = f'(x) - 1
    # Use a Newton-Raphson-type algorithm:
    #  - r_n+1 = r_n - g(r_n) / g'(r_n)
    #  - r_n+1 = r_n - (f(r_n) - r_n) / (f'(r_n) - 1)
    #
    # (f(r_n) - r_n) / (f'(r_n) - 1) can be seen as x = b/a where we use least
    #     squares fitting to determine x when ax = b
    # f(r_n) - r_n is denoted b
    # f'(r_n) is the 4x4 jacobian, denoted j4

    keep_lattice = kwargs.pop('keep_lattice', False)
    convergence = kwargs.pop('convergence', CONVERGENCE)
    max_iterations = kwargs.pop('max_iterations', MAX_ITERATIONS)
    step_size = kwargs.pop('step_size', STEP_SIZE)
    ref_in = numpy.zeros((6, ), order='F') if guess is None else guess
    ref_in[4] = dp

    delta_matrix = numpy.zeros((6, 5), order='F')
    for i in range(4):
        delta_matrix[i, i] = step_size
    id4 = numpy.asfortranarray(numpy.identity(4))
    change = 1
    itercount = 0
    while (change > convergence) and itercount < max_iterations:
        in_mat = ref_in.reshape((6, 1)) + delta_matrix
        _ = lattice_pass(ring, in_mat, refpts=[], keep_lattice=keep_lattice)
        # the reference particle after one turn
        ref_out = in_mat[:, 4]
        # 4x4 jacobian matrix from numerical differentiation:
        # f(x+d) - f(x) / d
        j4 = (in_mat[:4, :4] - in_mat[:4, 4:]) / step_size
        a = j4 - id4  # f'(r_n) - 1
        b = ref_out[:4] - ref_in[:4]
        b_over_a, _, _, _ = numpy.linalg.lstsq(a, b, rcond=-1)
        r_next = ref_in - numpy.append(b_over_a, numpy.zeros((2, )))
        # determine if we are close enough
        change = numpy.linalg.norm(r_next - ref_in)
        itercount += 1
        ref_in = r_next
        keep_lattice = True

    if itercount == max_iterations:
        warnings.warn(
            AtWarning('Maximum number of iterations reached.'
                      'Possible non-convergence'))

    uint32refs = uint32_refpts(refpts, len(ring))
    # bug in numpy < 1.13
    all_points = numpy.empty(
        (0, 6), dtype=float) if len(uint32refs) == 0 else numpy.squeeze(
            lattice_pass(ring,
                         ref_in.copy(order='K'),
                         refpts=uint32refs,
                         keep_lattice=keep_lattice),
            axis=(1, 3)).T

    return ref_in, all_points
Example #17
0
def find_orbit6(ring, refpts=None, guess=None, **kwargs):
    """find_orbit6 finds the closed orbit in the full 6-D phase space
    by numerically solving  for a fixed point of the one turn
    map M calculated with lattice_pass

    (X, PX, Y, PY, DP, CT2 ) = M (X, PX, Y, PY, DP, CT1)

    with constraint  CT2 - CT1 = C*HarmNumber(1/Frf - 1/Frf0)

    IMPORTANT!!! find_orbit6 is a realistic simulation
    1.  The Frf frequency in the RF cavities (may be different from Frf0)
        imposes the synchronous condition
        CT2 - CT1 = C*HarmNumber(1/Frf - 1/Frf0)
    2.  The algorithm numerically calculates
        6-by-6 Jacobian matrix J6. In order for (J-E) matrix
        to be non-singular it is NECESSARY to use a realistic
        PassMethod for cavities with non-zero momentum kick
        (such as ThinCavityPass).
    3.  find_orbit6 can find orbits with radiation.
        In order for the solution to exist the cavity must supply
        adequate energy compensation.
        In the simplest case of a single cavity, it must have
        'Voltage' field set so that Voltage > Erad - energy loss per turn
    4.  There is a family of solutions that correspond to different RF buckets
        They differ in the 6-th coordinate by C*Nb/Frf. Nb = 1 .. HarmNum-1
    5.  The value of the 6-th coordinate found at the cavity gives
        the equilibrium RF phase. If there is no radiation the phase is 0;

    PARAMETERS
        ring            Sequence of AT elements
        refpts          elements at which data is returned. It can be:
                        1) an integer in the range [-len(ring), len(ring)-1]
                           selecting the element according to python indexing
                           rules. As a special case, len(ring) is allowed and
                           refers to the end of the last element,
                        2) an ordered list of such integers without duplicates,
                        3) a numpy array of booleans of maximum length
                           len(ring)+1, where selected elements are True.
                        Defaults to None, if refpts is None an empty array is
                        returned for orbit.

    OUTPUT
        orbit0          ((6,) closed orbit vector at the entrance of the
                        1-st element (x,px,y,py)
        orbit           (6, Nrefs) closed orbit vector at each location
                        specified in refpts

    KEYWORDS
        keep_lattice    Assume no lattice change since the previous tracking.
                        Default: False
        guess           Initial value for the closed orbit. It may help
                        convergence. Default: (0, 0, 0, 0)
        convergence     Convergence criterion. Default: 1.e-12
        max_iterations  Maximum number of iterations. Default: 20
        step_size       Step size. Default: 1.e-6

    See also find_orbit4, find_sync_orbit.
    """
    keep_lattice = kwargs.pop('keep_lattice', False)
    convergence = kwargs.pop('convergence', CONVERGENCE)
    max_iterations = kwargs.pop('max_iterations', MAX_ITERATIONS)
    step_size = kwargs.pop('step_size', STEP_SIZE)
    ref_in = numpy.zeros((6, ), order='F') if guess is None else guess

    # Get evolution period
    l0 = get_s_pos(ring, len(ring))
    cavities = [elem for elem in ring if isinstance(elem, elements.RFCavity)]
    if len(cavities) == 0:
        raise AtError('No cavity found in the lattice.')

    f_rf = cavities[0].Frequency
    harm_number = cavities[0].HarmNumber

    theta = numpy.zeros((6, ))
    theta[5] = constants.speed_of_light * harm_number / f_rf - l0

    delta_matrix = numpy.zeros((6, 7), order='F')
    for i in range(6):
        delta_matrix[i, i] = step_size

    id6 = numpy.asfortranarray(numpy.identity(6))
    change = 1
    itercount = 0
    while (change > convergence) and itercount < max_iterations:
        in_mat = ref_in.reshape((6, 1)) + delta_matrix
        _ = lattice_pass(ring, in_mat, refpts=[], keep_lattice=keep_lattice)
        # the reference particle after one turn
        ref_out = in_mat[:, 6]
        # 6x6 jacobian matrix from numerical differentiation:
        # f(x+d) - f(x) / d
        j6 = (in_mat[:, :6] - in_mat[:, 6:]) / step_size
        a = j6 - id6  # f'(r_n) - 1
        b = ref_out[:] - ref_in[:] - theta
        b_over_a, _, _, _ = numpy.linalg.lstsq(a, b, rcond=-1)
        r_next = ref_in - b_over_a
        # determine if we are close enough
        change = numpy.linalg.norm(r_next - ref_in)
        itercount += 1
        ref_in = r_next
        keep_lattice = True

    if itercount == max_iterations:
        warnings.warn(
            AtWarning('Maximum number of iterations reached. '
                      'Possible non-convergence'))

    uint32refs = uint32_refpts(refpts, len(ring))
    # bug in numpy < 1.13
    all_points = numpy.empty(
        (0, 6), dtype=float) if len(uint32refs) == 0 else numpy.squeeze(
            lattice_pass(ring,
                         ref_in.copy(order='K'),
                         refpts=uint32refs,
                         keep_lattice=keep_lattice),
            axis=(1, 3)).T

    return ref_in, all_points
Example #18
0
def linopt(ring,
           dp=0.0,
           refpts=None,
           get_chrom=False,
           orbit=None,
           keep_lattice=False,
           ddp=DDP,
           coupled=True):
    """
    Perform linear analysis of a lattice

    lindata0, tune, chrom, lindata = linopt(ring, dp[, refpts])

    PARAMETERS
        ring            lattice description.
        dp=0.0          momentum deviation.
        refpts=None     elements at which data is returned. It can be:
                        1) an integer in the range [-len(ring), len(ring)-1]
                           selecting the element according to python indexing
                           rules. As a special case, len(ring) is allowed and
                           refers to the end of the last element,
                        2) an ordered list of such integers without duplicates,
                        3) a numpy array of booleans of maximum length
                           len(ring)+1, where selected elements are True.

    KEYWORDS
        orbit           avoids looking for the closed orbit if is already known
                        ((6,) array)
        get_chrom=False compute dispersion and chromaticities. Needs computing
                        the optics at 2 different momentum deviations around
                        the central one.
        keep_lattice    Assume no lattice change since the previous tracking.
                        Defaults to False
        ddp=1.0E-8      momentum deviation used for computation of
                        chromaticities and dispersion
        coupled=True    if False, simplify the calculations by assuming
                        no H/V coupling

    OUTPUT
        lindata0        linear optics data at the entrance/end of the ring
        tune            [tune_A, tune_B], linear tunes for the two normal modes
                        of linear motion [1]
        chrom           [ksi_A , ksi_B], chromaticities ksi = d(nu)/(dP/P).
                        Only computed if 'get_chrom' is True
        lindata         linear optics at the points refered to by refpts, if
                        refpts is None an empty lindata structure is returned.

        lindata is a record array with fields:
        idx             element index in the ring
        s_pos           longitudinal position [m]
        closed_orbit    (6,) closed orbit vector
        dispersion      (4,) dispersion vector
                        Only computed if 'get_chrom' is True
        m44             (4, 4) transfer matrix M from the beginning of ring
                        to the entrance of the element [2]
        A               (2, 2) matrix A in [3]
        B               (2, 2) matrix B in [3]
        C               (2, 2) matrix C in [3]
        gamma           gamma parameter of the transformation to eigenmodes
        mu              [mux, muy], betatron phase (modulo 2*pi)
        beta            [betax, betay] vector
        alpha           [alphax, alphay] vector
        All values given at the entrance of each element specified in refpts.

        Field values can be obtained with either
        lindata['idx']    or
        lindata.idx

    REFERENCES
        [1] D.Edwars,L.Teng IEEE Trans.Nucl.Sci. NS-20, No.3, p.885-888, 1973
        [2] E.Courant, H.Snyder
        [3] D.Sagan, D.Rubin Phys.Rev.Spec.Top.-Accelerators and beams,
            vol.2 (1999)

    See also get_twiss

    """

    # noinspection PyShadowingNames
    def analyze(r44):
        t44 = r44.reshape((4, 4))
        mm = t44[:2, :2]
        nn = t44[2:, 2:]
        m = t44[:2, 2:]
        n = t44[2:, :2]
        gamma = sqrt(numpy.linalg.det(numpy.dot(n, C) + numpy.dot(G, nn)))
        msa = (G.dot(mm) - m.dot(_jmt.dot(C.T.dot(_jmt.T)))) / gamma
        msb = (numpy.dot(n, C) + numpy.dot(G, nn)) / gamma
        cc = (numpy.dot(mm, C) + numpy.dot(G, m)).dot(
            _jmt.dot(msb.T.dot(_jmt.T)))
        return msa, msb, gamma, cc

    uintrefs = uint32_refpts([] if refpts is None else refpts, len(ring))

    if orbit is None:
        orbit, _ = find_orbit4(ring, dp, keep_lattice=keep_lattice)
        keep_lattice = True
    orbs = numpy.squeeze(lattice_pass(ring,
                                      orbit.copy(order='K'),
                                      refpts=uintrefs,
                                      keep_lattice=keep_lattice),
                         axis=(1, 3)).T
    m44, mstack = find_m44(ring, dp, uintrefs, orbit=orbit, keep_lattice=True)
    nrefs = uintrefs.size

    M = m44[:2, :2]
    N = m44[2:, 2:]
    m = m44[:2, 2:]
    n = m44[2:, :2]

    if coupled:
        # Calculate A, B, C, gamma at the first element
        H = m + _jmt.dot(n.T.dot(_jmt.T))
        t = numpy.trace(M - N)
        t2 = t * t
        t2h = t2 + 4.0 * numpy.linalg.det(H)

        g = sqrt(1.0 + sqrt(t2 / t2h)) / sqrt(2.0)
        G = numpy.diag((g, g))
        C = -H * numpy.sign(t) / (g * sqrt(t2h))
        A = G.dot(G.dot(M)) - numpy.dot(
            G, (m.dot(_jmt.dot(C.T.dot(_jmt.T))) + C.dot(n))) + C.dot(
                N.dot(_jmt.dot(C.T.dot(_jmt.T))))
        B = G.dot(G.dot(N)) + numpy.dot(
            G, (_jmt.dot(C.T.dot(_jmt.T.dot(m))) + n.dot(C))) + _jmt.dot(
                C.T.dot(_jmt.T.dot(M.dot(C))))
    else:
        A = M
        B = N
        C = numpy.zeros((2, 2))
        g = 1.0

    # Get initial twiss parameters
    a0_a, b0_a, tune_a = _closure(A)
    a0_b, b0_b, tune_b = _closure(B)
    tune = numpy.array([tune_a, tune_b])

    if get_chrom:
        d0_up, tune_up, _, l_up = linopt(ring,
                                         dp + 0.5 * ddp,
                                         uintrefs,
                                         keep_lattice=True,
                                         coupled=coupled)
        d0_down, tune_down, _, l_down = linopt(ring,
                                               dp - 0.5 * ddp,
                                               uintrefs,
                                               keep_lattice=True,
                                               coupled=coupled)
        chrom = (tune_up - tune_down) / ddp
        dispersion = (l_up['closed_orbit'] -
                      l_down['closed_orbit'])[:, :4] / ddp
        disp0 = (d0_up['closed_orbit'] - d0_down['closed_orbit'])[:4] / ddp
    else:
        chrom = numpy.array([numpy.NaN, numpy.NaN])
        dispersion = numpy.NaN
        disp0 = numpy.NaN

    lindata0 = numpy.rec.fromarrays(
        (len(ring), get_s_pos(ring, len(ring))[0], orbit, disp0,
         numpy.array([a0_a, a0_b]), numpy.array(
             [b0_a, b0_b]), 2.0 * pi * tune, m44, A, B, C, g),
        dtype=LINDATA_DTYPE)

    # Propagate to reference points
    lindata = numpy.rec.array(numpy.zeros(nrefs, dtype=LINDATA_DTYPE))
    if nrefs > 0:
        if coupled:
            MSA, MSB, gamma, CL = zip(*[analyze(ms44) for ms44 in mstack])
            msa = numpy.stack(MSA, axis=0)
            msb = numpy.stack(MSB, axis=0)
        else:
            msa = mstack[:, :2, :2]
            msb = mstack[:, 2:, 2:]
            gamma = 1.0
            CL = numpy.zeros((1, 2, 2))

        alpha_a, beta_a, mu_a = _twiss22(msa, a0_a, b0_a)
        alpha_b, beta_b, mu_b = _twiss22(msb, a0_b, b0_b)

        lindata['idx'] = uintrefs
        lindata['s_pos'] = get_s_pos(ring, uintrefs)
        lindata['closed_orbit'] = orbs
        lindata['m44'] = mstack
        lindata['alpha'] = numpy.stack((alpha_a, alpha_b), axis=1)
        lindata['beta'] = numpy.stack((beta_a, beta_b), axis=1)
        lindata['mu'] = numpy.stack((mu_a, mu_b), axis=1)
        lindata['A'] = [
            ms.dot(A.dot(_jmt.dot(ms.T.dot(_jmt.T)))) for ms in msa
        ]
        lindata['B'] = [
            ms.dot(B.dot(_jmt.dot(ms.T.dot(_jmt.T)))) for ms in msb
        ]
        lindata['C'] = CL
        lindata['gamma'] = gamma
        lindata['dispersion'] = dispersion

    return lindata0, tune, chrom, lindata
Example #19
0
def find_m44(ring,
             dp=0.0,
             refpts=None,
             orbit=None,
             keep_lattice=False,
             **kwargs):
    """find_m44 numerically finds the 4x4 transfer matrix of an accelerator
    lattice for a particle with relative momentum deviation DP

    IMPORTANT!!! find_m44 assumes constant momentum deviation.
    PassMethod used for any element in the lattice SHOULD NOT
    1.  change the longitudinal momentum dP
        (cavities , magnets with radiation, ...)
    2.  have any time dependence (localized impedance, fast kickers, ...)

    m44, t = find_m44(lattice, dp=0.0, refpts)
        return 4x4 transfer matrices between the entrance of the first element
        and each element indexed by refpts.
            m44:    full one-turn matrix at the entrance of the first element
            t:      4x4 transfer matrices between the entrance of the first
                    element and each element indexed by refpts:
                    (Nrefs, 4, 4) array

    Unless an input orbit is introduced, find_m44 assumes that the lattice is
    a ring and first finds the closed orbit.

    PARAMETERS
        ring            lattice description
        dp              momentum deviation. Defaults to 0
        refpts          elements at which data is returned. It can be:
                        1) an integer in the range [-len(ring), len(ring)-1]
                           selecting the element according to python indexing
                           rules. As a special case, len(ring) is allowed and
                           refers to the end of the last element,
                        2) an ordered list of such integers without duplicates,
                        3) a numpy array of booleans of maximum length
                           len(ring)+1, where selected elements are True.
                        Defaults to None, if refpts is None an empty array is
                        returned for mstack.

    KEYWORDS
        keep_lattice=False  When True, assume no lattice change since the
                            previous tracking.
        full=False          When True, matrices are full 1-turn matrices at
                            the entrance of each
                            element indexed by refpts.
        orbit=None          Avoids looking for the closed orbit if is already
                            known (6,) array
        XYStep=6.055e-6     transverse step for numerical computation

    See also find_m66, find_orbit4
    """
    def mrotate(m):
        m = numpy.squeeze(m)
        return m.dot(m44.dot(_jmt.T.dot(m.T.dot(_jmt))))

    xy_step = kwargs.pop('XYStep', XYDEFSTEP)
    full = kwargs.pop('full', False)
    if orbit is None:
        orbit, _ = find_orbit4(ring, dp, keep_lattice=keep_lattice)
        keep_lattice = True
    # Construct matrix of plus and minus deltas
    dg = numpy.asfortranarray(0.5 * numpy.diag([xy_step] * 6)[:, :4])
    dmat = numpy.concatenate((dg, -dg), axis=1)
    # Add the deltas to multiple copies of the closed orbit
    in_mat = orbit.reshape(6, 1) + dmat

    refs = uint32_refpts(refpts, len(ring))
    out_mat = numpy.rollaxis(
        numpy.squeeze(lattice_pass(ring,
                                   in_mat,
                                   refpts=refs,
                                   keep_lattice=keep_lattice),
                      axis=3), -1)
    # out_mat: 8 particles at n refpts for one turn
    # (x + d) - (x - d) / d
    m44 = (in_mat[:4, :4] - in_mat[:4, 4:]) / xy_step

    if len(refs) > 0:
        mstack = (out_mat[:, :4, :4] - out_mat[:, :4, 4:]) / xy_step
        if full:
            mstack = numpy.stack([mrotate(mat) for mat in mstack], axis=0)
    else:
        mstack = numpy.empty((0, 4, 4), dtype=float)

    return m44, mstack
Example #20
0
def match(ring,
          variables,
          constraints,
          verbose=2,
          max_nfev=1000,
          diff_step=1.0e-10,
          method=None):
    """Perform matching of constraints by varying variables

    PARAMETERS
        ring                ring lattice or transfer line
        variables           sequence of Variable objects
        constraints         sequance of Constraints objects

    KEYWORDS
        verbose=2           See scipy.optimize.least_squares
        max_nfev=1000           "
        diff_step=1.0e-10       "
    """
    def fun(vals):
        for value, variable in zip(vals, variables):
            variable.set(ring1, value)
            variable.set(ring2, value)

        c1 = [cons.evaluate(ring1) for cons in cst1]
        c2 = [cons.evaluate(ring2) for cons in cst2]
        return np.concatenate(c1 + c2, axis=None)

    cst1 = [cons for cons in constraints if not cons.rad]
    cst2 = [cons for cons in constraints if cons.rad]

    ring1 = ring.copy()  # Make a shallow copy of ring
    for var in variables:  # Make a deep copy of varying elements
        for ref in uint32_refpts(var.refpts, len(ring1)):
            ring1[ref] = ring1[ref].deepcopy()

    ring2 = ring1.radiation_on(copy=True)

    aaa = [(var.get(ring1), var.bounds) for var in variables]
    vini, bounds = zip(*aaa)
    bounds = np.array(bounds).T

    cini1 = [cst.values(ring1) for cst in cst1]
    cini2 = [cst.values(ring2) for cst in cst2]
    ntargets = sum(np.size(a) for a in chain.from_iterable(cini1 + cini2))

    if method is None:
        if np.all(abs(bounds) == np.inf) and ntargets >= len(variables):
            method = 'lm'
        else:
            method = 'trf'
    if verbose >= 1:
        print('\n{} constraints, {} variables, using method {}\n'.format(
            ntargets, len(variables), method))

    least_squares(fun,
                  vini,
                  bounds=bounds,
                  verbose=verbose,
                  max_nfev=max_nfev,
                  method=method,
                  diff_step=diff_step)

    if verbose >= 1:
        print(Constraints.header())
        for cst, ini in zip(cst1, cini1):
            print(cst.status(ring1, initial=ini))
        for cst, ini in zip(cst2, cini2):
            print(cst.status(ring2, initial=ini))

        print(Variable.header())
        for var, vini in zip(variables, vini):
            print(var.status(ring1, vini=vini))

    return ring1
Example #21
0
def find_m66(ring, refpts=None, orbit=None, keep_lattice=False, **kwargs):
    """find_m66 numerically finds the 6x6 transfer matrix of an accelerator
    lattice by differentiation of lattice_pass near the closed orbit.
    find_m66 uses find_orbit6 to search for the closed orbit in 6-D
    In order for this to work the ring MUST have a CAVITY element

    m66, t = find_m66(lattice, refpts)
        m66:    full one-turn 6-by-6 matrix at the entrance of the
                first element.
        t:      6x6 transfer matrices between the entrance of the first
                element and each element indexed by refpts (nrefs, 6, 6) array.

    PARAMETERS
        ring            lattice description
        dp              momentum deviation. Defaults to 0
        refpts          elements at which data is returned. It can be:
                        1) an integer in the range [-len(ring), len(ring)-1]
                           selecting the element according to python indexing
                           rules. As a special case, len(ring) is allowed and
                           refers to the end of the last element,
                        2) an ordered list of such integers without duplicates,
                        3) a numpy array of booleans of maximum length
                           len(ring)+1, where selected elements are True.
                        Defaults to None, if refpts is None an empty array is
                        returned for mstack.

    KEYWORDS
        keep_lattice=False  When True, assume no lattice change since the
                            previous tracking.
        orbit=None          Avoids looking for the closed orbit if is already
                            known (6,) array
        XYStep=6.055e-6     transverse step for numerical computation
        DPStep=6.055e-6     longitudinal step for numerical computation

    See also find_m44, find_orbit6
    """
    xy_step = kwargs.pop('XYStep', XYDEFSTEP)
    dp_step = kwargs.pop('DPStep', DPSTEP)
    if orbit is None:
        orbit, _ = find_orbit6(ring, keep_lattice=keep_lattice)
        keep_lattice = True

    # Construct matrix of plus and minus deltas
    scaling = numpy.array(
        [xy_step, xy_step, xy_step, xy_step, dp_step, dp_step])
    dg = numpy.asfortranarray(0.5 * numpy.diag(scaling))
    dmat = numpy.concatenate((dg, -dg), axis=1)

    in_mat = orbit.reshape(6, 1) + dmat

    refs = uint32_refpts(refpts, len(ring))
    out_mat = numpy.rollaxis(
        numpy.squeeze(lattice_pass(ring,
                                   in_mat,
                                   refpts=refs,
                                   keep_lattice=keep_lattice),
                      axis=3), -1)
    # out_mat: 12 particles at n refpts for one turn
    # (x + d) - (x - d) / d
    m66 = (in_mat[:, :6] - in_mat[:, 6:]) / scaling.reshape((1, 6))

    if len(refs) > 0:
        mstack = (out_mat[:, :, :6] - out_mat[:, :, 6:]) / xy_step
    else:
        mstack = numpy.empty((0, 6, 6), dtype=float)

    return m66, mstack
Example #22
0
def test_uint32_refpts_throws_ValueError_if_input_invalid(input):
    with pytest.raises(ValueError):
        r = lattice.uint32_refpts(input, 2)
Example #23
0
def linopt(ring,
           dp=0.0,
           refpts=None,
           get_chrom=False,
           orbit=None,
           keep_lattice=False,
           coupled=True,
           twiss_in=None,
           get_w=False,
           **kwargs):
    """
    Perform linear analysis of a lattice
    lindata0, tune, chrom, lindata = linopt(ring, dp[, refpts])
    PARAMETERS
        ring            lattice description.
        dp=0.0          momentum deviation.
        refpts=None     elements at which data is returned. It can be:
                        1) an integer in the range [-len(ring), len(ring)-1]
                           selecting the element according to python indexing
                           rules. As a special case, len(ring) is allowed and
                           refers to the end of the last element,
                        2) an ordered list of such integers without duplicates,
                        3) a numpy array of booleans of maximum length
                           len(ring)+1, where selected elements are True.
    KEYWORDS
        orbit           avoids looking for the closed orbit if is already known
                        ((6,) array)
        get_chrom=False compute dispersion and chromaticities. Needs computing
                        the tune and orbit at 2 different momentum deviations
                        around the central one.
        keep_lattice    Assume no lattice change since the previous tracking.
                        Defaults to False
        XYStep=1.0e-8   transverse step for numerical computation
        DPStep=1.0E-6   momentum deviation used for computation of
                        chromaticities and dispersion
        coupled=True    if False, simplify the calculations by assuming
                        no H/V coupling
        twiss_in=None   Initial twiss to compute transfer line optics of the
                        type lindata, the initial orbit in twiss_in is ignored,
                        only the beta and alpha are required other quatities
                        set to 0 if absent
        get_w=False     computes chromatic amplitude functions (W) [4], need to
                        compute the optics at 2 different momentum deviations
                        around the central one.
    OUTPUT
        lindata0        linear optics data at the entrance/end of the ring
        tune            [tune_A, tune_B], linear tunes for the two normal modes
                        of linear motion [1]
        chrom           [ksi_A , ksi_B], chromaticities ksi = d(nu)/(dP/P).
                        Only computed if 'get_chrom' is True
        lindata         linear optics at the points refered to by refpts, if
                        refpts is None an empty lindata structure is returned.

        lindata is a record array with fields:
        idx             element index in the ring
        s_pos           longitudinal position [m]
        closed_orbit    (6,) closed orbit vector
        dispersion      (4,) dispersion vector
        W               (2,) chromatic amplitude function
                        Only computed if 'get_chrom' is True
        m44             (4, 4) transfer matrix M from the beginning of ring
                        to the entrance of the element [2]
        mu              [mux, muy], betatron phase (modulo 2*pi)
        beta            [betax, betay] vector
        alpha           [alphax, alphay] vector
        All values given at the entrance of each element specified in refpts.
        In case coupled = True additional outputs are available:
        A               (2, 2) matrix A in [3]
        B               (2, 2) matrix B in [3]
        C               (2, 2) matrix C in [3]
        gamma           gamma parameter of the transformation to eigenmodes
        Field values can be obtained with either
        lindata['idx']    or
        lindata.idx
    REFERENCES
        [1] D.Edwars,L.Teng IEEE Trans.Nucl.Sci. NS-20, No.3, p.885-888, 1973
        [2] E.Courant, H.Snyder
        [3] D.Sagan, D.Rubin Phys.Rev.Spec.Top.-Accelerators and beams,
            vol.2 (1999)
        [4] Brian W. Montague Report LEP Note 165, CERN, 1979
    """

    # noinspection PyShadowingNames
    def analyze(r44):
        t44 = r44.reshape((4, 4))
        mm = t44[:2, :2]
        nn = t44[2:, 2:]
        m = t44[:2, 2:]
        n = t44[2:, :2]
        gamma = sqrt(numpy.linalg.det(numpy.dot(n, C) + numpy.dot(G, nn)))
        msa = (G.dot(mm) - m.dot(_jmt.dot(C.T.dot(_jmt.T)))) / gamma
        msb = (numpy.dot(n, C) + numpy.dot(G, nn)) / gamma
        cc = (numpy.dot(mm, C) + numpy.dot(G, m)).dot(
            _jmt.dot(msb.T.dot(_jmt.T)))
        return msa, msb, gamma, cc

    xy_step = kwargs.pop('XYStep', DConstant.XYStep)
    dp_step = kwargs.pop('DPStep', DConstant.DPStep)
    uintrefs = uint32_refpts([] if refpts is None else refpts, len(ring))

    # Get initial orbit
    if twiss_in is None:
        if orbit is None:
            orbit, _ = find_orbit4(ring,
                                   dp,
                                   keep_lattice=keep_lattice,
                                   XYStep=xy_step)
            keep_lattice = True
        disp0 = numpy.NaN
        if get_chrom or get_w:
            orbit_up, _ = find_orbit4(ring,
                                      dp + 0.5 * dp_step,
                                      XYStep=xy_step,
                                      keep_lattice=keep_lattice)
            orbit_down, _ = find_orbit4(ring,
                                        dp - 0.5 * dp_step,
                                        XYStep=xy_step,
                                        keep_lattice=keep_lattice)
            disp0 = numpy.array(orbit_up - orbit_down)[:4] / dp_step
    else:
        if orbit is None:
            orbit = numpy.zeros((6, ))
        disp0 = numpy.NaN
        if get_chrom or get_w:
            try:
                disp0 = twiss_in['dispersion']
            except KeyError:
                print('Dispersion not found in twiss_in, setting to zero')
                disp0 = numpy.zeros((4, ))
            dorbit = numpy.hstack(
                (0.5 * dp_step * disp0, numpy.array([0.5 * dp_step, 0])))
            orbit_up = orbit + dorbit
            orbit_down = orbit - dorbit

    orbs = numpy.squeeze(lattice_pass(ring,
                                      orbit.copy(order='K'),
                                      refpts=uintrefs,
                                      keep_lattice=keep_lattice),
                         axis=(1, 3)).T
    m44, mstack = find_m44(ring,
                           dp,
                           uintrefs,
                           orbit=orbit,
                           keep_lattice=True,
                           XYStep=xy_step)

    nrefs = uintrefs.size

    M = m44[:2, :2]
    N = m44[2:, 2:]
    m = m44[:2, 2:]
    n = m44[2:, :2]

    # Calculate A, B, C, gamma at the first element
    if coupled:
        H = m + _jmt.dot(n.T.dot(_jmt.T))
        t = numpy.trace(M - N)
        t2 = t * t
        t2h = t2 + 4.0 * numpy.linalg.det(H)

        g = sqrt(1.0 + sqrt(t2 / t2h)) / sqrt(2.0)
        G = numpy.diag((g, g))
        C = -H * numpy.sign(t) / (g * sqrt(t2h))
        A = G.dot(G.dot(M)) - numpy.dot(
            G, (m.dot(_jmt.dot(C.T.dot(_jmt.T))) + C.dot(n))) + C.dot(
                N.dot(_jmt.dot(C.T.dot(_jmt.T))))
        B = G.dot(G.dot(N)) + numpy.dot(
            G, (_jmt.dot(C.T.dot(_jmt.T.dot(m))) + n.dot(C))) + _jmt.dot(
                C.T.dot(_jmt.T.dot(M.dot(C))))
    else:
        A = M
        B = N
        C = numpy.zeros((2, 2))
        g = 1.0

    # Get initial twiss parameters
    if twiss_in is None:
        a0_a, b0_a, tune_a = _closure(A)
        a0_b, b0_b, tune_b = _closure(B)
        tune = numpy.array([tune_a, tune_b])
    else:
        try:
            a0_a, a0_b = twiss_in['alpha'][0], twiss_in['alpha'][1]
        except KeyError:
            raise ValueError('Initial alpha required for transfer line')
        try:
            b0_a, b0_b = twiss_in['beta'][0], twiss_in['beta'][1]
        except KeyError:
            raise ValueError('Initial beta required for transfer line')
        try:
            tune = numpy.array([twiss_in['mu'][0], twiss_in['mu'][1]
                                ]) / (2 * pi)
        except KeyError:
            print('Mu not found in twiss_in, setting to zero')
            tune = numpy.zeros((2, ))

    # Get initial chromatic functions and dispersion
    if get_w:
        # noinspection PyUnboundLocalVariable
        kwup = dict(orbit=orbit_up, twiss_in=twiss_in)
        # noinspection PyUnboundLocalVariable
        kwdown = dict(orbit=orbit_down, twiss_in=twiss_in)
        param_up = linopt(ring,
                          dp=dp + 0.5 * dp_step,
                          refpts=uintrefs,
                          keep_lattice=True,
                          coupled=coupled,
                          XYStep=xy_step,
                          **kwup)
        param_down = linopt(ring,
                            dp=dp - 0.5 * dp_step,
                            refpts=uintrefs,
                            keep_lattice=True,
                            coupled=coupled,
                            XYStep=xy_step,
                            **kwdown)
        param_up_down = param_up + param_down
        chrom, dispersion, w0, w = _chromfunc(dp_step, *param_up_down)
    elif get_chrom:
        # noinspection PyUnboundLocalVariable
        kwup = dict(orbit=orbit_up, twiss_in=twiss_in)
        # noinspection PyUnboundLocalVariable
        kwdown = dict(orbit=orbit_down, twiss_in=twiss_in)
        _, tune_up, _, _ = linopt(ring,
                                  dp=dp + 0.5 * dp_step,
                                  coupled=coupled,
                                  keep_lattice=True,
                                  XYStep=xy_step,
                                  DPStep=dp_step,
                                  **kwup)
        orb_up = numpy.squeeze(lattice_pass(ring,
                                            kwup['orbit'].copy(order='K'),
                                            refpts=uintrefs,
                                            keep_lattice=True),
                               axis=(1, 3)).T
        _, tune_down, _, _ = linopt(ring,
                                    dp=dp - 0.5 * dp_step,
                                    coupled=coupled,
                                    keep_lattice=True,
                                    XYStep=xy_step,
                                    DPStep=dp_step,
                                    **kwdown)
        orb_down = numpy.squeeze(lattice_pass(ring,
                                              kwdown['orbit'].copy(order='K'),
                                              refpts=uintrefs,
                                              keep_lattice=True),
                                 axis=(1, 3)).T
        chrom = (tune_up - tune_down) / dp_step
        dispersion = (orb_up - orb_down)[:, :4] / dp_step
        w0 = numpy.array([numpy.NaN, numpy.NaN])
        w = numpy.array([numpy.NaN, numpy.NaN])
    else:
        chrom = numpy.array([numpy.NaN, numpy.NaN])
        dispersion = numpy.array([numpy.NaN, numpy.NaN, numpy.NaN, numpy.NaN])
        w0 = numpy.array([numpy.NaN, numpy.NaN])
        w = numpy.array([numpy.NaN, numpy.NaN])

    lindata0 = numpy.rec.fromarrays(
        (len(ring), get_s_pos(ring, len(ring))[0], orbit, disp0,
         numpy.array([a0_a, a0_b]), numpy.array(
             [b0_a, b0_b]), 2.0 * pi * tune, m44, A, B, C, g, w0),
        dtype=LINDATA_DTYPE)

    # Propagate to reference points
    lindata = numpy.rec.array(numpy.zeros(nrefs, dtype=LINDATA_DTYPE))
    if nrefs > 0:
        if coupled:
            MSA, MSB, gamma, CL = zip(*[analyze(ms44) for ms44 in mstack])
            msa = numpy.stack(MSA, axis=0)
            msb = numpy.stack(MSB, axis=0)
            AL = [ms.dot(A.dot(_jmt.dot(ms.T.dot(_jmt.T)))) for ms in msa]
            BL = [ms.dot(B.dot(_jmt.dot(ms.T.dot(_jmt.T)))) for ms in msb]
        else:
            msa = mstack[:, :2, :2]
            msb = mstack[:, 2:, 2:]
            AL = numpy.NaN
            BL = numpy.NaN
            CL = numpy.NaN
            gamma = numpy.NaN

        alpha_a, beta_a, mu_a = _twiss22(msa, a0_a, b0_a)
        alpha_b, beta_b, mu_b = _twiss22(msb, a0_b, b0_b)

        if twiss_in is not None:
            qtmp = numpy.array([mu_a[-1], mu_b[-1]]) / (2 * numpy.pi)
            qtmp -= numpy.floor(qtmp)
            mu_a += tune[0] * 2 * pi
            mu_b += tune[1] * 2 * pi
            tune = qtmp

        lindata['idx'] = uintrefs
        lindata['s_pos'] = get_s_pos(ring, uintrefs)
        lindata['closed_orbit'] = orbs
        lindata['m44'] = mstack
        lindata['alpha'] = numpy.stack((alpha_a, alpha_b), axis=1)
        lindata['beta'] = numpy.stack((beta_a, beta_b), axis=1)
        lindata['dispersion'] = dispersion
        lindata['mu'] = numpy.stack((mu_a, mu_b), axis=1)
        lindata['A'] = AL
        lindata['B'] = BL
        lindata['C'] = CL
        lindata['gamma'] = gamma
        lindata['W'] = w

    return lindata0, tune, chrom, lindata
Example #24
0
def test_uint32_refpts_handles_array_as_input():
    expected = numpy.array([1, 2, 3], dtype=numpy.uint32)
    requested = numpy.array([1, 2, 3], dtype=numpy.uint32)
    numpy.testing.assert_equal(lattice.uint32_refpts(requested, 5), expected)
    requested = numpy.array([1, 2, 3], dtype=numpy.float64)
    numpy.testing.assert_equal(lattice.uint32_refpts(requested, 5), expected)
Example #25
0
def get_twiss(ring,
              dp=0.0,
              refpts=None,
              get_chrom=False,
              orbit=None,
              keep_lattice=False,
              ddp=DDP):
    """
    Perform linear analysis of the NON-COUPLED lattices

    twiss0, tune, chrom, twiss = get_twiss(ring, dp[, refpts])

    PARAMETERS
        ring            lattice description.
        dp=0.0          momentum deviation.
        refpts=None     elements at which data is returned. It can be:
                        1) an integer in the range [-len(ring), len(ring)-1]
                           selecting the element according to python indexing
                           rules. As a special case, len(ring) is allowed and
                           refers to the end of the last element,
                        2) an ordered list of such integers without duplicates,
                        3) a numpy array of booleans of maximum length
                           len(ring)+1, where selected elements are True.

    KEYWORDS
        orbit           avoids looking for the closed orbit if is already known
                        ((6,) array)
        get_chrom=False compute dispersion and chromaticities. Needs computing
                        the optics at 2 different momentum deviations around
                        the central one.
        keep_lattice    Assume no lattice change since the previous tracking.
                        Defaults to False
        ddp=1.0E-8      momentum deviation used for computation of
                        chromaticities and dispersion

    OUTPUT
        twiss0          linear optics data at the entrance/end of the ring
        tune            [tune_h, tune_v], fractional part of the linear tunes
        chrom           [ksi_h , ksi_v], chromaticities ksi = d(nu)/(dP/P).
                        Only computed if 'get_chrom' is True
        twiss           linear optics at the points refered to by refpts, if
                        refpts is None an empty twiss structure is returned.

        twiss is a record array with fields:
        idx             element index in the ring
        s_pos           longitudinal position [m]
        closed_orbit    (6,) closed orbit vector
        dispersion      (4,) dispersion vector
                        Only computed if 'get_chrom' is True
        m44             (4, 4) transfer matrix M from the beginning of ring
                        to the entrance of the element
        mu              (2,) betatron phase (modulo 2*pi)
        beta            [betax, betay] vector
        alpha           [alphax, alphay] vector
        All values given at the entrance of each element specified in refpts.

        Field values can be obtained with either
        twiss['idx']    or
        twiss.idx


    See also linopt
    """
    uintrefs = uint32_refpts(refpts, len(ring))

    if orbit is None:
        orbit, _ = find_orbit4(ring, dp, keep_lattice=keep_lattice)
        keep_lattice = True

    orbs = numpy.squeeze(lattice_pass(ring,
                                      orbit.copy(order='K'),
                                      refpts=uintrefs,
                                      keep_lattice=keep_lattice),
                         axis=(1, 3)).T
    m44, mstack = find_m44(ring, dp, uintrefs, orbit=orbit, keep_lattice=True)
    nrefs = uintrefs.size

    # Get initial twiss parameters
    a0_x, b0_x, tune_x = _closure(m44[:2, :2])
    a0_y, b0_y, tune_y = _closure(m44[2:, 2:])
    tune = numpy.array([tune_x, tune_y])

    # Calculate chromaticity by calling this function again at a slightly
    # different momentum.
    if get_chrom:
        d0_up, tune_up, _, l_up = get_twiss(ring,
                                            dp + 0.5 * ddp,
                                            uintrefs,
                                            keep_lattice=True)
        d0_down, tune_down, _, l_down = get_twiss(ring,
                                                  dp - 0.5 * ddp,
                                                  uintrefs,
                                                  keep_lattice=True)
        chrom = (tune_up - tune_down) / ddp
        dispersion = (l_up['closed_orbit'] -
                      l_down['closed_orbit'])[:, :4] / ddp
        disp0 = (d0_up['closed_orbit'] - d0_down['closed_orbit'])[:4] / ddp
    else:
        chrom = numpy.array([numpy.NaN, numpy.NaN])
        dispersion = numpy.NaN
        disp0 = numpy.NaN

    twiss0 = numpy.rec.fromarrays((len(ring), get_s_pos(
        ring, len(ring))[0], orbit, disp0, numpy.array(
            [a0_x, a0_y]), numpy.array([b0_x, b0_y]), 2.0 * pi * tune, m44),
                                  dtype=TWISS_DTYPE)

    # Propagate to reference points
    twiss = numpy.rec.array(numpy.zeros(nrefs, dtype=TWISS_DTYPE))
    if nrefs > 0:
        alpha_x, beta_x, mu_x = _twiss22(mstack[:, :2, :2], a0_x, b0_x)
        alpha_z, beta_z, mu_z = _twiss22(mstack[:, 2:, 2:], a0_y, b0_y)

        twiss['idx'] = uintrefs
        twiss['s_pos'] = get_s_pos(ring, uintrefs[:nrefs])
        twiss['closed_orbit'] = orbs
        twiss['m44'] = mstack
        twiss['alpha'] = numpy.stack((alpha_x, alpha_z), axis=1)
        twiss['beta'] = numpy.stack((beta_x, beta_z), axis=1)
        twiss['mu'] = numpy.stack((mu_x, mu_z), axis=1)
        twiss['dispersion'] = dispersion

    return twiss0, tune, chrom, twiss
Example #26
0
def find_orbit4(ring,
                dp=0.0,
                refpts=None,
                dct=None,
                orbit=None,
                keep_lattice=False,
                **kwargs):
    """findorbit4 finds the closed orbit in the 4-d transverse phase
    space by numerically solving for a fixed point of the one turn
    map M calculated with lattice_pass.

        (X, PX, Y, PY, dP, CT2 ) = M (X, PX, Y, PY, dP, CT1)

    under the CONSTANT MOMENTUM constraint dP and with NO constraint
    on the 6-th coordinate CT

    IMPORTANT!!! findorbit4 imposes a constraint on dP and relaxes
    the constraint on the revolution frequency. A physical storage
    ring does exactly the opposite: the momentum deviation of a
    particle on the closed orbit settles at the value
    such that the revolution is synchronous with the RF cavity

                HarmNumber*Frev = Frf

    To impose this artificial constraint in find_orbit4, PassMethod
    used for any element SHOULD NOT
    1.  change the longitudinal momentum dP (cavities , magnets with radiation)
    2.  have any time dependence (localized impedance, fast kickers etc)

    PARAMETERS
        ring            lattice description (radiation must be OFF)
        dp              momentum deviation. Defaults to 0
        refpts          elements at which data is returned. It can be:
                        1) an integer in the range [-len(ring), len(ring)-1]
                           selecting the element according to python indexing
                           rules. As a special case, len(ring) is allowed and
                           refers to the end of the last element,
                        2) an ordered list of such integers without duplicates,
                        3) a numpy array of booleans of maximum length
                           len(ring)+1, where selected elements are True.
                        Defaults to None, if refpts is None an empty array is
                        returned for orbit.

    OUTPUT
        orbit0          ((6,) closed orbit vector at the entrance of the
                        1-st element (x,px,y,py)
        orbit           (6, Nrefs) closed orbit vector at each location
                        specified in refpts

    KEYWORDS
        dct=None        path lengthening. If specified, dp is ignored and
                        the off-momentum is deduced from the path lengthening.
        orbit=None      avoids looking for initial the closed orbit if is
                        already known ((6,) array). find_orbit4 propagates it
                        to the specified refpts.
        guess           (6,) initial value for the closed orbit. It may help
                        convergence. Default: (0, 0, 0, 0, 0, 0)
        keep_lattice    Assume no lattice change since the previous tracking.
                        Default: False
        convergence     Convergence criterion. Default: 1.e-12
        max_iterations  Maximum number of iterations. Default: 20
        XYStep          Step size. Default: DConstant.XYStep

    See also find_sync_orbit, find_orbit6.
    """
    if orbit is None:
        if dct is not None:
            orbit = _orbit_dct(ring, dct, keep_lattice=keep_lattice, **kwargs)
        else:
            orbit = _orbit_dp(ring, dp, keep_lattice=keep_lattice, **kwargs)
        keep_lattice = True

    uint32refs = uint32_refpts(refpts, len(ring))
    # bug in numpy < 1.13
    all_points = numpy.empty(
        (0, 6), dtype=float) if len(uint32refs) == 0 else numpy.squeeze(
            lattice_pass(ring,
                         orbit.copy(order='K'),
                         refpts=uint32refs,
                         keep_lattice=keep_lattice),
            axis=(1, 3)).T
    return orbit, all_points
Example #27
0
def ohmi_envelope(ring, refpts=None, orbit=None, keep_lattice=False):
    """
    Calculate the equilibrium beam envelope in a
    circular accelerator using Ohmi's beam envelope formalism [1]

    emit0, beamdata, emit = ohmi_envelope(ring[, refpts])

    PARAMETERS
        ring            Lattice object.
        refpts=None     elements at which data is returned. It can be:
                        1) an integer in the range [-len(ring), len(ring)-1]
                           selecting the element according to python indexing
                           rules. As a special case, len(ring) is allowed and
                           refers to the end of the last element,
                        2) an ordered list of such integers without duplicates,
                        3) a numpy array of booleans of maximum length
                           len(ring)+1, where selected elements are True.

    KEYWORDS
        orbit=None          Avoids looking for the closed orbit if it is
                            already known ((6,) array)
        keep_lattice=False  Assume no lattice change since the previous
                            tracking

    OUTPUT
        emit0               emittance data at the start/end of the ring
        beamdata            beam parameters at the start of the ring
        emit                emittance data at the points refered to by refpts,
                            if refpts is None an empty structure is returned.

        emit is a record array with fields:
        r66                 (6, 6) equilibrium envelope matrix R
        r44                 (4, 4) betatron emittance matrix (dpp = 0)
        m66                 (6, 6) transfer matrix from the start of the ring
        orbit6              (6,) closed orbit
        emitXY              (2,) betatron emittance projected on xxp and yyp
        emitXYZ             (3,) 6x6 emittance projected on xxp, yyp, ldp

        beamdata is a record array with fields:
        tunes               tunes of the 3 normal modes
        damping_rates       damping rates of the 3 normal modes
        mode_matrices       R-matrices of the 3 normal modes
        mode_emittances     equilibrium emittances of the 3 normal modes

        Field values can be obtained with either
        emit['r66']    or
        emit.r66

    REFERENCES
        [1] K.Ohmi et al. Phys.Rev.E. Vol.49. (1994)
    """

    def process(r66):
        # projections on xx', zz', ldp
        emit3sq = numpy.array([det(r66[s, s]) for s in _submat])
        # Prevent from unrealistic negative values of the determinant
        emit3 = numpy.sqrt(numpy.maximum(emit3sq, 0.0))
        # Emittance cut for dpp=0
        if emit3[0] < 1.E-13:  # No equilibrium emittance
            r44 = numpy.nan * numpy.ones((4, 4))
        elif emit3[1] < 1.E-13:  # Uncoupled machine
            minv = inv(r66[[0, 1, 4, 5], :][:, [0, 1, 4, 5]])
            r44 = numpy.zeros((4, 4))
            r44[:2, :2] = inv(minv[:2, :2])
        else:  # Coupled machine
            minv = inv(r66)
            r44 = inv(minv[:4, :4])
        # betatron emittances (dpp=0)
        emit2sq = numpy.array(
            [det(r44[s, s], check_finite=False) for s in _submat[:2]])
        # Prevent from unrealistic negative values of the determinant
        emit2 = numpy.sqrt(numpy.maximum(emit2sq, 0.0))
        return r44, emit2, emit3

    def propag(m, cumb, orbit6):
        """Propagate the beam matrix to refpts"""
        sigmatrix = m.dot(rr).dot(m.T) + cumb
        m44, emit2, emit3 = process(sigmatrix)
        return sigmatrix, m44, m, orbit6, emit2, emit3

    nelems = len(ring)
    uint32refs = uint32_refpts(refpts, nelems)
    bbcum, orbs = _dmatr(ring, orbit=orbit, keep_lattice=keep_lattice)
    mring, ms = find_m66(ring, uint32refs, orbit=orbs[0], keep_lattice=True)
    # ------------------------------------------------------------------------
    # Equation for the moment matrix R is
    #         R = MRING*R*MRING' + BCUM;
    # We rewrite it in the form of Lyapunov-Sylvester equation to use scipy's
    # solve_sylvester function
    #            A*R + R*B = Q
    # where
    #               A =  inv(MRING)
    #               B = -MRING'
    #               Q = inv(MRING)*BCUM
    # ------------------------------------------------------------------------
    aa = inv(mring)
    bb = -mring.T
    qq = numpy.dot(aa, bbcum[-1])
    rr = solve_sylvester(aa, bb, qq)
    rr = 0.5 * (rr + rr.T)
    rr4, emitxy, emitxyz = process(rr)
    r66data = get_tunes_damp(mring, rr)

    data0 = numpy.rec.fromarrays(
        (rr, rr4, mring, orbs[0], emitxy, emitxyz),
        dtype=ENVELOPE_DTYPE)
    if uint32refs.shape == (0,):
        data = numpy.recarray((0,), dtype=ENVELOPE_DTYPE)
    else:
        data = numpy.rec.fromrecords(
            list(map(propag, ms, bbcum[uint32refs], orbs[uint32refs, :])),
            dtype=ENVELOPE_DTYPE)

    return data0, r66data, data
Example #28
0
def find_sync_orbit(ring,
                    dct=0.0,
                    refpts=None,
                    dp=None,
                    orbit=None,
                    keep_lattice=False,
                    **kwargs):
    """find_sync_orbit finds the closed orbit, synchronous with the RF cavity
    and momentum deviation dP (first 5 components of the phase space vector)
    % by numerically solving  for a fixed point
    % of the one turn map M calculated with lattice_pass

        (X, PX, Y, PY, dP, CT2 ) = M (X, PX, Y, PY, dP, CT1)

    under the constraint dCT = CT2 - CT1 = C/Frev - C/Frev0, where
    Frev0 = Frf0/HarmNumber is the design revolution frequency
    Frev  = (Frf0 + dFrf)/HarmNumber is the imposed revolution frequency

    IMPORTANT!!!  find_sync_orbit imposes a constraint (CT2 - CT1) and
    dP2 = dP1 but no constraint on the value of dP1, dP2
    The algorithm assumes time-independent fixed-momentum ring
    to reduce the dimensionality of the problem.

    To impose this artificial constraint in find_sync_orbit
    PassMethod used for any element SHOULD NOT
    1.  change the longitudinal momentum dP (cavities , magnets with radiation)
    2.  have any time dependence (localized impedance, fast kickers etc).

    PARAMETERS
        ring            lattice description (radiation must be OFF)
        dct             Path length deviation. Default: 0
        refpts          elements at which data is returned. It can be:
                        1) an integer in the range [-len(ring), len(ring)-1]
                           selecting the element according to python indexing
                           rules. As a special case, len(ring) is allowed and
                           refers to the end of the last element,
                        2) an ordered list of such integers without duplicates,
                        3) a numpy array of booleans of maximum length
                           len(ring)+1, where selected elements are True.
                        Defaults to None, if refpts is None an empty array is
                        returned for orbit.

    OUTPUT
        orbit0          ((6,) closed orbit vector at the entrance of the
                        1-st element (x,px,y,py)
        orbit           (6, Nrefs) closed orbit vector at each location
                        specified in refpts

    KEYWORDS
        orbit=None      avoids looking for initial the closed orbit if is
                        already known ((6,) array). find_sync_orbit propagates
                        it to the specified refpts.
        guess           (6,) initial value for the closed orbit. It may help
                        convergence. Default: (0, 0, 0, 0, 0, 0)
        keep_lattice    Assume no lattice change since the previous tracking.
                        Default: False
        convergence     Convergence criterion. Default: 1.e-12
        max_iterations  Maximum number of iterations. Default: 20
        XYStep          Step size. Default: DConstant.XYStep

    See also find_orbit4, find_orbit6.
    """
    if orbit is None:
        if dp is not None:
            orbit = _orbit_dp(ring, dp, keep_lattice=keep_lattice, **kwargs)
        else:
            orbit = _orbit_dct(ring, dct, keep_lattice=keep_lattice, **kwargs)
        keep_lattice = True

    uint32refs = uint32_refpts(refpts, len(ring))
    # bug in numpy < 1.13
    all_points = numpy.empty(
        (0, 6), dtype=float) if len(uint32refs) == 0 else numpy.squeeze(
            lattice_pass(ring,
                         orbit.copy(order='K'),
                         refpts=uint32refs,
                         keep_lattice=keep_lattice),
            axis=(1, 3)).T
    return orbit, all_points