diff --git a/examples/Diagnostics/Tunes/inputs/sns_ring.lat b/examples/Diagnostics/Tunes/inputs/sns_ring.lat new file mode 100644 index 00000000..cbfb8df1 --- /dev/null +++ b/examples/Diagnostics/Tunes/inputs/sns_ring.lat @@ -0,0 +1,768 @@ +kvx12 := kdc; +kdc = -3.412580596; +kta11c12 := 0; +brho := 1e9*(pc/c); +pc := sqrt(ek*(ek+2*e0)); +ek := 0.8; +e0 := 0.93827231; +c := 299792458; +khx13 := kfc; +kfc = 3.161218767; +kta10b13 := 0; +kdhta13 := 0; +kdvta13 := 0; +vkck12 := 0; +hkck12 := 0; +vkck13 := 0; +hkck13 := 0; +kdvtb1 := 0; +ksc_b01 := 0; +kssb1_9 = 0; +kvx1 := kdee; +kdee = -2.579970923; +ktb1_9 := 0; +ksv1 := 0; +ksc_b02 := 0; +kssb2_8 = 0; +khx2 := kf; +kf = 3.388342139; +ktb2d8 := 0; +ksh2 := 0; +kdvtb3 := 0; +ksc_b03 := 0; +kvx3 := kd; +kd = -3.731394588; +ktb357 := 0; +ksv3 := chrm3; +chrm3 := 0; +kdhtb4 := 0; +khx4 := kf26; +kf26 := kf*(lf/lf26); +lf := 0.25; +lf26 := 0.2705; +kta4b6 := 0; +ksh4 := chrm4; +chrm4 := 0; +ksv5 := chrm5; +chrm5 := 0; +kvx5 := kd; +kdvtb5 := 0; +ksc_b05 := 0; +ksh6 := chrm6; +chrm6 := 0; +khx6 := kf26; +kdhtb6 := 0; +ksv7 := chrm7; +chrm7 := chrm3; +kvx7 := kd; +kdvtb7 := 0; +ksc_b07 := 0; +ko_b08 := 0; +khx8 := kf; +kdhtb8 := 0; +ksc_b08 := 0; +ko_b09 := 0; +kvx9 := kdee; +kdvtb9 := 0; +ksc_b09 := 0; +kdhtb10 := 0; +kdvtb10 := 0; +khx10 := kfc; +kvx11 := kdc; +ktb11d12 := 0; +kdhtb13 := 0; +kdvtb13 := 0; +kdvtc1 := 0; +ksc_c01 := 0; +kssc1_9 = 0; +ktc1_9 := 0; +kdhtc2 := 0; +kssc2_8 = 0; +ksc_c02 := 0; +kta2c8 := 0; +kdvtc3 := 0; +ksc_c03 := 0; +ktc357 := 0; +kdhtc4 := 0; +ktc4d6 := 0; +kdvtc5 := 0; +ksc_c05 := 0; +kdhtc6 := 0; +kdvtc7 := 0; +ksc_c07 := 0; +ko_c08 := 0; +kdhtc8 := 0; +ksc_c08 := 0; +ko_c09 := 0; +kdvtc9 := 0; +ksc_c09 := 0; +kdhtc10 := 0; +kdvtc10 := 0; +ktc10d13 := 0; +kdhtc13 := 0; +kdvtc13 := 0; +ksisol := 1.22174*kssol; +kssol := solfield/brho; +solfield = 0; +kdvtd1 := 0; +ksc_d01 := 0; +kssd1_9 = 0; +ktd1_9 := 0; +kdhtd2 := 0; +ksc_d02 := 0; +kssd2_8 = 0; +kdvtd3 := 0; +ksc_d03 := 0; +ktd357 := 0; +lsv3 := lsxt; +lsxt := 0.317; +kdhtd4 := 0; +kdvtd5 := 0; +ksc_d05 := 0; +kdhtd6 := 0; +kdvtd7 := 0; +ksc_d07 := 0; +ko_d08 := 0; +kdhtd8 := 0; +ksc_d08 := 0; +ko_d09 := 0; +kdvtd9 := 0; +ksc_d09 := 0; +kdhtd10 := 0; +kdvtd10 := 0; +kdhtd13 := 0; +kdvtd13 := 0; +kdvta1 := 0; +ksc_a01 := 0; +kssa1_9 = 0; +kta1_9 := 0; +kdhta2 := 0; +ksc_a02 := 0; +kssa2_8 = 0; +kdvta3 := 0; +ksc_a03 := 0; +kta357 := 0; +kdhta4 := 0; +kdvta5 := 0; +kdhta6 := 0; +kdvta7 := 0; +ksc_a07 := 0; +ko_a08 := 0; +kdhta8 := 0; +ksc_a08 := 0; +ko_a09 := 0; +kdvta9 := 0; +ksc_a09 := 0; +hkck10 := 0; +vkck10 := 0; +hkck11 := 0; +vkck11 := 0; +kdhta10 := 0; +kdvta10 := 0; +injm1: marker; +dh_a12: sbend,l:= 0.9903829659,angle:= 0.0436,e1:= -0.003,e2:= 0.0466; +dh_a13: sbend,l:= 0.8903221964,angle:= -0.0466,e1:= -0.0466,e2:= -6.938893904e-18; +qtv_a12: quadrupole,l:= 0.533,k1:=( kvx12 + kta11c12 ) / brho ; +injm4: marker; +qth_a13: quadrupole,l:= 0.673,k1:=( khx13 + kta10b13 ) / brho ; +bpm_a13: monitor; +dchv_a13: kicker,l:= 0,hkick:=kdhta13 ,vkick:=kdvta13 ; +ikickv_a12: vkicker,l:= 0.428,kick:=vkck12 ; +ikickh_a12: hkicker,l:= 0.428,kick:=hkck12 ; +ikickv_a13: vkicker,l:= 0.839,kick:=vkck13 ; +ikickh_a13: hkicker,l:= 0.839,kick:=hkck13 ; +injm2: marker; +dmcv_b01: vkicker,l:= 0,kick:=kdvtb1 ; +qsc_b01: multipole,knl:={ 0,ksc_b01 }; +ssxc_b01: multipole,ksl:={ 0, 0,kssb1_9 }; +bpm_b01: monitor; +qtv_b01: quadrupole,l:= 0.5,k1:=( kvx1 + ktb1_9 ) / brho ; +scv_b01: sextupole,l:= 0.354,k2:=ksv1 ; +dh_b01: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +dmch_b02: hkicker,l:= 0,kick:= 0; +qsc_b02: multipole,knl:={ 0,ksc_b02 }; +ssxc_b02: multipole,ksl:={ 0, 0,kssb2_8 }; +bpm_b02: monitor; +qth_b02: quadrupole,l:= 0.5,k1:=( khx2 + ktb2d8 ) / brho ; +sch_b02: sextupole,l:= 0.354,k2:=ksh2 ; +dh_b02: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +dmcv_b03: vkicker,l:= 0,kick:=kdvtb3 ; +qsc_b03: multipole,knl:={ 0,ksc_b03 }; +bpm_b03: monitor; +qtv_b03: quadrupole,l:= 0.5,k1:=( kvx3 + ktb357 ) / brho ; +sv_b03: sextupole,l:= 0.317,k2:=ksv3 ; +dh_b03: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +dmch_b04: hkicker,l:= 0,kick:=kdhtb4 ; +bpm_b04: monitor; +qth_b04: quadrupole,l:= 0.541,k1:=( khx4 - kta4b6 ) / brho ; +sh_b04: sextupole,l:= 0.33,k2:=ksh4 ; +dh_b04: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +sv_b05: sextupole,l:= 0.317,k2:=ksv5 ; +qtv_b05: quadrupole,l:= 0.5,k1:=( kvx5 + ktb357 ) / brho ; +bpm_b05: monitor; +dmcv_b05: vkicker,l:= 0,kick:=kdvtb5 ; +qsc_b05: multipole,knl:={ 0,ksc_b05 }; +dh_b06: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +sh_b06: sextupole,l:= 0.33,k2:=ksh6 ; +qth_b06: quadrupole,l:= 0.541,k1:=( khx6 - kta4b6 ) / brho ; +bpm_b06: monitor; +dmch_b06: hkicker,l:= 0,kick:=kdhtb6 ; +dh_b07: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +sv_b07: sextupole,l:= 0.317,k2:=ksv7 ; +qtv_b07: quadrupole,l:= 0.5,k1:=( kvx7 + ktb357 ) / brho ; +bpm_b07: monitor; +dmcv_b07: vkicker,l:= 0,kick:=kdvtb7 ; +qsc_b07: multipole,knl:={ 0,ksc_b07 }; +dh_b08: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +oct_b08: octupole,l:= 0.33,k3:=ko_b08 ; +qth_b08: quadrupole,l:= 0.5,k1:=( khx8 + ktb2d8 ) / brho ; +bpm_b08: monitor; +dmch_b08: hkicker,l:= 0,kick:=kdhtb8 ; +qsc_b08: multipole,knl:={ 0,ksc_b08 }; +ssxc_b08: multipole,ksl:={ 0, 0,kssb2_8 }; +dh_b09: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +oct_b09: octupole,l:= 0.33,k3:=ko_b09 ; +qtv_b09: quadrupole,l:= 0.5,k1:=( kvx9 + ktb1_9 ) / brho ; +bpm_b09: monitor; +dmcv_b09: vkicker,l:= 0,kick:=kdvtb9 ; +qsc_b09: multipole,knl:={ 0,ksc_b09 }; +ssxc_b09: multipole,ksl:={ 0, 0,kssb1_9 }; +dchv_b10: kicker,l:= 0,hkick:=kdhtb10 ,vkick:=kdvtb10 ; +bpm_b10: monitor; +qth_b10: quadrupole,l:= 0.673,k1:=( khx10 - kta10b13 ) / brho ; +qtv_b11: quadrupole,l:= 0.533,k1:=( kvx11 + ktb11d12 ) / brho ; +qtv_b12: quadrupole,l:= 0.533,k1:=( kvx12 + ktb11d12 ) / brho ; +qth_b13: quadrupole,l:= 0.673,k1:=( khx13 - kta10b13 ) / brho ; +bpm_b13: monitor; +dchv_b13: kicker,l:= 0,hkick:=kdhtb13 ,vkick:=kdvtb13 ; +dampkicker1: marker; +dampkicker2: marker; +qmmkicker: marker; +tunekicker: marker; +dmcv_c01: vkicker,l:= 0,kick:=kdvtc1 ; +qsc_c01: multipole,knl:={ 0,ksc_c01 }; +ssxc_c01: multipole,ksl:={ 0, 0,kssc1_9 }; +bpm_c01: monitor; +qtv_c01: quadrupole,l:= 0.5,k1:=( kvx1 + ktc1_9 ) / brho ; +scv_c01: sextupole,l:= 0.354,k2:=ksv1 ; +dh_c01: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +dmch_c02: hkicker,l:= 0,kick:=kdhtc2 ; +ssxc_c02: multipole,ksl:={ 0, 0,kssc2_8 }; +qsc_c02: multipole,knl:={ 0,ksc_c02 }; +bpm_c02: monitor; +qth_c02: quadrupole,l:= 0.5,k1:=( khx2 + kta2c8 ) / brho ; +sch_c02: sextupole,l:= 0.354,k2:=ksh2 ; +dh_c02: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +dmcv_c03: vkicker,l:= 0,kick:=kdvtc3 ; +qsc_c03: multipole,knl:={ 0,ksc_c03 }; +bpm_c03: monitor; +qtv_c03: quadrupole,l:= 0.5,k1:=( kvx3 + ktc357 ) / brho ; +sv_c03: sextupole,l:= 0.317,k2:=ksv3 ; +dh_c03: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +dmch_c04: hkicker,l:= 0,kick:=kdhtc4 ; +bpm_c04: monitor; +qth_c04: quadrupole,l:= 0.541,k1:=( khx4 + ktc4d6 ) / brho ; +sh_c04: sextupole,l:= 0.33,k2:=ksh4 ; +dh_c04: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +sv_c05: sextupole,l:= 0.317,k2:=ksv5 ; +qtv_c05: quadrupole,l:= 0.5,k1:=( kvx5 + ktc357 ) / brho ; +bpm_c05: monitor; +dmcv_c05: vkicker,l:= 0,kick:=kdvtc5 ; +qsc_c05: multipole,knl:={ 0,ksc_c05 }; +dh_c06: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +sh_c06: sextupole,l:= 0.33,k2:=ksh6 ; +qth_c06: quadrupole,l:= 0.541,k1:=( khx6 + ktc4d6 ) / brho ; +bpm_c06: monitor; +dmch_c06: hkicker,l:= 0,kick:=kdhtc6 ; +dh_c07: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +sv_c07: sextupole,l:= 0.317,k2:=ksv7 ; +qtv_c07: quadrupole,l:= 0.5,k1:=( kvx7 + ktc357 ) / brho ; +bpm_c07: monitor; +dmcv_c07: vkicker,l:= 0,kick:=kdvtc7 ; +qsc_c07: multipole,knl:={ 0,ksc_c07 }; +dh_c08: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +oct_c08: octupole,l:= 0.33,k3:=ko_c08 ; +qth_c08: quadrupole,l:= 0.5,k1:=( khx8 + kta2c8 ) / brho ; +bpm_c08: monitor; +dmch_c08: hkicker,l:= 0,kick:=kdhtc8 ; +qsc_c08: multipole,knl:={ 0,ksc_c08 }; +ssxc_c08: multipole,ksl:={ 0,kssc2_8 }; +dh_c09: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +oct_c09: octupole,l:= 0.33,k3:=ko_c09 ; +qtv_c09: quadrupole,l:= 0.5,k1:=( kvx9 + ktc1_9 ) / brho ; +bpm_c09: monitor; +dmcv_c09: vkicker,l:= 0,kick:=kdvtc9 ; +qsc_c09: multipole,knl:={ 0,ksc_c09 }; +ssxc_c09: multipole,ksl:={ 0, 0,kssc1_9 }; +ekick01: vkicker,l:= 0.4; +ekick02: vkicker,l:= 0.4; +ekick03: vkicker,l:= 0.4; +ekick04: vkicker,l:= 0.505; +ekick05: vkicker,l:= 0.505; +ekick06: vkicker,l:= 0.505; +ekick07: vkicker,l:= 0.505; +dchv_c10: kicker,l:= 0,hkick:=kdhtc10 ,vkick:=kdvtc10 ; +bpm_c10: monitor; +qth_c10: quadrupole,l:= 0.673,k1:=( khx10 + ktc10d13 ) / brho ; +qtv_c11: quadrupole,l:= 0.533,k1:=( kvx11 - kta11c12 ) / brho ; +ekick08: vkicker,l:= 0.4275; +ekick09: vkicker,l:= 0.4275; +ekick10: vkicker,l:= 0.4275; +ekick11: vkicker,l:= 0.4275; +ekick12: vkicker,l:= 0.39; +ekick13: vkicker,l:= 0.39; +ekick14: vkicker,l:= 0.39; +qtv_c12: quadrupole,l:= 0.533,k1:=( kvx12 - kta11c12 ) / brho ; +qth_c13: quadrupole,l:= 0.673,k1:=( khx13 + ktc10d13 ) / brho ; +bpm_c13: monitor; +dchv_c13: kicker,l:= 0,hkick:=kdhtc13 ,vkick:=kdvtc13 ; +scbdsol_c13a: solenoid,l:= 1.22174,ks:= 0,ksi:=ksisol ; +scbdsol_c13b: solenoid,l:= 1.22174,ks:= 0,ksi:=ksisol ; +dmcv_d01: vkicker,l:= 0,kick:=kdvtd1 ; +qsc_d01: multipole,knl:={ 0,ksc_d01 }; +ssxc_d01: multipole,ksl:={ 0,kssd1_9 }; +bpm_d01: monitor; +qtv_d01: quadrupole,l:= 0.5,k1:=( kvx1 + ktd1_9 ) / brho ; +scv_d01: sextupole,l:= 0.354,k2:=ksv1 ; +dh_d01: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +dmch_d02: hkicker,l:= 0,kick:=kdhtd2 ; +qsc_d02: multipole,knl:={ 0,ksc_d02 }; +ssxc_d02: multipole,ksl:={ 0, 0,kssd2_8 }; +bpm_d02: monitor; +qth_d02: quadrupole,l:= 0.5,k1:=( khx2 + ktb2d8 ) / brho ; +sch_d02: sextupole,l:= 0.354,k2:=ksh2 ; +dh_d02: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +dmcv_d03: vkicker,l:= 0,kick:=kdvtd3 ; +qsc_d03: multipole,knl:={ 0,ksc_d03 }; +bpm_d03: monitor; +qtv_d03: quadrupole,l:= 0.5,k1:=( kvx3 + ktd357 ) / brho ; +sv_d03: sextupole,l:=lsv3 ,k2:=ksv3 ; +dh_d03: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +dmch_d04: hkicker,l:= 0,kick:=kdhtd4 ; +bpm_d04: monitor; +qth_d04: quadrupole,l:= 0.541,k1:=( khx4 - ktc4d6 ) / brho ; +sh_d04: sextupole,l:= 0.33,k2:=ksh4 ; +dh_d04: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +sv_d05: sextupole,l:= 0.317,k2:=ksv5 ; +qtv_d05: quadrupole,l:= 0.5,k1:=( kvx5 + ktd357 ) / brho ; +bpm_d05: monitor; +dmcv_d05: vkicker,l:= 0,kick:=kdvtd5 ; +qsc_d05: multipole,knl:={ 0,ksc_d05 }; +dh_d06: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +sh_d06: sextupole,l:= 0.33,k2:=ksh6 ; +qth_d06: quadrupole,l:= 0.541,k1:=( khx6 - ktc4d6 ) / brho ; +bpm_d06: monitor; +dmch_d06: hkicker,l:= 0,kick:=kdhtd6 ; +dh_d07: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +sv_d07: sextupole,l:= 0.317,k2:=ksv7 ; +qtv_d07: quadrupole,l:= 0.5,k1:=( kvx7 + ktd357 ) / brho ; +bpm_d07: monitor; +dmcv_d07: vkicker,l:= 0,kick:=kdvtd7 ; +qsc_d07: multipole,knl:={ 0,ksc_d07 }; +dh_d08: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +oct_d08: octupole,l:= 0.33,k3:=ko_d08 ; +qth_d08: quadrupole,l:= 0.5,k1:=( khx8 + ktb2d8 ) / brho ; +bpm_d08: monitor; +dmch_d08: hkicker,l:= 0,kick:=kdhtd8 ; +qsc_d08: multipole,knl:={ 0,ksc_d08 }; +ssxc_d08: multipole,ksl:={ 0, 0,kssd2_8 }; +dh_d09: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +oct_d09: octupole,l:= 0.33,k3:=ko_d09 ; +qtv_d09: quadrupole,l:= 0.5,k1:=( kvx9 + ktd1_9 ) / brho ; +bpm_d09: monitor; +dmcv_d09: vkicker,l:= 0,kick:=kdvtd9 ; +qsc_d09: multipole,knl:={ 0,ksc_d09 }; +ssxc_d09: multipole,ksl:={ 0, 0,kssd1_9 }; +tunepickup: marker; +qmmpickup: marker; +wcm: marker; +bcm: marker; +dchv_d10: kicker,l:= 0,hkick:=kdhtd10 ,vkick:=kdvtd10 ; +bpm_d10: monitor; +qth_d10: quadrupole,l:= 0.673,k1:=( khx10 - ktc10d13 ) / brho ; +qtv_d11: quadrupole,l:= 0.533,k1:=( kvx11 - ktb11d12 ) / brho ; +cav_01: rfcavity,l:= 2.1466,volt:= 0.0133,harmon:= 1; +cav_02: rfcavity,l:= 2.1466,volt:= 0.0133,harmon:= 1; +cav_03: rfcavity,l:= 2.1466,volt:= 0.0133,harmon:= 1; +cav_04: rfcavity,l:= 2.1466,volt:= -0.02,harmon:= 2; +qtv_d12: quadrupole,l:= 0.533,k1:=( kvx12 - ktb11d12 ) / brho ; +qth_d13: quadrupole,l:= 0.673,k1:=( khx13 - ktc10d13 ) / brho ; +bpm_d13: monitor; +dchv_d13: kicker,l:= 0,hkick:=kdhtd13 ,vkick:=kdvtd13 ; +haloscanner1: marker; +wirescanner1: marker; +ipm1: marker; +ipm2: marker; +wirescanner2: marker; +haloscanner2: marker; +dmcv_a01: vkicker,l:= 0,kick:=kdvta1 ; +qsc_a01: multipole,knl:={ 0,ksc_a01 }; +ssxc_a01: multipole,ksl:={ 0, 0,kssa1_9 }; +bpm_a01: monitor; +qtv_a01: quadrupole,l:= 0.5,k1:=( kvx1 + kta1_9 ) / brho ; +scv_a01: sextupole,l:= 0.354,k2:=ksv1 ; +dh_a01: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +dmch_a02: hkicker,l:= 0,kick:=kdhta2 ; +qsc_a02: multipole,knl:={ 0,ksc_a02 }; +ssxc_a02: multipole,ksl:={ 0, 0,kssa2_8 }; +bpm_a02: monitor; +qth_a02: quadrupole,l:= 0.5,k1:=( khx2 + kta2c8 ) / brho ; +sch_a02: sextupole,l:= 0.354,k2:=ksh2 ; +dh_a02: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +dmcv_a03: vkicker,l:= 0,kick:=kdvta3 ; +qsc_a03: multipole,knl:={ 0,ksc_a03 }; +bpm_a03: monitor; +qtv_a03: quadrupole,l:= 0.5,k1:=( kvx3 + kta357 ) / brho ; +sv_a03: sextupole,l:= 0.317,k2:=ksv3 ; +dh_a03: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +dmch_a04: hkicker,l:= 0,kick:=kdhta4 ; +bpm_a04: monitor; +qth_a04: quadrupole,l:= 0.541,k1:=( khx4 + kta4b6 ) / brho ; +sh_a04: sextupole,l:= 0.33,k2:=ksh4 ; +dh_a04: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +sv_a05: sextupole,l:= 0.317,k2:=ksv5 ; +qtv_a05: quadrupole,l:= 0.5,k1:=( kvx5 + kta357 ) / brho ; +bpm_a05: monitor; +dmcv_a05: vkicker,l:= 0,kick:=kdvta5 ; +qsc_a05: multipole,knl:={ 0}; +dh_a06: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +sh_a06: sextupole,l:= 0.33,k2:=ksh6 ; +qth_a06: quadrupole,l:= 0.541,k1:=( khx6 + kta4b6 ) / brho ; +bpm_a06: monitor; +dmch_a06: hkicker,l:= 0,kick:=kdhta6 ; +dh_a07: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +sv_a07: sextupole,l:= 0.317,k2:=ksv7 ; +qtv_a07: quadrupole,l:= 0.5,k1:=( kvx7 + kta357 ) / brho ; +bpm_a07: monitor; +dmcv_a07: vkicker,l:= 0,kick:=kdvta7 ; +qsc_a07: multipole,knl:={ 0,ksc_a07 }; +dh_a08: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +oct_a08: octupole,l:= 0.33,k3:=ko_a08 ; +qth_a08: quadrupole,l:= 0.5,k1:=( khx8 + kta2c8 ) / brho ; +bpm_a08: monitor; +dmch_a08: hkicker,l:= 0,kick:=kdhta8 ; +qsc_a08: multipole,knl:={ 0,ksc_a08 }; +ssxc_a08: multipole,ksl:={ 0, 0,kssa2_8 }; +dh_a09: sbend,l:= 1.4407,angle:= 0.1963495408,e1:= 0,e2:= 0; +oct_a09: octupole,l:= 0.33,k3:=ko_a09 ; +qtv_a09: quadrupole,l:= 0.5,k1:=( kvx9 + kta1_9 ) / brho ; +bpm_a09: monitor; +dmcv_a09: vkicker,l:= 0,kick:=kdvta9 ; +qsc_a09: multipole,knl:={ 0,ksc_a09 }; +ssxc_a09: multipole,ksl:={ 0, 0,kssa1_9 }; +ikickh_a10: hkicker,l:= 0.839,kick:=hkck10 ; +ikickv_a10: vkicker,l:= 0.839,kick:=vkck10 ; +ikickh_a11: hkicker,l:= 0.428,kick:=hkck11 ; +ikickv_a11: vkicker,l:= 0.428,kick:=vkck11 ; +dchv_a10: kicker,l:= 0,hkick:=kdhta10 ,vkick:=kdvta10 ; +bpm_a10: monitor; +qth_a10: quadrupole,l:= 0.673,k1:=( khx10 + kta10b13 ) / brho ; +injm3: marker; +qtv_a11: quadrupole,l:= 0.533,k1:=( kvx11 + kta11c12 ) / brho ; +dh_a10: sbend,l:= 0.8632537742,angle:= -0.042,e1:= 0,e2:= -0.042; +dh_a11: sbend,l:= 0.8722394086,angle:= 0.045,e1:= 0.042,e2:= 0.003; +rnginjsol: sequence, l = 248.0098418; +injm1, at = 0; +dh_a12, at = 1.378195456; +dh_a13, at = 3.408731523; +qtv_a12, at = 7.004892621; +injm4, at = 7.693392621; +qth_a13, at = 8.029892621; +bpm_a13, at = 8.547265121; +dchv_a13, at = 8.683688621; +ikickv_a12, at = 10.59989262; +ikickh_a12, at = 11.13989262; +ikickv_a13, at = 12.66989262; +ikickh_a13, at = 13.82989262; +injm2, at = 14.24939262; +dmcv_b01, at = 14.92668062; +qsc_b01, at = 14.92668062; +ssxc_b01, at = 14.92668062; +bpm_b01, at = 15.12176002; +qtv_b01, at = 15.47989262; +scv_b01, at = 16.03310462; +dh_b01, at = 17.47998825; +dmch_b02, at = 18.92687188; +qsc_b02, at = 18.92687188; +ssxc_b02, at = 18.92687188; +bpm_b02, at = 19.11879438; +bpm_b02, at = 19.23008388; +qth_b02, at = 19.48008388; +sch_b02, at = 20.03329588; +dh_b02, at = 21.4801795; +dmcv_b03, at = 22.92706313; +qsc_b03, at = 22.92706313; +bpm_b03, at = 23.11546513; +qtv_b03, at = 23.48027513; +sv_b03, at = 24.01443713; +dh_b03, at = 25.48037076; +dmch_b04, at = 26.89474238; +bpm_b04, at = 27.11860888; +qth_b04, at = 27.48046638; +sh_b04, at = 28.08524038; +dh_b04, at = 29.48056201; +sv_b05, at = 30.94649564; +qtv_b05, at = 31.48065764; +bpm_b05, at = 31.84546764; +dmcv_b05, at = 32.03386964; +qsc_b05, at = 32.03386964; +dh_b06, at = 33.48075326; +sh_b06, at = 34.87607489; +qth_b06, at = 35.48084889; +bpm_b06, at = 35.84270639; +dmch_b06, at = 36.06657289; +dh_b07, at = 37.48094452; +sv_b07, at = 38.94687815; +qtv_b07, at = 39.48104015; +bpm_b07, at = 39.84585015; +dmcv_b07, at = 40.03425215; +qsc_b07, at = 40.03425215; +dh_b08, at = 41.48113577; +oct_b08, at = 42.9280194; +qth_b08, at = 43.4812314; +bpm_b08, at = 43.8425209; +dmch_b08, at = 44.0344434; +qsc_b08, at = 44.0344434; +ssxc_b08, at = 44.0344434; +dh_b09, at = 45.48132703; +oct_b09, at = 46.92821065; +qtv_b09, at = 47.48142265; +bpm_b09, at = 47.83955525; +dmcv_b09, at = 48.03463465; +qsc_b09, at = 48.03463465; +ssxc_b09, at = 48.03463465; +dchv_b10, at = 54.27762665; +bpm_b10, at = 54.41405015; +qth_b10, at = 54.93142265; +qtv_b11, at = 55.95642265; +qtv_b12, at = 69.00642265; +qth_b13, at = 70.03142265; +bpm_b13, at = 70.54879515; +dchv_b13, at = 70.68521865; +dampkicker1, at = 73.19024865; +dampkicker2, at = 73.69024765; +qmmkicker, at = 74.31523965; +tunekicker, at = 75.44023065; +dmcv_c01, at = 76.92821065; +qsc_c01, at = 76.92821065; +ssxc_c01, at = 76.92821065; +bpm_c01, at = 77.12329005; +qtv_c01, at = 77.48142265; +scv_c01, at = 78.03463465; +dh_c01, at = 79.48151828; +dmch_c02, at = 80.92840191; +ssxc_c02, at = 80.92840191; +qsc_c02, at = 80.92840191; +bpm_c02, at = 81.12032441; +qth_c02, at = 81.48161391; +sch_c02, at = 82.03482591; +dh_c02, at = 83.48170954; +dmcv_c03, at = 84.92859316; +qsc_c03, at = 84.92859316; +bpm_c03, at = 85.11699516; +qtv_c03, at = 85.48180516; +sv_c03, at = 86.01596716; +dh_c03, at = 87.48190079; +dmch_c04, at = 88.89627242; +bpm_c04, at = 89.12013892; +qth_c04, at = 89.48199642; +sh_c04, at = 90.08677042; +dh_c04, at = 91.48209204; +sv_c05, at = 92.94802567; +qtv_c05, at = 93.48218767; +bpm_c05, at = 93.84699767; +dmcv_c05, at = 94.03539967; +qsc_c05, at = 94.03539967; +dh_c06, at = 95.4822833; +sh_c06, at = 96.87760493; +qth_c06, at = 97.48237893; +bpm_c06, at = 97.84423643; +dmch_c06, at = 98.06810293; +dh_c07, at = 99.48247455; +sv_c07, at = 100.9484082; +qtv_c07, at = 101.4825702; +bpm_c07, at = 101.8473802; +dmcv_c07, at = 102.0357822; +qsc_c07, at = 102.0357822; +dh_c08, at = 103.4826658; +oct_c08, at = 104.9295494; +qth_c08, at = 105.4827614; +bpm_c08, at = 105.8440509; +dmch_c08, at = 106.0359734; +qsc_c08, at = 106.0359734; +ssxc_c08, at = 106.0359734; +dh_c09, at = 107.4828571; +oct_c09, at = 108.9297407; +qtv_c09, at = 109.4829527; +bpm_c09, at = 109.8410853; +dmcv_c09, at = 110.0361647; +qsc_c09, at = 110.0361647; +ssxc_c09, at = 110.0361647; +ekick01, at = 112.3299527; +ekick02, at = 112.8099527; +ekick03, at = 113.2899527; +ekick04, at = 113.8229527; +ekick05, at = 114.4079527; +ekick06, at = 114.9929527; +ekick07, at = 115.5779527; +dchv_c10, at = 116.2791567; +bpm_c10, at = 116.4155802; +qth_c10, at = 116.9329527; +qtv_c11, at = 117.9579527; +ekick08, at = 118.9059527; +ekick09, at = 119.4149527; +ekick10, at = 119.9249527; +ekick11, at = 120.4309527; +ekick12, at = 120.9202527; +ekick13, at = 121.3895527; +ekick14, at = 121.8606527; +qtv_c12, at = 131.0079527; +qth_c13, at = 132.0329527; +bpm_c13, at = 132.5503252; +dchv_c13, at = 132.6867487; +scbdsol_c13a, at = 134.3897627; +scbdsol_c13b, at = 138.0255227; +dmcv_d01, at = 138.9297407; +qsc_d01, at = 138.9297407; +ssxc_d01, at = 138.9297407; +bpm_d01, at = 139.1248201; +qtv_d01, at = 139.4829527; +scv_d01, at = 140.0361647; +dh_d01, at = 141.4830483; +dmch_d02, at = 142.9299319; +qsc_d02, at = 142.9299319; +ssxc_d02, at = 142.9299319; +bpm_d02, at = 143.1218544; +qth_d02, at = 143.4831439; +sch_d02, at = 144.0363559; +dh_d02, at = 145.4832396; +dmcv_d03, at = 146.9301232; +qsc_d03, at = 146.9301232; +bpm_d03, at = 147.1185252; +qtv_d03, at = 147.4833352; +sv_d03, at = 148.0174972; +dh_d03, at = 149.4834308; +dmch_d04, at = 150.8978024; +bpm_d04, at = 151.1216689; +qth_d04, at = 151.4835264; +sh_d04, at = 152.0883004; +dh_d04, at = 153.4836221; +sv_d05, at = 154.9495557; +qtv_d05, at = 155.4837177; +bpm_d05, at = 155.8485277; +dmcv_d05, at = 156.0369297; +qsc_d05, at = 156.0369297; +dh_d06, at = 157.4838133; +sh_d06, at = 158.879135; +qth_d06, at = 159.483909; +bpm_d06, at = 159.8457665; +dmch_d06, at = 160.069633; +dh_d07, at = 161.4840046; +sv_d07, at = 162.9499382; +qtv_d07, at = 163.4841002; +bpm_d07, at = 163.8489102; +dmcv_d07, at = 164.0373122; +qsc_d07, at = 164.0373122; +dh_d08, at = 165.4841958; +oct_d08, at = 166.9310795; +qth_d08, at = 167.4842915; +bpm_d08, at = 167.845581; +dmch_d08, at = 168.0375035; +qsc_d08, at = 168.0375035; +ssxc_d08, at = 168.0375035; +dh_d09, at = 169.4843871; +oct_d09, at = 170.9312707; +qtv_d09, at = 171.4844827; +bpm_d09, at = 171.8426153; +dmcv_d09, at = 172.0376947; +qsc_d09, at = 172.0376947; +ssxc_d09, at = 172.0376947; +tunepickup, at = 173.5472277; +qmmpickup, at = 174.6722197; +wcm, at = 175.7778927; +bcm, at = 176.7642687; +dchv_d10, at = 178.2806867; +bpm_d10, at = 178.4171102; +qth_d10, at = 178.9344827; +qtv_d11, at = 179.9594827; +cav_01, at = 183.0386827; +cav_02, at = 185.3358827; +cav_03, at = 187.6330827; +cav_04, at = 189.9302827; +qtv_d12, at = 193.0094827; +qth_d13, at = 194.0344827; +bpm_d13, at = 194.5518552; +dchv_d13, at = 194.6882787; +haloscanner1, at = 196.4528147; +wirescanner1, at = 196.6309197; +ipm1, at = 197.0093417; +ipm2, at = 199.3134267; +wirescanner2, at = 199.6918487; +haloscanner2, at = 199.8699527; +dmcv_a01, at = 200.9312707; +qsc_a01, at = 200.9312707; +ssxc_a01, at = 200.9312707; +bpm_a01, at = 201.1263501; +qtv_a01, at = 201.4844827; +scv_a01, at = 202.0376947; +dh_a01, at = 203.4845783; +dmch_a02, at = 204.931462; +qsc_a02, at = 204.931462; +ssxc_a02, at = 204.931462; +bpm_a02, at = 205.1233845; +qth_a02, at = 205.484674; +sch_a02, at = 206.037886; +dh_a02, at = 207.4847696; +dmcv_a03, at = 208.9316532; +qsc_a03, at = 208.9316532; +bpm_a03, at = 209.1200552; +qtv_a03, at = 209.4848652; +sv_a03, at = 210.0190272; +dh_a03, at = 211.4849609; +dmch_a04, at = 212.8993325; +bpm_a04, at = 213.123199; +qth_a04, at = 213.4850565; +sh_a04, at = 214.0898305; +dh_a04, at = 215.4851521; +sv_a05, at = 216.9510857; +qtv_a05, at = 217.4852477; +bpm_a05, at = 217.8500577; +dmcv_a05, at = 218.0384597; +qsc_a05, at = 218.0384597; +dh_a06, at = 219.4853434; +sh_a06, at = 220.880665; +qth_a06, at = 221.485439; +bpm_a06, at = 221.8472965; +dmch_a06, at = 222.071163; +dh_a07, at = 223.4855346; +sv_a07, at = 224.9514682; +qtv_a07, at = 225.4856302; +bpm_a07, at = 225.8504402; +dmcv_a07, at = 226.0388422; +qsc_a07, at = 226.0388422; +dh_a08, at = 227.4857259; +oct_a08, at = 228.9326095; +qth_a08, at = 229.4858215; +bpm_a08, at = 229.847111; +dmch_a08, at = 230.0390335; +qsc_a08, at = 230.0390335; +ssxc_a08, at = 230.0390335; +dh_a09, at = 231.4859171; +oct_a09, at = 232.9328008; +qtv_a09, at = 233.4860128; +bpm_a09, at = 233.8441454; +dmcv_a09, at = 234.0392248; +qsc_a09, at = 234.0392248; +ssxc_a09, at = 234.0392248; +ikickh_a10, at = 235.1360128; +ikickv_a10, at = 236.2960128; +ikickh_a11, at = 237.8260128; +ikickv_a11, at = 238.3660128; +dchv_a10, at = 240.2822168; +bpm_a10, at = 240.4186403; +qth_a10, at = 240.9360128; +injm3, at = 241.2725128; +qtv_a11, at = 241.9610128; +dh_a10, at = 245.1911396; +dh_a11, at = 247.5737221; +endsequence; diff --git a/examples/Diagnostics/Tunes/test_sns_2d.py b/examples/Diagnostics/Tunes/test_sns_2d.py new file mode 100644 index 00000000..cdd04265 --- /dev/null +++ b/examples/Diagnostics/Tunes/test_sns_2d.py @@ -0,0 +1,155 @@ +"""Test one-turn tune estimation in uncoupled lattice. + +This example tracks a Gaussian distribution through a FODO lattice. The tunes +are estimated from the phase space coordinates before/after tracking using the +`BunchTuneAnalysis` class. +""" +import argparse +import math +import os +import pathlib +import random +from pprint import pprint + +import numpy as np +import pandas as pd + +from orbit.core.bunch import Bunch +from orbit.core.bunch import BunchTwissAnalysis +from orbit.bunch_generators import TwissContainer +from orbit.bunch_generators import GaussDist2D +from orbit.diagnostics import TeapotTuneAnalysisNode +from orbit.diagnostics.matrix import TransferMatrixAnalysis +from orbit.teapot import TEAPOT_Lattice +from orbit.teapot import TEAPOT_MATRIX_Lattice +from orbit.utils.consts import mass_proton + +from utils import make_lattice_sns +from utils import get_tmat + + +# Arguments +parser = argparse.ArgumentParser() +parser.add_argument("--norm-from", type=str, default="tmat", choices=["tmat", "cov", "twiss"]) +args = parser.parse_args() + +# Setup +path = pathlib.Path(__file__) +output_dir = os.path.join("outputs", path.stem) +os.makedirs(output_dir, exist_ok=True) + +# Initialize lattice and bunch +lattice = make_lattice_sns() + +bunch = Bunch() +bunch.mass(mass_proton) +bunch.getSyncParticle().kinEnergy(1.000) + +# Calculate transfer matrix +matrix_lattice = TEAPOT_MATRIX_Lattice(lattice, bunch) +lattice_params = matrix_lattice.getRingParametersDict() +pprint(lattice_params) + +# Store some parameters as variables +lattice_alpha_x = lattice_params["alpha x"] +lattice_alpha_y = lattice_params["alpha y"] +lattice_beta_x = lattice_params["beta x [m]"] +lattice_beta_y = lattice_params["beta y [m]"] +lattice_eta_x = lattice_params["dispersion x [m]"] +lattice_etap_x = lattice_params["dispersion prime x"] + +# Add tune diagnostic node +tune_node = TeapotTuneAnalysisNode() + +if args.norm_from == "twiss": + tune_node.setNormMatrixFromTwiss( + betax=lattice_beta_x, + alphax=lattice_alpha_x, + etax=lattice_eta_x, + etapx=lattice_etap_x, + betay=lattice_beta_y, + alphay=lattice_alpha_y, + ) +elif args.norm_from == "tmat": + tmat = get_tmat(lattice, bunch) + tune_node.setNormMatrixFromTransferMatrix(tmat) +elif args.norm_from == "cov": + tmat = get_tmat(lattice, bunch) + tmat_analysis = TransferMatrixAnalysis(tmat) + cov_matrix = tmat_analysis.cov_matrix(1e-7, 1e-7) + tune_node.setNormMatrixFromCovMatrix(cov_matrix) +else: + raise ValueError("Invalid norm_from argument") + +lattice.getNodes()[0].addChildNode(tune_node, 0) + +# Generate particles +emittance_x = 0.1e-06 +emittance_y = 0.1e-06 +twiss_x = TwissContainer(lattice_alpha_x, lattice_beta_x, emittance_x) +twiss_y = TwissContainer(lattice_alpha_y, lattice_beta_y, emittance_y) +dist = GaussDist2D(twiss_x, twiss_y) + +for index in range(1000): + x, xp, y, yp = dist.getCoordinates() + z = random.uniform(-25.0, 25.0) + bunch.addParticle(x, xp, y, yp, z, 0.0) + +# Track particles +for turn in range(10): + lattice.trackBunch(bunch) + + twiss_calc = BunchTwissAnalysis() + twiss_calc.analyzeBunch(bunch) + xrms = 1000.0 * math.sqrt(twiss_calc.getCorrelation(0, 0)) + yrms = 1000.0 * math.sqrt(twiss_calc.getCorrelation(2, 2)) + print("turn={} xrms={:0.3f} yrms={:0.3f}".format(turn + 1, xrms, yrms)) + +# Test writing to file +filename = "bunch.dat" +filename = os.path.join(output_dir, filename) +bunch.dumpBunch(filename) + +# Collect phase data from bunch +phase_data = tune_node.getData(bunch) +phase_data = pd.DataFrame(phase_data) + +# Read phase data from file +particles = np.loadtxt(filename, comments="%") +particles = pd.DataFrame( + particles, + columns=[ # https://github.com/PyORBIT-Collaboration/PyORBIT3/issues/78 + "x", + "xp", + "y", + "yp", + "z", + "dE", + "phase_x", + "phase_y", + "tune_x", + "tune_y", + "action_x", + "action_y", + ], +) +print(particles.iloc[:, 6:]) + +# Check against tune from transfer matrix +tune_x_true = lattice_params["fractional tune x"] +tune_y_true = lattice_params["fractional tune y"] +tune_x_calc = np.mean(phase_data["tune_1"]) +tune_y_calc = np.mean(phase_data["tune_2"]) + +tune_x_err = tune_x_calc - tune_x_true +tune_y_err = tune_y_calc - tune_y_true + +print("tune_x_true", tune_x_true) +print("tune_x_calc", tune_x_calc) +print("tune_y_true", tune_y_true) +print("tune_y_calc", tune_y_calc) +print("tune_x_err", tune_x_err) +print("tune_y_err", tune_y_err) + +assert np.abs(tune_x_err) < 1.00e-06 +assert np.abs(tune_y_err) < 1.00e-06 diff --git a/examples/Diagnostics/Tunes/test_sns_4d.py b/examples/Diagnostics/Tunes/test_sns_4d.py new file mode 100644 index 00000000..be53b774 --- /dev/null +++ b/examples/Diagnostics/Tunes/test_sns_4d.py @@ -0,0 +1,107 @@ +"""Test one-turn tune estimation in coupled lattice.""" + +import argparse +import math +import os +import pathlib + +import numpy as np +import pandas as pd + +from orbit.core.bunch import Bunch +from orbit.core.bunch import BunchTwissAnalysis +from orbit.diagnostics import TeapotTuneAnalysisNode +from orbit.diagnostics.matrix import TransferMatrixAnalysis +from orbit.teapot import TEAPOT_Ring +from orbit.teapot import TEAPOT_MATRIX_Lattice +from orbit.utils.consts import mass_proton + +from utils import make_lattice_sns +from utils import get_tmat + + +# Arguments +parser = argparse.ArgumentParser() +parser.add_argument("--coupled", type=int, default=1) +parser.add_argument("--norm-from", type=str, default="tmat", choices=["tmat", "cov"]) +parser.add_argument("--eps1", type=float, default=1.0) +parser.add_argument("--eps2", type=float, default=1.01) +args = parser.parse_args() + +# Setup +path = pathlib.Path(__file__) +output_dir = os.path.join("outputs", path.stem) +os.makedirs(output_dir, exist_ok=True) + +# Initialize lattice and bunch +lattice = make_lattice_sns(sol=args.coupled) +lattice.initialize() + +bunch = Bunch() +bunch.mass(mass_proton) +bunch.getSyncParticle().kinEnergy(1.000) + +# Calculate transfer matrix +M = get_tmat(lattice, bunch) +tmat_analysis = TransferMatrixAnalysis(M) +tune_1_true = tmat_analysis.eigtunes[0] +tune_2_true = tmat_analysis.eigtunes[1] + +# Calculate matched covariance matrix +eps_1 = args.eps1 * 1e-6 +eps_2 = args.eps2 * 1e-6 +S_matched = tmat_analysis.cov_matrix(eps_1, eps_2) + +# Add tune diagnostic node +tune_node = TeapotTuneAnalysisNode() +if args.norm_from == "tmat": + tune_node.setNormMatrixFromTransferMatrix(M) +else: + tune_node.setNormMatrixFromCovMatrix(S_matched) +lattice.getNodes()[0].addChildNode(tune_node, 0) + +# Generate particles +rng = np.random.default_rng(123) +particles = np.zeros((1000, 6)) +particles[:, :4] = rng.multivariate_normal( + mean=np.zeros(4), cov=S_matched, size=particles.shape[0] +) +particles[:, 4] = rng.uniform(-25.0, 25.0, size=particles.shape[0]) +particles[:, 5] = 0.0 + +for index in range(particles.shape[0]): + bunch.addParticle(*particles[index]) + +# Track particles +for turn in range(10): + lattice.trackBunch(bunch) + + twiss_calc = BunchTwissAnalysis() + twiss_calc.analyzeBunch(bunch) + xrms = 1000.0 * math.sqrt(twiss_calc.getCorrelation(0, 0)) + yrms = 1000.0 * math.sqrt(twiss_calc.getCorrelation(2, 2)) + print("turn={} xrms={:0.3f} yrms={:0.3f}".format(turn + 1, xrms, yrms)) + +# Analysis +phase_data = tune_node.getData(bunch) # phase_data = pd.DataFrame(phase_data) + +tune_1_calc = np.mean(phase_data["tune_1"]) +tune_2_calc = np.mean(phase_data["tune_2"]) + +# Order depends on eps1/eps2. To check values against transfer matrix, +# sort tunes by magnitude. +if tune_1_calc < tune_2_calc: + (tune_1_calc, tune_2_calc) = (tune_2_calc, tune_1_calc) + +tune_1_err = tune_1_calc - tune_1_true +tune_2_err = tune_2_calc - tune_2_true + +print("tune_1_true", tune_1_true) +print("tune_1_calc", tune_1_calc) +print("tune_2_true", tune_2_true) +print("tune_2_calc", tune_2_calc) +print("tune_1_err", tune_1_err) +print("tune_2_err", tune_2_err) + +assert np.abs(tune_1_err) < 1.00e-05 +assert np.abs(tune_2_err) < 1.00e-05 diff --git a/examples/Diagnostics/Tunes/test_update_norm_mat.py b/examples/Diagnostics/Tunes/test_update_norm_mat.py new file mode 100644 index 00000000..4ae4c196 --- /dev/null +++ b/examples/Diagnostics/Tunes/test_update_norm_mat.py @@ -0,0 +1,99 @@ +"""Test updating normalization matrix during tracking. + +Average tunes are printed out after each turn. The normalization matrix +is updated twice during tracking. A warning message should print that +indicates the normalization matrix has changed, and that the tunes +will be inaccurate until the next tracked turn. +""" +import os +import pathlib +import random +from pprint import pprint + +import numpy as np + +from orbit.core.bunch import Bunch +from orbit.core.bunch import BunchTwissAnalysis +from orbit.bunch_generators import TwissContainer +from orbit.bunch_generators import GaussDist2D +from orbit.diagnostics import TeapotTuneAnalysisNode +from orbit.teapot import TEAPOT_MATRIX_Lattice +from orbit.utils.consts import mass_proton + +from utils import make_lattice_sns + +# Setup +path = pathlib.Path(__file__) +output_dir = os.path.join("outputs", path.stem) +os.makedirs(output_dir, exist_ok=True) + +# Initialize lattice and bunch +lattice = make_lattice_sns() + +bunch = Bunch() +bunch.mass(mass_proton) +bunch.getSyncParticle().kinEnergy(1.000) + +# Calculate transfer matrix +matrix_lattice = TEAPOT_MATRIX_Lattice(lattice, bunch) +lattice_params = matrix_lattice.getRingParametersDict() +pprint(lattice_params) + +# Store some parameters as variables +lattice_alpha_x = lattice_params["alpha x"] +lattice_alpha_y = lattice_params["alpha y"] +lattice_beta_x = lattice_params["beta x [m]"] +lattice_beta_y = lattice_params["beta y [m]"] +lattice_eta_x = lattice_params["dispersion x [m]"] +lattice_etap_x = lattice_params["dispersion prime x"] + +# Add tune diagnostic node +tune_node = TeapotTuneAnalysisNode() + +tune_node.setNormMatrixFromTwiss( + betax=lattice_beta_x, + alphax=lattice_alpha_x, + etax=lattice_eta_x, + etapx=lattice_etap_x, + betay=lattice_beta_y, + alphay=lattice_alpha_y, +) + +lattice.getNodes()[0].addChildNode(tune_node, 0) + +# Generate particles +emittance_x = 0.1e-06 +emittance_y = 0.1e-06 +twiss_x = TwissContainer(lattice_alpha_x, lattice_beta_x, emittance_x) +twiss_y = TwissContainer(lattice_alpha_y, lattice_beta_y, emittance_y) +dist = GaussDist2D(twiss_x, twiss_y) + +for index in range(10_000): + x, xp, y, yp = dist.getCoordinates() + z = random.uniform(-25.0, 25.0) + bunch.addParticle(x, xp, y, yp, z, 0.0) + +# Track particles +for turn in range(20): + lattice.trackBunch(bunch) + + + if turn >= 0: + phase_data = tune_node.getData(bunch) + nu_x = np.mean(phase_data["tune_1"]) + nu_y = np.mean(phase_data["tune_2"]) + + print("turn{} nux={:0.5f} nuy={:0.5f}".format(turn, nu_x, nu_y)) + + if turn == 5: + tune_node.setNormMatrixFromBunch(bunch) + + if turn == 10: + tune_node.setNormMatrixFromTwiss( + betax=lattice_beta_x, + alphax=lattice_alpha_x, + etax=lattice_eta_x, + etapx=lattice_etap_x, + betay=lattice_beta_y, + alphay=lattice_alpha_y, + ) diff --git a/examples/Diagnostics/Tunes/utils.py b/examples/Diagnostics/Tunes/utils.py new file mode 100644 index 00000000..dc25c6c3 --- /dev/null +++ b/examples/Diagnostics/Tunes/utils.py @@ -0,0 +1,38 @@ +import numpy as np + +from orbit.core.bunch import Bunch +from orbit.core.bunch import BunchTwissAnalysis +from orbit.lattice import AccLattice +from orbit.teapot import TEAPOT_Ring +from orbit.teapot import TEAPOT_MATRIX_Lattice + + +def get_tmat(lattice: AccLattice, bunch: Bunch) -> np.ndarray: + matrix_lattice = TEAPOT_MATRIX_Lattice(lattice, bunch) + + M = np.zeros((4, 4)) + for i in range(4): + for j in range(4): + M[i, j] = matrix_lattice.getOneTurnMatrix().get(i, j) + return M + + +def make_lattice_sns(sol: bool = False) -> TEAPOT_Ring: + lattice = TEAPOT_Ring() + lattice.readMADX("inputs/sns_ring.lat", "rnginjsol") + lattice.initialize() + + for node in lattice.getNodes(): + try: + node.setUsageFringeFieldIN(False) + node.setUsageFringeFieldOUT(False) + except: + pass + + for name in ["scbdsol_c13a", "scbdsol_c13b"]: + node = lattice.getNodeForName(name) + B = 0.0 + if sol: + B = 0.15 / 2.0 + node.setParam("B", B) + return lattice \ No newline at end of file diff --git a/py/orbit/diagnostics/TeapotDiagnosticsNode.py b/py/orbit/diagnostics/TeapotDiagnosticsNode.py index 4d7b2b42..ab26978f 100644 --- a/py/orbit/diagnostics/TeapotDiagnosticsNode.py +++ b/py/orbit/diagnostics/TeapotDiagnosticsNode.py @@ -1,247 +1,347 @@ -""" -This module is a collimator node class for TEAPOT lattice -""" +"""TEAPOT-style bunch diagnostic nodes.""" +from typing import IO -import os -import math +import numpy as np -# import the auxiliary classes -from ..utils import orbitFinalize, NamedObject, ParamsDictObject +from orbit.core.bunch import Bunch +from orbit.core.bunch import BunchTwissAnalysis +from orbit.core.bunch import BunchTuneAnalysis +from orbit.teapot import DriftTEAPOT -# import general accelerator elements and lattice -from ..lattice import AccNode, AccActionsContainer, AccNodeBunchTracker - - -# import Diagnostics classes -from .diagnostics import StatLats, StatLatsSetMember -from .diagnostics import Moments, MomentsSetMember, BPMSignal - -# import teapot drift class -from ..teapot import DriftTEAPOT - - -# import Bunch diagnostics -from orbit.core import bunch - -BunchTuneAnalysis = bunch.BunchTuneAnalysis +from .diagnostics import StatLats +from .diagnostics import StatLatsSetMember +from .diagnostics import Moments +from .diagnostics import MomentsSetMember +from .diagnostics import BPMSignal +from .matrix import build_norm_matrix_from_cov +from .matrix import build_norm_matrix_from_tmat +from .matrix import TransferMatrixAnalysis class TeapotStatLatsNode(DriftTEAPOT): - """ - The statlats node class for TEAPOT lattice - """ - - def __init__(self, filename, name="statlats no name"): - """ - Constructor. Creates the StatLats TEAPOT element. - """ + def __init__(self, filename: str, name: str = "statlats no name") -> None: DriftTEAPOT.__init__(self, name) self.statlats = StatLats(filename) self.setType("statlats teapot") self.setLength(0.0) self.position = 0.0 - self.lattlength = 0.0 + self.lattice_length = 0.0 self.file_out = open(filename, "w") - def track(self, paramsDict): - """ - The statlats-teapot class implementation of the AccNodeBunchTracker class track(probe) method. - """ - length = self.getLength(self.getActivePartIndex()) - bunch = paramsDict["bunch"] - self.statlats.writeStatLats(self.position, bunch, self.lattlength) + def track(self, params_dict: dict) -> None: + bunch = params_dict["bunch"] + self.statlats.writeStatLats(self.position, bunch, self.lattice_length) - def setPosition(self, pos): - self.position = pos + def setPosition(self, position: float) -> None: + self.position = position - def closeStatLats(self): + def closeStatLats(self) -> None: self.file_out.close() - def setLatticeLength(self, lattlength): - self.lattlength = lattlength + def setLatticeLength(self, length: float) -> None: + self.lattice_length = length class TeapotStatLatsNodeSetMember(DriftTEAPOT): - """ - The statlats node class for TEAPOT lattice - """ - - def __init__(self, file, name="statlats no name"): - """ - Constructor. Creates the StatLats TEAPOT element. - """ + def __init__(self, file: IO[str], name: str = "statlats no name") -> None: DriftTEAPOT.__init__(self, name) self.statlats = StatLatsSetMember(file) self.setType("statlats teapot") self.setLength(0.0) self.position = 0.0 - self.lattlength = 0.0 + self.lattice_length = 0.0 self.active = True self.file = file - def track(self, paramsDict): - """ - The statlats-teapot class implementation of the AccNodeBunchTracker class track(probe) method. - """ + def track(self, params_dict: dict) -> None: if self.active: - length = self.getLength(self.getActivePartIndex()) - bunch = paramsDict["bunch"] - self.statlats.writeStatLats(self.position, bunch, self.lattlength) + bunch = params_dict["bunch"] + self.statlats.writeStatLats(self.position, bunch, self.lattice_length) - def setPosition(self, pos): - self.position = pos + def setPosition(self, position: float) -> None: + self.position = position - def setLatticeLength(self, lattlength): - self.lattlength = lattlength + def setLatticeLength(self, length: float) -> None: + self.lattice_length = length - def activate(self): + def activate(self) -> None: self.active = True - def deactivate(self): + def deactivate(self) -> None: self.active = False - def resetFile(self, file): + def resetFile(self, file: IO[str]) -> None: self.file = file self.statlats.resetFile(self.file) class TeapotMomentsNode(DriftTEAPOT): - """ - The moments node class for TEAPOT lattice - """ - - def __init__(self, filename, order, nodispersion=True, emitnorm=False, name="moments no name"): - """ - Constructor. Creates the StatLats TEAPOT element. - """ + def __init__(self, filename: str, order: int, no_dispersion: bool = True, emit_norm: bool = False, name: str = "moments no name") -> None: DriftTEAPOT.__init__(self, name) - self.moments = Moments(filename, order, nodispersion, emitnorm) + self.moments = Moments(filename, order, no_dispersion, emit_norm) self.setType("moments teapot") self.setLength(0.0) self.position = 0.0 - self.lattlength = 0.0 + self.lattice_length = 0.0 self.file_out = open(filename, "w") - def track(self, paramsDict): - """ - The moments-teapot class implementation of the AccNodeBunchTracker class track(probe) method. - """ - length = self.getLength(self.getActivePartIndex()) - bunch = paramsDict["bunch"] - self.moments.writeMoments(self.position, bunch, self.lattlength) + def track(self, params_dict: dict) -> None: + bunch = params_dict["bunch"] + self.moments.writeMoments(self.position, bunch, self.lattice_length) - def setPosition(self, pos): - self.position = pos + def setPosition(self, position: float) -> None: + self.position = position - def closeMoments(self): + def closeMoments(self) -> None: self.file_out.close() - def setLatticeLength(self, lattlength): - self.lattlength = lattlength + def setLatticeLength(self, length: float) -> None: + self.lattice_length = length class TeapotMomentsNodeSetMember(DriftTEAPOT): - """ - The moments node class for TEAPOT lattice - """ - - def __init__(self, file, order, nodispersion=True, emitnorm=False, name="moments no name"): - """ - Constructor. Creates the Moments TEAPOT element. - """ + def __init__(self, file: IO[str], order: int, no_dispersion: bool = True, emit_norm: bool = False, name: str = "moments no name") -> None: DriftTEAPOT.__init__(self, str(name)) self.file = file - self.moments = MomentsSetMember(self.file, order, nodispersion, emitnorm) + self.moments = MomentsSetMember(self.file, order, no_dispersion, emit_norm) self.setType("moments teapot") self.setLength(0.0) self.position = 0.0 - self.lattlength = 0.0 + self.lattice_length = 0.0 self.active = True - def track(self, paramsDict): - """ - The moments-teapot class implementation of the AccNodeBunchTracker class track(probe) method. - """ + def track(self, params_dict: dict) -> None: if self.active: - length = self.getLength(self.getActivePartIndex()) - bunch = paramsDict["bunch"] - self.moments.writeMoments(self.position, bunch, self.lattlength) + bunch = params_dict["bunch"] + self.moments.writeMoments(self.position, bunch, self.lattice_length) - def setPosition(self, pos): - self.position = pos + def setPosition(self, position: float) -> None: + self.position = position - def setLatticeLength(self, lattlength): - self.lattlength = lattlength + def setLatticeLength(self, length: float) -> None: + self.lattice_length = length - def activate(self): + def activate(self) -> None: self.active = True - def deactivate(self): + def deactivate(self) -> None: self.active = False - def resetFile(self, file): + def resetFile(self, file: IO[str]) -> None: self.file = file self.moments.resetFile(self.file) class TeapotTuneAnalysisNode(DriftTEAPOT): - def __init__(self, name="tuneanalysis no name"): - """ - Constructor. Creates the StatLats TEAPOT element. - """ + """Estimates tunes from coordinates on neighboring turns. + + This node computes the tunes and actions of each particle in the bunch. + We use the Average Phase Advance (APA) method to estimate the tunes [1]. + We use only a single turn rather than the average of multiple turns. + + [1] https://cds.cern.ch/record/292773/files/p147.pdf + [2] https://arxiv.org/pdf/1207.5526 + [3] S. Y. Lee, *Accelerator Physics* + """ + def __init__(self, name: str = "TeapotTuneAnalysis no name") -> None: DriftTEAPOT.__init__(self, name) - self.bunchtune = BunchTuneAnalysis() + self.tune_calc = BunchTuneAnalysis() self.setType("tune calculator teapot") - self.lattlength = 0.0 self.setLength(0.0) self.position = 0.0 + self.active = True + self.keys = ["phase_1", "phase_2", "tune_1", "tune_2", "action_1", "action_2"] - def track(self, paramsDict): - """ - The bunchtuneanalysis-teapot class implementation of the AccNodeBunchTracker class track(probe) method. + def track(self, params_dict: dict) -> None: + if self.active: + self.tune_calc.analyzeBunch(params_dict["bunch"]) + + def activate(self) -> None: + self.active = True + + def deactivate(self) -> None: + self.active = False + + def setPosition(self, position: float) -> None: + self.position = position + + def setNormMatrix(self, norm_matrix: np.ndarray) -> None: + ndim = norm_matrix.shape[0] + for i in range(ndim): + for j in range(ndim): + self.tune_calc.setNormMatrixElement(i, j, norm_matrix[i, j]) + + def getNormMatrix(self) -> np.ndarray: + norm_matrix = np.zeros((6, 6)) + for i in range(6): + for j in range(6): + norm_matrix[i][j] = self.tune_calc.getNormMatrixElement(i, j) + return norm_matrix + + def setNormMatrixFromTwiss( + self, + betax: float, + alphax: float, + etax: float, + etapx: float, + betay: float, + alphay: float, + ) -> None: + """Set normalization matrix from Twiss parameters (x, y) and dispersion. + + betax{y}: Beta parameter in x{y} plane. + alphax{y}: Alpha parameter in x{y} plane. + etax: Dispersion in x plane. + etapx: Disperion prime in x plane. """ - length = self.getLength(self.getActivePartIndex()) - bunch = paramsDict["bunch"] - self.bunchtune.analyzeBunch(bunch) + self.tune_calc.setNormMatrixFromTwiss(betax, alphax, etax, etapx, betay, alphay) - def setPosition(self, pos): - self.position = pos + def setNormMatrixFromTransferMatrix(self, transfer_matrix: np.ndarray) -> None: + """Set normalization matrix from transfer matrix. - def setLatticeLength(self, lattlength): - self.lattlength = lattlength + Assumes transfer matrix is periodic and stable. - def assignTwiss(self, betax, alphax, etax, etapx, betay, alphay): - self.bunchtune.assignTwiss(betax, alphax, etax, etapx, betay, alphay) + Args: + transfer_matrix: 4x4 or 6x6 transfer matrix. + """ + assert transfer_matrix.shape[0] == transfer_matrix.shape[1] + assert transfer_matrix.shape[0] in (4, 6) + norm_matrix = build_norm_matrix_from_tmat(transfer_matrix) + self.setNormMatrix(norm_matrix) + + def setNormMatrixFromCovMatrix(self, cov_matrix: np.ndarray) -> None: + """Set normalization matrix from covariance matrix. + + Assume that S = M S M^T, where S is the covariance matrix + and M is the transfer matrix. Then M and SU (U is the Poisson matrix) + have different eigenvalues but the same eigenvectors So we can compute + the normalization matrix directly from SU, without knowing M. + + I'm not sure how to order the eigenvectors of SU as there is no + guaranteed ordering from np.linalg.eig. By default, we sort the + eigenvectors of SU by their eigenvalues (eigenemittances), so the + smallest eigenemittance is mode 1, the next is mode 2, and so on. + So if you compare this method to `setNormMatrixFromTransferMatrix`, + you may get {nu1, nu2} -> {nu2, nu1}. + + This is only a problem in coupled lattices with 4D normalization. + With 2D normalization there is no ambiguity. This function will + check if there are off-block-diagonal terms in the covariance + matrix to determine whether to use 2D or 4D normalization. + norm_matrix = build_norm_matrix_from_cov(cov_matrix) + self.setNormMatrix(norm_matrix) + + Args: + cov_matrix: 4x4 or 6x6 covariance matrix. + """ + assert cov_matrix.shape[0] == cov_matrix.shape[1] + assert cov_matrix.shape[0] in (4, 6) + norm_matrix = build_norm_matrix_from_cov(cov_matrix) + self.setNormMatrix(norm_matrix) + def setNormMatrixFromBunch(self, bunch: Bunch, dim: int = 4) -> None: + """Set normalization matrix from bunch covariance matrix. -class TeapotBPMSignalNode(DriftTEAPOT): - def __init__(self, name="BPMSignal no name"): + The bunch covariance matrix is calculated from the macroparticles. + + Args: + bunch: Bunch object with at least one macroparticle. + """ + assert dim in (4, 6) + assert bunch.getSizeGlobal() > 0 + twiss_calc = BunchTwissAnalysis() + twiss_calc.analyzeBunch(bunch) + cov_matrix = np.zeros((dim, dim)) + for i in range(dim): + for j in range(dim): + cov_matrix[i, j] = cov_matrix[j, i] = twiss_calc.getCorrelation(i, j) + self.setNormMatrixFromCovMatrix(cov_matrix) + + def getData(self, bunch: Bunch, index: int = None) -> dict[str, float] | dict[str, np.ndarray]: + """Return tune and action data. + + Args: + bunch: A Bunch object. + index: Particle index. If None, return data for all particles. + + Returns: + data: Dictionary with the following keys: + - "phase_1" + - "phase_2" + - "tune_1" + - "tune_2" + - "action_1" + - "action_2" + - "action_3" + + If `index` is provided, each value is a float. Otherwise each value + is a list of floats. If the lattice is uncoupled, 1->x and 2->y. """ - Constructor. Creates the StatLats TEAPOT element. + data = {} + if index is None: + for j, key in enumerate(self.keys): + data[key] = [] + for index in range(bunch.getSize()): + value = bunch.partAttrValue("ParticlePhaseAttributes", index, j) + data[key].append(value) + data[key] = np.array(data[key]) + else: + index = int(index) + bunch_size = bunch.getSize() + if (index < bunch_size): + raise ValueError("particle index < 0") + if (index > bunch_size - 1): + raise ValueError("particle index > bunch.getSize() - 1") + for j, key in enumerate(self.keys): + data[key] = bunch.partAttrValue("ParticlePhaseAttributes", index, j) + return data + + def getTunes(self, bunch: Bunch, index: int = None) -> tuple[float, float] | tuple[np.ndarray, np.ndarray]: + """Return fractional tunes (nu_1, nu_2). + + Args: + bunch: A Bunch object. + index: Particle index (not ID). + + Returns: + tune_1: Fractional tune (mode 1). + tune_2: Fractional tune (mode 2). """ + data = self.getData(bunch, index) + return tuple([data[key] for key in ["tune_1", "tune_2"]]) + + def getActions(self, bunch: Bunch, index: int) -> tuple[float, float] | tuple[np.ndarray, np.ndarray]: + """Return actions (J_1, J_2). + + Args: + bunch: Bunch object. + index: Particle index (not ID). + + Returns: + J_1: Action (mode 1). + J_2: Action (mode 2). + """ + data = self.getData(bunch, index) + return tuple([data[key] for key in ["action_1", "action_2"]]) + + +class TeapotBPMSignalNode(DriftTEAPOT): + def __init__(self, name: str = "BPMSignal no name") -> None: DriftTEAPOT.__init__(self, name) self.bpm = BPMSignal() self.setType("BPMSignal") - self.lattlength = 0.0 self.setLength(0.0) self.position = 0.0 - def track(self, paramsDict): - """ - The bunchtuneanalysis-teapot class implementation of the AccNodeBunchTracker class track(probe) method. - """ - length = self.getLength(self.getActivePartIndex()) - bunch = paramsDict["bunch"] - self.bpm.analyzeSignal(bunch) - - def setPosition(self, pos): - self.position = pos + def track(self, params_dict: dict) -> None: + bunch = params_dict["bunch"] + self.bpm.analyzeSignal(params_dict["bunch"]) - def setLatticeLength(self, lattlength): - self.lattlength = lattlength + def setPosition(self, position: float) -> None: + self.position = position - def getSignal(self): - xAvg = self.bpm.getSignalX() - yAvg = self.bpm.getSignalY() - return xAvg, yAvg + def getSignal(self) -> tuple[float, float]: + x_avg = self.bpm.getSignalX() + y_avg = self.bpm.getSignalY() + return x_avg, y_avg diff --git a/py/orbit/diagnostics/__init__.py b/py/orbit/diagnostics/__init__.py index c1d197a3..51c3da15 100644 --- a/py/orbit/diagnostics/__init__.py +++ b/py/orbit/diagnostics/__init__.py @@ -13,6 +13,7 @@ from .TeapotDiagnosticsNode import TeapotStatLatsNode, TeapotStatLatsNodeSetMember from .TeapotDiagnosticsNode import TeapotMomentsNode, TeapotMomentsNodeSetMember from .TeapotDiagnosticsNode import TeapotTuneAnalysisNode +from . import matrix __all__ = [] @@ -30,3 +31,4 @@ __all__.append("addTeapotMomentsNodeSet") __all__.append("TeapotTuneAnalysisNode") __all__.append("profiles") +__all__.append("matrix") diff --git a/py/orbit/diagnostics/matrix.py b/py/orbit/diagnostics/matrix.py new file mode 100644 index 00000000..36f51e66 --- /dev/null +++ b/py/orbit/diagnostics/matrix.py @@ -0,0 +1,124 @@ +import numpy as np + + +def calc_eigtune(eigval: float) -> float: + return np.arccos(np.real(eigval)) / (2.0 * np.pi) + + +def build_poisson_matrix(ndim: int) -> np.ndarray: + U = np.zeros((ndim, ndim)) + for i in range(0, ndim, 2): + U[i : i + 2, i : i + 2] = [[0.0, 1.0], [-1.0, 0.0]] + return U + + +def normalize_eigvec(v: np.ndarray) -> np.ndarray: + v = np.copy(v) + U = build_poisson_matrix(len(v)) + + def complex_amplitude(v): + return np.conj(v).T @ U @ v + + if np.imag(complex_amplitude(v)) > 0.0: + v = np.conj(v) + + v *= np.sqrt(2.0 / np.abs(complex_amplitude(v))) + + assert np.isclose(np.imag(complex_amplitude(v)), -2.0) + assert np.isclose(np.real(complex_amplitude(v)), +0.0) + return v + + +def build_norm_matrix_from_eigvecs(*eigvecs: list[np.ndarray]) -> np.ndarray: + dim = len(eigvecs[0]) + + V = np.zeros((dim, dim)) + for i, v in enumerate(eigvecs): + V[:, 2 * i + 0] = +np.real(v) + V[:, 2 * i + 1] = -np.imag(v) + return np.linalg.inv(V) + + +def symplectic_eig(matrix: np.ndarray) -> tuple[np.ndarray, np.ndarray]: + eigvals, eigvecs = np.linalg.eig(matrix) + eigvecs = eigvecs.T + + eigvals = eigvals[::2] + eigvecs = eigvecs[::2] + + for i in range(eigvecs.shape[0]): + eigvecs[i] = normalize_eigvec(eigvecs[i]) + + return eigvals, eigvecs + + +def build_norm_matrix_from_tmat(matrix: np.ndarray) -> np.ndarray: + eigvals, eigvecs = symplectic_eig(matrix) + return build_norm_matrix_from_eigvecs(*eigvecs) + + +def build_norm_matrix_from_cov_uncoupled(cov_matrix: np.ndarray) -> np.ndarray: + S = cov_matrix + U = build_poisson_matrix(S.shape[0]) + + V_inv = np.zeros_like(S) + for i in range(0, S.shape[0], 2): + eigvals, eigvecs = symplectic_eig(S[i:i+2, i:i+2] @ U[i:i+2, i:i+2]) + V_inv[i:i+2, i:i+2] = build_norm_matrix_from_eigvecs(*eigvecs) + return V_inv + + +def build_norm_matrix_from_cov(cov_matrix: np.ndarray) -> np.ndarray: + if not is_coupled(cov_matrix): + return build_norm_matrix_from_cov_uncoupled(cov_matrix) + + S = cov_matrix + U = build_poisson_matrix(S.shape[0]) + + eigvals, eigvecs = symplectic_eig(S @ U) + + emittances = np.abs(np.imag(eigvals)) + if np.all(np.abs(emittances - emittances[0]) < 1e-15): + raise ValueError("Eigenemittances are equal eigenvectors are degenerate and V will not be correct.") + + # Order by emittances + order = np.argsort(emittances) + eigvals = eigvals[order] + eigvecs = eigvecs[order] + return build_norm_matrix_from_eigvecs(*eigvecs) + + +def is_coupled(matrix: np.ndarray) -> bool: + assert matrix.ndim == 2 + assert matrix.shape[0] == matrix.shape[1] + assert matrix.shape[0] % 2 == 0 + matrix = np.copy(matrix) + for i in range(0, matrix.shape[0], 2): + matrix[i:i+2, i:i+2] = 0.0 + return not np.all(np.isclose(matrix, 0.0)) + + +class TransferMatrixAnalysis: + def __init__(self, M: np.ndarray) -> None: + self.M = M + self.ndim = M.shape[0] + + self.eigvals, self.eigvecs = np.linalg.eig(M) + self.eigvecs = self.eigvecs.T + self.eigvecs = self.eigvecs[::2] + for i in range(self.eigvecs.shape[0]): + self.eigvecs[i] = normalize_eigvec(self.eigvecs[i]) + + self.eigvals = self.eigvals[::2] + self.eigtunes = [calc_eigtune(eigval) for eigval in self.eigvals] + + self.V_inv = build_norm_matrix_from_eigvecs(*self.eigvecs) + self.V = np.linalg.inv(self.V_inv) + + self.is_coupled = is_coupled(M) + + def cov_matrix(self, *emittances: float) -> np.ndarray: + S = np.eye(self.ndim) + if len(emittances) > 0: + S = np.diag(np.repeat(emittances, 2)) + return self.V @ S @ self.V.T diff --git a/py/orbit/diagnostics/meson.build b/py/orbit/diagnostics/meson.build index 26e40c8e..a91a9016 100644 --- a/py/orbit/diagnostics/meson.build +++ b/py/orbit/diagnostics/meson.build @@ -6,7 +6,8 @@ py_sources = files([ 'TeapotDiagnosticsNode.py', 'diagnosticsLatticeModifications.py', '__init__.py', - 'profiles.py' + 'profiles.py', + 'matrix.py' ]) python.install_sources( diff --git a/src/orbit/BunchDiagnostics/BunchTuneAnalysis.cc b/src/orbit/BunchDiagnostics/BunchTuneAnalysis.cc index 12d11937..659b58a6 100644 --- a/src/orbit/BunchDiagnostics/BunchTuneAnalysis.cc +++ b/src/orbit/BunchDiagnostics/BunchTuneAnalysis.cc @@ -7,97 +7,155 @@ #include #include -/** Constructor */ -BunchTuneAnalysis::BunchTuneAnalysis(): CppPyWrapper(NULL) -{ - betax = 0; - alphax = 0; - etax = 0; - etapx = 0; - betay = 0; - alphay = 0; + +BunchTuneAnalysis::BunchTuneAnalysis(): CppPyWrapper(NULL) { + double matrix[6][6] = { + {1.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 1.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 1.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 1.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 1.0} + }; + int erase = 1; } -/** Destructor */ -BunchTuneAnalysis::~BunchTuneAnalysis() -{ + +BunchTuneAnalysis::~BunchTuneAnalysis() {} + +double BunchTuneAnalysis::getNormMatrixElement(int i, int j) { + return matrix[i][j]; } -void BunchTuneAnalysis::assignTwiss(double bx, double ax, double dx, double dpx, double by, double ay){ - betax = bx; - alphax = ax; - etax = dx; - etapx = dpx; - betay = by; - alphay = ay; +void BunchTuneAnalysis::setNormMatrixElement(int i, int j, double value) { + matrix[i][j] = value; + erase = 1; +} + +void BunchTuneAnalysis::setNormMatrixFromTwiss(double betax, double alphax, double etax, double etapx, double betay, double alphay) { + erase = 1; + + // Set V_{-1} = I + for (int i = 0; i < 6; i++) { + for (int j = 0; j < 6; j++) { + matrix[i][j] = 0.0; + } + } + for (int i = 0; i < 6; i++) { + matrix[i][i] = 1.0; + } + + // 2D normalization (x-x') + matrix[0][0] = 1.0 / sqrt(betax); + matrix[0][1] = 0.0; + matrix[1][0] = alphax / sqrt(betax); + matrix[1][1] = sqrt(betax); + + // 2D normalization (y-y') + matrix[2][2] = 1.0 / sqrt(betay); + matrix[2][3] = 0.0; + matrix[3][2] = alphay / sqrt(betay); + matrix[3][3] = sqrt(betay); + + // Dispersion (x-x') + matrix[0][5] = -etax / sqrt(betax); + matrix[1][5] = -etax * (alphax / sqrt(betax)) - etapx * sqrt(betax); + + // Dispersion (y-y') + double etay = 0.0; + double etapy = 0.0; + matrix[2][5] = -etay / sqrt(betay); + matrix[3][5] = -etay * (alphay / sqrt(betay)) - etapy * sqrt(betay); + } -/** Performs the Tune analysis of the bunch */ void BunchTuneAnalysis::analyzeBunch(Bunch* bunch){ - //initialization bunch->compress(); SyncPart* syncPart = bunch->getSyncPart(); double beta = syncPart->getBeta(); double** part_coord_arr = bunch->coordArr(); if(!bunch->hasParticleAttributes("ParticlePhaseAttributes")){ - cerr<<"adding particle phase information attribute\n"; + cerr<<"BunchTuneAnalysis: Adding particle phase information attribute.\n"; std::map tunemap; - tunemap.insert(std::make_pair("xLastPhase", 0)); - tunemap.insert(std::make_pair("yLastPhase", 0)); - tunemap.insert(std::make_pair("xLastTune", 0)); - tunemap.insert(std::make_pair("yLastTune", 0)); - tunemap.insert(std::make_pair("xAction", 0)); - tunemap.insert(std::make_pair("yAction", 0)); + tunemap.insert(std::make_pair("phase_1", 0)); + tunemap.insert(std::make_pair("phase_2", 0)); + tunemap.insert(std::make_pair("tune_1", 0)); + tunemap.insert(std::make_pair("tune_2", 0)); + tunemap.insert(std::make_pair("action_1", 0)); + tunemap.insert(std::make_pair("action_2", 0)); bunch->addParticleAttributes("ParticlePhaseAttributes", tunemap); } - if(bunch->hasParticleAttributes("ParticlePhaseAttributes")){ - for (int i=0; i < bunch->getSize(); i++) - { - double x = part_coord_arr[i][0]; + if (bunch->hasParticleAttributes("ParticlePhaseAttributes") && erase == 1) { + cerr<<"BunchTuneAnalysis: Normalization matrix has been updated. Setting particle phases to zero. Tunes will be accurate after the next `analyzeBunch` call.\n"; + for (int i=0; i < bunch->getSize(); i++) { + bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i, 0) = 0.0; + bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i, 1) = 0.0; + bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i, 2) = 0.0; + bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i, 3) = 0.0; + bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i, 4) = 0.0; + bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i, 5) = 0.0; + } + } + + if (bunch->hasParticleAttributes("ParticlePhaseAttributes")) { + for (int i=0; i < bunch->getSize(); i++) { + // Extract phase space coordinates + double x = part_coord_arr[i][0]; double xp = part_coord_arr[i][1]; - double y = part_coord_arr[i][2]; + double y = part_coord_arr[i][2]; double yp = part_coord_arr[i][3]; + double z = part_coord_arr[i][4]; double Etot = syncPart->getEnergy() + syncPart->getMass(); - double dpp = 1/(beta*beta)*part_coord_arr[i][5]/Etot; - - double xval = (x - etax * dpp)/sqrt(betax); - double xpval = (xp - etapx * dpp) * sqrt(betax) + xval * alphax; - double yval = y / sqrt(betay); - double ypval = (yp + y * alphay/betay) * sqrt(betay); - - double angle = atan2(xpval, xval); - if(angle < 0.) angle += (2.0*OrbitConst::PI); - double xPhase = angle; - double xPhaseOld = bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i,0); - double xTune = (xPhaseOld - xPhase) / (2.0*OrbitConst::PI); - if(xTune < 0.) xTune += 1.; - bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i, 0) = xPhase; - bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i, 2) = xTune; - - angle = atan2(ypval, yval); - if(angle < 0.) angle += (2.0*OrbitConst::PI); - double yPhase = angle; - double yPhaseOld = bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i,1); - double yTune = (yPhaseOld - yPhase) / (2.0*OrbitConst::PI); - if(yTune < 0.) yTune += 1.; - bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i, 1) = yPhase; - bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i, 3) = yTune; - - double xcanonical = x - etax * dpp; - double ycanonical = y; - double xpfac = xp - etapx * dpp; - double ypfac = yp; - double pxcanonical = xpfac + xcanonical * (alphax/betax); - double pycanonical = ypfac + ycanonical * (alphay/betay); - double xAction = xcanonical * xcanonical / betax + pxcanonical * pxcanonical * betax; - double yAction = ycanonical * ycanonical / betay + pycanonical * pycanonical * betay; - - bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i, 4) = xAction; - bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i, 5) = yAction; + double dpp = 1.0 / (beta * beta) * part_coord_arr[i][5] / Etot; + + // Normalize phase space coordinates + double u1 = matrix[0][0] * x + matrix[0][1] * xp + matrix[0][2] * y + matrix[0][3] * yp + matrix[0][4] * z + matrix[0][5] * dpp; + double u1p = matrix[1][0] * x + matrix[1][1] * xp + matrix[1][2] * y + matrix[1][3] * yp + matrix[1][4] * z + matrix[1][5] * dpp; + double u2 = matrix[2][0] * x + matrix[2][1] * xp + matrix[2][2] * y + matrix[2][3] * yp + matrix[2][4] * z + matrix[2][5] * dpp; + double u2p = matrix[3][0] * x + matrix[3][1] * xp + matrix[3][2] * y + matrix[3][3] * yp + matrix[3][4] * z + matrix[3][5] * dpp; + double u3 = matrix[4][0] * x + matrix[4][1] * xp + matrix[4][2] * y + matrix[4][3] * yp + matrix[4][4] * z + matrix[4][5] * dpp; + double u3p = matrix[5][0] * x + matrix[5][1] * xp + matrix[5][2] * y + matrix[5][3] * yp + matrix[5][4] * z + matrix[5][5] * dpp; + + // Compute phase advance (mode 1) + double angle = atan2(u1p, u1); + if (angle < 0.0) { + angle += (2.0 * OrbitConst::PI); + } + double phase1 = angle; + double phase1Old = bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i, 0); + double tune1 = (phase1Old - phase1) / (2.0 * OrbitConst::PI); + if (tune1 < 0.0) { + tune1 += 1.0; } + + // Compute phase advance (mode 2) + angle = atan2(u2p, u2); + if (angle < 0.0) { + angle += (2.0 * OrbitConst::PI); + } + double phase2 = angle; + double phase2Old = bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i, 1); + double tune2 = (phase2Old - phase2) / (2.0 * OrbitConst::PI); + if (tune2 < 0.0) { + tune2 += 1.0; + } + + // Compute actions + double action1 = (u1 * u1 + u1p * u1p) / 2.0; + double action2 = (u2 * u2 + u2p * u2p) / 2.0; + + // Update ParticlePhaseAttributes + bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i, 0) = phase1; + bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i, 1) = phase2; + bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i, 2) = tune1; + bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i, 3) = tune2; + bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i, 4) = action1; + bunch->getParticleAttributes("ParticlePhaseAttributes")->attValue(i, 5) = action2; + } + erase = 0; } -} +} \ No newline at end of file diff --git a/src/orbit/BunchDiagnostics/BunchTuneAnalysis.hh b/src/orbit/BunchDiagnostics/BunchTuneAnalysis.hh index b3701788..84b137b0 100644 --- a/src/orbit/BunchDiagnostics/BunchTuneAnalysis.hh +++ b/src/orbit/BunchDiagnostics/BunchTuneAnalysis.hh @@ -1,7 +1,6 @@ #ifndef BUNCH_TUNE_ANALYSIS_H #define BUNCH_TUNE_ANALYSIS_H -//pyORBIT utils #include "CppPyWrapper.hh" #include "Bunch.hh" @@ -9,40 +8,37 @@ using namespace std; -/** - The BunchTuneAnalysis class calculates the particle tunes -*/ +/** Estimates particle tunes using average phase advance (APA) over one turn. */ class BunchTuneAnalysis: public OrbitUtils::CppPyWrapper { public: - /** Constructor*/ BunchTuneAnalysis(); /** Destructor */ virtual ~BunchTuneAnalysis(); - /** Performs the Twiss analysis of the bunch */ + /** Estimates tunes. */ void analyzeBunch(Bunch* bunch); - //** Assigns Twiss values at location of calculator */ - void assignTwiss(double bx, double ax, double dx, double dpx, double by, double ay); + /** Sets element of normalization matrix. */ + void setNormMatrixElement(int i, int j, double value); - /** Returns the average value for coordinate with index ic */ - double getTune(int ic); + /** Returns element of normalization matrix. */ + double getNormMatrixElement(int i, int j); + /** Sets normalization matrix based on uncoupled Twiss parameters. */ + void setNormMatrixFromTwiss(double betax, double alphax, double etax, double etapx, double betay, double alphay); private: - //** Twiss */ - double betax; - double alphax; - double etax; - double etapx; - double betay; - double alphay; + // Normalization matrix V^{-1} + double matrix[6][6]; + // Flag to erase stored phase/amplitude info. + int erase; }; + #endif //endif for BUNCH_TUNE_ANALYSIS_H diff --git a/src/orbit/BunchDiagnostics/wrap_bunch_tune_analysis.cc b/src/orbit/BunchDiagnostics/wrap_bunch_tune_analysis.cc index cce58059..3adeff0a 100644 --- a/src/orbit/BunchDiagnostics/wrap_bunch_tune_analysis.cc +++ b/src/orbit/BunchDiagnostics/wrap_bunch_tune_analysis.cc @@ -16,134 +16,162 @@ namespace wrap_bunch_tune_analysis{ extern "C" { #endif - /** - Constructor for python class wrapping c++ BunchTuneAnalysis instance. - It never will be called directly. - */ - static PyObject* BunchTuneAnalysis_new(PyTypeObject *type, PyObject *args, PyObject *kwds){ - pyORBIT_Object* self; - self = (pyORBIT_Object *) type->tp_alloc(type, 0); - self->cpp_obj = NULL; - return (PyObject *) self; + +/** +Constructor for Python class wrapping C++ BunchTuneAnalysis instance. +It never will be called directly. +*/ +static PyObject* BunchTuneAnalysis_new(PyTypeObject *type, PyObject *args, PyObject *kwds){ + pyORBIT_Object* self; + self = (pyORBIT_Object *) type->tp_alloc(type, 0); + self->cpp_obj = NULL; + return (PyObject *) self; +} + +/** Implementation of the __init__ method */ +static int BunchTuneAnalysis_init(pyORBIT_Object *self, PyObject *args, PyObject *kwds){ + self->cpp_obj = new BunchTuneAnalysis(); + ((BunchTuneAnalysis*) self->cpp_obj)->setPyWrapper((PyObject*) self); + return 0; +} + +/** Analyzes the bunch. */ +static PyObject* BunchTuneAnalysis_analyzeBunch(PyObject *self, PyObject *args){ + BunchTuneAnalysis* cpp_BunchTuneAnalysis = (BunchTuneAnalysis*)((pyORBIT_Object*) self)->cpp_obj; + PyObject* pyBunch; + if(!PyArg_ParseTuple(args,"O:analyzeBunch", &pyBunch)){ + ORBIT_MPI_Finalize("BunchTuneAnalysis - analyzeBunch(Bunch* bunch) - parameter are needed."); + } + PyObject* pyORBIT_Bunch_Type = wrap_orbit_bunch::getBunchType("Bunch"); + if(!PyObject_IsInstance(pyBunch,pyORBIT_Bunch_Type)){ + ORBIT_MPI_Finalize("BunchTuneAnalysis - analyzeBunch(Bunch* bunch) - method needs a Bunch."); } + Bunch* cpp_bunch = (Bunch*) ((pyORBIT_Object*)pyBunch)->cpp_obj; + cpp_BunchTuneAnalysis->analyzeBunch(cpp_bunch); + Py_INCREF(Py_None); + return Py_None; +} - /** This is implementation of the __init__ method */ - static int BunchTuneAnalysis_init(pyORBIT_Object *self, PyObject *args, PyObject *kwds){ - self->cpp_obj = new BunchTuneAnalysis(); - ((BunchTuneAnalysis*) self->cpp_obj)->setPyWrapper((PyObject*) self); - return 0; - } - - /** Performs the Tune analysis of the bunch */ - static PyObject* BunchTuneAnalysis_analyzeBunch(PyObject *self, PyObject *args){ - BunchTuneAnalysis* cpp_BunchTuneAnalysis = (BunchTuneAnalysis*)((pyORBIT_Object*) self)->cpp_obj; - PyObject* pyBunch; - if(!PyArg_ParseTuple(args,"O:analyzeBunch",&pyBunch)){ - ORBIT_MPI_Finalize("BunchTuneAnalysis - analyzeBunch(Bunch* bunch) - parameter are needed."); - } - PyObject* pyORBIT_Bunch_Type = wrap_orbit_bunch::getBunchType("Bunch"); - if(!PyObject_IsInstance(pyBunch,pyORBIT_Bunch_Type)){ - ORBIT_MPI_Finalize("BunchTuneAnalysis - analyzeBunch(Bunch* bunch) - method needs a Bunch."); - } - Bunch* cpp_bunch = (Bunch*) ((pyORBIT_Object*)pyBunch)->cpp_obj; - cpp_BunchTuneAnalysis->analyzeBunch(cpp_bunch); - Py_INCREF(Py_None); - return Py_None; - } - - /** Performs the Tune analysis of the bunch */ - static PyObject* BunchTuneAnalysis_assignTwiss(PyObject *self, PyObject *args){ - BunchTuneAnalysis* cpp_BunchTuneAnalysis = (BunchTuneAnalysis*)((pyORBIT_Object*) self)->cpp_obj; - double betax; - double alphax; - double etax; - double etapx; - double betay; - double alphay; - if(!PyArg_ParseTuple(args,"dddddd:assignTwiss",&betax, &alphax,&etax,&etapx,&betay,&alphay)){ - ORBIT_MPI_Finalize("BunchTuneAnalysis - getTwiss(double betax, double alphax, double etax, double etapx, double betay, double alphay) - parameter are needed."); - } - cpp_BunchTuneAnalysis->assignTwiss(betax, alphax, etax, etapx, betay, alphay); - Py_INCREF(Py_None); - return Py_None; +/** Sets normalization matrix element. */ +static PyObject* BunchTuneAnalysis_setNormMatrixElement(PyObject *self, PyObject *args){ + BunchTuneAnalysis* cpp_BunchTuneAnalysis = (BunchTuneAnalysis*)((pyORBIT_Object*) self)->cpp_obj; + double value; + int i; + int j; + if(!PyArg_ParseTuple(args,"iid:setNormMatrixElement", &i, &j, &value)){ + ORBIT_MPI_Finalize("BunchTuneAnalysis - setNormMatrixElement(int i, int j, double value) - parameters are needed."); } + cpp_BunchTuneAnalysis->setNormMatrixElement(i, j, value); + Py_INCREF(Py_None); + return Py_None; +} +/** Gets normalization matrix element. */ +static PyObject* BunchTuneAnalysis_getNormMatrixElement(PyObject *self, PyObject *args){ + BunchTuneAnalysis* cpp_BunchTuneAnalysis = (BunchTuneAnalysis*)((pyORBIT_Object*) self)->cpp_obj; + int i; + int j; + if(!PyArg_ParseTuple(args,"ii:getNormMatrixElement", &i, &j)){ + ORBIT_MPI_Finalize("BunchTuneAnalysis - getNormMatrixElement(int i, int j) - parameters are needed."); + } + double value = cpp_BunchTuneAnalysis->getNormMatrixElement(i, j); + return Py_BuildValue("d", value); + Py_INCREF(Py_None); + return Py_None; +} - //-------------------------------------------------------------- - //destructor for python BunchTuneAnalysis class (__del__ method). - //--------------------------------------------------------------- - static void BunchTuneAnalysis_del(pyORBIT_Object* self){ - //std::cerr<<"The BunchTuneAnalysis __del__ has been called!"<cpp_obj); - self->ob_base.ob_type->tp_free((PyObject*)self); - } - - // defenition of the methods of the python BunchTuneAnalysis wrapper class - // they will be vailable from python level - static PyMethodDef BunchTuneAnalysisClassMethods[] = { - { "analyzeBunch", BunchTuneAnalysis_analyzeBunch, METH_VARARGS,"Performs the Tune analysis of the bunch."}, - { "assignTwiss", BunchTuneAnalysis_assignTwiss, METH_VARARGS,"Assigns Twiss at location of tune calculator."}, - {NULL} - }; - - // defenition of the memebers of the python BunchTwissAnalysis wrapper class - // they will be vailable from python level - static PyMemberDef BunchTuneAnalysisClassMembers [] = { - {NULL} - }; - - //new python BunchTwissAnalysis wrapper type definition - static PyTypeObject pyORBIT_BunchTuneAnalysis_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "BunchTuneAnalysis", /*tp_name*/ - sizeof(pyORBIT_Object), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor) BunchTuneAnalysis_del , /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - "The BunchTuneAnalysis python wrapper", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - BunchTuneAnalysisClassMethods, /* tp_methods */ - BunchTuneAnalysisClassMembers, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc) BunchTuneAnalysis_init, /* tp_init */ - 0, /* tp_alloc */ - BunchTuneAnalysis_new, /* tp_new */ - }; - - - //-------------------------------------------------- - //Initialization BunchTwissAnalysis of the pyBunchTwissAnalysis class - //-------------------------------------------------- - void initbunchtuneanalysis(PyObject* module){ - if (PyType_Ready(&pyORBIT_BunchTuneAnalysis_Type) < 0) return; - Py_INCREF(&pyORBIT_BunchTuneAnalysis_Type); - PyModule_AddObject(module, "BunchTuneAnalysis", (PyObject *)&pyORBIT_BunchTuneAnalysis_Type); +/** Sets normalization matrix based on uncoupled Twiss parameters. */ +static PyObject* BunchTuneAnalysis_setNormMatrixFromTwiss(PyObject *self, PyObject *args){ + BunchTuneAnalysis* cpp_BunchTuneAnalysis = (BunchTuneAnalysis*)((pyORBIT_Object*) self)->cpp_obj; + double betax; + double alphax; + double etax; + double etapx; + double betay; + double alphay; + double etay; + double etapy; + if(!PyArg_ParseTuple(args,"dddddd:setNormMatrixFromTwiss",&betax, &alphax, &etax, &etapx, &betay, &alphay)){ + ORBIT_MPI_Finalize("BunchTuneAnalysis - getTwiss(double betax, double alphax, double etax, double etapx, double betay, double alphay) - parameter are needed."); } + cpp_BunchTuneAnalysis->setNormMatrixFromTwiss(betax, alphax, etax, etapx, betay, alphay); + Py_INCREF(Py_None); + return Py_None; +} + +/** Destructor for python BunchTuneAnalysis class (__del__ method). */ +static void BunchTuneAnalysis_del(pyORBIT_Object* self){ + //std::cerr<<"The BunchTuneAnalysis __del__ has been called!"<cpp_obj); + self->ob_base.ob_type->tp_free((PyObject*)self); +} + +// Definition of the methods of the python BunchTuneAnalysis wrapper class. +// They will be available from python level. +static PyMethodDef BunchTuneAnalysisClassMethods[] = { + { "analyzeBunch", BunchTuneAnalysis_analyzeBunch, METH_VARARGS,"Analyzes the bunch."}, + { "setNormMatrixElement", BunchTuneAnalysis_setNormMatrixElement, METH_VARARGS,"Sets normalization matrix element."}, + { "getNormMatrixElement", BunchTuneAnalysis_getNormMatrixElement, METH_VARARGS,"Gets normalization matrix element."}, + { "setNormMatrixFromTwiss", BunchTuneAnalysis_setNormMatrixFromTwiss, METH_VARARGS,"Sets normalization matrix based on uncoupled Twiss parameters."}, + {NULL} +}; + +// Definition of the memebers of the python BunchTuneAnalysis wrapper class. +// They will be vailable from python level. +static PyMemberDef BunchTuneAnalysisClassMembers [] = { + {NULL} +}; + +// New python BunchTuneAnalysis wrapper type definition. +static PyTypeObject pyORBIT_BunchTuneAnalysis_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "BunchTuneAnalysis", /*tp_name*/ + sizeof(pyORBIT_Object), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor) BunchTuneAnalysis_del , /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "The BunchTuneAnalysis python wrapper", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + BunchTuneAnalysisClassMethods, /* tp_methods */ + BunchTuneAnalysisClassMembers, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc) BunchTuneAnalysis_init, /* tp_init */ + 0, /* tp_alloc */ + BunchTuneAnalysis_new, /* tp_new */ +}; + +// Initialization of the pyBunchTuneAnalysis class. +void initbunchtuneanalysis(PyObject* module){ + if (PyType_Ready(&pyORBIT_BunchTuneAnalysis_Type) < 0) return; + Py_INCREF(&pyORBIT_BunchTuneAnalysis_Type); + PyModule_AddObject(module, "BunchTuneAnalysis", (PyObject *)&pyORBIT_BunchTuneAnalysis_Type); +} + #ifdef __cplusplus } diff --git a/src/orbit/BunchDiagnostics/wrap_bunch_tune_analysis.hh b/src/orbit/BunchDiagnostics/wrap_bunch_tune_analysis.hh index af0f0c0a..6511745e 100644 --- a/src/orbit/BunchDiagnostics/wrap_bunch_tune_analysis.hh +++ b/src/orbit/BunchDiagnostics/wrap_bunch_tune_analysis.hh @@ -7,7 +7,7 @@ extern "C" { #endif - namespace wrap_bunch_tune_analysis{ + namespace wrap_bunch_tune_analysis { void initbunchtuneanalysis(PyObject* module); }