diff --git a/mbtrack2/instability/ions.py b/mbtrack2/instability/ions.py index ee20a309c184c3af144ba4221554aecb02fd263e..8ddd8f7eac65c80ca4f28dc71f263719b9b41a3f 100644 --- a/mbtrack2/instability/ions.py +++ b/mbtrack2/instability/ions.py @@ -31,7 +31,7 @@ def ion_cross_section(ring, ion): by a relativistic electron using the relativistic Bethe asymptotic formula [1]. - Values of M02 and C02 from [2] and [3] (values of constants are independent of beam energy). + Values of M02 and C02 from [2-4] (values of constants are independent of beam energy). Parameters ---------- @@ -52,6 +52,9 @@ def ion_cross_section(ring, ion): [2] : P. F. Tavares, "Bremsstrahlung detection of ions trapped in the EPA electron beam", Part. Accel. 43 (1993). [3] : A. G. Mathewson, S. Zhang, "Beam-gas ionisation cross sections at 7.0 TEV", CERN Tech. rep. Vacuum-Technical-Note-96-01. https://cds.cern.ch/record/1489148/ + [4] : F. Rieke and W. Prepejchal, "Ionization Cross Sections of Gaseous + Atoms and Molecules for High-Energy Electrons and Positrons", + Phys. Rev. A 6, 1507 (1972). """ if ion == "CO": @@ -66,6 +69,9 @@ def ion_cross_section(ring, ion): elif ion == "CH4": M02 = 4.23 C0 = 42.85 + elif ion == "H20": + M02 = 3.24 + C0 = 32.26 else: raise NotImplementedError @@ -235,6 +241,10 @@ def fast_beam_ion( A = 28 elif ion == "H2": A = 2 + elif ion == "H2O": + A = 16 + elif ion == "CO2": + A = 44 sigma_i = ion_cross_section(ring, ion) diff --git a/mbtrack2/tracking/particles.py b/mbtrack2/tracking/particles.py index 395de32462c2d3a0692a8ec2ff88ab9ede3eac81..a96919fd71ebe6d2d2ebcc334ad4109535467728 100644 --- a/mbtrack2/tracking/particles.py +++ b/mbtrack2/tracking/particles.py @@ -282,16 +282,37 @@ class Bunch: """ Return the bunch emittance for each plane. """ - cor = np.squeeze([[self[name] - self[name].mean()] for name in self]) - emitX = np.sqrt( - np.mean(cor[0]**2) * np.mean(cor[1]**2) - - np.mean(cor[0] * cor[1])**2) - emitY = np.sqrt( - np.mean(cor[2]**2) * np.mean(cor[3]**2) - - np.mean(cor[2] * cor[3])**2) - emitS = np.sqrt( - np.mean(cor[4]**2) * np.mean(cor[5]**2) - - np.mean(cor[4] * cor[5])**2) + + cov_x = np.cov(self['x'], self['xp']) + cov_y = np.cov(self['y'], self['yp']) + cov_z = np.cov(self['tau'], self['delta']) + + if (np.array(self.ring.optics.local_dispersion) != np.array([0, 0, 0, 0])).all(): + cov_xdelta = np.cov(self['x'], self['delta']) + cov_xpdelta = np.cov(self['xp'], self['delta']) + cov_ydelta = np.cov(self['y'], self['delta']) + cov_ypdelta = np.cov(self['yp'], self['delta']) + + sig11 = cov_x[ + 0, 0] - cov_xdelta[0, 1] * cov_xdelta[0, 1] / cov_z[1, 1] + sig12 = cov_x[ + 0, 1] - cov_xdelta[0, 1] * cov_xpdelta[0, 1] / cov_z[1, 1] + sig22 = cov_x[ + 1, 1] - cov_xpdelta[0, 1] * cov_xpdelta[0, 1] / cov_z[1, 1] + emitX = np.sqrt(sig11*sig22 - sig12*sig12) + + sig11 = cov_y[ + 0, 0] - cov_ydelta[0, 1] * cov_ydelta[0, 1] / cov_z[1, 1] + sig12 = cov_y[ + 0, 1] - cov_ydelta[0, 1] * cov_ypdelta[0, 1] / cov_z[1, 1] + sig22 = cov_y[ + 1, 1] - cov_ypdelta[0, 1] * cov_ypdelta[0, 1] / cov_z[1, 1] + emitY = np.sqrt(sig11*sig22 - sig12*sig12) + else: + emitX = np.sqrt(np.linalg.det(cov_x)) + emitY = np.sqrt(np.linalg.det(cov_y)) + + emitS = np.sqrt(np.linalg.det(cov_z)) return np.array([emitX, emitY, emitS]) @property @@ -303,15 +324,19 @@ class Bunch: Twiss parameters need to be computed using the ring.get_longitudinal_twiss method. """ - Jx = ((self.ring.optics.local_gamma[0] * self["x"]**2) + - (2 * self.ring.optics.local_alpha[0] * self["x"]) * self["xp"] + - (self.ring.optics.local_beta[0] * self["xp"]**2)) - Jy = ((self.ring.optics.local_gamma[1] * self["y"]**2) + - (2 * self.ring.optics.local_alpha[1] * self["y"] * self["yp"]) + - (self.ring.optics.local_beta[1] * self["yp"]**2)) - Js = ((self.ring.long_gamma * self["tau"]**2) + - (2 * self.ring.long_alpha * self["tau"] * self["delta"]) + - (self.ring.long_beta * self["delta"]**2)) + xb = self['x'] - self['delta'] * self.ring.optics.local_dispersion[0] + yb = self['y'] - self['delta'] * self.ring.optics.local_dispersion[2] + xpb = self['xp'] - self['delta'] * self.ring.optics.local_dispersion[1] + ypb = self['yp'] - self['delta'] * self.ring.optics.local_dispersion[3] + Jx = (self.ring.optics.local_gamma[0] * xb**2) + \ + (2*self.ring.optics.local_alpha[0] *xb*xpb) + \ + (self.ring.optics.local_beta[0] * xpb**2) + Jy = (self.ring.optics.local_gamma[1] * yb**2) + \ + (2*self.ring.optics.local_alpha[1] * yb*ypb) + \ + (self.ring.optics.local_beta[1] * ypb**2) + Js = (self.ring.long_gamma * self['tau']**2) + \ + (2*self.ring.long_alpha * self['tau']*self['delta']) + \ + (self.ring.long_beta * self['delta']**2) return np.array((np.mean(Jx), np.mean(Jy), np.mean(Js))) def init_gaussian(self, cov=None, mean=None, **kwargs): diff --git a/tests/test_bunch.py b/tests/test_bunch.py index 7c1499a6af032b73f44a5f375cfb669f6c3861ce..cac173ee54f7f19aa3b74d17304e8dbd2ed3ab8e 100644 --- a/tests/test_bunch.py +++ b/tests/test_bunch.py @@ -70,4 +70,37 @@ def test_bunch_plots(mybunch): mybunch.plot_phasespace() mybunch.plot_profile() assert True +def test_bunch_emittance(demo_ring): + mp_number = 1_000_000 + current = 1.2e-3 + mybunch = Bunch(demo_ring, mp_number=mp_number, current=current, + track_alive=False) + mybunch.init_gaussian() + np.testing.assert_allclose(mybunch.emit[0], demo_ring.emit[0], rtol=0, atol=1e-10, + err_msg=f'Emittances do not match. {demo_ring.emit[0]} initialised, {mybunch.emit[0]:} calculated') + np.testing.assert_allclose(mybunch.emit[1], demo_ring.emit[1], rtol=0, atol=1e-10, + err_msg=f'Emittances do not match. {demo_ring.emit[1]} initialised, {mybunch.emit[1]:} calculated') + + np.testing.assert_allclose(mybunch.emit[0], mybunch.cs_invariant[0]/2, rtol=0, atol=1e-10, + err_msg=f'Emittances do not match. {mybunch.cs_invariant[0]/2} calculated with optics functions, {mybunch.emit[0]:} calculated with coordinates only') + np.testing.assert_allclose(mybunch.emit[1], mybunch.cs_invariant[1]/2, rtol=0, atol=1e-10, + err_msg=f'Emittances do not match. {mybunch.cs_invariant[1]/2} calculated with optics functions, {mybunch.emit[1]:} calculated with coordinates only') + + +def test_bunch_emittance_with_dispersion(demo_ring): + mp_number = 1_000_000 + current = 1.2e-3 + demo_ring.optics.local_dispersion = np.array([1e-2, 1e-3, 1e-2, 1e-3]) + mybunch = Bunch(demo_ring, mp_number=mp_number, current=current, + track_alive=False) + mybunch.init_gaussian() + np.testing.assert_allclose(mybunch.emit[0], demo_ring.emit[0], rtol=0, atol=1e-10, + err_msg=f'Emittances do not match. {demo_ring.emit[0]} initialised, {mybunch.emit[0]:} calculated') + np.testing.assert_allclose(mybunch.emit[1], demo_ring.emit[1], rtol=0, atol=1e-10, + err_msg=f'Emittances do not match. {demo_ring.emit[1]} initialised, {mybunch.emit[1]:} calculated') + + np.testing.assert_allclose(mybunch.emit[0], mybunch.cs_invariant[0]/2, rtol=0, atol=1e-10, + err_msg=f'Emittances do not match. {mybunch.cs_invariant[0]/2} calculated with optics functions, {mybunch.emit[0]:} calculated with coordinates only') + np.testing.assert_allclose(mybunch.emit[1], mybunch.cs_invariant[1]/2, rtol=0, atol=1e-10, + err_msg=f'Emittances do not match. {mybunch.cs_invariant[1]/2} calculated with optics functions, {mybunch.emit[1]:} calculated with coordinates only') \ No newline at end of file