Source code for at.physics.rdt

"""Resonance Driving Terms"""
from __future__ import annotations

import numpy as np
import multiprocessing
from functools import partial
from ..lattice import Lattice, Refpts, Multipole, All
from enum import Enum
from collections.abc import Container


__all__ = ["get_rdts", "RDTType"]


[docs] class RDTType(Enum): """Enum class for RDT type""" ALL = 0 #: all available RDTs FOCUSING = 1 #: Normal quadrupole RDTs COUPLING = 2 #: Linear coupling RDTs CHROMATIC = 3 #: Chromatic RDTs #: Geometric RDTs from sextupoles GEOMETRIC1 = 4 #: Geometric RDTs from octupoles #: optionally includes the second order contribution of sextupoles GEOMETRIC2 = 5 #: Amplitude detuning coefficients #: optionally includes the second order contribution of sextupoles TUNESHIFT = 6 #: Chromatic RDTs from octupoles #: optionally includes the second order contribution of sextupoles CHROMATIC2 = 7
def _get_polynom(elem, attr, index): try: val = getattr(elem, attr)[index] return val except (IndexError, AttributeError): return 0 def _compute_pf(tune, nperiods): """This uses the formula Sum(x^k, k=1->p) = x(x^p-1)/(x-1)""" pf = np.ones((9, 9), dtype=complex) if nperiods != 1: for i in range(9): for j in range(9): a1 = np.pi * 2 * (tune[0] * (i - 4) + tune[1] * (j - 4)) a2 = a1 / nperiods pf[i][j] = (np.exp(1j * a1) - 1.0) / (np.exp(1j * a2) - 1.0) return pf def _computedrivingterms( s, betax, betay, phix, phiy, etax, a2l, b2l, b3l, b4l, tune, rdttype, nperiods, refpt, second_order, periodic_factor, ): """ Original implementation from ELEGANT """ def pf(i, j): return periodic_factor[4 + i][4 + j] rdts = {} rdts2 = {} rbetax = np.sqrt(betax) rbetay = np.sqrt(betay) px = np.exp(1j * phix) py = np.exp(1j * phiy) rdts["refpts"] = refpt mask_a2l = np.absolute(a2l) > 1.0e-6 mask_b2l = np.absolute(b2l) > 1.0e-6 mask_b3l = np.absolute(b3l) > 1.0e-6 mask_b4l = np.absolute(b4l) > 1.0e-6 if (RDTType.FOCUSING in rdttype) or (RDTType.ALL in rdttype): b2lm = b2l[mask_b2l] betaxm = betax[mask_b2l] betaym = betay[mask_b2l] pxm = px[mask_b2l] pym = py[mask_b2l] rdts["h20000"] = 1 / 8 * pf(2, 0) * sum(b2lm * betaxm * pxm * pxm) rdts["h00200"] = -1 / 8 * pf(0, 2) * sum(b2lm * betaym * pym * pym) if (RDTType.COUPLING in rdttype) or (RDTType.ALL in rdttype): a2lm = a2l[mask_a2l] rbetaxm = rbetax[mask_a2l] rbetaym = rbetay[mask_a2l] pxm = px[mask_a2l] pym = py[mask_a2l] rdts["h10010"] = 1 / 4 * pf(1, -1) * sum(a2lm * rbetaxm * rbetaym * pxm / pym) rdts["h10100"] = 1 / 4 * pf(1, 1) * sum(a2lm * rbetaxm * rbetaym * pxm * pym) if (RDTType.CHROMATIC in rdttype) or (RDTType.ALL in rdttype): mask_b23l = mask_b2l | mask_b3l b2lm = b2l[mask_b23l] b3lm = b3l[mask_b23l] betaxm = betax[mask_b23l] rbetaxm = rbetax[mask_b23l] betaym = betay[mask_b23l] etaxm = etax[mask_b23l] pxm = px[mask_b23l] pym = py[mask_b23l] rdts["h11001"] = nperiods * sum(b3lm * betaxm * etaxm / 2 - b2lm * betaxm / 4) rdts["h00111"] = nperiods * sum(b2lm * betaym / 4 - b3lm * betaym * etaxm / 2) # fmt: off rdts["h20001"] = ( 1 / 2 * pf(2, 0) * sum((b3lm * betaxm * etaxm / 2 - b2lm * betaxm / 4) * pxm * pxm) ) rdts["h00201"] = ( 1 / 2 * pf(0, 2) * sum((b2lm * betaym / 4 - b3lm * betaym * etaxm / 2) * pym * pym) ) rdts["h10002"] = 1 / 2 * pf(1, 0 ) * sum( (b3lm * rbetaxm * etaxm * etaxm - b2lm * rbetaxm * etaxm) * pxm ) # fmt: on if (RDTType.GEOMETRIC1 in rdttype) or (RDTType.ALL in rdttype): b3lm = b3l[mask_b3l] betaxm = betax[mask_b3l] betaym = betay[mask_b3l] rbetaxm = rbetax[mask_b3l] pxm = px[mask_b3l] pym = py[mask_b3l] rdts["h21000"] = 1 / 8 * pf(1, 0) * sum(b3lm * rbetaxm * betaxm * pxm) rdts["h30000"] = ( 1 / 24 * pf(3, 0) * sum(b3lm * rbetaxm * betaxm * pxm * pxm * pxm) ) rdts["h10110"] = -1 / 4 * pf(1, 0) * sum(b3lm * rbetaxm * betaym * pxm) rdts["h10020"] = ( -1 / 8 * pf(1, -2) * sum(b3lm * rbetaxm * betaym * pxm * np.conj(pym * pym)) ) rdts["h10200"] = ( -1 / 8 * pf(1, 2) * sum(b3lm * rbetaxm * betaym * pxm * pym * pym) ) if (RDTType.TUNESHIFT in rdttype) or (RDTType.ALL in rdttype): b4lm = b4l[mask_b4l] betaxm = betax[mask_b4l] betaym = betay[mask_b4l] rdts["dnux_dJx"] = 3 / (8 * np.pi) * nperiods * sum(b4lm * betaxm * betaxm) rdts["dnux_dJy"] = -3 / (4 * np.pi) * nperiods * sum(b4lm * betaxm * betaym) rdts["dnuy_dJy"] = 3 / (8 * np.pi) * nperiods * sum(b4lm * betaym * betaym) if (RDTType.GEOMETRIC2 in rdttype) or (RDTType.ALL in rdttype): b4lm = b4l[mask_b4l] betaxm = betax[mask_b4l] betaym = betay[mask_b4l] pxm = px[mask_b4l] pym = py[mask_b4l] rdts["h22000"] = 3 / 32 * nperiods * sum(b4lm * betaxm * betaxm) rdts["h11110"] = -3 / 8 * nperiods * sum(b4lm * betaxm * betaym) rdts["h00220"] = 3 / 32 * nperiods * sum(b4lm * betaym * betaym) rdts["h31000"] = 1 / 16 * pf(2, 0) * sum(b4lm * betaxm * betaxm * pxm * pxm) rdts["h40000"] = ( 1 / 64 * pf(4, 0) * sum(b4lm * betaxm * betaxm * pxm * pxm * pxm * pxm) ) rdts["h20110"] = -3 / 16 * pf(2, 0) * sum(b4lm * betaxm * betaym * pxm * pxm) rdts["h11200"] = -3 / 16 * pf(0, 2) * sum(b4lm * betaxm * betaym * pym * pym) # fmt: off rdts["h20020"] = ( -3 / 32 * pf(2, -2) * sum(b4lm * betaxm * betaym * pxm * pxm * np.conj(pym * pym)) ) # fmt: on rdts["h20200"] = ( -3 / 32 * pf(2, 2) * sum(b4lm * betaxm * betaym * pxm * pxm * pym * pym) ) rdts["h00310"] = 1 / 16 * pf(0, 2) * sum(b4lm * betaym * betaym * pym * pym) rdts["h00400"] = ( 1 / 64 * pf(0, 4) * sum(b4lm * betaym * betaym * pym * pym * pym * pym) ) if (RDTType.CHROMATIC2 in rdttype) or (RDTType.ALL in rdttype): b4lm = b4l[mask_b4l] betaxm = betax[mask_b4l] betaym = betay[mask_b4l] etaxm = etax[mask_b4l] pxm = px[mask_b4l] pym = py[mask_b4l] rbetaxm = np.sqrt(betaxm) etaxm2 = etaxm * etaxm cpym = np.conj(pym) rdts["h21001"] = 3 / 8 * pf(1, 0) * sum(b4lm * etaxm * rbetaxm * betaxm * pxm) rdts["h30001"] = ( 1 / 8 * pf(3, 0) * sum(b4lm * etaxm * rbetaxm * betaxm * pxm * pxm * pxm) ) # fmt: off rdts["h10021"] = ( -3 / 8 * pf(1, -2) * sum(b4lm * etaxm * rbetaxm * betaym * pxm * cpym * cpym) ) # fmt: on rdts["h10111"] = -3 / 4 * pf(1, 0) * sum(b4lm * etaxm * rbetaxm * betaym * pxm) rdts["h10201"] = ( -3 / 8 * pf(1, 2) * sum(b4lm * etaxm * rbetaxm * betaym * pxm * pym * pym) ) rdts["h11002"] = 3 / 4 * nperiods * sum(b4lm * etaxm2 * betaxm) rdts["h20002"] = 3 / 8 * pf(2, 0) * sum(b4lm * etaxm2 * betaxm * pxm * pxm) rdts["h00112"] = -3 / 4 * nperiods * sum(b4lm * etaxm2 * betaym) rdts["h00202"] = -3 / 8 * pf(0, 2) * sum(b4lm * etaxm2 * betaym * pym * pym) rdts["h10003"] = 1 / 2 * pf(1, 0) * sum(b4lm * etaxm2 * etaxm * rbetaxm * pxm) rdts["h00004"] = 1 / 4 * nperiods * sum(b4lm * etaxm2 * etaxm2) if second_order: assert nperiods == 1, "Second order available only for nperiods=1" if (RDTType.GEOMETRIC2 in rdttype) or (RDTType.ALL in rdttype): nelem = sum(mask_b3l) sm = s[mask_b3l] b3lm = b3l[mask_b3l] betaxm = betax[mask_b3l] betaym = betay[mask_b3l] rbetaxm = rbetax[mask_b3l] rbbetaxm = betaxm * rbetaxm phixm = phix[mask_b3l] phiym = phiy[mask_b3l] pxm = px[mask_b3l] pxm2 = pxm * pxm pxm3 = pxm2 * pxm cpxm = np.conj(pxm) cpxm3 = np.conj(pxm3) pym = py[mask_b3l] pym2 = pym * pym cpym2 = np.conj(pym2) cpxym2 = np.conj(pxm * pym2) rdts2.update( { "h22000": 0.0, "h31000": 0.0, "h11110": 0.0, "h11200": 0.0, "h40000": 0.0, "h20020": 0.0, "h20110": 0.0, "h20200": 0.0, "h00220": 0.0, "h00310": 0.0, "h00400": 0.0, } ) # fmt: off bsign = np.array([np.sign(sm[i] - sm) * 1j * b3lm[i] * b3lm for i in range(nelem)]) rbbx = np.array([bsign[i] * rbbetaxm[i] * rbbetaxm for i in range(nelem)]) rbxy = np.array([bsign[i] * rbetaxm[i] * rbetaxm * betaym[i] for i in range(nelem)]) ppxm = np.array([pxm[i] * pxm for i in range(nelem)]) rdts2["h22000"] += (1.0 / 64) * np.sum([ rbbx[i] * (pxm3[i] * cpxm3 + 3 * pxm[i] * cpxm) for i in range(nelem)] ) rdts2["h31000"] += (1.0 / 32) * np.sum([ rbbx[i] * pxm3[i] * cpxm for i in range(nelem)] ) t1 = np.array([np.conj(pxm[i]) * pxm for i in range(nelem)]) t2 = np.conj(t1) rdts2["h11110"] += (1.0 / 16) * np.sum([ rbxy[i] * (betaxm * (t1[i] - t2[i]) + betaym * pym2[i] * cpym2 * (t2[i] + t1[i])) for i in range(nelem)] ) t1 = np.array([np.exp(-1j * (phixm[i] - phixm)) for i in range(nelem)]) t2 = np.conj(t1) rdts2["h11200"] += (1.0 / 32) * np.sum([ rbxy[i] * np.exp(1j * (2 * phiym[i])) * (betaxm * (t1[i] - t2[i]) + 2 * betaym * (t2[i] + t1[i])) for i in range(nelem)] ) rdts2["h40000"] += (1.0 / 64) * np.sum([ rbbx[i] * pxm3[i] * pxm for i in range(nelem)] ) rdts2["h20020"] += (1.0 / 64) * np.sum([ rbxy[i] * (betaxm * cpxym2[i] * pxm3 - (betaxm + 4 * betaym) * ppxm[i] * cpym2[i]) for i in range(nelem)] ) rdts2["h20110"] += (1.0 / 32) * np.sum([ rbxy[i] * (betaxm * (cpxm[i] * pxm3 - ppxm[i]) + 2 * betaym * ppxm[i] * pym2[i] * cpym2) for i in range(nelem)] ) rdts2["h20200"] += (1.0 / 64) * np.sum([ rbxy[i] * (betaxm * cpxm[i] * pxm3 * pym2[i] - (betaxm - 4 * betaym) * ppxm[i] * pym2[i]) for i in range(nelem)] ) rdts2["h00220"] += (1.0 / 64) * np.sum([ rbxy[i] * betaym * (pxm[i] * pym2[i] * cpxym2 + 4 * pxm[i] * cpxm - np.conj(pxm[i] * pym2) * pxm * pym2[i]) for i in range(nelem)] ) rdts2["h00310"] += (1.0 / 32) * np.sum([ rbxy[i] * betaym * pym2[i] * (pxm[i] * cpxm - pxm * cpxm[i]) for i in range(nelem)] ) rdts2["h00400"] += (1.0 / 64) * np.sum([ rbxy[i] * betaym * pxm[i] * cpxm * pym2[i] * pym2 for i in range(nelem)] ) # fmt: on if (RDTType.CHROMATIC2 in rdttype) or (RDTType.ALL in rdttype): mask_b23l = mask_b2l | mask_b3l nelem = sum(mask_b23l) b2lm = b2l[mask_b23l] b3lm = b3l[mask_b23l] betxm = betax[mask_b23l] rbetxm = rbetax[mask_b23l] betym = betay[mask_b23l] etaxm = etax[mask_b23l] pxm = px[mask_b23l] pym = py[mask_b23l] betxm3o2 = betxm * rbetxm pxm2 = pxm * pxm pxm3 = pxm2 * pxm cpxm = np.conj(pxm) cpxm2 = np.conj(pxm2) pym2 = pym * pym cpym = np.conj(pym) cpym2 = np.conj(pym2) rdts2.update( { "h21001": 0.0, "h30001": 0.0, "h10021": 0.0, "h10111": 0.0, "h10201": 0.0, "h11002": 0.0, "h20002": 0.0, "h00112": 0.0, "h00202": 0.0, "h10003": 0.0, "h00004": 0.0, } ) # The variable imag_and_sign multiplies by -1 the expression in # ANL/APS/LS-330 March 10, 2012. Chun-xi Wang. Eq (46) # in order to follow AT sign convention. imag_and_sign = 1j * (np.tri(nelem, nelem, -1) - 1 + np.tri(nelem)) b2b2lm = np.array([imag_and_sign[i] * b2lm[i] * b2lm for i in range(nelem)]) b3b2lm = np.array([imag_and_sign[i] * b3lm[i] * b2lm for i in range(nelem)]) b2b3lm = np.array([imag_and_sign[i] * b2lm[i] * b3lm for i in range(nelem)]) b3b3lm = np.array([imag_and_sign[i] * b3lm[i] * b3lm for i in range(nelem)]) bx3o2bx = np.array([betxm3o2[i] * betxm for i in range(nelem)]) bxbx = np.array([betxm[i] * betxm for i in range(nelem)]) byby = np.array([betym[i] * betym for i in range(nelem)]) bxbx3o2etax = np.array( [betxm[i] * betxm3o2 * etaxm[i] for i in range(nelem)] ) rbxbxby = np.array([rbetxm[i] * betxm * betym[i] for i in range(nelem)]) rbxbyby = np.array([rbetxm[i] * betym[i] * betym for i in range(nelem)]) bxrbxbyetax = np.array( [betxm[i] * rbetxm * betym * etaxm[i] for i in range(nelem)] ) rbxbybyetax = np.array( [rbetxm * betym[i] * betym * etaxm[i] for i in range(nelem)] ) rbxbx3o2etax = np.array( [rbetxm[i] * betxm3o2 * etaxm[i] for i in range(nelem)] ) rbxrbxbyetax = np.array( [rbetxm[i] * rbetxm * betym * etaxm[i] for i in range(nelem)] ) bxrbxetaxetax = np.array( [betxm[i] * rbetxm * etaxm[i] * etaxm for i in range(nelem)] ) rbxbxetax = np.array([rbetxm[i] * betxm * etaxm[i] for i in range(nelem)]) rbxrbxetaxetax = np.array( [rbetxm[i] * rbetxm * etaxm[i] * etaxm for i in range(nelem)] ) # fmt: off rdts2["h21001"] += (1.0 / 32) * np.sum([ - 1 * b3b2lm[i] * bx3o2bx[i] * (pxm[i] + pxm3[i] * cpxm2 - 2 * cpxm[i] * pxm2) - 2 * b3b3lm[i] * bxbx3o2etax[i] * (pxm - 2 * pxm2[i] * cpxm + cpxm2[i] * pxm3) for i in range(nelem)] ) rdts2["h30001"] += (1.0 / 32) * np.sum([ - 1 * b3b2lm[i] * bx3o2bx[i] * (pxm3[i] - pxm[i] * pxm2) - 2 * b3b3lm[i] * bxbx3o2etax[i] * (pxm3 - pxm2[i] * pxm) for i in range(nelem)] ) rdts2["h10021"] += (1.0 / 32) * np.sum([ + 1 * b3b2lm[i] * rbxbxby[i] * (pxm[i] * cpym2[i] - cpxm[i] * pxm2 * cpym2[i]) + 2 * b3b2lm[i] * rbxbyby[i] * (pxm[i] * cpym2[i] - pxm[i] * cpym2) + 2 * b3b3lm[i] * bxrbxbyetax[i] * (pxm * cpym2 - pxm2[i] * cpxm * cpym2) - 4 * b3b3lm[i] * rbxbybyetax[i] * (pxm * cpym2[i] - pxm * cpym2) for i in range(nelem)] ) rdts2["h10111"] += (1.0 / 16) * np.sum([ + 1 * b3b2lm[i] * rbxbxby[i] * (pxm[i] - cpxm[i] * pxm2) + 1 * b3b2lm[i] * rbxbyby[i] * (pxm[i] * cpym2[i] * pym2 - pxm[i] * pym2[i] * cpym2) + 2 * b3b3lm[i] * bxrbxbyetax[i] * (pxm - pxm2[i] * cpxm) - 2 * b3b3lm[i] * rbxbybyetax[i] * (pxm * cpym2[i] * pym2 - pxm * pym2[i] * cpym2) for i in range(nelem)] ) rdts2["h10201"] += (1.0 / 32) * np.sum([ + 1 * b3b2lm[i] * rbxbxby[i] * (pxm[i] * pym2[i] - cpxm[i] * pxm2 * pym2[i]) - 2 * b3b2lm[i] * rbxbyby[i] * (pxm[i] * pym2[i] - pxm[i] * pym2) + 2 * b3b3lm[i] * bxrbxbyetax[i] * (pxm * pym2 - pxm2[i] * cpxm * pym2) + 4 * b3b3lm[i] * rbxbybyetax[i] * (pxm * pym2[i] - pxm * pym2) for i in range(nelem)] ) rdts2["h11002"] += (1.0 / 16) * np.sum([ + 1 * bxbx[i] * ((b2b2lm[i] - 2 * b3b2lm[i] * etaxm[i] + 4 * b3b3lm[i] * etaxm[i] * etaxm) * pxm2[i] * cpxm2 + 2 * b3b2lm[i] * etaxm[i] * cpxm2[i] * pxm2) + 2 * rbxbx3o2etax[i] * (b3b3lm[i] * etaxm[i] - b2b3lm[i]) * (pxm[i] * cpxm - cpxm[i] * pxm) for i in range(nelem)] ) rdts2["h20002"] += (1.0 / 16) * np.sum([ + bxbx[i] * ((b2b2lm[i] - 2 * b3b2lm[i] * etaxm[i] + 4 * b3b3lm[i] * etaxm[i] * etaxm) * pxm2[i] + 2 * b3b2lm[i] * etaxm[i] * pxm2) + rbxbx3o2etax[i] * (b3b3lm[i] * etaxm[i] - b2b3lm[i]) * (pxm[i] * pxm - cpxm[i] * pxm3) for i in range(nelem)] ) rdts2["h00112"] += (1.0 / 16) * np.sum([ + 1 * byby[i] * ((b2b2lm[i] - 2 * b3b2lm[i] * etaxm[i] + 4 * b3b3lm[i] * etaxm[i] * etaxm) * pym2[i] * cpym2 + 2 * b3b2lm[i] * etaxm[i] * cpym2[i] * pym2) - 2 * rbxrbxbyetax[i] * (b3b3lm[i] * etaxm[i] - b2b3lm[i]) * (pxm[i] * cpxm - cpxm[i] * pxm) for i in range(nelem)] ) rdts2["h00202"] += (1.0 / 16) * np.sum([ + byby[i] * ((b2b2lm[i] - 2 * b3b2lm[i] * etaxm[i] + 4 * b3b3lm[i] * etaxm[i] * etaxm) * pym2[i] + 2 * b3b2lm[i] * etaxm[i] * pym2) - rbxrbxbyetax[i] * (b3b3lm[i] * etaxm[i] - b2b3lm[i]) * (pxm[i] * cpxm * pym2 - cpxm[i] * pxm * pym2) for i in range(nelem)] ) rdts2["h10003"] += (1.0 / 8) * np.sum([ + 2 * b3b2lm[i] * bxrbxetaxetax[i] * (pxm - pxm2[i] * cpxm) + 1 * rbxbxetax[i] * (b2b2lm[i] - b3b2lm[i] * etaxm[i] + 2 * b3b3lm[i] * etaxm[i] * etaxm) * (pxm[i] - cpxm[i] * pxm2) for i in range(nelem)] ) rdts2["h00004"] += (1.0 / 4) * np.sum([ + rbxrbxetaxetax[i] * ((b2b2lm[i] - b3b2lm[i] * etaxm[i] + b3b3lm[i] * etaxm[i] * etaxm) * pxm[i] * cpxm + b3b2lm[i] * etaxm[i] * cpxm[i] * pxm) for i in range(nelem)] ) # fmt: on if (RDTType.TUNESHIFT in rdttype) or (RDTType.ALL in rdttype): nelem = sum(mask_b3l) b3lm = b3l[mask_b3l] betaxm = betax[mask_b3l] betaym = betay[mask_b3l] phixm = phix[mask_b3l] phiym = phiy[mask_b3l] nux = tune[0] nuy = tune[1] # fmt: off b3f = [b3lm[i] * b3lm / np.pi for i in range(nelem)] sqbx = [np.sqrt(betaxm[i] * betaxm) for i in range(nelem)] bbx = [betaxm[i] * betaxm for i in range(nelem)] bby = [betaym[i] * betaym for i in range(nelem)] dphix = [abs(phixm[i] - phixm) for i in range(nelem)] dphiy = [abs(phiym[i] - phiym) for i in range(nelem)] rdts["dnux_dJx"] += np.sum([ -b3f[i] / 16 * sqbx[i] * bbx[i] * (3 * np.cos(dphix[i] - np.pi * nux) / np.sin(np.pi * nux) + np.cos(3 * dphix[i] - 3 * np.pi * nux) / np.sin(3 * np.pi * nux)) for i in range(nelem)] ) rdts["dnux_dJy"] += np.sum([ b3f[i] / 8 * sqbx[i] * betaym[i] * (2 * betaxm * np.cos(dphix[i] - np.pi * nux) / np.sin(np.pi * nux) - betaym * np.cos(dphix[i] + 2 * dphiy[i] - np.pi * (nux + 2 * nuy)) / np.sin(np.pi * (nux + 2 * nuy)) + betaym * np.cos(dphix[i] - 2 * dphiy[i] - np.pi * (nux - 2 * nuy)) / np.sin(np.pi * (nux - 2 * nuy))) for i in range(nelem)] ) rdts["dnuy_dJy"] += np.sum([ -b3f[i] / 16 * sqbx[i] * bby[i] * (4 * np.cos(dphix[i] - np.pi * nux) / np.sin(np.pi * nux) + np.cos(dphix[i] + 2 * dphiy[i] - np.pi * (nux + 2 * nuy)) / np.sin(np.pi * (nux + 2 * nuy)) + np.cos(dphix[i] - 2 * dphiy[i] - np.pi * (nux - 2 * nuy)) / np.sin(np.pi * (nux - 2 * nuy))) for i in range(nelem)] ) # fmt: on return rdts, rdts2 def _get_rdtlist( idx_mag, beta, etax, phi, avemu, mu, smag, sall, pols, rdt_type, tune, nperiods, second_order, refpts, ): rdtlist = [] rdtlist2 = [] pf = _compute_pf(tune, nperiods) for ii in refpts: start_idx = sum(idx_mag < ii) beta_rot = np.roll(beta, -start_idx, axis=0) etax_rot = np.roll(etax, -start_idx) phi_rot = np.roll(phi, -start_idx, axis=0) - avemu[ii] if start_idx > 0: phi_rot[-start_idx:] += mu s_rot = np.roll(smag, -start_idx) - sall[ii] s_rot[-start_idx:] += sall[-1] pols_rot = np.roll(pols, -start_idx, axis=0) rdt, rdt2 = _computedrivingterms( s_rot, beta_rot[:, 0], beta_rot[:, 1], phi_rot[:, 0], phi_rot[:, 1], etax_rot, pols_rot[:, 0], pols_rot[:, 1], pols_rot[:, 2], pols_rot[:, 3], tune, rdt_type, nperiods, ii, second_order, pf, ) rdtlist.append(rdt) rdtlist2.append(rdt2) return rdtlist, rdtlist2
[docs] def get_rdts( ring: Lattice, refpts: Refpts, rdt_type: Container[RDTType] | RDTType, second_order: bool = False, use_mp: bool = False, pool_size: int = None, ): """Get the lattice Resonance Driving Terms :py:func:`get_rdts` computes the ring RDTs based on the original implementation from ELEGANT. For consistency, pyAT keeps the sign convention of the AT MATLAB interface. Refs [#]_ and [#]_ were used to calculate the magnitude of first and second order rdts and Ref. [#]_ for the focusing rdt, however, different sign conventions are used along the references and in the original implementation. To overcome this issue, first and second order rdts are provided as a total and also separately so the user can redefine these conventions if needed. The resonance base of :py:obj:`~RDTType.GEOMETRIC2` rdts is explained in Eq. (54) of Ref [#]_. The periodicity property of the lattice is automatically taken into account in the rdt calculation, however the calculation of the second order contribution of sextupoles to the :py:obj:`~RDTType.GEOMETRIC2` and :py:obj:`~RDTType.TUNESHIFT` RDT types can only be derived for periodicity=1 (i.e. full ring provided). Parameters: ring: :py:class:`.Lattice` object refpts: Element refpts at which the RDTs are calculated rdt_type: Type of RDTs to be calculated. Possible RDT types are: * :py:obj:`RDTType.ALL`: all available RDTs * :py:obj:`RDTType.FOCUSING`: Normal quadrupole RDTs * :py:obj:`RDTType.COUPLING`: Linear coupling RDTs from skew quadrupoles * :py:obj:`RDTType.CHROMATIC`: Chromatic RDTs from sextupoles and normal quadrupoles * :py:obj:`RDTType.CHROMATIC2`: Chromatic RDTs from octupoles. The second order contribution of sextupoles is added when *second_order* is True * :py:obj:`RDTType.GEOMETRIC1`: Geometric RDTs from sextupoles * :py:obj:`RDTType.GEOMETRIC2`: Geometric RDTs from octupoles. The second order contribution of sextupoles is added when *second_order* is True * :py:obj:`RDTType.TUNESHIFT`: Amplitude detuning coefficients. The second order contribution of sextupoles is added when *second_order* is True Keyword Args: second_order (bool): Compute second order terms. Computation is significantly longer using this method, use_mp (bool): Activate parallel calculation, pool_size (int): Number of processes used for parallelization. Returns: rdts (complex): rdt data at refpts, rdts2 (complex): contribution from sextupole second order terms Available only for :py:obj:`~RDTType.GEOMETRIC2` and :py:obj:`~RDTType.TUNESHIFT` terms, rdttot (complex): total rdts. **rdts** is a :py:class:`record array <numpy.recarray>` with fields: for :py:obj:`~RDTType.FOCUSING`: `h20000`, `h00200` for :py:obj:`~RDTType.COUPLING`: `h10010`, `h10100` for :py:obj:`~RDTType.CHROMATIC`: `h11001`, `h00111`, `h20001`, `h00201`, `h10002` for :py:obj:`~RDTType.GEOMETRIC1`: `h21000`, `h30000`, `h10110`, `h10020`, `h10200` for :py:obj:`~RDTType.GEOMETRIC2`: `h22000`, `h11110`, `h00220`, `h31000`, `h40000`, `h20110` `h11200`, `h20020`, `h20200`, `h00310`, `h00400` for :py:obj:`~RDTType.CHROMATIC2`: `h21001`, `h30001`, `h10021`, `h10111`, `h10201`, `h11002` `h20002`, `h00112`, `h00202`, `h10003`, `h00004` for :py:obj:`~RDTType.TUNESHIFT`: `dnux_dJx`, `dnux_dJy`, `dnuy_dJy` Example: >>> get_rdts(ring, reftps, [RDTType.COUPLING, RDTType.CHROMATIC]) References: .. [#] J.Bengtsson, SLS Note 9 / 97, March 7, 1997, with corrections per W.Guo (NSLS) .. [#] Revised to follow C.X.Wang AOP - TN - 2009 - 020 for second - order terms .. [#] A.Franchi et al. arxiv 1711.06589, PRAB 17.074001 .. [#] Chunxi Wang and Alex Chao. Notes on Lie algebraic analysis of achromats. SLAC/AP-100. Jan/1995 """ if not isinstance(rdt_type, Container): rdt_type = {rdt_type} nperiods = ring.periodicity if second_order: assert nperiods == 1, "Second order available only for ring.periodicity=1" refpts = ring.uint32_refpts(refpts) idx_mag = ring.get_uint32_index(Multipole) lo, avebeta, avemu, avedisp, *_ = ring.avlinopt(refpts=All) sall = ring.get_s_pos(All) smag = sall[idx_mag] beta = avebeta[idx_mag] etax = avedisp[idx_mag, 0] phi = avemu[idx_mag] pols = [ [ _get_polynom(e, "PolynomA", 1) * e.Length, _get_polynom(e, "PolynomB", 1) * e.Length, _get_polynom(e, "PolynomB", 2) * e.Length, _get_polynom(e, "PolynomB", 3) * e.Length, ] for e in ring[idx_mag] ] mu = lo[-1].mu tune = mu / 2.0 / np.pi * nperiods fun = partial( _get_rdtlist, idx_mag, beta, etax, phi, avemu, mu, smag, sall, pols, rdt_type, tune, nperiods, second_order, ) if use_mp: if pool_size is None: pool_size = min(len(refpts), multiprocessing.cpu_count()) ctx = multiprocessing.get_context() refpts = np.array_split(refpts, pool_size) with ctx.Pool(pool_size) as pool: results = pool.map(fun, refpts) rdtlist, rdtlist2 = zip(*results) rdtlist = np.concatenate(rdtlist) rdtlist2 = np.concatenate(rdtlist2) else: rdtlist, rdtlist2 = fun(refpts) rdts = {} rdts2 = {} rdttot = {} keylist = rdtlist[0].keys() for k in keylist: val = [rdt.get(k, None) for rdt in rdtlist] val2 = [rdt.get(k, None) for rdt in rdtlist2] if val[0] is not None: rdts[k] = np.array(val) rdttot[k] = np.array(val, dtype=complex) if val2[0] is not None: rdts2[k] = np.array(val2) rdttot[k] += np.array(val2, dtype=complex) rdts2["refpts"] = rdts["refpts"] rdttot["refpts"] = rdts["refpts"] ardts = np.rec.fromarrays(rdts.values(), names=list(rdts.keys())) ardts2 = np.rec.fromarrays(rdts2.values(), names=list(rdts2.keys())) ardttot = np.rec.fromarrays(rdttot.values(), names=list(rdttot.keys())) return ardts, ardts2, ardttot
Lattice.get_rdts = get_rdts