diff --git a/config/HybridXSecAlgorithm.xml b/config/HybridXSecAlgorithm.xml index e14294658..9147f9198 100644 --- a/config/HybridXSecAlgorithm.xml +++ b/config/HybridXSecAlgorithm.xml @@ -269,6 +269,246 @@ XSecAlg@Interaction=XX alg Yes Algorithm to use to handle interactio genie::RosenbluthPXSec/Default + + + genie::SmithMonizQELCCPXSec/Dipole + + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + + + genie::SmithMonizQELCCPXSec/ZExp + + + genie::LwlynSmithQELCCPXSec/ZExp + + genie::LwlynSmithQELCCPXSec/ZExp + + genie::LwlynSmithQELCCPXSec/ZExp + + genie::LwlynSmithQELCCPXSec/ZExp + + genie::LwlynSmithQELCCPXSec/ZExp + + genie::LwlynSmithQELCCPXSec/ZExp + + + + genie::SmithMonizQELCCPXSec/RunningMA + + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + + + genie::SmithMonizQELCCPXSec/RunningMA + + + genie::LwlynSmithQELCCPXSec/ZExp + + genie::LwlynSmithQELCCPXSec/ZExp + + genie::LwlynSmithQELCCPXSec/ZExp + + genie::LwlynSmithQELCCPXSec/ZExp + + genie::LwlynSmithQELCCPXSec/ZExp + + genie::LwlynSmithQELCCPXSec/ZExp + + + + genie::NievesQELCCPXSec/IsoscalarDipole + + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + + + genie::NievesQELCCPXSec/IsoscalarDipoleNoRPA + + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + + + genie::NievesQELCCPXSec/IsoscalarDipoleNoCoulomb + + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + + + genie::NievesQELCCPXSec/IsoscalarDipoleNoPauli + + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + genie::LwlynSmithQELCCPXSec/IsoscalarDipole + + + + genie::NievesQELCCPXSec/IsoscalarIsoscalarZExp + + + genie::LwlynSmithQELCCPXSec/IsoscalarZExp + + genie::LwlynSmithQELCCPXSec/IsoscalarZExp + + genie::LwlynSmithQELCCPXSec/IsoscalarZExp + + genie::LwlynSmithQELCCPXSec/IsoscalarZExp + + genie::LwlynSmithQELCCPXSec/IsoscalarZExp + + genie::LwlynSmithQELCCPXSec/IsoscalarZExp + + + + genie::NievesQELCCPXSec/Dipole + + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + + + genie::NievesQELCCPXSec/DipoleNoRPA + + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + + + genie::NievesQELCCPXSec/DipoleNoCoulomb + + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + + + genie::NievesQELCCPXSec/DipoleNoPauli + + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + genie::LwlynSmithQELCCPXSec/Dipole + + + + genie::NievesQELCCPXSec/ZExp + + + genie::LwlynSmithQELCCPXSec/ZExp + + genie::LwlynSmithQELCCPXSec/ZExp + + genie::LwlynSmithQELCCPXSec/ZExp + + genie::LwlynSmithQELCCPXSec/ZExp + + genie::LwlynSmithQELCCPXSec/ZExp + + genie::LwlynSmithQELCCPXSec/ZExp + + + diff --git a/config/LwlynSmithIsoFFCC.xml b/config/LwlynSmithIsoFFCC.xml new file mode 100644 index 000000000..e9e943262 --- /dev/null +++ b/config/LwlynSmithIsoFFCC.xml @@ -0,0 +1,64 @@ + + + + + + + + WeakInt,MagnMoments,StrongInt,ElasticFF + + + + + + genie::DipoleAxialFormFactorModel/Default + + + + genie::ZExpAxialFormFactorModel/Default + + + + genie::MArunAxialFormFactorModel/Default + + + + genie::GalsterELFormFactorsModel/MK + genie::DipoleAxialFormFactorModel/MK + true + + + + genie::GalsterELFormFactorsModel/MK + genie::DipoleAxialFormFactorModel/MK + false + + + + + diff --git a/config/LwlynSmithQELCCPXSec.xml b/config/LwlynSmithQELCCPXSec.xml index 9dfa64588..6b0dc0f45 100644 --- a/config/LwlynSmithQELCCPXSec.xml +++ b/config/LwlynSmithQELCCPXSec.xml @@ -14,6 +14,8 @@ XSec-Integrator alg No CabibboAngle double No Cabibbo angle CommonParam[CKM] QEL-CC-XSecScale double yes XSec Scaling factor for CC 1. QEL-NC-XSecScale double yes XSec Scaling factor for NC 1. +PreciseLeptonPol bool Yes Do precise calculation of No + lepton polarization ..................................................................................................... Parameters needed when Integrating with this model to generate splines: @@ -40,12 +42,16 @@ IntegralNuclearInfluenceCutoffEnergy double No genie::PauliBlocker/Default true - + true genie::LwlynSmithFFCC/Dipole + + + genie::LwlynSmithIsoFFCC/Dipole + @@ -56,12 +62,15 @@ IntegralNuclearInfluenceCutoffEnergy double No genie::LwlynSmithFFCC/ZExp - + + + + genie::LwlynSmithIsoFFCC/ZExp + genie::LwlynSmithFFCC/RunningMA - diff --git a/config/MKFFCC.xml b/config/MKFFCC.xml deleted file mode 100644 index 4f19876c4..000000000 --- a/config/MKFFCC.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - WeakInt,MagnMoments,StrongInt,ElasticFF - genie::GalsterELFormFactorsModel/MK - genie::DipoleAxialFormFactorModel/MK - - - - - - diff --git a/config/MKFFEM.xml b/config/MKFFEM.xml deleted file mode 100644 index 03bdf7a45..000000000 --- a/config/MKFFEM.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - ElasticFF - - genie::GalsterELFormFactorsModel/MK - - - - - - diff --git a/config/MKSPPPXSec2020.xml b/config/MKSPPPXSec2020.xml index e029549c9..e89597151 100644 --- a/config/MKSPPPXSec2020.xml +++ b/config/MKSPPPXSec2020.xml @@ -59,8 +59,8 @@ XSec-Integrator alg No 0.840 0.882856 - genie::MKFFCC/Default - genie::MKFFEM/Default + genie::LwlynSmithIsoFFCC/MK-CC + genie::LwlynSmithIsoFFCC/MK-EM genie::RSHelicityAmplModelCC/Default diff --git a/config/Messenger.xml b/config/Messenger.xml index 1cfd3ce03..d23d5badf 100644 --- a/config/Messenger.xml +++ b/config/Messenger.xml @@ -154,6 +154,7 @@ NOTICE INFO NOTICE + NOTICE WARN NOTICE WARN diff --git a/config/Messenger_inuke_verbose.xml b/config/Messenger_inuke_verbose.xml index e53bb4f35..bb37d6487 100644 --- a/config/Messenger_inuke_verbose.xml +++ b/config/Messenger_inuke_verbose.xml @@ -130,6 +130,7 @@ NOTICE INFO NOTICE + NOTICE NOTICE NOTICE NOTICE diff --git a/config/Messenger_laconic.xml b/config/Messenger_laconic.xml index 1cfaf6590..e54b8878c 100644 --- a/config/Messenger_laconic.xml +++ b/config/Messenger_laconic.xml @@ -148,6 +148,7 @@ WARN WARN WARN + WARN WARN WARN WARN diff --git a/config/Messenger_rambling.xml b/config/Messenger_rambling.xml index e591abc5e..64024fca5 100644 --- a/config/Messenger_rambling.xml +++ b/config/Messenger_rambling.xml @@ -147,6 +147,7 @@ NOTICE INFO NOTICE + NOTICE NOTICE NOTICE NOTICE diff --git a/config/Messenger_whisper.xml b/config/Messenger_whisper.xml index c8d945a86..f10c5fbc9 100644 --- a/config/Messenger_whisper.xml +++ b/config/Messenger_whisper.xml @@ -146,6 +146,7 @@ FATAL FATAL FATAL + FATAL FATAL FATAL FATAL diff --git a/config/NievesQELCCPXSec.xml b/config/NievesQELCCPXSec.xml index 2f41146ef..6e552502a 100644 --- a/config/NievesQELCCPXSec.xml +++ b/config/NievesQELCCPXSec.xml @@ -6,16 +6,17 @@ Configuration for the Nieves QEL CCP xsec algorithm. Configurable Parameters: -..................................................................................................... -Name Type Optional Comment Default -..................................................................................................... -FormFactorsAlg alg No QEL form factors algorithm -XSec-Integrator alg No -CabibboAngle double No Cabibbo angle CommonParam[CKM] -RPA bool Yes Turn RPA effects on or off true -Coulomb bool Yes Turn coulomb effects on or off true -QEL-CC-XSecScale double Yes Scaling factor for CC GPL value -QEL-NC-XSecScale double Yes Scaling factor for NC GPL value +................................................................................................................... +Name Type Optional Comment Default +................................................................................................................... +FormFactorsAlg alg No QEL form factors algorithm +XSec-Integrator alg No +CabibboAngle double No Cabibbo angle CommonParam[CKM] +RPA bool Yes Turn RPA effects on or off true +Coulomb bool Yes Turn coulomb effects on or off true +QEL-CC-XSecScale double Yes Scaling factor for CC GPL value +PreciseLeptonPol bool Yes Do precise calculation of lepton polarization No +LindhardFunction string Yes Which Lindhard functions to use? OriginalByNieves ..................................................................................................... Parameters needed when Integrating with this model to generate splines: @@ -35,22 +36,23 @@ RmaxMode string Yes Method to use to comput CKM,FermiGas,NUCL 1.000 - 1.000 genie::NewQELXSec/Default genie::NuclearModelMap/Default UseNuclearModel 10.0 - true + true true - false genie::PauliBlocker/Default true VertexGenerator + + true + OriginalByNieves + + + adaptive + 1e-4 + 7500 + 1000000 + + adaptive + 1e-4 + 1000000 + + + + + + + diff --git a/config/NievesSimoVacasMECPXSec2016.xml b/config/NievesSimoVacasMECPXSec2016.xml index 7bd6ed289..6128336d4 100644 --- a/config/NievesSimoVacasMECPXSec2016.xml +++ b/config/NievesSimoVacasMECPXSec2016.xml @@ -14,6 +14,8 @@ MEC-CC-XSecScale double Yes XSec Scaling factor GPL v MECScaleAlg alg Yes XSecScale algorithm used The cross section is not scaled to scale the cross section QvalueShifterAlg algo yes Possible Qshift algo none +PreciseLeptonPol bool Yes Do precise calculation of No + lepton polarization --> genie::MECXSec/Default @@ -22,6 +24,9 @@ QvalueShifterAlg algo yes Possible Qshift algo none 1.000 1.000 + + true + diff --git a/config/PaisQELLambdaPXSec.xml b/config/PaisQELLambdaPXSec.xml index 590af97be..82bddf1e4 100644 --- a/config/PaisQELLambdaPXSec.xml +++ b/config/PaisQELLambdaPXSec.xml @@ -9,6 +9,8 @@ Algorithm Configurable Parameters: ...................................................................................................... Name Type Optional Comment Default ...................................................................................................... +PreciseLeptonPol bool Yes Do precise calculation of No + lepton polarization XSec-Integrator alg No xsection integration algorithm --> @@ -18,6 +20,7 @@ XSec-Integrator alg No xsection integration algorithm genie::LwlynSmithFFDeltaS/Default genie::QELXSec/Default + true diff --git a/config/SmithMonizQELCCPXSec.xml b/config/SmithMonizQELCCPXSec.xml index 2348c88db..f2cecb992 100644 --- a/config/SmithMonizQELCCPXSec.xml +++ b/config/SmithMonizQELCCPXSec.xml @@ -12,6 +12,8 @@ Name Type Optional Comment Default FormFactorsAlg alg No QEL form factors algorithm XSec-Integrator alg No Integrator CKM-Vud double No Vud element of CKM-matrix CommonParam[CKM] +PreciseLeptonPol bool Yes Do precise calculation of No + lepton polarization QEL-CC-XSecScale double yes XSec Scaling factor 1. --> @@ -23,6 +25,7 @@ QEL-CC-XSecScale double yes XSec Scaling factor 1. 1.000 genie::LwlynSmithFFCC/Default genie::SmithMonizQELCCXSec/Default + true diff --git a/config/SmithMonizQELCCXSec.xml b/config/SmithMonizQELCCXSec.xml index 2d794afa3..e6b9d55fd 100644 --- a/config/SmithMonizQELCCXSec.xml +++ b/config/SmithMonizQELCCXSec.xml @@ -11,24 +11,26 @@ Name Type Optional Comment gsl-integration-type string Yes name of GSL 1D numerical integrator gauss gsl-max-size-of-subintervals int Yes GSL maximum number of sub-intervals 40000 for 1D integrator -gsl-relative-tolerance double Yes GSL max evaluations for 1D integrator 1e-3 +gsl-relative-tolerance double Yes GSL max evaluations for 1D integrator 1e-5 gsl-rule int Yes GSL Gauss-Kronrod integration rule 3 (only for GSL 1D adaptive type) gsl-integration-type-2D string Yes name of GSL 2D numerical integrator adaptive -gsl-max-eval int Yes required relative tolerance (error) 1000000000 - for 2D integrator -gsl-relative-tolerance-2D double Yes GSL max evaluations for 2D integrator 1e-7 +gsl-max-eval int Yes GSL max evaluations for 2D integrator 100000 +gsl-min-eval int Yes GSL min evaluations for 2D integrator 7500 +gsl-relative-tolerance-2D double Yes required relative tolerance (error) + for 2D integrator 1e-5 ..................................................................................................... --> gauss 40000 - 1e-3 + 1e-5 3 adaptive - 1000000000 - 1e-7 + 100000 + 7500 + 1e-5 diff --git a/config/SuSAv2MECPXSec.xml b/config/SuSAv2MECPXSec.xml index 83763ca0a..b5a907b9d 100644 --- a/config/SuSAv2MECPXSec.xml +++ b/config/SuSAv2MECPXSec.xml @@ -14,6 +14,8 @@ CKM-Vud double No Vud element of CKM matrix FermiMomentumTable string No Table of Fermi momentum (kF) constants CommonParam[FermiGas] MECScaleAlg alg Yes XSecScale algorithm used The cross section is unscaled to scale the cross section +PreciseLeptonPol bool Yes Do precise calculation of No + lepton polarization QvalueShifterAlg algo yes Qvalue shifter algo none --> @@ -24,6 +26,7 @@ QvalueShifterAlg algo yes Qvalue shifter algo 1.000 1.000 FermiGas + true diff --git a/config/SuSAv2QELPXSec.xml b/config/SuSAv2QELPXSec.xml index 1e9c80218..9ac2d809a 100644 --- a/config/SuSAv2QELPXSec.xml +++ b/config/SuSAv2QELPXSec.xml @@ -13,6 +13,8 @@ XSec-Integrator alg No QEL-CC-XSecScale double yes XSec Scaling factor for CC 1. QEL-NC-XSecScale double yes XSec Scaling factor for NC 1. QEL-EM-XSecScale double yes XSec Scaling factor for EM 1. +PreciseLeptonPol bool Yes Do precise calculation of No + lepton polarization FermiMomentumTable string No Table of Fermi momentum (kF) constants CommonParam[FermiGas] QvalueShifterAlg alg yes QValue shifter algo none ..................................................................................................... @@ -44,6 +46,8 @@ Name Type Optional Comment genie::MECXSec/Default + true + QELXSec.xml NewQELXSec.xml + NievesQELCCXSec.xml SmithMonizQELCCXSec.xml DISXSec.xml COHDNuXSec.xml @@ -217,7 +217,7 @@ ReinSehgalRESXSec.xml ReinSehgalSPPXSec.xml MKSPPPXSec2020.xml - SPPXSec.xml + SPPXSec.xml H3AMNuGammaPXSec.xml EmpiricalMECPXSec2015.xml NievesSimoVacasMECPXSec2016.xml diff --git a/src/Framework/Conventions/KinePhaseSpace.h b/src/Framework/Conventions/KinePhaseSpace.h index a51c72add..6d7a0813b 100644 --- a/src/Framework/Conventions/KinePhaseSpace.h +++ b/src/Framework/Conventions/KinePhaseSpace.h @@ -75,7 +75,9 @@ typedef enum EKinePhaseSpace { kPSn1n2n3fE, kPSWQ2ctpphipfE, kPSWQ2ctpfE, - kPSQ2vpfE + kPSQ2vpfE, + kPSyphi0fEx // Phase space dimension to set lepton polarization properly + // for cases when an output of XSecModel depends on phase space dim. } KinePhaseSpace_t; class KinePhaseSpace @@ -137,6 +139,7 @@ class KinePhaseSpace case(kPSn1n2n3fE) : return "<{n1,n2,n3}|E>"; break; case(kPSWQ2ctpphipfE): return "<{W, Q2, cost(theta_pion), phi_pion}|E>"; break; case(kPSWQ2ctpfE) : return "<{W, Q2, cost(theta_pion)}|E>"; break; + case(kPSyphi0fEx) : return "<{y, phi0}|E,x>"; break; } return "** Undefined kinematic phase space **"; } diff --git a/src/Framework/EventGen/HybridXSecAlgorithm.cxx b/src/Framework/EventGen/HybridXSecAlgorithm.cxx index e8bd161cf..65dd30f46 100644 --- a/src/Framework/EventGen/HybridXSecAlgorithm.cxx +++ b/src/Framework/EventGen/HybridXSecAlgorithm.cxx @@ -10,6 +10,7 @@ #include "Framework/EventGen/HybridXSecAlgorithm.h" #include "Framework/Messenger/Messenger.h" +#include "Framework/ParticleData/PDGCodes.h" using namespace genie; @@ -80,17 +81,16 @@ double HybridXSecAlgorithm::XSec(const Interaction* interaction, const XSecAlgorithmI* alg_to_use = this->ChooseXSecAlg( *interaction ); if ( !alg_to_use ) return 0.; - // Ad hoc solution of problem with inappropriate kinematic phase space - // reported by Julia so she can continue working. - // (The reason of problem: it is intended for LlewelynSmith, - // BUT also used by Rosenbluth) - // A more thoughtful solutions could be - // 1. Specify in the configuration file the phase space appropriate - // for each algorithm - // 2. Implement in RosenbluthPXSec the analog of method - // LwlynSmithQELCCPXSec::FullDifferentialXSec - Igor Kakorin - if (alg_to_use == fDefaultXSecAlg) return alg_to_use->XSec( interaction, kps ); - return alg_to_use->XSec( interaction, kPSQ2fE ); + // Special case, when the process is EM scattering on free nucleon. + // In this case genie::RosenbluthPXSec is used to calculate cross-section. + // However, input kps is equal to either kPSTlctl for SuSAv2 + // or kPSQELEvGen for LwlynSmithQELCCPXSec (2d phase spaces), + // while RosenbluthPXSec uses kPSQ2fE (1d phase space) + // and there is no way to transform 1d to 2d phase space. + if ( alg_to_use->Id().Name() == "genie::RosenbluthPXSec") + return alg_to_use->XSec( interaction, kPSQ2fE ); + return alg_to_use->XSec( interaction, kps ); + } //_________________________________________________________________________ double HybridXSecAlgorithm::Integral(const Interaction* interaction) const @@ -138,3 +138,13 @@ void HybridXSecAlgorithm::LoadConfig(void) assert( fDefaultXSecAlg ); } } +//____________________________________________________________________________ +const TVector3 & HybridXSecAlgorithm::FinalLeptonPolarization (const Interaction* interaction) const +{ + const XSecAlgorithmI* alg_to_use = this->ChooseXSecAlg( *interaction ); + + if ( !alg_to_use ) return XSecAlgorithmI::FinalLeptonPolarization(interaction); + + return alg_to_use->FinalLeptonPolarization( interaction ); +} +//____________________________________________________________________________ diff --git a/src/Framework/EventGen/HybridXSecAlgorithm.h b/src/Framework/EventGen/HybridXSecAlgorithm.h index 10d5a97a9..40624d0c8 100644 --- a/src/Framework/EventGen/HybridXSecAlgorithm.h +++ b/src/Framework/EventGen/HybridXSecAlgorithm.h @@ -44,6 +44,7 @@ class HybridXSecAlgorithm : public XSecAlgorithmI { double XSec(const Interaction* i, KinePhaseSpace_t k) const; double Integral(const Interaction* i) const; bool ValidProcess(const Interaction* i) const; + const TVector3 & FinalLeptonPolarization (const Interaction* i) const; // override the Algorithm::Configure methods to load configuration // data to private data members @@ -59,7 +60,7 @@ class HybridXSecAlgorithm : public XSecAlgorithmI { /// using the map. If no suitable algorithm was found, return a /// null pointer. const XSecAlgorithmI* ChooseXSecAlg(const Interaction& interaction) const; - + /// Map specifying the managed cross section algorithms. Keys are strings /// generated with Interaction::AsString() (identical to those used for /// splines). Values are pointers to the corresponding cross section diff --git a/src/Framework/EventGen/XSecAlgorithmI.cxx b/src/Framework/EventGen/XSecAlgorithmI.cxx index 6e370be0d..7d168ecc6 100644 --- a/src/Framework/EventGen/XSecAlgorithmI.cxx +++ b/src/Framework/EventGen/XSecAlgorithmI.cxx @@ -10,6 +10,7 @@ #include "Framework/EventGen/XSecAlgorithmI.h" #include "Framework/Messenger/Messenger.h" +#include "Framework/ParticleData/PDGUtils.h" using namespace genie; @@ -17,19 +18,22 @@ using namespace genie; XSecAlgorithmI::XSecAlgorithmI() : Algorithm() { - + fFinalLeptonPolarization = TVector3(0, 0, 0); + fIsPreciseLeptonPolarization = false; } //___________________________________________________________________________ XSecAlgorithmI::XSecAlgorithmI(string name) : Algorithm(name) { - + fFinalLeptonPolarization = TVector3(0, 0, 0); + fIsPreciseLeptonPolarization = false; } //___________________________________________________________________________ XSecAlgorithmI::XSecAlgorithmI(string name, string config) : Algorithm(name, config) { - + fFinalLeptonPolarization = TVector3(0, 0, 0); + fIsPreciseLeptonPolarization = false; } //___________________________________________________________________________ XSecAlgorithmI::~XSecAlgorithmI() @@ -57,3 +61,23 @@ bool XSecAlgorithmI::ValidKinematics(const Interaction* interaction) const return true; } //___________________________________________________________________________ +const TVector3 & XSecAlgorithmI::FinalLeptonPolarization (const Interaction* i) const +{ + if ( i->ProcInfo().IsEM() ) + { + LOG("XSecBase", pWARN) << "For EM processes doesn't work yet. Set it to zero."; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + int pdg = i->FSPrimLeptonPdg(); + if ( pdg::IsNeutrino(pdg) || pdg::IsElectron(pdg) || pdg::IsMuon(pdg) || pdg::IsTau(pdg) ) + { + fFinalLeptonPolarization = TVector3(0, 0, -1); + } + else + { + fFinalLeptonPolarization = TVector3(0, 0, 1); + } + return fFinalLeptonPolarization; +} +//___________________________________________________________________________ diff --git a/src/Framework/EventGen/XSecAlgorithmI.h b/src/Framework/EventGen/XSecAlgorithmI.h index 2674ce60f..43eaccf09 100644 --- a/src/Framework/EventGen/XSecAlgorithmI.h +++ b/src/Framework/EventGen/XSecAlgorithmI.h @@ -18,6 +18,8 @@ #ifndef _XSEC_ALGORITHM_I_H_ #define _XSEC_ALGORITHM_I_H_ +#include "TVector3.h" + #include "Framework/Algorithm/Algorithm.h" #include "Framework/Conventions/KinePhaseSpace.h" #include "Framework/Interaction/Interaction.h" @@ -31,6 +33,8 @@ class XSecAlgorithmI : public Algorithm { //! Compute the cross section for the input interaction virtual double XSec (const Interaction* i, KinePhaseSpace_t k=kPSfE) const = 0; + + virtual const TVector3 & FinalLeptonPolarization (const Interaction* i) const; //! Integrate the model over the kinematic phase space available to the //! input interaction (kinematical cuts can be included) @@ -46,6 +50,9 @@ class XSecAlgorithmI : public Algorithm { XSecAlgorithmI(); XSecAlgorithmI(string name); XSecAlgorithmI(string name, string config); + + mutable TVector3 fFinalLeptonPolarization; + bool fIsPreciseLeptonPolarization; }; } // genie namespace diff --git a/src/Framework/GHEP/GHepParticle.cxx b/src/Framework/GHEP/GHepParticle.cxx index a34d0e9f0..0a36264dc 100644 --- a/src/Framework/GHEP/GHepParticle.cxx +++ b/src/Framework/GHEP/GHepParticle.cxx @@ -68,10 +68,8 @@ fLastDaughter(daughter2) fP4 = new TLorentzVector(p); fX4 = new TLorentzVector(v); - + fPolarization = TVector3(0, 0, 0); fRescatterCode = -1; - fPolzTheta = -999; - fPolzPhi = -999; fIsBound = false; fRemovalEnergy = 0.; } @@ -92,10 +90,8 @@ fLastDaughter(daughter2) fP4 = new TLorentzVector(px,py,pz,En); fX4 = new TLorentzVector(x,y,z,t); - + fPolarization = TVector3(0, 0, 0); fRescatterCode = -1; - fPolzTheta = -999; - fPolzPhi = -999; fIsBound = false; fRemovalEnergy = 0.; } @@ -119,8 +115,7 @@ fFirstDaughter(-1), fLastDaughter(-1), fP4(0), fX4(0), -fPolzTheta(-999.), -fPolzPhi(-999.), +fPolarization( TVector3(0, 0, 0) ), fRemovalEnergy(0), fIsBound(false) { @@ -305,59 +300,6 @@ bool GHepParticle::IsOffMassShell(void) const return (! this->IsOnMassShell()); } //___________________________________________________________________________ -bool GHepParticle::PolzIsSet(void) const -{ -// checks whether the polarization angles have been set - - return (fPolzTheta > -999 && fPolzPhi > -999); -} -//___________________________________________________________________________ -void GHepParticle::GetPolarization(TVector3 & polz) -{ -// gets the polarization vector - - if(! this->PolzIsSet() ) { - polz.SetXYZ(0.,0.,0.); - return; - } - polz.SetX( TMath::Sin(fPolzTheta) * TMath::Cos(fPolzPhi) ); - polz.SetY( TMath::Sin(fPolzTheta) * TMath::Sin(fPolzPhi) ); - polz.SetZ( TMath::Cos(fPolzTheta) ); -} -//___________________________________________________________________________ -void GHepParticle::SetPolarization(double theta, double phi) -{ -// sets the polarization angles - - if(theta>=0 && theta<=kPi && phi>=0 && phi<2*kPi) - { - fPolzTheta = theta; - fPolzPhi = phi; - - } else { - LOG("GHepParticle", pERROR) - << "Invalid polarization angles (polar = " << theta - << ", azimuthal = " << phi << ")"; - } -} -//___________________________________________________________________________ -void GHepParticle::SetPolarization(const TVector3 & polz) -{ -// sets the polarization angles - - double p = polz.Mag(); - if(! (p>0) ) { - LOG("GHepParticle", pERROR) - << "Input polarization vector has non-positive norm! Ignoring it"; - return; - } - - double theta = TMath::ACos(polz.z()/p); - double phi = kPi + TMath::ATan2(-polz.y(), -polz.x()); - - this->SetPolarization(theta,phi); -} -//___________________________________________________________________________ void GHepParticle::SetBound(bool bound) { // only set it for p or n @@ -394,8 +336,7 @@ void GHepParticle::Init(void) fLastMother = -1; fFirstDaughter = -1; fLastDaughter = -1; - fPolzTheta = -999; - fPolzPhi = -999; + fPolarization = TVector3(0, 0, 0); fIsBound = false; fRemovalEnergy = 0.; fP4 = new TLorentzVector(0,0,0,0); @@ -525,8 +466,7 @@ void GHepParticle::Copy(const GHepParticle & particle) this->SetMomentum (*particle.P4()); this->SetPosition (*particle.X4()); - this->fPolzTheta = particle.fPolzTheta; - this->fPolzPhi = particle.fPolzPhi; + this->fPolarization = particle.fPolarization; this->fIsBound = particle.fIsBound; this->fRemovalEnergy = particle.fRemovalEnergy; diff --git a/src/Framework/GHEP/GHepParticle.h b/src/Framework/GHEP/GHepParticle.h index 6d70af7a9..1c0686db3 100644 --- a/src/Framework/GHEP/GHepParticle.h +++ b/src/Framework/GHEP/GHepParticle.h @@ -116,10 +116,10 @@ public : // Get the polarization. Most likely it is only the f/s primary lepton // for which this is usefull and might be set during event generation - double PolzPolarAngle (void) const { return fPolzTheta; } - double PolzAzimuthAngle (void) const { return fPolzPhi; } - bool PolzIsSet (void) const; - void GetPolarization (TVector3 & polz); + double PolzPolarAngle (void) const { return fPolarization.Mag()>0?fPolarization.Theta():0; } + double PolzAzimuthAngle (void) const { return fPolarization.Mag()>0?fPolarization.Phi():0; } + bool PolzIsSet (void) const { return fPolarization.Mag()>0;} + void GetPolarization (TVector3 & polz) const {polz = fPolarization;} // Set pdg code and status codes void SetPdgCode (int c); @@ -144,9 +144,7 @@ public : void SetPy (double py); void SetPz (double pz); - // Set the polarization angles - void SetPolarization(double theta, double phi); - void SetPolarization(const TVector3 & polz); + void SetPolarization(const TVector3 & polz) { fPolarization = polz;} // Set the bould flag & removal energy (bound flag set automatically // if a positive removal energy is set) @@ -180,12 +178,11 @@ public : int fLastDaughter; ///< last daughter idx TLorentzVector * fP4; ///< momentum 4-vector (GeV) TLorentzVector * fX4; ///< position 4-vector (in the target nucleus coordinate system / x,y,z in fm / t from the moment of the primary interaction in ys(yocto second = 10^-24 s) - double fPolzTheta; ///< polar polarization angle (rad) - double fPolzPhi; ///< azimuthal polarization angle (rad) + TVector3 fPolarization; ///< polarization vector of final lepton double fRemovalEnergy; ///< removal energy for bound nucleons (GeV) bool fIsBound; ///< 'is it a bound particle?' flag -ClassDef(GHepParticle, 2) +ClassDef(GHepParticle, 3) }; diff --git a/src/Framework/GHEP/GHepRecord.h b/src/Framework/GHEP/GHepRecord.h index 03f4013bb..b497e52d1 100644 --- a/src/Framework/GHEP/GHepRecord.h +++ b/src/Framework/GHEP/GHepRecord.h @@ -197,7 +197,7 @@ public : private: -ClassDef(GHepRecord, 2) +ClassDef(GHepRecord, 3) }; diff --git a/src/Framework/Interaction/KPhaseSpace.cxx b/src/Framework/Interaction/KPhaseSpace.cxx index 50b830202..1cce481eb 100644 --- a/src/Framework/Interaction/KPhaseSpace.cxx +++ b/src/Framework/Interaction/KPhaseSpace.cxx @@ -311,6 +311,7 @@ bool KPhaseSpace::IsAboveThreshold(void) const pi.IsDarkMatterDeepInelastic() || pi.IsDiffractive() || pi.IsSingleKaon() || + pi.IsSinglePion() || pi.IsAMNuGamma()) { E = init_state.ProbeE(kRfHitNucRest); diff --git a/src/Framework/Utils/KineUtils.cxx b/src/Framework/Utils/KineUtils.cxx index 5b5917134..9abbe4d9b 100644 --- a/src/Framework/Utils/KineUtils.cxx +++ b/src/Framework/Utils/KineUtils.cxx @@ -154,8 +154,8 @@ double genie::utils::kinematics::Jacobian( << KinePhaseSpace::AsString(fromps) << " --> " << KinePhaseSpace::AsString(tops); - double J=0; - bool forward; + double J = 0; + bool forward = true; const Kinematics & kine = i->Kine(); // cover the simple case @@ -321,14 +321,166 @@ double genie::utils::kinematics::Jacobian( // (it will be inverted below for the inverse transformation) J = W / ( 2. * pv * pl * M ); } + + else if ( TransformMatched(fromps,tops, kPSyphi0fEx, kPSQELEvGen, forward) ) + { + TLorentzVector* ki4 = i->InitStatePtr()->GetProbeP4(genie::kRfLab); + TLorentzVector* pi4 = i->InitStatePtr()->TgtPtr()->HitNucP4Ptr(); + TLorentzVector totMom = *ki4 + *pi4; + TVector3 beta = totMom.BoostVector(); + TLorentzVector kf4(i->KinePtr()->FSLeptonP4()); + kf4.Boost(-beta); + + TVector3 zvec(0., 0., 1.); + TVector3 rot = ( zvec.Cross(beta) ).Unit(); + double angle = beta.Angle( zvec ); + // Handle the edge case where beta is along z, so the + // cross product above vanishes + if ( beta.Perp() == 0. && beta.Z() < 0. ) + { + rot = TVector3(0., 1., 0.); + angle = genie::constants::kPi; + } + + if ( rot.Mag() > 0 ) + { + kf4.Rotate(angle, -rot); + } + + double kf4_mag = kf4.Vect().Mag(); + double theta_star = kf4.Theta(); + double phi_star = kf4.Phi(); + + TLorentzVector kf4_dtheta(kf4_mag*TMath::Cos(phi_star)*TMath::Cos(theta_star), kf4_mag*TMath::Sin(phi_star)*TMath::Cos(theta_star), -kf4_mag*TMath::Sin(theta_star), 0); + TLorentzVector kf4_dphi( -kf4_mag*TMath::Sin(phi_star)*TMath::Sin(theta_star), kf4_mag*TMath::Cos(phi_star)*TMath::Sin(theta_star), 0, 0); + if ( theta_star <= 0 || theta_star >= genie::constants::kPi) + { + kf4_dphi = TLorentzVector( -kf4_mag*TMath::Sin(phi_star), kf4_mag*TMath::Cos(phi_star), 0, 0 ); + } + // to LAB frame + if ( rot.Mag() > 0 ) + { + kf4_dtheta.Rotate(angle, rot); + kf4_dphi.Rotate(angle, rot); + } + kf4_dtheta.Boost(beta); + kf4_dphi.Boost(beta); + kf4 = i->KinePtr()->FSLeptonP4(); + + double dQ2dtheta = 2*((*ki4)*kf4_dtheta); + double dQ2dphi = 2*((*ki4)*kf4_dphi); + + // to hit nucleon rest frame + TVector3 beta1 = pi4->BoostVector(); + kf4_dtheta.Boost(-beta1); + kf4_dphi.Boost(-beta1); + kf4.Boost(-beta1); + + ki4->Boost(-beta1); + TVector3 ki3 = ki4->Vect(); + TVector3 rot1 = ( ki3.Cross(zvec) ).Unit(); + double angle1 = zvec.Angle( ki3 ); + // Handle the edge case where beta is along z, so the + // cross product above vanishes + if ( ki3.Perp() == 0. && ki3.Z() < 0. ) + { + rot1 = TVector3(0., 1., 0.); + angle1 = genie::constants::kPi; + } + if ( rot1.Mag() > 0 ) + { + kf4_dtheta.Rotate(angle1, rot1); + kf4_dphi.Rotate(angle1, rot1); + kf4.Rotate(angle1, rot1); + } + double t = 1/( kf4.Px()*kf4.Px() + kf4.Py()*kf4.Py() ); + double dphi0dtheta = t*(kf4_dtheta.Py()*kf4.Px() - kf4_dtheta.Px()*kf4.Py()); + double dphi0dphi = t*(kf4_dphi.Py()*kf4.Px() - kf4_dphi.Px()*kf4.Py()); + + double Q2 = i->Kine().GetKV( kKVQ2 ); + // mass of initial nucleon + double Mi = i->InitStatePtr()->TgtPtr()->HitNucMass(); + // Look up the (on-shell) mass of the final nucleon + TDatabasePDG *tb = TDatabasePDG::Instance(); + double Mf = tb->GetParticle( i->RecoilNucleonPdg() )->Mass(); + // Mandelstam s for the probe/hit nucleon system + double s = TMath::Sq( i->InitState().CMEnergy() ); + double dydQ2 = (s - Mi*Mi)/TMath::Sq(s - Mf*Mf - Q2); + if ( theta_star <= 0 || theta_star >= genie::constants::kPi) + J = 1; + else + J = 1/TMath::Sin(theta_star); + + // It is not entirely correct to divide by 2 pi, but solely to simplify the code it is better to do it here (Igor Kakorin) + J = TMath::Abs(J*dydQ2*(dQ2dtheta*dphi0dphi - dQ2dphi*dphi0dtheta)/2/genie::constants::kPi); + + delete ki4; + } + + else if ( TransformMatched(fromps,tops, kPSyphi0fEx, kPSQ2fE, forward) ) + { + double Q2 = i->Kine().GetKV( kKVQ2 ); + // mass of initial nucleon + double Mi = i->InitStatePtr()->TgtPtr()->HitNucMass(); + // Look up the (on-shell) mass of the final nucleon + TDatabasePDG *tb = TDatabasePDG::Instance(); + double Mf = tb->GetParticle( i->RecoilNucleonPdg() )->Mass(); + // Mandelstam s for the probe/hit nucleon system + double s = TMath::Sq( i->InitState().CMEnergy() ); + double dydQ2 = (s - Mi*Mi)/TMath::Sq(s - Mf*Mf - Q2); + J = dydQ2; + } + + else if ( TransformMatched(fromps,tops, kPSxyfE, kPSTlctl, forward) ) + { + TLorentzVector* ki4 = i->InitStatePtr()->GetProbeP4(); + TLorentzVector* pi4 = i->InitStatePtr()->TgtPtr()->HitNucP4Ptr(); + TLorentzVector kf4(i->KinePtr()->FSLeptonP4()); + // mass of initial nucleon + double M = i->InitStatePtr()->TgtPtr()->HitNucMass(); + + TVector3 beta = pi4->BoostVector(); + kf4.Boost(-beta); + double nu = ki4->Energy() - kf4.Energy(); + double Pf = kf4.Vect().Mag(); + J = TMath::Abs(Pf/M/nu); + + delete ki4; + } + + else if ( TransformMatched(fromps,tops, kPSQ2vfE, kPSxyfE, forward) ) + { + TLorentzVector* ki4 = i->InitStatePtr()->GetProbeP4(genie::kRfLab); + TLorentzVector kf4(i->KinePtr()->FSLeptonP4()); + double Ev = ki4->Energy(); + double l = kf4.Vect().Mag(); + J = Jacobian(i, kPSTlctl, kPSxyfE); + J = TMath::Abs(J*2*Ev*l); + + delete ki4; + } + + else if ( TransformMatched(fromps,tops, kPSxyfE, kPSWQ2fE, forward) ) + { + const InitialState & init_state = i->InitState(); + double Ev = init_state.ProbeE(kRfHitNucRest); + double M = init_state.Tgt().HitNucMass(); + double M2 = M*M; + double W = kine.W(); + double W2 = W*W; + double Q2 = kine.Q2(); + J = TMath::Abs( W/(Ev*M*(W2 + Q2 - M2)) ); + } else { std::ostringstream msg; msg << "Can not compute Jacobian for transforming: " << KinePhaseSpace::AsString(fromps) << " --> " << KinePhaseSpace::AsString(tops); - SLOG("KineLimits", pFATAL) << "*** " << msg.str(); - throw genie::exceptions::InteractionException(msg.str()); + SLOG("KineLimits", pNOTICE) << msg.str() + << ". Set Jacbian equal to zero!"; + //SLOG("KineLimits", pFATAL) << "*** " << msg.str(); + //throw genie::exceptions::InteractionException(msg.str()); //exit(1); } diff --git a/src/Framework/Utils/Range1.cxx b/src/Framework/Utils/Range1.cxx index 10911c954..b6fe7ebf0 100644 --- a/src/Framework/Utils/Range1.cxx +++ b/src/Framework/Utils/Range1.cxx @@ -45,6 +45,13 @@ void Range1F_t::Copy(const Range1F_t & r) max = r.max; } //____________________________________________________________________________ +Range1F_t & Range1F_t::operator=(const Range1F_t &r) +{ + min = r.min; + max = r.max; + return *this; +} +//____________________________________________________________________________ Range1D_t::Range1D_t(void) : min(0.), max(0.) @@ -77,6 +84,13 @@ void Range1D_t::Copy(const Range1D_t & r) max = r.max; } //____________________________________________________________________________ +Range1D_t & Range1D_t::operator=(const Range1D_t &r) +{ + min = r.min; + max = r.max; + return *this; +} +//____________________________________________________________________________ Range1I_t::Range1I_t(void) : min(0), max(0) @@ -109,3 +123,10 @@ void Range1I_t::Copy(const Range1I_t & r) max = r.max; } //____________________________________________________________________________ +Range1I_t & Range1I_t::operator=(const Range1I_t &r) +{ + min = r.min; + max = r.max; + return *this; +} +//____________________________________________________________________________ diff --git a/src/Framework/Utils/Range1.h b/src/Framework/Utils/Range1.h index 464ed1cbd..ba39369f1 100644 --- a/src/Framework/Utils/Range1.h +++ b/src/Framework/Utils/Range1.h @@ -31,6 +31,7 @@ class Range1F_t Range1F_t (void); Range1F_t (float _min, float _max); Range1F_t (const Range1F_t & r); + Range1F_t &operator=(const Range1F_t &r); ~Range1F_t (void); void Copy (const Range1F_t & r); @@ -45,6 +46,8 @@ class Range1D_t Range1D_t (void); Range1D_t (double _min, double _max); Range1D_t (const Range1D_t & r); + Range1D_t &operator=(const Range1D_t &r); + ~Range1D_t (void); void Copy (const Range1D_t & r); @@ -59,6 +62,7 @@ class Range1I_t Range1I_t (void); Range1I_t (int _min, int _max); Range1I_t (const Range1I_t & r); + Range1I_t &operator=(const Range1I_t &r); ~Range1I_t (void); void Copy (const Range1I_t & r); diff --git a/src/Physics/Common/NormGenerator.cxx b/src/Physics/Common/NormGenerator.cxx index 1107c298f..f6f9fb081 100644 --- a/src/Physics/Common/NormGenerator.cxx +++ b/src/Physics/Common/NormGenerator.cxx @@ -30,8 +30,14 @@ EventRecordVisitorI("genie::NormGenerator") } //___________________________________________________________________________ -NormGenerator::NormGenerator(string config): -EventRecordVisitorI("genie::NormGenerator") +NormGenerator::NormGenerator(string name): +EventRecordVisitorI(name) +{ + +} +//___________________________________________________________________________ +NormGenerator::NormGenerator(string name, string config): +EventRecordVisitorI(name, config) { } @@ -44,8 +50,7 @@ NormGenerator::~NormGenerator() void NormGenerator::ProcessEventRecord(GHepRecord * evrec) const { Interaction * interaction = evrec->Summary(); - const InitialState & init_state = interaction -> InitState(); - + // Access cross section algorithm for running thread RunningThreadInfo * rtinfo = RunningThreadInfo::Instance(); const EventGeneratorI * evg = rtinfo->RunningThread(); diff --git a/src/Physics/Common/NormGenerator.h b/src/Physics/Common/NormGenerator.h index 49cc5388e..d3b0d63ab 100644 --- a/src/Physics/Common/NormGenerator.h +++ b/src/Physics/Common/NormGenerator.h @@ -34,7 +34,8 @@ namespace genie { public : NormGenerator(); - NormGenerator(string config); + NormGenerator(string name); + NormGenerator(string name, string config); ~NormGenerator(); // implement the EventRecordVisitorI interface diff --git a/src/Physics/Common/PrimaryLeptonUtils.cxx b/src/Physics/Common/PrimaryLeptonUtils.cxx index 25ad55800..215d297cf 100644 --- a/src/Physics/Common/PrimaryLeptonUtils.cxx +++ b/src/Physics/Common/PrimaryLeptonUtils.cxx @@ -8,16 +8,27 @@ */ //____________________________________________________________________________ +#include "TLorentzVector.h" #include "TVector3.h" +#include "TMath.h" +#include "TMatrixD.h" + #include "TDecompSVD.h" +#include "Framework/Conventions/Constants.h" #include "Physics/Common/PrimaryLeptonUtils.h" #include "Framework/GHEP/GHepRecord.h" #include "Framework/GHEP/GHepParticle.h" #include "Framework/Messenger/Messenger.h" #include "Framework/ParticleData/PDGUtils.h" +#include "Framework/EventGen/EventGeneratorI.h" +#include "Framework/EventGen/RunningThreadInfo.h" +#include "Framework/EventGen/XSecAlgorithmI.h" +#include "Framework/ParticleData/PDGLibrary.h" using namespace genie; using namespace genie::utils; +using namespace genie::constants; +using namespace std::complex_literals; //___________________________________________________________________________ void genie::utils::SetPrimaryLeptonPolarization( GHepRecord * ev ) @@ -26,12 +37,6 @@ void genie::utils::SetPrimaryLeptonPolarization( GHepRecord * ev ) // accessible for generators that use a more unified approach (e.g., // QELEventGenerator and MECGenerator). -- S. Gardiner -// Set the final state lepton polarization. A mass-less lepton would be fully -// polarized. This would be exact for neutrinos and a very good approximation -// for electrons for the energies this generator is going to be used. This is -// not the case for muons and, mainly, for taus. I need to refine this later. -// How? See Kuzmin, Lyubushkin and Naumov, hep-ph/0312107 - // get the final state primary lepton GHepParticle * fsl = ev->FinalStatePrimaryLepton(); if ( !fsl ) { @@ -39,27 +44,255 @@ void genie::utils::SetPrimaryLeptonPolarization( GHepRecord * ev ) << "Final state lepton not set yet! \n" << *ev; return; } - - // Get (px,py,pz) @ LAB - TVector3 plab( fsl->Px(), fsl->Py(), fsl->Pz() ); - - // In the limit m/E->0: leptons are left-handed and their anti-particles - // are right-handed - int pdgc = fsl->Pdg(); - if ( pdg::IsNeutrino(pdgc) || pdg::IsElectron(pdgc) || - pdg::IsMuon(pdgc) || pdg::IsTau(pdgc) ) - { - plab *= -1; // left-handed - } - + //-- Get the interaction + Interaction * interaction = ev->Summary(); + //-- Access cross section algorithm for running thread + RunningThreadInfo * rtinfo = RunningThreadInfo::Instance(); + const EventGeneratorI * evg = rtinfo->RunningThread(); + const XSecAlgorithmI * xsec_alg = evg->CrossSectionAlg(); + fsl->SetPolarization(xsec_alg->FinalLeptonPolarization(interaction)); + LOG("LeptonicVertex", pINFO) << "Setting polarization angles for particle: " << fsl->Name(); - fsl->SetPolarization( plab ); - if ( fsl->PolzIsSet() ) { LOG("LeptonicVertex", pINFO) << "Polarization (rad): Polar = " << fsl->PolzPolarAngle() << ", Azimuthal = " << fsl->PolzAzimuthAngle(); } + +} +//___________________________________________________________________________ +void genie::utils::CalculatePolarizationVectorWithNuclearTensor( + TVector3 & polarization, + const TLorentzVector & neutrinoMom, + const TLorentzVector & leptonMom, + bool isLeftPolarized, + const HermitianMatrix & NTensor +) +{ + double k[4], l[4], s[4], eskl[4]; + std::complex jp[4], jm[4]; + // k_\mu + k[0] = neutrinoMom.E(); + k[1] = -neutrinoMom.Px(); + k[2] = -neutrinoMom.Py(); + k[3] = -neutrinoMom.Pz(); + // l_\mu + l[0] = leptonMom.E(); + l[1] = -leptonMom.Px(); + l[2] = -leptonMom.Py(); + l[3] = -leptonMom.Pz(); + + double ml = leptonMom.M(); + // s_\mu + s[0] = leptonMom.P()/ml; + s[1] = -leptonMom.Vect().Unit().X()*leptonMom.E()/ml; + s[2] = -leptonMom.Vect().Unit().Y()*leptonMom.E()/ml; + s[3] = -leptonMom.Vect().Unit().Z()*leptonMom.E()/ml; + + + + // epsilon_\alpha\beta\gamma\delta s^\beta k^\gamma l^\delta + for (int a = 0; a < 4; a++) + { + eskl[a] = 0; + for (int b = 0; b < 4; b++) + { + if (b == a) continue; + for (int g = 0; g < 4; g++) + { + if (g == b || g == a) continue; + for (int d = 0; d < 4; d++) + { + if (d == g || d == b || d == a) continue; + double sb = s[b]*genie::utils::g(b,b); + double kg = k[g]*genie::utils::g(g,g); + double ld = l[d]*genie::utils::g(d,d); + eskl[a] += e(a,b,g,d)*sb*kg*ld; + } + } + } + } + + double kl = k[0]*l[0] - k[1]*l[1] - k[2]*l[2] - k[3]*l[3]; + double ks = k[0]*s[0] - k[1]*s[1] - k[2]*s[2] - k[3]*s[3]; + + for (int a = 0; a < 4; a++) + { + double aux_plus = kl + ml*ks; + double aux_minus = kl - ml*ks; + if (isLeftPolarized) + { + jp[a] = aux_plus > 0 ? (l[a]*ks - s[a]*kl - 1i*eskl[a] + ml*k[a])/sqrt(aux_plus) : 0; //jp_\alpha + jm[a] = aux_minus > 0 ? (-l[a]*ks + s[a]*kl + 1i*eskl[a] + ml*k[a])/sqrt(aux_minus) : 0; //jm_\alpha + } + else + { + jp[a] = aux_minus > 0 ? (l[a]*ks - s[a]*kl + 1i*eskl[a] - ml*k[a])/sqrt(aux_minus) : 0; //jp_\alpha + jm[a] = aux_plus > 0 ? (l[a]*ks - s[a]*kl + 1i*eskl[a] + ml*k[a])/sqrt(aux_plus) : 0; //jm_\alpha + } + } + + std::complex LWpp(0, 0), LWpm(0, 0), LWmp(0, 0), LWmm(0, 0); + for(int mu = 0; mu < 4; mu++) + { + for(int nu = mu;nu < 4; nu++) + { + LWpp += jp[mu]*std::conj(jp[nu])*NTensor(mu,nu); // Lpp_\mu\nu*W^\mu\nu + LWpm += jp[mu]*std::conj(jm[nu])*NTensor(mu,nu); // Lpm_\mu\nu*W^\mu\nu + LWmp += jm[mu]*std::conj(jp[nu])*NTensor(mu,nu); // Lmp_\mu\nu*W^\mu\nu + LWmm += jm[mu]*std::conj(jm[nu])*NTensor(mu,nu); // Lmm_\mu\nu*W^\mu\nu + if (mu != nu) + { + LWpp += jp[nu]*std::conj(jp[mu])*NTensor(nu,mu); // Lpp_\mu\nu*W^\mu\nu + LWpm += jp[nu]*std::conj(jm[mu])*NTensor(nu,mu); // Lpm_\mu\nu*W^\mu\nu + LWmp += jm[nu]*std::conj(jp[mu])*NTensor(nu,mu); // Lmp_\mu\nu*W^\mu\nu + LWmm += jm[nu]*std::conj(jm[mu])*NTensor(nu,mu); // Lmm_\mu\nu*W^\mu\nu + } + } + } + + std::complex LWppmm = LWpp + LWmm; + if (LWppmm.real() == 0 && LWppmm.imag() == 0) + { + polarization = TVector3(0, 0, 0); + return; + } + std::complex rhopp = LWpp/LWppmm; + std::complex rhopm = LWpm/LWppmm; + std::complex rhomp = LWmp/LWppmm; + std::complex rhomm = LWmm/LWppmm; + double PL = std::real(rhopp - rhomm); + double PP = std::real(rhopm + rhomp); + double PT = std::imag(rhomp - rhopm); + + TVector3 neutrinoMom3 = neutrinoMom.Vect(); + TVector3 leptonMom3 = leptonMom.Vect(); + TVector3 Pz = leptonMom3.Unit(); + TVector3 Px = neutrinoMom3.Cross(leptonMom3).Unit(); + TVector3 Py = Pz.Cross(Px); + polarization = PT*Px + PP*Py + PL*Pz; + //std::cout << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"; + //std::cout << "PL = " << PL << ", PP = " << PP << ", PT = " << PT << "\n"; + //std::cout << "UT@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" << std::endl; +} +//____________________________________________________________________________ +void genie::utils::CalculatePolarizationVectorWithStructureFunctions( + TVector3 & polarization, + const TLorentzVector & neutrinoMom, + const TLorentzVector & leptonMom, + const TLorentzVector & inNucleonMom, + const TLorentzVector & q4, + bool isLeftPolarized, + double W1, + double W2, + double W3, + double W4, + double W5, + double W6 +) +{ + double M2 = inNucleonMom.M2(); + double p[4], q[4], epq[4][4]; + p[0] = inNucleonMom.E(); + p[1] = inNucleonMom.Px(); + p[2] = inNucleonMom.Py(); + p[3] = inNucleonMom.Pz(); + + q[0] = q4.E(); + q[1] = q4.Px(); + q[2] = q4.Py(); + q[3] = q4.Pz(); + // epsilon^\alpha\beta\gamma\delta p_\gamma q_\delta + for (int a = 0; a < 4; a++) + { + for (int b = 0; b < 4; b++) + { + epq[a][b] = 0; + if (b == a) continue; + for (int g = 0; g < 4; g++) + { + if (g == b || g == a) continue; + for (int d = 0; d < 4; d++) + { + if (d == g || d == b || d == a) continue; + epq[a][b] += e(a,b,g,d)*genie::utils::g(a,a)*genie::utils::g(b,b)*p[g]*q[d]; + } + } + } + } + HermitianMatrix NucleonTensor(4); + for(int mu = 0; mu < 4; mu++) + { + for(int nu = mu; nu < 4; nu++) + { + double Wreal = -g(mu,nu)*W1 + p[mu]*p[nu]*W2/M2 + q[mu]*q[nu]*W4/M2 + (p[mu]*q[nu] + q[mu]*p[nu])*W5/2/M2; + double Wimag = epq[mu][nu]*W3/2/M2 + (q[mu]*p[nu] - p[mu]*q[nu])*W6/2/M2; + NucleonTensor.set(mu, nu, Wreal - 1i*Wimag); // W^\mu\nu + if (mu != nu) NucleonTensor.set(nu, mu, Wreal + 1i*Wimag); + } + } + + CalculatePolarizationVectorWithNuclearTensor( + polarization, + neutrinoMom, + leptonMom, + isLeftPolarized, + NucleonTensor); + } +//____________________________________________________________________________ +void genie::utils::CalculatePolarizationVectorInTargetRestFrame( + TVector3 & polarization, + const TLorentzVector & neutrinoMomTRF, + const TLorentzVector & leptonMomTRF, + bool isLeftPolarized, + double M, + double W1, + double W2, + double W3, + double W4, + double W5, + double W6 +) +{ + double ml = leptonMomTRF.M(); + double ml2 = ml*ml; + double M2 = M*M; + double Ev = neutrinoMomTRF.E(); + double El = leptonMomTRF.E(); + double Pl = leptonMomTRF.P(); + double cost = TMath::Cos( neutrinoMomTRF.Angle(leptonMomTRF.Vect()) ); + double sint = TMath::Sqrt(1 - cost*cost); + int sign = isLeftPolarized?-1:1; + double auxm = (El - Pl*cost)/2/M; + double auxp = (El + Pl*cost)/2/M; + double aux1m = (Pl - El*cost)/2/M; + double aux1p = (Pl + El*cost)/2/M; + double aux1 = ml2/2/M2; + double aux2 = (Ev + El)/M; + double R = 2*auxm*(W1 + aux1*W4) + auxp*W2 - sign*(aux2*auxm - aux1)*W3 - aux1*W5; + if (R == 0) + { + polarization = TVector3(0, 0, 0); + return; + } + double PL = sign*(2*aux1m*(W1 - aux1*W4) + aux1p*W2 - sign*(aux2*aux1m + aux1*cost)*W3 - aux1*cost*W5)/R; + double PP = sign*ml*sint*(2*W1 - W2 -sign*Ev*W3/M - ml2*W4/M2 + El*W5/M)/2/M/R; + double PT = - ml*Pl*sint*W6/2/M2/R; + + + TVector3 neutrinoMomTRF3 = neutrinoMomTRF.Vect(); + TVector3 leptonMomTRF3 = leptonMomTRF.Vect(); + TVector3 Pz = leptonMomTRF3.Unit(); + TVector3 Px = neutrinoMomTRF3.Cross(leptonMomTRF3).Unit(); + TVector3 Py = Pz.Cross(Px); + polarization = PT*Px + PP*Py + PL*Pz; + //std::cout << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"; + //std::cout << "PL = " << PL << ", PP = " << PP << ", PT = " << PT << ", R = " << R << "\n"; + //std::cout << "UT@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" << std::endl; +} +//____________________________________________________________________________ + + diff --git a/src/Physics/Common/PrimaryLeptonUtils.h b/src/Physics/Common/PrimaryLeptonUtils.h index bd68efcfc..723b1ff33 100644 --- a/src/Physics/Common/PrimaryLeptonUtils.h +++ b/src/Physics/Common/PrimaryLeptonUtils.h @@ -20,13 +20,115 @@ #ifndef _PRIMARY_LEPTON_UTILS_H #define _PRIMARY_LEPTON_UTILS_H +#include +#include +#include + +#include "TVector3.h" + namespace genie { class GHepRecord; namespace utils { + class HermitianMatrix; void SetPrimaryLeptonPolarization( GHepRecord* ev ); + + void CalculatePolarizationVectorWithNuclearTensor( + TVector3 & polarization, + const TLorentzVector & neutrinoMom, + const TLorentzVector & leptonMom, + bool isLeftPolarized, + const HermitianMatrix & NTensor); + + void CalculatePolarizationVectorWithStructureFunctions( + TVector3 & polarization, + const TLorentzVector & neutrinoMom, + const TLorentzVector & leptonMom, + const TLorentzVector & inNucleonMom, + const TLorentzVector & q4, + bool isLeftPolarized, + double W1, + double W2, + double W3, + double W4, + double W5, + double W6); + + void CalculatePolarizationVectorInTargetRestFrame( + TVector3 & polarization, + const TLorentzVector & neutrinoMomTRF, + const TLorentzVector & leptonMomTRF, + bool isLeftPolarized, + double M, + double W1, + double W2, + double W3, + double W4, + double W5, + double W6); + + + inline int g(int a, int b) ///< metric g^{ab}=g_{ab}=diag(1,-1,-1,1) + { + return (a==b)*(2*(a==0) - 1); + } + inline int e(int a, int b, int c, int d) ///< Levi-Chevita symbol, where e_{0123}=+1 + { + return (b - a)*(c - a)*(d - a)*(c - b)*(d - b)*(d - c)/12; + } + + + class HermitianMatrix + { + public: + // Constructor + explicit HermitianMatrix(size_t size) : n(size), data(size * (size + 1) / 2) {} + + // Set an element + void set(size_t i, size_t j, const std::complex& value) + { + if (i >= n || j >= n) + { + throw std::out_of_range("Indices out of range."); + } + if (i == j) + { + data[index(i, j)] = std::real(value); + } + data[index(i, j)] = (i < j) ? value : std::conj(value); + } + + // Get an element + std::complex get(size_t i, size_t j) const + { + if (i >= n || j >= n) + { + throw std::out_of_range("Indices out of range."); + } + return (i <= j) ? data[index(i, j)] : std::conj(data[index(j, i)]); + } + + // Operator () for accessing elements + std::complex operator()(size_t i, size_t j) const + { + return get(i, j); + } + + private: + size_t n; // Matrix size + std::vector> data; // Vector for storing elements of the upper triangular part + + // Convert (i, j) indices to a single index in the 1D array + size_t index(size_t i, size_t j) const + { + if (i > j) std::swap(i, j); // Use only the upper triangular part + return i * n - i * (i + 1) / 2 + j; + } + }; + + } // utils namespace } // genie namespace diff --git a/src/Physics/Multinucleon/EventGen/MECGenerator.cxx b/src/Physics/Multinucleon/EventGen/MECGenerator.cxx index d6d9ca9b9..2488f880a 100644 --- a/src/Physics/Multinucleon/EventGen/MECGenerator.cxx +++ b/src/Physics/Multinucleon/EventGen/MECGenerator.cxx @@ -92,6 +92,8 @@ void MECGenerator::ProcessEventRecord(GHepRecord * event) const // copy of an earlier version of the `DecayNucleonCluster` method here - but, watch // for this... this -> DecayNucleonCluster(event); + // Set the final-state lepton polarization + utils::SetPrimaryLeptonPolarization( event ); } else if (fXSecModel->Id().Name() == "genie::SuSAv2MECPXSec") { event->Print(); this -> SelectSuSALeptonKinematics(event); @@ -110,7 +112,7 @@ void MECGenerator::ProcessEventRecord(GHepRecord * event) const "ProcessEventRecord >> Cannot calculate kinematics for " << fXSecModel->Id().Name(); } - + } //___________________________________________________________________________ @@ -378,6 +380,8 @@ void MECGenerator::AddFinalStateLepton(GHepRecord * event) const // Boost final state primary lepton to the lab frame p4l.Boost(beta); // active Lorentz transform + + interaction->KinePtr()->SetFSLeptonP4(p4l); // Figure out the final-state primary lepton PDG code int pdgc = interaction->FSPrimLepton()->PdgCode(); @@ -839,6 +843,7 @@ void MECGenerator::SelectNSVLeptonKinematics (GHepRecord * event) const interaction->KinePtr()->Sety(gy, true); interaction->KinePtr()->Setx(gx, true); interaction->KinePtr()->SetW(gW, true); + interaction->KinePtr()->ClearRunningValues(); interaction->KinePtr()->SetFSLeptonP4(p4l); // in later methods // will also set the four-momentum and W^2 of the hadron system. @@ -846,9 +851,6 @@ void MECGenerator::SelectNSVLeptonKinematics (GHepRecord * event) const // -- Lepton event->AddParticle( pdgc, kIStStableFinalState, momidx, -1, -1, -1, p4l, v4); - // Set the final-state lepton polarization - utils::SetPrimaryLeptonPolarization( event ); - LOG("MEC",pDEBUG) << "~~~ LEPTON DONE ~~~"; } //___________________________________________________________________________ @@ -1113,12 +1115,15 @@ void MECGenerator::SelectSuSALeptonKinematics(GHepRecord* event) const interaction->KinePtr()->Sety(gy, true); interaction->KinePtr()->Setx(gx, true); interaction->KinePtr()->SetW(gW, true); + interaction->KinePtr()->ClearRunningValues(); interaction->KinePtr()->SetFSLeptonP4(p4l); // in later methods // will also set the four-momentum and W^2 of the hadron system. // -- Lepton event->AddParticle( pdgc, kIStStableFinalState, momidx, -1, -1, -1, p4l, v4 ); + + utils::SetPrimaryLeptonPolarization( event ); LOG("MEC", pDEBUG) << "~~~ LEPTON DONE ~~~"; } @@ -1315,6 +1320,7 @@ void MECGenerator::GenerateNSVInitialHadrons(GHepRecord * event) const event->AddParticle(p1); + interaction->InitStatePtr()->TgtPtr()->SetHitNucP4(p4initial_cluster); interaction->KinePtr()->SetHadSystP4(p4final_cluster); } //___________________________________________________________________________ diff --git a/src/Physics/Multinucleon/XSection/NievesSimoVacasMECPXSec2016.cxx b/src/Physics/Multinucleon/XSection/NievesSimoVacasMECPXSec2016.cxx index 7c1b2d904..5bfc0b0ba 100644 --- a/src/Physics/Multinucleon/XSection/NievesSimoVacasMECPXSec2016.cxx +++ b/src/Physics/Multinucleon/XSection/NievesSimoVacasMECPXSec2016.cxx @@ -21,6 +21,7 @@ #include "Physics/Multinucleon/XSection/NievesSimoVacasMECPXSec2016.h" #include "Physics/Multinucleon/XSection/MECUtils.h" #include "Physics/XSectionIntegration/XSecIntegratorI.h" +#include "Physics/Common/PrimaryLeptonUtils.h" using namespace genie; using namespace genie::constants; @@ -166,10 +167,10 @@ double NievesSimoVacasMECPXSec2016::XSec( // Check that the input kinematical point is within the range // in which hadron tensors are known (for chosen target) - double Ev = interaction->InitState().ProbeE(kRfLab); - double Tl = interaction->Kine().GetKV(kKVTl); - double costl = interaction->Kine().GetKV(kKVctl); - double ml = interaction->FSPrimLepton()->Mass(); + //double Ev = interaction->InitState().ProbeE(kRfLab); + //double Tl = interaction->Kine().GetKV(kKVTl); + //double costl = interaction->Kine().GetKV(kKVctl); + //double ml = interaction->FSPrimLepton()->Mass(); double Q0 = interaction->Kine().GetKV(kKVQ0); double Q3 = interaction->Kine().GetKV(kKVQ3); @@ -323,16 +324,10 @@ double NievesSimoVacasMECPXSec2016::XSec( if( fMECScaleAlg ) xsec *= fMECScaleAlg->GetScaling( * interaction ) ; - if ( kps != kPSTlctl && kps != kPSWQ2fE ) { - LOG("NievesSimoVacasMEC", pWARN) - << "Doesn't support transformation from " - << KinePhaseSpace::AsString(kPSTlctl) << " to " - << KinePhaseSpace::AsString(kps); - xsec = 0; - } - else if ( kps == kPSWQ2fE && xsec != 0. ) { - double J = utils::kinematics::Jacobian( interaction, kPSTlctl, kps ); - xsec *= J; + if ( kps != kPSTlctl ) { + // Compute the appropriate Jacobian for transformation to the requested phase space + double J = utils::kinematics::Jacobian(interaction, kPSTlctl, kps); + xsec *= J; } return xsec; @@ -376,6 +371,9 @@ void NievesSimoVacasMECPXSec2016::LoadConfig(void) // Cross section scaling factor GetParam( "MEC-CC-XSecScale", fXSecCCScale ) ; GetParam( "MEC-NC-XSecScale", fXSecNCScale ) ; + + // Do precise calculation of lepton polarization + GetParamDef( "PreciseLeptonPol", fIsPreciseLeptonPolarization, false ) ; fHadronTensorModel = dynamic_cast ( this->SubAlg("HadronTensorAlg") ); if( !fHadronTensorModel ) { @@ -415,3 +413,352 @@ void NievesSimoVacasMECPXSec2016::LoadConfig(void) } } +//_________________________________________________________________________ +const TVector3 & NievesSimoVacasMECPXSec2016::FinalLeptonPolarization (const Interaction* interaction) const +{ + if (!fIsPreciseLeptonPolarization) return XSecAlgorithmI::FinalLeptonPolarization(interaction); + + const Kinematics& kinematics = interaction -> Kine(); + const InitialState& init_state = interaction -> InitState(); + const Target& tgt = init_state.Tgt(); + + TLorentzVector * tempNeutrino = init_state.GetProbeP4(kRfLab); + TLorentzVector neutrinoMom = *tempNeutrino; + delete tempNeutrino; + const TLorentzVector leptonMom = kinematics.FSLeptonP4(); + + double Ev = neutrinoMom.E(); + double El = leptonMom.E(); + double ml = interaction->FSPrimLepton()->Mass(); + double Tl = El - ml; + TVector3 neutrinoMom3 = neutrinoMom.Vect(); + TVector3 leptonMom3 = leptonMom.Vect(); + double pv = neutrinoMom3.Mag(); + double pl = leptonMom3.Mag(); + double costl = pl*pv != 0 ? neutrinoMom3.Dot(leptonMom3)/pl/pv : 0; + double Q0 = 0.; + double Q3 = 0.; + genie::utils::mec::Getq0q3FromTlCostl(Tl, costl, Ev, ml, Q0, Q3); + + const double M = 1; // the polarization doesn't depend on mass of target in target rest frame + // This function returns d2sigma/(dTmu dcos_mu) in GeV^(-3) + int target_pdg = tgt.Pdg(); + + int A_request = pdg::IonPdgCodeToA(target_pdg); + int Z_request = pdg::IonPdgCodeToZ(target_pdg); + + // To generate cross-sections for nuclei other than those with hadron + // tensors we need to pull both the full cross-section and + // the pn initial state fraction. + // Non-isoscalar nuclei are beyond the original published Valencia model + // and scale with A according to the number of pp, pn, or nn pairs + // the probe is expected to find. + // There is some by-hand optimization here, skipping the delta part when + // only the total cross-section is requested. + // Possible future models without a Delta had tensor would also use that + // flag to call this without computing the Delta part. + + // Try to look up a hadron tensor in the pool that is an exact match for + // the target nucleus. If an exact match cannot be found, decide upon a + // suitable substitute based on the mass number A and proton number Z. + + int tensor_pdg = target_pdg; + + /// \todo Replace these hard-coded replacements with an equivalent XML + /// configuration + if ( ! fHadronTensorModel->GetTensor(tensor_pdg, genie::kHT_MEC_FullAll) ) + { + + if ( A_request == 4 && Z_request == 2 ) + { + tensor_pdg = kPdgTgtC12; + // This is for helium 4, but use carbon tensor + // the use of nuclear density parameterization is suspicious + // but some users (MINERvA) need something not nothing. + // The pn will be exactly 1/3, but pp and nn will be ~1/4 + // Because the combinatorics are different. + // Could do lithium beryllium boron which you don't need + } + else if (A_request < 9) + { + // refuse to do D, T, He3, Li, and some Be, B + // actually it would work technically, maybe except D, T + MAXLOG("NievesSimoVacasMEC", pWARN, 10) + << "Asked to scale to deuterium through boron " + << target_pdg << " nope, lets not do that."; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + else if (A_request >= 9 && A_request < 15) + { + tensor_pdg = kPdgTgtC12; + //} + // could explicitly put in nitrogen for air + //else if ( A_request >= 14 && A < 15) { // AND CHANGE <=14 to <14. + // tensor_pdg = kPdgTgtN14; + } + else if (A_request >= 15 && A_request < 22) + { + tensor_pdg = kPdgTgtO16; + } + else if (A_request >= 22 && A_request < 33) + { + // of special interest, this gets Al27 and Si28 + tensor_pdg = 1000140280; + } + else if (A_request >= 33 && A_request < 50) + { + // of special interest, this gets Ar40 and Ti48 + tensor_pdg = kPdgTgtCa40; + } + else if (A_request >= 50 && A_request < 90) + { + // pseudoFe56, also covers many other ferrometals and Ge + tensor_pdg = 1000280560; + } + else if (A_request >= 90 && A_request < 160) + { + // use Ba112 = PseudoCd. Row5 of Periodic table useless. Ag, Xe? + tensor_pdg = 1000561120; + } + else if (A_request >= 160) + { + // use Rf208 = pseudoPb + tensor_pdg = 1001042080; + } + else + { + MAXLOG("NievesSimoVacasMEC", pWARN, 10) << "Can't calculate final lepton polarization. Set it to zero."; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + } + + const LabFrameHadronTensorI* tensor + = dynamic_cast( + fHadronTensorModel->GetTensor(tensor_pdg, genie::kHT_MEC_FullAll) ); + + // If retrieving the tensor failed, complain and return zero + if ( !tensor ) + { + LOG("NievesSimoVacasMEC", pWARN) << "Failed to load a" + " hadronic tensor for the nuclide " << tensor_pdg; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + + // Assume for now that the range of validity for the "FullAll" hadron + // tensor is the same as for the partial hadron tensors + /// \todo Revisit this assumption, and perhaps implement something + /// more robust + double Q0min = tensor->q0Min(); + double Q0max = tensor->q0Max(); + double Q3min = tensor->qMagMin(); + double Q3max = tensor->qMagMax(); + if (Q0 < Q0min || Q0 > Q0max || Q3 < Q3min || Q3 > Q3max) + { + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + + // Get the Q-value needed to calculate the cross sections using the + // hadron tensor. + /// \todo Shouldn't we get this from the nuclear model? + int nu_pdg = interaction->InitState().ProbePdg(); + double Q_value = genie::utils::mec::Qvalue(target_pdg, nu_pdg); + + // Apply Qvalue relative shift if needed: + if( fQvalueShifter ) Q_value += Q_value * fQvalueShifter -> Shift( interaction->InitState().Tgt() ) ; + + // By default, we will compute the full cross-section. If a resonance is + // set, we will calculate the part of the cross-section with an internal + // Delta line without a final state pion (usually called PPD for pioness + // Delta decay). If a {p,n} hit dinucleon was set we will calculate the + // cross-section for that component only (either full or PDD cross-section) + bool delta = interaction->ExclTag().KnownResonance(); + bool pn = (tgt.HitNucPdg() == kPdgClusterNP); + + double W1_all(0), W2_all(0), W3_all(0), W4_all(0), W5_all(0); + double W1_pn(0), W2_pn(0), W3_pn(0), W4_pn(0), W5_pn(0); + double W1(0), W2(0), W3(0), W4(0), W5(0); + + if ( delta ) + { + const LabFrameHadronTensorI* tensor_delta_all + = dynamic_cast( + fHadronTensorModel->GetTensor(tensor_pdg, genie::kHT_MEC_DeltaAll) ); + + if ( !tensor_delta_all ) + { + LOG("NievesSimoVacasMEC", pWARN) << "Failed to load a \"DeltaAll\"" + << " hadronic tensor for nuclide " << tensor_pdg; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + + const LabFrameHadronTensorI* tensor_delta_pn + = dynamic_cast( + fHadronTensorModel->GetTensor(tensor_pdg, genie::kHT_MEC_Deltapn) ); + + if ( !tensor_delta_pn ) + { + LOG("NievesSimoVacasMEC", pWARN) << "Failed to load a \"Deltapn\"" + << " hadronic tensor for nuclide " << tensor_pdg; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + + W1_all = tensor_delta_all->W1(Q0 - Q_value, Q3, M); + W2_all = tensor_delta_all->W2(Q0 - Q_value, Q3, M); + W3_all = -tensor_delta_all->W3(Q0 - Q_value, Q3, M); + W4_all = tensor_delta_all->W4(Q0 - Q_value, Q3, M); + W5_all = tensor_delta_all->W5(Q0 - Q_value, Q3, M); + W1_pn = tensor_delta_pn->W1(Q0 - Q_value, Q3, M); + W2_pn = tensor_delta_pn->W2(Q0 - Q_value, Q3, M); + W3_pn = -tensor_delta_pn->W3(Q0 - Q_value, Q3, M); + W4_pn = tensor_delta_pn->W4(Q0 - Q_value, Q3, M); + W5_pn = tensor_delta_pn->W5(Q0 - Q_value, Q3, M); + } + else + { + const LabFrameHadronTensorI* tensor_full_all + = dynamic_cast( + fHadronTensorModel->GetTensor(tensor_pdg, genie::kHT_MEC_FullAll) ); + + if ( !tensor_full_all ) + { + LOG("NievesSimoVacasMEC", pWARN) << "Failed to load a \"FullAll\"" + << " hadronic tensor for nuclide " << tensor_pdg; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + + const LabFrameHadronTensorI* tensor_full_pn + = dynamic_cast( + fHadronTensorModel->GetTensor(tensor_pdg, genie::kHT_MEC_Fullpn) ); + + if ( !tensor_full_pn ) + { + LOG("NievesSimoVacasMEC", pWARN) << "Failed to load a \"Fullpn\"" + << " hadronic tensor for nuclide " << tensor_pdg; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + + W1_all = tensor_full_all->W1(Q0 - Q_value, Q3, M); + W2_all = tensor_full_all->W2(Q0 - Q_value, Q3, M); + W3_all = -tensor_full_all->W3(Q0 - Q_value, Q3, M); + W4_all = tensor_full_all->W4(Q0 - Q_value, Q3, M); + W5_all = tensor_full_all->W5(Q0 - Q_value, Q3, M); + W1_pn = tensor_full_pn->W1(Q0 - Q_value, Q3, M); + W2_pn = tensor_full_pn->W2(Q0 - Q_value, Q3, M); + W3_pn = -tensor_full_pn->W3(Q0 - Q_value, Q3, M); + W4_pn = tensor_full_pn->W4(Q0 - Q_value, Q3, M); + W5_pn = tensor_full_pn->W5(Q0 - Q_value, Q3, M); + } + + // We need to scale the cross section appropriately if + // we are using a hadronic tensor for a nuclide that is different + // from the actual target + bool need_to_scale = (target_pdg != tensor_pdg); + + // would need to trap and treat He3, T, D special here. + if ( need_to_scale ) + { + + double PP = Z_request; + double NN = A_request - PP; + double P = pdg::IonPdgCodeToZ(tensor_pdg); + double N = pdg::IonPdgCodeToA(tensor_pdg) - P; + + double scale_pn = TMath::Sqrt( (PP*NN)/(P*N) ); + double scale_pp = TMath::Sqrt( (PP * (PP - 1.)) / (P * (P - 1.)) ); + double scale_nn = TMath::Sqrt( (NN * (NN - 1.)) / (N * (N - 1.)) ); + + LOG("NievesSimoVacasMEC", pDEBUG) + << "Scale pn pp nn for (" << target_pdg << ", " << tensor_pdg << ")" + << " : " << scale_pn << " " << scale_pp << " " << scale_nn; + + // This is an approximation in at least three senses: + // 1. We are scaling from an isoscalar nucleus using p and n counting + // 2. We are not using the right qvalue in the had tensor + // 3. We are not scaling the Delta faster than the non-Delta. + // The guess is that these are good approximations. + // A test we could document is to scale from O16 to N14 or C12 using this + // algorithm and see how many percent deviation we see from the full + // calculation. + double temp1_all = W1_all; + double temp1_pn = W1_pn * scale_pn; + double temp2_all = W2_all; + double temp2_pn = W2_pn * scale_pn; + double temp3_all = W3_all; + double temp3_pn = W3_pn * scale_pn; + double temp4_all = W4_all; + double temp4_pn = W4_pn * scale_pn; + double temp5_all = W5_all; + double temp5_pn = W5_pn * scale_pn; + if (nu_pdg > 0) + { + // matter neutrinos + temp1_all = W1_pn * scale_pn + (W1_all - W1_pn) * scale_nn; + temp2_all = W2_pn * scale_pn + (W2_all - W2_pn) * scale_nn; + temp3_all = W3_pn * scale_pn + (W3_all - W3_pn) * scale_nn; + temp4_all = W4_pn * scale_pn + (W4_all - W4_pn) * scale_nn; + temp5_all = W5_pn * scale_pn + (W5_all - W5_pn) * scale_nn; + } + else + { + // antineutrinos + temp1_all = W1_pn * scale_pn + (W1_all - W1_pn) * scale_pp; + temp2_all = W2_pn * scale_pn + (W2_all - W2_pn) * scale_pp; + temp3_all = W3_pn * scale_pn + (W3_all - W3_pn) * scale_pp; + temp4_all = W4_pn * scale_pn + (W4_all - W4_pn) * scale_pp; + temp5_all = W5_pn * scale_pn + (W5_all - W5_pn) * scale_pp; + } + + W1_all = temp1_all; + W1_pn = temp1_pn; + W2_all = temp2_all; + W2_pn = temp2_pn; + W3_all = temp3_all; + W3_pn = temp3_pn; + W4_all = temp4_all; + W4_pn = temp4_pn; + W5_all = temp5_all; + W5_pn = temp5_pn; + } + + // Choose the right kind of cross section ("all" or "pn") to return + // based on whether a {p, n} dinucleon was hit + if (pn) + { + W1 = W1_pn; + W2 = W2_pn; + W3 = W3_pn; + W4 = W4_pn; + W5 = W5_pn; + } + else + { + W1 = W1_all; + W2 = W2_all; + W3 = W3_all; + W4 = W4_all; + W5 = W5_all; + } + + bool is_neutrino = pdg::IsNeutrino(init_state.ProbePdg()); + genie::utils::CalculatePolarizationVectorInTargetRestFrame( + fFinalLeptonPolarization, + neutrinoMom, + leptonMom, + is_neutrino, + M, W1,W2,W3,W4,W5,0); + + +// std::cout << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"; +// std::cout << fFinalLeptonPolarization.Mag() << "\n"; +// std::cout << "NVMEC@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" << std::endl; + + return fFinalLeptonPolarization; +} diff --git a/src/Physics/Multinucleon/XSection/NievesSimoVacasMECPXSec2016.h b/src/Physics/Multinucleon/XSection/NievesSimoVacasMECPXSec2016.h index 273126f14..9228280d3 100644 --- a/src/Physics/Multinucleon/XSection/NievesSimoVacasMECPXSec2016.h +++ b/src/Physics/Multinucleon/XSection/NievesSimoVacasMECPXSec2016.h @@ -49,6 +49,7 @@ class NievesSimoVacasMECPXSec2016 : public XSecAlgorithmI { double XSec (const Interaction * i, KinePhaseSpace_t k) const; double Integral (const Interaction * i) const; bool ValidProcess (const Interaction * i) const; + const TVector3 & FinalLeptonPolarization (const Interaction* i) const; // override the Algorithm::Configure methods to load configuration // data to private data members diff --git a/src/Physics/Multinucleon/XSection/SuSAv2MECPXSec.cxx b/src/Physics/Multinucleon/XSection/SuSAv2MECPXSec.cxx index 180a4bf8a..a73c3325f 100644 --- a/src/Physics/Multinucleon/XSection/SuSAv2MECPXSec.cxx +++ b/src/Physics/Multinucleon/XSection/SuSAv2MECPXSec.cxx @@ -21,6 +21,7 @@ #include "Physics/XSectionIntegration/XSecIntegratorI.h" #include "Physics/NuclearState/FermiMomentumTablePool.h" #include "Physics/NuclearState/FermiMomentumTable.h" +#include "Physics/Common/PrimaryLeptonUtils.h" using namespace genie; @@ -51,7 +52,7 @@ double SuSAv2MECPXSec::XSec(const Interaction* interaction, int target_pdg = interaction->InitState().Tgt().Pdg(); int probe_pdg = interaction->InitState().ProbePdg(); int A_request = pdg::IonPdgCodeToA(target_pdg); - int Z_request = pdg::IonPdgCodeToZ(target_pdg); +// int Z_request = pdg::IonPdgCodeToZ(target_pdg); bool need_to_scale = false; HadronTensorType_t tensor_type = kHT_Undefined; @@ -128,7 +129,7 @@ double SuSAv2MECPXSec::XSec(const Interaction* interaction, // dinucleon was set we will calculate the cross-section for that // component only - bool pn = (interaction->InitState().Tgt().HitNucPdg() == kPdgClusterNP); + //bool pn = (interaction->InitState().Tgt().HitNucPdg() == kPdgClusterNP); // Compute the cross section using the hadron tensor double xsec = tensor->dSigma_dT_dCosTheta_rosenbluth(interaction, Delta_Q_value); @@ -160,11 +161,9 @@ double SuSAv2MECPXSec::XSec(const Interaction* interaction, if( fMECScaleAlg ) xsec *= fMECScaleAlg->GetScaling( * interaction ) ; if ( kps != kPSTlctl ) { - LOG("SuSAv2MEC", pWARN) - << "Doesn't support transformation from " - << KinePhaseSpace::AsString(kPSTlctl) << " to " - << KinePhaseSpace::AsString(kps); - xsec = 0.; + // Compute the appropriate Jacobian for transformation to the requested phase space + double J = utils::kinematics::Jacobian(interaction, kPSTlctl, kps); + xsec *= J; } return xsec; @@ -417,6 +416,9 @@ void SuSAv2MECPXSec::LoadConfig(void) GetParamDef("MEC-CC-XSecScale", fXSecCCScale, 1.) ; GetParamDef("MEC-NC-XSecScale", fXSecNCScale, 1.) ; GetParamDef("MEC-EM-XSecScale", fXSecEMScale, 1.) ; + + // Do precise calculation of lepton polarization + GetParamDef( "PreciseLeptonPol", fIsPreciseLeptonPolarization, false ) ; fHadronTensorModel = dynamic_cast ( this->SubAlg("HadronTensorAlg") ); if( !fHadronTensorModel ) { @@ -473,3 +475,132 @@ void SuSAv2MECPXSec::LoadConfig(void) } } +//_________________________________________________________________________ +const TVector3 & SuSAv2MECPXSec::FinalLeptonPolarization (const Interaction* interaction) const +{ + const ProcessInfo& proc_info = interaction->ProcInfo(); + if ( proc_info.IsEM() ) + { + LOG("SuSAv2MECPXSec", pWARN) << "For EM processes doesn't work yet. Set it to zero."; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + if (!fIsPreciseLeptonPolarization) return XSecAlgorithmI::FinalLeptonPolarization(interaction); + // Get the hadron tensor for the selected nuclide. Check the probe PDG code + // to know whether to use the tensor for CC neutrino scattering or for + // electron scattering + const double M = 1; // the polarization doesn't depend on mass of target in target rest frame + const Kinematics& kinematics = interaction -> Kine(); + const InitialState& init_state = interaction -> InitState(); + int probe_pdg = interaction->InitState().ProbePdg(); + + HadronTensorType_t tensor_type = kHT_Undefined; + if ( pdg::IsNeutrino(probe_pdg) || pdg::IsAntiNeutrino(probe_pdg) ) + { + tensor_type = kHT_MEC_FullAll; + } + else + { + LOG("SuSAv2MECPXSec", pWARN) << "Doesn't work for processes other than weak ones. Set it to zero."; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + //else + //{ + //tensor_type = kHT_MEC_EM; + //} + + // Currently we only have the relative pair contributions for C12. + int tensor_pdg = kPdgTgtC12; + // The rescaling factor will be reduced. Therefore there is no need in rescaling. +// if(tensor_pdg != target_pdg) need_to_scale = true; + + // The SuSAv2-MEC hadron tensors are defined using the same conventions + // as the Valencia MEC model, so we can use the same sort of tensor + // object to describe them. + const LabFrameHadronTensorI* tensor + = dynamic_cast( fHadronTensorModel->GetTensor(tensor_pdg, + tensor_type) ); + + // If retrieving the tensor failed, complain and return zero + if ( !tensor ) + { + LOG("SuSAv2MEC", pWARN) << "Can't calculate final lepton polarization. Set it to zero."; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + + TLorentzVector * tempNeutrino = init_state.GetProbeP4(kRfLab); + TLorentzVector neutrinoMom = *tempNeutrino; + delete tempNeutrino; + const TLorentzVector leptonMom = kinematics.FSLeptonP4(); + + double Ev = neutrinoMom.E(); + double El = leptonMom.E(); + double ml = interaction->FSPrimLepton()->Mass(); + double Tl = El - ml; + TVector3 neutrinoMom3 = neutrinoMom.Vect(); + TVector3 leptonMom3 = leptonMom.Vect(); + double pv = neutrinoMom3.Mag(); + double pl = leptonMom3.Mag(); + double costl = pl*pv != 0 ? neutrinoMom3.Dot(leptonMom3)/pl/pv : 0; + double Q0 = 0; + double Q3 = 0; + + // The Q-Value essentially corrects q0 to account for nuclear + // binding energy in the Valencia model but this effect is already + // in Guille's tensors so its set it to 0. + // However, additional corrections may be necessary: + double Delta_Q_value = Qvalue( * interaction ) ; + + genie::utils::mec::Getq0q3FromTlCostl(Tl, costl, Ev, ml, Q0, Q3); + + double Q0min = tensor->q0Min(); + double Q0max = tensor->q0Max(); + double Q3min = tensor->qMagMin(); + double Q3max = tensor->qMagMax(); + if (Q0-Delta_Q_value < Q0min || Q0-Delta_Q_value > Q0max || Q3 < Q3min || Q3 > Q3max) + { + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + + // *** Enforce the global Q^2 cut (important for EM scattering) *** + // Choose the appropriate minimum Q^2 value based on the interaction + // mode (this is important for EM interactions since the differential + // cross section blows up as Q^2 --> 0) + double Q2min = genie::controls::kMinQ2Limit; // CC/NC limit + //if ( interaction->ProcInfo().IsEM() ) + //Q2min = genie::utils::kinematics::electromagnetic::kMinQ2Limit; // EM limit + + // Neglect shift due to binding energy. The cut is on the actual + // value of Q^2, not the effective one to use in the tensor contraction. + double Q2 = Q3*Q3 - Q0*Q0; + if ( Q2 < Q2min ) + { + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + + + double W1 = tensor->W1(Q0 - Delta_Q_value, Q3, M); + double W2 = tensor->W2(Q0 - Delta_Q_value, Q3, M); + double W3 =-tensor->W3(Q0 - Delta_Q_value, Q3, M); // note invert sign + double W4 = tensor->W4(Q0 - Delta_Q_value, Q3, M); + double W5 = tensor->W5(Q0 - Delta_Q_value, Q3, M); + + bool is_neutrino = pdg::IsNeutrino(init_state.ProbePdg()); + genie::utils::CalculatePolarizationVectorInTargetRestFrame( + fFinalLeptonPolarization, + neutrinoMom, + leptonMom, + is_neutrino, + M, W1,W2,W3,W4,W5,0); + + +// std::cout << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"; +// std::cout << fFinalLeptonPolarization.Mag() << "\n"; +// std::cout << "SU@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" << std::endl; + + return fFinalLeptonPolarization; +} diff --git a/src/Physics/Multinucleon/XSection/SuSAv2MECPXSec.h b/src/Physics/Multinucleon/XSection/SuSAv2MECPXSec.h index 9c93967fb..b425b7996 100644 --- a/src/Physics/Multinucleon/XSection/SuSAv2MECPXSec.h +++ b/src/Physics/Multinucleon/XSection/SuSAv2MECPXSec.h @@ -49,6 +49,8 @@ class SuSAv2MECPXSec : public XSecAlgorithmI { double XSec(const Interaction* i, KinePhaseSpace_t k) const; double Integral(const Interaction* i) const; bool ValidProcess(const Interaction* i) const; + const TVector3 & FinalLeptonPolarization (const Interaction* i) const; + // override the Algorithm::Configure methods to load configuration // data to private data members diff --git a/src/Physics/NuclearState/NuclearUtils.cxx b/src/Physics/NuclearState/NuclearUtils.cxx index 7f6c985cc..37ddff5be 100644 --- a/src/Physics/NuclearState/NuclearUtils.cxx +++ b/src/Physics/NuclearState/NuclearUtils.cxx @@ -453,9 +453,10 @@ double genie::utils::nuclear::DensityGaus( ring = TMath::Min(ring, 0.3*a); double aeval = a + ring; - double norm = 1./((5.568 + alf*8.353)*TMath::Power(a,3.)); //0.0132; - double b = TMath::Power(r/aeval, 2.); - double dens = norm * (1. + alf*b) * TMath::Exp(-b); + double a3 = a*a*a; + double norm = 1/(5.568 + alf*8.353)/a3; //0.0132; + double b = TMath::Sq(r/aeval); + double dens = norm*(1 + alf*b)*TMath::Exp(-b); LOG("Nuclear", pINFO) << "r = " << r << ", norm = " << norm << ", dens = " << dens @@ -480,8 +481,9 @@ double genie::utils::nuclear::DensityWoodsSaxon( ring = TMath::Min(ring, 0.75*c); double ceval = c + ring; - double norm = (3./(4.*kPi*TMath::Power(c,3)))*1./(1.+TMath::Power((kPi*z/c),2)); - double dens = norm / (1 + TMath::Exp((r-ceval)/z)); + double c3 = c*c*c; + double norm = 3/4./kPi/c3/(1 + TMath::Sq(kPi*z/c)); + double dens = norm/(1 + TMath::Exp((r - ceval)/z)); LOG("Nuclear", pINFO) << "r = " << r << ", norm = " << norm diff --git a/src/Physics/QuasiElastic/EventGen/QELEventGeneratorSM.cxx b/src/Physics/QuasiElastic/EventGen/QELEventGeneratorSM.cxx index 072f19807..db4ad196a 100644 --- a/src/Physics/QuasiElastic/EventGen/QELEventGeneratorSM.cxx +++ b/src/Physics/QuasiElastic/EventGen/QELEventGeneratorSM.cxx @@ -179,7 +179,7 @@ void QELEventGeneratorSM::ProcessEventRecord(GHepRecord * evrec) const Range1D_t rkF = sm_utils->kFQES_SM_lim(Q2, v); // rkF.max = Fermi momentum - kF = rnd->RndKine().Rndm()*sm_utils->GetFermiMomentum(); + kF = rnd->RndKine().Rndm()*sm_utils->GetInitialFermiMomentum(); if (kF < rkF.min) { continue; @@ -263,6 +263,9 @@ void QELEventGeneratorSM::ProcessEventRecord(GHepRecord * evrec) const outNucleonMom.Rotate(theta-theta_k, yvec); outNucleonMom.Rotate(phi, zvec); + // One can also rotate the neutrino by an angle psi in Z frame. + // The angles of incoing particles will be same, + // The angles of outgoing particles will be fi_p - psi outLeptonMom.Rotate(psi, unit_nudir); inNucleonMom.Rotate(psi, unit_nudir); outNucleonMom.Rotate(psi, unit_nudir); @@ -286,6 +289,8 @@ void QELEventGeneratorSM::ProcessEventRecord(GHepRecord * evrec) const kinematics::WQ2toXY(E,M,W,Q2,x,y); // lock selected kinematics & clear running values + interaction->KinePtr()->SetFSLeptonP4(outLeptonMom); + interaction->KinePtr()->SetHadSystP4(outNucleonMom); interaction->KinePtr()->SetQ2(Q2, true); interaction->KinePtr()->SetW (W, true); interaction->KinePtr()->Setx (x, true); @@ -468,7 +473,7 @@ double QELEventGeneratorSM::ComputeMaxXSec(const Interaction * interaction) cons const double logQ2min = TMath::Log(TMath::Max(rQ2.min, eps)); const double logQ2max = TMath::Log(TMath::Min(rQ2.max, fQ2Min)); Kinematics * kinematics = interaction->KinePtr(); - const double pFmax = sm_utils->GetFermiMomentum(); + const double pFmax = sm_utils->GetInitialFermiMomentum(); // Now scan through kinematical variables Q2,v,kF for (int Q2_n=0; Q2_n <= N_Q2; Q2_n++) { @@ -477,10 +482,11 @@ double QELEventGeneratorSM::ComputeMaxXSec(const Interaction * interaction) cons kinematics->SetKV(kKVQ2, Q2); Range1D_t rv = sm_utils->vQES_SM_lim(Q2); const double logvmin = TMath::Log(TMath::Max(rv.min, eps)); - const double logvmax = TMath::Log(TMath::Max(rv.max, TMath::Max(rv.min, eps))); + const double logvmax = TMath::Max(TMath::Log(rv.max), logvmin); for (int v_n=0; v_n <= N_v; v_n++) { // Scan around v + // for not heavy nucleus gives nan, but it doesn't matter for latter calculations double v = TMath::Exp(v_n*(logvmax-logvmin)/N_v + logvmin); kinematics->SetKV(kKVv, v); kinematics->SetKV(kKVPn, pFmax); @@ -552,7 +558,7 @@ double QELEventGeneratorSM::ComputeMaxXSec(const Interaction * interaction, cons const double logQ2min = TMath::Log(fQ2Min); const double logQ2max = TMath::Log(rQ2.max); Kinematics * kinematics = interaction->KinePtr(); - const double pFmax = sm_utils->GetFermiMomentum(); + const double pFmax = sm_utils->GetInitialFermiMomentum(); // Now scan through kinematical variables Q2,v,kF for (int Q2_n=0; Q2_n <= N_Q2; Q2_n++) { @@ -561,10 +567,11 @@ double QELEventGeneratorSM::ComputeMaxXSec(const Interaction * interaction, cons kinematics->SetKV(kKVQ2, Q2); Range1D_t rv = sm_utils->vQES_SM_lim(Q2); const double logvmin = TMath::Log(TMath::Max(rv.min, eps)); - const double logvmax = TMath::Log(TMath::Max(rv.max, TMath::Max(rv.min, eps))); + const double logvmax = TMath::Max(TMath::Log(rv.max), logvmin); for (int v_n=0; v_n <= N_v; v_n++) { // Scan around v + // for not heavy nucleus gives nan, but it doesn't matter for latter calculations double v = TMath::Exp(v_n*(logvmax-logvmin)/N_v + logvmin); kinematics->SetKV(kKVv, v); kinematics->SetKV(kKVPn, pFmax); @@ -659,6 +666,9 @@ d3XSecSM_dQ2dvdkF_E::d3XSecSM_dQ2dvdkF_E( fInteraction(i), fpF(pF) { + AlgFactory * algf = AlgFactory::Instance(); + sm_utils = const_cast(dynamic_cast(algf->GetAlgorithm("genie::SmithMonizUtils","Default"))); + sm_utils->SetInteraction(fInteraction); } d3XSecSM_dQ2dvdkF_E::~d3XSecSM_dQ2dvdkF_E() { @@ -672,6 +682,8 @@ double d3XSecSM_dQ2dvdkF_E::DoEval(const double * xin) const // outputs: // differential cross section // + Range1D_t rv = sm_utils->vQES_SM_lim(xin[0]); + if (xin[1] < rv.min || xin[1] > rv.max) return 0; fInteraction->KinePtr()->SetKV(kKVQ2, xin[0]); fInteraction->KinePtr()->SetKV(kKVv, xin[1]); fInteraction->KinePtr()->SetKV(kKVPn, fpF); diff --git a/src/Physics/QuasiElastic/EventGen/QELEventGeneratorSM.h b/src/Physics/QuasiElastic/EventGen/QELEventGeneratorSM.h index 97b23e7ae..d2a3c94f8 100644 --- a/src/Physics/QuasiElastic/EventGen/QELEventGeneratorSM.h +++ b/src/Physics/QuasiElastic/EventGen/QELEventGeneratorSM.h @@ -104,6 +104,7 @@ class d3XSecSM_dQ2dvdkF_E: public ROOT::Math::IBaseFunctionMultiDim const XSecAlgorithmI * fModel; const Interaction * fInteraction; const double fpF; + mutable SmithMonizUtils * sm_utils; }; // // genie::utils::gsl::d1XSecSM_dQ2_E diff --git a/src/Physics/QuasiElastic/EventGen/QELEventGeneratorSuSA.cxx b/src/Physics/QuasiElastic/EventGen/QELEventGeneratorSuSA.cxx index 71ac4a97e..28c67119b 100644 --- a/src/Physics/QuasiElastic/EventGen/QELEventGeneratorSuSA.cxx +++ b/src/Physics/QuasiElastic/EventGen/QELEventGeneratorSuSA.cxx @@ -31,6 +31,7 @@ #include "Framework/ParticleData/PDGCodes.h" #include "Physics/QuasiElastic/EventGen/QELEventGeneratorSuSA.h" #include "Physics/Multinucleon/XSection/MECUtils.h" +#include "Physics/Common/PrimaryLeptonUtils.h" #include "Physics/NuclearState/NuclearModelI.h" #include "Framework/Numerical/MathUtils.h" @@ -276,10 +277,14 @@ void QELEventGeneratorSuSA::SelectLeptonKinematics (GHepRecord * event) const interaction->KinePtr()->Sety(gy, true); interaction->KinePtr()->Setx(gx, true); interaction->KinePtr()->SetW(gW, true); + interaction->KinePtr()->ClearRunningValues(); interaction->KinePtr()->SetFSLeptonP4(p4l); // -- Lepton event->AddParticle( pdgc, kIStStableFinalState, momidx, -1, -1, -1, p4l, v4); + + // Set its polarization + utils::SetPrimaryLeptonPolarization( event ); LOG("QELEvent",pDEBUG) << "~~~ LEPTON DONE ~~~"; } diff --git a/src/Physics/QuasiElastic/XSection/LinkDef.h b/src/Physics/QuasiElastic/XSection/LinkDef.h index 85c97a998..c5e00521c 100644 --- a/src/Physics/QuasiElastic/XSection/LinkDef.h +++ b/src/Physics/QuasiElastic/XSection/LinkDef.h @@ -23,8 +23,7 @@ #pragma link C++ class genie::LwlynSmithFFCC; #pragma link C++ class genie::LwlynSmithFFNC; #pragma link C++ class genie::LwlynSmithFF; -#pragma link C++ class genie::MKFFEM; -#pragma link C++ class genie::MKFFCC; +#pragma link C++ class genie::LwlynSmithIsoFFCC; #pragma link C++ class genie::GalsterELFormFactorsModel; #pragma link C++ class genie::LwlynSmithFFDeltaS; #pragma link C++ class genie::AxialFormFactorModelI; @@ -33,6 +32,7 @@ #pragma link C++ class genie::ZExpAxialFormFactorModel; #pragma link C++ class genie::MArunAxialFormFactorModel; #pragma link C++ class genie::NievesQELCCPXSec; +#pragma link C++ class genie::NievesQELCCXSec; #pragma link C++ class genie::SuSAv2QELPXSec; #pragma link C++ class genie::SmithMonizQELCCPXSec; #pragma link C++ class genie::SmithMonizQELCCXSec; @@ -47,5 +47,6 @@ // Wrappers for GSL/MathMore lib #pragma link C++ class genie::utils::gsl::d2Xsec_dQ2dv; +#pragma link C++ class genie::utils::gsl::d3XSec_dElepdCosThetalepdR_E; #endif diff --git a/src/Physics/QuasiElastic/XSection/MKFFCC.cxx b/src/Physics/QuasiElastic/XSection/LwlynSmithIsoFFCC.cxx similarity index 53% rename from src/Physics/QuasiElastic/XSection/MKFFCC.cxx rename to src/Physics/QuasiElastic/XSection/LwlynSmithIsoFFCC.cxx index d637ae2ed..5af4e0c19 100644 --- a/src/Physics/QuasiElastic/XSection/MKFFCC.cxx +++ b/src/Physics/QuasiElastic/XSection/LwlynSmithIsoFFCC.cxx @@ -14,7 +14,7 @@ Author: Igor Kakorin , Joint Institute for Nuclear Research //____________________________________________________________________________ #include "Framework/Conventions/Constants.h" -#include "Physics/QuasiElastic/XSection/MKFFCC.h" +#include "Physics/QuasiElastic/XSection/LwlynSmithIsoFFCC.h" #include "Framework/Messenger/Messenger.h" #include "Framework/ParticleData/PDGLibrary.h" #include "Framework/ParticleData/PDGCodes.h" @@ -23,44 +23,72 @@ using namespace genie; using namespace genie::constants; //____________________________________________________________________________ -MKFFCC::MKFFCC() : -LwlynSmithFF("genie::MKFFCC") +LwlynSmithIsoFFCC::LwlynSmithIsoFFCC() : +LwlynSmithFF("genie::LwlynSmithIsoFFCC") { } //____________________________________________________________________________ -MKFFCC::MKFFCC(string config) : -LwlynSmithFF("genie::MKFFCC", config) +LwlynSmithIsoFFCC::LwlynSmithIsoFFCC(string config) : +LwlynSmithFF("genie::LwlynSmithIsoFFCC", config) { } //____________________________________________________________________________ -MKFFCC::~MKFFCC() +LwlynSmithIsoFFCC::~LwlynSmithIsoFFCC() { } //____________________________________________________________________________ -double MKFFCC::F1V(const Interaction * interaction) const +double LwlynSmithIsoFFCC::F1V(const Interaction * interaction) const { - return LwlynSmithFF::F1V(interaction); + if (fIsCC) + return LwlynSmithFF::F1V(interaction); + + double F1p = this->F1P(interaction); + double F1n = this->F1N(interaction); + double _F1V = F1p + F1n; + return _F1V; } //____________________________________________________________________________ -double MKFFCC::xiF2V(const Interaction * interaction) const +double LwlynSmithIsoFFCC::xiF2V(const Interaction * interaction) const { - return LwlynSmithFF::xiF2V(interaction); + if (fIsCC) + return LwlynSmithFF::xiF2V(interaction); + + double F2p = this->F2P(interaction); + double F2n = this->F2N(interaction); + double _xiF2V = F2p + F2n; + return _xiF2V; } //____________________________________________________________________________ -double MKFFCC::FA(const Interaction * interaction) const +double LwlynSmithIsoFFCC::FA(const Interaction * interaction) const { return LwlynSmithFF::FA(interaction); } //____________________________________________________________________________ -double MKFFCC::Fp(const Interaction * interaction) const +double LwlynSmithIsoFFCC::Fp(const Interaction * interaction) const { - return LwlynSmithFF::Fp(interaction); + // get momentum transfer + const Kinematics & kine = interaction->Kine(); + double q2 = kine.q2(); + + // get struck nucleon mass & set pion mass + PDGLibrary * pdglib = PDGLibrary::Instance(); + double M = (pdglib->Find(kPdgProton)->Mass() + pdglib->Find(kPdgNeutron)->Mass())/2; + double M2 = M*M; + double Mpi = kPionMass; + double Mpi2 = TMath::Power(Mpi, 2); + + // calculate FA + double fa = this->FA(interaction); + + // calculate Fp + double _Fp = 2. * M2 * fa/(Mpi2-q2); + return _Fp; } //____________________________________________________________________________ -double MKFFCC::tau(const Interaction * interaction) const +double LwlynSmithIsoFFCC::tau(const Interaction * interaction) const { // computes q^2 / (4 * Misoscalar^2) @@ -75,6 +103,11 @@ double MKFFCC::tau(const Interaction * interaction) const //-- calculate q^2 / (4*Mnuc^2) return q2/(4*M*M); } - +//____________________________________________________________________________ +void LwlynSmithIsoFFCC::LoadConfig(void) +{ + LwlynSmithFF::LoadConfig(); + GetParamDef( "IsCCFormFactors", fIsCC, true); +} diff --git a/src/Physics/QuasiElastic/XSection/MKFFCC.h b/src/Physics/QuasiElastic/XSection/LwlynSmithIsoFFCC.h similarity index 70% rename from src/Physics/QuasiElastic/XSection/MKFFCC.h rename to src/Physics/QuasiElastic/XSection/LwlynSmithIsoFFCC.h index a582c32f9..b8c622859 100644 --- a/src/Physics/QuasiElastic/XSection/MKFFCC.h +++ b/src/Physics/QuasiElastic/XSection/LwlynSmithIsoFFCC.h @@ -1,10 +1,11 @@ //____________________________________________________________________________ /*! -\class genie::MKFFCC +\class genie::LwlynSmithIsoFFCC \brief Is a concrete implementation of the QELFormFactorsModelI: - Form Factors for MK SPP model. + Form Factors for Quasi Elastic CC vN scattering according to + Llewellyn-Smith model for isoscalar nucleon. \author Igor Kakorin , Joint Institute for Nuclear Research \n @@ -20,19 +21,19 @@ */ //____________________________________________________________________________ -#ifndef _MK_CC_FORM_FACTOR_MODEL_H_ -#define _MK_CC_FORM_FACTOR_MODEL_H_ +#ifndef _LLEWELLYN_SMITH_ISO_CC_FORM_FACTOR_MODEL_H_ +#define _LLEWELLYN_SMITH_ISO_CC_FORM_FACTOR_MODEL_H_ #include "Physics/QuasiElastic/XSection/LwlynSmithFF.h" namespace genie { -class MKFFCC : public LwlynSmithFF { +class LwlynSmithIsoFFCC : public LwlynSmithFF { public: - MKFFCC(); - MKFFCC(string config); - virtual ~MKFFCC(); + LwlynSmithIsoFFCC(); + LwlynSmithIsoFFCC(string config); + virtual ~LwlynSmithIsoFFCC(); // QELFormFactorModelI interface implementation double F1V (const Interaction * interaction) const; @@ -41,6 +42,10 @@ class MKFFCC : public LwlynSmithFF { double Fp (const Interaction * interaction) const; double tau (const Interaction * interaction) const; +protected: + virtual void LoadConfig (void); + bool fIsCC; + }; diff --git a/src/Physics/QuasiElastic/XSection/LwlynSmithQELCCPXSec.cxx b/src/Physics/QuasiElastic/XSection/LwlynSmithQELCCPXSec.cxx index 5d743afbc..360fc37a8 100644 --- a/src/Physics/QuasiElastic/XSection/LwlynSmithQELCCPXSec.cxx +++ b/src/Physics/QuasiElastic/XSection/LwlynSmithQELCCPXSec.cxx @@ -32,6 +32,7 @@ #include "Framework/Utils/KineUtils.h" #include "Physics/NuclearState/NuclearUtils.h" #include "Physics/QuasiElastic/XSection/QELUtils.h" +#include "Physics/Common/PrimaryLeptonUtils.h" using namespace genie; using namespace genie::constants; @@ -209,7 +210,7 @@ double LwlynSmithQELCCPXSec::FullDifferentialXSec(const Interaction* interactio double mNi = init_state.Tgt().HitNucMass(); // Hadronic matrix element for CC neutrino interactions should really use - // the "nucleon mass," i.e., the mean of the proton and neutrino masses. + // the "nucleon mass," i.e., the mean of the proton and neutron masses. // This expression would also work for NC and EM scattering (since the // initial and final on-shell nucleon masses would be the same) double mNucleon = ( mNi + interaction->RecoilNucleon()->Mass() ) / 2.; @@ -260,8 +261,8 @@ double LwlynSmithQELCCPXSec::FullDifferentialXSec(const Interaction* interactio double tau = Q2tilde / (4 * std::pow(mNucleon, 2)); double h1 = FA*FA*(1 + tau) + tau*(F1V + xiF2V)*(F1V + xiF2V); double h2 = FA*FA + F1V*F1V + tau*xiF2V*xiF2V; - double h3 = 2.0 * FA * (F1V + xiF2V); - double h4 = 0.25 * xiF2V*xiF2V *(1-tau) + 0.5*F1V*xiF2V + FA*Fp - tau*Fp*Fp; + double h3 = 2.0 * FA * (F1V + xiF2V); // toghether with sign of FA ("-") will give correct total sign + double h4 = (0.25 * xiF2V*xiF2V *(1-tau) + 0.5*F1V*xiF2V + FA*Fp - tau*Fp*Fp); bool is_neutrino = pdg::IsNeutrino(init_state.ProbePdg()); int sign = (is_neutrino) ? -1 : 1; @@ -427,6 +428,9 @@ void LwlynSmithQELCCPXSec::LoadConfig(void) // Cross section scaling factor GetParam( "QEL-CC-XSecScale", fXSecCCScale ) ; GetParam( "QEL-NC-XSecScale", fXSecNCScale ) ; + + // Do precise calculation of lepton polarization + GetParamDef( "PreciseLeptonPol", fIsPreciseLeptonPolarization, false ) ; double thc ; GetParam( "CabibboAngle", thc ) ; @@ -474,3 +478,161 @@ void LwlynSmithQELCCPXSec::LoadConfig(void) // Decide whether or not it should be used in FullDifferentialXSec GetParamDef( "DoPauliBlocking", fDoPauliBlocking, true ); } +//____________________________________________________________________________ +const TVector3 & LwlynSmithQELCCPXSec::FinalLeptonPolarization (const Interaction* interaction) const +{ + if (!fIsPreciseLeptonPolarization) return XSecAlgorithmI::FinalLeptonPolarization(interaction); + const QELFormFactorsModelI* qel_ff_mod = dynamic_cast (this->SubAlg("FormFactorsAlg")); + const AlgId & qel_ff_mod_id = qel_ff_mod->Id(); + std::string qel_ff_mod_name = qel_ff_mod_id.Name(); + bool isosymmetry = true; + if (qel_ff_mod_name == "genie::LwlynSmithFFCC") isosymmetry = false; + if (qel_ff_mod_name == "genie::LwlynSmithIsoFFCC") isosymmetry = true; + + // First we need access to all of the particles in the interaction + // The particles were stored in the lab frame + const Kinematics& kinematics = interaction -> Kine(); + const InitialState& init_state = interaction -> InitState(); + const Target& tgt = init_state.Tgt(); + + bool is_neutrino = pdg::IsNeutrino(init_state.ProbePdg()); + + // HitNucMass() looks up the PDGLibrary (on-shell) value for the initial + // struck nucleon + double Mi_onshell = tgt.HitNucMass(); + + // On-shell mass of final nucleon (from PDGLibrary) + double Mf = interaction->RecoilNucleon()->Mass(); + + // Isoscalar mass of nucleon + double Miso = (Mi_onshell + Mf)/2; + + // Note that GetProbeP4 defaults to returning the probe 4-momentum in the + // struck nucleon rest frame, so we have to explicitly ask for the lab frame + // here + TLorentzVector * tempNeutrino = init_state.GetProbeP4(kRfLab); + TLorentzVector neutrinoMom = *tempNeutrino; + delete tempNeutrino; + TLorentzVector inNucleonMom(*init_state.TgtPtr()->HitNucP4Ptr()); + TLorentzVector inNucleonMomOnShell(inNucleonMom); + + const TLorentzVector leptonMom = kinematics.FSLeptonP4(); + const TLorentzVector outNucleonMom = kinematics.HadSystP4(); + TLorentzVector outNucleonMomOnShell(outNucleonMom); + + // Apply Pauli blocking if enabled + if ( fDoPauliBlocking && tgt.IsNucleus() && !interaction->TestBit(kIAssumeFreeNucleon) ) { + int final_nucleon_pdg = interaction->RecoilNucleonPdg(); + double kF = fPauliBlocker->GetFermiMomentum(tgt, final_nucleon_pdg, tgt.HitNucPosition()); + double pNf = outNucleonMom.P(); + if ( pNf < kF ) + { + LOG("LwlynSmith", pWARN) << "Can't calculate final lepton polarization. Set it to zero"; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + } + + if (isosymmetry) + { + double inNucleonOnShellEnergy = TMath::Hypot(Miso, inNucleonMomOnShell.P() ); + double outNucleonOnShellEnergy = TMath::Hypot(Miso, outNucleonMomOnShell.P() ); + inNucleonMomOnShell.SetE(inNucleonOnShellEnergy); + outNucleonMomOnShell.SetE(outNucleonOnShellEnergy); + } + else + { + double inNucleonOnShellEnergy = TMath::Hypot(Mi_onshell, inNucleonMomOnShell.P() ); + inNucleonMomOnShell.SetE(inNucleonOnShellEnergy); + } + + // Ordinary 4-momentum transfer + TLorentzVector qP4 = neutrinoMom - leptonMom; + double Q2 = -qP4.Mag2(); + + // Effective 4-momentum transfer (according to the deForest prescription) for + // use in computing the hadronic tensor + TLorentzVector qTildeP4 = outNucleonMomOnShell - inNucleonMomOnShell; + // If the binding energy correction causes an unphysical value + // of q0Tilde or Q2tilde, just return 0. + if ( qTildeP4.E() < 0 && init_state.Tgt().IsNucleus() && !interaction->TestBit(kIAssumeFreeNucleon) ) + { + LOG("LwlynSmith", pWARN) << "Can't calculate final lepton polarization. Set it to zero"; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + double Q2tilde = -qTildeP4.Mag2(); + if ( Q2tilde < 0 ) + { + LOG("LwlynSmith", pWARN) << "Can't calculate final lepton polarization. Set it to zero"; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + + // Store Q2tilde in the kinematic variable representing Q2. + // This will ensure that the form factors are calculated correctly + // using the de Forest prescription (Q2tilde instead of Q2). + interaction->KinePtr()->SetQ2(Q2tilde); + + // Calculate the QEL form factors + fFormFactors.Calculate(interaction); + + double FV = fFormFactors.F1V(); + double FM = fFormFactors.xiF2V(); + double FA = fFormFactors.FA(); + double FP = 2*fFormFactors.Fp(); + double FS = 2*0; + double FT = 0; + double FFV = FV*FV; + double FFM = FM*FM; + double FFT = FT*FT; + double FFS = FS*FS; + double FFVM = (FV + FM)*(FV + FM); + double FFAT = (FA + FT)*(FA + FT); + double FFMS = (FM - FS)*(FM - FS); + double FFTP = (FT - FP)*(FT - FP); + + // Off shell mass of initial nucleon + double Mi = inNucleonMom.M(); + double M = (Mi + Mf)/2; + double M2 = M*M; + double r = (Mi - Mf)/2/M; + double r2 = r*r; + + double tau = Q2/4/M2; + double w01 = (1 + tau)*FFAT + tau*FFVM; + double w21 = FFVM; + double w02 = FFV + FFAT + tau*(FFM + FFT); + double w12 = 2*FT*(FA + FT); + double w22 = FFT; + double w03 = -2*(FV + FM )*(FA + FT); + double w04 = 1/4.*(FFS - FFM + 2*(FV*(FS - FM) + (FT - FP)*(FA + FT)) + tau*(FFMS + FFTP)); + double w14 = 1/2.*((FV + FM)*(FS - FM) + (FA + FT)*(FT - FP)); + double w24 = 1/4.*FFTP; + double w05 = w02 + FS*(FV - tau*FM) + FT*(FA + FT - tau*FP); + double w15 = w12 - FM*(FV + FM) - FP*(FA + FT); + double w25 = w22 - FP*FT; + + // common factor 2M^2V^2 will be cancelled in the calculation of rho + double W1 = w01 + + w21*r2; + double W2 = w02 + w12*r + w22*r2; + double W3 = w03 ; + double W4 = w04 + w14*r + w24*r2; + double W5 = w05 + w15*r + w25*r2; + + CalculatePolarizationVectorWithStructureFunctions( + fFinalLeptonPolarization, + neutrinoMom, + leptonMom, + inNucleonMom, + qP4, + is_neutrino, + W1,W2,W3,W4,W5,0); + +// std::cout << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"; +// fFinalLeptonPolarization.Print(); +// std::cout << fFinalLeptonPolarization.Mag() << "\n"; +// std::cout << "LW@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" << std::endl; + + return fFinalLeptonPolarization; +} diff --git a/src/Physics/QuasiElastic/XSection/LwlynSmithQELCCPXSec.h b/src/Physics/QuasiElastic/XSection/LwlynSmithQELCCPXSec.h index 49bbb8f36..5541dffd4 100644 --- a/src/Physics/QuasiElastic/XSection/LwlynSmithQELCCPXSec.h +++ b/src/Physics/QuasiElastic/XSection/LwlynSmithQELCCPXSec.h @@ -23,6 +23,7 @@ #ifndef _LLEWELLYN_SMITH_QELCC_CROSS_SECTION_H_ #define _LLEWELLYN_SMITH_QELCC_CROSS_SECTION_H_ + #include "Physics/NuclearState/NuclearModelI.h" #include "Framework/EventGen/XSecAlgorithmI.h" #include "Physics/QuasiElastic/XSection/QELFormFactors.h" @@ -45,6 +46,7 @@ class LwlynSmithQELCCPXSec : public XSecAlgorithmI { double XSec (const Interaction * i, KinePhaseSpace_t k) const; double Integral (const Interaction * i) const; bool ValidProcess (const Interaction * i) const; + const TVector3 & FinalLeptonPolarization (const Interaction* i) const; // Override the Algorithm::Configure methods to load configuration // data to private data members diff --git a/src/Physics/QuasiElastic/XSection/MKFFEM.cxx b/src/Physics/QuasiElastic/XSection/MKFFEM.cxx deleted file mode 100644 index f96749e7c..000000000 --- a/src/Physics/QuasiElastic/XSection/MKFFEM.cxx +++ /dev/null @@ -1,149 +0,0 @@ -//____________________________________________________________________________ -/* - Copyright (c) 2003-2019, The GENIE Collaboration - For the full text of the license visit http://copyright.genie-mc.org - or see $GENIE/LICENSE - - Author: Igor Kakorin , Joint Institute for Nuclear Research - based on code of Costas Andreopoulos - University of Liverpool & STFC Rutherford Appleton Lab - - For the class documentation see the corresponding header file. - - -*/ -//____________________________________________________________________________ - -#include - -#include "Framework/Algorithm/AlgConfigPool.h" -#include "Framework/Algorithm/AlgFactory.h" -#include "Physics/QuasiElastic/XSection/ELFormFactors.h" -#include "Physics/QuasiElastic/XSection/ELFormFactorsModelI.h" -#include "Physics/QuasiElastic/XSection/MKFFEM.h" -#include "Framework/Conventions/Constants.h" -#include "Framework/Messenger/Messenger.h" -#include "Framework/ParticleData/PDGLibrary.h" -#include "Framework/ParticleData/PDGCodes.h" - -using namespace genie; -using namespace genie::constants; - -//____________________________________________________________________________ -MKFFEM::MKFFEM() : -QELFormFactorsModelI("genie::MKFFEM") -{ - -} -//____________________________________________________________________________ -MKFFEM::MKFFEM(string config) : -QELFormFactorsModelI("genie::MKFFEM", config) -{ - -} -//____________________________________________________________________________ -MKFFEM::~MKFFEM() -{ - -} -//____________________________________________________________________________ -double MKFFEM::F1P(const Interaction * interaction) const -{ - fELFF.Calculate(interaction); - double t = this->tau(interaction); - double T = 1 / (1 - t); - return T * (fELFF.Gep() - t * fELFF.Gmp()); -} -//____________________________________________________________________________ -double MKFFEM::F2P(const Interaction * interaction) const -{ - fELFF.Calculate(interaction); - double t = this->tau(interaction); - double T = 1 / (1 - t); - return T * (fELFF.Gmp() - fELFF.Gep()); -} -//____________________________________________________________________________ -double MKFFEM::F1N(const Interaction * interaction) const -{ - fELFF.Calculate(interaction); - double t = this->tau(interaction); - double T = 1 / (1 - t); - return T * (fELFF.Gen() - t * fELFF.Gmn()); -} -//____________________________________________________________________________ -double MKFFEM::F2N(const Interaction * interaction) const -{ - fELFF.Calculate(interaction); - double t = this->tau(interaction); - double T = 1 / (1 - t); - return T * (fELFF.Gmn() - fELFF.Gen()); -} -//____________________________________________________________________________ -double MKFFEM::F1V(const Interaction * interaction) const -{ - double F1p = this->F1P(interaction); - double F1n = this->F1N(interaction); - - double _F1V = F1p + F1n; - return _F1V; -} -//____________________________________________________________________________ -double MKFFEM::xiF2V(const Interaction * interaction) const -{ - double F2p = this->F2P(interaction); - double F2n = this->F2N(interaction); - - double _xiF2V = F2p + F2n; - return _xiF2V; -} -//____________________________________________________________________________ -double MKFFEM::FA(const Interaction * /*interaction*/ ) const -{ - return 0.; -} -//____________________________________________________________________________ -double MKFFEM::Fp(const Interaction * /*interaction*/ ) const -{ - return 0.; -} -//____________________________________________________________________________ -void MKFFEM::Configure(const Registry & config) -{ - Algorithm::Configure(config); - this->LoadConfig(); -} -//____________________________________________________________________________ -void MKFFEM::Configure(string config) -{ - Algorithm::Configure(config); - this->LoadConfig(); -} -//____________________________________________________________________________ -void MKFFEM::LoadConfig(void) -{ -// Load configuration data from its configuration Registry (or global defaults) -// to private data members - fElFFModel = - dynamic_cast (this->SubAlg("ElasticFormFactorsModel")); - assert(fElFFModel); - fELFF.SetModel(fElFFModel); - - -} -//____________________________________________________________________________ -double MKFFEM::tau(const Interaction * interaction) const -{ -// computes q^2 / (4 * MNucl^2) - - //-- get kinematics & initial state parameters - const Kinematics & kinematics = interaction->Kine(); - // const InitialState & init_state = interaction->InitState(); - double q2 = kinematics.q2(); - - PDGLibrary * pdglib = PDGLibrary::Instance(); - double M = (pdglib->Find(kPdgProton)->Mass() + pdglib->Find(kPdgNeutron)->Mass())/2; - - //-- calculate q^2 / (4*Mnuc^2) - return q2/(4*M*M); -} -//____________________________________________________________________________ diff --git a/src/Physics/QuasiElastic/XSection/MKFFEM.h b/src/Physics/QuasiElastic/XSection/MKFFEM.h deleted file mode 100644 index 9e65d43f0..000000000 --- a/src/Physics/QuasiElastic/XSection/MKFFEM.h +++ /dev/null @@ -1,69 +0,0 @@ -//____________________________________________________________________________ -/*! - -\class genie::MKFFEM - -\brief Electromagnetic form factors for MK SPP model - - -\author Igor Kakorin , Joint Institute for Nuclear Research \n - based on code of - Costas Andreopoulos - University of Liverpool & STFC Rutherford Appleton Lab - -\created Nov 12, 2019 - -\cpright Copyright (c) 2003-2019, The GENIE Collaboration - For the full text of the license visit http://copyright.genie-mc.org - or see $GENIE/LICENSE -*/ -//____________________________________________________________________________ - -#ifndef _MK_EM_FORM_FACTOR_MODEL_H_ -#define _MK_EM_FORM_FACTOR_MODEL_H_ - -#include "Physics/QuasiElastic/XSection/QELFormFactorsModelI.h" -#include "Physics/QuasiElastic/XSection/ELFormFactors.h" - -namespace genie { - -class ELFormFactorsModelI; - -class MKFFEM : public QELFormFactorsModelI { - -public: - MKFFEM(); - MKFFEM(string name); - ~MKFFEM(); - - double F1V (const Interaction * interaction) const; - double xiF2V (const Interaction * interaction) const; - double FA (const Interaction * interaction) const; - double Fp (const Interaction * interaction) const; - - // Overload the Algorithm::Configure() methods to load private data - // members from configuration options - void Configure(const Registry & config); - void Configure(string config); - -private: - - void LoadConfig (void); - - double tau (const Interaction * interaction) const; - - double F1P (const Interaction * interaction) const; - double F2P (const Interaction * interaction) const; - double F1N (const Interaction * interaction) const; - double F2N (const Interaction * interaction) const; - - const ELFormFactorsModelI * fElFFModel; - - mutable ELFormFactors fELFF; - -}; - -} // genie namespace - -#endif - diff --git a/src/Physics/QuasiElastic/XSection/NievesQELCCPXSec.cxx b/src/Physics/QuasiElastic/XSection/NievesQELCCPXSec.cxx index afe33592f..503526ebe 100644 --- a/src/Physics/QuasiElastic/XSection/NievesQELCCPXSec.cxx +++ b/src/Physics/QuasiElastic/XSection/NievesQELCCPXSec.cxx @@ -13,7 +13,6 @@ #include #include #include -#include #include "Framework/Algorithm/AlgConfigPool.h" #include "Physics/XSectionIntegration/XSecIntegratorI.h" @@ -30,24 +29,25 @@ #include "Physics/NuclearState/FermiMomentumTablePool.h" #include "Physics/NuclearState/FermiMomentumTable.h" #include "Physics/QuasiElastic/XSection/NievesQELCCPXSec.h" +#include "Physics/QuasiElastic/XSection/NievesQELCCXSec.h" #include "Framework/Numerical/RandomGen.h" #include "Framework/ParticleData/PDGCodes.h" -#include "Framework/ParticleData/PDGLibrary.h" #include "Framework/ParticleData/PDGUtils.h" +#include "Framework/ParticleData/PDGLibrary.h" #include "Framework/Numerical/MathUtils.h" #include "Framework/Utils/KineUtils.h" #include "Physics/NuclearState/NuclearUtils.h" #include "Framework/Utils/PrintUtils.h" #include "Framework/Numerical/GSLUtils.h" +#include "Physics/Common/PrimaryLeptonUtils.h" -#include // Used for testing code -#include // Used for testing code #include "Physics/NuclearState/NuclearModelI.h" using namespace genie; using namespace genie::constants; using namespace genie::controls; using namespace genie::utils; +using namespace std::complex_literals; //____________________________________________________________________________ NievesQELCCPXSec::NievesQELCCPXSec() : @@ -70,20 +70,6 @@ NievesQELCCPXSec::~NievesQELCCPXSec() double NievesQELCCPXSec::XSec(const Interaction * interaction, KinePhaseSpace_t kps) const { - /*// TESTING CODE: - // The first time this method is called, output tensor elements and other - // kinmeatics variables for various kinematics. This can the be compared - // to Nieves' fortran code for validation purposes - if(fCompareNievesTensors){ - LOG("Nieves",pNOTICE) << "Printing tensor elements for specific " - << "kinematics for testing purposes"; - CompareNievesTensors(interaction); - fCompareNievesTensors = false; - exit(0); - } - // END TESTING CODE*/ - - if ( !this->ValidProcess (interaction) ) return 0.; if ( !this->ValidKinematics(interaction) ) return 0.; @@ -161,10 +147,10 @@ double NievesQELCCPXSec::XSec(const Interaction * interaction, double Vc = vcr(& target, r); // Outgoing lepton energy and momentum including Coulomb potential - int sign = is_neutrino ? 1 : -1; + int sign = is_neutrino ? -1 : 1; double El = leptonMom.E(); double pl = leptonMom.P(); - double ElLocal = El - sign*Vc; + double ElLocal = El + sign*Vc; if ( ElLocal - ml <= 0. ) { LOG("Nieves", pDEBUG) << "Event should be rejected. Coulomb effects" @@ -316,7 +302,7 @@ double NievesQELCCPXSec::XSec(const Interaction * interaction, xsec *= xsec_scale ; - LOG("Nieves",pDEBUG) << "TESTING: RPA=" << fRPA + LOG("Nieves",pDEBUG) << "RPA=" << fRPA << ", Coulomb=" << fCoulomb << ", q2 = " << q2 << ", xsec = " << xsec; @@ -352,12 +338,20 @@ double NievesQELCCPXSec::Integral(const Interaction * in) const // let the cross section integrator do all of the work. It's smart // enough to handle free nucleon vs. nuclear targets, different // nuclear models (including the local Fermi gas model), etc. - if ( fXSecIntegrator->Id().Name() == "genie::NewQELXSec" ) { + if ( fXSecIntegrator->Id().Name() == "genie::NewQELXSec" ) + { return fXSecIntegrator->Integrate(this, in); } - else { + else if ( fXSecIntegrator->Id().Name() == "genie::NievesQELCCXSec" ) + { + Target * tgt = in->InitStatePtr()->TgtPtr(); + tgt->SetHitNucPosition(MaximalRadius(tgt) ); + return fXSecIntegrator->Integrate(this, in); + } + else + { LOG("Nieves", pFATAL) << "Splines for the Nieves CCQE model must be" - << " generated using genie::NewQELXSec"; + << " generated using genie::NievesQELCCPXSec"; std::exit(1); } } @@ -370,6 +364,10 @@ bool NievesQELCCPXSec::ValidProcess(const Interaction * interaction) const const ProcessInfo & proc_info = interaction->ProcInfo(); if(!proc_info.IsQuasiElastic()) return false; + + // The calculation is only appropriate for complex nuclear targets, + // not free nucleons. + if ( !init_state.Tgt().IsNucleus() ) return false; int nuc = init_state.Tgt().HitNucPdg(); int nu = init_state.ProbePdg(); @@ -400,13 +398,16 @@ void NievesQELCCPXSec::Configure(string config) void NievesQELCCPXSec::LoadConfig(void) { bool good_config = true ; - double thc; - GetParam( "CabibboAngle", thc ) ; - fCos8c2 = TMath::Power(TMath::Cos(thc), 2); + + double Vud; + GetParam( "CKM-Vud", Vud ) ; + fCos8c2 = TMath::Power( Vud, 2 ); + + // Do precise calculation of lepton polarization + GetParamDef( "PreciseLeptonPol", fIsPreciseLeptonPolarization, false ) ; // Cross section scaling factor - GetParam( "QEL-CC-XSecScale", fXSecCCScale ) ; - GetParam( "QEL-NC-XSecScale", fXSecNCScale ) ; + GetParam( "QEL-CC-XSecScale", fXSecCCScale ); // hbarc for unit conversion, GeV*fm fhbarc = kLightSpeed*kPlankConstant/genie::units::fermi; @@ -452,16 +453,14 @@ void NievesQELCCPXSec::LoadConfig(void) assert( fKFTable ); } - // TESTING CODE - GetParamDef( "PrintDebugData", fCompareNievesTensors, false ) ; - // END TESTING CODE - // Nuclear radius parameter (R = R0*A^(1/3)) to use when computing // the maximum radius to use to integrate the Coulomb potential GetParam("NUCL-R0", fR0) ; // fm std::string temp_mode; GetParamDef( "RmaxMode", temp_mode, std::string("VertexGenerator") ) ; + + GetParamDef( "LindhardFunction", fLindhardFunction, std::string("OriginalByNieves") ) ; // Translate the string setting the Rmax mode to the appropriate // enum value, or complain if one couldn't be found @@ -514,20 +513,29 @@ void NievesQELCCPXSec::LoadConfig(void) // Scaling factor for the Coulomb potential GetParamDef( "CoulombScale", fCoulombScale, 1.0 ); + + + AlgFactory * algf = AlgFactory::Instance(); + NievesQELCCXSec* nvi = dynamic_cast(algf->AdoptAlgorithm("genie::NievesQELCCXSec", "Default")); + f1DimIntgType = nvi->Get1DimIntgType(); + f1DimRelTol = nvi->Get1DimRelTol(); + f1DimMaxEval = nvi->Get1DimMaxEval(); + } //___________________________________________________________________________ void NievesQELCCPXSec::CNCTCLimUcalc(TLorentzVector qTildeP4, - double M, double r, bool is_neutrino, bool tgtIsNucleus, int tgt_pdgc, - int A, int Z, int N, bool hitNucIsProton, double & CN, double & CT, double & CL, - double & imaginaryU, double & t0, double & r00, bool assumeFreeNucleon) const + double M, double r, bool tgtIsNucleus, int A, int Z, int N, + double & CN, double & CT, double & CL, bool assumeFreeNucleon) const { - if ( tgtIsNucleus && !assumeFreeNucleon ) { - double dq = qTildeP4.Vect().Mag(); - double dq2 = TMath::Power(dq,2); - double q2 = 1 * qTildeP4.Mag2(); + if ( tgtIsNucleus && !assumeFreeNucleon ) + { + double q0 = qTildeP4.E(); + double dq = qTildeP4.Vect().Mag(); + double dq2 = TMath::Sq(dq); + double q2 = qTildeP4.Mag2(); //Terms for polarization coefficients CN,CT, and CL - double hbarc2 = TMath::Power(fhbarc,2); - double c0 = 0.380/fhbarc;//Constant for CN in natural units + double hbarc2 = TMath::Sq(fhbarc); + double c0 = 0.380/fhbarc/hbarc2; //Constant for CN in natural units //Density gives the nuclear density, normalized to 1 //Input radius r must be in fm @@ -536,389 +544,358 @@ void NievesQELCCPXSec::CNCTCLimUcalc(TLorentzVector qTildeP4, double rho = rhop + rhon; double rho0 = A*nuclear::Density(0,A); - double fPrime = (0.33*rho/rho0+0.45*(1-rho/rho0))*c0; + double fPrime = (0.33*rho/rho0 + 0.45*(1 - rho/rho0)); - // Get Fermi momenta - double kF1, kF2; - if(fLFG){ - if(hitNucIsProton){ - kF1 = TMath::Power(3*kPi2*rhop, 1.0/3.0) *fhbarc; - kF2 = TMath::Power(3*kPi2*rhon, 1.0/3.0) *fhbarc; - }else{ - kF1 = TMath::Power(3*kPi2*rhon, 1.0/3.0) *fhbarc; - kF2 = TMath::Power(3*kPi2*rhop, 1.0/3.0) *fhbarc; - } - }else{ - if(hitNucIsProton){ - kF1 = fKFTable->FindClosestKF(tgt_pdgc, kPdgProton); - kF2 = fKFTable->FindClosestKF(tgt_pdgc, kPdgNeutron); - }else{ - kF1 = fKFTable->FindClosestKF(tgt_pdgc, kPdgNeutron); - kF2 = fKFTable->FindClosestKF(tgt_pdgc, kPdgProton); - } - } - - double kF = TMath::Power(1.5*kPi2*rho, 1.0/3.0) *fhbarc; - - std::complex imU(relLindhardIm(qTildeP4.E(),dq,kF1,kF2, - M,is_neutrino,t0,r00)); - - imaginaryU = imag(imU); + double kF = TMath::Power(1.5*kPi2*rho, 1./3.)*fhbarc; - std::complex relLin(0,0),udel(0,0); + std::complex Unuc( LindhardNuclear(q0, dq, kF, M) ); + std::complex Udel( LindhardDelta(q0, dq, kF, M, rho*hbarc2*fhbarc) ); + std::complex Utot = Unuc + Udel; + +// CRho = 2, DeltaRho = 2500 MeV, (2.5 GeV)^2 = 6.25 GeV^2, mRho = 770 MeV, (0.770 GeV)^2 = 0.5929 GeV^2, g' = 0.63 + double aux = 0.08*4*kPi/kPionMass2; + double Vt = aux*(2*TMath::Sq( (6.25 - 0.5929)/(6.25 - q2) )*dq2/(q2 - 0.5929) + 0.63); +// f^2/4/Pi = 0.08, DeltaSubPi = 1200 MeV, (1.2 GeV)^2 = 1.44 GeV^2, g' = 0.63 + double Vl = aux*(TMath::Sq( (1.44 - kPionMass2)/(1.44 - q2) )*dq2/(q2 - kPionMass2) + 0.63); - // By comparison with Nieves' fortran code - if(imaginaryU < 0.){ - relLin = relLindhard(qTildeP4.E(),dq,kF,M,is_neutrino,imU); - udel = deltaLindhard(qTildeP4.E(),dq,rho,kF); - } - std::complex relLinTot(relLin + udel); - - /* CRho = 2 - DeltaRho = 2500 MeV, (2.5 GeV)^2 = 6.25 GeV^2 - mRho = 770 MeV, (0.770 GeV)^2 = 0.5929 GeV^2 - g' = 0.63 */ - double Vt = 0.08*4*kPi/kPionMass2 * - (2* TMath::Power((6.25-0.5929)/(6.25-q2),2)*dq2/(q2-0.5929) + 0.63); - /* f^2/4/Pi = 0.08 - DeltaSubPi = 1200 MeV, (1.2 GeV)^2 = 1.44 GeV^2 - g' = 0.63 */ - double Vl = 0.08*4*kPi/kPionMass2 * - (TMath::Power((1.44-kPionMass2)/(1.44-q2),2)*dq2/(q2-kPionMass2)+0.63); - - CN = 1.0/TMath::Power(abs(1.0-fPrime*relLin/hbarc2),2); - - CT = 1.0/TMath::Power(abs(1.0-relLinTot*Vt),2); - CL = 1.0/TMath::Power(abs(1.0-relLinTot*Vl),2); + CN = 1/std::norm(1. - c0*fPrime*Unuc); + CT = 1/std::norm(1. - Vt*Utot); + CL = 1/std::norm(1. - Vl*Utot); } - else { + else + { //Polarization Coefficients: all equal to 1.0 for free nucleon CN = 1.0; CT = 1.0; CL = 1.0; - imaginaryU = 0.0; } } //____________________________________________________________________________ -// Gives the imaginary part of the relativistic lindhard function in GeV^2 -// and sets the values of t0 and r00 -std::complex NievesQELCCPXSec::relLindhardIm(double q0, double dq, - double kFn, double kFp, - double M, - bool isNeutrino, - double & t0, - double & r00) const +// Gives the imaginary part of the relativistic lindhard function in GeV^2, Ref.1, Eq.B2 +double NievesQELCCPXSec::relLindhardIm(double q0, double dq, + double kFn, double kFp, + double M, bool isNeutrino) const { - double M2 = TMath::Power(M,2); + double M2 = TMath::Sq(M); double EF1,EF2; - if(isNeutrino){ - EF1 = TMath::Sqrt(M2+TMath::Power(kFn,2)); //EFn - EF2 = TMath::Sqrt(M2+TMath::Power(kFp,2)); //EFp - }else{ - EF1 = TMath::Sqrt(M2+TMath::Power(kFp,2)); //EFp - EF2 = TMath::Sqrt(M2+TMath::Power(kFn,2)); //EFn + if(isNeutrino) + { + EF1 = TMath::Sqrt(M2 + TMath::Sq(kFn)); //EFn + EF2 = TMath::Sqrt(M2 + TMath::Sq(kFp)); //EFp } + else + { + EF1 = TMath::Sqrt(M2 + TMath::Sq(kFp)); //EFp + EF2 = TMath::Sqrt(M2 + TMath::Sq(kFn)); //EFn + } + + double q2 = TMath::Sq(q0) - TMath::Sq(dq); + double a = (-q0 + dq*TMath::Sqrt(1 - 4*M2/q2))/2; + double epsRP = TMath::Max(TMath::Max(a,EF2 - q0),M); + // theta functions q0>0 and -q2>0 are always handled + if ( (EF2 - q0 >= EF1) || (a >= EF1) ) return 0; - double q2 = TMath::Power(q0,2) - TMath::Power(dq,2); - double a = (-q0+dq*TMath::Sqrt(1-4.0*M2/q2))/2.0; - double epsRP = TMath::Max(TMath::Max(M,EF2-q0),a); - - // Other theta functions for q are handled by nuclear suppression - // That is, q0>0 and -q2>0 are always handled, and q0>EF2-EF1 is - // handled if pauli blocking is on, because otherwise the final - // nucleon would be below the fermi sea - //if(fNievesSuppression && !interaction->TestBit(kIAssumeFreeNucleon ) - //&& !EF1-epsRP<0){ - //LOG("Nieves", pINFO) << "Average value of E(p) above Fermi sea"; - //return 0; - //}else{ - t0 = 0.5*(EF1+epsRP); - r00 = (TMath::Power(EF1,2)+TMath::Power(epsRP,2)+EF1*epsRP)/3.0; - std::complex result(0.0,-M2/2.0/kPi/dq*(EF1-epsRP)); - return result; - //} + return -M2/2/kPi/dq*(EF1 - epsRP); } //____________________________________________________________________________ -//Following obtained from fortran code by J Nieves, which contained the following comment: -/* - NUCLEON relativistic Lindhard Function - Same normalization as ULIN - Real part - taken from Eur.Phys.J.A25:299-318,2005 (Barbaro et al) - Eq. 61 - - Im. part: Juan. - - INPUT: Real*8 - q0:Energy [fm] - qm: modulus 3mom [fm] - kf: Fermi mom [fm] - - OUTPUT: Complex*16 [fm] - - USES: ruLinRelX, relLindhardIm - */ -//Takes inputs in GeV (with imU in GeV^2), and gives output in GeV^2 -std::complex NievesQELCCPXSec::relLindhard(double q0gev, - double dqgev, double kFgev, double M, - bool isNeutrino, - std::complex /* relLindIm */) const +//Inputs assumed to be in natural units, Ref.2, Eq.61 +double NievesQELCCPXSec::ruLinRelX(double q0, double dq, + double kF, double M) const { - double q0 = q0gev/fhbarc; - double qm = dqgev/fhbarc; - double kf = kFgev/fhbarc; - double m = M/fhbarc; - - if(q0>qm){ - LOG("Nieves", pWARN) << "relLindhard() failed"; - return 0.0; - } - - std::complex RealLinRel(ruLinRelX(q0,qm,kf,m)+ruLinRelX(-q0,qm,kf,m)); - double t0,r00; - std::complex ImLinRel(relLindhardIm(q0gev,dqgev,kFgev,kFgev,M,isNeutrino,t0,r00)); - //Units of GeV^2 - return(RealLinRel*TMath::Power(fhbarc,2) + 2.0*ImLinRel); + double q02 = q0*q0; + double dq2 = dq*dq; + double kF2 = kF*kF; + double M2 = M*M; + double M4 = M2*M2; + + double EF = TMath::Sqrt(M2 + kF2); + double q2 = q02 - dq2; + double q4 = q2*q2; + double ds = TMath::Sqrt(1 - 4*M2/q2); + double L1 = TMath::Log((kF + EF)/M); + + double aux1 = TMath::Sq(EF + q0); + double aux2 = (M2 + TMath::Sq(kF - dq))/aux1; + double aux3 = (M2 + TMath::Sq(kF + dq))/aux1; + double L2 = TMath::Log(TMath::Abs((1 - aux2)/(1 - aux3))); + + double aux5 = TMath::Sq(2*kF + q0*ds)/dq2; + double aux6 = TMath::Sq(2*kF - q0*ds)/dq2; + double aux7 = 4*M4*dq2/q4; + double aux8 = TMath::Sq(kF - EF*ds)/aux7; + double aux9 = TMath::Sq(kF + EF*ds)/aux7; + double L3 = TMath::Log(TMath::Abs((aux5 - 1)/(aux6 - 1)* + (aux8 - 1)/(aux9 - 1))); + + return M2*(-L1 + L2*(2*EF + q0)/2/dq - L3*ds/4)/kPi2; } -//____________________________________________________________________________ -//Inputs assumed to be in natural units -std::complex NievesQELCCPXSec::ruLinRelX(double q0, double qm, - double kf, double m) const +//____________________________________________________________________________ +//Takes inputs in GeV and gives output in GeV^2 +std::complex NievesQELCCPXSec::LindhardNuclear(double q0, double dq, double kF, double M) const { - double q02 = TMath::Power(q0, 2); - double qm2 = TMath::Power(qm, 2); - double kf2 = TMath::Power(kf, 2); - double m2 = TMath::Power(m, 2); - double m4 = TMath::Power(m, 4); - - double ef = TMath::Sqrt(m2+kf2); - double q2 = q02-qm2; - double q4 = TMath::Power(q2,2); - double ds = TMath::Sqrt(1.0-4.0*m2/q2); - double L1 = log((kf+ef)/m); - std::complex uL2( - TMath::Log(TMath::Abs( - (ef + q0 - TMath::Sqrt(m2+TMath::Power(kf-qm,2)))/ - (ef + q0 - TMath::Sqrt(m2 + TMath::Power(kf + qm,2))))) + - TMath::Log(TMath::Abs( - (ef + q0 + TMath::Sqrt(m2 + TMath::Power(kf - qm,2)))/ - (ef + q0 + TMath::Sqrt(m2 + TMath::Power(kf + qm,2)))))); - - std::complex uL3( - TMath::Log(TMath::Abs((TMath::Power(2*kf + q0*ds,2)-qm2)/ - (TMath::Power(2*kf - q0*ds,2)-qm2))) + - TMath::Log(TMath::Abs((TMath::Power(kf-ef*ds,2) - (4*m4*qm2)/q4)/ - (TMath::Power(kf+ef*ds,2) - (4*m4*qm2)/q4)))); - - std::complex RlinrelX(-L1/(16.0*kPi2)+ - uL2*(2.0*ef+q0)/(32.0*kPi2*qm)- - uL3*ds/(64.0*kPi2)); - - return RlinrelX*16.0*m2; + if (fLindhardFunction == "CJP46") + { + // Ref.4, Eqs.27-29 + double ReUnuc = 0; + double ImUnuc = 0; + if (kF != 0) + { + double v = q0*M/kF/kF; + double q = dq/kF; + double q2 = q*q; + double auxm = v/q - q/2; + double auxp = v/q + q/2; + double auxm2 = auxm*auxm; + double auxp2 = auxp*auxp; + ReUnuc = M*kF/kPi2*(-1 + ( (1 - auxm2)*TMath::Log( TMath::Abs( (auxm + 1)/(auxm - 1) ) ) - + (1 - auxp2)*TMath::Log( TMath::Abs( (auxp + 1)/(auxp - 1) ) ) )/2/q); + + double uplim = q + q2/2; + double lowlim = q - q2/2; + if ((q > 2 && uplim >= v && v >= -lowlim ) || (q < 2 && uplim >= v && v >= lowlim) ) + ImUnuc = -M*kF*(1 - auxm2 )/2/kPi/q; + else if (q < 2 && 0 <= v && v <= lowlim) + ImUnuc = -M*kF*v/kPi/q; + } + return std::complex(ReUnuc, ImUnuc); + } + + //Following obtained from fortran code by J Nieves, which contained the following comment: + // NUCLEON relativistic Lindhard Function + // Same normalization as ULIN + // Real part + // taken from Eur.Phys.J.A25:299-318,2005 (Barbaro et al) + // Eq. 61 + // Im. part: Juan. + double relLindIm = relLindhardIm(q0, dq, kF, kF, M, true); + std::complex relLind(ruLinRelX(q0,dq,kF,M) + ruLinRelX(-q0,dq,kF,M), 2*relLindIm); + return relLind; } //____________________________________________________________________________ -//Following obtained from fortran code by J Nieves, which contained the following comment: -/* - complex Lindhard function for symmetric nuclear matter: - from Appendix of - E.Oset et al Phys. Rept. 188:79, 1990 - formula A.4 - - input variables: - q_zero [fm^-1] : Energy - q_mod [fm^-1] : Momentum - rho [fm^3] : Nuclear density - k_fermi[fm^-1] : Fermi momentum - - All variables are real*8 - - output variable: - delta_lind [fm^-2] - - ATTENTION!!! - Only works properly for real q_zero, - if q_zero has an imaginary part calculates the L. function - assuming Gamma= 0. - Therefore this subroutine provides two different functions - depending on whether q_zero is real or not!!!!!!!!!!! -*/ -std::complex NievesQELCCPXSec::deltaLindhard(double q0, - double dq, double rho, double kF) const +std::complex NievesQELCCPXSec::LindhardDelta(double q0, double dq, double kF, double M, double rho) const { - double q_zero = q0/fhbarc; - double q_mod = dq/fhbarc; - double k_fermi = kF/fhbarc; - //Divide by hbarc in order to use natural units (rho is already in the correct units) - - //m = 939/197.3, md = 1232/197.3, mpi = 139/197.3 - double m = 4.7592; - double md = 6.2433; - double mpi = 0.7045; - - double fdel_f = 2.13; - double wr = md-m; - double gamma = 0; - double gammap = 0; - - double q_zero2 = TMath::Power(q_zero, 2); - double q_mod2 = TMath::Power(q_mod, 2); - double k_fermi2 = TMath::Power(k_fermi, 2); - - double m2 = TMath::Power(m, 2); - double m4 = TMath::Power(m, 4); - double mpi2 = TMath::Power(mpi, 2); - double mpi4 = TMath::Power(mpi, 4); - - double fdel_f2 = TMath::Power(fdel_f, 2); - - //For the current code q_zero is always real - //If q_zero can have an imaginary part then only the real part is used - //until z and zp are calculated - - double s = m2+q_zero2-q_mod2+ - 2.0*q_zero *TMath::Sqrt(m2+3.0/5.0*k_fermi2); - - if(s>TMath::Power(m+mpi,2)){ - double srot = TMath::Sqrt(s); - double qcm = TMath::Sqrt(TMath::Power(s,2)+mpi4+m4-2.0*(s*mpi2+s*m2+ - mpi2*m2)) /(2.0*srot); - gamma = 1.0/3.0 * 1.0/(4.0*kPi) * fdel_f2* - TMath::Power(qcm,3)/srot*(m+TMath::Sqrt(m2+TMath::Power(qcm,2)))/mpi2; - } - double sp = m2+q_zero2-q_mod2- - 2.0*q_zero *TMath::Sqrt(m2+3.0/5.0*k_fermi2); - - - if(sp > TMath::Power(m+mpi,2)){ - double srotp = TMath::Sqrt(sp); - double qcmp = TMath::Sqrt(TMath::Power(sp,2)+mpi4+m4-2.0*(sp*mpi2+sp*m2+ - mpi2*m2))/(2.0*srotp); - gammap = 1.0/3.0 * 1.0/(4.0*kPi) * fdel_f2* - TMath::Power(qcmp,3)/srotp*(m+TMath::Sqrt(m2+TMath::Power(qcmp,2)))/mpi2; - } - //}//End if statement - const std::complex iNum(0,1.0); - - std::complex z(md/(q_mod*k_fermi)*(q_zero-q_mod2/(2.0*md) - -wr +iNum*gamma/2.0)); - std::complex zp(md/(q_mod*k_fermi)*(-q_zero-q_mod2/(2.0*md) - -wr +iNum*gammap/2.0)); - - std::complex pzeta(0.0); - if(abs(z) > 50.0){ - pzeta = 2.0/(3.0*z)+2.0/(15.0*z*z*z); - }else if(abs(z) < TMath::Power(10.0,-2)){ - pzeta = 2.0*z-2.0/3.0*z*z*z-iNum*kPi/2.0*(1.0-z*z); - }else{ - pzeta = z + (1.0-z*z) * log((z+1.0)/(z-1.0))/2.0; - } - - std::complex pzetap(0); - if(abs(zp) > 50.0){ - pzetap = 2.0/(3.0*zp)+2.0/(15.0*zp*zp*zp); - }else if(abs(zp) < TMath::Power(10.0,-2)){ - pzetap = 2.0*zp-2.0/3.0*zp*zp*zp-iNum*kPi/2.0*(1.0-zp*zp); - }else{ - pzetap = zp+ (1.0-zp*zp) * log((zp+1.0)/(zp-1.0))/2.0; - } + double MD = 1.232; + double mpi = kPionMass; + + double fs2_f2 = 4.5; + double wR = MD - M; + double gamma = 0; + double gammap = 0; + + double q02 = q0*q0; + double dq2 = dq*dq; + double kF2 = kF*kF; + + double M2 = M*M; + double M4 = M2*M2; + double mpi2 = mpi*mpi; + double mpi4 = mpi2*mpi2; + //For the current code q0 is always real + //If q0 can have an imaginary part then only the real part is used + double aux1 = M2 + q02 - dq2; + double aux2 = 2*q0*TMath::Sqrt(M2 + 3*kF2/5); + double aux3 = TMath::Sq(M + mpi); + double s = aux1 + aux2; + double sp = aux1 - aux2; + + if (fLindhardFunction == "CJP46") + { + if (kF == 0) return 0.*1i; + // Ref.4, Eq.30-31 + const double fs2 = 4*kPi*0.36; + if(s > aux3) + { + double srot = TMath::Sqrt(s); + double qcm = TMath::Sqrt(TMath::Sq(s) + mpi4 + M4 - 2*(s*mpi2 + s*M2 + mpi2*M2))/2/srot; + double qcm3 = qcm*qcm*qcm; + gamma = fs2*M*qcm3/mpi2/12/kPi/srot; + } + + if(sp > aux3) + { + double srotp = TMath::Sqrt(sp); + double qcmp = TMath::Sqrt(TMath::Sq(sp) + mpi4 + M4 - 2*(sp*mpi2 + sp*M2 + mpi2*M2))/2/srotp; + double qcmp3 = qcmp*qcmp*qcmp; + gammap = fs2*M*qcmp3/mpi2/12/kPi/srotp; + } + + double b = dq/MD; + double b3 = b*b*b; + std::complex a ( q0 - dq2/2/MD - wR, gamma ); + std::complex ap(-q0 - dq2/2/MD - wR, gammap); + a /= kF; + ap /= kF; + + std::complex a_plus_b = a + b; + std::complex a_minus_b = a - b; + std::complex ap_plus_b = ap + b; + std::complex ap_minus_b = ap - b; + + std::complex L, Lp; + + if (gamma <= 0) + L = log(abs(a_plus_b/a_minus_b)); + else + L = log(a_plus_b/a_minus_b); + + if (gammap <= 0) + Lp = log(abs(ap_plus_b/ap_minus_b)); + else + Lp = log(ap_plus_b/ap_minus_b); + + double factor = 4*kF2*fs2_f2/9/kPi2; + return factor*(b*(a + ap) - (a_plus_b*a_minus_b*L + ap_plus_b*ap_minus_b*Lp)/2.)/b3; + } + + // Following obtained from fortran code by J Nieves, which contained the following comment: + // complex Lindhard function for symmetric nuclear matter: + // from Appendix of + // E.Oset et al Phys. Rept. 188:79, 1990 + // formula A.4 + // + // ATTENTION!!! + // Only works properly for real q0, + // if q0 has an imaginary part calculates the L. function + // assuming Gamma= 0. + // Therefore this subroutine provides two different functions + // depending on whether q0 is real or not!!!!!!!!!!! + // For the current code q0 is always real + // If q0 can have an imaginary part then only the real part is used + // until z and zp are calculated + + // Ref.3, Eq. A4 + if(s > aux3) + { + double srot = TMath::Sqrt(s); + double qcm = TMath::Sqrt(s*s + mpi4 + M4 - 2*(s*mpi2 + s*M2 + mpi2*M2))/2/srot; + double qcm2 = qcm*qcm; + gamma = fs2_f2*qcm*qcm2*(M + TMath::Sqrt(M2 + qcm2))/mpi2/12/kPi/srot; + } + + if(sp > aux3) + { + double srotp = TMath::Sqrt(sp); + double qcmp = TMath::Sqrt(sp*sp + mpi4 + M4 - 2*(sp*mpi2 + sp*M2 + mpi2*M2))/2/srotp; + double qcmp2 = qcmp*qcmp; + gammap = fs2_f2*qcmp*qcmp2*(M + TMath::Sqrt(M2 + qcmp2))/mpi2/12/kPi/srotp; + } + + std::complex z ( q0 - dq2/2./MD - wR, gamma/2. ); + std::complex zp(-q0 - dq2/2./MD - wR, gammap/2.); + z *= MD/dq/kF; + zp *= MD/dq/kF; + + std::complex pzeta(0, 0); + std::complex z2(z*z); + double abs_z = abs(z); + if(abs_z > 50) + { + pzeta = 2.*(1. + 1./5./z2)/3./z; + } + else if(abs_z < 1e-2) + { + pzeta = 2.*z*(1. - z2/3.) - 1i*kPi*(1. - z2)/2.; + } + else + { + pzeta = z + (1. - z2)*log((z + 1.)/(z - 1.))/2.; + } + + std::complex pzetap(0,0); + std::complex zp2(zp*zp); + double abs_zp = abs(zp); + if(abs_zp > 50) + { + pzetap = 2.*(1. + 1./5./zp2)/3./zp; + } + else if(abs_zp < 1e-2) + { + pzetap = 2.*zp*(1. - zp2/3.) - 1i*kPi*(1. - zp2)/2.; + } + else + { + pzetap = zp + (1. - zp2)*log((zp + 1.)/(zp - 1.))/2.; + } + + return 2.*rho*MD*(pzeta + pzetap)*fs2_f2/dq/kF/3.; - //Multiply by hbarc^2 to give answer in units of GeV^2 - return 2.0/3.0 * rho * md/(q_mod*k_fermi) * (pzeta +pzetap) * fdel_f2 * - TMath::Power(fhbarc,2); } - //____________________________________________________________________________ // Gives coulomb potential in units of GeV -double NievesQELCCPXSec::vcr(const Target * target, double Rcurr) const{ - if(target->IsNucleus()){ +double NievesQELCCPXSec::vcr(const Target * target, double Rcurr) const +{ + double Rmax = MaximalRadius(target); + if (Rmax == 0) return 0; + if(Rcurr >= Rmax) + { + LOG("Nieves",pNOTICE) << "Radius greater than maximum radius for coulomb corrections." + << " Integrating to max radius."; + Rcurr = Rmax; + } + int A = target->A(); int Z = target->Z(); + + ROOT::Math::IBaseFunctionOneDim * func = new utils::gsl::wrap::NievesQELvcrIntegrand(Rcurr,A,Z); + ROOT::Math::IntegrationOneDim::Type ig_type = utils::gsl::Integration1DimTypeFromString( f1DimIntgType ); + ROOT::Math::Integrator ig(*func, ig_type, 0, f1DimRelTol, f1DimMaxEval); + double result = ig.Integral(0, Rmax); + delete func; + + // Multiply by Z to normalize densities to number of protons + // Multiply by hbarc to put result in GeV instead of fm + // Multiply by an extra configurable scaling factor that defaults to unity + return -kAem*4*kPi*result*fhbarc*fCoulombScale; + +} +//____________________________________________________________________________ +double NievesQELCCPXSec::MaximalRadius(const Target * target) const +{ + if(target->IsNucleus()) + { + int A = target->A(); double Rmax = 0.; - if ( fCoulombRmaxMode == kMatchNieves ) { + if ( fCoulombRmaxMode == kMatchNieves ) + { // Rmax calculated using formula from Nieves' fortran code and default // charge and neutron matter density parameters from NuclearUtils.cxx - if (A > 20) { + if (A > 20) + { double c = TMath::Power(A,0.35), z = 0.54; Rmax = c + 9.25*z; } - else { + else + { // c = 1.75 for A <= 20 Rmax = TMath::Sqrt(20.0)*1.75; } } - else if ( fCoulombRmaxMode == kMatchVertexGeneratorRmax ) { + else if ( fCoulombRmaxMode == kMatchVertexGeneratorRmax ) + { // TODO: This solution is fragile. If the formula used by VertexGenerator // changes, then this one will need to change too. Switch to using // a common function to get Rmax for both. - Rmax = 3. * fR0 * std::pow(A, 1./3.); + Rmax = 3*fR0*std::pow(A, 1./3); } - else { + else + { LOG("Nieves", pFATAL) << "Unrecognized setting for fCoulombRmaxMode encountered" << " in NievesQELCCPXSec::vcr()"; gAbortingInErr = true; std::exit(1); } + + return Rmax; - if(Rcurr >= Rmax){ - LOG("Nieves",pNOTICE) << "Radius greater than maximum radius for coulomb corrections." - << " Integrating to max radius."; - Rcurr = Rmax; - } - - ROOT::Math::IBaseFunctionOneDim * func = new - utils::gsl::wrap::NievesQELvcrIntegrand(Rcurr,A,Z); - ROOT::Math::IntegrationOneDim::Type ig_type = - utils::gsl::Integration1DimTypeFromString("adaptive"); - - double abstol = 1; // We mostly care about relative tolerance; - double reltol = 1E-4; - int nmaxeval = 100000; - ROOT::Math::Integrator ig(*func,ig_type,abstol,reltol,nmaxeval); - double result = ig.Integral(0,Rmax); - delete func; - - // Multiply by Z to normalize densities to number of protons - // Multiply by hbarc to put result in GeV instead of fm - // Multiply by an extra configurable scaling factor that defaults to unity - return -kAem*4*kPi*result*fhbarc*fCoulombScale; - }else{ + } + else + { // If target is not a nucleus the potential will be 0 return 0.0; } } //____________________________________________________________________________ -int NievesQELCCPXSec::leviCivita(int input[]) const{ - int copy[4] = {input[0],input[1],input[2],input[3]}; - int permutations = 0; - int temp; - - for(int i=0;i<4;i++){ - for(int j=i+1;j<4;j++){ - //If any two elements are equal return 0 - if(input[i] == input[j]) - return 0; - //If a larger number is before a smaller one, use permutations - //(exchanges of two adjacent elements) to move the smaller element - //so it is before the larger element, eg 2341->2314->2134->1234 - if(copy[i]>copy[j]){ - temp = copy[j]; - for(int k=j;k>i;k--){ - copy[k] = copy[k-1]; - permutations++; - } - copy[i] = temp; - } - } - } - - if(permutations % 2 == 0){ - return 1; - }else{ - return -1; - } -} -//____________________________________________________________________________ // Calculates the constraction of the leptonic and hadronic tensors. The // expressions used here are valid in a frame in which the // initial nucleus is at rest, and qTilde must be in the z direction. @@ -929,12 +906,11 @@ const Target& target, bool assumeFreeNucleon) const { double r = target.HitNucPosition(); bool tgtIsNucleus = target.IsNucleus(); - int tgt_pdgc = target.Pdg(); + int A = target.A(); int Z = target.Z(); int N = target.N(); - bool hitNucIsProton = pdg::IsProton( target.HitNucPdg() ); - + const double k[4] = {neutrinoMom.E(),neutrinoMom.Px(),neutrinoMom.Py(),neutrinoMom.Pz()}; const double kPrime[4] = {leptonMom.E(),leptonMom.Px(), leptonMom.Py(),leptonMom.Pz()}; @@ -942,7 +918,7 @@ const Target& target, bool assumeFreeNucleon) const double q2 = qTildeP4.Mag2(); const double q[4] = {qTildeP4.E(),qTildeP4.Px(),qTildeP4.Py(),qTildeP4.Pz()}; - double q0 = q[0]; + double dq = TMath::Sqrt(TMath::Power(q[1],2)+ TMath::Power(q[2],2)+TMath::Power(q[3],2)); @@ -962,28 +938,18 @@ const Target& target, bool assumeFreeNucleon) const #endif // Calculate auxiliary parameters - double M2 = TMath::Power(M, 2); - double FA2 = TMath::Power(FA, 2); - double Fp2 = TMath::Power(Fp, 2); - double F1V2 = TMath::Power(F1V, 2); - double xiF2V2 = TMath::Power(xiF2V, 2); - double q02 = TMath::Power(q[0], 2); - double dq2 = TMath::Power(dq, 2); - double q4 = TMath::Power(q2, 2); - - double t0,r00; - double CN=1.,CT=1.,CL=1.,imU=0; - CNCTCLimUcalc(qTildeP4, M, r, is_neutrino, tgtIsNucleus, - tgt_pdgc, A, Z, N, hitNucIsProton, CN, CT, CL, imU, - t0, r00, assumeFreeNucleon); - - if ( imU > kASmallNum ) - return 0.; - - if ( !fRPA || assumeFreeNucleon ) { - CN = 1.0; - CT = 1.0; - CL = 1.0; + double M2 = M*M; + double FA2 = FA*FA; + double F1V2 = F1V*F1V; + double xiF2V2 = xiF2V*xiF2V; + double q02 = q[0]*q[0]; + double dq2 = dq*dq; + + double CN(1),CT(1),CL(1); + if (fRPA) + { + CNCTCLimUcalc(qTildeP4, M, r, tgtIsNucleus, + A, Z, N, CN, CT, CL, assumeFreeNucleon); } double tulin[4] = {0.,0.,0.,0.}; @@ -992,51 +958,19 @@ const Target& target, bool assumeFreeNucleon) const {0.,0.,0.,0.}, {0.,0.,0.,0.} }; - // TESTING CODE: - if(fCompareNievesTensors){ - // Use average values for initial momentum to calculate A, as given - // in Appendix B of Nieves' paper. T gives average values of components - // of p, and R gives the average value of two components multiplied - // together - double t3 = (0.5*q2 + q0*t0)/dq; // Average pz - - // Vector of p - - tulin[0] = t0; - tulin[3] = t3; - - // R is a 4x4 matrix, with R[mu][nu] is the average - // value of p[mu]*p[nu] - double aR = r00-M2; - double bR = (q4+4.0*r00*q02+4.0*q2*q0*t0)/(4.0*dq2); - double gamma = (aR-bR)/2.0; - double delta = (-aR+3.0*bR)/2.0/dq2; - - double r03 = (0.5*q2*t0 + q0*r00)/dq; // Average E(p)*pz - - rulin[0][0] = r00; - rulin[0][3] = r03; - rulin[1][1] = gamma; - rulin[2][2] = gamma; - rulin[3][0] = r03; - rulin[3][3] = gamma+delta*dq2; // END TESTING CODE - } - else { - // For normal code execulation, tulin is the initial nucleon momentum - tulin[0] = inNucleonMomOnShell.E(); - tulin[1] = inNucleonMomOnShell.Px(); - tulin[2] = inNucleonMomOnShell.Py(); - tulin[3] = inNucleonMomOnShell.Pz(); - for(int i=0; i<4; i++) + // Tulin is the initial nucleon momentum + tulin[0] = inNucleonMomOnShell.E(); + tulin[1] = inNucleonMomOnShell.Px(); + tulin[2] = inNucleonMomOnShell.Py(); + tulin[3] = inNucleonMomOnShell.Pz(); + + for(int i=0; i<4; i++) for(int j=0; j<4; j++) rulin[i][j] = tulin[i]*tulin[j]; - } + //Additional constants and variables - const int g[4][4] = {{1,0,0,0},{0,-1,0,0},{0,0,-1,0},{0,0,0,-1}}; - const std::complex iNum(0,1); - int leviCivitaIndexArray[4]; double imaginaryPart = 0; std::complex sum(0.0,0.0); @@ -1049,64 +983,50 @@ const Target& target, bool assumeFreeNucleon) const // Calculate LmunuAnumu by iterating over mu and nu // In each iteration, add LmunuAnumu to sum if mu=nu, and add // LmunuAnumu + LnumuAmunu if mu != nu, since we start nu at mu - double axx=0.,azz=0.,a0z=0.,a00=0.,axy=0.; for(int mu=0;mu<4;mu++){ for(int nu=mu;nu<4;nu++){ imaginaryPart = 0; if(mu == nu){ //if mu==nu then levi-civita = 0, so imaginary part = 0 - Lmunu = g[mu][mu]*kPrime[mu]*g[nu][nu]*k[nu]+g[nu][nu]*kPrime[nu]*g[mu][mu]*k[mu]-g[mu][nu]*kPrimek; + Lmunu = g(mu, mu)*kPrime[mu]*g(nu, nu)*k[nu]+g(nu, nu)*kPrime[nu]*g(mu, mu)*k[mu]-g(mu, nu)*kPrimek; }else{ //if mu!=nu, then g[mu][nu] = 0 //This same leviCivitaIndex array can be used in the else portion when //calculating Anumu - leviCivitaIndexArray[0] = mu; - leviCivitaIndexArray[1] = nu; for(int a=0;a<4;a++){ for(int b=0;b<4;b++){ - leviCivitaIndexArray[2] = a; - leviCivitaIndexArray[3] = b; - imaginaryPart += - leviCivita(leviCivitaIndexArray)*kPrime[a]*k[b]; + imaginaryPart -= e(mu, nu,a, b)*kPrime[a]*k[b]; } } //real(Lmunu) is symmetric, and imag(Lmunu) is antisymmetric - //std::complex num(g[mu][mu]*kPrime[mu]*g[nu][nu]*k[nu]+g[nu][nu]*kPrime[nu]*g[mu][mu]*k[mu],imaginaryPart); - Lmunu = g[mu][mu]*kPrime[mu]*g[nu][nu]*k[nu]+g[nu][nu]*kPrime[nu]*g[mu][mu]*k[mu] + iNum*imaginaryPart; - Lnumu = g[nu][nu]*kPrime[nu]*g[mu][mu]*k[mu]+g[mu][mu]*kPrime[mu]*g[nu][nu]*k[nu ]- iNum*imaginaryPart; + Lmunu = g(mu, mu)*kPrime[mu]*g(nu, nu)*k[nu]+g(nu, nu)*kPrime[nu]*g(mu, mu)*k[mu] + 1i*imaginaryPart; + Lnumu = g(nu, nu)*kPrime[nu]*g(mu, mu)*k[mu]+g(mu, mu)*kPrime[mu]*g(nu, nu)*k[nu] - 1i*imaginaryPart; } // End Lmunu calculation - + double aux1 = 2*CL*Fp*(Fp*q2 + 4*FA*M); if(mu ==0 && nu == 0){ Amunu = 16.0*F1V2*(2.0*rulin[0][0]*CN+2.0*q[0]*tulin[0]+q2/2.0)+ 2.0*q2*xiF2V2* (4.0-4.0*rulin[0][0]/M2-4.0*q[0]*tulin[0]/M2-q02*(4.0/q2+1.0/M2)) + 4.0*FA2*(2.0*rulin[0][0]+2.0*q[0]*tulin[0]+(q2/2.0-2.0*M2))- - (2.0*CL*Fp2*q2+8.0*FA*Fp*CL*M)*q02-16.0*F1V*xiF2V*(-q2+q02)*CN; - a00 = real(Amunu); // TESTING CODE + aux1*q02-16.0*F1V*xiF2V*(-q2+q02)*CN; sum += Lmunu*Amunu; }else if(mu == 0 && nu == 3){ Amunu = 16.0*F1V2*((2.0*rulin[0][3]+tulin[0]*dq)*CN+tulin[3]*q[0])+ - 2.0*q2*xiF2V2* - (-4.0*rulin[0][3]/M2-2.0*(dq*tulin[0]+q[0]*tulin[3])/M2-dq*q[0]*(4.0/q2+1.0/M2))+ - 4.0*FA2*((2.0*rulin[0][3]+dq*tulin[0])*CL+q[0]*tulin[3])- - (2.0*CL*Fp2*q2+8.0*FA*Fp*CL*M)*dq*q[0]- - 16.0*F1V*xiF2V*dq*q[0]; - a0z= real(Amunu); // TESTING CODE + -4.0*q2*xiF2V2*(2.0*rulin[0][3]/M2+(dq*tulin[0]+q[0]*tulin[3])/M2+dq*q[0]*(2.0/q2+0.5/M2))+ + 4.0*FA2*((2.0*rulin[0][3]+dq*tulin[0])*CL+q[0]*tulin[3])-dq*q[0]*(aux1+16.0*F1V*xiF2V); Anumu = Amunu; sum += Lmunu*Anumu + Lnumu*Amunu; }else if(mu == 3 && nu == 3){ Amunu = 16.0*F1V2*(2.0*rulin[3][3]+2.0*dq*tulin[3]-q2/2.0)+ 2.0*q2*xiF2V2*(-4.0-4.0*rulin[3][3]/M2-4.0*dq*tulin[3]/M2-dq2*(4.0/q2+1.0/M2))+ 4.0*FA2*(2.0*rulin[3][3]+2.0*dq*tulin[3]-(q2/2.0-2.0*CL*M2))- - (2.0*CL*Fp2*q2+8.0*FA*Fp*CL*M)*dq2- - 16.0*F1V*xiF2V*(q2+dq2); - azz = real(Amunu); // TESTING CODE + aux1*dq2-16.0*F1V*xiF2V*q02; sum += Lmunu*Amunu; }else if(mu ==1 && nu == 1){ Amunu = 16.0*F1V2*(2.0*rulin[1][1]-q2/2.0)+ 2.0*q2*xiF2V2*(-4.0*CT-4.0*rulin[1][1]/M2) + 4.0*FA2*(2.0*rulin[1][1]-(q2/2.0-2.0*CT*M2))- 16.0*F1V*xiF2V*CT*q2; - axx = real(Amunu); // TESTING CODE sum += Lmunu*Amunu; }else if(mu == 2 && nu == 2){ // Ayy not explicitly listed in paper. This is included so rotating the @@ -1117,9 +1037,8 @@ const Target& target, bool assumeFreeNucleon) const 16.0*F1V*xiF2V*CT*q2; sum += Lmunu*Amunu; }else if(mu ==1 && nu == 2){ - Amunu = sign*16.0*iNum*FA*(xiF2V+F1V)*(-dq*tulin[0]*CT + q[0]*tulin[3]); + Amunu = sign*16.0*1i*FA*(xiF2V+F1V)*(-dq*tulin[0]*CT + q[0]*tulin[3]); Anumu = -Amunu; // Im(A) is antisymmetric - axy = imag(Amunu); // TESTING CODE sum += Lmunu*Anumu+Lnumu*Amunu; } // All other terms will be 0 because the initial nucleus is at rest and @@ -1128,38 +1047,6 @@ const Target& target, bool assumeFreeNucleon) const } // End loop over nu } // End loop over mu - // TESTING CODE - if(fCompareNievesTensors){ - // get tmu - double tmugev = leptonMom.E() - leptonMom.Mag(); - // Print Q2, form factors, and tensor elts - std::ofstream ffstream; - ffstream.open(fTensorsOutFile, std::ios_base::app); - if(q0 > 0){ - ffstream << -q2 << "\t" << q[0] << "\t" << dq - << "\t" << axx << "\t" << azz << "\t" << a0z - << "\t" << a00 << "\t" << axy << "\t" - << CT << "\t" << CL << "\t" << CN << "\t" - << tmugev << "\t" << imU << "\t" - << F1V << "\t" << xiF2V << "\t" - << FA << "\t" << Fp << "\t" - << tulin[0] << "\t"<< tulin[1] << "\t" - << tulin[2] << "\t"<< tulin[3] << "\t" - << rulin[0][0]<< "\t"<< rulin[0][1]<< "\t" - << rulin[0][2]<< "\t"<< rulin[0][3]<< "\t" - << rulin[1][0]<< "\t"<< rulin[1][1]<< "\t" - << rulin[1][2]<< "\t"<< rulin[1][3]<< "\t" - << rulin[2][0]<< "\t"<< rulin[2][1]<< "\t" - << rulin[2][2]<< "\t"<< rulin[2][3]<< "\t" - << rulin[3][0]<< "\t"<< rulin[3][1]<< "\t" - << rulin[3][2]<< "\t"<< rulin[3][3]<< "\t" - << fVc << "\t" << fCoulombFactor << "\t"; - ffstream << "\n"; - } - ffstream.close(); - } - // END TESTING CODE - // Since the real parts of A and L are both symmetric and the imaginary // parts are antisymmetric, the contraction should be real if ( imag(sum) > kASmallNum ) @@ -1207,123 +1094,357 @@ ROOT::Math::IBaseFunctionOneDim * { return new utils::gsl::wrap::NievesQELvcrIntegrand(fRcurr,fA,fZ); } + //____________________________________________________________________________ +utils::gsl::wrap::NievesQELSmithMonizIntegrand::NievesQELSmithMonizIntegrand( + const NievesQELCCPXSec* alg_, const Interaction* interaction_, int mod_): +ROOT::Math::IBaseFunctionOneDim(), +alg(alg_), +interaction(interaction_), +mod(mod_) +{ +} //____________________________________________________________________________ -// -// NOTE: THE REMAINING IS TESTING CODE -// -// This method prints the tensor elements (as well as various inputs) for -// different kinematics. The tensor elements can then be compared to the -// output of Nieves' fortran code. -// -// The results of this code will only agree exactlly with Nieves' fortran -// if Dipole form factors are set (in UserPhysicsOptions). -// -void NievesQELCCPXSec::CompareNievesTensors(const Interaction* in) - const { - Interaction * interaction = new Interaction(*in); // copy in - - // Set input values here - double ein = 0.2; - double ctl = 0.5; - double rmaxfrac = 0.25; - - bool carbon = false; // true -> C12, false -> Pb208 - - if(fRPA) - fTensorsOutFile = "gen.RPA"; - else - fTensorsOutFile = "gen.noRPA"; - - // Calculate radius - bool klave; - double rp,ap,rn,an; - if(carbon){ - klave = true; - rp = 1.692; - ap = 1.082; - rn = 1.692; - an = 1.082; - }else{ - // Pb208 - klave = false; - rp = 6.624; - ap = 0.549; - rn = 6.890; - an = 0.549; +utils::gsl::wrap::NievesQELSmithMonizIntegrand::~NievesQELSmithMonizIntegrand() +{ + +} +//____________________________________________________________________________ +unsigned int utils::gsl::wrap::NievesQELSmithMonizIntegrand::NDim(void) const +{ + return 1; +} +//____________________________________________________________________________ +double utils::gsl::wrap::NievesQELSmithMonizIntegrand::DoEval(double rin) const +{ + return alg->IntegratedOverMomentum(interaction, rin, mod); +} +//____________________________________________________________________________ +ROOT::Math::IBaseFunctionOneDim * + utils::gsl::wrap::NievesQELSmithMonizIntegrand::Clone(void) const +{ + return new utils::gsl::wrap::NievesQELSmithMonizIntegrand(alg, interaction, mod); +} +//____________________________________________________________________________ +const TVector3 & NievesQELCCPXSec::FinalLeptonPolarization (const Interaction* interaction) const +{ + if (!fIsPreciseLeptonPolarization) return XSecAlgorithmI::FinalLeptonPolarization(interaction); + + // Get kinematics and init-state parameters + const Kinematics & kinematics = interaction -> Kine(); + const InitialState & init_state = interaction -> InitState(); + const Target & target = init_state.Tgt(); + + double Rmax = MaximalRadius(&target); + if (Rmax <= 0) return XSecAlgorithmI::FinalLeptonPolarization(interaction); + + ROOT::Math::IntegrationOneDim::Type ig_type = utils::gsl::Integration1DimTypeFromString( f1DimIntgType ); + ROOT::Math::IBaseFunctionOneDim * func = new utils::gsl::wrap::NievesQELSmithMonizIntegrand(this, interaction, 1); + ROOT::Math::Integrator ig(*func, ig_type, 0, f1DimRelTol, f1DimMaxEval); + double R = ig.Integral(0, Rmax); + delete func; + if (R == 0) + { + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; } - double rmax; - if(!klave) - rmax = TMath::Max(rp,rn) + 9.25*TMath::Max(ap,an); - else - rmax = TMath::Sqrt(20.0)*TMath::Max(rp,rn); - double r = rmax * rmaxfrac; + + func = new utils::gsl::wrap::NievesQELSmithMonizIntegrand(this, interaction, 2); + ig.SetFunction(*func); + double PLR = ig.Integral(0, Rmax); + delete func; + + func = new utils::gsl::wrap::NievesQELSmithMonizIntegrand(this, interaction, 3); + ig.SetFunction(*func); + double PPR = ig.Integral(0, Rmax); + delete func; + + double PL = PLR/R; + double PP = PPR/R; + + TLorentzVector * tempNeutrino = init_state.GetProbeP4(kRfLab); + TLorentzVector neutrinoMom = *tempNeutrino; + delete tempNeutrino; + const TLorentzVector leptonMom = kinematics.FSLeptonP4(); + + TVector3 neutrinoMom3 = neutrinoMom.Vect(); + TVector3 leptonMom3 = leptonMom.Vect(); + TVector3 Pz = leptonMom3.Unit(); + TVector3 Px = neutrinoMom3.Cross(leptonMom3).Unit(); + TVector3 Py = Pz.Cross(Px); + fFinalLeptonPolarization = PP*Py + PL*Pz; + + if (fFinalLeptonPolarization.Mag2()>1) return XSecAlgorithmI::FinalLeptonPolarization(interaction); + + return fFinalLeptonPolarization; - // Relevant objects and parameters - //const Kinematics & kinematics = interaction -> Kine(); +} +//___________________________________________________________________________________ +double NievesQELCCPXSec::IntegratedOverMomentum (const Interaction* interaction, double r, int mod) const +{ + // Get kinematics and init-state parameters + const Kinematics & kinematics = interaction -> Kine(); const InitialState & init_state = interaction -> InitState(); const Target & target = init_state.Tgt(); - // Parameters required for LmunuAnumu - double M = target.HitNucMass(); + // HitNucMass() looks up the PDGLibrary (on-shell) value for the initial + // struck nucleon + double Mi_onshell = target.HitNucMass(); + + // On-shell mass of final nucleon (from PDGLibrary) + double Mf = interaction->RecoilNucleon()->Mass(); + + // Isoscalar mass of nucleon + double M = (Mi_onshell + Mf)/2; + + // Note that GetProbeP4 defaults to returning the probe 4-momentum in the + // struck nucleon rest frame, so we have to explicitly ask for the lab frame + // here + TLorentzVector * tempNeutrino = init_state.GetProbeP4(kRfLab); + TLorentzVector neutrinoMom = *tempNeutrino; + delete tempNeutrino; + TLorentzVector leptonMom = kinematics.FSLeptonP4(); double ml = interaction->FSPrimLepton()->Mass(); + double ml2 = ml*ml; + + + // Calculate Coulomb corrections + double PlLocal = leptonMom.P(); + bool is_neutrino = pdg::IsNeutrino(init_state.ProbePdg()); + int sign = is_neutrino ? -1 : 1; + + double ElLocal = leptonMom.E(); + if ( fCoulomb ) + { + // Coulomb potential + double Vc = vcr(&target, r); - // Iterate over lepton energy (which then affects q, which is passed to - // LmunuAnumu using in and out NucleonMom - double delta = (ein-0.025)/100.0; - for(int it=0;it<100;it++){ - double tmu = it*delta; - double eout = ml + tmu; - double pout = TMath::Sqrt(eout*eout-ml*ml); - - double pin = TMath::Sqrt(ein*ein); // Assume massless neutrinos - - double q0 = ein-eout; - double dq = TMath::Sqrt(pin*pin+pout*pout-2.0*ctl*pin*pout); - double q2 = q0*q0-dq*dq; - interaction->KinePtr()->SetQ2(-q2); - - // When this method is called, inNucleonMomOnShell is unused. - // I can thus provide the calculated values using a null vector for - // inNucleonMomOnShell. I also need to put qTildeP4 in the z direction, as - // Nieves does in his paper. - TLorentzVector qTildeP4(0, 0, dq, q0); - TLorentzVector inNucleonMomOnShell(0,0,0,0); - - // neutrinoMom and leptonMom only directly affect the leptonic tensor, which - // we are not calculating now. Use them to transfer q. - TLorentzVector neutrinoMom(0,0,pout+dq,eout+q0); - TLorentzVector leptonMom(0,0,pout,eout); - - if(fCoulomb){ // Use same steps as in XSec() - // Coulomb potential - double Vc = vcr(& target, r); - fVc = Vc; - - // Outgoing lepton energy and momentum including coulomb potential - int sign = is_neutrino ? 1 : -1; - double El = leptonMom.E(); - double ElLocal = El - sign*Vc; - if(ElLocal - ml <= 0.0){ - LOG("Nieves",pINFO) << "Event should be rejected. Coulomb effects " - << "push kinematics below threshold"; - return; - } - double plLocal = TMath::Sqrt(ElLocal*ElLocal-ml*ml); + // Outgoing lepton energy and momentum including Coulomb potential + double El = ElLocal; + ElLocal = El + sign*Vc; - // Correction factor - double coulombFactor= plLocal*ElLocal/leptonMom.Vect().Mag()/El; - fCoulombFactor = coulombFactor; // Store and print - } + if ( ElLocal - ml <= 0 ) return 0; - // TODO: apply Coulomb correction to 3-momentum transfer dq + // The Coulomb correction factor blows up as pl -> 0. To guard against + // unphysically huge corrections here, require that the lepton kinetic energy + // (at infinity) is larger than the magnitude of the Coulomb potential + // (should be around a few MeV) + double KEl = El - ml; + if ( KEl <= TMath::Abs(Vc) ) return 0; + + // Local value of the lepton 3-momentum magnitude for the Coulomb correction + PlLocal = TMath::Sqrt(ElLocal*ElLocal - ml2); - fFormFactors.Calculate(interaction); - LmunuAnumu(neutrinoMom, inNucleonMomOnShell, leptonMom, qTildeP4, - M, is_neutrino, target, false); } - return; -} // END TESTING CODE -//____________________________________________________________________________ + + double q0Tilde = neutrinoMom.E() - leptonMom.E(); + + + double kFi, kFf; + ModelNuclParams(interaction, r, kFi, kFf); + + + double EFi = TMath::Hypot(M, kFi); + double EFf = TMath::Hypot(M, kFf); + + int nucl_pdg_ini = target.HitNucPdg(); + bool tgtIsNucleus = target.IsNucleus(); + int A = target.A(); + int Z = target.Z(); + int N = target.N(); + bool hitNucIsProton = pdg::IsProton( nucl_pdg_ini ); + + + // This part of the code is strictly in accordance with the original Nieves' paper + double Mi = target.Mass(); + int Zf = (hitNucIsProton) ? Z - 1 : Z + 1; + PDGLibrary * pdglib = PDGLibrary::Instance(); + TParticlePDG * nucl_f = pdglib->Find( pdg::IonPdgCode(A, Zf) ); + double Q = 0; + if(nucl_f) Q = nucl_f -> Mass() - Mi; + double Q_LFG = EFf - EFi; + q0Tilde -= (Q - Q_LFG); + + // Shift the q0Tilde if required: + if( fQvalueShifter ) q0Tilde += q0Tilde * fQvalueShifter->Shift(*interaction) ; + + // If binding energy effects pull us into an unphysical region, return + // zero + if ( q0Tilde <= 0 ) return 0; + + // Note that we're working in the lab frame (i.e., the rest frame + // of the target nucleus). We can therefore use Nieves' explicit + // form of the Amunu tensor if we rotate the 3-momenta so that + // qTilde is in the +z direction + TVector3 neutrinoMom3 = neutrinoMom.Vect(); + TVector3 leptonMom3 = leptonMom.Vect(); + + // If Coulomb corrections are being used, adjust the lepton 3-momentum used + // to get q3VecTilde so that its magnitude matches the local + // Coulomb-corrected value calculated earlier. Note that, although the + // treatment of Coulomb corrections by Nieves et al. doesn't change the + // direction of the lepton 3-momentum, it *does* change the direction of the + // 3-momentum transfer, and so the correction should be applied *before* + // rotating coordinates into a frame where q3VecTilde lies along the positive + // z axis. + TVector3 leptonMomCoulomb3 = !fCoulomb ? leptonMom3: PlLocal*leptonMom3*(1/leptonMom3.Mag()); + TVector3 q3VecTilde = neutrinoMom3 - leptonMomCoulomb3; + // Calculate qTilde + TLorentzVector qTildeP4(0., 0., q3VecTilde.Mag(), q0Tilde); + double Q2tilde = -qTildeP4.Mag2(); + + // Check that Q2tilde > 0 (accounting for rounding errors) + if (Q2tilde < 0) return 0; + + // Store Q2tilde in the kinematic variable representing Q2. + // This will ensure that the form factors are calculated correctly + // using the de Forest prescription (Q2tilde instead of Q2). + interaction->KinePtr()->SetQ2(Q2tilde); + // Calculate form factors + fFormFactors.Calculate( interaction ); + + // Get the QEL form factors (were calculated before this method was called) + double F1V = 0.5*fFormFactors.F1V(); + double xiF2V = 0.5*fFormFactors.xiF2V(); + double FA = fFormFactors.FA(); + // According to Nieves' paper, Fp = 2.0*M*FA/(kPionMass2-q2), but Llewelyn- + // Smith uses Fp = 2.0*M^2*FA/(kPionMass2-q2), so I divide by M + // This gives units of GeV^-1 + double Fp = fFormFactors.Fp()/M; + + double CN(1), CT(1), CL(1); + if (fRPA) + { + CNCTCLimUcalc(qTildeP4, M, r, tgtIsNucleus, A, Z, N, + CN, CT, CL, interaction->TestBit(kIAssumeFreeNucleon) ); + } + + // Calculate auxiliary parameters + // Off shell mass of initial nucleon + double M2 = M*M; + double FA2 = FA*FA; + double F1V2 = F1V*F1V; + double xiF2V2 = xiF2V*xiF2V; + double q0 = qTildeP4.E(); + double dq = qTildeP4.Pz(); + double dq2 = dq*dq; + double q02 = q0*q0; + double q2 = q02 - dq2; + + double factor = PlLocal*ElLocal*r*r/dq; + + double c = q0/dq; + double d = q2/2/M/dq; + + double Elow = TMath::Max( EFf - q0, M*(c*d + TMath::Sqrt(1- c*c + d*d))/(1 - c*c) ); + double Eup = EFi; + + if (Elow >= Eup) return 0; + + double Elow2 = Elow*Elow; + double Elow3 = Elow*Elow2; + double Eup2 = Eup*Eup; + double Eup3 = Eup*Eup2; + + double b0 = factor*(Eup - Elow); + double b1 = factor/M/2*(Eup2 - Elow2); + double b2 = factor/M2/3*(Eup3 - Elow3); + + double a1 = b0; + double a2 = b2 - b0; + double a3 = c*c*b2 + 2*c*d*b1 + d*d*b0; + double a4 = b2; + double a5 = c*b2 + d*b1; + double a6 = c*b1 + d*b0; + double a7 = b1; + + double Ep = M*a7; + double Ep2 = M2*a4; + double px2 = 0.5*M2*(a2 - a3); //py2=px2 + double pz = M*a6; + double Eppz = M2*a5; + double pz2 = M2*a3; + + double aux = 2*CL*Fp*(Fp*q2 + 4*FA*M); + double W00 = 32*F1V2*(Ep2*CN + Ep*q0 + a1*q2/4)+ + 8*q2*xiF2V2*(a1*(1 - q02*(1/q2 + 1/M2/4)) - Ep2/M2 - Ep*q0/M2) + + 8*FA2*(Ep2 + Ep*q0 + a1*(q2/4 - M2)) - a1*(aux*q02 + 16*F1V*xiF2V*(q02 - q2)*CN); + double Wxx = 32*F1V2*(px2 - a1*q2/4) - 8*q2*xiF2V2*(a1*CT + px2/M2) + + 8*FA2*(px2 + a1*(CT*M2 - q2/4)) - 16*a1*F1V*xiF2V*CT*q2; + double Wzz = 32*F1V2*(pz2 + pz*dq - a1*q2/4)- + 8*q2*xiF2V2*(a1 + pz2/M2 + pz*dq/M2 + a1*dq2*(1/q2 + 1/M2/4))+ + 8*FA2*(pz2 + pz*dq + a1*(CL*M2 - q2/4)) - a1*(aux*dq2 + 16*F1V*xiF2V*q02); + double ReW0z = 16*F1V2*((2*Eppz + Ep*dq)*CN + pz*q0) + -4*q2*xiF2V2*(2*Eppz/M2 + (Ep*dq + pz*q0)/M2 + a1*dq*q0*(2/q2 + 1/M2/2))+ + 4*FA2*((2*Eppz + Ep*dq)*CL + pz*q0) - a1*dq*q0*(aux + 16*F1V*xiF2V); + double ImWxy = -16*FA*(xiF2V+F1V)*(pz*q0 - Ep*dq*CT); + + + TLorentzVector q4 = neutrinoMom - leptonMom; + double v = q4.E(); + double v2 = v*v; + double qv2 = q4.Vect().Mag2(); + double qv = TMath::Sqrt(qv2); + + // Naumov definitions + double W1 = Wxx; + double W2 = W00 + Wxx + v2/qv2*(Wzz - Wxx) - 2*v/qv*ReW0z; + double W3 = -2*ImWxy/qv; + double W4 = (Wzz - Wxx)/qv2; + double W5 = 2*(ReW0z - v/qv*(Wzz - Wxx))/qv; + + double Ev = neutrinoMom.E(); + double El = leptonMom.E(); + double Pl = leptonMom.P(); + double cost = TMath::Cos( neutrinoMom.Angle(leptonMom.Vect()) ); + double sint = TMath::Sqrt(1 - cost*cost); + + double auxm = (El - Pl*cost)/2; + double auxp = (El + Pl*cost)/2; + double aux1m = (Pl - El*cost)/2; + double aux1p = (Pl + El*cost)/2; + double aux1 = ml2/2; + double aux2 = (Ev + El); + + if (mod == 2) return sign*(2*aux1m*(W1 - aux1*W4) + aux1p*W2 - sign*(aux2*aux1m + aux1*cost)*W3 - aux1*cost*W5); //PL*R + if (mod == 3) return sign*ml*sint*(2*W1 - W2 -sign*Ev*W3 - ml2*W4 + El*W5)/2; //PP*R + double R = 2*auxm*(W1 + aux1*W4) + auxp*W2 - sign*(aux2*auxm - aux1)*W3 - aux1*W5; + if (mod == 1) return R; + + // factor kPi2 comes from above definition of b; coloumb corrections factor also is taken into account + double extrafactor = kGF2*fCos8c2*TMath::Sq(kMw2/(kMw2 - q4.Mag2() ) )/El/4/kPi2; + + // Calculate xsec + double xsec = extrafactor*R; + xsec *= fXSecCCScale ; + + return xsec; + +} +//___________________________________________________________________________________ +double NievesQELCCPXSec::d2sigma_dQ2dv(const Interaction* interaction) const +{ + const InitialState & init_state = interaction -> InitState(); + const Target & target = init_state.Tgt(); + + double Rmax = MaximalRadius(&target); + if (Rmax <= 0) return 0; + + ROOT::Math::IntegrationOneDim::Type ig_type = utils::gsl::Integration1DimTypeFromString( f1DimIntgType ); + ROOT::Math::IBaseFunctionOneDim * func = new utils::gsl::wrap::NievesQELSmithMonizIntegrand(this, interaction, 0); + ROOT::Math::Integrator ig(*func, ig_type, 0, f1DimRelTol, f1DimMaxEval); + double xsec = ig.Integral(0, Rmax); + delete func; + return xsec; +} +//___________________________________________________________________________________ +void NievesQELCCPXSec::ModelNuclParams(const Interaction* interaction, double r, double & kFi, double & kFf) const +{ + const InitialState & init_state = interaction -> InitState(); + const Target & target = init_state.Tgt(); + int nucl_pdg_ini = target.HitNucPdg(); + kFi = fPauliBlocker->GetFermiMomentum(target, nucl_pdg_ini, r); + kFf = fPauliBlocker->GetFermiMomentum(target, interaction->RecoilNucleonPdg(), r); +} diff --git a/src/Physics/QuasiElastic/XSection/NievesQELCCPXSec.h b/src/Physics/QuasiElastic/XSection/NievesQELCCPXSec.h index b0707e0db..610778897 100644 --- a/src/Physics/QuasiElastic/XSection/NievesQELCCPXSec.h +++ b/src/Physics/QuasiElastic/XSection/NievesQELCCPXSec.h @@ -7,13 +7,19 @@ with RPA corrections Is a concrete implementation of the XSecAlgorithmI interface. \n -\ref Physical Review C 70, 055503 (2004) +\ref 1. PRC70(2004)055503 + 2. EPJA25(2005)299-318 + 3. Phys.Rept.188(1990)79 + 4. CJP46(1996)0673-0720 \author Joe Johnston, University of Pittsburgh Steven Dytman, University of Pittsburgh + Igor Kakorin, JINR \created April 2016 +\updated March 2025 + \cpright Copyright (c) 2003-2023, The GENIE Collaboration For the full text of the license visit http://copyright.genie-mc.org */ @@ -54,9 +60,12 @@ class NievesQELCCPXSec : public XSecAlgorithmI { // XSecAlgorithmI interface implementation double XSec (const Interaction * i, KinePhaseSpace_t k) const; + double d2sigma_dQ2dv (const Interaction * i) const; double Integral (const Interaction * i) const; bool ValidProcess (const Interaction * i) const; - + const TVector3 & FinalLeptonPolarization (const Interaction* i) const; + double IntegratedOverMomentum (const Interaction* i, double r, int mod) const; + void ModelNuclParams(const Interaction* interaction, double r, double & kFi, double & kFf) const; // Override the Algorithm::Configure methods to load configuration // data to private data members void Configure (const Registry & config); @@ -69,6 +78,10 @@ class NievesQELCCPXSec : public XSecAlgorithmI { const QELFormFactorsModelI * fFormFactorsModel; ///< const XSecIntegratorI * fXSecIntegrator; ///< double fCos8c2; ///< cos^2(cabibbo angle) + + string f1DimIntgType; ///< Type of 1D integrator from NievesQELCCXSec config + double f1DimRelTol; ///< Relative tolerance for 1D integrator from NievesQELCCXSec config + unsigned int f1DimMaxEval; ///< Number of max evaluations for 1D integrator from NievesQELCCXSec config double fXSecCCScale; ///< external xsec scaling factor for CC double fXSecNCScale; ///< external xsec scaling factor for NC @@ -78,7 +91,7 @@ class NievesQELCCPXSec : public XSecAlgorithmI { double fhbarc; ///< hbar*c in GeV*fm // mutable for testing purposes only! - mutable bool fRPA; ///< use RPA corrections + mutable bool fRPA; ///< use RPA corrections bool fCoulomb; ///< use Coulomb corrections const NuclearModelI* fNuclModel; ///< Nuclear Model for integration @@ -87,6 +100,7 @@ class NievesQELCCPXSec : public XSecAlgorithmI { bool fLFG; const FermiMomentumTable * fKFTable; string fKFTableName; + string fLindhardFunction; /// Enum specifying the method to use when calculating the binding energy of /// the initial hit nucleon during spline generation @@ -118,46 +132,39 @@ class NievesQELCCPXSec : public XSecAlgorithmI { // Calculates values of CN, CT, CL, and imU, and stores them in the provided // variables. If target is not a nucleus, then CN, CN, and CL are all 1.0. // r must be in units of fm. - void CNCTCLimUcalc(TLorentzVector qTildeP4, double M, double r, - bool is_neutrino, bool tgtIsNucleus, int tgt_pdgc, int A, int Z, int N, - bool hitNucIsProton, double & CN, double & CT, double & CL, double & imU, - double & t0, double & r00, bool assumeFreeNucleon) const; - - //Equations to calculate the relativistic Lindhard function for Amunu - std::complex relLindhardIm(double q0gev, double dqgev, - double kFngev, double kFpgev, - double M, bool isNeutrino, - double & t0, double & r00) const; - std::complex relLindhard(double q0gev, double dqgev, - double kFgev, double M, - bool isNeutrino, - std::complex relLindIm) const; - std::complex ruLinRelX(double q0, double qm, - double kf, double m) const; - std::complex deltaLindhard(double q0gev, double dqgev, - double rho, double kFgev) const; - + void CNCTCLimUcalc(TLorentzVector qTildeP4, double M, double r, + bool tgtIsNucleus, int A, int Z, int N, double & CN, double & CT, + double & CL, bool assumeFreeNucleon) const; + + //Relativistic Lindhard function as is in Fortran code provided by j.Nieves + // Ref.1, Eq.B2 + double relLindhardIm(double q0, double dq, double kFn, double kFp, double M, bool isNeutrino) const; + // Ref.2, Eq.61 + double ruLinRelX(double q0, double qm, double kf, double m) const; + + std::complex LindhardNuclear(double q0, double dq, double kF, double M) const; + std::complex LindhardDelta (double q0, double dq, double kF, double M, double rho) const; + // Potential for coulomb correction double vcr(const Target * target, double r) const; - - //input must be length 4. Returns 1 if input is an even permutation of 0123, - //-1 if input is an odd permutation of 0123, and 0 if any two elements - //are equal - int leviCivita(int input[]) const; + + double MaximalRadius(const Target * target) const; + + inline int g(int a, int b) const///< metric g^{ab}=g_{ab}=diag(1,-1,-1,1) + { + return (a==b)*(2*(a==0) - 1); + } + inline int e(int a, int b, int c, int d) const ///< Levi-Chevita symbol, where e_{0123}=+1 + { + return (b - a)*(c - a)*(d - a)*(c - b)*(d - b)*(d - c)/12; + } double LmunuAnumu(const TLorentzVector neutrinoMom, const TLorentzVector inNucleonMom, const TLorentzVector leptonMom, const TLorentzVector outNucleonMom, double M, bool is_neutrino, const Target& target, bool assumeFreeNucleon) const; + - // NOTE: THE FOLLOWING CODE IS FOR TESTING PURPOSES ONLY - // Used to print tensor elements and various inputs for comparison to Nieves' - // fortran code - mutable bool fCompareNievesTensors; ///< print tensors - mutable TString fTensorsOutFile; ///< file to print tensors to - mutable double fVc,fCoulombFactor; - void CompareNievesTensors(const Interaction* i) const; - // END TESTING CODE }; } // genie namespace @@ -194,6 +201,21 @@ namespace genie { double fA; double fZ; }; + + class NievesQELSmithMonizIntegrand : public ROOT::Math::IBaseFunctionOneDim + { + public: + NievesQELSmithMonizIntegrand(const NievesQELCCPXSec* alg_, const Interaction* interaction_, int mod_); + ~NievesQELSmithMonizIntegrand(); + // ROOT::Math::IBaseFunctionOneDim interface + unsigned int NDim (void) const; + double DoEval (double rin) const; + ROOT::Math::IBaseFunctionOneDim * Clone (void) const; + private: + const NievesQELCCPXSec* alg; + const Interaction* interaction; + int mod; + }; } // wrap namespace } // gsl namespace diff --git a/src/Physics/QuasiElastic/XSection/NievesQELCCXSec.cxx b/src/Physics/QuasiElastic/XSection/NievesQELCCXSec.cxx new file mode 100644 index 000000000..e5c14a85c --- /dev/null +++ b/src/Physics/QuasiElastic/XSection/NievesQELCCXSec.cxx @@ -0,0 +1,167 @@ +//____________________________________________________________________________ +/* + Copyright (c) 2003-2025, The GENIE Collaboration + For the full text of the license visit http://copyright.genie-mc.org + + Igor Kakorin JINR +*/ +//____________________________________________________________________________ + +#include "Framework/Algorithm/AlgConfigPool.h" +#include "Framework/Algorithm/AlgFactory.h" +#include "Framework/Conventions/GBuild.h" +#include "Framework/Conventions/Constants.h" +#include "Framework/Conventions/Controls.h" +#include "Framework/Conventions/Units.h" +#include "Framework/Conventions/KineVar.h" +#include "Framework/Conventions/RefFrame.h" +#include "Physics/QuasiElastic/XSection/NievesQELCCXSec.h" + +#include "Physics/XSectionIntegration/GSLXSecFunc.h" +#include "Framework/Messenger/Messenger.h" +#include "Framework/Numerical/RandomGen.h" +#include "Framework/ParticleData/PDGUtils.h" +#include "Framework/Utils/KineUtils.h" +#include "Framework/Utils/Range1.h" +#include "Framework/Numerical/GSLUtils.h" + + +using namespace genie; +using namespace genie::constants; +using namespace genie::utils::gsl; + +//____________________________________________________________________________ +NievesQELCCXSec::NievesQELCCXSec() : XSecIntegratorI("genie::NievesQELCCXSec") +{ + +} +//____________________________________________________________________________ +NievesQELCCXSec::NievesQELCCXSec(std::string config) : XSecIntegratorI("genie::NievesQELCCXSec", config) +{ + +} +//____________________________________________________________________________ +double NievesQELCCXSec::Integrate(const XSecAlgorithmI* model, const Interaction* in) const +{ + if(! model->ValidProcess(in)) return 0; + + Target * tgt = in->InitStatePtr()->TgtPtr(); + double Rmax = tgt->HitNucPosition(); + utils::gsl::d3XSec_dElepdCosThetalepdR_E func(model, in, Rmax ) ; + ROOT::Math::IntegrationMultiDim::Type ig_type = utils::gsl::IntegrationNDimTypeFromString(fGSLIntgType); + ROOT::Math::IntegratorMultiDim ig(ig_type, 0, fGSLRelTol, fGSLMaxEval); + if (ig_type == ROOT::Math::IntegrationMultiDim::kADAPTIVE) + { + ROOT::Math::AdaptiveIntegratorMultiDim * cast = dynamic_cast( ig.GetIntegrator() ); + assert(cast); + cast->SetMinPts(fGSLMinEval); + } + ig.SetFunction(func); + double kine_min[3] = { 0., 0., 0.}; + double kine_max[3] = { 1., 1., 1.}; + double xsec = ig.Integral(kine_min, kine_max)*(1E-38 * units::cm2); + return xsec; +} +//____________________________________________________________________________ +void NievesQELCCXSec::Configure(const Registry & config) +{ + Algorithm::Configure(config); + this->LoadConfig(); +} +//____________________________________________________________________________ +void NievesQELCCXSec::Configure(string config) +{ + Algorithm::Configure(config); + this->LoadConfig(); +} +//____________________________________________________________________________ +void NievesQELCCXSec::LoadConfig(void) +{ + // Get GSL integration type & relative tolerance + GetParamDef( "gsl-integration-type", fGSLIntgType, std::string("adaptive") ); + GetParamDef( "gsl-relative-tolerance", fGSLRelTol, 1e-4 ); + int max; + GetParamDef( "gsl-max-eval", max, 1000000 ); + fGSLMaxEval = static_cast( max ); + int min; + GetParamDef( "gsl-min-eval", min, 7500 ) ; + fGSLMinEval = static_cast( min ); + + GetParamDef( "gsl-1dim-integration-type", fGSL1DimIntgType, std::string("adaptive") ); + GetParamDef( "gsl-1dim-relative-tolerance", fGSL1DimRelTol, 1e-4 ); + GetParamDef( "gsl-1dim-max-eval", max, 1000000 ); + fGSL1DimMaxEval = static_cast( max ); +} +//____________________________________________________________________________ +// GSL wrappers +//____________________________________________________________________________ +genie::utils::gsl::d3XSec_dElepdCosThetalepdR_E::d3XSec_dElepdCosThetalepdR_E( + const XSecAlgorithmI * m, const Interaction * interaction, double Rmax) : + ROOT::Math::IBaseFunctionMultiDim(), + fModel(m), + fXsec_model( dynamic_cast(fModel) ), + fInteraction(interaction), + fRmax(Rmax) +{ + // Get kinematical parameters + AlgFactory * algf = AlgFactory::Instance(); + sm_utils = const_cast(dynamic_cast(algf->GetAlgorithm("genie::SmithMonizUtils","Default"))); + const InitialState & init_state = interaction -> InitState(); + fEnu = init_state.ProbeE(kRfHitNucRest); + fml = interaction->FSPrimLepton()->Mass(); + fml2 = fml*fml; + sm_utils->SetInteraction(interaction); + fKinematics = fInteraction->KinePtr(); +} +genie::utils::gsl::d3XSec_dElepdCosThetalepdR_E::~d3XSec_dElepdCosThetalepdR_E() +{ + +} +unsigned int genie::utils::gsl::d3XSec_dElepdCosThetalepdR_E::NDim(void) const +{ + return 3; +} +double genie::utils::gsl::d3XSec_dElepdCosThetalepdR_E::DoEval(const double * xin) const +{ + // inputs: + // normalized Q2 from 0 to 1 + // normalized v from 0 to 1 + // normalized R from 0 to 1 + // outputs: + // differential cross section [10^-38 cm^2] + // + double R = fRmax*xin[2]; + double kFi, kFf; + fXsec_model->ModelNuclParams(fInteraction, R, kFi, kFf); + sm_utils->SetBindingEnergy(0); + sm_utils->SetInitialFermiMomentum(kFi); + sm_utils->SetFinalFermiMomentum(kFf); + + if (fEnu < sm_utils->E_nu_thr_SM()) return 0; + + Range1D_t rQ2 = sm_utils->Q2QES_SM_lim(); + double Q2 = (rQ2.max - rQ2.min)*xin[0] + rQ2.min; + Range1D_t rv = sm_utils->vQES_SM_lim(Q2); + double v = (rv.max - rv.min)*xin[1] + rv.min; + + double El = fEnu - v; + if (El < fml) return 0.0; + double Pl = TMath::Sqrt(El*El - fml2); + double cosTl = (El - (Q2 + fml2)/2/fEnu)/Pl; + if (cosTl < -1.0 || cosTl > 1.0 ) return 0.0; + double sinTl = TMath::Sqrt(1 - cosTl*cosTl); + fKinematics->SetFSLeptonP4(Pl*sinTl, 0, Pl*cosTl, El); + + double xsec = fXsec_model->IntegratedOverMomentum(fInteraction, R, 0); + + // Jacobian for transformation d/dEldcosT->d/dQ2dv + double J = (rQ2.max - rQ2.min)*(rv.max - rv.min)*fRmax/2/fEnu/Pl; + + return xsec*J*units::fm3/(1E-38 * units::cm2); +} +ROOT::Math::IBaseFunctionMultiDim * +genie::utils::gsl::d3XSec_dElepdCosThetalepdR_E::Clone() const +{ + return + new genie::utils::gsl::d3XSec_dElepdCosThetalepdR_E(fModel,fInteraction,fRmax); +} diff --git a/src/Physics/QuasiElastic/XSection/NievesQELCCXSec.h b/src/Physics/QuasiElastic/XSection/NievesQELCCXSec.h new file mode 100644 index 000000000..4a5ce6bee --- /dev/null +++ b/src/Physics/QuasiElastic/XSection/NievesQELCCXSec.h @@ -0,0 +1,114 @@ +//____________________________________________________________________________ +/*! + +\class genie::NievesQELCCXSec + +\brief Computes the Quasi Elastic (QEL) total cross section. \n + Is a concrete implementation of the XSecIntegratorI interface. \n + +\author Igor Kaorin JINR + +\created March 23, 2025 + +\cpright Copyright (c) 2003-2025, The GENIE Collaboration + For the full text of the license visit http://copyright.genie-mc.org +*/ +//____________________________________________________________________________ + +#ifndef _NIEVES_QEL_XSEC_H_ +#define _NIEVES_QEL_XSEC_H_ + +#include "Framework/EventGen/XSecAlgorithmI.h" +#include "Physics/XSectionIntegration/XSecIntegratorI.h" +#include "Physics/QuasiElastic/XSection/QELUtils.h" +#include "Physics/QuasiElastic/XSection/SmithMonizUtils.h" +#include "Physics/QuasiElastic/XSection/NievesQELCCPXSec.h" + +#include +#include +#include + +namespace genie { +class NievesQELCCXSec : public XSecIntegratorI { + +public: + + NievesQELCCXSec(void); + NievesQELCCXSec(std::string config); + + /// XSecIntegratorI interface implementation + double Integrate(const XSecAlgorithmI* model, const Interaction* i) const; + + std::string Get1DimIntgType() const + { + return fGSL1DimIntgType; + } + + unsigned int Get1DimMaxEval() const + { + return fGSL1DimMaxEval; + } + + double Get1DimRelTol() const + { + return fGSL1DimRelTol; + } + + /// Overload the Algorithm::Configure() methods to load private data + /// members from configuration options + void Configure(const Registry& config); + void Configure(std::string config); + +private: + + void LoadConfig (void); + + // XML configuration parameters + std::string fGSLIntgType; + double fGSLRelTol; + unsigned int fGSLMaxEval; + + std::string fGSL1DimIntgType; + double fGSL1DimRelTol; + unsigned int fGSL1DimMaxEval; + +}; + +class XSecAlgorithmI; +class Interaction; + +namespace utils { +namespace gsl { +//..................................................................................... +// +// A 3-D cross section function: d3xsec/dEldCosThetadR = f(El,CosTheta, R)|(fixed E) +// +class d3XSec_dElepdCosThetalepdR_E: public ROOT::Math::IBaseFunctionMultiDim +{ +public: + d3XSec_dElepdCosThetalepdR_E(const XSecAlgorithmI * m, const Interaction * i, double Rmax); + ~d3XSec_dElepdCosThetalepdR_E(); + + // ROOT::Math::IBaseFunctionMultiDim interface + unsigned int NDim (void) const; + double DoEval (const double * xin) const; + ROOT::Math::IBaseFunctionMultiDim * Clone (void) const; + +private: + const XSecAlgorithmI * fModel; + const NievesQELCCPXSec * fXsec_model; + const Interaction * fInteraction; + mutable SmithMonizUtils * sm_utils; + Kinematics * fKinematics; + double fRmax; + double fEnu; + double fml; + double fml2; +}; + +} // gsl namespace +} // utils namespace + +} // genie namespace + +#endif // _NIEVES_QEL_XSEC_H_ diff --git a/src/Physics/QuasiElastic/XSection/RosenbluthPXSec.cxx b/src/Physics/QuasiElastic/XSection/RosenbluthPXSec.cxx index f18d6d3c5..b2f73371a 100644 --- a/src/Physics/QuasiElastic/XSection/RosenbluthPXSec.cxx +++ b/src/Physics/QuasiElastic/XSection/RosenbluthPXSec.cxx @@ -57,7 +57,6 @@ double RosenbluthPXSec::XSec( const InitialState & init_state = interaction -> InitState(); const Kinematics & kinematics = interaction -> Kine(); const Target & target = init_state.Tgt(); - const ProcessInfo & proc_info = interaction->ProcInfo(); int nucpdgc = target.HitNucPdg(); double E = init_state.ProbeE(kRfHitNucRest); @@ -203,3 +202,9 @@ void RosenbluthPXSec::LoadConfig(void) assert(fXSecIntegrator); } //____________________________________________________________________________ +const TVector3 & RosenbluthPXSec::FinalLeptonPolarization (const Interaction* interaction) const +{ + LOG("Rosenbluth", pWARN) << "For EM processes doesn't work yet. Set it to zero."; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; +} diff --git a/src/Physics/QuasiElastic/XSection/RosenbluthPXSec.h b/src/Physics/QuasiElastic/XSection/RosenbluthPXSec.h index b3ee0a780..b109959d1 100644 --- a/src/Physics/QuasiElastic/XSection/RosenbluthPXSec.h +++ b/src/Physics/QuasiElastic/XSection/RosenbluthPXSec.h @@ -40,6 +40,7 @@ class RosenbluthPXSec : public XSecAlgorithmI { double XSec (const Interaction * i, KinePhaseSpace_t k) const; double Integral (const Interaction * i) const; bool ValidProcess (const Interaction * i) const; + const TVector3 & FinalLeptonPolarization (const Interaction* i) const; // override the Algorithm::Configure methods to load configuration // data to private data members diff --git a/src/Physics/QuasiElastic/XSection/SmithMonizQELCCPXSec.cxx b/src/Physics/QuasiElastic/XSection/SmithMonizQELCCPXSec.cxx index e30dd5216..4cf9ca85e 100644 --- a/src/Physics/QuasiElastic/XSection/SmithMonizQELCCPXSec.cxx +++ b/src/Physics/QuasiElastic/XSection/SmithMonizQELCCPXSec.cxx @@ -45,28 +45,28 @@ #include "Framework/Utils/KineUtils.h" #include "Framework/Utils/Range1.h" #include "Physics/QuasiElastic/XSection/SmithMonizUtils.h" +#include "Physics/Common/PrimaryLeptonUtils.h" using namespace genie; using namespace genie::constants; using namespace genie::utils; -using std::ostringstream; //____________________________________________________________________________ SmithMonizQELCCPXSec::SmithMonizQELCCPXSec() : XSecAlgorithmI("genie::SmithMonizQELCCPXSec") { - + InitGaussIntArrays(); } //____________________________________________________________________________ SmithMonizQELCCPXSec::SmithMonizQELCCPXSec(string config) : XSecAlgorithmI("genie::SmithMonizQELCCPXSec", config) { - + InitGaussIntArrays(); } //____________________________________________________________________________ SmithMonizQELCCPXSec::~SmithMonizQELCCPXSec() { - + InitGaussIntArrays(); } //____________________________________________________________________________ double SmithMonizQELCCPXSec::XSec( @@ -95,11 +95,42 @@ double SmithMonizQELCCPXSec::XSec( if(kpsdim == 2) { + if(!interaction->TestBit(kISkipProcessChk)) + { + sm_utils->SetInteraction(interaction); + const InitialState & init_state = interaction->InitState(); + double E_nu = init_state.ProbeE(kRfLab); + if ( E_nu < sm_utils->E_nu_thr_SM() ) return 0.; + const Kinematics & kinematics = interaction -> Kine(); + double Q2 = kinematics.GetKV(kKVQ2); + Range1D_t rQ2 = sm_utils->Q2QES_SM_lim(); + if (Q2 < rQ2.min || Q2 > rQ2.max) return 0.; + Range1D_t rv = sm_utils->vQES_SM_lim(Q2); + double v = kinematics.GetKV(kKVv); + if (v < rv.min || v > rv.max) return 0.; + } xsec = this->d2sQES_dQ2dv_SM(interaction); } if(kpsdim == 3) { + if(!interaction->TestBit(kISkipProcessChk)) + { + sm_utils->SetInteraction(interaction); + const InitialState & init_state = interaction->InitState(); + double E_nu = init_state.ProbeE(kRfLab); + if ( E_nu < sm_utils->E_nu_thr_SM() ) return 0.; + const Kinematics & kinematics = interaction -> Kine(); + double Q2 = kinematics.GetKV(kKVQ2); + Range1D_t rQ2 = sm_utils->Q2QES_SM_lim(); + if (Q2 < rQ2.min || Q2 > rQ2.max) return 0.; + Range1D_t rv = sm_utils->vQES_SM_lim(Q2); + double v = kinematics.GetKV(kKVv); + if (v < rv.min || v > rv.max) return 0.; + Range1D_t rkF = sm_utils->kFQES_SM_lim(Q2, v); + double kF = kinematics.GetKV(kKVPn); + if (kF < rkF.min || kF > rkF.max) return 0.; + } xsec = this->d3sQES_dQ2dvdkF_SM(interaction); } @@ -136,6 +167,8 @@ bool SmithMonizQELCCPXSec::ValidProcess(const Interaction * interaction) const const ProcessInfo & proc_info = interaction->ProcInfo(); if(!proc_info.IsQuasiElastic()) return false; + + if ( !init_state.Tgt().IsNucleus() ) return false; int nuc = init_state.Tgt().HitNucPdg(); int nu = init_state.ProbePdg(); @@ -145,7 +178,7 @@ bool SmithMonizQELCCPXSec::ValidProcess(const Interaction * interaction) const bool isnu = pdg::IsNeutrino(nu); bool isnub = pdg::IsAntiNeutrino(nu); - bool prcok = proc_info.IsWeakCC() && ((isP&&isnub) || (isN&&isnu)); + bool prcok = proc_info.IsWeakCC() && ((isP && isnub) || (isN && isnu)); if(!prcok) return false; return true; @@ -174,6 +207,9 @@ void SmithMonizQELCCPXSec::LoadConfig(void) // Cross section scaling factor GetParamDef( "QEL-CC-XSecScale", fXSecScale, 1. ) ; + + // Do precise calculation of lepton polarization + GetParamDef( "PreciseLeptonPol", fIsPreciseLeptonPolarization, false ) ; double Vud; GetParam( "CKM-Vud", Vud ) ; @@ -210,19 +246,12 @@ double SmithMonizQELCCPXSec::d3sQES_dQ2dvdkF_SM(const Interaction * interaction) double v = kinematics.GetKV(kKVv); double kF = kinematics.GetKV(kKVPn); double kkF = kF*kF; - int nucl_pdg_ini = target.HitNucPdg(); - int nucl_pdg_fin = genie::pdg::SwitchProtonNeutron(nucl_pdg_ini); - PDGLibrary * pdglib = PDGLibrary::Instance(); - TParticlePDG * nucl_fin = pdglib->Find( nucl_pdg_fin ); - double E_BIN = sm_utils->GetBindingEnergy(); double m_ini = target.HitNucMass(); double mm_ini = m_ini*m_ini; - double m_fin = nucl_fin -> Mass(); // Mass of final hadron or hadron system (GeV) + double m_fin = interaction->RecoilNucleon()->Mass(); // Mass of final hadron or hadron system (GeV) double mm_fin = m_fin*m_fin; - double m_tar = target.Mass(); // Mass of target nucleus (GeV) - double mm_tar = m_tar*m_tar; // One of the xsec terms changes sign for antineutrinos bool is_neutrino = pdg::IsNeutrino(init_state.ProbePdg()); @@ -238,7 +267,7 @@ double SmithMonizQELCCPXSec::d3sQES_dQ2dvdkF_SM(const Interaction * interaction) return 0.0; } - double pF = TMath::Sqrt(kkF+(2*kF*qv)*cosT_p+qqv); + double pF = TMath::Sqrt(kkF + 2*kF*qv*cosT_p + qqv); double E_lep = E_nu-v; double m_lep = interaction->FSPrimLepton()->Mass(); @@ -247,35 +276,35 @@ double SmithMonizQELCCPXSec::d3sQES_dQ2dvdkF_SM(const Interaction * interaction) { return 0.0; } - double P_lep = TMath::Sqrt(E_lep*E_lep-mm_lep); - double k6 = (Q2+mm_lep)/(2*E_nu); - double cosT_lep= (E_lep-k6)/P_lep; + double P_lep = TMath::Sqrt(E_lep*E_lep - mm_lep); + double k6 = (Q2 + mm_lep)/(2*E_nu); + double cosT_lep= (E_lep - k6)/P_lep; if (cosT_lep < -1.0 || cosT_lep > 1.0 ) return 0.0; - double cosT_k = (v+k6)/qv; + double cosT_k = (v + k6)/qv; if (cosT_k < -1.0 || cosT_k > 1.0 ) return 0.0; - double b2_flux = (E_p-kF*cosT_k*cosT_p)*(E_p-kF*cosT_k*cosT_p); - double c2_flux = kkF*(1-cosT_p*cosT_p)*(1-cosT_k*cosT_k); - - double k1 = fVud2*kNucleonMass2*kPi; - double k2 = mm_lep/(2*mm_tar); + double b2_flux = TMath::Sq(E_p - kF*cosT_k*cosT_p); + double c2_flux = kkF*(1 - cosT_p*cosT_p)*(1 - cosT_k*cosT_k); + + double k1 = fVud2*mm_ini*kPi; + double k2 = mm_lep/2; double k7 = P_lep*cosT_lep; - double P_Fermi = sm_utils->GetFermiMomentum(); - double FV_SM = 4.0*TMath::Pi()/3*TMath::Power(P_Fermi, 3); - double factor = k1*(m_tar*kF/(FV_SM*qv*TMath::Sqrt(b2_flux-c2_flux)))*SmithMonizUtils::rho(P_Fermi, 0.0, kF)*(1-SmithMonizUtils::rho(P_Fermi, 0.01, pF)); + double P_Fermi = sm_utils->GetInitialFermiMomentum(); + double FV_SM = 4.0*kPi/3*TMath::Power(P_Fermi, 3); + double factor = k1*kF/(FV_SM*qv*TMath::Sqrt(b2_flux-c2_flux))*SmithMonizUtils::rho(P_Fermi, 0.0, kF)*(1-SmithMonizUtils::rho(P_Fermi, 0.01, pF)); - double a2 = kkF/kNucleonMass2; + double a2 = kkF/mm_ini; double a3 = a2*cosT_p*cosT_p; - double a6 = kF*cosT_p/kNucleonMass; - double a7 = E_p/kNucleonMass; + double a6 = kF*cosT_p/m_ini; + double a7 = E_p/m_ini; double a4 = a7*a7; double a5 = 2*a7*a6; double k3 = v/qv; double k4 = (3*a3-a2)/qqv; - double k5 = (a7-a6*k3)*m_tar/kNucleonMass; + double k5 = (a7-a6*k3)/m_ini; // Calculate the QEL form factors fFormFactors.Calculate(interaction); @@ -287,21 +316,21 @@ double SmithMonizQELCCPXSec::d3sQES_dQ2dvdkF_SM(const Interaction * interaction) double FF_M = F_M*F_M; double FF_A = F_A*F_A; - double t = Q2/(4*kNucleonMass2); + double t = Q2/(4*mm_ini); double W_1 = FF_A*(1+t)+t*(F_V+F_M)*(F_V+F_M); //Ref.[1], \tilde{T}_1 double W_2 = FF_A+FF_V+t*FF_M; //Ref.[1], \tilde{T}_2 double W_3 =-2*F_A*(F_V+F_M); //Ref.[1], \tilde{T}_8 double W_4 =-0.5*F_V*F_M-F_A*F_P+t*F_P*F_P-0.25*(1-t)*FF_M; //Ref.[1], \tilde{T}_\alpha - double W_5 = FF_V+t*FF_M+FF_A; - + double W_5 = W_2; + double T_1 = 1.0*W_1+(a2-a3)*0.5*W_2; //Ref.[1], W_1 double T_2 = ((a2-a3)*Q2/(2*qqv)+a4-k3*(a5-k3*a3))*W_2; //Ref.[1], W_2 double T_3 = k5*W_3; //Ref.[1], W_8 - double T_4 = mm_tar*(0.5*W_2*k4+1.0*W_4/kNucleonMass2+a6*W_5/(kNucleonMass*qv)); //Ref.[1], W_\alpha - double T_5 = k5*W_5+m_tar*(a5/qv-v*k4)*W_2; + double T_4 = 0.5*W_2*k4+1.0*W_4/mm_ini+a6*W_5/(m_ini*qv); //Ref.[1], W_\alpha + double T_5 = k5*W_5+(a5/qv-v*k4)*W_2; - double xsec = kGF2*factor*((E_lep-k7)*(T_1+k2*T_4)/m_tar+(E_lep+k7)*T_2/(2*m_tar) - +n_NT*T_3*((E_nu+E_lep)*(E_lep-k7)/(2*mm_tar)-k2)-k2*T_5) + double xsec = kGF2*factor*((E_lep-k7)*(T_1+k2*T_4)+(E_lep+k7)*T_2/2 + +n_NT*T_3*((E_nu+E_lep)*(E_lep-k7)/2-k2)-k2*T_5) *(kMw2/(kMw2+Q2))*(kMw2/(kMw2+Q2))/E_nu/kPi; return xsec; @@ -326,68 +355,16 @@ double SmithMonizQELCCPXSec::d2sQES_dQ2dv_SM(const Interaction * interaction) co const Target & target = init_state.Tgt(); - - -// Gaussian quadratures integrate over Fermi momentum - double R[48]= { 0.16276744849602969579e-1,0.48812985136049731112e-1, - 0.81297495464425558994e-1,1.13695850110665920911e-1, - 1.45973714654896941989e-1,1.78096882367618602759e-1, - 2.10031310460567203603e-1,2.41743156163840012328e-1, - 2.73198812591049141487e-1,3.04364944354496353024e-1, - 3.35208522892625422616e-1,3.65696861472313635031e-1, - 3.95797649828908603285e-1,4.25478988407300545365e-1, - 4.54709422167743008636e-1,4.83457973920596359768e-1, - 5.11694177154667673586e-1,5.39388108324357436227e-1, - 5.66510418561397168404e-1,5.93032364777572080684e-1, - 6.18925840125468570386e-1,6.44163403784967106798e-1, - 6.68718310043916153953e-1,6.92564536642171561344e-1, - 7.15676812348967626225e-1,7.38030643744400132851e-1, - 7.59602341176647498703e-1,7.80369043867433217604e-1, - 8.00308744139140817229e-1,8.19400310737931675539e-1, - 8.37623511228187121494e-1,8.54959033434601455463e-1, - 8.71388505909296502874e-1,8.86894517402420416057e-1, - 9.01460635315852341319e-1,9.15071423120898074206e-1, - 9.27712456722308690965e-1,9.39370339752755216932e-1, - 9.50032717784437635756e-1,9.59688291448742539300e-1, - 9.68326828463264212174e-1,9.75939174585136466453e-1, - 9.82517263563014677447e-1,9.88054126329623799481e-1, - 9.92543900323762624572e-1,9.95981842987209290650e-1, - 9.98364375863181677724e-1,9.99689503883230766828e-1}; - - double W[48]= { 0.00796792065552012429e-1,0.01853960788946921732e-1, - 0.02910731817934946408e-1,0.03964554338444686674e-1, - 0.05014202742927517693e-1,0.06058545504235961683e-1, - 0.07096470791153865269e-1,0.08126876925698759217e-1, - 0.09148671230783386633e-1,0.10160770535008415758e-1, - 0.11162102099838498591e-1,0.12151604671088319635e-1, - 0.13128229566961572637e-1,0.14090941772314860916e-1, - 0.15038721026994938006e-1,0.15970562902562291381e-1, - 0.16885479864245172450e-1,0.17782502316045260838e-1, - 0.18660679627411467395e-1,0.19519081140145022410e-1, - 0.20356797154333324595e-1,0.21172939892191298988e-1, - 0.21966644438744349195e-1,0.22737069658329374001e-1, - 0.23483399085926219842e-1,0.24204841792364691282e-1, - 0.24900633222483610288e-1,0.25570036005349361499e-1, - 0.26212340735672413913e-1,0.26826866725591762198e-1, - 0.27412962726029242823e-1,0.27970007616848334440e-1, - 0.28497411065085385646e-1,0.28994614150555236543e-1, - 0.29461089958167905970e-1,0.29896344136328385984e-1, - 0.30299915420827593794e-1,0.30671376123669149014e-1, - 0.31010332586313837423e-1,0.31316425596861355813e-1, - 0.31589330770727168558e-1,0.31828758894411006535e-1, - 0.32034456231992663218e-1,0.32206204794030250669e-1, - 0.32343822568575928429e-1,0.32447163714064269364e-1, - 0.32516118713868835987e-1,0.32550614492363166242e-1}; - + int d = fR.size(); double Sum = 0; - for(int i = 0;i<48;i++) + for(int i = 0; i < d ;i++) { - double kF = 0.5*(-R[i]*(rkF.max-rkF.min)+rkF.min+rkF.max); + double kF = 0.5*(-fR[i]*(rkF.max-rkF.min)+rkF.min+rkF.max); kinematics->SetKV(kKVPn, kF); - Sum+=d3sQES_dQ2dvdkF_SM(interaction)*W[47-i]; - kF = 0.5*(R[i]*(rkF.max-rkF.min)+rkF.min+rkF.max); + Sum+=d3sQES_dQ2dvdkF_SM(interaction)*fW[d - 1 - i]; + kF = 0.5*(fR[i]*(rkF.max-rkF.min)+rkF.min+rkF.max); kinematics->SetKV(kKVPn, kF); - Sum+=d3sQES_dQ2dvdkF_SM(interaction)*W[47-i]; + Sum+=d3sQES_dQ2dvdkF_SM(interaction)*fW[d - 1 - i]; } double xsec = 0.5*Sum*(rkF.max-rkF.min); @@ -482,7 +459,6 @@ double SmithMonizQELCCPXSec::dsQES_dQ2_SM(const Interaction * interaction) const const double mp2 = kProtonMass2; const double mn2 = kNeutronMass2; const double Ee = E + ( (q2 - mn2 + mp2) / 2.0 / mp ); - assert(Ee > 0.0); // must be non-zero and positive rc = 6.0 + (1.5 * TMath::Log(kProtonMass / 2.0 / Ee)); rc += 1.2 * TMath::Power((kElectronMass / Ee), 1.5); rc *= kAem / kPi; @@ -492,3 +468,212 @@ double SmithMonizQELCCPXSec::dsQES_dQ2_SM(const Interaction * interaction) const xsec *= rc; return xsec; } +//____________________________________________________________________________ + void SmithMonizQELCCPXSec::InitGaussIntArrays(void) + { + // Gaussian quadratures integrate over Fermi momentum + fR = { 0.16276744849602969579e-1,0.48812985136049731112e-1, + 0.81297495464425558994e-1,1.13695850110665920911e-1, + 1.45973714654896941989e-1,1.78096882367618602759e-1, + 2.10031310460567203603e-1,2.41743156163840012328e-1, + 2.73198812591049141487e-1,3.04364944354496353024e-1, + 3.35208522892625422616e-1,3.65696861472313635031e-1, + 3.95797649828908603285e-1,4.25478988407300545365e-1, + 4.54709422167743008636e-1,4.83457973920596359768e-1, + 5.11694177154667673586e-1,5.39388108324357436227e-1, + 5.66510418561397168404e-1,5.93032364777572080684e-1, + 6.18925840125468570386e-1,6.44163403784967106798e-1, + 6.68718310043916153953e-1,6.92564536642171561344e-1, + 7.15676812348967626225e-1,7.38030643744400132851e-1, + 7.59602341176647498703e-1,7.80369043867433217604e-1, + 8.00308744139140817229e-1,8.19400310737931675539e-1, + 8.37623511228187121494e-1,8.54959033434601455463e-1, + 8.71388505909296502874e-1,8.86894517402420416057e-1, + 9.01460635315852341319e-1,9.15071423120898074206e-1, + 9.27712456722308690965e-1,9.39370339752755216932e-1, + 9.50032717784437635756e-1,9.59688291448742539300e-1, + 9.68326828463264212174e-1,9.75939174585136466453e-1, + 9.82517263563014677447e-1,9.88054126329623799481e-1, + 9.92543900323762624572e-1,9.95981842987209290650e-1, + 9.98364375863181677724e-1,9.99689503883230766828e-1}; + + fW = { 0.00796792065552012429e-1,0.01853960788946921732e-1, + 0.02910731817934946408e-1,0.03964554338444686674e-1, + 0.05014202742927517693e-1,0.06058545504235961683e-1, + 0.07096470791153865269e-1,0.08126876925698759217e-1, + 0.09148671230783386633e-1,0.10160770535008415758e-1, + 0.11162102099838498591e-1,0.12151604671088319635e-1, + 0.13128229566961572637e-1,0.14090941772314860916e-1, + 0.15038721026994938006e-1,0.15970562902562291381e-1, + 0.16885479864245172450e-1,0.17782502316045260838e-1, + 0.18660679627411467395e-1,0.19519081140145022410e-1, + 0.20356797154333324595e-1,0.21172939892191298988e-1, + 0.21966644438744349195e-1,0.22737069658329374001e-1, + 0.23483399085926219842e-1,0.24204841792364691282e-1, + 0.24900633222483610288e-1,0.25570036005349361499e-1, + 0.26212340735672413913e-1,0.26826866725591762198e-1, + 0.27412962726029242823e-1,0.27970007616848334440e-1, + 0.28497411065085385646e-1,0.28994614150555236543e-1, + 0.29461089958167905970e-1,0.29896344136328385984e-1, + 0.30299915420827593794e-1,0.30671376123669149014e-1, + 0.31010332586313837423e-1,0.31316425596861355813e-1, + 0.31589330770727168558e-1,0.31828758894411006535e-1, + 0.32034456231992663218e-1,0.32206204794030250669e-1, + 0.32343822568575928429e-1,0.32447163714064269364e-1, + 0.32516118713868835987e-1,0.32550614492363166242e-1}; + } +//____________________________________________________________________________ +const TVector3 & SmithMonizQELCCPXSec::FinalLeptonPolarization (const Interaction* interaction) const +{ + if (!fIsPreciseLeptonPolarization) return XSecAlgorithmI::FinalLeptonPolarization(interaction); + // Get kinematics & init-state parameters + const Kinematics & kinematics = interaction -> Kine(); + sm_utils->SetInteraction(interaction); + const InitialState & init_state = interaction -> InitState(); + const Target & target = init_state.Tgt(); + TLorentzVector * tempNeutrino = init_state.GetProbeP4(kRfLab); + TLorentzVector neutrinoMom = *tempNeutrino; + delete tempNeutrino; + const TLorentzVector leptonMom = kinematics.FSLeptonP4(); + double Enu = neutrinoMom.E(); + double El = leptonMom.E(); + double ml2 = leptonMom.M2(); + double Pl = leptonMom.P(); + double v = Enu - El; + TVector3 neutrinoMom3 = neutrinoMom.Vect(); + TVector3 leptonMom3 = leptonMom.Vect(); + double pv = neutrinoMom3.Mag(); + double pl = leptonMom3.Mag(); + double cos_theta = pl*pv != 0 ? neutrinoMom3.Dot(leptonMom3)/pl/pv : 0; + double aux1 = El - Pl*cos_theta; + double Q2 = 2*Enu*aux1 - ml2; + + Range1D_t rkF = sm_utils->kFQES_SM_lim(Q2,v); + + bool is_neutrino = pdg::IsNeutrino(init_state.ProbePdg()); + + //|\vec{q}| + double qqv = v*v + Q2; + double qv = TMath::Sqrt(qqv); + double E_BIN = sm_utils->GetBindingEnergy(); + double m_ini = target.HitNucMass(); + double mm_ini = m_ini*m_ini; + double m_fin = interaction->RecoilNucleon()->Mass(); // Mass of final hadron or hadron system (GeV) + double mm_fin = m_fin*m_fin; + double m_tar = target.Mass(); // Mass of target nucleus (GeV) + double cosT_k = (v + aux1)/qv; + if (cosT_k < -1.0 || cosT_k > 1.0 ) + { + LOG("SmithMoniz", pWARN) << "Can't calculate final lepton polarization. Set it to zero."; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + + double P_Fermi = sm_utils->GetInitialFermiMomentum(); + + int d = fR.size(); + double a1(0), a2(0), a3(0), a4(0), a5(0), a6(0), a7(0); + double Tf(0.01); + if (rkF.max > 0) + { + // nuclear target + for(int i = 0; i < d ;i++) + { + double kF = 0.5*(-fR[i]*(rkF.max - rkF.min) + rkF.min + rkF.max); + double kkF = kF*kF; + double E_p = TMath::Sqrt(mm_ini + kkF) - E_BIN; + double cosT_p = ((v - E_BIN)*(2*E_p + v + E_BIN) - qqv + mm_ini - mm_fin)/(2*kF*qv); //\cos\theta_p + if (cosT_p >= -1.0 && cosT_p <= 1.0 ) + { + double pF = TMath::Sqrt(kkF + 2*kF*qv*cosT_p + qqv); + double b2_flux = TMath::Sq(E_p - kF*cosT_k*cosT_p); + double c2_flux = kkF*(1 - cosT_p*cosT_p)*(1 - cosT_k*cosT_k); + // factor fVud2*mm_ini*kPi*m_tar/qv/FV_SM will be cancelled when dividing by R + double factor = kF/TMath::Sqrt(b2_flux - c2_flux)*SmithMonizUtils::rho(P_Fermi, 0, kF)*(1 - SmithMonizUtils::rho(P_Fermi, Tf, pF))*fW[d - 1 - i]; + a1 += factor; + a2 += factor*kkF/mm_ini; + a3 += factor*kkF*cosT_p*cosT_p/mm_ini; + a4 += factor*E_p*E_p/mm_ini; + a5 += factor*2*E_p*kF*cosT_p/mm_ini; + a6 += factor*kF*cosT_p/m_ini; + a7 += factor*E_p/m_ini; + } + + kF = 0.5*(fR[i]*(rkF.max - rkF.min) + rkF.min + rkF.max); + kkF = kF*kF; + E_p = TMath::Sqrt(mm_ini + kkF)-E_BIN; + cosT_p = ((v - E_BIN)*(2*E_p + v + E_BIN) - qqv + mm_ini - mm_fin)/(2*kF*qv); + if (cosT_p >= -1.0 && cosT_p <= 1.0 ) + { + double pF = TMath::Sqrt(kkF + 2*kF*qv*cosT_p + qqv); + double b2_flux = TMath::Sq(E_p - kF*cosT_k*cosT_p); + double c2_flux = kkF*(1 - cosT_p*cosT_p)*(1 - cosT_k*cosT_k); + double factor = kF/TMath::Sqrt(b2_flux - c2_flux)*SmithMonizUtils::rho(P_Fermi, 0, kF)*(1 - SmithMonizUtils::rho(P_Fermi, Tf, pF))*fW[d - 1 - i]; + a1 += factor; + a2 += factor*kkF/mm_ini; + a3 += factor*kkF*cosT_p*cosT_p/mm_ini; + a4 += factor*E_p*E_p/mm_ini; + a5 += factor*2*E_p*kF*cosT_p/mm_ini; + a6 += factor*kF*cosT_p/m_ini; + a7 += factor*E_p/m_ini; + } + } + a1 *= 0.5*(rkF.max - rkF.min); + a2 *= 0.5*(rkF.max - rkF.min); + a3 *= 0.5*(rkF.max - rkF.min); + a4 *= 0.5*(rkF.max - rkF.min); + a5 *= 0.5*(rkF.max - rkF.min); + a6 *= 0.5*(rkF.max - rkF.min); + a7 *= 0.5*(rkF.max - rkF.min); + } + else + { + // nucleon target + m_tar = m_ini; + a1 = a4 = a7 = 1; + } + double mm_tar = m_tar*m_tar; + double k3 = v/qv; + double k4 = (3*a3 - a2)/qqv; + double k5 = (a7 - a6*k3)*m_tar/m_ini; + + interaction->KinePtr()->SetQ2(Q2); + // Calculate the QEL form factors + fFormFactors.Calculate(interaction); + + double F_V = fFormFactors.F1V(); + double F_M = fFormFactors.xiF2V(); + double F_A = fFormFactors.FA(); + double F_P = fFormFactors.Fp(); + double FF_V = F_V*F_V; + double FF_M = F_M*F_M; + double FF_A = F_A*F_A; + + double t = Q2/(4*mm_ini); + double W1 = FF_A*(1+t)+t*(F_V+F_M)*(F_V+F_M); //Ref.[1], \tilde{T}_1 + double W2 = FF_A+FF_V+t*FF_M; //Ref.[1], \tilde{T}_2 + double W3 =-2*F_A*(F_V+F_M); //Ref.[1], \tilde{T}_8 + double W4 =-0.5*F_V*F_M-F_A*F_P+t*F_P*F_P-0.25*(1-t)*FF_M; //Ref.[1], \tilde{T}_\alpha + double W5 = W2; + + double T1 = a1*W1 + 0.5*(a2 - a3)*W2; //Ref.[1], W_1 + double T2 = ((a2 - a3)*Q2/(2*qqv) + a4 - k3*(a5 - k3*a3))*W2; //Ref.[1], W_2 + double T3 = k5*W3; //Ref.[1], W_8 + double T4 = mm_tar*(0.5*k4*W2 + a1*W4/mm_ini + a6*W5/m_ini/qv); //Ref.[1], W_\alpha + double T5 = m_tar*(a5/qv - v*k4)*W2 + k5*W5; + + CalculatePolarizationVectorInTargetRestFrame( + fFinalLeptonPolarization, + neutrinoMom, + leptonMom, + is_neutrino, + m_tar, T1,T2,T3,T4,T5,0); + + +// std::cout << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"; +// std::cout << fFinalLeptonPolarization.Mag() << "\n"; +// std::cout << "SM@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" << std::endl; + + return fFinalLeptonPolarization; +} +//____________________________________________________________________________ diff --git a/src/Physics/QuasiElastic/XSection/SmithMonizQELCCPXSec.h b/src/Physics/QuasiElastic/XSection/SmithMonizQELCCPXSec.h index 4817edd7c..d9403a38f 100644 --- a/src/Physics/QuasiElastic/XSection/SmithMonizQELCCPXSec.h +++ b/src/Physics/QuasiElastic/XSection/SmithMonizQELCCPXSec.h @@ -39,6 +39,8 @@ #ifndef _SMITH_MONITZ_QELCC_CROSS_SECTION_H_ #define _SMITH_MONITZ_QELCC_CROSS_SECTION_H_ +#include + #include "Framework/EventGen/XSecAlgorithmI.h" #include "Physics/QuasiElastic/XSection/QELFormFactors.h" #include "Physics/QuasiElastic/XSection/SmithMonizUtils.h" @@ -60,6 +62,7 @@ class SmithMonizQELCCPXSec : public XSecAlgorithmI { double XSec (const Interaction * i, KinePhaseSpace_t kps) const; double Integral (const Interaction * i) const; bool ValidProcess (const Interaction * i) const; + const TVector3 & FinalLeptonPolarization (const Interaction* i) const; // Override the Algorithm::Configure methods to load configuration // data to private data members @@ -70,17 +73,20 @@ class SmithMonizQELCCPXSec : public XSecAlgorithmI { mutable SmithMonizUtils * sm_utils; void LoadConfig (void); - double d3sQES_dQ2dvdkF_SM (const Interaction * interaction) const; double dsQES_dQ2_SM(const Interaction * interaction) const; - double d2sQES_dQ2dv_SM(const Interaction * i) const; + double d2sQES_dQ2dv_SM(const Interaction * interaction) const; + double d3sQES_dQ2dvdkF_SM (const Interaction * interaction) const; + void InitGaussIntArrays(void); double fXSecScale; ///< external xsec scaling factor mutable QELFormFactors fFormFactors; const QELFormFactorsModelI * fFormFactorsModel; const XSecIntegratorI * fXSecIntegrator; double fVud2; ///< |Vud|^2(square of magnitude ud-element of CKM-matrix) - - + + std::array fW; + std::array fR; + }; diff --git a/src/Physics/QuasiElastic/XSection/SmithMonizQELCCXSec.cxx b/src/Physics/QuasiElastic/XSection/SmithMonizQELCCXSec.cxx index 63d141bf1..73c360ebb 100644 --- a/src/Physics/QuasiElastic/XSection/SmithMonizQELCCXSec.cxx +++ b/src/Physics/QuasiElastic/XSection/SmithMonizQELCCXSec.cxx @@ -92,7 +92,7 @@ double SmithMonizQELCCXSec::Integrate( { Interaction * interaction = new Interaction(*in); sm_utils->SetInteraction(in); - if (interaction->InitState().ProbeE(kRfLab)E_nu_thr_SM()) + if (interaction->InitState().ProbeE(kRfLab) < sm_utils->E_nu_thr_SM()) { return 0; } @@ -100,18 +100,22 @@ double SmithMonizQELCCXSec::Integrate( interaction->SetBit(kISkipKinematicChk); double xsec = 0; + utils::gsl::d2Xsec_dQ2dv func(model, interaction); + ROOT::Math::IntegrationMultiDim::Type ig_type = utils::gsl::IntegrationNDimTypeFromString(fGSLIntgType2D); + ROOT::Math::IntegratorMultiDim ig(ig_type, 0, fGSLRelTol2D, fGSLMaxEval); + if (ig_type == ROOT::Math::IntegrationMultiDim::kADAPTIVE) + { + ROOT::Math::AdaptiveIntegratorMultiDim * cast = dynamic_cast( ig.GetIntegrator() ); + assert(cast); + cast->SetMinPts(fGSLMinEval); + } + ig.SetFunction(func); - ROOT::Math::IBaseFunctionMultiDim * func = new utils::gsl::d2Xsec_dQ2dv(model, interaction); double kine_min[2] = { 0, 0}; double kine_max[2] = { 1, 1}; - ROOT::Math::IntegrationMultiDim::Type ig_type = utils::gsl::IntegrationNDimTypeFromString(fGSLIntgType2D); - - double abstol = 0; //We mostly care about relative tolerance. - ROOT::Math::IntegratorMultiDim ig(*func, ig_type, abstol, fGSLRelTol2D, fGSLMaxEval); + xsec = ig.Integral(kine_min, kine_max)*(1E-38 * units::cm2); - xsec = ig.Integral(kine_min, kine_max) * (1E-38 * units::cm2); - delete func; delete interaction; return xsec; @@ -143,7 +147,7 @@ void SmithMonizQELCCXSec::LoadConfig(void) // Get GSL integration type & relative tolerance GetParamDef( "gsl-integration-type", fGSLIntgType, string("gauss") ); - GetParamDef( "gsl-relative-tolerance", fGSLRelTol, 1e-3 ); + GetParamDef( "gsl-relative-tolerance", fGSLRelTol, 1e-5 ); int max_size_of_subintervals; GetParamDef( "gsl-max-size-of-subintervals", max_size_of_subintervals, 40000); fGSLMaxSizeOfSubintervals = (unsigned int) max_size_of_subintervals; @@ -152,8 +156,11 @@ void SmithMonizQELCCXSec::LoadConfig(void) fGSLRule = (unsigned int) rule; if (fGSLRule>6) fGSLRule=3; GetParamDef( "gsl-integration-type-2D", fGSLIntgType2D, string("adaptive") ); - GetParamDef( "gsl-relative-tolerance-2D", fGSLRelTol2D, 1e-7); - GetParamDef( "gsl-max-eval", fGSLMaxEval, 1000000000); + GetParamDef( "gsl-relative-tolerance-2D", fGSLRelTol2D, 1e-5); + GetParamDef( "gsl-max-eval", fGSLMaxEval, 100000); + int min; + GetParamDef( "gsl-min-eval", min, 7500 ) ; + fGSLMinEval = static_cast( min ); sm_utils = const_cast( dynamic_cast( diff --git a/src/Physics/QuasiElastic/XSection/SmithMonizQELCCXSec.h b/src/Physics/QuasiElastic/XSection/SmithMonizQELCCXSec.h index 107725cd8..856a7e01f 100644 --- a/src/Physics/QuasiElastic/XSection/SmithMonizQELCCXSec.h +++ b/src/Physics/QuasiElastic/XSection/SmithMonizQELCCXSec.h @@ -33,6 +33,7 @@ #define _SMITH_MONIZ_QEL_XSEC_H_ #include +#include #include "Physics/XSectionIntegration/XSecIntegratorI.h" #include "Physics/QuasiElastic/XSection/SmithMonizUtils.h" diff --git a/src/Physics/QuasiElastic/XSection/SmithMonizUtils.cxx b/src/Physics/QuasiElastic/XSection/SmithMonizUtils.cxx index c49d46419..9d2063f0c 100644 --- a/src/Physics/QuasiElastic/XSection/SmithMonizUtils.cxx +++ b/src/Physics/QuasiElastic/XSection/SmithMonizUtils.cxx @@ -146,7 +146,7 @@ void SmithMonizUtils::SetInteraction(const Interaction * interaction) // RFG is not applied for A<4 if (target.A()<4) { - E_BIN = P_Fermi = m_rnu = mm_rnu = 0; + E_BIN = kFi = m_rnu = mm_rnu = 0; return; } @@ -170,27 +170,33 @@ void SmithMonizUtils::SetInteraction(const Interaction * interaction) int Z = target.Z(); int A = target.A(); int N = A-Z; - - - // maximum value of Fermi momentum of target nucleon (GeV) + if (A < 6 || !fUseParametrization) { // look up the Fermi momentum for this Target FermiMomentumTablePool * kftp = FermiMomentumTablePool::Instance(); const FermiMomentumTable * kft = kftp->GetTable(fKFTable); - P_Fermi = kft->FindClosestKF(pdg::IonPdgCode(A, Z), nucl_pdg_ini); + kFi = kft->FindClosestKF(pdg::IonPdgCode(A, Z), nucl_pdg_ini); + kFf = kft->FindClosestKF(pdg::IonPdgCode(A, Z), nucl_pdg_fin); } else { // define the Fermi momentum for this Target - // - P_Fermi = utils::nuclear::FermiMomentumForIsoscalarNucleonParametrization(target); + double P_Fermi = utils::nuclear::FermiMomentumForIsoscalarNucleonParametrization(target); // correct the Fermi momentum for the struck nucleon - if(is_p) P_Fermi *= TMath::Power( 2.*Z/A, 1./3); + if(is_p) + { + kFi = P_Fermi*TMath::Power( 2.*Z/A, 1./3); + kFf = P_Fermi*TMath::Power( 2.*N/A, 1./3); + } else - P_Fermi *= TMath::Power( 2.*N/A, 1./3); + { + kFf = P_Fermi*TMath::Power( 2.*Z/A, 1./3); + kFi = P_Fermi*TMath::Power( 2.*N/A, 1./3); + } } + // neutrino binding energy (GeV) if (target.A() < 6 || !fUseParametrization) { @@ -221,7 +227,7 @@ double SmithMonizUtils::E_nu_thr_SM(void) const double E_min = ((m_lep + m_rnu + m_fin)*(m_lep + m_rnu + m_fin) - mm_tar)/2/m_tar; // Energy threshold of scattering on bound nucleon (Eq. (2) of Ref. 3) - double E_min2 = ((m_lep + m_fin)*(m_lep + m_fin)-mm_ini-E_BIN*E_BIN+2*E_BIN*TMath::Sqrt(mm_ini+P_Fermi*P_Fermi))/2/(TMath::Sqrt(mm_ini+P_Fermi*P_Fermi)-E_BIN+P_Fermi); + double E_min2 = ((m_lep + m_fin)*(m_lep + m_fin)-mm_ini-E_BIN*E_BIN+2*E_BIN*TMath::Sqrt(mm_ini+kFi*kFi))/2/(TMath::Sqrt(mm_ini+kFi*kFi)-E_BIN+kFi); E_min = TMath::Max(E_min, E_min2); @@ -275,11 +281,11 @@ double SmithMonizUtils::Q2lim1_SM(double Q2, double Enu) const // maximal energy transfer (see Eq. (9) of Ref.3) double nu_max = Enu*Q2/(Q2+mm_lep)-(Q2+mm_lep)/4/Enu; - double E = sqrt(P_Fermi*P_Fermi+mm_ini); - double b = (E-E_BIN)*(E-E_BIN)-P_Fermi*P_Fermi; + double E = TMath::Sqrt(kFi*kFi+mm_ini); + double b = (E-E_BIN)*(E-E_BIN)-kFi*kFi; double a = 0.5*(Q2+mm_fin-b); // minimal energy transfer for bound nucleon (see Eqs. (11) of Ref. 3), - double nu_1 = (a*(E-E_BIN)-P_Fermi*TMath::Sqrt(a*a+Q2*b))/b; + double nu_1 = (a*(E-E_BIN)-kFi*TMath::Sqrt(a*a+Q2*b))/b; return nu_1-nu_max; } @@ -290,11 +296,11 @@ double SmithMonizUtils::Q2lim2_SM(double Q2) const // minimal energy transfer for scattering on nucleus (see Eq. (7) of Ref.3) double nu_min = ((m_rnu+m_fin)*(m_rnu+m_fin)+Q2-mm_tar)/(2*m_tar); - double E = sqrt(P_Fermi*P_Fermi+mm_ini); - double b = (E-E_BIN)*(E-E_BIN)-P_Fermi*P_Fermi; + double E = TMath::Sqrt(kFi*kFi+mm_ini); + double b = (E-E_BIN)*(E-E_BIN)-kFi*kFi; double a = (Q2+mm_fin-b)*0.5; // maximal energy transfer for bound nucleon (see Eqs. (11) of Ref. 3) - double nu_2 = (a*(E-E_BIN)+P_Fermi*TMath::Sqrt(a*a+Q2*b))/b; + double nu_2 = (a*(E-E_BIN)+kFi*TMath::Sqrt(a*a+Q2*b))/b; return nu_min-nu_2; } @@ -323,7 +329,7 @@ Range1D_t SmithMonizUtils::Q2QES_SM_lim(void) const const double EPSREL = 1.0e-08; const double Precision = std::numeric_limits::epsilon(); // if the nucleus mass is less than 4 then this is a special case - if (E_BIN == 0 && P_Fermi == 0) + if (E_BIN == 0 && kFi == 0) { double s = 2*E_nu*m_ini+mm_ini; // minimal W2 for scattering on nucleus (see Eq. (6) of Ref.3) @@ -427,18 +433,18 @@ Range1D_t SmithMonizUtils::vQES_SM_lim(double Q2) const double nu_min= ((m_rnu+m_fin)*(m_rnu+m_fin)+Q2-mm_tar)/2/m_tar; // if the target is nucleon then nu_min=nu_max=(m_fin^2+Q^2-m_ini^2)/(2*m_ini) - if (E_BIN == 0 && P_Fermi == 0) + if (E_BIN == 0 && kFi == 0) return Range1D_t(nu_min, nu_min); // maximal energy transfer (see Eq. (9) of Ref.3) double nu_max = E_nu*Q2/(Q2+mm_lep)-(Q2+mm_lep)/4/E_nu; // now we find limits for bound nucleon - double E = TMath::Sqrt(P_Fermi*P_Fermi+mm_ini); - double b = (E-E_BIN)*(E-E_BIN)-P_Fermi*P_Fermi; + double E = TMath::Sqrt(kFi*kFi+mm_ini); + double b = (E-E_BIN)*(E-E_BIN)-kFi*kFi; double a = (Q2+mm_fin-b)*0.5; double tmp1 = a*(E-E_BIN); - double tmp2 = P_Fermi*TMath::Sqrt(a*a+Q2*b); + double tmp2 = kFi*TMath::Sqrt(a*a+Q2*b); // minimal and maximal energy transfer for bound nucleon (see Eqs. (11) of Ref. 3) double nu_1 = (tmp1-tmp2)/b; double nu_2 = (tmp1+tmp2)/b; @@ -462,7 +468,7 @@ double SmithMonizUtils::vQES_SM_min(double Q2_min, double Q2_max) const const double EPS = 1.0e-08; const double Delta= 1.0e-14; - if (E_BIN == 0 && P_Fermi == 0) + if (E_BIN == 0 && kFi == 0) return vQES_SM_lim(Q2_min).min; double F_MIN, Q2_0; @@ -482,7 +488,7 @@ double SmithMonizUtils::vQES_SM_max(double Q2_min, double Q2_max) const const double EPS = 1.0e-08; const double Delta= 1.0e-14; - if (E_BIN == 0 && P_Fermi == 0) + if (E_BIN == 0 && kFi == 0) return vQES_SM_lim(Q2_max).min; double F_MIN, Q2_0; @@ -501,11 +507,11 @@ double SmithMonizUtils::vlim1_SM(double Q2) const // minimal energy transfer for scattering on nucleus (see Eq. (7) of Ref.3) double nu_min = ((m_rnu+m_fin)*(m_rnu+m_fin)+Q2-mm_tar)/(2*m_tar); - double E = sqrt(P_Fermi*P_Fermi+mm_ini); - double b = (E-E_BIN)*(E-E_BIN)-P_Fermi*P_Fermi; + double E = TMath::Sqrt(kFi*kFi+mm_ini); + double b = (E-E_BIN)*(E-E_BIN)-kFi*kFi; double a = (Q2+mm_fin-b)*0.5; // minimal energy transfer for bound nucleon (see Eqs. (11) of Ref. 3) - double nu_1 = (a*(E-E_BIN)-P_Fermi*TMath::Sqrt(a*a+Q2*b))/b; + double nu_1 = (a*(E-E_BIN)-kFi*TMath::Sqrt(a*a+Q2*b))/b; nu_min= TMath::Max(nu_min,nu_1); return nu_min; } @@ -516,11 +522,11 @@ double SmithMonizUtils::vlim2_SM(double Q2) const // maximal energy transfer (see Eq. (9) of Ref.3) double nu_max = E_nu*Q2/(Q2+mm_lep)-(Q2+mm_lep)/4/E_nu; - double E = sqrt(P_Fermi*P_Fermi+mm_ini); - double b = (E-E_BIN)*(E-E_BIN)-P_Fermi*P_Fermi; + double E = TMath::Sqrt(kFi*kFi+mm_ini); + double b = (E-E_BIN)*(E-E_BIN)-kFi*kFi; double a = 0.5*(Q2+mm_fin-b); // maximal energy transfer for bound nucleon (see Eqs. (11) of Ref. 3) - double nu_2 = (a*(E-E_BIN)+P_Fermi*TMath::Sqrt(a*a+Q2*b))/b; + double nu_2 = (a*(E-E_BIN)+kFi*TMath::Sqrt(a*a+Q2*b))/b; nu_max= TMath::Min(nu_max,nu_2); return -nu_max; } @@ -532,9 +538,9 @@ Range1D_t SmithMonizUtils::kFQES_SM_lim(double Q2, double nu) const double c_f = (nu-E_BIN)/qv; double d_f = (E_BIN*E_BIN-2*nu*E_BIN-Q2+mm_ini-mm_fin)/(2*qv*m_ini); // minimal energy of initial nucleon (see Eq. (13) of Ref.3) - double Ef_min= TMath::Max(m_ini*(c_f*d_f+TMath::Sqrt(1.0-c_f*c_f+d_f*d_f))/(1.0-c_f*c_f), TMath::Sqrt(P_Fermi*P_Fermi+mm_ini)-nu); - double kF_min= P_Fermi!=0?TMath::Sqrt(TMath::Max(Ef_min*Ef_min-mm_ini,0.0)):0.; - double kF_max= P_Fermi; + double Ef_min= TMath::Max(m_ini*(c_f*d_f+TMath::Sqrt(1.0-c_f*c_f+d_f*d_f))/(1.0-c_f*c_f), TMath::Sqrt(kFi*kFi+mm_ini)-nu); + double kF_min= kFi!=0?TMath::Sqrt(TMath::Max(Ef_min*Ef_min-mm_ini,0.0)):0.; + double kF_max= kFi; Range1D_t R; if (kF_min<=kF_max) R = Range1D_t(kF_min,kF_max); @@ -620,7 +626,8 @@ double SmithMonizUtils::rho(double P_Fermi, double T_Fermi, double p) else { //Fermi-Dirac distribution - return 1.0/(1.0 + TMath::Exp(-(P_Fermi-p)/T_Fermi)); + double aux = TMath::Exp((P_Fermi-p)/T_Fermi); + return aux/(1 + aux); } @@ -631,9 +638,29 @@ double SmithMonizUtils::GetBindingEnergy(void) const return E_BIN; } //____________________________________________________________________________ -double SmithMonizUtils::GetFermiMomentum(void) const +double SmithMonizUtils::GetInitialFermiMomentum(void) const +{ + return kFi; +} +//____________________________________________________________________________ +double SmithMonizUtils::GetFinalFermiMomentum(void) const +{ + return kFf; +} +//____________________________________________________________________________ +void SmithMonizUtils::SetBindingEnergy(double val) +{ + E_BIN = val; +} +//____________________________________________________________________________ +void SmithMonizUtils::SetInitialFermiMomentum(double val) +{ + kFi = val; +} +//____________________________________________________________________________ +void SmithMonizUtils::SetFinalFermiMomentum(double val) { - return P_Fermi; + kFf = val; } //____________________________________________________________________________ double SmithMonizUtils::GetTheta_k(double v, double qv) const diff --git a/src/Physics/QuasiElastic/XSection/SmithMonizUtils.h b/src/Physics/QuasiElastic/XSection/SmithMonizUtils.h index 73011e760..ed70bacf6 100644 --- a/src/Physics/QuasiElastic/XSection/SmithMonizUtils.h +++ b/src/Physics/QuasiElastic/XSection/SmithMonizUtils.h @@ -58,7 +58,11 @@ class SmithMonizUtils : public Algorithm { virtual ~SmithMonizUtils(); void SetInteraction(const Interaction * i); double GetBindingEnergy(void) const; - double GetFermiMomentum(void) const; + double GetInitialFermiMomentum(void) const; + double GetFinalFermiMomentum(void) const; + void SetBindingEnergy(double val); + void SetInitialFermiMomentum(double val); + void SetFinalFermiMomentum(double val); double GetTheta_k(double v, double qv) const; double GetTheta_p(double pv, double v, double qv, double &E_p) const; double E_nu_thr_SM(void) const; @@ -73,8 +77,6 @@ class SmithMonizUtils : public Algorithm { //! to build the fragmentation function from configuration data void Configure(const Registry & config); void Configure(string config); - -double QEL_EnuMin_SM(double E_nu) const; private: class Functor1D @@ -111,7 +113,7 @@ double QEL_EnuMin_SM(double E_nu) const; }; void LoadConfig (void); - // double QEL_EnuMin_SM(double E_nu) const; + double QEL_EnuMin_SM(double E_nu) const; double Q2lim1_SM(double Q2, double Enu) const; double Q2lim2_SM(double Q2) const; void DMINFC(Functor1D &F, double A,double B, double EPS, double DELTA, double &X, double &Y, bool &LLM) const; @@ -138,7 +140,8 @@ double QEL_EnuMin_SM(double E_nu) const; double mm_tar; ///< Squared mass of target nucleus (GeV) double m_rnu; ///< Mass of residual nucleus (GeV) double mm_rnu; ///< Squared mass of residual nucleus (GeV) - double P_Fermi; ///< Maximum value of Fermi momentum of target nucleon (GeV) + double kFi; ///< Fermi momentum of initial nucleon + double kFf; ///< Fermi momentum of finial nucleon double E_BIN; ///< Binding energy (GeV) diff --git a/src/Physics/QuasiElastic/XSection/SuSAv2QELPXSec.cxx b/src/Physics/QuasiElastic/XSection/SuSAv2QELPXSec.cxx index 99e1c90da..2766d8775 100644 --- a/src/Physics/QuasiElastic/XSection/SuSAv2QELPXSec.cxx +++ b/src/Physics/QuasiElastic/XSection/SuSAv2QELPXSec.cxx @@ -7,7 +7,6 @@ For the class documentation see the corresponding header file. */ //_________________________________________________________________________ - #include "Framework/Algorithm/AlgConfigPool.h" #include "Framework/Conventions/Units.h" #include "Framework/Messenger/Messenger.h" @@ -23,11 +22,13 @@ #include "Physics/NuclearState/FermiMomentumTablePool.h" #include "Physics/NuclearState/FermiMomentumTable.h" #include "Framework/Conventions/Constants.h" +#include "Physics/Common/PrimaryLeptonUtils.h" #include using namespace genie; +using namespace std::complex_literals; //_________________________________________________________________________ SuSAv2QELPXSec::SuSAv2QELPXSec() : XSecAlgorithmI("genie::SuSAv2QELPXSec") @@ -470,13 +471,12 @@ double SuSAv2QELPXSec::XSec(const Interaction* interaction, xsec *= xsec_scale ; - if ( kps != kPSTlctl ) { - LOG("SuSAv2QE", pWARN) - << "Doesn't support transformation from " - << KinePhaseSpace::AsString(kPSTlctl) << " to " - << KinePhaseSpace::AsString(kps); - xsec = 0.; - } + if ( kps != kPSTlctl ) + { + // Compute the appropriate Jacobian for transformation to the requested phase space + double J = utils::kinematics::Jacobian(interaction, kPSTlctl, kps); + xsec *= J; + } return xsec; } @@ -605,6 +605,9 @@ void SuSAv2QELPXSec::LoadConfig(void) GetParam( "QEL-CC-XSecScale", fXSecCCScale ) ; GetParam( "QEL-NC-XSecScale", fXSecNCScale ) ; GetParam( "QEL-EM-XSecScale", fXSecEMScale ) ; + + // Do precise calculation of lepton polarization + GetParamDef( "PreciseLeptonPol", fIsPreciseLeptonPolarization, false ) ; // Cross section model choice int modelChoice; @@ -664,3 +667,551 @@ void SuSAv2QELPXSec::LoadConfig(void) } } +//_________________________________________________________________________ +const TVector3 & SuSAv2QELPXSec::FinalLeptonPolarization (const Interaction* interaction) const +{ + const ProcessInfo& proc_info = interaction->ProcInfo(); + if ( proc_info.IsEM() ) + { + LOG("SuSAv2QE", pWARN) << "For EM processes doesn't work yet. Set it to zero."; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + if (!fIsPreciseLeptonPolarization || proc_info.IsWeakNC()) return XSecAlgorithmI::FinalLeptonPolarization(interaction); + const Kinematics& kinematics = interaction -> Kine(); + const InitialState& init_state = interaction -> InitState(); + const Target& tgt = init_state.Tgt(); + int target_pdg = tgt.Pdg(); + int probe_pdg = interaction->InitState().ProbePdg(); + + bool is_neutrino = pdg::IsNeutrino(probe_pdg); + bool is_antineutrino = pdg::IsAntiNeutrino(probe_pdg); + + const double M = 1; // the polarization doesn't depend on mass of target in target rest frame + + TLorentzVector * tempNeutrino = init_state.GetProbeP4(kRfLab); + TLorentzVector neutrinoMom = *tempNeutrino; + delete tempNeutrino; + const TLorentzVector leptonMom = kinematics.FSLeptonP4(); + + double Ev = neutrinoMom.E(); + double El = leptonMom.E(); + double ml = leptonMom.M(); + double Tl = El - ml; + TVector3 neutrinoMom3 = neutrinoMom.Vect(); + TVector3 leptonMom3 = leptonMom.Vect(); + double pv = neutrinoMom3.Mag(); + double pl = leptonMom3.Mag(); + double costl = pl*pv != 0 ? neutrinoMom3.Dot(leptonMom3)/pl/pv : 0; + double Q0 = 0; + double Q3 = 0; + genie::utils::mec::Getq0q3FromTlCostl(Tl, costl, Ev, ml, Q0, Q3); + + // *** Enforce the global Q^2 cut *** + double Q2min = genie::controls::kMinQ2Limit; // CC limit + //if ( interaction->ProcInfo().IsEM() ) + //Q2min = genie::utils::kinematics::electromagnetic::kMinQ2Limit; // EM limit + + // Neglect shift due to binding energy. The cut is on the actual + // value of Q^2, not the effective one to use in the tensor contraction. + double Q2 = Q3*Q3 - Q0*Q0; + if ( Q2 < Q2min ) + { + LOG("SuSAv2QE", pWARN) << "Can't calculate final lepton polarization. Set it to zero."; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + + + // ****************************** + // Now choose which tesor to use + // ****************************** + int tensor_pdg_susa = target_pdg; + int tensor_pdg_crpa = target_pdg; + int A_request = pdg::IonPdgCodeToA(target_pdg); + int Z_request = pdg::IonPdgCodeToZ(target_pdg); + bool need_to_scale_susa = false; + bool need_to_scale_crpa = false; + + // This gets a bit messy as the different models have different + // targets available and currently only SuSA does EM + HadronTensorType_t tensor_type_susa = kHT_Undefined; + HadronTensorType_t tensor_type_crpa = kHT_Undefined; + HadronTensorType_t tensor_type_blen = kHT_Undefined; + + if ( is_neutrino ) + { + tensor_type_susa = kHT_QE_Full; + tensor_type_blen = kHT_QE_SuSABlend; + // CRPA/HF tensors having q0 dependent binning, so are split + // CRPA + if (modelConfig == kMd_CRPA || modelConfig == kMd_CRPASuSAv2Hybrid) + { + if(Q0<0.060) tensor_type_crpa = kHT_QE_CRPA_Low; + else if(Q0<0.150) tensor_type_crpa = kHT_QE_CRPA_Medium; + else tensor_type_crpa = kHT_QE_CRPA_High; + } + // Hartree-Fock + if (modelConfig == kMd_HF || modelConfig == kMd_HFSuSAv2Hybrid) + { + if(Q0<0.060) tensor_type_crpa = kHT_QE_HF_Low; + else if(Q0<0.150) tensor_type_crpa = kHT_QE_HF_Medium; + else tensor_type_crpa = kHT_QE_HF_High; + } + if (modelConfig == kMd_CRPAPW || modelConfig == kMd_CRPAPWSuSAv2Hybrid) + { + if(Q0<0.060) tensor_type_crpa = kHT_QE_CRPAPW_Low; + else if(Q0<0.150) tensor_type_crpa = kHT_QE_CRPAPW_Medium; + else tensor_type_crpa = kHT_QE_CRPAPW_High; + } + // Hartree-Fock + if (modelConfig == kMd_HFPW || modelConfig == kMd_HFPWSuSAv2Hybrid) + { + if(Q0<0.060) tensor_type_crpa = kHT_QE_HFPW_Low; + else if(Q0<0.150) tensor_type_crpa = kHT_QE_HFPW_Medium; + else tensor_type_crpa = kHT_QE_HFPW_High; + } + } + else if ( is_antineutrino ) + { + // SuSA implementation doesn't accoutn for asymmetry between protons + // and neutrons. In general this is a small effect. + tensor_type_susa = kHT_QE_Full; + //For the blending case, Ar40 is treated specially: + if(A_request == 40 && Z_request == 18) + { + tensor_type_blen = kHT_QE_SuSABlend_anu; + } + else tensor_type_blen = kHT_QE_SuSABlend; + // CRPA tensors having q0 dependent binning, so are split: + //CRPA + if (modelConfig == kMd_CRPA || modelConfig == kMd_CRPASuSAv2Hybrid) + { + if(Q0<0.060) tensor_type_crpa = kHT_QE_CRPA_anu_Low; + else if(Q0<0.150) tensor_type_crpa = kHT_QE_CRPA_anu_Medium; + else tensor_type_crpa = kHT_QE_CRPA_anu_High; + } + // Hartree-Fock + if (modelConfig == kMd_HF || modelConfig == kMd_HFSuSAv2Hybrid) + { + if(Q0<0.060) tensor_type_crpa = kHT_QE_HF_anu_Low; + else if(Q0<0.150) tensor_type_crpa = kHT_QE_HF_anu_Medium; + else tensor_type_crpa = kHT_QE_HF_anu_High; + } + if (modelConfig == kMd_CRPAPW || modelConfig == kMd_CRPAPWSuSAv2Hybrid) + { + if(Q0<0.060) tensor_type_crpa = kHT_QE_CRPAPW_anu_Low; + else if(Q0<0.150) tensor_type_crpa = kHT_QE_CRPAPW_anu_Medium; + else tensor_type_crpa = kHT_QE_CRPAPW_anu_High; + } + // Hartree-Fock + if (modelConfig == kMd_HFPW || modelConfig == kMd_HFPWSuSAv2Hybrid) + { + if(Q0<0.060) tensor_type_crpa = kHT_QE_HFPW_anu_Low; + else if(Q0<0.150) tensor_type_crpa = kHT_QE_HFPW_anu_Medium; + else tensor_type_crpa = kHT_QE_HFPW_anu_High; + } + } + //else + //{ + //if ( pdg::IsProton(hit_nuc_pdg) ) + //{ + //tensor_type_susa = kHT_QE_EM_proton; + //} + //else if( pdg::IsNeutron(hit_nuc_pdg) ) + //{ + //tensor_type_susa = kHT_QE_EM_neutron; + //} + //else + //{ + //tensor_type_susa = kHT_QE_EM; // default + //} + //} + else + { + LOG("SuSAv2QE", pWARN) << "Doesn't work for processes other than weak ones. Set it to zero."; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + + double Eb_tgt=0; + double Eb_ten_susa=0; + double Eb_ten_crpa=0; + + if ( A_request <= 4 ) + { + // Use carbon tensor for very light nuclei. This is not ideal . . . + Eb_tgt = fEbHe; + tensor_pdg_susa = kPdgTgtC12; + tensor_pdg_crpa = kPdgTgtC12; + Eb_ten_susa = fEbC; + Eb_ten_crpa = fEbC; + } + else if (A_request < 9) + { + Eb_tgt=fEbLi; + tensor_pdg_susa = kPdgTgtC12; + tensor_pdg_crpa = kPdgTgtC12; + Eb_ten_susa = fEbC; + Eb_ten_crpa = fEbC; + } + else if (A_request >= 9 && A_request < 15) + { + Eb_tgt=fEbC; + tensor_pdg_susa = kPdgTgtC12; + tensor_pdg_crpa = kPdgTgtC12; + Eb_ten_susa = fEbC; + Eb_ten_crpa = fEbC; + } + else if(A_request >= 15 && A_request < 22) + { + Eb_tgt=fEbO; + tensor_pdg_susa = kPdgTgtC12; + tensor_pdg_crpa = kPdgTgtO16; + Eb_ten_susa = fEbC; + Eb_ten_crpa = fEbO; + } + else if(A_request == 40 && Z_request == 18) + { + // Treat the common non-isoscalar case specially + Eb_tgt=fEbAr; + tensor_pdg_susa = kPdgTgtC12; + tensor_pdg_crpa = 1000180400; + Eb_ten_susa = fEbC; + Eb_ten_crpa = fEbAr; + } + else if(A_request >= 22 && A_request < 40) + { + Eb_tgt=fEbMg; + tensor_pdg_susa = kPdgTgtC12; + tensor_pdg_crpa = kPdgTgtO16; + Eb_ten_susa = fEbC; + Eb_ten_crpa = fEbO; + } + else if(A_request >= 40 && A_request < 56) { + Eb_tgt=fEbAr; + tensor_pdg_susa = kPdgTgtC12; + tensor_pdg_crpa = kPdgTgtO16; + Eb_ten_susa = fEbC; + Eb_ten_crpa = fEbO; + } + else if(A_request >= 56 && A_request < 119) + { + Eb_tgt=fEbFe; + tensor_pdg_susa = kPdgTgtC12; + tensor_pdg_crpa = kPdgTgtO16; + Eb_ten_susa = fEbC; + Eb_ten_crpa = fEbO; + } + else if(A_request >= 119 && A_request < 206) + { + Eb_tgt=fEbSn; + tensor_pdg_susa = kPdgTgtC12; + tensor_pdg_crpa = kPdgTgtO16; + Eb_ten_susa = fEbC; + Eb_ten_crpa = fEbO; + } + else if(A_request >= 206) + { + Eb_tgt=fEbPb; + tensor_pdg_susa = kPdgTgtC12; + tensor_pdg_crpa = kPdgTgtO16; + Eb_ten_susa = fEbC; + Eb_ten_crpa = fEbO; + } + + if (tensor_pdg_susa != target_pdg) need_to_scale_susa = true; + if (tensor_pdg_crpa != target_pdg) need_to_scale_crpa = true; + + // Finally we can now get the tensors we need + const LabFrameHadronTensorI* tensor_susa; + const LabFrameHadronTensorI* tensor_crpa; + const LabFrameHadronTensorI* tensor_blen; + + if( modelConfig == kMd_SuSAv2 ) + { + tensor_susa = dynamic_cast + ( fHadronTensorModel->GetTensor (tensor_pdg_susa, tensor_type_susa) ); + + if ( !tensor_susa ) + { + LOG("SuSAv2QE", pWARN) << "Can't calculate final lepton polarization. Set it to zero."; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + } + + if( modelConfig == kMd_CRPASuSAv2Hybrid || modelConfig == kMd_HFSuSAv2Hybrid || + modelConfig == kMd_CRPAPWSuSAv2Hybrid || modelConfig == kMd_HFPWSuSAv2Hybrid || + modelConfig == kMd_SuSAv2Blend) + { + tensor_blen = dynamic_cast + ( fHadronTensorModel->GetTensor (tensor_pdg_crpa, tensor_type_blen) ); + + if ( !tensor_blen ) + { + LOG("SuSAv2QE", pWARN) << "Can't calculate final lepton polarization. Set it to zero."; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + } + + if( modelConfig != kMd_SuSAv2 && modelConfig != kMd_SuSAv2Blend) + { + tensor_crpa = dynamic_cast + ( fHadronTensorModel->GetTensor (tensor_pdg_crpa, tensor_type_crpa) ); + + if ( !tensor_crpa ) + { + LOG("SuSAv2QE", pWARN) << "Can't calculate final lepton polarization. Set it to zero."; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + } + + // ***************************** + // Q_value offset calculation + // ***************************** + + // SD: The Q-Value essentially corrects q0 to account for nuclear + // binding energy in the Valencia model but this effect is already + // in the SuSAv2 and CRPA/HF tensors so I'll set it to 0. + // However, if I want to scale I need to account for the altered + // binding energy. To first order I can use the Q_value for this + double Delta_Q_value_susa = Eb_tgt-Eb_ten_susa; + double Delta_Q_value_crpa = Eb_tgt-Eb_ten_crpa; + double Delta_Q_value_blen = Eb_tgt-Eb_ten_crpa; + + // Apply Qvalue relative shift if needed: + if( fQvalueShifter ) + { + // We have the option to add an additional shift on top of the binding energy correction + // The QvalueShifter, is a relative shift to the Q_value. + // The Q_value was already taken into account in the hadron tensor. Here we recalculate it + // to get the right absolute shift. + double tensor_Q_value_susa = genie::utils::mec::Qvalue(tensor_pdg_susa,probe_pdg); + double total_Q_value_susa = tensor_Q_value_susa + Delta_Q_value_susa ; + double Q_value_shift_susa = total_Q_value_susa * fQvalueShifter -> Shift( interaction->InitState().Tgt() ) ; + + double tensor_Q_value_crpa = genie::utils::mec::Qvalue(tensor_pdg_crpa,probe_pdg); + double total_Q_value_crpa = tensor_Q_value_crpa + Delta_Q_value_crpa ; + double Q_value_shift_crpa = total_Q_value_crpa * fQvalueShifter -> Shift( interaction->InitState().Tgt() ) ; + + Delta_Q_value_susa += Q_value_shift_susa; + Delta_Q_value_crpa += Q_value_shift_crpa; + Delta_Q_value_blen += Q_value_shift_crpa; + } + + // Set the xsec to zero for interactions with q0,q3 outside the requested range + + if( modelConfig == kMd_SuSAv2) + { + double Q0min = tensor_susa->q0Min(); + double Q0max = tensor_susa->q0Max(); + double Q3min = tensor_susa->qMagMin(); + double Q3max = tensor_susa->qMagMax(); + if (Q0-Delta_Q_value_susa < Q0min || Q0-Delta_Q_value_susa > Q0max || Q3 < Q3min || Q3 > Q3max) + { + LOG("SuSAv2QE", pWARN) << "Can't calculate final lepton polarization. Set it to zero."; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + } + else if ( modelConfig == kMd_CRPA || modelConfig == kMd_HF || + modelConfig == kMd_CRPAPW || modelConfig == kMd_HFPW ) + { + double Q0min = tensor_crpa->q0Min(); + double Q0max = tensor_crpa->q0Max(); + double Q3min = tensor_crpa->qMagMin(); + double Q3max = tensor_crpa->qMagMax(); + if (Q0-Delta_Q_value_crpa < Q0min || Q0-Delta_Q_value_crpa > Q0max || Q3 < Q3min || Q3 > Q3max) + { + LOG("SuSAv2QE", pWARN) << "Can't calculate final lepton polarization. Set it to zero."; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + } + else if ( modelConfig == kMd_SuSAv2Blend) + { + double Q0min = tensor_blen->q0Min(); + double Q0max = tensor_blen->q0Max(); + double Q3min = tensor_blen->qMagMin(); + double Q3max = tensor_blen->qMagMax(); + if (Q0-Delta_Q_value_blen < Q0min || Q0-Delta_Q_value_blen > Q0max || Q3 < Q3min || Q3 > Q3max) + { + LOG("SuSAv2QE", pWARN) << "Can't calculate final lepton polarization. Set it to zero."; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + } + else + { // hybrid (blending) cases. Low kinematics handled by CRPA/HF, high kinematics by blended SuSA + double Q0min = tensor_crpa->q0Min(); + double Q0max = tensor_blen->q0Max(); + double Q3min = tensor_crpa->qMagMin(); + double Q3max = tensor_blen->qMagMax(); + if (Q0-Delta_Q_value_crpa < Q0min || Q0-Delta_Q_value_blen > Q0max || Q3 < Q3min || Q3 > Q3max) + { + LOG("SuSAv2QE", pWARN) << "Can't calculate final lepton polarization. Set it to zero."; + fFinalLeptonPolarization = TVector3(0, 0, 0); + return fFinalLeptonPolarization; + } + } + + double W1_susa(0), W2_susa(0), W3_susa(0), W4_susa(0), W5_susa(0); + double W1_crpa(0), W2_crpa(0), W3_crpa(0), W4_crpa(0), W5_crpa(0); + double W1_blen(0), W2_blen(0), W3_blen(0), W4_blen(0), W5_blen(0); + + if ( modelConfig == kMd_SuSAv2 ) + { + W1_susa = tensor_susa->W1(Q0 - Delta_Q_value_susa, Q3, M); + W2_susa = tensor_susa->W2(Q0 - Delta_Q_value_susa, Q3, M); + W3_susa = -tensor_susa->W3(Q0 - Delta_Q_value_susa, Q3, M); // note invert sign + W4_susa = tensor_susa->W4(Q0 - Delta_Q_value_susa, Q3, M); + W5_susa = tensor_susa->W5(Q0 - Delta_Q_value_susa, Q3, M); + + double fact_susa = XSecScaling(1.0, interaction, target_pdg, tensor_pdg_susa, need_to_scale_susa); + W1_susa *= fact_susa; + W2_susa *= fact_susa; + W3_susa *= fact_susa; + W4_susa *= fact_susa; + W5_susa *= fact_susa; + } + if ( modelConfig == kMd_CRPASuSAv2Hybrid || modelConfig == kMd_HFSuSAv2Hybrid || + modelConfig == kMd_CRPAPWSuSAv2Hybrid || modelConfig == kMd_HFPWSuSAv2Hybrid || + modelConfig == kMd_SuSAv2Blend) + { + W1_blen = tensor_blen->W1(Q0 - Delta_Q_value_blen, Q3, M); + W2_blen = tensor_blen->W2(Q0 - Delta_Q_value_blen, Q3, M); + W3_blen = -tensor_blen->W3(Q0 - Delta_Q_value_blen, Q3, M); // note invert sign + W4_blen = tensor_blen->W4(Q0 - Delta_Q_value_blen, Q3, M); + W5_blen = tensor_blen->W5(Q0 - Delta_Q_value_blen, Q3, M); + + // The blended SuSAv2 calculation already gives the xsec per atom + // For the A-scaling below to make sense we need to transform them to per active nucleon + int A_tensor = pdg::IonPdgCodeToA(tensor_pdg_crpa); + int Z_tensor = pdg::IonPdgCodeToZ(tensor_pdg_crpa); + int N_tensor = A_tensor-Z_tensor; + double fact_blen = 1.0; + if ( pdg::IsNeutrino(probe_pdg) ) fact_blen *= 1.0/N_tensor; + else if ( pdg::IsAntiNeutrino(probe_pdg) ) fact_blen *= 1.0/Z_tensor; + fact_blen = XSecScaling(fact_blen, interaction, target_pdg, tensor_pdg_crpa, need_to_scale_crpa); + W1_blen *= fact_blen; + W2_blen *= fact_blen; + W3_blen *= fact_blen; + W4_blen *= fact_blen; + W5_blen *= fact_blen; + } + if( modelConfig != kMd_SuSAv2 && modelConfig != kMd_SuSAv2Blend) + { + W1_crpa = tensor_crpa->W1(Q0 - Delta_Q_value_crpa, Q3, M); + W2_crpa = tensor_crpa->W2(Q0 - Delta_Q_value_crpa, Q3, M); + W3_crpa = -tensor_crpa->W3(Q0 - Delta_Q_value_crpa, Q3, M); // note invert sign + W4_crpa = tensor_crpa->W4(Q0 - Delta_Q_value_crpa, Q3, M); + W5_crpa = tensor_crpa->W5(Q0 - Delta_Q_value_crpa, Q3, M); + + // The CRPA calculation already gives the xsec per atom + // For the A-scaling below to make sense we need to transform them to per active nucleon + int A_tensor = pdg::IonPdgCodeToA(tensor_pdg_crpa); + int Z_tensor = pdg::IonPdgCodeToZ(tensor_pdg_crpa); + int N_tensor = A_tensor-Z_tensor; + double fact_crpa = 1.0; + if ( pdg::IsNeutrino(probe_pdg) ) fact_crpa *= 1.0/N_tensor; + else if ( pdg::IsAntiNeutrino(probe_pdg) ) fact_crpa *= 1.0/Z_tensor; + fact_crpa = XSecScaling(fact_crpa, interaction, target_pdg, tensor_pdg_crpa, need_to_scale_crpa); + W1_crpa *= fact_crpa; + W2_crpa *= fact_crpa; + W3_crpa *= fact_crpa; + W4_crpa *= fact_crpa; + W5_crpa *= fact_crpa; + } + + // Apply blending if needed + double W1(0), W2(0), W3(0), W4(0), W5(0); + + if ( modelConfig == kMd_SuSAv2 ) + { + W1 = W1_susa; + W2 = W2_susa; + W3 = W3_susa; + W4 = W4_susa; + W5 = W5_susa; + + } + if ( modelConfig == kMd_SuSAv2Blend ) + { + W1 = W1_blen; + W2 = W2_blen; + W3 = W3_blen; + W4 = W4_blen; + W5 = W5_blen; + } + if( modelConfig == kMd_CRPA || modelConfig == kMd_HF || + modelConfig == kMd_CRPAPW || modelConfig == kMd_HFPW ) + { + W1 = W1_crpa; + W2 = W2_crpa; + W3 = W3_crpa; + W4 = W4_crpa; + W5 = W5_crpa; + } + else if( modelConfig == kMd_CRPASuSAv2Hybrid || + modelConfig == kMd_HFSuSAv2Hybrid || + modelConfig == kMd_CRPAPWSuSAv2Hybrid || + modelConfig == kMd_HFPWSuSAv2Hybrid ) + { + // blending cases + if(blendMode == 1) // Linear blending in q0 + if (Q0 < q0BlendStart) + { + W1 = W1_crpa; + W2 = W2_crpa; + W3 = W3_crpa; + W4 = W4_crpa; + W5 = W5_crpa; + } + else if (Q0 > q0BlendEnd) + { + W1 = W1_blen; + W2 = W2_blen; + W3 = W3_blen; + W4 = W4_blen; + W5 = W5_blen; + } + else + { + double SuSAFrac = (Q0 - q0BlendStart) / (q0BlendEnd - q0BlendStart); + double CRPAFrac = 1 - SuSAFrac; + W1 = SuSAFrac*W1_blen + CRPAFrac*W1_crpa; + W2 = SuSAFrac*W2_blen + CRPAFrac*W2_crpa; + W3 = SuSAFrac*W3_blen + CRPAFrac*W3_crpa; + W4 = SuSAFrac*W4_blen + CRPAFrac*W4_crpa; + W5 = SuSAFrac*W5_blen + CRPAFrac*W5_crpa; + } + else if(blendMode == 2) + { // Exp blending in q (from Alexis) + double phi_q = (genie::constants::kPi / 2.) * (1 - 1./(1+std::exp( (Q3 - qBlendRef)/qBlendDel)) ); + double sn2 = TMath::Sin(phi_q)*TMath::Sin(phi_q); + double cn2 = 1 - sn2; + W1 = sn2*W1_blen + cn2*W1_crpa; + W2 = sn2*W2_blen + cn2*W2_crpa; + W3 = sn2*W3_blen + cn2*W3_crpa; + W4 = sn2*W4_blen + cn2*W4_crpa; + W5 = sn2*W5_blen + cn2*W5_crpa; + } + } + + genie::utils::CalculatePolarizationVectorInTargetRestFrame( + fFinalLeptonPolarization, + neutrinoMom, + leptonMom, + is_neutrino, + M, W1,W2,W3,W4,W5,0); + + +// std::cout << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"; +// std::cout << fFinalLeptonPolarization.Mag() << "\n"; +// std::cout << "SU@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" << std::endl; + + return fFinalLeptonPolarization; +} diff --git a/src/Physics/QuasiElastic/XSection/SuSAv2QELPXSec.h b/src/Physics/QuasiElastic/XSection/SuSAv2QELPXSec.h index cb7cd69de..a8a67ca1c 100644 --- a/src/Physics/QuasiElastic/XSection/SuSAv2QELPXSec.h +++ b/src/Physics/QuasiElastic/XSection/SuSAv2QELPXSec.h @@ -49,6 +49,7 @@ class SuSAv2QELPXSec : public XSecAlgorithmI { double XSec(const Interaction* i, KinePhaseSpace_t k) const; double Integral(const Interaction* i) const; bool ValidProcess(const Interaction* i) const; + const TVector3 & FinalLeptonPolarization (const Interaction* i) const; // override the Algorithm::Configure methods to load configuration // data to private data members @@ -63,7 +64,7 @@ class SuSAv2QELPXSec : public XSecAlgorithmI { // Apply scaling of the first kind to the xsec // (A-scaling with tuning based on Fermi momentum) double XSecScaling(double xsec, const Interaction* i, int target_pdg, int tensor_pdg, bool need_to_scale) const; - + /// External scaling factor for this cross section double fXSecCCScale; diff --git a/src/Physics/Resonance/EventGen/SPPEventGenerator.cxx b/src/Physics/Resonance/EventGen/SPPEventGenerator.cxx index 9bcf8d991..a7a455cbb 100644 --- a/src/Physics/Resonance/EventGen/SPPEventGenerator.cxx +++ b/src/Physics/Resonance/EventGen/SPPEventGenerator.cxx @@ -209,14 +209,6 @@ void SPPEventGenerator::ProcessEventRecord(GHepRecord * evrec) const // set the cross section for the selected kinematics evrec->SetDiffXSec(xsec,kPSWQ2ctpphipfE); - // lock selected kinematics & clear running values - interaction->KinePtr()->SetQ2(Q2, true); - interaction->KinePtr()->SetW (W, true); - interaction->KinePtr()->Setx (x, true); - interaction->KinePtr()->Sety (y, true); - interaction->KinePtr()->ClearRunningValues(); - - double W2 = W*W; // Kinematical values of all participating particles in the isobaric frame double Enu_isb = (Ev*M - (ml2 + Q2)/2)/W; @@ -262,6 +254,15 @@ void SPPEventGenerator::ProcessEventRecord(GHepRecord * evrec) const tgt->SetHitNucP4(p1_copy); + + // lock selected kinematics & clear running values + interaction->KinePtr()->SetFSLeptonP4(k2_isb); + interaction->KinePtr()->SetHadSystP4(p2_isb); + interaction->KinePtr()->SetQ2(Q2, true); + interaction->KinePtr()->SetW (W, true); + interaction->KinePtr()->Setx (x, true); + interaction->KinePtr()->Sety (y, true); + interaction->KinePtr()->ClearRunningValues(); TLorentzVector x4l(*(evrec->Probe())->X4()); // add final lepton diff --git a/src/Physics/Strange/XSection/PaisQELLambdaPXSec.cxx b/src/Physics/Strange/XSection/PaisQELLambdaPXSec.cxx index 1d3aa04a0..db3fffd84 100644 --- a/src/Physics/Strange/XSection/PaisQELLambdaPXSec.cxx +++ b/src/Physics/Strange/XSection/PaisQELLambdaPXSec.cxx @@ -7,7 +7,6 @@ Tufts University */ //____________________________________________________________________________ - #include #include "Framework/Algorithm/AlgConfigPool.h" @@ -28,6 +27,7 @@ #include "Physics/QuasiElastic/XSection/QELFormFactors.h" #include "Physics/QuasiElastic/XSection/QELFormFactorsModelI.h" #include "Physics/Strange/XSection/PaisQELLambdaPXSec.h" +#include "Physics/Common/PrimaryLeptonUtils.h" using namespace genie; using namespace genie::constants; @@ -223,6 +223,9 @@ void PaisQELLambdaPXSec::LoadConfig(void) double thc ; GetParam( "CabibboAngle", thc ) ; fSin8c2 = TMath::Power(TMath::Sin(thc), 2); + + // Do precise calculation of lepton polarization + GetParamDef( "PreciseLeptonPol", fIsPreciseLeptonPolarization, false ) ; // load QEL form factors model fFormFactorsModel = dynamic_cast ( @@ -236,3 +239,77 @@ void PaisQELLambdaPXSec::LoadConfig(void) assert(fXSecIntegrator); } //____________________________________________________________________________ +const TVector3 & PaisQELLambdaPXSec::FinalLeptonPolarization (const Interaction* interaction) const +{ + if (!fIsPreciseLeptonPolarization) return XSecAlgorithmI::FinalLeptonPolarization(interaction); + // First we need access to all of the particles in the interaction + // The particles were stored in the lab frame + //----- get kinematics & init state - compute auxiliary vars + const Kinematics & kinematics = interaction->Kine(); + const InitialState & init_state = interaction->InitState(); + const Target & target = init_state.Tgt(); + + bool is_neutrino = pdg::IsNeutrino(init_state.ProbePdg()); + + // HitNucMass() looks up the PDGLibrary (on-shell) value for the initial + // struck nucleon + double Mnuc = target.HitNucMass(); + double Mnuc2 = Mnuc*Mnuc; + + // Note that GetProbeP4 defaults to returning the probe 4-momentum in the + // struck nucleon rest frame, so we have to explicitly ask for the lab frame + // here + TLorentzVector * tempNeutrino = init_state.GetProbeP4(kRfLab); + TLorentzVector neutrinoMom = *tempNeutrino; + delete tempNeutrino; + TLorentzVector inNucleonMom(*init_state.TgtPtr()->HitNucP4Ptr()); + const TLorentzVector leptonMom = kinematics.FSLeptonP4(); + + // Ordinary 4-momentum transfer + TLorentzVector qP4 = neutrinoMom - leptonMom; + double Q2 = -qP4.Mag2(); + interaction->KinePtr()->SetQ2(Q2); + // Calculate the QEL form factors + fFormFactors.Calculate(interaction); + double F1V = fFormFactors.F1V(); + double xiF2V = fFormFactors.xiF2V(); + double FA = fFormFactors.FA(); + + //neutrino momentum transfer + double q2 = -Q2; + + //----- Calculate the differential cross section dxsec/dQ^2 + //resonance mass & nucleon mass + double Mi = Mnuc; + double Mf = (this)->MHyperon(interaction); + // calculate w coefficients + //start with Mass terms + double Mp = Mf + Mi; + double Mm = Mf - Mi; + double Mm2 = Mm*Mm; + double Mp2 = Mp*Mp; + + //Powers of Form Factors + double FA2 = FA*FA; + + //Calculate W terms + double W1 = (Mm2 - q2)*TMath::Sq(F1V + xiF2V)/4/Mnuc2 + (Mp2 - q2)*FA2/4/Mnuc2; + double W2 = FA2 + TMath::Sq(F1V + xiF2V - Mp*xiF2V/2/Mnuc) - q2*xiF2V*xiF2V/4/Mnuc2; + double W3 = 2*FA*(F1V + xiF2V); + + CalculatePolarizationVectorWithStructureFunctions( + fFinalLeptonPolarization, + neutrinoMom, + leptonMom, + inNucleonMom, + qP4, + is_neutrino, + W1,W2,W3,0,0,0); + + +// std::cout << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"; +// std::cout << fFinalLeptonPolarization.Mag() << "\n"; +// std::cout << "PL@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n" << std::endl; + + return fFinalLeptonPolarization; +} diff --git a/src/Physics/Strange/XSection/PaisQELLambdaPXSec.h b/src/Physics/Strange/XSection/PaisQELLambdaPXSec.h index 392d11c12..b5aaf54f0 100644 --- a/src/Physics/Strange/XSection/PaisQELLambdaPXSec.h +++ b/src/Physics/Strange/XSection/PaisQELLambdaPXSec.h @@ -48,6 +48,7 @@ class PaisQELLambdaPXSec : public XSecAlgorithmI { double Integral (const Interaction * i) const; bool ValidProcess (const Interaction * i) const; bool ValidKinematics (const Interaction * i) const; + const TVector3 & FinalLeptonPolarization (const Interaction* i) const; //-- override the Algorithm::Configure methods to load configuration // data to private data members