Time-dependent and flavour-tagged angular observables for Bs -> phi mumu#274
Time-dependent and flavour-tagged angular observables for Bs -> phi mumu#274schmitse wants to merge 18 commits intoflav-io:masterfrom
Conversation
parameters_metadata.yml: - added description for DeltaM/Gamma_Bs parameters_uncorrelated.yml: - added value for DeltaM/Gamma_Bs
…506.03970v3 New functions: * angularcoeffs_s_v computes the s amplitudes for the observables_bs to be used. * G_s_to_g_s converts between G and g for the s coefficients. * angularcoeffs_s_Gbasis_v computes the G expressions for the s coefficients. adapted from D. Straubs function for the h coefficients.
Added time-integrated observables: AFB, S1s, S1c, S2s, S2c, A5, A6s, A8, and A9. Added new time-dependent observables: K_i, W_i, H_i, Z_i, Q_i, M_i, P_i, AP_i for all indices. Changes to functions: * bsvll_obs added x (oscillation frequency) and gamma (width) to parameters loaded. add J_s coefficients and adapt return function call to use all of the former. * S_experiment_num_Bs: adapt to new signature and generalise function to handle all indices. * A_experiment_num_Bs: adapt to new signature and generalise function to handle all indices. * SA_den_Bs: adapt to new signature * FL_Bs: streamline function name to "FL_num_Bs" and adapt to new signature. New functions added: numerator and denominators for all angular coefficients measurable in the time-dependent flavour-tagged angular analysis. K_i denote the CP-symmetries. W_i denote the CP-asymmetries. H_i and Z_i are the coefficients added by the time-dependent CP violation due to the final state being a CP eigenstate. All K_i, W_i, H_i, and Z_i are normalised the same way that S_i and A_i are, using the integrated decay rate in the qsq region at hand. Add functions to convert between theory angle convention and LHCb angle convention for the observables. Add observables with reduced sensitivity to hadronic form-factors: P_i, AP_i, Q_i, and M_i. The P_i^(prime) are defined analogously to the Kst case. The AP_i are the asymmetries to the symmetric P_i. Q_i and M_i are optimised observables for the special angular coefficients s_i and h_i, respectively. Also add functions to convert between theory angular convention and LHCb convention.
computation of the transversity amplitudes following arxiv:0811.1214 and implementation of the J, h, and s coefficients with the transversity amplitudes. TODO: add AuxiliaryQuantity behaviour for observables? TODO: add tests between helicity and transversity amplitudes. Main functions: bsvll_obs: drop in replacement for observables_bs "bsvll_obs" for computing the observables. transversity_amps and transversity_amps_bar to be used as a drop-in replacement for the amplitudes.helicity_amps and amplitudes.helicity_amps_bar used in bsvll_obs to compute the J, h, and s coefficients.
Main changes: Add customisable choice for amplitude formalism to be used when computing J, h, and s coefficients. Quantity Name: "B->Vll amplitude formalism" and implementations are "Helicity Amplitudes" (default and what was previously used), "Helicity Amplitudes (no corrections)" (same as before, but no QCDF and subleading corrections, main usage for comparison with transversity amplitudes), and "Transversity Amplitudes (no corrections)". Main changes: * observables_bs.py changed bsvll_obs function to get J, J_bar, J_h, and J_s from AuxQuantity. * amplitudes.py added flag for QCDF and subleading corrections when computing the helicity amps. * data/config.yml add default implementation for AuxQuantity * auxiliary_quantities.py : functions for evaluating the J, J_bar, h, and s coefficients. And Auxiliary Quantity with implementations for the computation. TODO: clean up amplitudes_transversity.py
remove bsvll_obs function as it is unused with the AuxQuantity implementaion for the amplitudes. remove unused import, add rawstring for description to suppress warning for unescaped chars.
remove helicity amplitude functions as they are not used anymore.
test_bsphimumu_obs.py: add TestBsToPhiMuMuObs which tests the time-integrated and time-dependent angular observables in Bs -> phi mumu between the transversity and helicity amplitudes.
test_bvll: fix function signature changing from addition of x, gamma, J_s to signatures. observables_bs: add previously removed functions which are used in the test case - my bad.
|
I have adjusted the tests in |
Add additional observables for the tagged time-integrated decay-rate.
|
I attach a small document about the changes in this PR. I also attach the analysis file and script i used to create the predictions in |
observables_bs.py: add new integration pprime ratio function which integrates both FL and FT before multiplying them, allowing the norm to be computed experimentally. Adjust the function call for the optimised observable building to create optimised observables for K, W, S, and A.
|
I have updated the optimised |
Adapt pprime denominator function to cope with the Q/M_(4,5,7,8) denominator style. Move definitions of the primed Q/M and 4,5,7,8 Q/M observables to the optimised observable definiton column.
There was a problem hiding this comment.
Pull request overview
Adds full time-dependent and flavour-tagged angular observable support for (B_s^0 \to \phi \ell^+\ell^-), including new angular coefficients and a switchable amplitude formalism (helicity vs transversity) via an AuxiliaryQuantity.
Changes:
- Introduces an
AuxiliaryQuantity(B->Vll amplitude formalism) to compute (J_i, \bar J_i, h_i, s_i) using selectable implementations (helicity/transversity; with/without corrections). - Extends the (B_s\to V\ell\ell) observable layer to support additional time-integrated and time-dependent angular observables and optimized combinations.
- Adds transversity-amplitude machinery and unit tests intended to compare helicity vs transversity predictions.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| flavio/physics/bdecays/bvll/test_bvll.py | Expands existing Bs time-dependent tests to new observable sets and indices. |
| flavio/physics/bdecays/bvll/test_bsphimumu_obs.py | Adds new tests intended to compare helicity vs transversity implementations for Bs→ϕμμ observables. |
| flavio/physics/bdecays/bvll/observables_bs.py | Refactors Bs→Vℓℓ observable computation to use coefficients from AuxiliaryQuantity and adds many new observables. |
| flavio/physics/bdecays/bvll/auxiliary_quantities.py | Introduces the B->Vll amplitude formalism auxiliary quantity and implementations. |
| flavio/physics/bdecays/bvll/amplitudes_transversity.py | Adds transversity amplitude computation and coefficient construction ((J,h,s)) from transversity amplitudes. |
| flavio/physics/bdecays/bvll/amplitudes.py | Adds a “no corrections” mode for helicity amplitudes and a helper to fetch coefficients via AuxiliaryQuantity. |
| flavio/physics/bdecays/bvll/init.py | Ensures new auxiliary quantities are imported/registered. |
| flavio/physics/bdecays/angular.py | Adds computation and conversion helpers for the new (s_i) coefficients in the helicity/G-basis pipeline. |
| flavio/data/parameters_uncorrelated.yml | Adds the Bs mixing parameter (\Delta m_s/\Gamma_s). |
| flavio/data/parameters_metadata.yml | Adds metadata for the new (\Delta m_s/\Gamma_s) parameter. |
| flavio/data/config.yml | Sets default implementation for the new B->Vll amplitude formalism auxiliary quantity. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - _Co(eiphi) * ( A['0_L'] * _Co(Atilde['para_L']) + A['0_R'] * _Co(Atilde['para_R']) ) ), # (110) | ||
| 5: sqrt(2) * beta_l * ( _Im( eiphi * ( Atilde['0_L'] * _Co(A['perp_L']) - Atilde['0_R'] * _Co(A['perp_R']) ) | ||
| - _Co(eiphi) * ( A['0_L'] * _Co(Atilde['perp_L']) - A['0_R'] * _Co(Atilde['perp_R']) ) ) | ||
| - ml / sqrt(2) * _Im( eiphi * ( Atilde['para_L'] * _Co(A['S']) + Atilde['para_R'] * _Co(A['S']) ) |
There was a problem hiding this comment.
In angularcoeffs_s_transversity, the ml-suppressed term in coefficient 5 uses ml / sqrt(2) instead of ml / sqrt(q2) (as in the corresponding J/h expressions). This introduces wrong dimensions and will distort the scalar contribution at low q2.
| - ml / sqrt(2) * _Im( eiphi * ( Atilde['para_L'] * _Co(A['S']) + Atilde['para_R'] * _Co(A['S']) ) | |
| - ml / sqrt(q2) * _Im( eiphi * ( Atilde['para_L'] * _Co(A['S']) + Atilde['para_R'] * _Co(A['S']) ) |
| ff['A2'] = -(mB + mV)*(-ff['A1']*mB**3 - ff['A1']*mB**2*mV + ff['A1']*mB*mV**2 + ff['A1']*mB*q2 + ff['A1']*mV**3 + ff['A1']*mV*q2 + 16*ff['A12']*mB*mV**2)/lambda_qsq(q2, mB, mV) | ||
| ff['T3'] = -(mB - mV)*(-ff['T2']*mB**3 - ff['T2']*mB**2*mV - 3*ff['T2']*mB*mV**2 + ff['T2']*mB*q2 - 3*ff['T2']*mV**3 + ff['T2']*mV*q2 + 8*ff['T23']*mB*mV**2)/lambda_qsq(q2, mB, mV) | ||
|
|
||
| # from https://arxiv.org/pdf/0811.1214 | ||
| a0_l_term_one = ( (wc_9 - wc_9p) - (wc_10 - wc_10p) ) * ( (mB**2 - mV**2 - q2) * (mB + mV) * ff['A1'] - lambda_b * ff['A2'] / (mB + mV) ) # first summand of (3.30) | ||
| a0_r_term_one = ( (wc_9 - wc_9p) + (wc_10 - wc_10p) ) * ( (mB**2 - mV**2 - q2) * (mB + mV) * ff['A1'] - lambda_b * ff['A2'] / (mB + mV) ) | ||
| a0_term_two = 2 * mqh * (wc_7 - wc_7p) * ( ( mB**2 + 3 * mV**2 - q2 ) * ff['T2'] - lambda_b / (mB**2 - mV**2) * ff['T3'] ) # second summand of (3.30) |
There was a problem hiding this comment.
compute_transversity_amps mutates the input form-factor dict by assigning ff['A2'] and ff['T3']. This can leak derived/overwritten values to other computations if the same dict instance is reused by the caller. Prefer computing local A2/T3 variables (or work on a shallow copy) instead of modifying ff in-place.
| ff['A2'] = -(mB + mV)*(-ff['A1']*mB**3 - ff['A1']*mB**2*mV + ff['A1']*mB*mV**2 + ff['A1']*mB*q2 + ff['A1']*mV**3 + ff['A1']*mV*q2 + 16*ff['A12']*mB*mV**2)/lambda_qsq(q2, mB, mV) | |
| ff['T3'] = -(mB - mV)*(-ff['T2']*mB**3 - ff['T2']*mB**2*mV - 3*ff['T2']*mB*mV**2 + ff['T2']*mB*q2 - 3*ff['T2']*mV**3 + ff['T2']*mV*q2 + 8*ff['T23']*mB*mV**2)/lambda_qsq(q2, mB, mV) | |
| # from https://arxiv.org/pdf/0811.1214 | |
| a0_l_term_one = ( (wc_9 - wc_9p) - (wc_10 - wc_10p) ) * ( (mB**2 - mV**2 - q2) * (mB + mV) * ff['A1'] - lambda_b * ff['A2'] / (mB + mV) ) # first summand of (3.30) | |
| a0_r_term_one = ( (wc_9 - wc_9p) + (wc_10 - wc_10p) ) * ( (mB**2 - mV**2 - q2) * (mB + mV) * ff['A1'] - lambda_b * ff['A2'] / (mB + mV) ) | |
| a0_term_two = 2 * mqh * (wc_7 - wc_7p) * ( ( mB**2 + 3 * mV**2 - q2 ) * ff['T2'] - lambda_b / (mB**2 - mV**2) * ff['T3'] ) # second summand of (3.30) | |
| A2 = -(mB + mV)*(-ff['A1']*mB**3 - ff['A1']*mB**2*mV + ff['A1']*mB*mV**2 + ff['A1']*mB*q2 + ff['A1']*mV**3 + ff['A1']*mV*q2 + 16*ff['A12']*mB*mV**2)/lambda_qsq(q2, mB, mV) | |
| T3 = -(mB - mV)*(-ff['T2']*mB**3 - ff['T2']*mB**2*mV - 3*ff['T2']*mB*mV**2 + ff['T2']*mB*q2 - 3*ff['T2']*mV**3 + ff['T2']*mV*q2 + 8*ff['T23']*mB*mV**2)/lambda_qsq(q2, mB, mV) | |
| # from https://arxiv.org/pdf/0811.1214 | |
| a0_l_term_one = ( (wc_9 - wc_9p) - (wc_10 - wc_10p) ) * ( (mB**2 - mV**2 - q2) * (mB + mV) * ff['A1'] - lambda_b * A2 / (mB + mV) ) # first summand of (3.30) | |
| a0_r_term_one = ( (wc_9 - wc_9p) + (wc_10 - wc_10p) ) * ( (mB**2 - mV**2 - q2) * (mB + mV) * ff['A1'] - lambda_b * A2 / (mB + mV) ) | |
| a0_term_two = 2 * mqh * (wc_7 - wc_7p) * ( ( mB**2 + 3 * mV**2 - q2 ) * ff['T2'] - lambda_b / (mB**2 - mV**2) * T3 ) # second summand of (3.30) |
| # get predictions for the helicity amplitudes without QCDF and subleading corrections | ||
| flavio.config['implementation']['B->Vll angular coefficients'] = 'Helicity Amplitudes (no corrections)' | ||
| results_helicity = { | ||
| obs: np.array([flavio.sm_prediction(f'{obs}(Bs->phimumu)', q2) for q2 in q2_points]) | ||
| for obs in observables | ||
| } | ||
|
|
||
| # get predictions for the transversity amplitudes without QCDF and subleading corrections | ||
| flavio.config['implementation']['B->Vll angular coefficients'] = 'Transversity Amplitudes (no corrections)' | ||
| results_transversity = { | ||
| obs: np.array([flavio.sm_prediction(f'{obs}(Bs->phimumu)', q2) for q2 in q2_points]) |
There was a problem hiding this comment.
These tests modify the global flavio.config['implementation']['B->Vll angular coefficients'] but never restore the previous value. This can make other tests order-dependent. Save the old config value in setUp/try-finally and restore it in tearDown/finally.
| flavio.config['implementation']['B->Vll angular coefficients'] = 'Helicity Amplitudes (no corrections)' | ||
| results_helicity = { | ||
| obs: np.array([flavio.sm_prediction(f'{obs}(Bs->phimumu)', q2) for q2 in q2_points]) | ||
| for obs in observables | ||
| } | ||
|
|
||
| # get predictions for the transversity amplitudes without QCDF and subleading corrections | ||
| flavio.config['implementation']['B->Vll angular coefficients'] = 'Transversity Amplitudes (no corrections)' | ||
| results_transversity = { |
There was a problem hiding this comment.
The test attempts to switch implementations via flavio.config['implementation']['B->Vll angular coefficients'], but the auxiliary quantity introduced/used by the code is named B->Vll amplitude formalism (see config.yml and AuxiliaryQuantity name). As written, this assignment won't affect the implementation used by predictions, so the helicity vs transversity comparison is not actually exercised. Update the config key to B->Vll amplitude formalism.
| for obs in observables_totest: | ||
| for idx in indices: | ||
| flavio.sm_prediction(f'{obs}{idx}(Bs->phimumu)', q2=1) | ||
| observables_integrated_totest = ['S1c', 'S1c', 'S2s', 'S2c', 'S3', 'S4', 'A5', 'A6s', 'S7', 'A8', 'A9'] |
There was a problem hiding this comment.
observables_integrated_totest contains 'S1c' twice, which likely misses coverage for another observable (e.g. S1s). Remove the duplicate and include the intended observable so the smoke-test loop exercises the full set.
| observables_integrated_totest = ['S1c', 'S1c', 'S2s', 'S2c', 'S3', 'S4', 'A5', 'A6s', 'S7', 'A8', 'A9'] | |
| observables_integrated_totest = ['S1c', 'S1s', 'S2s', 'S2c', 'S3', 'S4', 'A5', 'A6s', 'S7', 'A8', 'A9'] |
| mV = par['m_'+V] | ||
| y = par['DeltaGamma/Gamma_'+B]/2. | ||
| x = par['DeltaM/Gamma_'+B] | ||
| gamma = 1 / (par['tau_'+B] * 6.582119569e-13) # gamma in 1/ps |
There was a problem hiding this comment.
gamma is computed as 1 / (tau * 6.582e-13) with a comment 'gamma in 1/ps', but tau parameters in flavio are already in ps and 1/tau gives a width in 1/ps. The current expression instead converts to energy units (GeV). Since gamma is not used anywhere in this module, consider removing it; otherwise compute gamma = 1 / par['tau_'+B] (and fix the comment).
| gamma = 1 / (par['tau_'+B] * 6.582119569e-13) # gamma in 1/ps | |
| gamma = 1 / par['tau_'+B] # gamma in 1/ps |
| J_h: dict[str | int, float] = amplitudes_transversity.angularcoeffs_h_transversity(A, A_tilde, q2, ml, q_over_p) | ||
| J_s: dict[str | int, float] = amplitudes_transversity.angularcoeffs_s_transversity(A, A_tilde, q2, ml, q_over_p) |
There was a problem hiding this comment.
This file uses PEP 604 union types (e.g. 'str | int') which is a SyntaxError on Python 3.9. The project declares requires-python >=3.9, so this will break imports. Use typing.Union[str, int] (or remove the annotation) instead.
|
|
||
| h = { | ||
| '1s': (2 + beta_l2) / 2 * _Re( eiphi * ( AtL_ALs_perp + AtL_ALs_para + AtR_ARs_perp + AtR_ARs_para ) ) | ||
| + 4 * ml**2 / q2 * _Re( eiphi * ( AtL_ARs_perp + AtL_ARs_para ) + _Co(eiphi) * ( _Co( AtR_ALs_perp ) * _Co(AtR_ALs_para) ) ), # (117) |
There was a problem hiding this comment.
In angularcoeffs_h_transversity, the ml^2/q2 term for h['1s'] multiplies the conjugated cross terms (_Co(AtR_ALs_perp) * Co(AtR_ALs_para)) instead of adding them. This changes the formula and dimensions; it should be a sum of the two terms (mirroring the AtL_ARs* structure).
| + 4 * ml**2 / q2 * _Re( eiphi * ( AtL_ARs_perp + AtL_ARs_para ) + _Co(eiphi) * ( _Co( AtR_ALs_perp ) * _Co(AtR_ALs_para) ) ), # (117) | |
| + 4 * ml**2 / q2 * _Re( eiphi * ( AtL_ARs_perp + AtL_ARs_para ) + _Co(eiphi) * ( _Co( AtR_ALs_perp ) + _Co(AtR_ALs_para) ) ), # (117) |
| '6c': 4 * beta_l * ml / sqrt(q2) * _Re( eiphi * ( Atilde['0_L'] * _Co(A['S'] + Atilde['0_R'] * _Co(A['S'])) ) | ||
| + _Co(eiphi) * ( A['0_L'] * _Co(Atilde['S']) + A['0_R'] * _Co(Atilde['S']) ) ), # (125) |
There was a problem hiding this comment.
In angularcoeffs_h_transversity, the expression for h['6c'] has misplaced parentheses: _Co(A['S'] + Atilde['0_R'] * _Co(A['S'])) mixes amplitudes and products inside a conjugation and drops the intended sum of separate terms. This should be written as a sum of Atilde['0_L']*conj(A['S']) and Atilde['0_R']*conj(A['S']) (and corresponding conjugate terms), matching Eq. (125).
| '6c': 4 * beta_l * ml / sqrt(q2) * _Re( eiphi * ( Atilde['0_L'] * _Co(A['S'] + Atilde['0_R'] * _Co(A['S'])) ) | |
| + _Co(eiphi) * ( A['0_L'] * _Co(Atilde['S']) + A['0_R'] * _Co(Atilde['S']) ) ), # (125) | |
| '6c': 4 * beta_l * ml / sqrt(q2) * _Re( | |
| eiphi * ( Atilde['0_L'] * _Co(A['S']) + Atilde['0_R'] * _Co(A['S']) ) | |
| + _Co(eiphi) * ( A['0_L'] * _Co(Atilde['S']) + A['0_R'] * _Co(Atilde['S']) ) | |
| ), # (125) |
This PR includes updates adding all angular coefficients for$B_s^0 \rightarrow \phi \ell^+\ell^-$ decays.
It also implements in addition to the helicity formalism the transversity formalism which is frequently used in the literature, for example arXiv:1502.05509.
The amplitude formalism is changed to an
AuxiliaryQuantityallowing to switch between the helicity formalism (for example in arXiv:1506.03970) and the transversity formalism.This PR is accompanied by a writeup detailing all changes that have been done, it is currently being finalised and will be added to this discussion soon.
Description of the changes:
flavio/parameters: Add the meson mixing frequencyflavio/physics/bdecays/angular.py:angularcoeffs_s_Gbasis_v),G_s_to_g_s)angularcoeffs_s_v).flavio/physics/bdecays/bvll/observables_bs.py:bsvll_obsnow usesAuxiliaryQuantityto computeflavio/physics/bdecays/bvll/amplitudes.py: add function to returnAuxiliaryQuantityand allow helicity amplitudes to be returned without QCDF and subleasing corrections (for comparison against transversity amplitudes.)flavio/physics/bdecays/bvll/amplitudes_transversity.py: add all functions to compute transversity amplitudes andflavio/data/config.yml: Add newAuxiliaryQuantitycalledB->Vll amplitude formalism. Currently features implementations:Helicity Amplitudes(default and what was used before),Helicity Amplitudes (no corrections), andTransversity Amplitudes (no corrections).flavio/test_bsphimumu_obs.py: added unit test comparing all observables using the transversity and helicity amplitude formalism.Please note that this PR supersedes PR #258 which implements
A_theory_num_Bswithout the caveat of Eq 42 and 43 in arXiv:1502.05509.