def zemach_tensor(m2ab, m2ac, m2bc, m2d, m2a, m2b, m2c, spin): """Zemach tensor for 3-body D->ABC decay :param m2ab: :param m2ac: :param m2bc: :param m2d: :param m2a: :param m2b: :param m2c: :param spin: """ z = None if spin == 0: z = atfi.complex(atfi.const(1.), atfi.const(0.)) if spin == 1: z = atfi.complex(m2ac - m2bc + (m2d - m2c) * (m2b - m2a) / m2ab, atfi.const(0.)) if spin == 2: z = atfi.complex( (m2bc - m2ac + (m2d - m2c) * (m2a - m2b) / m2ab)**2 - 1. / 3. * (m2ab - 2. * (m2d + m2c) + (m2d - m2c)**2 / m2ab) * (m2ab - 2. * (m2a + m2b) + (m2a - m2b)**2 / m2ab), atfi.const(0.)) return z
def model(x) : m2dp = phsp.m2ab(x) m2ppi = phsp.m2bc(x) p4d, p4p, p4pi = phsp.final_state_momenta(m2dp, m2ppi) dp_theta_r, dp_phi_r, dp_theta_d, dp_phi_d = atfk.helicity_angles_3body(p4d, p4p, p4pi) # List of intermediate resonances corresponds to arXiv link above. resonances = [ (atfd.breit_wigner_lineshape(m2dp, mass_lcst, width_lcst, md, mp, mpi, mlb, dr, db, OrbitalMomentum(5, 1), 2), 5, 1, couplings[0][0], couplings[0][1] ), (atfd.breit_wigner_lineshape(m2dp, mass_lcx, width_lcx, md, mp, mpi, mlb, dr, db, OrbitalMomentum(3, 1), 1), 3, 1, couplings[1][0], couplings[1][1] ), (atfd.breit_wigner_lineshape(m2dp, mass_lcstst, width_lcstst, md, mp, mpi, mlb, dr, db, OrbitalMomentum(3, -1), 1), 3, -1, couplings[2][0], couplings[2][1] ), (atfd.exponential_nonresonant_lineshape(m2dp, mass0, alpha12p, md, mp, mpi, mlb, OrbitalMomentum(1, 1), 0), 1, 1, couplings[3][0], couplings[3][1] ), (atfd.exponential_nonresonant_lineshape(m2dp, mass0, alpha12m, md, mp, mpi, mlb, OrbitalMomentum(1, -1), 0), 1, -1, couplings[4][0], couplings[4][1] ), (atfd.exponential_nonresonant_lineshape(m2dp, mass0, alpha32p, md, mp, mpi, mlb, OrbitalMomentum(3, 1), 1), 3, 1, couplings[5][0], couplings[5][1] ), (atfd.exponential_nonresonant_lineshape(m2dp, mass0, alpha32m, md, mp, mpi, mlb, OrbitalMomentum(3, -1), 1), 3, -1, couplings[6][0], couplings[6][1] ), ] density = atfi.const(0.) # Decay density is an incoherent sum over initial and final state polarisations # (assumong no polarisation for Lambda_b^0), and for each polarisation combination # it is a coherent sum over intermediate states (including two polarisations of # the intermediate resonance). for pol_lb in [-1, 1] : for pol_p in [-1, 1] : ampl = atfi.complex(atfi.const(0.), atfi.const(0.)) for r in resonances : lineshape = r[0] spin = r[1] parity = r[2] if pol_p == -1 : sign = CouplingSign(spin, parity) coupling1 = atfi.complex(r[3][0], r[3][1]) * sign coupling2 = atfi.complex(r[4][0], r[4][1]) * sign else : coupling1 = atfi.complex(r[3][0], r[3][1]) coupling2 = atfi.complex(r[4][0], r[4][1]) ampl += coupling1*lineshape*\ atfk.helicity_amplitude_3body(dp_theta_r, dp_phi_r, dp_theta_d, dp_phi_d, 1, spin, pol_lb, 1, 0, pol_p, 0) ampl += coupling2*lineshape*\ atfk.helicity_amplitude_3body(dp_theta_r, dp_phi_r, dp_theta_d, dp_phi_d, 1, spin, pol_lb, -1, 0, pol_p, 0) density += atfi.density(ampl) return density
def _with_form_factor(self, dataset: dict) -> tf.Tensor: inv_mass_squared = dataset[self._dynamics_props.inv_mass_name] inv_mass = atfi.sqrt(inv_mass_squared) mass0 = self._dynamics_props.resonance_mass gamma0 = self._dynamics_props.resonance_width m_a = atfi.sqrt(dataset[self._dynamics_props.inv_mass_name_prod1]) m_b = atfi.sqrt(dataset[self._dynamics_props.inv_mass_name_prod2]) meson_radius = self._dynamics_props.meson_radius l_orbit = self._dynamics_props.orbit_angular_momentum q_squared = two_body_momentum_squared(inv_mass, m_a, m_b) q0_squared = two_body_momentum_squared(mass0, m_a, m_b) ff2 = blatt_weisskopf_ff_squared(q_squared, meson_radius, l_orbit) ff02 = blatt_weisskopf_ff_squared(q0_squared, meson_radius, l_orbit) width = gamma0 * (mass0 / inv_mass) * (ff2 / ff02) # So far its all in float64, # but for the sqrt operation it has to be converted to complex width = atfi.complex(width, tf.constant( 0.0, dtype=tf.float64)) * atfi.sqrt( atfi.complex( (q_squared / q0_squared), tf.constant(0.0, dtype=tf.float64), )) return relativistic_breit_wigner( inv_mass_squared, mass0, width) * atfi.complex( mass0 * gamma0 * atfi.sqrt(ff2), atfi.const(0.0))
def exponential_nonresonant_lineshape(m2, m0, alpha, ma, mb, mc, md, lr, ld, barrierFactor=True): """ Exponential nonresonant amplitude with orbital barriers """ if barrierFactor: m = atfi.sqrt(m2) q = atfk.two_body_momentum(md, m, mc) q0 = atfk.two_body_momentum(md, m0, mc) p = atfk.two_body_momentum(m, ma, mb) p0 = atfk.two_body_momentum(m0, ma, mb) b1 = orbital_barrier_factor(p, p0, lr) b2 = orbital_barrier_factor(q, q0, ld) return atfi.complex(b1 * b2 * atfi.exp(-alpha * (m2 - m0**2)), atfi.const(0.)) else: return atfi.complex(atfi.exp(-alpha * (m2 - m0**2)), atfi.const(0.))
def relativistic_breit_wigner(m2, mres, wres): """ Relativistic Breit-Wigner """ if wres.dtype is atfi.ctype(): return tf.math.reciprocal( atfi.cast_complex(mres * mres - m2) - atfi.complex(atfi.const(0.), mres) * wres) if wres.dtype is atfi.fptype(): return tf.math.reciprocal(atfi.complex(mres * mres - m2, -mres * wres)) return None
def polynomial_nonresonant_lineshape(m2, m0, coeffs, ma, mb, mc, md, lr, ld, barrierFactor=True): """ 2nd order polynomial nonresonant amplitude with orbital barriers coeffs: list of atfi.complex polynomial coefficients [a0, a1, a2] """ def poly(x, cs): return cs[0] + cs[1] * atfi.complex( x, atfi.const(0.)) + cs[2] * atfi.complex(x**2, atfi.const(0.)) if barrierFactor: m = atfi.sqrt(m2) q = atfk.two_body_momentum(md, m, mc) q0 = atfk.two_body_momentum(md, m0, mc) p = atfk.two_body_momentum(m, ma, mb) p0 = atfk.two_body_momentum(m0, ma, mb) b1 = orbital_barrier_factor(p, p0, lr) b2 = orbital_barrier_factor(q, q0, ld) return poly(m - m0, coeffs) * atfi.complex(b1 * b2, atfi.const(0.)) else: return poly(m - m0, coeffs)
def subthreshold_breit_wigner_lineshape(m2, m0, gamma0, ma, mb, mc, md, dr, dd, lr, ld, barrier_factor=True): """ Breit-Wigner amplitude (with the mass under kinematic threshold) with Blatt-Weisskopf formfactors, mass-dependent width and orbital barriers """ m = atfi.sqrt(m2) mmin = ma + mb mmax = md - mc tanhterm = atfi.tanh((m0 - ((mmin + mmax) / 2.)) / (mmax - mmin)) m0eff = mmin + (mmax - mmin) * (1. + tanhterm) / 2. q = atfk.two_body_momentum(md, m, mc) q0 = atfk.two_body_momentum(md, m0eff, mc) p = atfk.two_body_momentum(m, ma, mb) p0 = atfk.two_body_momentum(m0eff, ma, mb) ffr = blatt_weisskopf_ff(p, p0, dr, lr) ffd = blatt_weisskopf_ff(q, q0, dd, ld) width = mass_dependent_width(m, m0, gamma0, p, p0, ffr, lr) bw = relativistic_breit_wigner(m2, m0, width) ff = ffr * ffd if barrier_factor: b1 = orbital_barrier_factor(p, p0, lr) b2 = orbital_barrier_factor(q, q0, ld) ff *= b1 * b2 return bw * atfi.complex(ff, atfi.const(0.))
def breit_wigner_lineshape(m2, m0, gamma0, ma, mb, mc, md, dr, dd, lr, ld, barrier_factor=True, ma0=None, md0=None): """ Breit-Wigner amplitude with Blatt-Weisskopf formfactors, mass-dependent width and orbital barriers """ m = atfi.sqrt(m2) q = atfk.two_body_momentum(md, m, mc) q0 = atfk.two_body_momentum(md if md0 is None else md0, m0, mc) p = atfk.two_body_momentum(m, ma, mb) p0 = atfk.two_body_momentum(m0, ma if ma0 is None else ma0, mb) ffr = blatt_weisskopf_ff(p, p0, dr, lr) ffd = blatt_weisskopf_ff(q, q0, dd, ld) width = mass_dependent_width(m, m0, gamma0, p, p0, ffr, lr) bw = relativistic_breit_wigner(m2, m0, width) ff = ffr * ffd if barrier_factor: b1 = orbital_barrier_factor(p, p0, lr) b2 = orbital_barrier_factor(q, q0, ld) ff *= b1 * b2 return bw * atfi.complex(ff, atfi.const(0.))
def gounaris_sakurai_lineshape(s, m, gamma, m_pi): """ Gounaris-Sakurai shape for rho->pipi s : squared pipi inv. mass m : rho mass gamma : rho width m_pi : pion mass """ m2 = m * m m_pi2 = m_pi * m_pi ss = atfi.sqrt(s) ppi2 = (s - 4. * m_pi2) / 4. p02 = (m2 - 4. * m_pi2) / 4. p0 = atfi.sqrt(p02) ppi = atfi.sqrt(ppi2) hs = 2. * ppi / atfi.pi() / ss * atfi.log((ss + 2. * ppi) / 2. / m_pi) hm = 2. * p0 / atfi.pi() / m * atfi.log((m + 2. * ppi) / 2. / m_pi) dhdq = hm * (1. / 8. / p02 - 1. / 2. / m2) + 1. / 2. / atfi.pi() / m2 f = gamma * m2 / (p0**3) * (ppi2 * (hs - hm) - p02 * (s - m2) * dhdq) gamma_s = gamma * m2 * (ppi**3) / s / (p0**3) dr = m2 - s + f di = ss * gamma_s r = dr / (dr**2 + di**2) i = di / (dr**2 + di**2) return atfi.complex(r, i)
def helicity_amplitude_3body(thetaR, phiR, thetaA, phiA, spinD, spinR, mu, lambdaR, lambdaA, lambdaB, lambdaC): """Calculate complex helicity amplitude for the 3-body decay D->ABC thetaR, phiR : polar and azimuthal angles of AB resonance in D rest frame thetaA, phiA : polar and azimuthal angles of A in AB rest frame spinD : D spin spinR : spin of the intermediate R resonance mu : D spin projection onto z axis lambdaR : R resonance helicity lambdaA : A helicity lambdaB : B helicity lambdaC : C helicity :param thetaR: :param phiR: :param thetaA: :param phiA: :param spinD: :param spinR: :param mu: :param lambdaR: :param lambdaA: :param lambdaB: :param lambdaC: """ lambda1 = lambdaR - lambdaC lambda2 = lambdaA - lambdaB ph = (mu - lambda1) / 2. * phiR + (lambdaR - lambda2) / 2. * phiA d_terms = wigner_small_d(thetaR, spinD, mu, lambda1) * \ wigner_small_d(thetaA, spinR, lambdaR, lambda2) h = atfi.complex(d_terms * atfi.cos(ph), d_terms * atfi.sin(ph)) return h
def helicity_couplings_from_ls(ja, jb, jc, lb, lc, bls): """Helicity couplings from a list of LS couplings. ja : spin of A (decaying) particle jb : spin of B (1st decay product) jc : spin of C (2nd decay product) lb : B helicity lc : C helicity bls : dictionary of LS couplings, where: keys are tuples corresponding to (L,S) pairs values are values of LS couplings Note that ALL j,l,s should be doubled, e.g. S=1 for spin-1/2, L=2 for p-wave etc. :param ja: :param jb: :param jc: :param lb: :param lc: :param bls: """ a = 0. # print("%d %d %d %d %d" % (ja, jb, jc, lb, lc)) for ls, b in bls.items(): l = ls[0] s = ls[1] coeff = math.sqrt((l+1)/(ja+1))*atfi.clebsch(jb, lb, jc, -lc, s, lb-lc)*\ atfi.clebsch( l, 0, s, lb-lc, ja, lb-lc) if coeff: a += atfi.complex(atfi.const(float(coeff)), atfi.const(0.)) * b return a
def model(x, mrho, wrho, mkst, wkst, a1r, a1i, a2r, a2i, a3r, a3i, switches): a1 = atfi.complex(a1r, a1i) a2 = atfi.complex(a2r, a2i) a3 = atfi.complex(a3r, a3i) m2ab = phsp.m2ab(x) m2bc = phsp.m2bc(x) m2ac = phsp.m2ac(x) hel_ab = phsp.cos_helicity_ab(x) hel_bc = phsp.cos_helicity_bc(x) hel_ac = phsp.cos_helicity_ac(x) ampl = atfi.complex(atfi.const(0.0), atfi.const(0.0)) if switches[0]: ampl += ( a1 * atfd.breit_wigner_lineshape( m2ab, mkst, wkst, mpi, mk, mpi, md, rd, rr, 1, 1 ) * atfd.helicity_amplitude(hel_ab, 1) ) if switches[1]: ampl += ( a2 * atfd.breit_wigner_lineshape( m2bc, mkst, wkst, mpi, mk, mpi, md, rd, rr, 1, 1 ) * atfd.helicity_amplitude(hel_bc, 1) ) if switches[2]: ampl += ( a3 * atfd.breit_wigner_lineshape( m2ac, mrho, wrho, mpi, mpi, mk, md, rd, rr, 1, 1 ) * atfd.helicity_amplitude(hel_ac, 1) ) if switches[3]: ampl += atfi.cast_complex(atfi.ones(m2ab)) * atfi.complex( atfi.const(5.0), atfi.const(0.0) ) return atfd.density(ampl)
def nonresonant_lass_lineshape(m2ab, a, r, ma, mb): """ LASS line shape, nonresonant part """ m = atfi.sqrt(m2ab) q = atfk.two_body_momentum(m, ma, mb) cot_deltab = 1. / a / q + 1. / 2. * r * q ampl = atfi.cast_complex(m) / atfi.complex(q * cot_deltab, -q) return ampl
def helicity_amplitude(x, spin): """ Helicity amplitude for a resonance in scalar-scalar state x : cos(helicity angle) spin : spin of the resonance """ if spin == 0: return atfi.complex(atfi.const(1.), atfi.const(0.)) if spin == 1: return atfi.complex(x, atfi.const(0.)) if spin == 2: return atfi.complex((3. * x**2 - 1.) / 2., atfi.const(0.)) if spin == 3: return atfi.complex((5. * x**3 - 3. * x) / 2., atfi.const(0.)) if spin == 4: return atfi.complex((35. * x**4 - 30. * x**2 + 3.) / 8., atfi.const(0.)) return None
def _model(a1r, a1i, a2r, a2i, a3r, a3i, switches=4 * [1]): a1 = atfi.complex(a1r, a1i) a2 = atfi.complex(a2r, a2i) a3 = atfi.complex(a3r, a3i) ampl = atfi.cast_complex(atfi.ones(m2ab)) * atfi.complex( atfi.const(0.0), atfi.const(0.0)) if switches[0]: ampl += a1 * bw1 * hel_ab if switches[1]: ampl += a2 * bw2 * hel_bc if switches[2]: ampl += a3 * bw3 * hel_ac if switches[3]: ampl += atfi.cast_complex(atfi.ones(m2ab)) * atfi.complex( atfi.const(5.0), atfi.const(0.0)) return atfd.density(ampl)
def resonant_lass_lineshape(m2ab, m0, gamma0, a, r, ma, mb): """ LASS line shape, resonant part """ m = atfi.sqrt(m2ab) q0 = atfk.two_body_momentum(m0, ma, mb) q = atfk.two_body_momentum(m, ma, mb) cot_deltab = 1. / a / q + 1. / 2. * r * q phase = atfi.atan(1. / cot_deltab) width = gamma0 * q / m * m0 / q0 ampl = relativistic_breit_wigner(m2ab, m0, width) * atfi.complex( atfi.cos(phase), atfi.sin(phase)) * atfi.cast_complex( m2ab * gamma0 / q0) return ampl
def _with_form_factor(self, dataset: dict) -> tf.Tensor: inv_mass = atfi.sqrt(dataset[self._dynamics_props.inv_mass_name]) m_a = atfi.sqrt(dataset[self._dynamics_props.inv_mass_name_prod1]) m_b = atfi.sqrt(dataset[self._dynamics_props.inv_mass_name_prod2]) meson_radius = self._dynamics_props.meson_radius l_orbit = self._dynamics_props.orbit_angular_momentum q_squared = two_body_momentum_squared(inv_mass, m_a, m_b) return atfi.complex( atfi.sqrt( blatt_weisskopf_ff_squared(q_squared, meson_radius, l_orbit)), atfi.const(0.0), )
def breit_wigner_decay_lineshape(m2, m0, gamma0, ma, mb, meson_radius, l_orbit): """ Breit-Wigner amplitude with Blatt-Weisskopf form factor for the decay products, mass-dependent width and orbital barriers Note: This function does not include the production form factor. """ inv_mass = atfi.sqrt(m2) q_squared = atfk.two_body_momentum_squared(inv_mass, ma, mb) q0_squared = atfk.two_body_momentum_squared(m0, ma, mb) ff2 = blatt_weisskopf_ff_squared(q_squared, meson_radius, l_orbit) ff02 = blatt_weisskopf_ff_squared(q0_squared, meson_radius, l_orbit) width = gamma0 * (m0 / inv_mass) * (ff2 / ff02) # So far its all in float64, # but for the sqrt operation it has to be converted to complex width = atfi.complex(width, atfi.const(0.0)) * atfi.sqrt( atfi.complex( (q_squared / q0_squared), atfi.const(0.0), )) return relativistic_breit_wigner(m2, m0, width) * atfi.complex( m0 * gamma0 * atfi.sqrt(ff2), atfi.const(0.0))
def complex_two_body_momentum(md, ma, mb): """Momentum of two-body decay products D->AB in the D rest frame. Output value is a complex number, analytic continuation for the region below threshold. :param md: :param ma: :param mb: """ return atfi.sqrt( atfi.complex( (md**2 - (ma + mb)**2) * (md**2 - (ma - mb)**2) / (4 * md**2), atfi.const(0.)))
def dabba_lineshape(m2ab, b, alpha, beta, ma, mb): """ Dabba line shape """ mSum = ma + mb m2a = ma**2 m2b = mb**2 sAdler = max(m2a, m2b) - 0.5 * min(m2a, m2b) mSum2 = mSum * mSum mDiff = m2ab - mSum2 rho = atfi.sqrt(1. - mSum2 / m2ab) realPart = 1.0 - beta * mDiff imagPart = b * atfi.exp(-alpha * mDiff) * (m2ab - sAdler) * rho denomFactor = realPart * realPart + imagPart * imagPart ampl = atfi.complex(realPart, imagPart) / atfi.cast_complex(denomFactor) return ampl
def special_flatte_lineshape(m2, m0, gamma0, ma, mb, mc, md, dr, dd, lr, ld, barrier_factor=True): """ Flatte amplitude with Blatt-Weisskopf formfactors, 2 component mass-dependent width and orbital barriers as done in Pentaquark analysis for L(1405) that peaks below pK threshold. ma = [ma1, ma2] and mb = [mb1, mb2] NB: The dominant decay for a given resonance should be the 2nd channel i.e. R -> a2 b2. This is because (as done in pentaquark analysis) in calculating p0 (used in Blatt-Weisskopf FF) for both channels, the dominant decay is used. Another assumption made in pentaquark is equal couplings ie. gamma0_1 = gamma0_2 = gamma and only differ in phase space factors """ ma1, ma2 = ma[0], ma[1] mb1, mb2 = mb[0], mb[1] m = atfi.sqrt(m2) # D->R c q = atfk.two_body_momentum(md, m, mc) q0 = atfk.two_body_momentum(md, m0, mc) ffd = blatt_weisskopf_ff(q, q0, dd, ld) # R -> a1 b1 p_1 = atfk.two_body_momentum(m, ma1, mb1) p0_1 = atfk.two_body_momentum(m0, ma1, mb1) ffr_1 = blatt_weisskopf_ff(p_1, p0_1, dr, lr) # R -> a2 b2 p_2 = atfk.two_body_momentum(m, ma2, mb2) p0_2 = atfk.two_body_momentum(m0, ma2, mb2) ffr_2 = blatt_weisskopf_ff(p_2, p0_2, dr, lr) # lineshape width_1 = mass_dependent_width(m, m0, gamma0, p_1, p0_2, blatt_weisskopf_ff(p_1, p0_2, dr, lr), lr) width_2 = mass_dependent_width(m, m0, gamma0, p_2, p0_2, ffr_2, lr) width = width_1 + width_2 bw = relativistic_breit_wigner(m2, m0, width) # Form factor def ff = ffr_1 * ffd if barrier_factor: b1 = orbital_barrier_factor(p_1, p0_1, lr) b2 = orbital_barrier_factor(q, q0, ld) ff *= b1 * b2 return bw * atfi.complex(ff, atfi.const(0.))
def wigner_capital_d(phi, theta, psi, j, m1, m2): """Calculate Wigner capital-D function. phi, theta, psi : Rotation angles j : spin (in units of 1/2, e.g. 1 for spin=1/2) m1 and m2 : spin projections (in units of 1/2, e.g. 1 for projection 1/2) :param phi: :param theta: :param psi: :param j2: :param m2_1: :param m2_2: """ i = atfi.complex(atfi.const(0), atfi.const(1)) return Exp(-i*atfi.cast_complex(m1/2.*phi)) * \ atfi.cast_complex(wigner_small_d(theta, j, m1, m2)) * \ Exp(-i * atfi.cast_complex(m2 / 2. * psi))
def poly(x, cs): return cs[0] + cs[1] * atfi.complex( x, atfi.const(0.)) + cs[2] * atfi.complex(x**2, atfi.const(0.))
def spline_lineshape(m, ampl_re, ampl_im): return atfi.complex(f(m, ampl_re), f(m, ampl_im))
def __call__(self, dataset: dict) -> tf.Tensor: seq_amp = atfi.complex(atfi.const(1.0), atfi.const(0.0)) for amp in self._seq_amps: seq_amp = seq_amp * amp(dataset) return seq_amp